layout/style/test/test_media_queries.html

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/layout/style/test/test_media_queries.html	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,822 @@
     1.4 +<!DOCTYPE HTML>
     1.5 +<html>
     1.6 +<!--
     1.7 +https://bugzilla.mozilla.org/show_bug.cgi?id=156716
     1.8 +-->
     1.9 +<head>
    1.10 +  <title>Test for Bug 156716</title>
    1.11 +  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
    1.12 +  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
    1.13 +</head>
    1.14 +<body onload="run()">
    1.15 +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=156716">Mozilla Bug 156716</a>
    1.16 +<iframe id="subdoc" src="media_queries_iframe.html"></iframe>
    1.17 +<div id="content" style="display: none">
    1.18 +  
    1.19 +</div>
    1.20 +<pre id="test">
    1.21 +<script class="testbody" type="application/javascript">
    1.22 +
    1.23 +/** Test for Bug 156716 **/
    1.24 +
    1.25 +// Note that many other tests are in test_acid3_test46.html .
    1.26 +
    1.27 +SimpleTest.waitForExplicitFinish();
    1.28 +
    1.29 +var iframe;
    1.30 +
    1.31 +function getScreenPixelsPerCSSPixel() {
    1.32 +  return SpecialPowers.DOMWindowUtils.screenPixelsPerCSSPixel;
    1.33 +}
    1.34 +
    1.35 +function run() {
    1.36 +  iframe = document.getElementById("subdoc");
    1.37 +  var subdoc = iframe.contentDocument;
    1.38 +  var subwin = iframe.contentWindow;
    1.39 +  var style = subdoc.getElementById("style");
    1.40 +  var iframe_style = iframe.style;
    1.41 +  var body_cs = subdoc.defaultView.getComputedStyle(subdoc.body, "");
    1.42 +
    1.43 +  function query_applies(q) {
    1.44 +    style.setAttribute("media", q);
    1.45 +    return body_cs.getPropertyValue("text-decoration") == "underline";
    1.46 +  }
    1.47 +
    1.48 +  function should_apply(q) {
    1.49 +    ok(query_applies(q), q + " should apply");
    1.50 +    test_serialization(q, true, true);
    1.51 +  }
    1.52 +
    1.53 +  function should_not_apply(q) {
    1.54 +    ok(!query_applies(q), q + " should not apply");
    1.55 +    test_serialization(q, true, false);
    1.56 +  }
    1.57 +
    1.58 +  /* for queries that are parseable standalone but not within CSS */
    1.59 +  function should_apply_unbalanced(q) {
    1.60 +    ok(query_applies(q), q + " should apply");
    1.61 +  }
    1.62 +
    1.63 +  /* for queries that are parseable standalone but not within CSS */
    1.64 +  function should_not_apply_unbalanced(q) {
    1.65 +    ok(!query_applies(q), q + " should not apply");
    1.66 +  }
    1.67 +
    1.68 +  /*
    1.69 +   * Functions to test whether a query is parseable at all.  (Should not
    1.70 +   * be used for parse errors within expressions.)
    1.71 +   */
    1.72 +  var parse_test_style_element = document.createElement("style");
    1.73 +  parse_test_style_element.type = "text/css";
    1.74 +  parse_test_style_element.disabled = true; // for performance, hopefully
    1.75 +  var parse_test_style_text = document.createTextNode("");
    1.76 +  parse_test_style_element.appendChild(parse_test_style_text);
    1.77 +  document.getElementsByTagName("head")[0]
    1.78 +    .appendChild(parse_test_style_element);
    1.79 +
    1.80 +  function query_is_parseable(q) {
    1.81 +    parse_test_style_text.data = "@media screen, " + q + " {}";
    1.82 +    var sheet = parse_test_style_element.sheet; // XXX yikes, not live!
    1.83 +    if (sheet.cssRules.length == 1 &&
    1.84 +        sheet.cssRules[0].type == CSSRule.MEDIA_RULE)
    1.85 +      return sheet.cssRules[0].media.mediaText != "screen, not all";
    1.86 +    ok(false, "unexpected result testing whether query " + q +
    1.87 +              " is parseable");
    1.88 +    return true; // doesn't matter, we already failed
    1.89 +  }
    1.90 +
    1.91 +  function query_should_be_parseable(q) {
    1.92 +    ok(query_is_parseable(q), "query " + q + " should be parseable");
    1.93 +    test_serialization(q, false, false);
    1.94 +  }
    1.95 +
    1.96 +  function query_should_not_be_parseable(q) {
    1.97 +    ok(!query_is_parseable(q), "query " + q + " should not be parseable");
    1.98 +  }
    1.99 +
   1.100 +  /*
   1.101 +   * Functions to test whether a single media expression is parseable.
   1.102 +   */
   1.103 +  function expression_is_parseable(e) {
   1.104 +    style.setAttribute("media", "all and (" + e + ")");
   1.105 +    return style.sheet.media.mediaText != "not all";
   1.106 +  }
   1.107 +
   1.108 +  function expression_should_be_parseable(e) {
   1.109 +    ok(expression_is_parseable(e),
   1.110 +       "expression " + e + " should be parseable");
   1.111 +    test_serialization("all and (" + e + ")", false, false);
   1.112 +  }
   1.113 +
   1.114 +  function expression_should_not_be_parseable(e) {
   1.115 +    ok(!expression_is_parseable(e),
   1.116 +       "expression " + e + " should not be parseable");
   1.117 +  }
   1.118 +
   1.119 +  function test_serialization(q, test_application, should_apply) {
   1.120 +    style.setAttribute("media", q);
   1.121 +    var ser1 = style.sheet.media.mediaText;
   1.122 +    isnot(ser1, "", "serialization of '" + q + "' should not be empty");
   1.123 +    style.setAttribute("media", ser1);
   1.124 +    var ser2 = style.sheet.media.mediaText;
   1.125 +    is(ser2, ser1, "parse+serialize of '" + q + "' should be idempotent");
   1.126 +    if (test_application) {
   1.127 +      var applies = body_cs.getPropertyValue("text-decoration") == "underline";
   1.128 +      is(applies, should_apply,
   1.129 +         "Media query '" + q + "' should " + (should_apply ? "" : "NOT ") +
   1.130 +         "apply after serialize + reparse");
   1.131 +    }
   1.132 +
   1.133 +    // Test cloning
   1.134 +    var sheet = "@media " + q + " { body { text-decoration: underline } }"
   1.135 +    var sheeturl = "data:text/css," + escape(sheet);
   1.136 +    var link = "<link rel='stylesheet' href='" + sheeturl + "'>";
   1.137 +    var htmldoc = "<!DOCTYPE HTML>" + link + link  + "<body>";
   1.138 +    var docurl = "data:text/html," + escape(htmldoc);
   1.139 +    post_clone_test(docurl, function() {
   1.140 +      var clonedoc = iframe.contentDocument;
   1.141 +      var clonewin = iframe.contentWindow;
   1.142 +      var links = clonedoc.getElementsByTagName("link");
   1.143 +      // cause a clone
   1.144 +      var clonedsheet = links[1].sheet;
   1.145 +      clonedsheet.insertRule("#nonexistent { color: purple}", 1);
   1.146 +      // remove the uncloned sheet
   1.147 +      links[0].parentNode.removeChild(links[0]);
   1.148 +
   1.149 +      var ser3 = clonedsheet.cssRules[0].media.mediaText;
   1.150 +      is(ser3, ser1, "cloning query '" + q + "' should not change " +
   1.151 +                     "serialization");
   1.152 +      if (test_application) {
   1.153 +        var applies = clonewin.getComputedStyle(clonedoc.body, "").
   1.154 +                        textDecoration == "underline";
   1.155 +        is(applies, should_apply,
   1.156 +           "Media query '" + q + "' should " + (should_apply ? "" : "NOT ") +
   1.157 +           "apply after cloning");
   1.158 +      }
   1.159 +    });
   1.160 +  }
   1.161 +
   1.162 +  // The no-type syntax doesn't mix with the not and only keywords.
   1.163 +  query_should_be_parseable("(orientation)");
   1.164 +  query_should_not_be_parseable("not (orientation)");
   1.165 +  query_should_not_be_parseable("only (orientation)");
   1.166 +  query_should_be_parseable("all and (orientation)");
   1.167 +  query_should_be_parseable("not all and (orientation)");
   1.168 +  query_should_be_parseable("only all and (orientation)");
   1.169 +
   1.170 +  query_should_be_parseable("(-moz-device-orientation)");
   1.171 +  query_should_not_be_parseable("not (-moz-device-orientation)");
   1.172 +  query_should_not_be_parseable("only (-moz-device-orientation)");
   1.173 +  query_should_be_parseable("all and (-moz-device-orientation)");
   1.174 +  query_should_be_parseable("not all and (-moz-device-orientation)");
   1.175 +  query_should_be_parseable("only all and (-moz-device-orientation)");
   1.176 +
   1.177 +  // Test that the 'not', 'only', 'and', and 'or' keywords are not
   1.178 +  // allowed as media types.
   1.179 +  query_should_not_be_parseable("not");
   1.180 +  query_should_not_be_parseable("and");
   1.181 +  query_should_not_be_parseable("or");
   1.182 +  query_should_not_be_parseable("only");
   1.183 +  query_should_be_parseable("unknowntype");
   1.184 +  query_should_not_be_parseable("not not");
   1.185 +  query_should_not_be_parseable("not and");
   1.186 +  query_should_not_be_parseable("not or");
   1.187 +  query_should_not_be_parseable("not only");
   1.188 +  query_should_be_parseable("not unknowntype");
   1.189 +  query_should_not_be_parseable("only not");
   1.190 +  query_should_not_be_parseable("only and");
   1.191 +  query_should_not_be_parseable("only or");
   1.192 +  query_should_not_be_parseable("only only");
   1.193 +  query_should_be_parseable("only unknowntype");
   1.194 +  query_should_not_be_parseable("not and (width)");
   1.195 +  query_should_not_be_parseable("and and (width)");
   1.196 +  query_should_not_be_parseable("or and (width)");
   1.197 +  query_should_not_be_parseable("only and (width)");
   1.198 +  query_should_be_parseable("unknowntype and (width)");
   1.199 +  query_should_not_be_parseable("not not and (width)");
   1.200 +  query_should_not_be_parseable("not and and (width)");
   1.201 +  query_should_not_be_parseable("not or and (width)");
   1.202 +  query_should_not_be_parseable("not only and (width)");
   1.203 +  query_should_be_parseable("not unknowntype and (width)");
   1.204 +  query_should_not_be_parseable("only not and (width)");
   1.205 +  query_should_not_be_parseable("only and and (width)");
   1.206 +  query_should_not_be_parseable("only or and (width)");
   1.207 +  query_should_not_be_parseable("only only and (width)");
   1.208 +  query_should_be_parseable("only unknowntype and (width)");
   1.209 +
   1.210 +  var features = [ "width", "height", "device-width", "device-height" ];
   1.211 +  var feature;
   1.212 +  var i;
   1.213 +  for (i in features) {
   1.214 +    feature = features[i];
   1.215 +    expression_should_be_parseable(feature);
   1.216 +    expression_should_be_parseable(feature + ": 0");
   1.217 +    expression_should_be_parseable(feature + ": 0px");
   1.218 +    expression_should_be_parseable(feature + ": 0em");
   1.219 +    expression_should_be_parseable(feature + ": -0");
   1.220 +    expression_should_be_parseable("min-" + feature + ": -0");
   1.221 +    expression_should_be_parseable("max-" + feature + ": -0");
   1.222 +    expression_should_be_parseable(feature + ": -0cm");
   1.223 +    expression_should_be_parseable(feature + ": 1px");
   1.224 +    expression_should_be_parseable(feature + ": 0.001mm");
   1.225 +    expression_should_be_parseable(feature + ": 100000px");
   1.226 +    expression_should_not_be_parseable(feature + ": -1px");
   1.227 +    expression_should_not_be_parseable("min-" + feature + ": -1px");
   1.228 +    expression_should_not_be_parseable("max-" + feature + ": -1px");
   1.229 +    expression_should_not_be_parseable(feature + ": -0.00001mm");
   1.230 +    expression_should_not_be_parseable(feature + ": -100000em");
   1.231 +    expression_should_not_be_parseable("min-" + feature);
   1.232 +    expression_should_not_be_parseable("max-" + feature);
   1.233 +  }
   1.234 +
   1.235 +  var content_div = document.getElementById("content");
   1.236 +  content_div.style.font = "initial";
   1.237 +  var em_size =
   1.238 +    getComputedStyle(content_div, "").fontSize.match(/^(\d+)px$/)[1];
   1.239 +
   1.240 +  // in this test, assume the common underlying implementation is correct
   1.241 +  var width_val = 117; // pick two not-too-round numbers
   1.242 +  var height_val = 76;
   1.243 +  change_state(function() {
   1.244 +    iframe_style.width = width_val + "px";
   1.245 +    iframe_style.height = height_val + "px";
   1.246 +  });
   1.247 +  var device_width = window.screen.width;
   1.248 +  var device_height = window.screen.height;
   1.249 +  features = { "width": width_val,
   1.250 +                    "height": height_val,
   1.251 +                    "device-width": device_width,
   1.252 +                    "device-height": device_height };
   1.253 +  for (feature in features) {
   1.254 +    var value = features[feature];
   1.255 +    should_apply("all and (" + feature + ": " + value + "px)");
   1.256 +    should_not_apply("all and (" + feature + ": " + (value + 1) + "px)");
   1.257 +    should_not_apply("all and (" + feature + ": " + (value - 1) + "px)");
   1.258 +    should_apply("all and (min-" + feature + ": " + value + "px)");
   1.259 +    should_not_apply("all and (min-" + feature + ": " + (value + 1) + "px)");
   1.260 +    should_apply("all and (min-" + feature + ": " + (value - 1) + "px)");
   1.261 +    should_apply("all and (max-" + feature + ": " + value + "px)");
   1.262 +    should_apply("all and (max-" + feature + ": " + (value + 1) + "px)");
   1.263 +    should_not_apply("all and (max-" + feature + ": " + (value - 1) + "px)");
   1.264 +    should_not_apply("all and (min-" + feature + ": " +
   1.265 +                     (Math.ceil(value/em_size) + 1) + "em)");
   1.266 +    should_apply("all and (min-" + feature + ": " +
   1.267 +                 (Math.floor(value/em_size) - 1) + "em)");
   1.268 +    should_apply("all and (max-" + feature + ": " +
   1.269 +                 (Math.ceil(value/em_size) + 1) + "em)");
   1.270 +    should_not_apply("all and (max-" + feature + ": " +
   1.271 +                     (Math.floor(value/em_size) - 1) + "em)");
   1.272 +    should_not_apply("all and (min-" + feature + ": " +
   1.273 +                     (Math.ceil(value/em_size) + 1) + "rem)");
   1.274 +    should_apply("all and (min-" + feature + ": " +
   1.275 +                 (Math.floor(value/em_size) - 1) + "rem)");
   1.276 +    should_apply("all and (max-" + feature + ": " +
   1.277 +                 (Math.ceil(value/em_size) + 1) + "rem)");
   1.278 +    should_not_apply("all and (max-" + feature + ": " +
   1.279 +                     (Math.floor(value/em_size) - 1) + "rem)");
   1.280 +  }
   1.281 +
   1.282 +  change_state(function() {
   1.283 +    iframe_style.width = "0";
   1.284 +  });
   1.285 +  should_apply("all and (height)");
   1.286 +  should_not_apply("all and (width)");
   1.287 +  change_state(function() {
   1.288 +    iframe_style.height = "0";
   1.289 +  });
   1.290 +  should_not_apply("all and (height)");
   1.291 +  should_not_apply("all and (width)");
   1.292 +  should_apply("all and (device-height)");
   1.293 +  should_apply("all and (device-width)");
   1.294 +  change_state(function() {
   1.295 +    iframe_style.width = width_val + "px";
   1.296 +  });
   1.297 +  should_not_apply("all and (height)");
   1.298 +  should_apply("all and (width)");
   1.299 +  change_state(function() {
   1.300 +    iframe_style.height = height_val + "px";
   1.301 +  });
   1.302 +  should_apply("all and (height)");
   1.303 +  should_apply("all and (width)");
   1.304 +
   1.305 +  // ratio that reduces to 59/40
   1.306 +  change_state(function() {
   1.307 +    iframe_style.width = "236px";
   1.308 +    iframe_style.height = "160px";
   1.309 +  });
   1.310 +  expression_should_be_parseable("orientation");
   1.311 +  expression_should_be_parseable("orientation: portrait");
   1.312 +  expression_should_be_parseable("orientation: landscape");
   1.313 +  expression_should_not_be_parseable("min-orientation");
   1.314 +  expression_should_not_be_parseable("min-orientation: portrait");
   1.315 +  expression_should_not_be_parseable("min-orientation: landscape");
   1.316 +  expression_should_not_be_parseable("max-orientation");
   1.317 +  expression_should_not_be_parseable("max-orientation: portrait");
   1.318 +  expression_should_not_be_parseable("max-orientation: landscape");
   1.319 +  should_apply("(orientation)");
   1.320 +  should_apply("(orientation: landscape)");
   1.321 +  should_not_apply("(orientation: portrait)");
   1.322 +  should_apply("not all and (orientation: portrait)");
   1.323 +  // ratio that reduces to 59/80
   1.324 +  change_state(function() {
   1.325 +    iframe_style.height = "320px";
   1.326 +  });
   1.327 +  should_apply("(orientation)");
   1.328 +  should_not_apply("(orientation: landscape)");
   1.329 +  should_apply("not all and (orientation: landscape)");
   1.330 +  should_apply("(orientation: portrait)");
   1.331 +
   1.332 +  expression_should_be_parseable("-moz-device-orientation");
   1.333 +  expression_should_be_parseable("-moz-device-orientation: portrait");
   1.334 +  expression_should_be_parseable("-moz-device-orientation: landscape");
   1.335 +  expression_should_not_be_parseable("min--moz-device-orientation");
   1.336 +  expression_should_not_be_parseable("min--moz-device-orientation: portrait");
   1.337 +  expression_should_not_be_parseable("min--moz-device-orientation: landscape");
   1.338 +  expression_should_not_be_parseable("max--moz-device-orientation");
   1.339 +  expression_should_not_be_parseable("max--moz-device-orientation: portrait");
   1.340 +  expression_should_not_be_parseable("max--moz-device-orientation: landscape");
   1.341 +
   1.342 +  // determine the actual configuration of the screen and test against it
   1.343 +  var device_orientation = (device_width > device_height) ? "landscape" : "portrait";
   1.344 +  var not_device_orientation = (device_orientation == "landscape") ? "portrait" : "landscape";
   1.345 +  should_apply("(-moz-device-orientation)");
   1.346 +  should_apply("(-moz-device-orientation: " + device_orientation + ")");
   1.347 +  should_not_apply("(-moz-device-orientation: " + not_device_orientation + ")");
   1.348 +  should_apply("not all and (-moz-device-orientation: " + not_device_orientation + ")");
   1.349 +
   1.350 +  should_apply("(aspect-ratio: 59/80)");
   1.351 +  should_not_apply("(aspect-ratio: 58/80)");
   1.352 +  should_not_apply("(aspect-ratio: 59/81)");
   1.353 +  should_not_apply("(aspect-ratio: 60/80)");
   1.354 +  should_not_apply("(aspect-ratio: 59/79)");
   1.355 +  should_apply("(aspect-ratio: 177/240)");
   1.356 +  should_apply("(aspect-ratio: 413/560)");
   1.357 +  should_apply("(aspect-ratio: 5900/8000)");
   1.358 +  should_not_apply("(aspect-ratio: 5901/8000)");
   1.359 +  should_not_apply("(aspect-ratio: 5899/8000)");
   1.360 +  should_not_apply("(aspect-ratio: 5900/8001)");
   1.361 +  should_not_apply("(aspect-ratio: 5900/7999)");
   1.362 +  should_apply("(aspect-ratio)");
   1.363 +
   1.364 +  should_apply("(min-aspect-ratio: 59/80)");
   1.365 +  should_apply("(min-aspect-ratio: 58/80)");
   1.366 +  should_apply("(min-aspect-ratio: 59/81)");
   1.367 +  should_not_apply("(min-aspect-ratio: 60/80)");
   1.368 +  should_not_apply("(min-aspect-ratio: 59/79)");
   1.369 +  expression_should_not_be_parseable("min-aspect-ratio");
   1.370 +
   1.371 +  should_apply("(max-aspect-ratio: 59/80)");
   1.372 +  should_not_apply("(max-aspect-ratio: 58/80)");
   1.373 +  should_not_apply("(max-aspect-ratio: 59/81)");
   1.374 +  should_apply("(max-aspect-ratio: 60/80)");
   1.375 +  should_apply("(max-aspect-ratio: 59/79)");
   1.376 +  expression_should_not_be_parseable("max-aspect-ratio");
   1.377 +
   1.378 +  var real_dar = device_width + "/" + device_height;
   1.379 +  var high_dar_1 = (device_width + 1) + "/" + device_height;
   1.380 +  var high_dar_2 = device_width + "/" + (device_height - 1);
   1.381 +  var low_dar_1 = (device_width - 1) + "/" + device_height;
   1.382 +  var low_dar_2 = device_width + "/" + (device_height + 1);
   1.383 +  should_apply("(device-aspect-ratio: " + real_dar + ")");
   1.384 +  should_apply("not all and (device-aspect-ratio: " + high_dar_1 + ")");
   1.385 +  should_not_apply("all and (device-aspect-ratio: " + high_dar_2 + ")");
   1.386 +  should_not_apply("all and (device-aspect-ratio: " + low_dar_1 + ")");
   1.387 +  should_apply("not all and (device-aspect-ratio: " + low_dar_2 + ")");
   1.388 +  should_apply("(device-aspect-ratio)");
   1.389 +
   1.390 +  should_apply("(min-device-aspect-ratio: " + real_dar + ")");
   1.391 +  should_not_apply("all and (min-device-aspect-ratio: " + high_dar_1 + ")");
   1.392 +  should_apply("not all and (min-device-aspect-ratio: " + high_dar_2 + ")");
   1.393 +  should_not_apply("not all and (min-device-aspect-ratio: " + low_dar_1 + ")");
   1.394 +  should_apply("all and (min-device-aspect-ratio: " + low_dar_2 + ")");
   1.395 +  expression_should_not_be_parseable("min-device-aspect-ratio");
   1.396 +
   1.397 +  should_apply("all and (max-device-aspect-ratio: " + real_dar + ")");
   1.398 +  should_apply("(max-device-aspect-ratio: " + high_dar_1 + ")");
   1.399 +  should_apply("(max-device-aspect-ratio: " + high_dar_2 + ")");
   1.400 +  should_not_apply("all and (max-device-aspect-ratio: " + low_dar_1 + ")");
   1.401 +  should_apply("not all and (max-device-aspect-ratio: " + low_dar_2 + ")");
   1.402 +  expression_should_not_be_parseable("max-device-aspect-ratio");
   1.403 +
   1.404 +  var real_dpr = 1.0 * getScreenPixelsPerCSSPixel();
   1.405 +  var high_dpr = 1.1 * getScreenPixelsPerCSSPixel();
   1.406 +  var low_dpr = 0.9 * getScreenPixelsPerCSSPixel();
   1.407 +  should_apply("all and (max--moz-device-pixel-ratio: " + real_dpr + ")");
   1.408 +  should_apply("all and (min--moz-device-pixel-ratio: " + real_dpr + ")");
   1.409 +  should_not_apply("not all and (max--moz-device-pixel-ratio: " + real_dpr + ")");
   1.410 +  should_not_apply("not all and (min--moz-device-pixel-ratio: " + real_dpr + ")");
   1.411 +  should_apply("all and (min--moz-device-pixel-ratio: " + low_dpr + ")");
   1.412 +  should_apply("all and (max--moz-device-pixel-ratio: " + high_dpr + ")");
   1.413 +  should_not_apply("all and (max--moz-device-pixel-ratio: " + low_dpr + ")");
   1.414 +  should_not_apply("all and (min--moz-device-pixel-ratio: " + high_dpr + ")");
   1.415 +  should_apply("not all and (max--moz-device-pixel-ratio: " + low_dpr + ")");
   1.416 +  should_apply("not all and (min--moz-device-pixel-ratio: " + high_dpr + ")");
   1.417 +  should_apply("(-moz-device-pixel-ratio: " + real_dpr + ")");
   1.418 +  should_not_apply("(-moz-device-pixel-ratio: " + high_dpr + ")");
   1.419 +  should_not_apply("(-moz-device-pixel-ratio: " + low_dpr + ")");
   1.420 +  should_apply("(-moz-device-pixel-ratio)");
   1.421 +  expression_should_not_be_parseable("min--moz-device-pixel-ratio");
   1.422 +  expression_should_not_be_parseable("max--moz-device-pixel-ratio");
   1.423 +
   1.424 +  features = [ "max-aspect-ratio", "device-aspect-ratio" ];
   1.425 +  for (i in features) {
   1.426 +    feature = features[i];
   1.427 +    expression_should_be_parseable(feature + ": 1/1");
   1.428 +    expression_should_be_parseable(feature + ": 1  /1");
   1.429 +    expression_should_be_parseable(feature + ": 1  / \t\n1");
   1.430 +    expression_should_be_parseable(feature + ": 1/\r1");
   1.431 +    expression_should_not_be_parseable(feature + ": 1");
   1.432 +    expression_should_not_be_parseable(feature + ": 0.5");
   1.433 +    expression_should_not_be_parseable(feature + ": 1.0/1");
   1.434 +    expression_should_not_be_parseable(feature + ": 1/1.0");
   1.435 +    expression_should_not_be_parseable(feature + ": 1.0/1.0");
   1.436 +    expression_should_not_be_parseable(feature + ": 0/1");
   1.437 +    expression_should_not_be_parseable(feature + ": 1/0");
   1.438 +    expression_should_not_be_parseable(feature + ": 0/0");
   1.439 +    expression_should_not_be_parseable(feature + ": -1/1");
   1.440 +    expression_should_not_be_parseable(feature + ": 1/-1");
   1.441 +    expression_should_not_be_parseable(feature + ": -1/-1");
   1.442 +  }
   1.443 +
   1.444 +  var is_monochrome = query_applies("all and (min-monochrome: 1)");
   1.445 +  test_serialization("all and (min-monochrome: 1)", true, is_monochrome);
   1.446 +  var is_color = query_applies("all and (min-color: 1)");
   1.447 +  test_serialization("all and (min-color: 1)", true, is_color);
   1.448 +  isnot(is_monochrome, is_color, "should be either monochrome or color");
   1.449 +
   1.450 +  function depth_query(prefix, depth) {
   1.451 +    return "all and (" + prefix + (is_color ? "color" : "monochrome") +
   1.452 +           ":" + depth + ")";
   1.453 +  }
   1.454 +
   1.455 +  var depth = 0;
   1.456 +  do {
   1.457 +    if (depth > 50) {
   1.458 +      ok(false, "breaking from loop, depth > 50");
   1.459 +      break;
   1.460 +    }
   1.461 +  } while (query_applies(depth_query("min-", ++depth)));
   1.462 +  --depth;
   1.463 +
   1.464 +  should_apply(depth_query("", depth));
   1.465 +  should_not_apply(depth_query("", depth - 1));
   1.466 +  should_not_apply(depth_query("", depth + 1));
   1.467 +  should_apply(depth_query("max-", depth));
   1.468 +  should_not_apply(depth_query("max-", depth - 1));
   1.469 +  should_apply(depth_query("max-", depth + 1));
   1.470 +
   1.471 +  (is_color ? should_apply : should_not_apply)("all and (color)");
   1.472 +  expression_should_not_be_parseable("max-color");
   1.473 +  expression_should_not_be_parseable("min-color");
   1.474 +  (is_color ? should_not_apply : should_apply)("all and (monochrome)");
   1.475 +  expression_should_not_be_parseable("max-monochrome");
   1.476 +  expression_should_not_be_parseable("min-monochrome");
   1.477 +  (is_color ? should_apply : should_not_apply)("not all and (monochrome)");
   1.478 +  (is_color ? should_not_apply : should_apply)("not all and (color)");
   1.479 +  (is_color ? should_apply : should_not_apply)("only all and (color)");
   1.480 +  (is_color ? should_not_apply : should_apply)("only all and (monochrome)");
   1.481 +
   1.482 +  features = [ "color", "min-monochrome", "max-color-index" ];
   1.483 +  for (i in features) {
   1.484 +    feature = features[i];
   1.485 +    expression_should_be_parseable(feature + ": 1");
   1.486 +    expression_should_be_parseable(feature + ": 327");
   1.487 +    expression_should_be_parseable(feature + ": 0");
   1.488 +    expression_should_not_be_parseable(feature + ": 1.0");
   1.489 +    expression_should_not_be_parseable(feature + ": -1");
   1.490 +    expression_should_not_be_parseable(feature + ": 1/1");
   1.491 +  }
   1.492 +
   1.493 +  // Presume that we never support indexed color (at least not usefully
   1.494 +  // enough to call it indexed color).
   1.495 +  should_apply("(color-index: 0)");
   1.496 +  should_not_apply("(color-index: 1)");
   1.497 +  should_apply("(min-color-index: 0)");
   1.498 +  should_not_apply("(min-color-index: 1)");
   1.499 +  should_apply("(max-color-index: 0)");
   1.500 +  should_apply("(max-color-index: 1)");
   1.501 +  should_apply("(max-color-index: 157)");
   1.502 +
   1.503 +  features = [ "resolution", "min-resolution", "max-resolution" ];
   1.504 +  for (i in features) {
   1.505 +    feature = features[i];
   1.506 +    expression_should_be_parseable(feature + ": 3dpi");
   1.507 +    expression_should_be_parseable(feature + ":3dpi");
   1.508 +    expression_should_be_parseable(feature + ": 3.0dpi");
   1.509 +    expression_should_be_parseable(feature + ": 3.4dpi");
   1.510 +    expression_should_be_parseable(feature + "\t: 120dpcm");
   1.511 +    expression_should_be_parseable(feature + ": 1dppx");
   1.512 +    expression_should_be_parseable(feature + ": 1.5dppx");
   1.513 +    expression_should_be_parseable(feature + ": 2.0dppx");
   1.514 +    expression_should_not_be_parseable(feature + ": 0dpi");
   1.515 +    expression_should_not_be_parseable(feature + ": -3dpi");
   1.516 +    expression_should_not_be_parseable(feature + ": 0dppx");
   1.517 +  }
   1.518 +
   1.519 +  // Find the resolution using max-resolution
   1.520 +  var resolution = 0;
   1.521 +  do {
   1.522 +    ++resolution;
   1.523 +    if (resolution > 10000) {
   1.524 +      ok(false, "resolution greater than 10000dpi???");
   1.525 +      break;
   1.526 +    }
   1.527 +  } while (!query_applies("(max-resolution: " + resolution + "dpi)"));
   1.528 +
   1.529 +  // resolution should now be Math.ceil() of the actual resolution.
   1.530 +  var dpi_high;
   1.531 +  var dpi_low = resolution - 1;
   1.532 +  if (query_applies("(min-resolution: " + resolution + "dpi)")) {
   1.533 +    // It's exact!
   1.534 +    should_apply("(resolution: " + resolution + "dpi)");
   1.535 +    should_apply("(resolution: " + Math.floor(resolution/96) + "dppx)");
   1.536 +    should_not_apply("(resolution: " + (resolution + 1) + "dpi)");
   1.537 +    should_not_apply("(resolution: " + (resolution - 1) + "dpi)");
   1.538 +    dpi_high = resolution + 1;
   1.539 +  } else {
   1.540 +	// We have no way to test resolution applying since it need not be
   1.541 +	// an integer.
   1.542 +    should_not_apply("(resolution: " + resolution + "dpi)");
   1.543 +    should_not_apply("(resolution: " + (resolution - 1) + "dpi)");
   1.544 +    dpi_high = resolution;
   1.545 +  }
   1.546 +
   1.547 +  should_apply("(min-resolution: " + dpi_low + "dpi)");
   1.548 +  should_not_apply("not all and (min-resolution: " + dpi_low + "dpi)");
   1.549 +  should_apply("not all and (min-resolution: " + dpi_high + "dpi)");
   1.550 +  should_not_apply("all and (min-resolution: " + dpi_high + "dpi)");
   1.551 +
   1.552 +  // Test dpcm units based on what we computed in dpi.
   1.553 +  var dpcm_high = Math.ceil(dpi_high / 2.54);
   1.554 +  var dpcm_low = Math.floor(dpi_low / 2.54);
   1.555 +  should_apply("(min-resolution: " + dpcm_low + "dpcm)");
   1.556 +  should_apply("(max-resolution: " + dpcm_high + "dpcm)");
   1.557 +  should_not_apply("(max-resolution: " + dpcm_low + "dpcm)");
   1.558 +  should_apply("not all and (min-resolution: " + dpcm_high + "dpcm)");
   1.559 +
   1.560 +  expression_should_be_parseable("scan");
   1.561 +  expression_should_be_parseable("scan: progressive");
   1.562 +  expression_should_be_parseable("scan:interlace");
   1.563 +  expression_should_not_be_parseable("min-scan:interlace");
   1.564 +  expression_should_not_be_parseable("scan: 1");
   1.565 +  expression_should_not_be_parseable("max-scan");
   1.566 +  expression_should_not_be_parseable("max-scan: progressive");
   1.567 +  // Assume we don't support tv devices.
   1.568 +  should_not_apply("(scan)");
   1.569 +  should_not_apply("(scan: progressive)");
   1.570 +  should_not_apply("(scan: interlace)");
   1.571 +  should_apply("not all and (scan)");
   1.572 +  should_apply("not all and (scan: progressive)");
   1.573 +  should_apply("not all and (scan: interlace)");
   1.574 +
   1.575 +  expression_should_be_parseable("grid");
   1.576 +  expression_should_be_parseable("grid: 0");
   1.577 +  expression_should_be_parseable("grid: 1");
   1.578 +  expression_should_be_parseable("grid: 1");
   1.579 +  expression_should_not_be_parseable("min-grid");
   1.580 +  expression_should_not_be_parseable("min-grid:0");
   1.581 +  expression_should_not_be_parseable("max-grid: 1");
   1.582 +  expression_should_not_be_parseable("grid: 2");
   1.583 +  expression_should_not_be_parseable("grid: -1");
   1.584 +
   1.585 +  // Assume we don't support grid devices
   1.586 +  should_not_apply("(grid)");
   1.587 +  should_apply("(grid: 0)");
   1.588 +  should_not_apply("(grid: 1)");
   1.589 +  should_not_apply("(grid: 2)");
   1.590 +  should_not_apply("(grid: -1)");
   1.591 +
   1.592 +  // System metrics
   1.593 +  expression_should_be_parseable("-moz-scrollbar-start-backward");
   1.594 +  expression_should_be_parseable("-moz-scrollbar-start-forward");
   1.595 +  expression_should_be_parseable("-moz-scrollbar-end-backward");
   1.596 +  expression_should_be_parseable("-moz-scrollbar-end-forward");
   1.597 +  expression_should_be_parseable("-moz-scrollbar-thumb-proportional");
   1.598 +  expression_should_be_parseable("-moz-images-in-menus");
   1.599 +  expression_should_be_parseable("-moz-images-in-buttons");
   1.600 +  expression_should_be_parseable("-moz-overlay-scrollbars");
   1.601 +  expression_should_be_parseable("-moz-windows-default-theme");
   1.602 +  expression_should_be_parseable("-moz-mac-graphite-theme");
   1.603 +  expression_should_be_parseable("-moz-mac-lion-theme");
   1.604 +  expression_should_be_parseable("-moz-windows-compositor");
   1.605 +  expression_should_be_parseable("-moz-windows-classic");
   1.606 +  expression_should_be_parseable("-moz-windows-glass");
   1.607 +  expression_should_be_parseable("-moz-touch-enabled");
   1.608 +  expression_should_be_parseable("-moz-swipe-animation-enabled");
   1.609 +
   1.610 +  expression_should_be_parseable("-moz-scrollbar-start-backward: 0");
   1.611 +  expression_should_be_parseable("-moz-scrollbar-start-forward: 0");
   1.612 +  expression_should_be_parseable("-moz-scrollbar-end-backward: 0");
   1.613 +  expression_should_be_parseable("-moz-scrollbar-end-forward: 0");
   1.614 +  expression_should_be_parseable("-moz-scrollbar-thumb-proportional: 0");
   1.615 +  expression_should_be_parseable("-moz-images-in-menus: 0");
   1.616 +  expression_should_be_parseable("-moz-images-in-buttons: 0");
   1.617 +  expression_should_be_parseable("-moz-overlay-scrollbars: 0");
   1.618 +  expression_should_be_parseable("-moz-windows-default-theme: 0");
   1.619 +  expression_should_be_parseable("-moz-mac-graphite-theme: 0");
   1.620 +  expression_should_be_parseable("-moz-mac-lion-theme: 0");
   1.621 +  expression_should_be_parseable("-moz-windows-compositor: 0");
   1.622 +  expression_should_be_parseable("-moz-windows-classic: 0");
   1.623 +  expression_should_be_parseable("-moz-windows-glass: 0");
   1.624 +  expression_should_be_parseable("-moz-touch-enabled: 0");
   1.625 +  expression_should_be_parseable("-moz-swipe-animation-enabled: 0");
   1.626 +
   1.627 +  expression_should_be_parseable("-moz-scrollbar-start-backward: 1");
   1.628 +  expression_should_be_parseable("-moz-scrollbar-start-forward: 1");
   1.629 +  expression_should_be_parseable("-moz-scrollbar-end-backward: 1");
   1.630 +  expression_should_be_parseable("-moz-scrollbar-end-forward: 1");
   1.631 +  expression_should_be_parseable("-moz-scrollbar-thumb-proportional: 1");
   1.632 +  expression_should_be_parseable("-moz-images-in-menus: 1");
   1.633 +  expression_should_be_parseable("-moz-images-in-buttons: 1");
   1.634 +  expression_should_be_parseable("-moz-overlay-scrollbars: 1");
   1.635 +  expression_should_be_parseable("-moz-windows-default-theme: 1");
   1.636 +  expression_should_be_parseable("-moz-mac-graphite-theme: 1");
   1.637 +  expression_should_be_parseable("-moz-mac-lion-theme: 1");
   1.638 +  expression_should_be_parseable("-moz-windows-compositor: 1");
   1.639 +  expression_should_be_parseable("-moz-windows-classic: 1");
   1.640 +  expression_should_be_parseable("-moz-windows-glass: 1");
   1.641 +  expression_should_be_parseable("-moz-touch-enabled: 1");
   1.642 +  expression_should_be_parseable("-moz-swipe-animation-enabled: 1");
   1.643 +
   1.644 +  expression_should_not_be_parseable("-moz-scrollbar-start-backward: -1");
   1.645 +  expression_should_not_be_parseable("-moz-scrollbar-start-forward: -1");
   1.646 +  expression_should_not_be_parseable("-moz-scrollbar-end-backward: -1");
   1.647 +  expression_should_not_be_parseable("-moz-scrollbar-end-forward: -1");
   1.648 +  expression_should_not_be_parseable("-moz-scrollbar-thumb-proportional: -1");
   1.649 +  expression_should_not_be_parseable("-moz-images-in-menus: -1");
   1.650 +  expression_should_not_be_parseable("-moz-images-in-buttons: -1");
   1.651 +  expression_should_not_be_parseable("-moz-overlay-scrollbars: -1");
   1.652 +  expression_should_not_be_parseable("-moz-windows-default-theme: -1");
   1.653 +  expression_should_not_be_parseable("-moz-mac-graphite-theme: -1");
   1.654 +  expression_should_not_be_parseable("-moz-mac-lion-theme: -1");
   1.655 +  expression_should_not_be_parseable("-moz-windows-compositor: -1");
   1.656 +  expression_should_not_be_parseable("-moz-windows-classic: -1");
   1.657 +  expression_should_not_be_parseable("-moz-windows-glass: -1");
   1.658 +  expression_should_not_be_parseable("-moz-touch-enabled: -1");
   1.659 +  expression_should_not_be_parseable("-moz-swipe-animation-enabled: -1");
   1.660 +
   1.661 +  expression_should_not_be_parseable("-moz-scrollbar-start-backward: true");
   1.662 +  expression_should_not_be_parseable("-moz-scrollbar-start-forward: true");
   1.663 +  expression_should_not_be_parseable("-moz-scrollbar-end-backward: true");
   1.664 +  expression_should_not_be_parseable("-moz-scrollbar-end-forward: true");
   1.665 +  expression_should_not_be_parseable("-moz-scrollbar-thumb-proportional: true");
   1.666 +  expression_should_not_be_parseable("-moz-images-in-menus: true");
   1.667 +  expression_should_not_be_parseable("-moz-images-in-buttons: true");
   1.668 +  expression_should_not_be_parseable("-moz-overlay-scrollbars: true");
   1.669 +  expression_should_not_be_parseable("-moz-windows-default-theme: true");
   1.670 +  expression_should_not_be_parseable("-moz-mac-graphite-theme: true");
   1.671 +  expression_should_not_be_parseable("-moz-mac-lion-theme: true");
   1.672 +  expression_should_not_be_parseable("-moz-windows-compositor: true");
   1.673 +  expression_should_not_be_parseable("-moz-windows-classic: true");
   1.674 +  expression_should_not_be_parseable("-moz-windows-glass: true");
   1.675 +  expression_should_not_be_parseable("-moz-touch-enabled: true");
   1.676 +  expression_should_not_be_parseable("-moz-swipe-animation-enabled: true");
   1.677 +
   1.678 +  // windows theme media queries
   1.679 +  expression_should_be_parseable("-moz-windows-theme: aero");
   1.680 +  expression_should_be_parseable("-moz-windows-theme: aero-lite");
   1.681 +  expression_should_be_parseable("-moz-windows-theme: luna-blue");
   1.682 +  expression_should_be_parseable("-moz-windows-theme: luna-olive");
   1.683 +  expression_should_be_parseable("-moz-windows-theme: luna-silver");
   1.684 +  expression_should_be_parseable("-moz-windows-theme: royale");
   1.685 +  expression_should_be_parseable("-moz-windows-theme: generic");
   1.686 +  expression_should_be_parseable("-moz-windows-theme: zune");
   1.687 +  expression_should_be_parseable("-moz-windows-theme: garbage");
   1.688 +  expression_should_not_be_parseable("-moz-windows-theme: ''");
   1.689 +  expression_should_not_be_parseable("-moz-windows-theme: ");
   1.690 +
   1.691 +  // os version media queries (currently windows only)
   1.692 +  expression_should_be_parseable("-moz-os-version: windows-xp");
   1.693 +  expression_should_be_parseable("-moz-os-version: windows-vista");
   1.694 +  expression_should_be_parseable("-moz-os-version: windows-win7");
   1.695 +  expression_should_be_parseable("-moz-os-version: windows-win8");
   1.696 +  expression_should_not_be_parseable("-moz-os-version: ");
   1.697 +
   1.698 +  // OpenType SVG media features
   1.699 +  query_should_be_parseable("(-moz-is-glyph)");
   1.700 +  query_should_not_be_parseable("not (-moz-is-glyph)");
   1.701 +  query_should_not_be_parseable("only (-moz-is-glyph)");
   1.702 +  query_should_be_parseable("all and (-moz-is-glyph)");
   1.703 +  query_should_be_parseable("not all and (-moz-is-glyph)");
   1.704 +  query_should_be_parseable("only all and (-moz-is-glyph)");
   1.705 +
   1.706 +  query_should_be_parseable("(-moz-is-glyph:0)");
   1.707 +  query_should_not_be_parseable("not (-moz-is-glyph:0)");
   1.708 +  query_should_not_be_parseable("only (-moz-is-glyph:0)");
   1.709 +  query_should_be_parseable("all and (-moz-is-glyph:0)");
   1.710 +  query_should_be_parseable("not all and (-moz-is-glyph:0)");
   1.711 +  query_should_be_parseable("only all and (-moz-is-glyph:0)");
   1.712 +
   1.713 +  query_should_be_parseable("(-moz-is-glyph:1)");
   1.714 +  query_should_not_be_parseable("not (-moz-is-glyph:1)");
   1.715 +  query_should_not_be_parseable("only (-moz-is-glyph:1)");
   1.716 +  query_should_be_parseable("all and (-moz-is-glyph:1)");
   1.717 +  query_should_be_parseable("not all and (-moz-is-glyph:1)");
   1.718 +  query_should_be_parseable("only all and (-moz-is-glyph:1)");
   1.719 +
   1.720 +  query_should_not_be_parseable("(min--moz-is-glyph:0)");
   1.721 +  query_should_not_be_parseable("(max--moz-is-glyph:0)");
   1.722 +  query_should_not_be_parseable("(min--moz-is-glyph:1)");
   1.723 +  query_should_not_be_parseable("(max--moz-is-glyph:1)");
   1.724 +
   1.725 +  should_apply("not all and (-moz-is-glyph)");
   1.726 +  should_apply("(-moz-is-glyph:0)");
   1.727 +  should_apply("not all and (-moz-is-glyph:1)");
   1.728 +  should_apply("only all and (-moz-is-glyph:0)");
   1.729 +  should_not_apply("(-moz-is-glyph)");
   1.730 +  should_not_apply("(-moz-is-glyph:1)");
   1.731 +  should_not_apply("not all and (-moz-is-glyph:0)");
   1.732 +  should_not_apply("only all and (-moz-is-glyph:1)");
   1.733 +
   1.734 +  // Parsing tests
   1.735 +  // bug 454227
   1.736 +  should_apply_unbalanced("(orientation");
   1.737 +  should_not_apply_unbalanced("not all and (orientation");
   1.738 +  should_not_apply_unbalanced("(orientation:");
   1.739 +  should_apply_unbalanced("all,(orientation:");
   1.740 +  should_not_apply_unbalanced("(orientation:,all");
   1.741 +  should_apply_unbalanced("not all and (grid");
   1.742 +  should_not_apply_unbalanced("only all and (grid");
   1.743 +  should_not_apply_unbalanced("(grid");
   1.744 +  should_apply_unbalanced("all,(grid");
   1.745 +  should_not_apply_unbalanced("(grid,all");
   1.746 +  // bug 454226
   1.747 +  should_apply(",all");
   1.748 +  should_apply("all,");
   1.749 +  should_apply(",all,");
   1.750 +  should_apply("all,badmedium");
   1.751 +  should_apply("badmedium,all");
   1.752 +  should_not_apply(",badmedium,");
   1.753 +  should_apply("all,(badexpression)");
   1.754 +  should_apply("(badexpression),all");
   1.755 +  should_not_apply("(badexpression),badmedium");
   1.756 +  should_not_apply("badmedium,(badexpression)");
   1.757 +  should_apply("all,[badsyntax]");
   1.758 +  should_apply("[badsyntax],all");
   1.759 +  should_not_apply("badmedium,[badsyntax]");
   1.760 +  should_not_apply("[badsyntax],badmedium");
   1.761 +  // bug 528096
   1.762 +  should_not_apply_unbalanced("((resolution),all");
   1.763 +  should_not_apply_unbalanced("(resolution(),all");
   1.764 +  should_not_apply_unbalanced("(resolution (),all");
   1.765 +  should_not_apply_unbalanced("(resolution:(),all");
   1.766 +
   1.767 +  handle_posted_items();
   1.768 +}
   1.769 +
   1.770 +/*
   1.771 + * The cloning tests have to post tests that wait for onload.  However,
   1.772 + * we also make a bunch of state changes during the tests above.  So we
   1.773 + * always change state using the change_state call, with both makes the
   1.774 + * change immediately and posts an item in the same queue so that we
   1.775 + * make the same state change again later.
   1.776 + */
   1.777 +
   1.778 +var posted_items = [];
   1.779 +
   1.780 +function change_state(func)
   1.781 +{
   1.782 +  func();
   1.783 +  posted_items.push({state: func});
   1.784 +}
   1.785 +
   1.786 +function post_clone_test(docurl, testfunc)
   1.787 +{
   1.788 +  posted_items.push({docurl: docurl, testfunc: testfunc});
   1.789 +}
   1.790 +
   1.791 +function handle_posted_items()
   1.792 +{
   1.793 +  if (posted_items.length == 0) {
   1.794 +    SimpleTest.finish();
   1.795 +    return;
   1.796 +  }
   1.797 +
   1.798 +  if ("state" in posted_items[0]) {
   1.799 +    var item = posted_items.shift();
   1.800 +    item.state();
   1.801 +    handle_posted_items();
   1.802 +    return;
   1.803 +  }
   1.804 +
   1.805 +  var docurl = posted_items[0].docurl;
   1.806 +  iframe.onload = handle_iframe_onload;
   1.807 +  iframe.src = docurl;
   1.808 +}
   1.809 +
   1.810 +function handle_iframe_onload(event)
   1.811 +{
   1.812 +  if (event.target != iframe)
   1.813 +    return;
   1.814 +
   1.815 +  var item = posted_items.shift();
   1.816 +  item.testfunc();
   1.817 +  handle_posted_items();
   1.818 +}
   1.819 +
   1.820 +</script>
   1.821 +</pre>
   1.822 +</body>
   1.823 +</html>
   1.824 +
   1.825 +

mercurial