6 <title>Test for parsing, storage, and serialization of CSS values
</title>
7 <script type=
"text/javascript" src=
"/MochiKit/MochiKit.js"></script>
8 <script type=
"text/javascript" src=
"/tests/SimpleTest/SimpleTest.js"></script>
9 <script type=
"text/javascript" src=
"property_database.js"></script>
10 <link rel=
"stylesheet" type=
"text/css" href=
"/tests/SimpleTest/test.css" />
14 <div id=
"content" style=
"display: none">
16 <div id=
"testnode"></div>
20 <script class=
"testbody" type=
"text/javascript">
22 /** Test for parsing, storage, and serialization of CSS values **/
25 * The idempotence tests here deserve a little bit of explanation. What
26 * we're testing here are the following operations:
27 * parse: string -
> CSS rule
28 * serialize: CSS rule -
> string (normalization
1)
29 * (this actually has two variants that go through partly different
30 * codepaths, which we exercise with getPropertyValue and cssText)
31 * compute: CSS rule -
> computed style
32 * cserialize: computed style -
> string (normalization
2)
34 * Both serialize and cserialize do some normalization, so we can't test
35 * for pure round-tripping, and we also can't compare their output since
36 * they could normalize differently. (We might at some point in the
37 * future want to guarantee that any output of cserialize is
38 * untouched by going through parse+serialize, though.)
40 * So we test idempotence of parse + serialize by running the whole
41 * operation twice. Likewise for parse + compute + cserialize.
43 * Slightly more interestingly, we test that serialize + parse is the
44 * identity transform by comparing the output of parse + compute +
45 * cserialize to the output of parse + serialize + parse + compute +
49 var gShorthandsWithoutCondensingSerialize = {
50 "-moz-border-radius": true,
51 "-moz-outline-radius": true,
52 "background": true, // really there, but not complete
61 "-moz-column-width": [
"50%" ],
62 "list-style": [
"none disc outside" ],
70 "small-caption": true,
75 // output wrapped around to positive, in exponential notation
76 "-moz-box-ordinal-group": [
"-1",
"-1000" ],
79 var gShortenableValues = {
80 "border-color": [
"currentColor currentColor currentcolor CURRENTcolor" ],
81 "border-style": [
"none none none none",
"groove none none none",
"none none double none" ],
84 function xfail_accepted(property, value)
86 if (property in gNotAccepted &&
87 gNotAccepted[property].indexOf(value) != -
1)
93 function xfail_accepted_split(property, subprop, value)
95 if (property in gNotAccepted &&
96 gNotAccepted[property].indexOf(value) != -
1)
102 function xfail_ser_val(property, value)
104 if (property !=
"font" && xfail_accepted(property, value))
105 // We already failed the first test, which will make us always pass this
109 if (property in gShorthandsWithoutCondensingSerialize)
112 // We output unneeded -moz-use-text-color only in the value getter and
113 // not the serialization.
114 // XXXbz is there any way we could actually filter for that, so that colors
115 // other than green could be used in the property database here?
116 if ((property.match(/^border(|-bottom|-left|-right|-top)$/) ||
117 property.match(/^-moz-border(|-start|-end)$/)) &&
118 !value.match(/(green|currentcolor)/i))
121 // We condense multiple values in the serialization, but not in the
123 if (property.match(/^(border-(color|style|width)|margin|padding)$/) &&
124 value.split(
" ").length !=
4)
126 if (property in gShortenableValues &&
127 gShortenableValues[property].indexOf(value) != -
1)
133 function xfail_idparseser(property, value)
135 if (property !=
"font" && xfail_accepted(property, value))
136 // We already failed the first test, which will make us always pass this
143 function xfail_idserparse_compute(property, value)
148 function xfail_idsersplitparse_compute(property, subprop, value, step1subcomp)
153 function xfail_idparsesplitser(property, value)
158 function xfail_compute(property, value)
160 if (property in gBadCompute &&
161 gBadCompute[property].indexOf(value) != -
1)
167 function xfail_split_compute(property, value)
172 var gElement = document.getElementById(
"testnode");
173 var gDeclaration = gElement.style;
174 var gComputedStyle = window.getComputedStyle(gElement,
"");
176 function test_property(property)
178 var info = gCSSProperties[property];
180 var test_computed = !(
"backend_only" in info);
182 function test_value(value) {
183 gDeclaration.setProperty(property, value,
"");
187 var step1val = gDeclaration.getPropertyValue(property);
189 var step1ser = gDeclaration.cssText;
190 if (
"subproperties" in info)
191 for (idx in info.subproperties)
192 step1vals.push(gDeclaration.getPropertyValue(info.subproperties[idx]));
195 if (test_computed && info.type != CSS_TYPE_TRUE_SHORTHAND)
196 step1comp = gComputedStyle.getPropertyValue(property);
197 if (test_computed &&
"subproperties" in info)
198 for (idx in info.subproperties)
199 step1comps.push(gComputedStyle.getPropertyValue(info.subproperties[idx]));
201 func = xfail_accepted(property, value) ? todo_isnot : isnot;
202 func(step1val,
"",
"setting '" + value +
"' on '" + property +
"'");
203 if (
"subproperties" in info)
204 for (idx in info.subproperties) {
205 var subprop = info.subproperties[idx];
206 func = xfail_accepted_split(property, subprop, value)
207 ? todo_isnot : isnot;
208 func(gDeclaration.getPropertyValue(subprop),
"",
209 "setting '" + value +
"' on '" + property +
"'");
212 // We don't care particularly about the whitespace or the placement of
213 // semicolons, but for simplicity we'll test the current behavior.
214 func = xfail_ser_val(property, value) ? todo_is : is;
215 var expected_serialization =
"";
217 expected_serialization = property +
": " + step1val +
";";
218 func(step1ser, expected_serialization,
219 "serialization should match property value");
221 gDeclaration.removeProperty(property);
222 gDeclaration.setProperty(property, step1val,
"");
224 func = xfail_idparseser(property, value) ? todo_is : is;
225 func(gDeclaration.getPropertyValue(property), step1val,
226 "parse+serialize should be idempotent for '" +
227 property +
": " + value +
"'");
228 if (test_computed && info.type != CSS_TYPE_TRUE_SHORTHAND) {
229 func = xfail_idserparse_compute(property, value) ? todo_is : is;
230 func(gComputedStyle.getPropertyValue(property), step1comp,
231 "serialize+parse should be identity transform for '" +
232 property +
": " + value +
"'");
235 if (
"subproperties" in info &&
236 // Using setProperty over subproperties is not sufficient for
237 // system fonts, since the shorthand does more than its parts.
238 (property !=
"font" || !(value in gSystemFont))) {
239 gDeclaration.removeProperty(property);
240 for (idx in info.subproperties) {
241 var subprop = info.subproperties[idx];
242 gDeclaration.setProperty(subprop, step1vals[idx],
"");
245 // Now that all the subprops are set, check their values. Note that we
246 // need this in a separate loop, in case parts of the shorthand affect
247 // the computed values of other parts.
248 for (idx in info.subproperties) {
249 var subprop = info.subproperties[idx];
250 if (test_computed && !(
"backend_only" in gCSSProperties[subprop])) {
252 xfail_idsersplitparse_compute(property, subprop, value, step1comps[idx])
254 func(gComputedStyle.getPropertyValue(subprop), step1comps[idx],
255 "serialize(" + subprop +
")+parse should be the identity " +
256 "transform for '" + property +
": " + value +
"'");
259 func = xfail_idparsesplitser(property, value) ? todo_is : is;
260 func(gDeclaration.getPropertyValue(property), step1val,
261 "parse+split+serialize should be idempotent for '" +
262 property +
": " + value +
"'");
265 if (test_computed && info.type != CSS_TYPE_TRUE_SHORTHAND) {
266 gDeclaration.removeProperty(property);
267 gDeclaration.setProperty(property, step1comp,
"");
268 func = xfail_compute(property, value) ? todo_is : is;
269 func(gComputedStyle.getPropertyValue(property), step1comp,
270 "parse+compute+serialize should be idempotent for '" +
271 property +
": " + value +
"'");
273 if (test_computed &&
"subproperties" in info) {
274 gDeclaration.removeProperty(property);
275 for (idx in info.subproperties) {
276 var subprop = info.subproperties[idx];
277 if (
"backend_only" in gCSSProperties[subprop])
279 gDeclaration.setProperty(subprop, step1comps[idx],
"");
282 // Now that all the subprops are set, check their values. Note that we
283 // need this in a separate loop, in case parts of the shorthand affect
284 // the computed values of other parts.
285 func = xfail_split_compute(property, value) ? todo_is : is;
286 for (idx in info.subproperties) {
287 var subprop = info.subproperties[idx];
288 if (
"backend_only" in gCSSProperties[subprop])
290 func(gComputedStyle.getPropertyValue(subprop), step1comps[idx],
291 "parse+compute+serialize(" + subprop +
") should be idempotent for '" +
292 property +
": " + value +
"'");
296 gDeclaration.removeProperty(property);
300 for (idx in info.initial_values)
301 test_value(info.initial_values[idx]);
302 for (idx in info.other_values)
303 test_value(info.other_values[idx]);
306 // To avoid triggering the slow script dialog, we have to test one
307 // property at a time.
308 SimpleTest.waitForExplicitFinish();
310 for (var prop in gCSSProperties)
312 props = props.reverse();
315 // SimpleTest.finish() is really slow, so we have to disable the
316 // slow script dialog for this part
317 netscape.security.PrivilegeManager.enablePrivilege(
"UniversalXPConnect");
318 var prefService = Components.classes[
"@mozilla.org/preferences-service;1"].
319 getService(Components.interfaces.nsIPrefService);
320 var domBranch = prefService.getBranch(
"dom.");
321 var oldVal = domBranch.getIntPref(
"max_script_run_time");
322 domBranch.setIntPref(
"max_script_run_time",
0);
326 domBranch.setIntPref(
"max_script_run_time", oldVal);
330 test_property(l.pop());
331 setTimeout(do_one,
0, l);
333 setTimeout(do_one,
0, props);