layout/style/test/test_media_queries.html

Wed, 31 Dec 2014 07:16:47 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:16:47 +0100
branch
TOR_BUG_9701
changeset 3
141e0f1194b1
permissions
-rw-r--r--

Revert simplistic fix pending revisit of Mozilla integration attempt.

michael@0 1 <!DOCTYPE HTML>
michael@0 2 <html>
michael@0 3 <!--
michael@0 4 https://bugzilla.mozilla.org/show_bug.cgi?id=156716
michael@0 5 -->
michael@0 6 <head>
michael@0 7 <title>Test for Bug 156716</title>
michael@0 8 <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
michael@0 9 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
michael@0 10 </head>
michael@0 11 <body onload="run()">
michael@0 12 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=156716">Mozilla Bug 156716</a>
michael@0 13 <iframe id="subdoc" src="media_queries_iframe.html"></iframe>
michael@0 14 <div id="content" style="display: none">
michael@0 15
michael@0 16 </div>
michael@0 17 <pre id="test">
michael@0 18 <script class="testbody" type="application/javascript">
michael@0 19
michael@0 20 /** Test for Bug 156716 **/
michael@0 21
michael@0 22 // Note that many other tests are in test_acid3_test46.html .
michael@0 23
michael@0 24 SimpleTest.waitForExplicitFinish();
michael@0 25
michael@0 26 var iframe;
michael@0 27
michael@0 28 function getScreenPixelsPerCSSPixel() {
michael@0 29 return SpecialPowers.DOMWindowUtils.screenPixelsPerCSSPixel;
michael@0 30 }
michael@0 31
michael@0 32 function run() {
michael@0 33 iframe = document.getElementById("subdoc");
michael@0 34 var subdoc = iframe.contentDocument;
michael@0 35 var subwin = iframe.contentWindow;
michael@0 36 var style = subdoc.getElementById("style");
michael@0 37 var iframe_style = iframe.style;
michael@0 38 var body_cs = subdoc.defaultView.getComputedStyle(subdoc.body, "");
michael@0 39
michael@0 40 function query_applies(q) {
michael@0 41 style.setAttribute("media", q);
michael@0 42 return body_cs.getPropertyValue("text-decoration") == "underline";
michael@0 43 }
michael@0 44
michael@0 45 function should_apply(q) {
michael@0 46 ok(query_applies(q), q + " should apply");
michael@0 47 test_serialization(q, true, true);
michael@0 48 }
michael@0 49
michael@0 50 function should_not_apply(q) {
michael@0 51 ok(!query_applies(q), q + " should not apply");
michael@0 52 test_serialization(q, true, false);
michael@0 53 }
michael@0 54
michael@0 55 /* for queries that are parseable standalone but not within CSS */
michael@0 56 function should_apply_unbalanced(q) {
michael@0 57 ok(query_applies(q), q + " should apply");
michael@0 58 }
michael@0 59
michael@0 60 /* for queries that are parseable standalone but not within CSS */
michael@0 61 function should_not_apply_unbalanced(q) {
michael@0 62 ok(!query_applies(q), q + " should not apply");
michael@0 63 }
michael@0 64
michael@0 65 /*
michael@0 66 * Functions to test whether a query is parseable at all. (Should not
michael@0 67 * be used for parse errors within expressions.)
michael@0 68 */
michael@0 69 var parse_test_style_element = document.createElement("style");
michael@0 70 parse_test_style_element.type = "text/css";
michael@0 71 parse_test_style_element.disabled = true; // for performance, hopefully
michael@0 72 var parse_test_style_text = document.createTextNode("");
michael@0 73 parse_test_style_element.appendChild(parse_test_style_text);
michael@0 74 document.getElementsByTagName("head")[0]
michael@0 75 .appendChild(parse_test_style_element);
michael@0 76
michael@0 77 function query_is_parseable(q) {
michael@0 78 parse_test_style_text.data = "@media screen, " + q + " {}";
michael@0 79 var sheet = parse_test_style_element.sheet; // XXX yikes, not live!
michael@0 80 if (sheet.cssRules.length == 1 &&
michael@0 81 sheet.cssRules[0].type == CSSRule.MEDIA_RULE)
michael@0 82 return sheet.cssRules[0].media.mediaText != "screen, not all";
michael@0 83 ok(false, "unexpected result testing whether query " + q +
michael@0 84 " is parseable");
michael@0 85 return true; // doesn't matter, we already failed
michael@0 86 }
michael@0 87
michael@0 88 function query_should_be_parseable(q) {
michael@0 89 ok(query_is_parseable(q), "query " + q + " should be parseable");
michael@0 90 test_serialization(q, false, false);
michael@0 91 }
michael@0 92
michael@0 93 function query_should_not_be_parseable(q) {
michael@0 94 ok(!query_is_parseable(q), "query " + q + " should not be parseable");
michael@0 95 }
michael@0 96
michael@0 97 /*
michael@0 98 * Functions to test whether a single media expression is parseable.
michael@0 99 */
michael@0 100 function expression_is_parseable(e) {
michael@0 101 style.setAttribute("media", "all and (" + e + ")");
michael@0 102 return style.sheet.media.mediaText != "not all";
michael@0 103 }
michael@0 104
michael@0 105 function expression_should_be_parseable(e) {
michael@0 106 ok(expression_is_parseable(e),
michael@0 107 "expression " + e + " should be parseable");
michael@0 108 test_serialization("all and (" + e + ")", false, false);
michael@0 109 }
michael@0 110
michael@0 111 function expression_should_not_be_parseable(e) {
michael@0 112 ok(!expression_is_parseable(e),
michael@0 113 "expression " + e + " should not be parseable");
michael@0 114 }
michael@0 115
michael@0 116 function test_serialization(q, test_application, should_apply) {
michael@0 117 style.setAttribute("media", q);
michael@0 118 var ser1 = style.sheet.media.mediaText;
michael@0 119 isnot(ser1, "", "serialization of '" + q + "' should not be empty");
michael@0 120 style.setAttribute("media", ser1);
michael@0 121 var ser2 = style.sheet.media.mediaText;
michael@0 122 is(ser2, ser1, "parse+serialize of '" + q + "' should be idempotent");
michael@0 123 if (test_application) {
michael@0 124 var applies = body_cs.getPropertyValue("text-decoration") == "underline";
michael@0 125 is(applies, should_apply,
michael@0 126 "Media query '" + q + "' should " + (should_apply ? "" : "NOT ") +
michael@0 127 "apply after serialize + reparse");
michael@0 128 }
michael@0 129
michael@0 130 // Test cloning
michael@0 131 var sheet = "@media " + q + " { body { text-decoration: underline } }"
michael@0 132 var sheeturl = "data:text/css," + escape(sheet);
michael@0 133 var link = "<link rel='stylesheet' href='" + sheeturl + "'>";
michael@0 134 var htmldoc = "<!DOCTYPE HTML>" + link + link + "<body>";
michael@0 135 var docurl = "data:text/html," + escape(htmldoc);
michael@0 136 post_clone_test(docurl, function() {
michael@0 137 var clonedoc = iframe.contentDocument;
michael@0 138 var clonewin = iframe.contentWindow;
michael@0 139 var links = clonedoc.getElementsByTagName("link");
michael@0 140 // cause a clone
michael@0 141 var clonedsheet = links[1].sheet;
michael@0 142 clonedsheet.insertRule("#nonexistent { color: purple}", 1);
michael@0 143 // remove the uncloned sheet
michael@0 144 links[0].parentNode.removeChild(links[0]);
michael@0 145
michael@0 146 var ser3 = clonedsheet.cssRules[0].media.mediaText;
michael@0 147 is(ser3, ser1, "cloning query '" + q + "' should not change " +
michael@0 148 "serialization");
michael@0 149 if (test_application) {
michael@0 150 var applies = clonewin.getComputedStyle(clonedoc.body, "").
michael@0 151 textDecoration == "underline";
michael@0 152 is(applies, should_apply,
michael@0 153 "Media query '" + q + "' should " + (should_apply ? "" : "NOT ") +
michael@0 154 "apply after cloning");
michael@0 155 }
michael@0 156 });
michael@0 157 }
michael@0 158
michael@0 159 // The no-type syntax doesn't mix with the not and only keywords.
michael@0 160 query_should_be_parseable("(orientation)");
michael@0 161 query_should_not_be_parseable("not (orientation)");
michael@0 162 query_should_not_be_parseable("only (orientation)");
michael@0 163 query_should_be_parseable("all and (orientation)");
michael@0 164 query_should_be_parseable("not all and (orientation)");
michael@0 165 query_should_be_parseable("only all and (orientation)");
michael@0 166
michael@0 167 query_should_be_parseable("(-moz-device-orientation)");
michael@0 168 query_should_not_be_parseable("not (-moz-device-orientation)");
michael@0 169 query_should_not_be_parseable("only (-moz-device-orientation)");
michael@0 170 query_should_be_parseable("all and (-moz-device-orientation)");
michael@0 171 query_should_be_parseable("not all and (-moz-device-orientation)");
michael@0 172 query_should_be_parseable("only all and (-moz-device-orientation)");
michael@0 173
michael@0 174 // Test that the 'not', 'only', 'and', and 'or' keywords are not
michael@0 175 // allowed as media types.
michael@0 176 query_should_not_be_parseable("not");
michael@0 177 query_should_not_be_parseable("and");
michael@0 178 query_should_not_be_parseable("or");
michael@0 179 query_should_not_be_parseable("only");
michael@0 180 query_should_be_parseable("unknowntype");
michael@0 181 query_should_not_be_parseable("not not");
michael@0 182 query_should_not_be_parseable("not and");
michael@0 183 query_should_not_be_parseable("not or");
michael@0 184 query_should_not_be_parseable("not only");
michael@0 185 query_should_be_parseable("not unknowntype");
michael@0 186 query_should_not_be_parseable("only not");
michael@0 187 query_should_not_be_parseable("only and");
michael@0 188 query_should_not_be_parseable("only or");
michael@0 189 query_should_not_be_parseable("only only");
michael@0 190 query_should_be_parseable("only unknowntype");
michael@0 191 query_should_not_be_parseable("not and (width)");
michael@0 192 query_should_not_be_parseable("and and (width)");
michael@0 193 query_should_not_be_parseable("or and (width)");
michael@0 194 query_should_not_be_parseable("only and (width)");
michael@0 195 query_should_be_parseable("unknowntype and (width)");
michael@0 196 query_should_not_be_parseable("not not and (width)");
michael@0 197 query_should_not_be_parseable("not and and (width)");
michael@0 198 query_should_not_be_parseable("not or and (width)");
michael@0 199 query_should_not_be_parseable("not only and (width)");
michael@0 200 query_should_be_parseable("not unknowntype and (width)");
michael@0 201 query_should_not_be_parseable("only not and (width)");
michael@0 202 query_should_not_be_parseable("only and and (width)");
michael@0 203 query_should_not_be_parseable("only or and (width)");
michael@0 204 query_should_not_be_parseable("only only and (width)");
michael@0 205 query_should_be_parseable("only unknowntype and (width)");
michael@0 206
michael@0 207 var features = [ "width", "height", "device-width", "device-height" ];
michael@0 208 var feature;
michael@0 209 var i;
michael@0 210 for (i in features) {
michael@0 211 feature = features[i];
michael@0 212 expression_should_be_parseable(feature);
michael@0 213 expression_should_be_parseable(feature + ": 0");
michael@0 214 expression_should_be_parseable(feature + ": 0px");
michael@0 215 expression_should_be_parseable(feature + ": 0em");
michael@0 216 expression_should_be_parseable(feature + ": -0");
michael@0 217 expression_should_be_parseable("min-" + feature + ": -0");
michael@0 218 expression_should_be_parseable("max-" + feature + ": -0");
michael@0 219 expression_should_be_parseable(feature + ": -0cm");
michael@0 220 expression_should_be_parseable(feature + ": 1px");
michael@0 221 expression_should_be_parseable(feature + ": 0.001mm");
michael@0 222 expression_should_be_parseable(feature + ": 100000px");
michael@0 223 expression_should_not_be_parseable(feature + ": -1px");
michael@0 224 expression_should_not_be_parseable("min-" + feature + ": -1px");
michael@0 225 expression_should_not_be_parseable("max-" + feature + ": -1px");
michael@0 226 expression_should_not_be_parseable(feature + ": -0.00001mm");
michael@0 227 expression_should_not_be_parseable(feature + ": -100000em");
michael@0 228 expression_should_not_be_parseable("min-" + feature);
michael@0 229 expression_should_not_be_parseable("max-" + feature);
michael@0 230 }
michael@0 231
michael@0 232 var content_div = document.getElementById("content");
michael@0 233 content_div.style.font = "initial";
michael@0 234 var em_size =
michael@0 235 getComputedStyle(content_div, "").fontSize.match(/^(\d+)px$/)[1];
michael@0 236
michael@0 237 // in this test, assume the common underlying implementation is correct
michael@0 238 var width_val = 117; // pick two not-too-round numbers
michael@0 239 var height_val = 76;
michael@0 240 change_state(function() {
michael@0 241 iframe_style.width = width_val + "px";
michael@0 242 iframe_style.height = height_val + "px";
michael@0 243 });
michael@0 244 var device_width = window.screen.width;
michael@0 245 var device_height = window.screen.height;
michael@0 246 features = { "width": width_val,
michael@0 247 "height": height_val,
michael@0 248 "device-width": device_width,
michael@0 249 "device-height": device_height };
michael@0 250 for (feature in features) {
michael@0 251 var value = features[feature];
michael@0 252 should_apply("all and (" + feature + ": " + value + "px)");
michael@0 253 should_not_apply("all and (" + feature + ": " + (value + 1) + "px)");
michael@0 254 should_not_apply("all and (" + feature + ": " + (value - 1) + "px)");
michael@0 255 should_apply("all and (min-" + feature + ": " + value + "px)");
michael@0 256 should_not_apply("all and (min-" + feature + ": " + (value + 1) + "px)");
michael@0 257 should_apply("all and (min-" + feature + ": " + (value - 1) + "px)");
michael@0 258 should_apply("all and (max-" + feature + ": " + value + "px)");
michael@0 259 should_apply("all and (max-" + feature + ": " + (value + 1) + "px)");
michael@0 260 should_not_apply("all and (max-" + feature + ": " + (value - 1) + "px)");
michael@0 261 should_not_apply("all and (min-" + feature + ": " +
michael@0 262 (Math.ceil(value/em_size) + 1) + "em)");
michael@0 263 should_apply("all and (min-" + feature + ": " +
michael@0 264 (Math.floor(value/em_size) - 1) + "em)");
michael@0 265 should_apply("all and (max-" + feature + ": " +
michael@0 266 (Math.ceil(value/em_size) + 1) + "em)");
michael@0 267 should_not_apply("all and (max-" + feature + ": " +
michael@0 268 (Math.floor(value/em_size) - 1) + "em)");
michael@0 269 should_not_apply("all and (min-" + feature + ": " +
michael@0 270 (Math.ceil(value/em_size) + 1) + "rem)");
michael@0 271 should_apply("all and (min-" + feature + ": " +
michael@0 272 (Math.floor(value/em_size) - 1) + "rem)");
michael@0 273 should_apply("all and (max-" + feature + ": " +
michael@0 274 (Math.ceil(value/em_size) + 1) + "rem)");
michael@0 275 should_not_apply("all and (max-" + feature + ": " +
michael@0 276 (Math.floor(value/em_size) - 1) + "rem)");
michael@0 277 }
michael@0 278
michael@0 279 change_state(function() {
michael@0 280 iframe_style.width = "0";
michael@0 281 });
michael@0 282 should_apply("all and (height)");
michael@0 283 should_not_apply("all and (width)");
michael@0 284 change_state(function() {
michael@0 285 iframe_style.height = "0";
michael@0 286 });
michael@0 287 should_not_apply("all and (height)");
michael@0 288 should_not_apply("all and (width)");
michael@0 289 should_apply("all and (device-height)");
michael@0 290 should_apply("all and (device-width)");
michael@0 291 change_state(function() {
michael@0 292 iframe_style.width = width_val + "px";
michael@0 293 });
michael@0 294 should_not_apply("all and (height)");
michael@0 295 should_apply("all and (width)");
michael@0 296 change_state(function() {
michael@0 297 iframe_style.height = height_val + "px";
michael@0 298 });
michael@0 299 should_apply("all and (height)");
michael@0 300 should_apply("all and (width)");
michael@0 301
michael@0 302 // ratio that reduces to 59/40
michael@0 303 change_state(function() {
michael@0 304 iframe_style.width = "236px";
michael@0 305 iframe_style.height = "160px";
michael@0 306 });
michael@0 307 expression_should_be_parseable("orientation");
michael@0 308 expression_should_be_parseable("orientation: portrait");
michael@0 309 expression_should_be_parseable("orientation: landscape");
michael@0 310 expression_should_not_be_parseable("min-orientation");
michael@0 311 expression_should_not_be_parseable("min-orientation: portrait");
michael@0 312 expression_should_not_be_parseable("min-orientation: landscape");
michael@0 313 expression_should_not_be_parseable("max-orientation");
michael@0 314 expression_should_not_be_parseable("max-orientation: portrait");
michael@0 315 expression_should_not_be_parseable("max-orientation: landscape");
michael@0 316 should_apply("(orientation)");
michael@0 317 should_apply("(orientation: landscape)");
michael@0 318 should_not_apply("(orientation: portrait)");
michael@0 319 should_apply("not all and (orientation: portrait)");
michael@0 320 // ratio that reduces to 59/80
michael@0 321 change_state(function() {
michael@0 322 iframe_style.height = "320px";
michael@0 323 });
michael@0 324 should_apply("(orientation)");
michael@0 325 should_not_apply("(orientation: landscape)");
michael@0 326 should_apply("not all and (orientation: landscape)");
michael@0 327 should_apply("(orientation: portrait)");
michael@0 328
michael@0 329 expression_should_be_parseable("-moz-device-orientation");
michael@0 330 expression_should_be_parseable("-moz-device-orientation: portrait");
michael@0 331 expression_should_be_parseable("-moz-device-orientation: landscape");
michael@0 332 expression_should_not_be_parseable("min--moz-device-orientation");
michael@0 333 expression_should_not_be_parseable("min--moz-device-orientation: portrait");
michael@0 334 expression_should_not_be_parseable("min--moz-device-orientation: landscape");
michael@0 335 expression_should_not_be_parseable("max--moz-device-orientation");
michael@0 336 expression_should_not_be_parseable("max--moz-device-orientation: portrait");
michael@0 337 expression_should_not_be_parseable("max--moz-device-orientation: landscape");
michael@0 338
michael@0 339 // determine the actual configuration of the screen and test against it
michael@0 340 var device_orientation = (device_width > device_height) ? "landscape" : "portrait";
michael@0 341 var not_device_orientation = (device_orientation == "landscape") ? "portrait" : "landscape";
michael@0 342 should_apply("(-moz-device-orientation)");
michael@0 343 should_apply("(-moz-device-orientation: " + device_orientation + ")");
michael@0 344 should_not_apply("(-moz-device-orientation: " + not_device_orientation + ")");
michael@0 345 should_apply("not all and (-moz-device-orientation: " + not_device_orientation + ")");
michael@0 346
michael@0 347 should_apply("(aspect-ratio: 59/80)");
michael@0 348 should_not_apply("(aspect-ratio: 58/80)");
michael@0 349 should_not_apply("(aspect-ratio: 59/81)");
michael@0 350 should_not_apply("(aspect-ratio: 60/80)");
michael@0 351 should_not_apply("(aspect-ratio: 59/79)");
michael@0 352 should_apply("(aspect-ratio: 177/240)");
michael@0 353 should_apply("(aspect-ratio: 413/560)");
michael@0 354 should_apply("(aspect-ratio: 5900/8000)");
michael@0 355 should_not_apply("(aspect-ratio: 5901/8000)");
michael@0 356 should_not_apply("(aspect-ratio: 5899/8000)");
michael@0 357 should_not_apply("(aspect-ratio: 5900/8001)");
michael@0 358 should_not_apply("(aspect-ratio: 5900/7999)");
michael@0 359 should_apply("(aspect-ratio)");
michael@0 360
michael@0 361 should_apply("(min-aspect-ratio: 59/80)");
michael@0 362 should_apply("(min-aspect-ratio: 58/80)");
michael@0 363 should_apply("(min-aspect-ratio: 59/81)");
michael@0 364 should_not_apply("(min-aspect-ratio: 60/80)");
michael@0 365 should_not_apply("(min-aspect-ratio: 59/79)");
michael@0 366 expression_should_not_be_parseable("min-aspect-ratio");
michael@0 367
michael@0 368 should_apply("(max-aspect-ratio: 59/80)");
michael@0 369 should_not_apply("(max-aspect-ratio: 58/80)");
michael@0 370 should_not_apply("(max-aspect-ratio: 59/81)");
michael@0 371 should_apply("(max-aspect-ratio: 60/80)");
michael@0 372 should_apply("(max-aspect-ratio: 59/79)");
michael@0 373 expression_should_not_be_parseable("max-aspect-ratio");
michael@0 374
michael@0 375 var real_dar = device_width + "/" + device_height;
michael@0 376 var high_dar_1 = (device_width + 1) + "/" + device_height;
michael@0 377 var high_dar_2 = device_width + "/" + (device_height - 1);
michael@0 378 var low_dar_1 = (device_width - 1) + "/" + device_height;
michael@0 379 var low_dar_2 = device_width + "/" + (device_height + 1);
michael@0 380 should_apply("(device-aspect-ratio: " + real_dar + ")");
michael@0 381 should_apply("not all and (device-aspect-ratio: " + high_dar_1 + ")");
michael@0 382 should_not_apply("all and (device-aspect-ratio: " + high_dar_2 + ")");
michael@0 383 should_not_apply("all and (device-aspect-ratio: " + low_dar_1 + ")");
michael@0 384 should_apply("not all and (device-aspect-ratio: " + low_dar_2 + ")");
michael@0 385 should_apply("(device-aspect-ratio)");
michael@0 386
michael@0 387 should_apply("(min-device-aspect-ratio: " + real_dar + ")");
michael@0 388 should_not_apply("all and (min-device-aspect-ratio: " + high_dar_1 + ")");
michael@0 389 should_apply("not all and (min-device-aspect-ratio: " + high_dar_2 + ")");
michael@0 390 should_not_apply("not all and (min-device-aspect-ratio: " + low_dar_1 + ")");
michael@0 391 should_apply("all and (min-device-aspect-ratio: " + low_dar_2 + ")");
michael@0 392 expression_should_not_be_parseable("min-device-aspect-ratio");
michael@0 393
michael@0 394 should_apply("all and (max-device-aspect-ratio: " + real_dar + ")");
michael@0 395 should_apply("(max-device-aspect-ratio: " + high_dar_1 + ")");
michael@0 396 should_apply("(max-device-aspect-ratio: " + high_dar_2 + ")");
michael@0 397 should_not_apply("all and (max-device-aspect-ratio: " + low_dar_1 + ")");
michael@0 398 should_apply("not all and (max-device-aspect-ratio: " + low_dar_2 + ")");
michael@0 399 expression_should_not_be_parseable("max-device-aspect-ratio");
michael@0 400
michael@0 401 var real_dpr = 1.0 * getScreenPixelsPerCSSPixel();
michael@0 402 var high_dpr = 1.1 * getScreenPixelsPerCSSPixel();
michael@0 403 var low_dpr = 0.9 * getScreenPixelsPerCSSPixel();
michael@0 404 should_apply("all and (max--moz-device-pixel-ratio: " + real_dpr + ")");
michael@0 405 should_apply("all and (min--moz-device-pixel-ratio: " + real_dpr + ")");
michael@0 406 should_not_apply("not all and (max--moz-device-pixel-ratio: " + real_dpr + ")");
michael@0 407 should_not_apply("not all and (min--moz-device-pixel-ratio: " + real_dpr + ")");
michael@0 408 should_apply("all and (min--moz-device-pixel-ratio: " + low_dpr + ")");
michael@0 409 should_apply("all and (max--moz-device-pixel-ratio: " + high_dpr + ")");
michael@0 410 should_not_apply("all and (max--moz-device-pixel-ratio: " + low_dpr + ")");
michael@0 411 should_not_apply("all and (min--moz-device-pixel-ratio: " + high_dpr + ")");
michael@0 412 should_apply("not all and (max--moz-device-pixel-ratio: " + low_dpr + ")");
michael@0 413 should_apply("not all and (min--moz-device-pixel-ratio: " + high_dpr + ")");
michael@0 414 should_apply("(-moz-device-pixel-ratio: " + real_dpr + ")");
michael@0 415 should_not_apply("(-moz-device-pixel-ratio: " + high_dpr + ")");
michael@0 416 should_not_apply("(-moz-device-pixel-ratio: " + low_dpr + ")");
michael@0 417 should_apply("(-moz-device-pixel-ratio)");
michael@0 418 expression_should_not_be_parseable("min--moz-device-pixel-ratio");
michael@0 419 expression_should_not_be_parseable("max--moz-device-pixel-ratio");
michael@0 420
michael@0 421 features = [ "max-aspect-ratio", "device-aspect-ratio" ];
michael@0 422 for (i in features) {
michael@0 423 feature = features[i];
michael@0 424 expression_should_be_parseable(feature + ": 1/1");
michael@0 425 expression_should_be_parseable(feature + ": 1 /1");
michael@0 426 expression_should_be_parseable(feature + ": 1 / \t\n1");
michael@0 427 expression_should_be_parseable(feature + ": 1/\r1");
michael@0 428 expression_should_not_be_parseable(feature + ": 1");
michael@0 429 expression_should_not_be_parseable(feature + ": 0.5");
michael@0 430 expression_should_not_be_parseable(feature + ": 1.0/1");
michael@0 431 expression_should_not_be_parseable(feature + ": 1/1.0");
michael@0 432 expression_should_not_be_parseable(feature + ": 1.0/1.0");
michael@0 433 expression_should_not_be_parseable(feature + ": 0/1");
michael@0 434 expression_should_not_be_parseable(feature + ": 1/0");
michael@0 435 expression_should_not_be_parseable(feature + ": 0/0");
michael@0 436 expression_should_not_be_parseable(feature + ": -1/1");
michael@0 437 expression_should_not_be_parseable(feature + ": 1/-1");
michael@0 438 expression_should_not_be_parseable(feature + ": -1/-1");
michael@0 439 }
michael@0 440
michael@0 441 var is_monochrome = query_applies("all and (min-monochrome: 1)");
michael@0 442 test_serialization("all and (min-monochrome: 1)", true, is_monochrome);
michael@0 443 var is_color = query_applies("all and (min-color: 1)");
michael@0 444 test_serialization("all and (min-color: 1)", true, is_color);
michael@0 445 isnot(is_monochrome, is_color, "should be either monochrome or color");
michael@0 446
michael@0 447 function depth_query(prefix, depth) {
michael@0 448 return "all and (" + prefix + (is_color ? "color" : "monochrome") +
michael@0 449 ":" + depth + ")";
michael@0 450 }
michael@0 451
michael@0 452 var depth = 0;
michael@0 453 do {
michael@0 454 if (depth > 50) {
michael@0 455 ok(false, "breaking from loop, depth > 50");
michael@0 456 break;
michael@0 457 }
michael@0 458 } while (query_applies(depth_query("min-", ++depth)));
michael@0 459 --depth;
michael@0 460
michael@0 461 should_apply(depth_query("", depth));
michael@0 462 should_not_apply(depth_query("", depth - 1));
michael@0 463 should_not_apply(depth_query("", depth + 1));
michael@0 464 should_apply(depth_query("max-", depth));
michael@0 465 should_not_apply(depth_query("max-", depth - 1));
michael@0 466 should_apply(depth_query("max-", depth + 1));
michael@0 467
michael@0 468 (is_color ? should_apply : should_not_apply)("all and (color)");
michael@0 469 expression_should_not_be_parseable("max-color");
michael@0 470 expression_should_not_be_parseable("min-color");
michael@0 471 (is_color ? should_not_apply : should_apply)("all and (monochrome)");
michael@0 472 expression_should_not_be_parseable("max-monochrome");
michael@0 473 expression_should_not_be_parseable("min-monochrome");
michael@0 474 (is_color ? should_apply : should_not_apply)("not all and (monochrome)");
michael@0 475 (is_color ? should_not_apply : should_apply)("not all and (color)");
michael@0 476 (is_color ? should_apply : should_not_apply)("only all and (color)");
michael@0 477 (is_color ? should_not_apply : should_apply)("only all and (monochrome)");
michael@0 478
michael@0 479 features = [ "color", "min-monochrome", "max-color-index" ];
michael@0 480 for (i in features) {
michael@0 481 feature = features[i];
michael@0 482 expression_should_be_parseable(feature + ": 1");
michael@0 483 expression_should_be_parseable(feature + ": 327");
michael@0 484 expression_should_be_parseable(feature + ": 0");
michael@0 485 expression_should_not_be_parseable(feature + ": 1.0");
michael@0 486 expression_should_not_be_parseable(feature + ": -1");
michael@0 487 expression_should_not_be_parseable(feature + ": 1/1");
michael@0 488 }
michael@0 489
michael@0 490 // Presume that we never support indexed color (at least not usefully
michael@0 491 // enough to call it indexed color).
michael@0 492 should_apply("(color-index: 0)");
michael@0 493 should_not_apply("(color-index: 1)");
michael@0 494 should_apply("(min-color-index: 0)");
michael@0 495 should_not_apply("(min-color-index: 1)");
michael@0 496 should_apply("(max-color-index: 0)");
michael@0 497 should_apply("(max-color-index: 1)");
michael@0 498 should_apply("(max-color-index: 157)");
michael@0 499
michael@0 500 features = [ "resolution", "min-resolution", "max-resolution" ];
michael@0 501 for (i in features) {
michael@0 502 feature = features[i];
michael@0 503 expression_should_be_parseable(feature + ": 3dpi");
michael@0 504 expression_should_be_parseable(feature + ":3dpi");
michael@0 505 expression_should_be_parseable(feature + ": 3.0dpi");
michael@0 506 expression_should_be_parseable(feature + ": 3.4dpi");
michael@0 507 expression_should_be_parseable(feature + "\t: 120dpcm");
michael@0 508 expression_should_be_parseable(feature + ": 1dppx");
michael@0 509 expression_should_be_parseable(feature + ": 1.5dppx");
michael@0 510 expression_should_be_parseable(feature + ": 2.0dppx");
michael@0 511 expression_should_not_be_parseable(feature + ": 0dpi");
michael@0 512 expression_should_not_be_parseable(feature + ": -3dpi");
michael@0 513 expression_should_not_be_parseable(feature + ": 0dppx");
michael@0 514 }
michael@0 515
michael@0 516 // Find the resolution using max-resolution
michael@0 517 var resolution = 0;
michael@0 518 do {
michael@0 519 ++resolution;
michael@0 520 if (resolution > 10000) {
michael@0 521 ok(false, "resolution greater than 10000dpi???");
michael@0 522 break;
michael@0 523 }
michael@0 524 } while (!query_applies("(max-resolution: " + resolution + "dpi)"));
michael@0 525
michael@0 526 // resolution should now be Math.ceil() of the actual resolution.
michael@0 527 var dpi_high;
michael@0 528 var dpi_low = resolution - 1;
michael@0 529 if (query_applies("(min-resolution: " + resolution + "dpi)")) {
michael@0 530 // It's exact!
michael@0 531 should_apply("(resolution: " + resolution + "dpi)");
michael@0 532 should_apply("(resolution: " + Math.floor(resolution/96) + "dppx)");
michael@0 533 should_not_apply("(resolution: " + (resolution + 1) + "dpi)");
michael@0 534 should_not_apply("(resolution: " + (resolution - 1) + "dpi)");
michael@0 535 dpi_high = resolution + 1;
michael@0 536 } else {
michael@0 537 // We have no way to test resolution applying since it need not be
michael@0 538 // an integer.
michael@0 539 should_not_apply("(resolution: " + resolution + "dpi)");
michael@0 540 should_not_apply("(resolution: " + (resolution - 1) + "dpi)");
michael@0 541 dpi_high = resolution;
michael@0 542 }
michael@0 543
michael@0 544 should_apply("(min-resolution: " + dpi_low + "dpi)");
michael@0 545 should_not_apply("not all and (min-resolution: " + dpi_low + "dpi)");
michael@0 546 should_apply("not all and (min-resolution: " + dpi_high + "dpi)");
michael@0 547 should_not_apply("all and (min-resolution: " + dpi_high + "dpi)");
michael@0 548
michael@0 549 // Test dpcm units based on what we computed in dpi.
michael@0 550 var dpcm_high = Math.ceil(dpi_high / 2.54);
michael@0 551 var dpcm_low = Math.floor(dpi_low / 2.54);
michael@0 552 should_apply("(min-resolution: " + dpcm_low + "dpcm)");
michael@0 553 should_apply("(max-resolution: " + dpcm_high + "dpcm)");
michael@0 554 should_not_apply("(max-resolution: " + dpcm_low + "dpcm)");
michael@0 555 should_apply("not all and (min-resolution: " + dpcm_high + "dpcm)");
michael@0 556
michael@0 557 expression_should_be_parseable("scan");
michael@0 558 expression_should_be_parseable("scan: progressive");
michael@0 559 expression_should_be_parseable("scan:interlace");
michael@0 560 expression_should_not_be_parseable("min-scan:interlace");
michael@0 561 expression_should_not_be_parseable("scan: 1");
michael@0 562 expression_should_not_be_parseable("max-scan");
michael@0 563 expression_should_not_be_parseable("max-scan: progressive");
michael@0 564 // Assume we don't support tv devices.
michael@0 565 should_not_apply("(scan)");
michael@0 566 should_not_apply("(scan: progressive)");
michael@0 567 should_not_apply("(scan: interlace)");
michael@0 568 should_apply("not all and (scan)");
michael@0 569 should_apply("not all and (scan: progressive)");
michael@0 570 should_apply("not all and (scan: interlace)");
michael@0 571
michael@0 572 expression_should_be_parseable("grid");
michael@0 573 expression_should_be_parseable("grid: 0");
michael@0 574 expression_should_be_parseable("grid: 1");
michael@0 575 expression_should_be_parseable("grid: 1");
michael@0 576 expression_should_not_be_parseable("min-grid");
michael@0 577 expression_should_not_be_parseable("min-grid:0");
michael@0 578 expression_should_not_be_parseable("max-grid: 1");
michael@0 579 expression_should_not_be_parseable("grid: 2");
michael@0 580 expression_should_not_be_parseable("grid: -1");
michael@0 581
michael@0 582 // Assume we don't support grid devices
michael@0 583 should_not_apply("(grid)");
michael@0 584 should_apply("(grid: 0)");
michael@0 585 should_not_apply("(grid: 1)");
michael@0 586 should_not_apply("(grid: 2)");
michael@0 587 should_not_apply("(grid: -1)");
michael@0 588
michael@0 589 // System metrics
michael@0 590 expression_should_be_parseable("-moz-scrollbar-start-backward");
michael@0 591 expression_should_be_parseable("-moz-scrollbar-start-forward");
michael@0 592 expression_should_be_parseable("-moz-scrollbar-end-backward");
michael@0 593 expression_should_be_parseable("-moz-scrollbar-end-forward");
michael@0 594 expression_should_be_parseable("-moz-scrollbar-thumb-proportional");
michael@0 595 expression_should_be_parseable("-moz-images-in-menus");
michael@0 596 expression_should_be_parseable("-moz-images-in-buttons");
michael@0 597 expression_should_be_parseable("-moz-overlay-scrollbars");
michael@0 598 expression_should_be_parseable("-moz-windows-default-theme");
michael@0 599 expression_should_be_parseable("-moz-mac-graphite-theme");
michael@0 600 expression_should_be_parseable("-moz-mac-lion-theme");
michael@0 601 expression_should_be_parseable("-moz-windows-compositor");
michael@0 602 expression_should_be_parseable("-moz-windows-classic");
michael@0 603 expression_should_be_parseable("-moz-windows-glass");
michael@0 604 expression_should_be_parseable("-moz-touch-enabled");
michael@0 605 expression_should_be_parseable("-moz-swipe-animation-enabled");
michael@0 606
michael@0 607 expression_should_be_parseable("-moz-scrollbar-start-backward: 0");
michael@0 608 expression_should_be_parseable("-moz-scrollbar-start-forward: 0");
michael@0 609 expression_should_be_parseable("-moz-scrollbar-end-backward: 0");
michael@0 610 expression_should_be_parseable("-moz-scrollbar-end-forward: 0");
michael@0 611 expression_should_be_parseable("-moz-scrollbar-thumb-proportional: 0");
michael@0 612 expression_should_be_parseable("-moz-images-in-menus: 0");
michael@0 613 expression_should_be_parseable("-moz-images-in-buttons: 0");
michael@0 614 expression_should_be_parseable("-moz-overlay-scrollbars: 0");
michael@0 615 expression_should_be_parseable("-moz-windows-default-theme: 0");
michael@0 616 expression_should_be_parseable("-moz-mac-graphite-theme: 0");
michael@0 617 expression_should_be_parseable("-moz-mac-lion-theme: 0");
michael@0 618 expression_should_be_parseable("-moz-windows-compositor: 0");
michael@0 619 expression_should_be_parseable("-moz-windows-classic: 0");
michael@0 620 expression_should_be_parseable("-moz-windows-glass: 0");
michael@0 621 expression_should_be_parseable("-moz-touch-enabled: 0");
michael@0 622 expression_should_be_parseable("-moz-swipe-animation-enabled: 0");
michael@0 623
michael@0 624 expression_should_be_parseable("-moz-scrollbar-start-backward: 1");
michael@0 625 expression_should_be_parseable("-moz-scrollbar-start-forward: 1");
michael@0 626 expression_should_be_parseable("-moz-scrollbar-end-backward: 1");
michael@0 627 expression_should_be_parseable("-moz-scrollbar-end-forward: 1");
michael@0 628 expression_should_be_parseable("-moz-scrollbar-thumb-proportional: 1");
michael@0 629 expression_should_be_parseable("-moz-images-in-menus: 1");
michael@0 630 expression_should_be_parseable("-moz-images-in-buttons: 1");
michael@0 631 expression_should_be_parseable("-moz-overlay-scrollbars: 1");
michael@0 632 expression_should_be_parseable("-moz-windows-default-theme: 1");
michael@0 633 expression_should_be_parseable("-moz-mac-graphite-theme: 1");
michael@0 634 expression_should_be_parseable("-moz-mac-lion-theme: 1");
michael@0 635 expression_should_be_parseable("-moz-windows-compositor: 1");
michael@0 636 expression_should_be_parseable("-moz-windows-classic: 1");
michael@0 637 expression_should_be_parseable("-moz-windows-glass: 1");
michael@0 638 expression_should_be_parseable("-moz-touch-enabled: 1");
michael@0 639 expression_should_be_parseable("-moz-swipe-animation-enabled: 1");
michael@0 640
michael@0 641 expression_should_not_be_parseable("-moz-scrollbar-start-backward: -1");
michael@0 642 expression_should_not_be_parseable("-moz-scrollbar-start-forward: -1");
michael@0 643 expression_should_not_be_parseable("-moz-scrollbar-end-backward: -1");
michael@0 644 expression_should_not_be_parseable("-moz-scrollbar-end-forward: -1");
michael@0 645 expression_should_not_be_parseable("-moz-scrollbar-thumb-proportional: -1");
michael@0 646 expression_should_not_be_parseable("-moz-images-in-menus: -1");
michael@0 647 expression_should_not_be_parseable("-moz-images-in-buttons: -1");
michael@0 648 expression_should_not_be_parseable("-moz-overlay-scrollbars: -1");
michael@0 649 expression_should_not_be_parseable("-moz-windows-default-theme: -1");
michael@0 650 expression_should_not_be_parseable("-moz-mac-graphite-theme: -1");
michael@0 651 expression_should_not_be_parseable("-moz-mac-lion-theme: -1");
michael@0 652 expression_should_not_be_parseable("-moz-windows-compositor: -1");
michael@0 653 expression_should_not_be_parseable("-moz-windows-classic: -1");
michael@0 654 expression_should_not_be_parseable("-moz-windows-glass: -1");
michael@0 655 expression_should_not_be_parseable("-moz-touch-enabled: -1");
michael@0 656 expression_should_not_be_parseable("-moz-swipe-animation-enabled: -1");
michael@0 657
michael@0 658 expression_should_not_be_parseable("-moz-scrollbar-start-backward: true");
michael@0 659 expression_should_not_be_parseable("-moz-scrollbar-start-forward: true");
michael@0 660 expression_should_not_be_parseable("-moz-scrollbar-end-backward: true");
michael@0 661 expression_should_not_be_parseable("-moz-scrollbar-end-forward: true");
michael@0 662 expression_should_not_be_parseable("-moz-scrollbar-thumb-proportional: true");
michael@0 663 expression_should_not_be_parseable("-moz-images-in-menus: true");
michael@0 664 expression_should_not_be_parseable("-moz-images-in-buttons: true");
michael@0 665 expression_should_not_be_parseable("-moz-overlay-scrollbars: true");
michael@0 666 expression_should_not_be_parseable("-moz-windows-default-theme: true");
michael@0 667 expression_should_not_be_parseable("-moz-mac-graphite-theme: true");
michael@0 668 expression_should_not_be_parseable("-moz-mac-lion-theme: true");
michael@0 669 expression_should_not_be_parseable("-moz-windows-compositor: true");
michael@0 670 expression_should_not_be_parseable("-moz-windows-classic: true");
michael@0 671 expression_should_not_be_parseable("-moz-windows-glass: true");
michael@0 672 expression_should_not_be_parseable("-moz-touch-enabled: true");
michael@0 673 expression_should_not_be_parseable("-moz-swipe-animation-enabled: true");
michael@0 674
michael@0 675 // windows theme media queries
michael@0 676 expression_should_be_parseable("-moz-windows-theme: aero");
michael@0 677 expression_should_be_parseable("-moz-windows-theme: aero-lite");
michael@0 678 expression_should_be_parseable("-moz-windows-theme: luna-blue");
michael@0 679 expression_should_be_parseable("-moz-windows-theme: luna-olive");
michael@0 680 expression_should_be_parseable("-moz-windows-theme: luna-silver");
michael@0 681 expression_should_be_parseable("-moz-windows-theme: royale");
michael@0 682 expression_should_be_parseable("-moz-windows-theme: generic");
michael@0 683 expression_should_be_parseable("-moz-windows-theme: zune");
michael@0 684 expression_should_be_parseable("-moz-windows-theme: garbage");
michael@0 685 expression_should_not_be_parseable("-moz-windows-theme: ''");
michael@0 686 expression_should_not_be_parseable("-moz-windows-theme: ");
michael@0 687
michael@0 688 // os version media queries (currently windows only)
michael@0 689 expression_should_be_parseable("-moz-os-version: windows-xp");
michael@0 690 expression_should_be_parseable("-moz-os-version: windows-vista");
michael@0 691 expression_should_be_parseable("-moz-os-version: windows-win7");
michael@0 692 expression_should_be_parseable("-moz-os-version: windows-win8");
michael@0 693 expression_should_not_be_parseable("-moz-os-version: ");
michael@0 694
michael@0 695 // OpenType SVG media features
michael@0 696 query_should_be_parseable("(-moz-is-glyph)");
michael@0 697 query_should_not_be_parseable("not (-moz-is-glyph)");
michael@0 698 query_should_not_be_parseable("only (-moz-is-glyph)");
michael@0 699 query_should_be_parseable("all and (-moz-is-glyph)");
michael@0 700 query_should_be_parseable("not all and (-moz-is-glyph)");
michael@0 701 query_should_be_parseable("only all and (-moz-is-glyph)");
michael@0 702
michael@0 703 query_should_be_parseable("(-moz-is-glyph:0)");
michael@0 704 query_should_not_be_parseable("not (-moz-is-glyph:0)");
michael@0 705 query_should_not_be_parseable("only (-moz-is-glyph:0)");
michael@0 706 query_should_be_parseable("all and (-moz-is-glyph:0)");
michael@0 707 query_should_be_parseable("not all and (-moz-is-glyph:0)");
michael@0 708 query_should_be_parseable("only all and (-moz-is-glyph:0)");
michael@0 709
michael@0 710 query_should_be_parseable("(-moz-is-glyph:1)");
michael@0 711 query_should_not_be_parseable("not (-moz-is-glyph:1)");
michael@0 712 query_should_not_be_parseable("only (-moz-is-glyph:1)");
michael@0 713 query_should_be_parseable("all and (-moz-is-glyph:1)");
michael@0 714 query_should_be_parseable("not all and (-moz-is-glyph:1)");
michael@0 715 query_should_be_parseable("only all and (-moz-is-glyph:1)");
michael@0 716
michael@0 717 query_should_not_be_parseable("(min--moz-is-glyph:0)");
michael@0 718 query_should_not_be_parseable("(max--moz-is-glyph:0)");
michael@0 719 query_should_not_be_parseable("(min--moz-is-glyph:1)");
michael@0 720 query_should_not_be_parseable("(max--moz-is-glyph:1)");
michael@0 721
michael@0 722 should_apply("not all and (-moz-is-glyph)");
michael@0 723 should_apply("(-moz-is-glyph:0)");
michael@0 724 should_apply("not all and (-moz-is-glyph:1)");
michael@0 725 should_apply("only all and (-moz-is-glyph:0)");
michael@0 726 should_not_apply("(-moz-is-glyph)");
michael@0 727 should_not_apply("(-moz-is-glyph:1)");
michael@0 728 should_not_apply("not all and (-moz-is-glyph:0)");
michael@0 729 should_not_apply("only all and (-moz-is-glyph:1)");
michael@0 730
michael@0 731 // Parsing tests
michael@0 732 // bug 454227
michael@0 733 should_apply_unbalanced("(orientation");
michael@0 734 should_not_apply_unbalanced("not all and (orientation");
michael@0 735 should_not_apply_unbalanced("(orientation:");
michael@0 736 should_apply_unbalanced("all,(orientation:");
michael@0 737 should_not_apply_unbalanced("(orientation:,all");
michael@0 738 should_apply_unbalanced("not all and (grid");
michael@0 739 should_not_apply_unbalanced("only all and (grid");
michael@0 740 should_not_apply_unbalanced("(grid");
michael@0 741 should_apply_unbalanced("all,(grid");
michael@0 742 should_not_apply_unbalanced("(grid,all");
michael@0 743 // bug 454226
michael@0 744 should_apply(",all");
michael@0 745 should_apply("all,");
michael@0 746 should_apply(",all,");
michael@0 747 should_apply("all,badmedium");
michael@0 748 should_apply("badmedium,all");
michael@0 749 should_not_apply(",badmedium,");
michael@0 750 should_apply("all,(badexpression)");
michael@0 751 should_apply("(badexpression),all");
michael@0 752 should_not_apply("(badexpression),badmedium");
michael@0 753 should_not_apply("badmedium,(badexpression)");
michael@0 754 should_apply("all,[badsyntax]");
michael@0 755 should_apply("[badsyntax],all");
michael@0 756 should_not_apply("badmedium,[badsyntax]");
michael@0 757 should_not_apply("[badsyntax],badmedium");
michael@0 758 // bug 528096
michael@0 759 should_not_apply_unbalanced("((resolution),all");
michael@0 760 should_not_apply_unbalanced("(resolution(),all");
michael@0 761 should_not_apply_unbalanced("(resolution (),all");
michael@0 762 should_not_apply_unbalanced("(resolution:(),all");
michael@0 763
michael@0 764 handle_posted_items();
michael@0 765 }
michael@0 766
michael@0 767 /*
michael@0 768 * The cloning tests have to post tests that wait for onload. However,
michael@0 769 * we also make a bunch of state changes during the tests above. So we
michael@0 770 * always change state using the change_state call, with both makes the
michael@0 771 * change immediately and posts an item in the same queue so that we
michael@0 772 * make the same state change again later.
michael@0 773 */
michael@0 774
michael@0 775 var posted_items = [];
michael@0 776
michael@0 777 function change_state(func)
michael@0 778 {
michael@0 779 func();
michael@0 780 posted_items.push({state: func});
michael@0 781 }
michael@0 782
michael@0 783 function post_clone_test(docurl, testfunc)
michael@0 784 {
michael@0 785 posted_items.push({docurl: docurl, testfunc: testfunc});
michael@0 786 }
michael@0 787
michael@0 788 function handle_posted_items()
michael@0 789 {
michael@0 790 if (posted_items.length == 0) {
michael@0 791 SimpleTest.finish();
michael@0 792 return;
michael@0 793 }
michael@0 794
michael@0 795 if ("state" in posted_items[0]) {
michael@0 796 var item = posted_items.shift();
michael@0 797 item.state();
michael@0 798 handle_posted_items();
michael@0 799 return;
michael@0 800 }
michael@0 801
michael@0 802 var docurl = posted_items[0].docurl;
michael@0 803 iframe.onload = handle_iframe_onload;
michael@0 804 iframe.src = docurl;
michael@0 805 }
michael@0 806
michael@0 807 function handle_iframe_onload(event)
michael@0 808 {
michael@0 809 if (event.target != iframe)
michael@0 810 return;
michael@0 811
michael@0 812 var item = posted_items.shift();
michael@0 813 item.testfunc();
michael@0 814 handle_posted_items();
michael@0 815 }
michael@0 816
michael@0 817 </script>
michael@0 818 </pre>
michael@0 819 </body>
michael@0 820 </html>
michael@0 821
michael@0 822

mercurial