diff options
Diffstat (limited to 'layout/style/test/test_value_storage.html')
-rw-r--r-- | layout/style/test/test_value_storage.html | 352 |
1 files changed, 352 insertions, 0 deletions
diff --git a/layout/style/test/test_value_storage.html b/layout/style/test/test_value_storage.html new file mode 100644 index 000000000..5e7fa6b69 --- /dev/null +++ b/layout/style/test/test_value_storage.html @@ -0,0 +1,352 @@ +<!DOCTYPE HTML> +<html> +<!-- +--> +<head> + <title>Test for parsing, storage, and serialization of CSS values</title> + <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" /> + <style type="text/css" id="prereqsheet"> + #testnode {} + </style> +</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 gSystemFont = { + "caption": true, + "icon": true, + "menu": true, + "message-box": true, + "small-caption": true, + "status-bar": true, + "-moz-window": true, + "-moz-document": true, + "-moz-desktop": true, + "-moz-info": true, + "-moz-dialog": true, + "-moz-button": true, + "-moz-pull-down-menu": true, + "-moz-list": true, + "-moz-field": true, + "-moz-workspace": true, +}; + +var gBadCompute = { + // output wrapped around to positive, in exponential notation + "-moz-box-ordinal-group": [ "-1", "-1000" ], +}; + +function xfail_compute(property, value) +{ + if (property in gBadCompute && + gBadCompute[property].indexOf(value) != -1) + return true; + + return false; +} + +// constructed to map longhands ==> list of containing shorthands +var gPropertyShorthands = {}; + +var gElement = document.getElementById("testnode"); +var gDeclaration = gElement.style; +var gComputedStyle = window.getComputedStyle(gElement, ""); + +var gPrereqDeclaration = + document.getElementById("prereqsheet").sheet.cssRules[0].style; + +// On Android, avoid most 'TEST-PASS' logging by overriding +// SimpleTest.is/isnot, to improve performance +if (navigator.appVersion.indexOf("Android") >= 0) { + is = function is(a, b, name) + { + var pass = Object.is(a, b); + if (!pass) + SimpleTest.is(a, b, name); + } + + isnot = function isnot(a, b, name) + { + var pass = !Object.is(a, b); + if (!pass) + SimpleTest.isnot(a, b, name); + } +} + +// Returns true if propA and propB are equivalent, considering aliasing. +// (i.e. if one is an alias of the other, or if they're both aliases of +// the same 3rd property) +function are_properties_aliased(propA, propB) +{ + // If either property is an alias, replace it with the property it aliases. + if ("alias_for" in gCSSProperties[propA]) { + propA = gCSSProperties[propA].alias_for; + } + if ("alias_for" in gCSSProperties[propB]) { + propB = gCSSProperties[propB].alias_for; + } + + return propA == propB; +} + +function test_property(property) +{ + var info = gCSSProperties[property]; + + // can all properties be removed from the style? + function test_remove_all_properties(property, value) { + var i, p = []; + for (i = 0; i < gDeclaration.length; i++) p.push(gDeclaration[i]); + for (i = 0; i < p.length; i++) gDeclaration.removeProperty(p[i]); + var errstr = "when setting property " + property + " to " + value; + is(gDeclaration.length, 0, "unremovable properties " + errstr); + is(gDeclaration.cssText, "", "non-empty serialization after removing all properties " + errstr); + } + + function test_other_shorthands_empty(value, subprop) { + if (!(subprop in gPropertyShorthands)) return; + var shorthands = gPropertyShorthands[subprop]; + for (idx in shorthands) { + var sh = shorthands[idx]; + if (are_properties_aliased(sh, property)) { + continue; + } + is(gDeclaration.getPropertyValue(sh), "", + "setting '" + value + "' on '" + property + "' (for shorthand '" + sh + "')"); + } + } + + function test_value(value, resolved_value) { + var value_has_variable_reference = resolved_value != null; + + gDeclaration.setProperty(property, value, ""); + + var idx; + + 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 (info.type != CSS_TYPE_TRUE_SHORTHAND) + step1comp = gComputedStyle.getPropertyValue(property); + if ("subproperties" in info) + for (idx in info.subproperties) + step1comps.push(gComputedStyle.getPropertyValue(info.subproperties[idx])); + + SimpleTest.isnot(step1val, "", "setting '" + value + "' on '" + property + "'"); + if ("subproperties" in info) + for (idx in info.subproperties) { + var subprop = info.subproperties[idx]; + if (value_has_variable_reference && + (!info.alias_for || info.type == CSS_TYPE_TRUE_SHORTHAND)) { + is(gDeclaration.getPropertyValue(subprop), "", + "setting '" + value + "' on '" + property + "' (for '" + subprop + "')"); + test_other_shorthands_empty(value, subprop); + } else { + isnot(gDeclaration.getPropertyValue(subprop), "", + "setting '" + value + "' on '" + property + "' (for '" + subprop + "')"); + } + } + + // We don't care particularly about the whitespace or the placement of + // semicolons, but for simplicity we'll test the current behavior. + var expected_serialization = ""; + if (step1val != "") { + if ("alias_for" in info) { + expected_serialization = info.alias_for + ": " + step1val + ";"; + } else { + expected_serialization = property + ": " + step1val + ";"; + } + } + is(step1ser, expected_serialization, + "serialization should match property value"); + + gDeclaration.removeProperty(property); + gDeclaration.setProperty(property, step1val, ""); + + is(gDeclaration.getPropertyValue(property), step1val, + "parse+serialize should be idempotent for '" + + property + ": " + value + "'"); + if (info.type != CSS_TYPE_TRUE_SHORTHAND) { + is(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)) && + // Likewise for special compatibility values of transform + (property != "-moz-transform" || !value.match(/^matrix.*(px|em|%)/)) && + !value_has_variable_reference) { + 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]; + is(gComputedStyle.getPropertyValue(subprop), step1comps[idx], + "serialize(" + subprop + ")+parse should be the identity " + + "transform for '" + property + ": " + value + "'"); + } + is(gDeclaration.getPropertyValue(property), step1val, + "parse+split+serialize should be idempotent for '" + + property + ": " + value + "'"); + } + + if (info.type != CSS_TYPE_TRUE_SHORTHAND && + property != "mask") { + gDeclaration.removeProperty(property); + gDeclaration.setProperty(property, step1comp, ""); + var func = xfail_compute(property, value) ? todo_is : is; + func(gComputedStyle.getPropertyValue(property), step1comp, + "parse+compute+serialize should be idempotent for '" + + property + ": " + value + "'"); + } + if ("subproperties" in info) { + gDeclaration.removeProperty(property); + for (idx in info.subproperties) { + var subprop = info.subproperties[idx]; + 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. + for (idx in info.subproperties) { + var subprop = info.subproperties[idx]; + is(gComputedStyle.getPropertyValue(subprop), step1comps[idx], + "parse+compute+serialize(" + subprop + ") should be idempotent for '" + + property + ": " + value + "'"); + } + } + + // sanity check shorthands to make sure disabled props aren't exposed + if (info.type != CSS_TYPE_LONGHAND) { + gDeclaration.setProperty(property, value, ""); + test_remove_all_properties(property, value); + } + + gDeclaration.removeProperty(property); + } + + function test_value_without_variable(value) { + test_value(value, null); + } + + function test_value_with_variable(value) { + gPrereqDeclaration.setProperty("--a", value, ""); + test_value("var(--a)", value); + gPrereqDeclaration.removeProperty("--a"); + } + + if ("prerequisites" in info) { + var prereqs = info.prerequisites; + for (var prereq in prereqs) { + gPrereqDeclaration.setProperty(prereq, prereqs[prereq], ""); + } + } + + var idx; + for (idx in info.initial_values) { + test_value_without_variable(info.initial_values[idx]); + test_value_with_variable(info.initial_values[idx]); + } + for (idx in info.other_values) { + test_value_without_variable(info.other_values[idx]); + test_value_with_variable(info.other_values[idx]); + } + + if ("prerequisites" in info) { + for (var prereq in info.prerequisites) { + gPrereqDeclaration.removeProperty(prereq); + } + } + +} + +function runTest() { + // To avoid triggering the slow script dialog, we have to test one + // property at a time. + ok(SpecialPowers.getBoolPref("layout.css.variables.enabled"), "pref not set #1"); + var props = []; + for (var prop in gCSSProperties) { + var info = gCSSProperties[prop]; + if ("subproperties" in info) { + for (var idx in info.subproperties) { + var subprop = info.subproperties[idx]; + if (!(subprop in gPropertyShorthands)) { + gPropertyShorthands[subprop] = []; + } + gPropertyShorthands[subprop].push(prop); + } + } + props.push(prop); + } + props = props.reverse(); + function do_one() { + if (props.length == 0) { + SimpleTest.finish(); + return; + } + test_property(props.pop()); + SimpleTest.executeSoon(do_one); + } + SimpleTest.executeSoon(do_one); +} + +SimpleTest.waitForExplicitFinish(); +SimpleTest.requestLongerTimeout(7); + +SpecialPowers.pushPrefEnv({ set: [["layout.css.variables.enabled", true]] }, + runTest); +</script> +</pre> +</body> +</html> |