зеркало из https://github.com/mozilla/pjs.git
339 строки
11 KiB
HTML
339 строки
11 KiB
HTML
<!DOCTYPE HTML>
|
|
<html>
|
|
<!--
|
|
-->
|
|
<head>
|
|
<title>Test for parsing, storage, and serialization of CSS values</title>
|
|
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
|
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
|
<script type="text/javascript" src="property_database.js"></script>
|
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
|
</head>
|
|
<body>
|
|
<p id="display"></p>
|
|
<div id="content" style="display: none">
|
|
|
|
<div id="testnode"></div>
|
|
|
|
</div>
|
|
<pre id="test">
|
|
<script class="testbody" type="text/javascript">
|
|
|
|
/** Test for parsing, storage, and serialization of CSS values **/
|
|
|
|
/*
|
|
* The idempotence tests here deserve a little bit of explanation. What
|
|
* we're testing here are the following operations:
|
|
* parse: string -> CSS rule
|
|
* serialize: CSS rule -> string (normalization 1)
|
|
* (this actually has two variants that go through partly different
|
|
* codepaths, which we exercise with getPropertyValue and cssText)
|
|
* compute: CSS rule -> computed style
|
|
* cserialize: computed style -> string (normalization 2)
|
|
*
|
|
* Both serialize and cserialize do some normalization, so we can't test
|
|
* for pure round-tripping, and we also can't compare their output since
|
|
* they could normalize differently. (We might at some point in the
|
|
* future want to guarantee that any output of cserialize is
|
|
* untouched by going through parse+serialize, though.)
|
|
*
|
|
* So we test idempotence of parse + serialize by running the whole
|
|
* operation twice. Likewise for parse + compute + cserialize.
|
|
*
|
|
* Slightly more interestingly, we test that serialize + parse is the
|
|
* identity transform by comparing the output of parse + compute +
|
|
* cserialize to the output of parse + serialize + parse + compute +
|
|
* cserialize.
|
|
*/
|
|
|
|
var gShorthandsWithoutCondensingSerialize = {
|
|
"-moz-border-radius": true,
|
|
"-moz-outline-radius": true,
|
|
"background": true, // really there, but not complete
|
|
"cue": true,
|
|
"font": true,
|
|
"list-style": true,
|
|
"outline": true,
|
|
"pause": true,
|
|
};
|
|
|
|
var gNotAccepted = {
|
|
"-moz-column-width": [ "50%" ],
|
|
"list-style": [ "none disc outside" ],
|
|
};
|
|
|
|
var gSystemFont = {
|
|
"caption": true,
|
|
"icon": true,
|
|
"menu": true,
|
|
"message-box": true,
|
|
"small-caption": true,
|
|
"status-bar": true,
|
|
};
|
|
|
|
var gBadCompute = {
|
|
// output wrapped around to positive, in exponential notation
|
|
"-moz-box-ordinal-group": [ "-1", "-1000" ],
|
|
};
|
|
|
|
var gShortenableValues = {
|
|
"border-color": [ "currentColor currentColor currentcolor CURRENTcolor" ],
|
|
"border-style": [ "none none none none", "groove none none none", "none none double none" ],
|
|
};
|
|
|
|
function xfail_accepted(property, value)
|
|
{
|
|
if (property in gNotAccepted &&
|
|
gNotAccepted[property].indexOf(value) != -1)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
function xfail_accepted_split(property, subprop, value)
|
|
{
|
|
if (property in gNotAccepted &&
|
|
gNotAccepted[property].indexOf(value) != -1)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
function xfail_ser_val(property, value)
|
|
{
|
|
if (property != "font" && xfail_accepted(property, value))
|
|
// We already failed the first test, which will make us always pass this
|
|
// one.
|
|
return false;
|
|
|
|
if (property in gShorthandsWithoutCondensingSerialize)
|
|
return true;
|
|
|
|
// We output unneeded -moz-use-text-color only in the value getter and
|
|
// not the serialization.
|
|
// XXXbz is there any way we could actually filter for that, so that colors
|
|
// other than green could be used in the property database here?
|
|
if ((property.match(/^border(|-bottom|-left|-right|-top)$/) ||
|
|
property.match(/^-moz-border(|-start|-end)$/)) &&
|
|
!value.match(/(green|currentcolor)/i))
|
|
return true;
|
|
|
|
// We condense multiple values in the serialization, but not in the
|
|
// value getter.
|
|
if (property.match(/^(border-(color|style|width)|margin|padding)$/) &&
|
|
value.split(" ").length != 4)
|
|
return true;
|
|
if (property in gShortenableValues &&
|
|
gShortenableValues[property].indexOf(value) != -1)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
function xfail_idparseser(property, value)
|
|
{
|
|
if (property != "font" && xfail_accepted(property, value))
|
|
// We already failed the first test, which will make us always pass this
|
|
// one.
|
|
return false;
|
|
|
|
return false;
|
|
}
|
|
|
|
function xfail_idserparse_compute(property, value)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
function xfail_idsersplitparse_compute(property, subprop, value, step1subcomp)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
function xfail_idparsesplitser(property, value)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
function xfail_compute(property, value)
|
|
{
|
|
if (property in gBadCompute &&
|
|
gBadCompute[property].indexOf(value) != -1)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
function xfail_split_compute(property, value)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var gElement = document.getElementById("testnode");
|
|
var gDeclaration = gElement.style;
|
|
var gComputedStyle = window.getComputedStyle(gElement, "");
|
|
|
|
function test_property(property)
|
|
{
|
|
var info = gCSSProperties[property];
|
|
|
|
var test_computed = !("backend_only" in info);
|
|
|
|
function test_value(value) {
|
|
gDeclaration.setProperty(property, value, "");
|
|
|
|
var idx, func;
|
|
|
|
var step1val = gDeclaration.getPropertyValue(property);
|
|
var step1vals = [];
|
|
var step1ser = gDeclaration.cssText;
|
|
if ("subproperties" in info)
|
|
for (idx in info.subproperties)
|
|
step1vals.push(gDeclaration.getPropertyValue(info.subproperties[idx]));
|
|
var step1comp;
|
|
var step1comps = [];
|
|
if (test_computed && info.type != CSS_TYPE_TRUE_SHORTHAND)
|
|
step1comp = gComputedStyle.getPropertyValue(property);
|
|
if (test_computed && "subproperties" in info)
|
|
for (idx in info.subproperties)
|
|
step1comps.push(gComputedStyle.getPropertyValue(info.subproperties[idx]));
|
|
|
|
func = xfail_accepted(property, value) ? todo_isnot : isnot;
|
|
func(step1val, "", "setting '" + value + "' on '" + property + "'");
|
|
if ("subproperties" in info)
|
|
for (idx in info.subproperties) {
|
|
var subprop = info.subproperties[idx];
|
|
func = xfail_accepted_split(property, subprop, value)
|
|
? todo_isnot : isnot;
|
|
func(gDeclaration.getPropertyValue(subprop), "",
|
|
"setting '" + value + "' on '" + property + "'");
|
|
}
|
|
|
|
// We don't care particularly about the whitespace or the placement of
|
|
// semicolons, but for simplicity we'll test the current behavior.
|
|
func = xfail_ser_val(property, value) ? todo_is : is;
|
|
var expected_serialization = "";
|
|
if (step1val != "")
|
|
expected_serialization = property + ": " + step1val + ";";
|
|
func(step1ser, expected_serialization,
|
|
"serialization should match property value");
|
|
|
|
gDeclaration.removeProperty(property);
|
|
gDeclaration.setProperty(property, step1val, "");
|
|
|
|
func = xfail_idparseser(property, value) ? todo_is : is;
|
|
func(gDeclaration.getPropertyValue(property), step1val,
|
|
"parse+serialize should be idempotent for '" +
|
|
property + ": " + value + "'");
|
|
if (test_computed && info.type != CSS_TYPE_TRUE_SHORTHAND) {
|
|
func = xfail_idserparse_compute(property, value) ? todo_is : is;
|
|
func(gComputedStyle.getPropertyValue(property), step1comp,
|
|
"serialize+parse should be identity transform for '" +
|
|
property + ": " + value + "'");
|
|
}
|
|
|
|
if ("subproperties" in info &&
|
|
// Using setProperty over subproperties is not sufficient for
|
|
// system fonts, since the shorthand does more than its parts.
|
|
(property != "font" || !(value in gSystemFont))) {
|
|
gDeclaration.removeProperty(property);
|
|
for (idx in info.subproperties) {
|
|
var subprop = info.subproperties[idx];
|
|
gDeclaration.setProperty(subprop, step1vals[idx], "");
|
|
}
|
|
|
|
// Now that all the subprops are set, check their values. Note that we
|
|
// need this in a separate loop, in case parts of the shorthand affect
|
|
// the computed values of other parts.
|
|
for (idx in info.subproperties) {
|
|
var subprop = info.subproperties[idx];
|
|
if (test_computed && !("backend_only" in gCSSProperties[subprop])) {
|
|
func =
|
|
xfail_idsersplitparse_compute(property, subprop, value, step1comps[idx])
|
|
? todo_is : is;
|
|
func(gComputedStyle.getPropertyValue(subprop), step1comps[idx],
|
|
"serialize(" + subprop + ")+parse should be the identity " +
|
|
"transform for '" + property + ": " + value + "'");
|
|
}
|
|
}
|
|
func = xfail_idparsesplitser(property, value) ? todo_is : is;
|
|
func(gDeclaration.getPropertyValue(property), step1val,
|
|
"parse+split+serialize should be idempotent for '" +
|
|
property + ": " + value + "'");
|
|
}
|
|
|
|
if (test_computed && info.type != CSS_TYPE_TRUE_SHORTHAND) {
|
|
gDeclaration.removeProperty(property);
|
|
gDeclaration.setProperty(property, step1comp, "");
|
|
func = xfail_compute(property, value) ? todo_is : is;
|
|
func(gComputedStyle.getPropertyValue(property), step1comp,
|
|
"parse+compute+serialize should be idempotent for '" +
|
|
property + ": " + value + "'");
|
|
}
|
|
if (test_computed && "subproperties" in info) {
|
|
gDeclaration.removeProperty(property);
|
|
for (idx in info.subproperties) {
|
|
var subprop = info.subproperties[idx];
|
|
if ("backend_only" in gCSSProperties[subprop])
|
|
continue;
|
|
gDeclaration.setProperty(subprop, step1comps[idx], "");
|
|
}
|
|
|
|
// Now that all the subprops are set, check their values. Note that we
|
|
// need this in a separate loop, in case parts of the shorthand affect
|
|
// the computed values of other parts.
|
|
func = xfail_split_compute(property, value) ? todo_is : is;
|
|
for (idx in info.subproperties) {
|
|
var subprop = info.subproperties[idx];
|
|
if ("backend_only" in gCSSProperties[subprop])
|
|
continue;
|
|
func(gComputedStyle.getPropertyValue(subprop), step1comps[idx],
|
|
"parse+compute+serialize(" + subprop + ") should be idempotent for '" +
|
|
property + ": " + value + "'");
|
|
}
|
|
}
|
|
|
|
gDeclaration.removeProperty(property);
|
|
}
|
|
|
|
var idx;
|
|
for (idx in info.initial_values)
|
|
test_value(info.initial_values[idx]);
|
|
for (idx in info.other_values)
|
|
test_value(info.other_values[idx]);
|
|
}
|
|
|
|
// To avoid triggering the slow script dialog, we have to test one
|
|
// property at a time.
|
|
SimpleTest.waitForExplicitFinish();
|
|
var props = [];
|
|
for (var prop in gCSSProperties)
|
|
props.push(prop);
|
|
props = props.reverse();
|
|
function do_one(l) {
|
|
if (l.length == 0) {
|
|
// SimpleTest.finish() is really slow, so we have to disable the
|
|
// slow script dialog for this part
|
|
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
|
var prefService = Components.classes["@mozilla.org/preferences-service;1"].
|
|
getService(Components.interfaces.nsIPrefService);
|
|
var domBranch = prefService.getBranch("dom.");
|
|
var oldVal = domBranch.getIntPref("max_script_run_time");
|
|
domBranch.setIntPref("max_script_run_time", 0);
|
|
|
|
SimpleTest.finish();
|
|
|
|
domBranch.setIntPref("max_script_run_time", oldVal);
|
|
|
|
return;
|
|
}
|
|
test_property(l.pop());
|
|
setTimeout(do_one, 0, l);
|
|
}
|
|
setTimeout(do_one, 0, props);
|
|
|
|
</script>
|
|
</pre>
|
|
</body>
|
|
</html>
|