Bug 1914685 - [wpt-sync] Update web-platform-tests to 26c88095d89792c886494e30c85aca3...
[gecko.git] / layout / style / test / test_value_storage.html
blob542cad91ed42baf9e638a77a19a6a1e8924a54c5
1 <!DOCTYPE HTML>
2 <html>
3 <!--
4 -->
5 <head>
6 <title>Test for parsing, storage, and serialization of CSS values</title>
7 <script src="/tests/SimpleTest/SimpleTest.js"></script>
8 <script type="text/javascript" src="property_database.js"></script>
9 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
10 <style type="text/css" id="prereqsheet">
11 #testnode {}
12 </style>
13 </head>
14 <body>
15 <p id="display"></p>
16 <div id="content" style="display: none">
18 <div id="testnode"></div>
20 </div>
21 <pre id="test">
22 <script class="testbody" type="text/javascript">
24 /** Test for parsing, storage, and serialization of CSS values **/
27 * The idempotence tests here deserve a little bit of explanation. What
28 * we're testing here are the following operations:
29 * parse: string -> CSS rule
30 * serialize: CSS rule -> string (normalization 1)
31 * (this actually has two variants that go through partly different
32 * codepaths, which we exercise with getPropertyValue and cssText)
33 * compute: CSS rule -> computed style
34 * cserialize: computed style -> string (normalization 2)
36 * Both serialize and cserialize do some normalization, so we can't test
37 * for pure round-tripping, and we also can't compare their output since
38 * they could normalize differently. (We might at some point in the
39 * future want to guarantee that any output of cserialize is
40 * untouched by going through parse+serialize, though.)
42 * So we test idempotence of parse + serialize by running the whole
43 * operation twice. Likewise for parse + compute + cserialize.
45 * Slightly more interestingly, we test that serialize + parse is the
46 * identity transform by comparing the output of parse + compute +
47 * cserialize to the output of parse + serialize + parse + compute +
48 * cserialize.
51 var gSystemFont = [
52 "caption",
53 "icon",
54 "menu",
55 "message-box",
56 "small-caption",
57 "status-bar",
58 "-moz-button",
59 "-moz-pull-down-menu",
60 "-moz-list",
61 "-moz-field",
64 var gBadCompute = {
65 // output wrapped around to positive, in exponential notation
66 "-moz-box-ordinal-group": [ "-1", "-1000" ],
69 function xfail_compute(property, value)
71 if (property in gBadCompute &&
72 gBadCompute[property].includes(value))
73 return true;
75 return false;
78 // constructed to map longhands ==> list of containing shorthands
79 var gPropertyShorthands = {};
81 var gElement = document.getElementById("testnode");
82 var gDeclaration = gElement.style;
83 var gComputedStyle = window.getComputedStyle(gElement);
85 var gPrereqDeclaration =
86 document.getElementById("prereqsheet").sheet.cssRules[0].style;
88 // On Android, avoid most 'TEST-PASS' logging by overriding
89 // SimpleTest.is/isnot, to improve performance
90 if (navigator.appVersion.includes("Android")) {
91 is = function is(a, b, name)
93 var pass = Object.is(a, b);
94 if (!pass)
95 SimpleTest.is(a, b, name);
98 isnot = function isnot(a, b, name)
100 var pass = !Object.is(a, b);
101 if (!pass)
102 SimpleTest.isnot(a, b, name);
106 // Returns true if propA and propB are equivalent, considering aliasing.
107 // (i.e. if one is an alias of the other, or if they're both aliases of
108 // the same 3rd property)
109 function are_properties_aliased(propA, propB)
111 // If either property is an alias, replace it with the property it aliases.
112 if ("alias_for" in gCSSProperties[propA]) {
113 propA = gCSSProperties[propA].alias_for;
115 if ("alias_for" in gCSSProperties[propB]) {
116 propB = gCSSProperties[propB].alias_for;
119 return propA == propB;
122 function test_property(property)
124 var info = gCSSProperties[property];
126 // can all properties be removed from the style?
127 function test_remove_all_properties(propName, value) {
128 var i, p = [];
129 for (i = 0; i < gDeclaration.length; i++) p.push(gDeclaration[i]);
130 for (i = 0; i < p.length; i++) gDeclaration.removeProperty(p[i]);
131 var errstr = "when setting property " + propName + " to " + value;
132 is(gDeclaration.length, 0, "unremovable properties " + errstr);
133 is(gDeclaration.cssText, "", "non-empty serialization after removing all properties " + errstr);
136 function test_other_shorthands_empty(value, subprop) {
137 if (!(subprop in gPropertyShorthands)) return;
138 var shorthands = gPropertyShorthands[subprop];
139 for (idx in shorthands) {
140 var sh = shorthands[idx];
141 if (are_properties_aliased(sh, property)) {
142 continue;
144 is(gDeclaration.getPropertyValue(sh), "",
145 "setting '" + value + "' on '" + property + "' (for shorthand '" + sh + "')");
149 function test_value(value, resolved_value) {
150 var value_has_variable_reference = resolved_value != null;
151 var is_system_font = property == "font" && gSystemFont.includes(value);
153 var colon = ": ";
154 gDeclaration.setProperty(property, value, "");
156 var idx;
158 var step1val = gDeclaration.getPropertyValue(property);
159 var step1vals = [];
160 var step1ser = gDeclaration.cssText;
161 if ("subproperties" in info)
162 for (idx in info.subproperties)
163 step1vals.push(gDeclaration.getPropertyValue(info.subproperties[idx]));
164 var step1comp;
165 var step1comps = [];
166 if (info.type != CSS_TYPE_TRUE_SHORTHAND)
167 step1comp = gComputedStyle.getPropertyValue(property);
168 if ("subproperties" in info)
169 for (idx in info.subproperties)
170 step1comps.push(gComputedStyle.getPropertyValue(info.subproperties[idx]));
172 SimpleTest.isnot(step1val, "", "setting '" + value + "' on '" + property + "'");
173 if ("subproperties" in info &&
174 // System font doesn't produce meaningful value for subproperties.
175 !is_system_font)
176 for (idx in info.subproperties) {
177 var subprop = info.subproperties[idx];
178 if (value_has_variable_reference &&
179 (!info.alias_for || info.type == CSS_TYPE_TRUE_SHORTHAND ||
180 info.type == CSS_TYPE_LEGACY_SHORTHAND)) {
181 is(gDeclaration.getPropertyValue(subprop), "",
182 "setting '" + value + "' on '" + property + "' (for '" + subprop + "')");
183 test_other_shorthands_empty(value, subprop);
184 } else {
185 isnot(gDeclaration.getPropertyValue(subprop), "",
186 "setting '" + value + "' on '" + property + "' (for '" + subprop + "')");
190 // We don't care particularly about the whitespace or the placement of
191 // semicolons, but for simplicity we'll test the current behavior.
192 var expected_serialization = "";
193 if (step1val != "") {
194 if ("alias_for" in info) {
195 let newValue = info.legacy_mapping && info.legacy_mapping[step1val]
196 ? info.legacy_mapping[step1val] : step1val;
197 // FIXME(emilio): This is a bit unfortunate:
198 // https://github.com/w3c/csswg-drafts/issues/3332
199 if (info.type == CSS_TYPE_LEGACY_SHORTHAND && value_has_variable_reference)
200 newValue = "";
201 expected_serialization = info.alias_for + colon + newValue + ";";
202 } else if (info.type == CSS_TYPE_LEGACY_SHORTHAND) {
203 is(property, "zoom", "Zoom is a bit special because it never " +
204 "serializes as-is, we always serialize the longhands, " +
205 "but it doesn't just map to a single property " +
206 "(and thus we can't use the 'alias_for' mechanism)");
207 let transform = step1val == "1" ? "none" : "scale(" + step1val + ")";
208 let origin = step1val == "1" ? "50% 50% 0px" : "0px 0px 0px";
209 if (value_has_variable_reference) { // See above.
210 transform = "";
211 origin = "";
213 expected_serialization = "transform" + colon + transform + "; transform-origin" + colon + origin + ";";
214 } else {
215 expected_serialization = property + colon + step1val + ";";
218 is(step1ser, expected_serialization,
219 "serialization should match property value");
221 gDeclaration.removeProperty(property);
222 gDeclaration.setProperty(property, step1val, "");
224 is(gDeclaration.getPropertyValue(property), step1val,
225 "parse+serialize should be idempotent for '" +
226 property + colon + value + "'");
227 if (info.type != CSS_TYPE_TRUE_SHORTHAND) {
228 is(gComputedStyle.getPropertyValue(property), step1comp,
229 "serialize+parse should be identity transform for '" +
230 property + ": " + value + "'");
233 if ("subproperties" in info &&
234 // Using setProperty over subproperties is not sufficient for
235 // system fonts, since the shorthand does more than its parts.
236 !is_system_font &&
237 !value_has_variable_reference) {
238 gDeclaration.removeProperty(property);
239 for (idx in info.subproperties) {
240 var subprop = info.subproperties[idx];
241 gDeclaration.setProperty(subprop, step1vals[idx], "");
244 // Now that all the subprops are set, check their values. Note that we
245 // need this in a separate loop, in case parts of the shorthand affect
246 // the computed values of other parts.
247 for (idx in info.subproperties) {
248 var subprop = info.subproperties[idx];
249 is(gComputedStyle.getPropertyValue(subprop), step1comps[idx],
250 "serialize(" + subprop + ")+parse should be the identity " +
251 "transform for '" + property + ": " + value + "'");
253 is(gDeclaration.getPropertyValue(property), step1val,
254 "parse+split+serialize should be idempotent for '" +
255 property + colon + value + "'");
258 // FIXME(emilio): Why is mask special?
259 if (info.type != CSS_TYPE_TRUE_SHORTHAND &&
260 property != "mask") {
261 gDeclaration.removeProperty(property);
262 gDeclaration.setProperty(property, step1comp, "");
263 var func = xfail_compute(property, value) ? todo_is : is;
264 func(gComputedStyle.getPropertyValue(property), step1comp,
265 "parse+compute+serialize should be idempotent for '" +
266 property + ": " + value + "'");
268 if ("subproperties" in info && !is_system_font) {
269 gDeclaration.removeProperty(property);
270 for (idx in info.subproperties) {
271 var subprop = info.subproperties[idx];
272 gDeclaration.setProperty(subprop, step1comps[idx], "");
275 // Now that all the subprops are set, check their values. Note that we
276 // need this in a separate loop, in case parts of the shorthand affect
277 // the computed values of other parts.
278 for (idx in info.subproperties) {
279 var subprop = info.subproperties[idx];
280 is(gComputedStyle.getPropertyValue(subprop), step1comps[idx],
281 "parse+compute+serialize(" + subprop + ") should be idempotent for '" +
282 property + ": " + value + "'");
286 // sanity check shorthands to make sure disabled props aren't exposed
287 if (info.type != CSS_TYPE_LONGHAND) {
288 gDeclaration.setProperty(property, value, "");
289 test_remove_all_properties(property, value);
292 gDeclaration.removeProperty(property);
295 function test_value_without_variable(value) {
296 test_value(value, null);
299 function test_value_with_variable(value) {
300 gPrereqDeclaration.setProperty("--a", value, "");
301 test_value("var(--a)", value);
302 gPrereqDeclaration.removeProperty("--a");
305 if ("prerequisites" in info) {
306 var prereqs = info.prerequisites;
307 for (var prereq in prereqs) {
308 gPrereqDeclaration.setProperty(prereq, prereqs[prereq], "");
312 var idx;
313 for (idx in info.initial_values) {
314 test_value_without_variable(info.initial_values[idx]);
315 test_value_with_variable(info.initial_values[idx]);
317 for (idx in info.other_values) {
318 test_value_without_variable(info.other_values[idx]);
319 test_value_with_variable(info.other_values[idx]);
322 if ("prerequisites" in info) {
323 for (var prereq in info.prerequisites) {
324 gPrereqDeclaration.removeProperty(prereq);
330 function runTest() {
331 // To avoid triggering the slow script dialog, we have to test one
332 // property at a time.
333 var props = [];
334 for (var prop in gCSSProperties) {
335 var info = gCSSProperties[prop];
336 if ("subproperties" in info) {
337 for (var idx in info.subproperties) {
338 var subprop = info.subproperties[idx];
339 if (!(subprop in gPropertyShorthands)) {
340 gPropertyShorthands[subprop] = [];
342 gPropertyShorthands[subprop].push(prop);
345 props.push(prop);
347 props = props.reverse();
348 function do_one() {
349 if (props.length == 0) {
350 SimpleTest.finish();
351 return;
353 test_property(props.pop());
354 SimpleTest.executeSoon(do_one);
356 SimpleTest.executeSoon(do_one);
359 SimpleTest.waitForExplicitFinish();
360 SimpleTest.requestLongerTimeout(7);
361 runTest();
362 </script>
363 </pre>
364 </body>
365 </html>