summaryrefslogtreecommitdiffstats
path: root/layout/style/test/test_value_storage.html
diff options
context:
space:
mode:
Diffstat (limited to 'layout/style/test/test_value_storage.html')
-rw-r--r--layout/style/test/test_value_storage.html365
1 files changed, 365 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 0000000000..542cad91ed
--- /dev/null
+++ b/layout/style/test/test_value_storage.html
@@ -0,0 +1,365 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+-->
+<head>
+ <title>Test for parsing, storage, and serialization of CSS values</title>
+ <script 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",
+ "icon",
+ "menu",
+ "message-box",
+ "small-caption",
+ "status-bar",
+ "-moz-button",
+ "-moz-pull-down-menu",
+ "-moz-list",
+ "-moz-field",
+];
+
+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].includes(value))
+ 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.includes("Android")) {
+ 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(propName, 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 " + propName + " 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;
+ var is_system_font = property == "font" && gSystemFont.includes(value);
+
+ var colon = ": ";
+ 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 &&
+ // System font doesn't produce meaningful value for subproperties.
+ !is_system_font)
+ for (idx in info.subproperties) {
+ var subprop = info.subproperties[idx];
+ if (value_has_variable_reference &&
+ (!info.alias_for || info.type == CSS_TYPE_TRUE_SHORTHAND ||
+ info.type == CSS_TYPE_LEGACY_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) {
+ let newValue = info.legacy_mapping && info.legacy_mapping[step1val]
+ ? info.legacy_mapping[step1val] : step1val;
+ // FIXME(emilio): This is a bit unfortunate:
+ // https://github.com/w3c/csswg-drafts/issues/3332
+ if (info.type == CSS_TYPE_LEGACY_SHORTHAND && value_has_variable_reference)
+ newValue = "";
+ expected_serialization = info.alias_for + colon + newValue + ";";
+ } else if (info.type == CSS_TYPE_LEGACY_SHORTHAND) {
+ is(property, "zoom", "Zoom is a bit special because it never " +
+ "serializes as-is, we always serialize the longhands, " +
+ "but it doesn't just map to a single property " +
+ "(and thus we can't use the 'alias_for' mechanism)");
+ let transform = step1val == "1" ? "none" : "scale(" + step1val + ")";
+ let origin = step1val == "1" ? "50% 50% 0px" : "0px 0px 0px";
+ if (value_has_variable_reference) { // See above.
+ transform = "";
+ origin = "";
+ }
+ expected_serialization = "transform" + colon + transform + "; transform-origin" + colon + origin + ";";
+ } else {
+ expected_serialization = property + colon + 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 + colon + 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.
+ !is_system_font &&
+ !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 + colon + value + "'");
+ }
+
+ // FIXME(emilio): Why is mask special?
+ 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 && !is_system_font) {
+ 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.
+ 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);
+runTest();
+</script>
+</pre>
+</body>
+</html>