Wed, 31 Dec 2014 13:27:57 +0100
Ignore runtime configuration files generated during quality assurance.
1 <!DOCTYPE HTML>
2 <html>
3 <!--
4 -->
5 <head>
6 <title>Test for parsing, storage, and serialization of CSS values</title>
7 <script type="text/javascript" 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 **/
26 /*
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)
35 *
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.)
41 *
42 * So we test idempotence of parse + serialize by running the whole
43 * operation twice. Likewise for parse + compute + cserialize.
44 *
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.
49 */
51 var gSystemFont = {
52 "caption": true,
53 "icon": true,
54 "menu": true,
55 "message-box": true,
56 "small-caption": true,
57 "status-bar": true,
58 "-moz-window": true,
59 "-moz-document": true,
60 "-moz-desktop": true,
61 "-moz-info": true,
62 "-moz-dialog": true,
63 "-moz-button": true,
64 "-moz-pull-down-menu": true,
65 "-moz-list": true,
66 "-moz-field": true,
67 "-moz-workspace": true,
68 };
70 var gBadCompute = {
71 // output wrapped around to positive, in exponential notation
72 "-moz-box-ordinal-group": [ "-1", "-1000" ],
73 };
75 function xfail_compute(property, value)
76 {
77 if (property in gBadCompute &&
78 gBadCompute[property].indexOf(value) != -1)
79 return true;
81 return false;
82 }
84 var gBadSerialize = {
85 // Font-families which include quotes don't get those escaped when
86 //serializing: see bug 660397
87 "font-family": ["\\\"Times New Roman", "Times, \\\"Times New Roman"],
88 };
90 function xfail_serialize(property, value)
91 {
92 if (property in gBadSerialize &&
93 gBadSerialize[property].indexOf(value) != -1)
94 return true;
96 return false;
97 }
99 var gElement = document.getElementById("testnode");
100 var gDeclaration = gElement.style;
101 var gComputedStyle = window.getComputedStyle(gElement, "");
103 var gPrereqDeclaration =
104 document.getElementById("prereqsheet").sheet.cssRules[0].style;
106 function test_property(property)
107 {
108 ok(SpecialPowers.getBoolPref("layout.css.variables.enabled"), "pref not set #2");
109 var info = gCSSProperties[property];
111 var test_computed = !("backend_only" in info);
113 // can all properties be removed from the style?
114 function test_remove_all_properties(property, value) {
115 var i, p = [];
116 for (i = 0; i < gDeclaration.length; i++) p.push(gDeclaration[i]);
117 for (i = 0; i < p.length; i++) gDeclaration.removeProperty(p[i]);
118 var errstr = "when setting property " + property + " to " + value;
119 is(gDeclaration.length, 0, "unremovable properties " + errstr);
120 is(gDeclaration.cssText, "", "non-empty serialization after removing all properties " + errstr);
121 }
123 function test_value(value, resolved_value) {
124 var value_has_variable_reference = resolved_value != null;
126 gDeclaration.setProperty(property, value, "");
128 var idx;
130 var step1val = gDeclaration.getPropertyValue(property);
131 var step1vals = [];
132 var step1ser = gDeclaration.cssText;
133 if ("subproperties" in info)
134 for (idx in info.subproperties)
135 step1vals.push(gDeclaration.getPropertyValue(info.subproperties[idx]));
136 var step1comp;
137 var step1comps = [];
138 if (test_computed && info.type != CSS_TYPE_TRUE_SHORTHAND)
139 step1comp = gComputedStyle.getPropertyValue(property);
140 if (test_computed && "subproperties" in info)
141 for (idx in info.subproperties)
142 step1comps.push(gComputedStyle.getPropertyValue(info.subproperties[idx]));
144 isnot(step1val, "", "setting '" + value + "' on '" + property + "'");
145 if ("subproperties" in info)
146 for (idx in info.subproperties) {
147 var subprop = info.subproperties[idx];
148 if (value_has_variable_reference &&
149 (!info.alias_for || info.type == CSS_TYPE_TRUE_SHORTHAND)) {
150 is(gDeclaration.getPropertyValue(subprop), "",
151 "setting '" + value + "' on '" + property + "' (for '" + subprop + "')");
152 } else {
153 isnot(gDeclaration.getPropertyValue(subprop), "",
154 "setting '" + value + "' on '" + property + "' (for '" + subprop + "')");
155 }
156 }
158 // We don't care particularly about the whitespace or the placement of
159 // semicolons, but for simplicity we'll test the current behavior.
160 var expected_serialization = "";
161 if (step1val != "") {
162 if ("alias_for" in info) {
163 expected_serialization = info.alias_for + ": " + step1val + ";";
164 } else {
165 expected_serialization = property + ": " + step1val + ";";
166 }
167 }
168 is(step1ser, expected_serialization,
169 "serialization should match property value");
171 gDeclaration.removeProperty(property);
172 gDeclaration.setProperty(property, step1val, "");
174 var serialize_func = xfail_serialize(property, value) &&
175 !value_has_variable_reference ? todo_is : is;
177 serialize_func(gDeclaration.getPropertyValue(property), step1val,
178 "parse+serialize should be idempotent for '" +
179 property + ": " + value + "'");
180 if (test_computed && info.type != CSS_TYPE_TRUE_SHORTHAND) {
181 serialize_func(gComputedStyle.getPropertyValue(property), step1comp,
182 "serialize+parse should be identity transform for '" +
183 property + ": " + value + "'");
184 }
186 if ("subproperties" in info &&
187 // Using setProperty over subproperties is not sufficient for
188 // system fonts, since the shorthand does more than its parts.
189 (property != "font" || !(value in gSystemFont)) &&
190 // Likewise for special compatibility values of transform
191 (property != "-moz-transform" || !value.match(/^matrix.*(px|em|%)/)) &&
192 !value_has_variable_reference) {
193 gDeclaration.removeProperty(property);
194 for (idx in info.subproperties) {
195 var subprop = info.subproperties[idx];
196 gDeclaration.setProperty(subprop, step1vals[idx], "");
197 }
199 // Now that all the subprops are set, check their values. Note that we
200 // need this in a separate loop, in case parts of the shorthand affect
201 // the computed values of other parts.
202 for (idx in info.subproperties) {
203 var subprop = info.subproperties[idx];
204 if (test_computed && !("backend_only" in gCSSProperties[subprop])) {
205 is(gComputedStyle.getPropertyValue(subprop), step1comps[idx],
206 "serialize(" + subprop + ")+parse should be the identity " +
207 "transform for '" + property + ": " + value + "'");
208 }
209 }
210 is(gDeclaration.getPropertyValue(property), step1val,
211 "parse+split+serialize should be idempotent for '" +
212 property + ": " + value + "'");
213 }
215 if (test_computed && info.type != CSS_TYPE_TRUE_SHORTHAND) {
216 gDeclaration.removeProperty(property);
217 gDeclaration.setProperty(property, step1comp, "");
218 var func = (xfail_compute(property, value) ||
219 xfail_serialize(property, resolved_value || value)) ? todo_is : is;
220 func(gComputedStyle.getPropertyValue(property), step1comp,
221 "parse+compute+serialize should be idempotent for '" +
222 property + ": " + value + "'");
223 }
224 if (test_computed && "subproperties" in info) {
225 gDeclaration.removeProperty(property);
226 for (idx in info.subproperties) {
227 var subprop = info.subproperties[idx];
228 if ("backend_only" in gCSSProperties[subprop])
229 continue;
230 gDeclaration.setProperty(subprop, step1comps[idx], "");
231 }
233 // Now that all the subprops are set, check their values. Note that we
234 // need this in a separate loop, in case parts of the shorthand affect
235 // the computed values of other parts.
236 for (idx in info.subproperties) {
237 var subprop = info.subproperties[idx];
238 if ("backend_only" in gCSSProperties[subprop])
239 continue;
240 is(gComputedStyle.getPropertyValue(subprop), step1comps[idx],
241 "parse+compute+serialize(" + subprop + ") should be idempotent for '" +
242 property + ": " + value + "'");
243 }
244 }
246 // sanity check shorthands to make sure disabled props aren't exposed
247 if (info.type != CSS_TYPE_LONGHAND) {
248 gDeclaration.setProperty(property, value, "");
249 test_remove_all_properties(property, value);
250 }
252 gDeclaration.removeProperty(property);
253 }
255 function test_value_without_variable(value) {
256 test_value(value, null);
257 }
259 function test_value_with_variable(value) {
260 gPrereqDeclaration.setProperty("--a", value, "");
261 test_value("var(--a)", value);
262 gPrereqDeclaration.removeProperty("--a");
263 }
265 if ("prerequisites" in info) {
266 var prereqs = info.prerequisites;
267 for (var prereq in prereqs) {
268 gPrereqDeclaration.setProperty(prereq, prereqs[prereq], "");
269 }
270 }
272 var idx;
273 for (idx in info.initial_values) {
274 test_value_without_variable(info.initial_values[idx]);
275 test_value_with_variable(info.initial_values[idx]);
276 }
277 for (idx in info.other_values) {
278 test_value_without_variable(info.other_values[idx]);
279 test_value_with_variable(info.other_values[idx]);
280 }
282 if ("prerequisites" in info) {
283 for (var prereq in info.prerequisites) {
284 gPrereqDeclaration.removeProperty(prereq);
285 }
286 }
288 }
290 function runTest() {
291 // To avoid triggering the slow script dialog, we have to test one
292 // property at a time.
293 ok(SpecialPowers.getBoolPref("layout.css.variables.enabled"), "pref not set #1");
294 var props = [];
295 for (var prop in gCSSProperties)
296 props.push(prop);
297 props = props.reverse();
298 function do_one() {
299 if (props.length == 0) {
300 SimpleTest.finish();
301 return;
302 }
303 test_property(props.pop());
304 SimpleTest.executeSoon(do_one);
305 }
306 SimpleTest.executeSoon(do_one);
307 }
309 SimpleTest.waitForExplicitFinish();
310 SimpleTest.requestLongerTimeout(2);
312 SpecialPowers.pushPrefEnv({ set: [["layout.css.variables.enabled", true]] },
313 runTest);
314 </script>
315 </pre>
316 </body>
317 </html>