tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test.html (184124B)


      1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
      2 <html class="reftest-wait">
      3 <title>The Acid3 Test</title>
      4 <link rel="match" href="reference.sub.html">
      5 <script type="text/javascript">
      6  var startTime = new Date();
      7 </script>
      8 <style type="text/css">
      9 
     10  /* set some basic styles so that we can get reliably exact results */
     11  * { margin: 0; border: 1px blue; padding: 0; border-spacing: 0; font: inherit; line-height: 1.2; color: inherit; background: transparent; }
     12  :link, :visited { color: blue; }
     13 
     14  /* header and general layout */
     15  html { font: 20px Arial, sans-serif; border: 2cm solid gray; width: 32em; margin: 1em; }
     16  :root { background: silver; color: black; border-width: 0 0.2em 0.2em 0; } /* left and top content edges: 1*20px = 20px */
     17  body { padding: 2em 2em 0; background: url(data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAABGdBTUEAAK%2FINwWK6QAAAAlwSFlzAAAASAAAAEgARslrPgAAABtJREFUOMtj%2FM9APmCiQO%2Bo5lHNo5pHNVNBMwAinAEnIWw89gAAACJ6VFh0U29mdHdhcmUAAHjac0zJT0pV8MxNTE8NSk1MqQQAL5wF1K4MqU0AAAAASUVORK5CYII%3D) no-repeat 99.8392283% 1px white; border: solid 1px black; margin: -0.2em 0 0 -0.2em; } /* left and top content edges: 20px-0.2*20px+1px+2*20px = 57px */
     18  h1:first-child { cursor: help; font-size: 5em; font-weight: bolder; margin-bottom: -0.4em; text-shadow: rgba(192, 192, 192, 1.0) 3px 3px; } /* (left:57px, top:57px) */
     19  #result { font-weight: bolder; width: 5.68em; text-align: right; }
     20  #result { font-size: 5em; margin: -2.19em 0 0; } /* (right:57px+5.2*5*20px = 577px, top:57px+1.2*5*20px-0.4*5*20px+1px+1*40px+1*40px+1px+2*40px+150px-2.19*5*20px = 230px) */
     21  .hidden { visibility: hidden; }
     22  #slash { color: red; color: hsla(0, 0%, 0%, 1.0); }
     23  #instructions { margin-top: 0; font-size: 0.8em; color: gray; color: -acid3-bogus; height: 6.125em; } /* (left:57px, top:230px+1.2*5*20+0 = 350px) */
     24  #instructions { margin-right: -20px; padding-right: 20px; background: url(data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAABGdBTUEAAK%2FINwWK6QAAAAlwSFlzAAAASAAAAEgARslrPgAAABtJREFUOMtj%2FM9APmCiQO%2Bo5lHNo5pHNVNBMwAinAEnIWw89gAAACJ6VFh0U29mdHdhcmUAAHjac0zJT0pV8MxNTE8NSk1MqQQAL5wF1K4MqU0AAAAASUVORK5CYII%3D) no-repeat top right; }
     25  #instructions span { float: right; width: 20px; margin-right: -20px; background: white; height: 20px; }
     26  @font-face { font-family: "AcidAhemTest"; src: url(/fonts/Ahem.ttf); }
     27  map::after { position: absolute; top: 18px; left: 638px; content: "X"; background: fuchsia; color: white; font: 20px/1 AcidAhemTest; }
     28  iframe { float: left; height: 0; width: 0; } /* hide iframes but don't make them display: none */
     29  object { position: fixed; left: 130.5px; top: 84.3px; background: transparent; } /* show objects if they have content */
     30  .removed { position: absolute; top: 80px; left: 380px; height: 100px; width: 100px; opacity: 0; }
     31 
     32  /* set the line height of the line of coloured boxes so we can add them without the layout changing height */
     33  .buckets { font: 0/0 Arial, sans-serif; }
     34  .buckets { padding: 0 0 150px 3px; }
     35 
     36  /* the next two rules give the six coloured blocks their default styles (they match the same elements); the third hides them */
     37  :first-child + * .buckets p { display: inline-block; vertical-align: 2em; border: 2em dotted red; padding: 1.0em 0 1.0em 2em; }
     38  * + * > * > p { margin: 0; border: 1px solid ! important; }
     39  .z { visibility: hidden; } /* only matches the buckets with no score */
     40 
     41  /* sizes for the six buckets */
     42  #bucket1 { font-size: 20px; margin-left: 0.2em; padding-left: 1.3em; padding-right: 1.3em; margin-right: 0.0001px; }
     43  #bucket2 { font-size: 24px; margin-left: 0.375em; padding-left: 30px; padding-right: 32px; margin-right: 2px; }
     44  #bucket3 { font-size: 28px; margin-left: 8.9999px; padding-left: 17px; padding-right: 55px; margin-right: 12px; }
     45  #bucket4 { font-size: 32px; margin-left: 0; padding-left: 84px; padding-right: 0; margin-right: 0; }
     46  #bucket5 { font-size: 36px; margin-left: 13px; padding-left: 0; padding-right: 94px; margin-right: 25px; }
     47  #bucket6 { font-size: 40px; margin-left: -10px; padding-left: 104px; padding-right: -10px; }
     48 
     49  /* colours for them */
     50  .z, .zP, .zPP, .zPPP, .zPPPP, .zPPPPP { background: black; }
     51  .zPPPPPP, .zPPPPPPP, .zPPPPPPPP, .zPPPPPPPP, .zPPPPPPPPP,
     52  .zPPPPPPPPPP { background: grey; }
     53  .zPPPPPPPPPPP, .zPPPPPPPPPPPP, .zPPPPPPPPPPPPP,
     54  .zPPPPPPPPPPPPPP, .zPPPPPPPPPPPPPPP { background: silver; }
     55  #bucket1.zPPPPPPPPPPPPPPPP { background: red; }
     56  #bucket2.zPPPPPPPPPPPPPPPP { background: orange; }
     57  #bucket3.zPPPPPPPPPPPPPPPP { background: yellow; }
     58  #bucket4.zPPPPPPPPPPPPPPPP { background: lime; }
     59  #bucket5.zPPPPPPPPPPPPPPPP { background: blue; }
     60  #bucket6.zPPPPPPPPPPPPPPPP { background: purple; }
     61 
     62  /* The line-height for the .bucket div is worked out as follows:
     63   *
     64   * The div.bucket element has a line box with a few
     65   * inline-blocks. Each inline-block consists of:
     66   *
     67   *     2.0em vertical-align from baseline to bottom of inline-block
     68   *     1px bottom border
     69   *     1.0em bottom padding
     70   *     1.0em top padding
     71   *     1px top border
     72   *
     73   * The biggest inline-block has font-size: 40px.
     74   *
     75   * Thus the distance from the baseline to the top of the biggest
     76   * inline-block is (2em+1em+1em)*2em*20px+2px = 162px.
     77   *
     78   * The line box itself has no other contents, and its strut has zero
     79   * height and there is no half-leading, so the height of the
     80   * div.bucket is 162px.
     81   *
     82   * (Why use line-height:0 and font-size:0? Well:
     83   *
     84   * The div.bucket line box would have a height that is the maximum
     85   * of the following two sums:
     86   *
     87   *  1: half-leading + font descent at 1em + font ascent at 1em + half-leading
     88   *  2: half-leading + font descent at 1em + 162px
     89   *
     90   * Now the half-leading is (line-height - (font-ascent + font-descent))/2, so that is really:
     91   *
     92   *  1: (line-height - (font-ascent + font-descent))/2 + font descent + font ascent + (line-height - (font-ascent + font-descent))/2
     93   *  2: (line-height - (font-ascent + font-descent))/2 + font descent + 162px
     94   *
     95   * Which simplify to:
     96   *
     97   *  1: line-height
     98   *  2: line-height/2 + (font descent - font-ascent)/2 + 162px
     99   *
    100   * So if the following expression is true:
    101   *
    102   *    line-height > line-height/2 + (font descent - font-ascent)/2 + 162px
    103   *
    104   * That is, if this is true:
    105   *
    106   *    line-height > font descent - font-ascent + 324px
    107   *
    108   * ...then the line-height matters, otherwise the font does. Note
    109   * that font descent - font-ascent will be in the region of
    110   * 10px-30px (with Ahem, exactly 12px). However, if we make the
    111   * line-height big, then the _positioning_ of the inline-blocks will
    112   * depend on the font descent, since that is what will decide the
    113   * distance from the bottom of the line box to the baseline of the
    114   * block (since the baseline is set by the strut).
    115   *
    116   * However, in Acid2 a dependency on the font metrics was introduced
    117   * and this caused all kinds of problems. And we can't require Ahem
    118   * in the Acid tests, since it's unlikely most people will have it
    119   * installed.
    120   *
    121   * What we want is for the font to not matter, and the baseline to
    122   * be as high as possible. We can do that by saying that the font
    123   * and the line-height are zero.
    124   *
    125   * One word of warning. If your browser has a minimum font size feature
    126   * that forces font sizes up even when there is no text, you will need
    127   * to disable it before running this test.
    128   *
    129   */
    130 
    131  /* rules specific to the tests below */
    132  #instructions:last-child { white-space: pre-wrap; white-space: x-bogus; }
    133  /* replaced for http://dbaron.org/mozilla/visited-privacy with the three rules after it:
    134     #linktest:link { display: block; color: red; text-align: center; text-decoration: none; }
    135     #linktest.pending, #linktest:visited { display: none; } */
    136  #linktest { position: absolute; left: 17px; top: 18px; color: red; width: 80px; text-decoration: none; font: 900 small-caps 10px sans-serif; }
    137  #linktest:link { color: red; }
    138  #linktest.pending, #linktest:visited { color: white; }
    139  #\ { color: transparent; color: hsla(0, 0, 0, 1); position: fixed; top: 10px; left: 10px; font: 40px Arial, sans-serif; }
    140  #\  #result, #\  #score { position: fixed; top: 10%; left: 10%; width: 4em; z-index: 1; color: yellow; font-size: 50px; background: fuchsia; border: solid 1em purple; }
    141 </style>
    142 
    143 <!-- part of the HTTP tests -->
    144 <link rel="stylesheet" href="empty.css"><!-- text/html file (should be ignored, <h1> will go red if it isn't) -->
    145 
    146 <!-- the next five script blocks are part of one of the tests -->
    147 <script type="text/javascript">
    148  var d1 = "fail";
    149  var d2 = "fail";
    150  var d3 = "fail";
    151  var d4 = "fail";
    152  var d5 = "fail";
    153 </script>
    154 <script type="text/javascript" src="data:text/javascript,d1%20%3D%20'one'%3B"></script>
    155 <script type="text/javascript" src="data:text/javascript;base64,ZDIgPSAndHdvJzs%3D"></script>
    156 <script type="text/javascript" src="data:text/javascript;base64,%5a%44%4d%67%50%53%41%6e%64%47%68%79%5a%57%55%6e%4f%77%3D%3D"></script>
    157 <script type="text/javascript" src="data:text/javascript;base64,%20ZD%20Qg%0D%0APS%20An%20Zm91cic%0D%0A%207%20"></script>
    158 <script type="text/javascript" src="data:text/javascript,d5%20%3D%20'five%5Cu0027s'%3B"></script>
    159 
    160 <!-- part of the JS regexp and \0 value tests test -->
    161 <script type="text/javascript">
    162  var nullInRegexpArgumentResult = 0 < /script/.test('\0script') ? "passed" : "failed";
    163 </script>
    164 
    165 <!-- main test body -->
    166 <script type="text/javascript">
    167  var notifications = {};
    168  function notify(file) {
    169    // used in cross-file tests
    170    notifications[file] = 1;
    171  }
    172  function fail(message) {
    173    throw { message: message };
    174  }
    175  function assert(condition, message) {
    176    if (!condition)
    177      fail(message);
    178  }
    179  function assertEquals(expression, value, message) {
    180    if (expression != value) {
    181      expression = (""+expression).replace(/[\r\n]+/g, "\\n");
    182      value = (""+value).replace(/\r?\n/g, "\\n");
    183      fail("expected '" + value + "' but got '" + expression + "' - " + message);
    184    }
    185  }
    186  function getTestDocument() {
    187    var iframe = document.getElementById("selectors");
    188    var doc = iframe.contentDocument;
    189    for (var i = doc.documentElement.childNodes.length-1; i >= 0; i -= 1)
    190      doc.documentElement.removeChild(doc.documentElement.childNodes[i]);
    191    doc.documentElement.appendChild(doc.createElement('head'));
    192    doc.documentElement.firstChild.appendChild(doc.createElement('title'));
    193    doc.documentElement.appendChild(doc.createElement('body'));
    194    return doc;
    195  }
    196  function selectorTest(tester) {
    197    var doc = getTestDocument();
    198    var style = doc.createElement('style');
    199    style.appendChild(doc.createTextNode("* { z-index: 0; position: absolute; }\n"));
    200    doc.documentElement.firstChild.appendChild(style);
    201    var ruleCount = 0;
    202    tester(doc, function (selector) {
    203        ruleCount += 1;
    204        style.appendChild(doc.createTextNode(selector + " { z-index: " + ruleCount + "; }\n"));
    205        return ruleCount;
    206      }, function(node, rule, message) {
    207        var value = doc.defaultView.getComputedStyle(node, "").zIndex;
    208        assert(value != 'auto', "underlying problems prevent this test from running properly");
    209        assertEquals(value, rule, message);
    210    });
    211  }
    212  var kungFuDeathGrip = null; // used to hold things from test to test
    213  var tests = [
    214 
    215    // there are 6 buckets with 16 tests each, plus four special tests (0, 97, 98, and 99).
    216 
    217    // Remove the "JS required" message and the <script> element in the <body>
    218    function () {
    219      // test 0: whether removing an element that is the last child correctly recomputes styles for the new last child
    220      // also tests support for getComputedStyle, :last-child, pre-wrap, removing a <script> element
    221      // removing script:
    222      var scripts = document.getElementsByTagName('script');
    223      document.body.removeChild(scripts[scripts.length-1]);
    224      // removing last child:
    225      var last = document.getElementById('remove-last-child-test');
    226      var penultimate = last.previousSibling; // this should be the whitespace node
    227      penultimate = penultimate.previousSibling; // this should now be the actual penultimate element
    228      last.parentNode.removeChild(last);
    229      assertEquals(document.defaultView.getComputedStyle(penultimate, '').whiteSpace, 'pre-wrap', "found unexpected computed style");
    230      return 7;
    231    },
    232 
    233    // bucket 1: DOM Traversal, DOM Range, HTTP
    234    // DOM Traversal
    235    function () {
    236      // test 1: NodeFilters and Exceptions
    237      var doc = getTestDocument(); // looks like <!DOCTYPE><html><head><title/><\head><body/><\html>     (the '\'s are to avoid validation errors)
    238      var iteration = 0;
    239      var exception = "Roses";
    240      var test = function(node) {
    241        iteration += 1;
    242        switch (iteration) {
    243          case 1: case 3: case 4: case 6: case 7: case 8: case 9: case 14: case 15: throw exception;
    244          case 2: case 5: case 10: case 11: case 12: case 13: return true; // ToNumber(true) => 1
    245          default: throw 0;
    246        };
    247      };
    248      var check = function(o, method) {
    249        var ok = false;
    250        try {
    251          o[method]();
    252        } catch (e) {
    253          if (e === exception)
    254            ok = true;
    255        }
    256        assert(ok, "method " + o + "." + method + "() didn't forward exception");
    257      };
    258      var i = doc.createNodeIterator(doc.documentElement, 0xFFFFFFFF, test, true);
    259      check(i, "nextNode"); // 1
    260      assertEquals(i.nextNode(), doc.documentElement, "i.nextNode() didn't return the right node"); // 2
    261      check(i, "previousNode"); // 3
    262      var w = document.createTreeWalker(doc.documentElement, 0xFFFFFFFF, test, true);
    263      check(w, "nextNode"); // 4
    264      assertEquals(w.nextNode(), doc.documentElement.firstChild, "w.nextNode() didn't return the right node"); // 5
    265      check(w, "previousNode"); // 6
    266      check(w, "firstChild"); // 7
    267      check(w, "lastChild"); // 8
    268      check(w, "nextSibling"); // 9
    269      assertEquals(iteration, 9, "iterations went wrong");
    270      assertEquals(w.previousSibling(), null, "w.previousSibling() didn't return the right node"); // doesn't call filter
    271      assertEquals(iteration, 9, "filter called incorrectly for previousSibling()");
    272      assertEquals(w.lastChild(), doc.getElementsByTagName('title')[0], "w.lastChild() didn't return the right node"); // 10
    273      assertEquals(w.nextSibling(), null, "w.nextSibling() didn't return the right node"); // 11 (filter called on parent, to see if it's included, otherwise it could skip that and find a nextsibling elsewhere)
    274      assertEquals(iteration, 11, "filter called incorrectly for nextSibling()");
    275      assertEquals(w.parentNode(), doc.documentElement.firstChild, "w.parentNode() didn't return the right node"); // 12
    276      assertEquals(w.nextSibling(), doc.documentElement.lastChild, "w.nextSibling() didn't return the right node"); // 13
    277      check(w, "previousSibling"); // 14
    278      check(w, "parentNode"); // 15
    279      return 1;
    280    },
    281    function () {
    282      // test 2: Removing nodes during iteration
    283      var count = 0;
    284      var expect = function(n, node1, node2) {
    285        count += 1;
    286        assert(n == count, "reached expectation " + n + " when expecting expectation " + count);
    287        assertEquals(node1, node2, "expectation " + count + " failed");
    288      };
    289      var doc = getTestDocument();
    290      var t1 = doc.body.appendChild(doc.createElement('t1'));
    291      var t2 = doc.body.appendChild(doc.createElement('t2'));
    292      var t3 = doc.body.appendChild(doc.createElement('t3'));
    293      var t4 = doc.body.appendChild(doc.createElement('t4'));
    294      var callCount = 0;
    295      var filterFunctions = [
    296        function (node) { expect(1, node, doc.body); return true; }, // filter 0
    297        function (node) { expect(3, node, t1); return true; }, // filter 1
    298        function (node) { expect(5, node, t2); return true; }, // filter 2
    299        function (node) { expect(7, node, t3); doc.body.removeChild(t4); return true; }, // filter 3
    300        function (node) { expect(9, node, t4); return true; }, // filter 4
    301        function (node) { expect(11, node, t4); doc.body.removeChild(t4); return 2 /* REJECT */; }, // filter 5
    302        function (node) { expect(12, node, t3); return true; }, // filter 6
    303        function (node) { expect(14, node, t2); doc.body.removeChild(t2); return true; }, // filter 7
    304        function (node) { expect(16, node, t1); return true; }, // filter 8
    305      ];
    306      var i = doc.createNodeIterator(doc.documentElement.lastChild, 0xFFFFFFFF, function (node) { return filterFunctions[callCount++](node); }, true);
    307      // * B 1 2 3 4
    308      expect(2, i.nextNode(), doc.body); // filter 0
    309      // [B] * 1 2 3 4
    310      expect(4, i.nextNode(), t1); // filter 1
    311      // B [1] * 2 3 4
    312      expect(6, i.nextNode(), t2); // filter 2
    313      // B 1 [2] * 3 4
    314      expect(8, i.nextNode(), t3); // filter 3
    315      // B 1 2 [3] *
    316      doc.body.appendChild(t4);
    317      // B 1 2 [3] * 4
    318      expect(10, i.nextNode(), t4); // filter 4
    319      // B 1 2 3 [4] *
    320      expect(13, i.previousNode(), t3); // filters 5, 6
    321        // B 1 2 3 * (4) // filter 5
    322        // B 1 2 [3] *   // between 5 and 6
    323        // B 1 2 * (3)   // filter 6
    324      // B 1 2 * [3]
    325      expect(15, i.previousNode(), t2); // filter 7
    326        // B 1 * (2) [3]
    327        // -- spec says "For instance, if a NodeFilter removes a node
    328        //    from a document, it can still accept the node, which
    329        //    means that the node may be returned by the NodeIterator
    330        //    or TreeWalker even though it is no longer in the subtree
    331        //    being traversed."
    332        // -- but it also says "If changes to the iterated list do not
    333        //    remove the reference node, they do not affect the state
    334        //    of the NodeIterator."
    335      // B 1 * [3]
    336      expect(17, i.previousNode(), t1); // filter 8
    337      // B [1] * 3
    338      return 1;
    339    },
    340    function () {
    341      // test 3: the infinite iterator
    342      var doc = getTestDocument();
    343      for (var i = 0; i < 5; i += 1) {
    344        doc.body.appendChild(doc.createElement('section'));
    345        doc.body.lastChild.title = i;
    346      }
    347      var count = 0;
    348      var test = function() {
    349        if (count > 3 && count < 12)
    350          doc.body.appendChild(doc.body.firstChild);
    351        count += 1;
    352        return (count % 2 == 0) ? 1 : 2;
    353      };
    354      var i = doc.createNodeIterator(doc.body, 0xFFFFFFFF, test, true);
    355      assertEquals(i.nextNode().title, "0", "failure 1");
    356      assertEquals(i.nextNode().title, "2", "failure 2");
    357      assertEquals(i.nextNode().title, "4", "failure 3");
    358      assertEquals(i.nextNode().title, "1", "failure 4");
    359      assertEquals(i.nextNode().title, "3", "failure 5");
    360      assertEquals(i.nextNode().title, "0", "failure 6");
    361      assertEquals(i.nextNode().title, "2", "failure 7");
    362      assertEquals(i.nextNode(), null, "failure 8");
    363      return 1;
    364    },
    365    function () {
    366      // test 4: ignoring whitespace text nodes with node iterators
    367      var count = 0;
    368      var expect = function(node1, node2) {
    369        count += 1;
    370        assertEquals(node1, node2, "expectation " + count + " failed");
    371      };
    372      var allButWS = function (node) {
    373        if (node.nodeType == 3 && node.data.match(/^\s*$/))
    374          return 2;
    375        return 1;
    376      };
    377      var i = document.createNodeIterator(document.body, 0x01 | 0x04 | 0x08 | 0x10 | 0x20, allButWS, true);
    378      // now walk the document body and make sure everything is in the right place
    379      expect(i.nextNode(), document.body); // 1
    380      expect(i.nextNode(), document.getElementsByTagName('h1')[0]);
    381      expect(i.nextNode(), document.getElementsByTagName('h1')[0].firstChild);
    382      expect(i.nextNode(), document.getElementsByTagName('div')[0]);
    383      expect(i.nextNode(), document.getElementById('bucket1'));
    384      expect(i.nextNode(), document.getElementById('bucket2'));
    385      expect(i.nextNode(), document.getElementById('bucket3'));
    386      expect(i.nextNode(), document.getElementById('bucket4'));
    387      expect(i.nextNode(), document.getElementById('bucket5'));
    388      expect(i.nextNode(), document.getElementById('bucket6')); // 10
    389      expect(i.nextNode(), document.getElementById('result'));
    390      expect(i.nextNode(), document.getElementById('score'));
    391      expect(i.nextNode(), document.getElementById('score').firstChild);
    392      expect(i.nextNode(), document.getElementById('slash'));
    393      expect(i.nextNode(), document.getElementById('slash').firstChild);
    394      expect(i.nextNode(), document.getElementById('slash').nextSibling);
    395      expect(i.nextNode(), document.getElementById('slash').nextSibling.firstChild);
    396      expect(i.nextNode(), document.getElementsByTagName('map')[0]);
    397      expect(i.nextNode(), document.getElementsByTagName('area')[0]);
    398      expect(i.nextNode(), document.getElementsByTagName('iframe')[0]); // 20
    399      expect(i.nextNode(), document.getElementsByTagName('iframe')[0].firstChild);
    400      expect(i.nextNode(), document.getElementsByTagName('iframe')[1]);
    401      expect(i.nextNode(), document.getElementsByTagName('iframe')[1].firstChild);
    402      expect(i.nextNode(), document.getElementsByTagName('iframe')[2]);
    403      expect(i.nextNode(), document.forms[0]);
    404      expect(i.nextNode(), document.forms.form.elements[0]);
    405      expect(i.nextNode(), document.getElementsByTagName('table')[0]);
    406      expect(i.nextNode(), document.getElementsByTagName('tbody')[0]);
    407      expect(i.nextNode(), document.getElementsByTagName('tr')[0]);
    408      expect(i.nextNode(), document.getElementsByTagName('td')[0]);
    409      expect(i.nextNode(), document.getElementsByTagName('td')[0].getElementsByTagName('p')[0]);
    410      expect(i.nextNode(), document.getElementById('instructions'));
    411      expect(i.nextNode(), document.getElementById('instructions').firstChild);
    412      expect(i.nextNode().nodeName, "SPAN");
    413      expect(i.nextNode().nodeName, "#text");
    414      expect(i.nextNode(), document.links[1]);
    415      expect(i.nextNode(), document.links[1].firstChild);
    416      expect(i.nextNode(), document.getElementById('instructions').lastChild);
    417      expect(i.nextNode(), null);
    418      // walk it backwards for good measure
    419      expect(i.previousNode(), document.getElementById('instructions').lastChild);
    420      expect(i.previousNode(), document.links[1].firstChild);
    421      expect(i.previousNode(), document.links[1]);
    422      expect(i.previousNode().nodeName, "#text");
    423      expect(i.previousNode().nodeName, "SPAN");
    424      expect(i.previousNode(), document.getElementById('instructions').firstChild);
    425      expect(i.previousNode(), document.getElementById('instructions'));
    426      expect(i.previousNode(), document.getElementsByTagName('td')[0].getElementsByTagName('p')[0]);
    427      expect(i.previousNode(), document.getElementsByTagName('td')[0]);
    428      expect(i.previousNode(), document.getElementsByTagName('tr')[0]);
    429      expect(i.previousNode(), document.getElementsByTagName('tbody')[0]);
    430      expect(i.previousNode(), document.getElementsByTagName('table')[0]);
    431      expect(i.previousNode(), document.forms.form.elements[0]);
    432      expect(i.previousNode(), document.forms[0]);
    433      expect(i.previousNode(), document.getElementsByTagName('iframe')[2]);
    434      expect(i.previousNode(), document.getElementsByTagName('iframe')[1].firstChild);
    435      expect(i.previousNode(), document.getElementsByTagName('iframe')[1]);
    436      expect(i.previousNode(), document.getElementsByTagName('iframe')[0].firstChild);
    437      expect(i.previousNode(), document.getElementsByTagName('iframe')[0]); // 20
    438      expect(i.previousNode(), document.getElementsByTagName('area')[0]);
    439      expect(i.previousNode(), document.getElementsByTagName('map')[0]);
    440      expect(i.previousNode(), document.getElementById('slash').nextSibling.firstChild);
    441      expect(i.previousNode(), document.getElementById('slash').nextSibling);
    442      expect(i.previousNode(), document.getElementById('slash').firstChild);
    443      expect(i.previousNode(), document.getElementById('slash'));
    444      expect(i.previousNode(), document.getElementById('score').firstChild);
    445      expect(i.previousNode(), document.getElementById('score'));
    446      expect(i.previousNode(), document.getElementById('result'));
    447      expect(i.previousNode(), document.getElementById('bucket6'));
    448      expect(i.previousNode(), document.getElementById('bucket5'));
    449      expect(i.previousNode(), document.getElementById('bucket4'));
    450      expect(i.previousNode(), document.getElementById('bucket3'));
    451      expect(i.previousNode(), document.getElementById('bucket2'));
    452      expect(i.previousNode(), document.getElementById('bucket1'));
    453      expect(i.previousNode(), document.getElementsByTagName('div')[0]);
    454      expect(i.previousNode(), document.getElementsByTagName('h1')[0].firstChild);
    455      expect(i.previousNode(), document.getElementsByTagName('h1')[0]);
    456      expect(i.previousNode(), document.body);
    457      expect(i.previousNode(), null);
    458      return 1;
    459    },
    460    function () {
    461      // test 5: ignoring whitespace text nodes with tree walkers
    462      var count = 0;
    463      var expect = function(node1, node2) {
    464        count += 1;
    465        assertEquals(node1, node2, "expectation " + count + " failed");
    466      };
    467      var allButWS = function (node) {
    468        if (node.nodeType == 3 && node.data.match(/^\s*$/))
    469          return 3;
    470        return 1;
    471      };
    472      var w = document.createTreeWalker(document.body, 0x01 | 0x04 | 0x08 | 0x10 | 0x20, allButWS, true);
    473      expect(w.currentNode, document.body);
    474      expect(w.parentNode(), null);
    475      expect(w.currentNode, document.body);
    476      expect(w.firstChild(), document.getElementsByTagName('h1')[0]);
    477      expect(w.firstChild().nodeType, 3);
    478      expect(w.parentNode(), document.getElementsByTagName('h1')[0]);
    479      expect(w.nextSibling().previousSibling.nodeType, 3);
    480      expect(w.nextSibling(), document.getElementsByTagName('p')[6]);
    481      expect(w.nextSibling(), document.getElementsByTagName('map')[0]);
    482      expect(w.lastChild(), document.getElementsByTagName('table')[0]);
    483      expect(w.lastChild(), document.getElementsByTagName('tbody')[0]);
    484      expect(w.nextNode(), document.getElementsByTagName('tr')[0]);
    485      expect(w.nextNode(), document.getElementsByTagName('td')[0]);
    486      expect(w.nextNode(), document.getElementsByTagName('p')[7]);
    487      expect(w.nextNode(), document.getElementsByTagName('p')[8]); // instructions.inc paragraph
    488      expect(w.previousSibling(), document.getElementsByTagName('map')[0]);
    489      expect(w.previousNode().data, "100");
    490      expect(w.parentNode().tagName, "SPAN");
    491      expect(w.parentNode(), document.getElementById('result'));
    492      expect(w.parentNode(), document.body);
    493      expect(w.lastChild().id, "instructions");
    494      expect(w.lastChild().data.substr(0,1), ".");
    495      expect(w.previousNode(), document.links[1].firstChild);
    496      return 1;
    497    },
    498    function () {
    499      // test 6: walking outside a tree
    500      var doc = getTestDocument();
    501      var p = doc.createElement('p');
    502      doc.body.appendChild(p);
    503      var b = doc.body;
    504      var w = document.createTreeWalker(b, 0xFFFFFFFF, null, true);
    505      assertEquals(w.currentNode, b, "basic use of TreeWalker failed: currentNode");
    506      assertEquals(w.lastChild(), p, "basic use of TreeWalker failed: lastChild()");
    507      assertEquals(w.previousNode(), b, "basic use of TreeWalker failed: previousNode()");
    508      doc.documentElement.removeChild(b);
    509      assertEquals(w.lastChild(), p, "TreeWalker failed after removing the current node from the tree");
    510      assertEquals(w.nextNode(), null, "failed to walk into the end of a subtree");
    511      doc.documentElement.appendChild(p);
    512      assertEquals(w.previousNode(), doc.getElementsByTagName('title')[0], "failed to handle regrafting correctly");
    513      p.appendChild(b);
    514      assertEquals(w.nextNode(), p, "couldn't retrace steps");
    515      assertEquals(w.nextNode(), b, "couldn't step back into root");
    516      assertEquals(w.previousNode(), null, "root didn't retake its rootish position");
    517      return 1;
    518    },
    519 
    520    // DOM Range
    521    function () {
    522      // test 7: basic ranges tests
    523      var r = document.createRange();
    524      assert(r, "range not created");
    525      assert(r.collapsed, "new range wasn't collapsed");
    526      assertEquals(r.commonAncestorContainer, document, "new range's common ancestor wasn't the document");
    527      assertEquals(r.startContainer, document, "new range's start container wasn't the document");
    528      assertEquals(r.startOffset, 0, "new range's start offset wasn't zero");
    529      assertEquals(r.endContainer, document, "new range's end container wasn't the document");
    530      assertEquals(r.endOffset, 0, "new range's end offset wasn't zero");
    531      assert(r.cloneContents(), "cloneContents() didn't return an object");
    532      assertEquals(r.cloneContents().childNodes.length, 0, "nothing cloned was more than nothing");
    533      assertEquals(r.cloneRange().toString(), "", "nothing cloned stringifed to more than nothing");
    534      r.collapse(true); // no effect
    535      assertEquals(r.compareBoundaryPoints(r.START_TO_END, r.cloneRange()), 0, "starting boundary point of range wasn't the same as the end boundary point of the clone range");
    536      r.deleteContents(); // no effect
    537      assertEquals(r.extractContents().childNodes.length, 0, "nothing removed was more than nothing");
    538      var endOffset = r.endOffset;
    539      r.insertNode(document.createComment("commented inserted to test ranges"));
    540      r.setEnd(r.endContainer, endOffset + 1); // added to work around spec bug that smaug is blocking the errata for
    541      try {
    542        assert(!r.collapsed, "range with inserted comment is collapsed");
    543        assertEquals(r.commonAncestorContainer, document, "range with inserted comment has common ancestor that isn't the document");
    544        assertEquals(r.startContainer, document, "range with inserted comment has start container that isn't the document");
    545        assertEquals(r.startOffset, 0, "range with inserted comment has start offset that isn't zero");
    546        assertEquals(r.endContainer, document, "range with inserted comment has end container that isn't the document");
    547        assertEquals(r.endOffset, 1, "range with inserted comment has end offset that isn't after the comment");
    548      } finally {
    549        document.removeChild(document.firstChild);
    550      }
    551      return 1;
    552    },
    553    function () {
    554      // test 8: moving boundary points
    555      var doc = document.implementation.createDocument(null, null, null);
    556      var root = doc.createElement("root");
    557      doc.appendChild(root);
    558      var e1 = doc.createElement("e");
    559      root.appendChild(e1);
    560      var e2 = doc.createElement("e");
    561      root.appendChild(e2);
    562      var e3 = doc.createElement("e");
    563      root.appendChild(e3);
    564      var r = doc.createRange();
    565      r.setStart(e2, 0);
    566      r.setEnd(e3, 0);
    567      assert(!r.collapsed, "non-empty range claims to be collapsed");
    568      r.setEnd(e1, 0);
    569      assert(r.collapsed, "setEnd() didn't collapse the range");
    570      assertEquals(r.startContainer, e1, "startContainer is wrong after setEnd()");
    571      assertEquals(r.startOffset, 0, "startOffset is wrong after setEnd()");
    572      assertEquals(r.endContainer, e1, "endContainer is wrong after setEnd()");
    573      assertEquals(r.endOffset, 0, "endOffset is wrong after setEnd()");
    574      r.setStartBefore(e3);
    575      assert(r.collapsed, "setStartBefore() didn't collapse the range");
    576      assertEquals(r.startContainer, root, "startContainer is wrong after setStartBefore()");
    577      assertEquals(r.startOffset, 2, "startOffset is wrong after setStartBefore()");
    578      assertEquals(r.endContainer, root, "endContainer is wrong after setStartBefore()");
    579      assertEquals(r.endOffset, 2, "endOffset is wrong after setStartBefore()");
    580      r.setEndAfter(root);
    581      assert(!r.collapsed, "setEndAfter() didn't uncollapse the range");
    582      assertEquals(r.startContainer, root, "startContainer is wrong after setEndAfter()");
    583      assertEquals(r.startOffset, 2, "startOffset is wrong after setEndAfter()");
    584      assertEquals(r.endContainer, doc, "endContainer is wrong after setEndAfter()");
    585      assertEquals(r.endOffset, 1, "endOffset is wrong after setEndAfter()");
    586      r.setStartAfter(e2);
    587      assert(!r.collapsed, "setStartAfter() collapsed the range");
    588      assertEquals(r.startContainer, root, "startContainer is wrong after setStartAfter()");
    589      assertEquals(r.startOffset, 2, "startOffset is wrong after setStartAfter()");
    590      assertEquals(r.endContainer, doc, "endContainer is wrong after setStartAfter()");
    591      assertEquals(r.endOffset, 1, "endOffset is wrong after setStartAfter()");
    592      var msg = '';
    593      try {
    594        r.setEndBefore(doc);
    595        msg = "no exception thrown for setEndBefore() the document itself";
    596      } catch (e) {
    597 // COMMENTED OUT FOR 2011 UPDATE - we may want to merge RangeException and DOMException
    598 //        if (e.BAD_BOUNDARYPOINTS_ERR != 1)
    599 //          msg = 'not a RangeException';
    600 //        else
    601 //        if (e.INVALID_NODE_TYPE_ERR != 2)
    602 //          msg = 'RangeException has no INVALID_NODE_TYPE_ERR';
    603 //        else
    604 //        if ("INVALID_ACCESS_ERR" in e)
    605 //          msg = 'RangeException has DOMException constants';
    606 //        else
    607        if (e.code != e.INVALID_NODE_TYPE_ERR)
    608          msg = 'wrong exception raised from setEndBefore()';
    609      }
    610      assert(msg == "", msg);
    611      assert(!r.collapsed, "setEndBefore() collapsed the range");
    612      assertEquals(r.startContainer, root, "startContainer is wrong after setEndBefore()");
    613      assertEquals(r.startOffset, 2, "startOffset is wrong after setEndBefore()");
    614      assertEquals(r.endContainer, doc, "endContainer is wrong after setEndBefore()");
    615      assertEquals(r.endOffset, 1, "endOffset is wrong after setEndBefore()");
    616      r.collapse(false);
    617      assert(r.collapsed, "collapse() collapsed the range");
    618      assertEquals(r.startContainer, doc, "startContainer is wrong after collapse()");
    619      assertEquals(r.startOffset, 1, "startOffset is wrong after collapse()");
    620      assertEquals(r.endContainer, doc, "endContainer is wrong after collapse()");
    621      assertEquals(r.endOffset, 1, "endOffset is wrong after collapse()");
    622      r.selectNodeContents(root);
    623      assert(!r.collapsed, "collapsed is wrong after selectNodeContents()");
    624      assertEquals(r.startContainer, root, "startContainer is wrong after selectNodeContents()");
    625      assertEquals(r.startOffset, 0, "startOffset is wrong after selectNodeContents()");
    626      assertEquals(r.endContainer, root, "endContainer is wrong after selectNodeContents()");
    627      assertEquals(r.endOffset, 3, "endOffset is wrong after selectNodeContents()");
    628      r.selectNode(e2);
    629      assert(!r.collapsed, "collapsed is wrong after selectNode()");
    630      assertEquals(r.startContainer, root, "startContainer is wrong after selectNode()");
    631      assertEquals(r.startOffset, 1, "startOffset is wrong after selectNode()");
    632      assertEquals(r.endContainer, root, "endContainer is wrong after selectNode()");
    633      assertEquals(r.endOffset, 2, "endOffset is wrong after selectNode()");
    634      return 1;
    635    },
    636    function () {
    637      // test 9: extractContents() in a Document
    638      var doc = getTestDocument();
    639      var h1 = doc.createElement('h1');
    640      var t1 = doc.createTextNode('Hello ');
    641      h1.appendChild(t1);
    642      var em = doc.createElement('em');
    643      var t2 = doc.createTextNode('Wonderful');
    644      em.appendChild(t2);
    645      h1.appendChild(em);
    646      var t3 = doc.createTextNode(' Kitty');
    647      h1.appendChild(t3);
    648      doc.body.appendChild(h1);
    649      var p = doc.createElement('p');
    650      var t4 = doc.createTextNode('How are you?');
    651      p.appendChild(t4);
    652      doc.body.appendChild(p);
    653      var r = doc.createRange();
    654      r.selectNodeContents(doc);
    655      assertEquals(r.toString(), "Hello Wonderful KittyHow are you?", "toString() on range selecting Document gave wrong output");
    656      r.setStart(t2, 6);
    657      r.setEnd(p, 0);
    658      // <body><h1>Hello <em>Wonder ful<\em> Kitty<\h1><p> How are you?<\p><\body>     (the '\'s are to avoid validation errors)
    659      //                           ^----------------------^
    660      assertEquals(r.toString(), "ful Kitty", "toString() on range crossing text nodes gave wrong output");
    661      var f = r.extractContents();
    662      // <h1><em>ful<\em> Kitty<\h1><p><\p>
    663      // ccccccccccccccccMMMMMMcccccccccccc
    664      assertEquals(f.nodeType, 11, "failure 1");
    665      assert(f.childNodes.length == 2, "expected two children in the result, got " + f.childNodes.length);
    666      assertEquals(f.childNodes[0].tagName, "H1", "failure 3");
    667      assert(f.childNodes[0] != h1, "failure 4");
    668      assertEquals(f.childNodes[0].childNodes.length, 2, "failure 5");
    669      assertEquals(f.childNodes[0].childNodes[0].tagName, "EM", "failure 6");
    670      assert(f.childNodes[0].childNodes[0] != em, "failure 7");
    671      assertEquals(f.childNodes[0].childNodes[0].childNodes.length, 1, "failure 8");
    672      assertEquals(f.childNodes[0].childNodes[0].childNodes[0].data, "ful", "failure 9");
    673      assert(f.childNodes[0].childNodes[0].childNodes[0] != t2, "failure 10");
    674      assertEquals(f.childNodes[0].childNodes[1], t3, "failure 11");
    675      assert(f.childNodes[0].childNodes[1] != em, "failure 12");
    676      assertEquals(f.childNodes[1].tagName, "P", "failure 13");
    677      assertEquals(f.childNodes[1].childNodes.length, 0, "failure 14");
    678      assert(f.childNodes[1] != p, "failure 15");
    679      return 1;
    680    },
    681    function () {
    682      // test 10: Ranges and Attribute Nodes
    683 // COMMENTED OUT FOR 2011 UPDATE - turns out instead of dropping Attr entirely, as Acid3 originally expected, the API is just being refactored
    684 //      var e = document.getElementById('result');
    685 //      if (!e.getAttributeNode)
    686 //        return 1; // support for attribute nodes is optional in Acid3, because attribute nodes might be removed from DOM Core in the future.
    687 //      // however, if they're supported, they'd better work:
    688 //      var a = e.getAttributeNode('id');
    689 //      var r = document.createRange();
    690 //      r.selectNodeContents(a);
    691 //      assertEquals(r.toString(), "result", "toString() didn't work for attribute node");
    692 //      var t = a.firstChild;
    693 //      var f = r.extractContents();
    694 //      assertEquals(f.childNodes.length, 1, "extracted contents were the wrong length");
    695 //      assertEquals(f.childNodes[0], t, "extracted contents were the wrong node");
    696 //      assertEquals(t.textContent, 'result', "extracted contents didn't match old attribute value");
    697 //      assertEquals(r.toString(), '', "extracting contents didn't empty attribute value; instead equals '" + r.toString() + "'");
    698 //      assertEquals(e.getAttribute('id'), '', "extracting contents didn't change 'id' attribute to empty string");
    699 //      e.id = 'result';
    700      return 1;
    701    },
    702    function () {
    703      // test 11: Ranges and Comments
    704      var msg;
    705      var doc = getTestDocument();
    706      var c1 = doc.createComment("11111");
    707      doc.appendChild(c1);
    708      var r = doc.createRange();
    709      r.selectNode(c1);
    710      msg = 'wrong exception raised';
    711      try {
    712        r.surroundContents(doc.createElement('a'));
    713        msg = 'no exception raised';
    714      } catch (e) {
    715        if ('code' in e)
    716          msg += '; code = ' + e.code;
    717        if (e.code == 3) // HIERARCHY_REQUEST_ERR
    718          msg = '';
    719      }
    720      assert(msg == '', "when inserting <a> into Document with another child: " + msg);
    721      var c2 = doc.createComment("22222");
    722      doc.body.appendChild(c2);
    723      var c3 = doc.createComment("33333");
    724      doc.body.appendChild(c3);
    725      r.setStart(c2, 2);
    726      r.setEnd(c3, 3);
    727      var msg = 'wrong exception raised';
    728      try {
    729        r.surroundContents(doc.createElement('a'));
    730        msg = 'no exception raised';
    731      } catch (e) {
    732 // COMMENTED OUT FOR 2011 UPDATE - DOM Core changes the exception from RangeException.BAD_BOUNDARYPOINTS_ERR (1) to DOMException.INVALID_STATE_ERR (11)
    733 //        if ('code' in e)
    734 //          msg += '; code = ' + e.code;
    735 //        if (e.code == 1)
    736          msg = '';
    737      }
    738      assert(msg == '', "when trying to surround two halves of comment: " + msg);
    739      assertEquals(r.toString(), "", "comments returned text");
    740      return 1;
    741    },
    742    function () {
    743      // test 12: Ranges under mutations: insertion into text nodes
    744      var doc = getTestDocument();
    745      var p = doc.createElement('p');
    746      var t1 = doc.createTextNode('12345');
    747      p.appendChild(t1);
    748      var t2 = doc.createTextNode('ABCDE');
    749      p.appendChild(t2);
    750      doc.body.appendChild(p);
    751      var r = doc.createRange();
    752      r.setStart(p.firstChild, 2);
    753      r.setEnd(p.firstChild, 3);
    754      assert(!r.collapsed, "collapsed is wrong at start");
    755      assertEquals(r.commonAncestorContainer, p.firstChild, "commonAncestorContainer is wrong at start");
    756      assertEquals(r.startContainer, p.firstChild, "startContainer is wrong at start");
    757      assertEquals(r.startOffset, 2, "startOffset is wrong at start");
    758      assertEquals(r.endContainer, p.firstChild, "endContainer is wrong at start");
    759      assertEquals(r.endOffset, 3, "endOffset is wrong at start");
    760      assertEquals(r.toString(), "3", "range in text node stringification failed");
    761      r.insertNode(p.lastChild);
    762      assertEquals(p.childNodes.length, 3, "insertion of node made wrong number of child nodes");
    763      assertEquals(p.childNodes[0], t1, "unexpected first text node");
    764      assertEquals(p.childNodes[0].data, "12", "unexpected first text node contents");
    765      assertEquals(p.childNodes[1], t2, "unexpected second text node");
    766      assertEquals(p.childNodes[1].data, "ABCDE", "unexpected second text node");
    767      assertEquals(p.childNodes[2].data, "345", "unexpected third text node contents");
    768      // The spec is very vague about what exactly should be in the range afterwards:
    769      // the insertion results in a splitText(), which it says is equivalent to a truncation
    770      // followed by an insertion, but it doesn't say what to do when you have a truncation,
    771      // so we don't know where either the start or the end boundary points end up.
    772      // The spec really should be clarified for how to handle splitText() and
    773      // text node truncation in general
    774      // The only thing that seems very clear is that the inserted text node should
    775      // be in the range, and it has to be at the start, since insertion always puts it at
    776      // the start.
    777      assert(!r.collapsed, "collapsed is wrong after insertion");
    778      assert(r.toString().match(/^ABCDE/), "range didn't start with the expected text; range stringified to '" + r.toString() + "'");
    779      return 1;
    780    },
    781    function () {
    782      // test 13: Ranges under mutations: deletion
    783      var doc = getTestDocument();
    784      var p = doc.createElement('p');
    785      p.appendChild(doc.createTextNode("12345"));
    786      doc.body.appendChild(p);
    787      var r = doc.createRange();
    788      r.setEnd(doc.body, 1);
    789      r.setStart(p.firstChild, 2);
    790      assert(!r.collapsed, "collapsed is wrong at start");
    791      assertEquals(r.commonAncestorContainer, doc.body, "commonAncestorContainer is wrong at start");
    792      assertEquals(r.startContainer, p.firstChild, "startContainer is wrong at start");
    793      assertEquals(r.startOffset, 2, "startOffset is wrong at start");
    794      assertEquals(r.endContainer, doc.body, "endContainer is wrong at start");
    795      assertEquals(r.endOffset, 1, "endOffset is wrong at start");
    796      doc.body.removeChild(p);
    797      assert(r.collapsed, "collapsed is wrong after deletion");
    798      assertEquals(r.commonAncestorContainer, doc.body, "commonAncestorContainer is wrong after deletion");
    799      assertEquals(r.startContainer, doc.body, "startContainer is wrong after deletion");
    800      assertEquals(r.startOffset, 0, "startOffset is wrong after deletion");
    801      assertEquals(r.endContainer, doc.body, "endContainer is wrong after deletion");
    802      assertEquals(r.endOffset, 0, "endOffset is wrong after deletion");
    803      return 1;
    804    },
    805 
    806    // HTTP
    807    function () {
    808      // test 14: HTTP - Content-Type: image/png
    809      assert(!notifications['empty.png'], "privilege escalation security bug: PNG ran script");
    810      var iframe = document.getElementsByTagName('iframe')[0];
    811      assert(iframe, "no <iframe> support");
    812      if (iframe && iframe.contentDocument) {
    813        var ps = iframe.contentDocument.getElementsByTagName('p');
    814        if (ps.length > 0) {
    815          if (ps[0].firstChild && ps[0].firstChild.data && ps[0].firstChild.data == 'FAIL')
    816            fail("PNG was parsed as HTML.");
    817        }
    818      }
    819      return 1;
    820    },
    821    function () {
    822      // test 15: HTTP - Content-Type: text/plain
    823      assert(!notifications['empty.txt'], "privilege escalation security bug: text file ran script");
    824      var iframe = document.getElementsByTagName('iframe')[1];
    825      assert(iframe, "no <iframe> support");
    826      if (iframe && iframe.contentDocument) {
    827        var ps = iframe.contentDocument.getElementsByTagName('p');
    828        if (ps.length > 0) {
    829          if (ps[0].firstChild && ps[0].firstChild.data && ps[0].firstChild.data == 'FAIL')
    830            fail("text/plain file was parsed as HTML");
    831        }
    832      }
    833      return 1;
    834    },
    835    function () {
    836      // test 16: <object> handling and HTTP status codes
    837      var oC = document.createElement('object');
    838      oC.appendChild(document.createTextNode("FAIL"));
    839      var oB = document.createElement('object');
    840      var oA = document.createElement('object');
    841      oA.data = "support-a.png?pipe=status(404)";
    842      oB.data = "support-b.png";
    843      oB.appendChild(oC);
    844      oC.data = "support-c.png";
    845      oA.appendChild(oB);
    846      document.getElementsByTagName("map")[0].appendChild(oA);
    847      // assuming the above didn't raise any exceptions, this test has passed
    848      // (the real test is whether the rendering is correct)
    849      return 1;
    850    },
    851 
    852    // bucket 2: DOM2 Core and DOM2 Events
    853    // Core
    854    function () {
    855      // test 17: hasAttribute
    856      // missing attribute
    857      assert(!document.getElementsByTagName('map')[0].hasAttribute('id'), "hasAttribute failure for 'id' on map");
    858      // implied attribute
    859      assert(!document.getElementsByTagName('form')[0].hasAttribute('method'), "hasAttribute failure for 'method' on form");
    860      // actually present attribute
    861      assert(document.getElementsByTagName('form')[0].hasAttribute('action'), "hasAttribute failure for 'action' on form");
    862      assertEquals(document.getElementsByTagName('form')[0].getAttribute('action'), '', "attribute 'action' on form has wrong value");
    863      return 2;
    864    },
    865    function () {
    866      // test 18: nodeType (this test also relies on accurate parsing of the document)
    867      assertEquals(document.nodeType, 9, "document nodeType wrong");
    868      assertEquals(document.documentElement.nodeType, 1, "element nodeType wrong");
    869 // COMMENTED OUT FOR 2011 UPDATE - turns out instead of dropping Attr entirely, as Acid3 originally expected, the API is just being refactored
    870 //      if (document.createAttribute) // support for attribute nodes is optional in Acid3, because attribute nodes might be removed from DOM Core in the future.
    871 //        assertEquals(document.createAttribute('test').nodeType, 2, "attribute nodeType wrong"); // however, if they're supported, they'd better work
    872      assertEquals(document.getElementById('score').firstChild.nodeType, 3, "text node nodeType wrong");
    873      assertEquals(document.firstChild.nodeType, 10, "DOCTYPE nodeType wrong");
    874      return 2;
    875    },
    876    function () {
    877      // test 19: value of constants
    878      var e = null;
    879      try {
    880        document.body.appendChild(document.documentElement);
    881         // raises a HIERARCHY_REQUEST_ERR
    882      } catch (err) {
    883        e = err;
    884      }
    885      assertEquals(document.DOCUMENT_FRAGMENT_NODE, 11, "document DOCUMENT_FRAGMENT_NODE constant missing or wrong");
    886      assertEquals(document.body.COMMENT_NODE, 8, "element COMMENT_NODE constant missing or wrong");
    887      assertEquals(document.createTextNode('').ELEMENT_NODE, 1, "text node ELEMENT_NODE constant missing or wrong");
    888      assert(e.HIERARCHY_REQUEST_ERR == 3, "exception HIERARCHY_REQUEST_ERR constant missing or wrong")
    889      assertEquals(e.code, 3, "incorrect exception raised from appendChild()");
    890      return 2;
    891    },
    892    function () {
    893      // test 20: nulls bytes in various places
    894      assert(!document.getElementById('bucket1\0error'), "null in getElementById() probably terminated string");
    895      var ok = true;
    896      try {
    897        document.createElement('form\0div');
    898        ok = false;
    899      } catch (e) {
    900        if (e.code != 5)
    901          ok = false;
    902      }
    903      assert(ok, "didn't raise the right exception for null byte in createElement()");
    904      return 2;
    905    },
    906    function () {
    907      // test 21: basic namespace stuff
    908      var element = document.createElementNS('http://ns.example.com/', 'prefix:localname');
    909      assertEquals(element.tagName, 'prefix:localname', "wrong tagName");
    910      assertEquals(element.nodeName, 'prefix:localname', "wrong nodeName");
    911      assertEquals(element.prefix, 'prefix', "wrong prefix");
    912      assertEquals(element.localName, 'localname', "wrong localName");
    913      assertEquals(element.namespaceURI, 'http://ns.example.com/', "wrong namespaceURI");
    914      return 2;
    915    },
    916    function () {
    917      // test 22: createElement() with invalid tag names
    918      var test = function (name) {
    919        var result;
    920        try {
    921          var div = document.createElement(name);
    922        } catch (e) {
    923          result = e;
    924        }
    925        assert(result, "no exception for createElement('" + name + "')");
    926        assertEquals(result.code, 5, "wrong exception for createElement('" + name + "')"); // INVALID_CHARACTER_ERR
    927      }
    928      test('<div>');
    929      test('0div');
    930      test('di v');
    931      test('-div');
    932      test('.div');
    933      return 2;
    934    },
    935    function () {
    936      // test 23: createElementNS() with invalid tag names
    937      var test = function (name, ns, code) {
    938        var result;
    939        try {
    940          var div = document.createElementNS(ns, name);
    941        } catch (e) {
    942          result = e;
    943        }
    944        assert(result, "no exception for createElementNS('" + ns + "', '" + name + "')");
    945        assertEquals(result.code, code, "wrong exception for createElementNS('" + ns + "', '" + name + "')");
    946      }
    947      test('<div>', null, 5);
    948      test('0div', null, 5);
    949      test('di v', null, 5);
    950      test('-div', null, 5);
    951      test('.div', null, 5);
    952      test('<div>', "http://example.com/", 5);
    953      test('0div', "http://example.com/", 5);
    954      test('-div', "http://example.com/", 5);
    955      test('.div', "http://example.com/", 5);
    956      //test(':div', null, 14);
    957      //test(':div', "http://example.com/", 14);
    958      test('d:iv', null, 14);
    959      test('xml:test', "http://example.com/", 14);
    960      test('xmlns:test', "http://example.com/", 14); // (technically a DOM3 Core test)
    961      test('x:test', "http://www.w3.org/2000/xmlns/", 14); // (technically a DOM3 Core test)
    962      document.createElementNS("http://www.w3.org/2000/xmlns/", 'xmlns:test'); // (technically a DOM3 Core test)
    963      return 2;
    964    },
    965    function () {
    966      // test 24: event handler attributes
    967      assertEquals(document.body.getAttribute('onload'), "update()  /* this attribute's value is tested in one of the tests */ ", "onload value wrong");
    968      return 2;
    969    },
    970    function () {
    971      // test 25: test namespace checking in createDocumentType, and
    972      // check that exceptions that are thrown are DOMException objects
    973      var message = "";
    974      try {
    975        document.implementation.createDocumentType('a>', '', ''); /* doesn't contain an illegal character; is malformed */
    976        message = "failed to raise exception";
    977      } catch (e) {
    978        /*if (e.code != e.NAMESPACE_ERR)
    979          message = "wrong exception";
    980        else if (e.INVALID_ACCESS_ERR != 15)
    981          message = "exceptions don't have all the constants";*/
    982      }
    983      if (message)
    984        fail(message);
    985      return 2;
    986    },
    987    function () {
    988      // test 26: check that document tree survives while still accessible
    989      var d;
    990      // e1 - an element that's in a document
    991      d = document.implementation.createDocument(null, null, null);
    992      var e1 = d.createElement('test');
    993      d.appendChild(d.createElement('root'));
    994      d.documentElement.appendChild(e1);
    995      assert(e1.parentNode, "e1 - parent element doesn't exist");
    996      assert(e1.parentNode.ownerDocument, "e1 - document doesn't exist");
    997      // e2 - an element that's not in a document
    998      d = document.implementation.createDocument(null, null, null);
    999      var e2 = d.createElement('test');
   1000      d.createElement('root').appendChild(e2);
   1001      assert(e2.parentNode, "e2 - parent element doesn't exist");
   1002      assert(e2.parentNode.ownerDocument, "e2 - document doesn't exist");
   1003      // now try to decouple them
   1004      d = null;
   1005      kungFuDeathGrip = [e1, e2];
   1006      assert(e1.parentNode, "e1 - parent element doesn't exist after dropping reference to document");
   1007      assert(e1.parentNode.ownerDocument, "e1 - document doesn't exist after dropping reference to document");
   1008      assert(e2.parentNode, "e2 - parent element doesn't exist after dropping reference to document");
   1009      assert(e2.parentNode.ownerDocument, "e2 - document doesn't exist after dropping reference to document");
   1010      var loops = new Date().valueOf() * 2.813435e-9 - 2412; // increases linearly over time
   1011      for (var i = 0; i < loops; i += 1) {
   1012        // we want to force a GC here, so we use up lots of memory
   1013        // we take the opportunity to sneak in a perf test to make DOM and JS stuff faster...
   1014        d = new Date();
   1015        d = new (function (x) { return { toString: function () { return x.toString() } } })(d.valueOf());
   1016        d = document.createTextNode("iteration " + i + " at " + d);
   1017        document.createElement('a').appendChild(d);
   1018        d = d.parentNode;
   1019        document.body.insertBefore(d, document.getElementById('bucket1').parentNode);
   1020        assert(document.getElementById('bucket2').nextSibling.parentNode.previousSibling.firstChild.data.match(/AT\W/i), "iteration " + i + " failed");
   1021        d.setAttribute('class', d.textContent);
   1022        document.body.removeChild(d);
   1023      }
   1024      assert(e1.parentNode, "e1 - parent element doesn't exist after looping");
   1025      assert(e1.parentNode.ownerDocument, "e1 - document doesn't exist after looping");
   1026      assertEquals(e1.parentNode.ownerDocument.nodeType, 9, "e1 - document node type has wrong node type");
   1027      assert(e2.parentNode, "e2 - parent element doesn't exist after looping");
   1028      assert(e2.parentNode.ownerDocument, "e2 - document doesn't exist after looping");
   1029      assertEquals(e2.parentNode.ownerDocument.nodeType, 9, "e2 - document node type has wrong node type");
   1030      return 2;
   1031    },
   1032    function () {
   1033      // test 27: a continuation of the previous test
   1034      var e1 = kungFuDeathGrip[0];
   1035      var e2 = kungFuDeathGrip[1];
   1036      kungFuDeathGrip = null;
   1037      assert(e1, "e1 - element itself didn't survive across tests");
   1038      assert(e1.parentNode, "e1 - parent element doesn't exist after waiting");
   1039      assert(e1.parentNode.ownerDocument, "e1 - document doesn't exist after waiting");
   1040      assertEquals(e1.parentNode.ownerDocument.nodeType, 9, "e1 - document node type has wrong node type after waiting");
   1041      assert(e2, "e2 - element itself didn't survive across tests");
   1042      assert(e2.parentNode, "e2 - parent element doesn't exist after waiting");
   1043      assert(e2.parentNode.ownerDocument, "e2 - document doesn't exist after waiting");
   1044      assertEquals(e2.parentNode.ownerDocument.nodeType, 9, "e2 - document node type has wrong node type after waiting");
   1045      return 2;
   1046    },
   1047    function () {
   1048      // test 28: getElementById()
   1049      // ...and name=""
   1050      assert(document.getElementById('form') !== document.getElementsByTagName('form')[0], "getElementById() searched on 'name'");
   1051      // ...and a space character as the ID
   1052      var div = document.createElement('div');
   1053      div.appendChild(document.createTextNode('FAIL'));
   1054      div.id = " ";
   1055      document.body.appendChild(div); // it's hidden by CSS
   1056      assert(div === document.getElementById(" "), "getElementById() didn't return the right element");
   1057      return 2;
   1058    },
   1059    function () {
   1060      // test 29: check that whitespace survives cloning
   1061      var t1 = document.getElementsByTagName('table')[0];
   1062      var t2 = t1.cloneNode(true);
   1063      assertEquals(t2.tBodies[0].rows[0].cells[0].firstChild.tagName, 'P', "<p> didn't clone right");
   1064      assertEquals(t2.tBodies[0].rows[0].cells[0].firstChild.childNodes.length, 0, "<p> got child nodes after cloning");
   1065      assertEquals(t2.childNodes.length, 2, "cloned table had wrong number of children");
   1066      assertEquals(t2.lastChild.data, " ", "cloned table lost whitespace text node");
   1067      return 2;
   1068    },
   1069 
   1070    // Events
   1071    function () {
   1072      // test 30: dispatchEvent()
   1073      var count = 0;
   1074      var ok = true;
   1075      var test = function (event) {
   1076        if (event.detail != 6)
   1077          ok = false;
   1078        count++;
   1079      };
   1080      // test event listener addition
   1081      document.getElementById('result').addEventListener('test', test, false);
   1082      // test event creation
   1083      var event = document.createEvent('UIEvents');
   1084      event.initUIEvent('test', true, false, null, 6);
   1085      // test event dispatch on elements and text nodes
   1086      assert(document.getElementById('score').dispatchEvent(event), "dispatchEvent #1 failed");
   1087      assert(document.getElementById('score').nextSibling.dispatchEvent(event), "dispatchEvent #2 failed");
   1088      // test event listener removal
   1089      document.getElementById('result').removeEventListener('test', test, false);
   1090      assert(document.getElementById('score').dispatchEvent(event), "dispatchEvent #3 failed");
   1091      assertEquals(count, 2, "unexpected number of events handled");
   1092      assert(ok, "unexpected events handled");
   1093      return 2;
   1094    },
   1095    function () {
   1096      // test 31: event.stopPropagation() and capture
   1097      // we're going to use an input element because we can cause events to bubble from it
   1098      var input = document.createElement('input');
   1099      var div = document.createElement('div');
   1100      div.appendChild(input);
   1101      document.body.appendChild(div);
   1102      // the test will consist of two event handlers:
   1103      var ok = true;
   1104      var captureCount = 0;
   1105      var testCapture = function (event) {
   1106        ok = ok &&
   1107             (event.type == 'click') &&
   1108             (event.target == input) &&
   1109             (event.currentTarget == div) &&
   1110             (event.eventPhase == 1) &&
   1111             (event.bubbles) &&
   1112             (event.cancelable);
   1113        captureCount++;
   1114        event.stopPropagation(); // this shouldn't stop it from firing both times on the div element
   1115      };
   1116      var testBubble = function (event) {
   1117        ok = false;
   1118      };
   1119      // one of which is added twice:
   1120      div.addEventListener('click', function (event) { testCapture(event) }, true);
   1121      div.addEventListener('click', function (event) { testCapture(event) }, true);
   1122      div.addEventListener('click', testBubble, false);
   1123      // we cause an event to bubble like this:
   1124      input.type = 'reset';
   1125      input.click();
   1126      // cleanup afterwards
   1127      document.body.removeChild(div);
   1128      // capture handler should have been called twice
   1129      assertEquals(captureCount, 2, "capture handler called the wrong number of times");
   1130      assert(ok, "capture handler called incorrectly");
   1131      return 2;
   1132    },
   1133    function () {
   1134      // test 32: events bubbling through Document node
   1135      // event handler:
   1136      var ok = true;
   1137      var count = 0;
   1138      var test = function (event) {
   1139        count += 1;
   1140        if (event.eventPhase != 3)
   1141          ok = false;
   1142      }
   1143      // register event handler
   1144      document.body.addEventListener('click', test, false);
   1145      // create an element that bubbles an event, and bubble it
   1146      var input = document.createElement('input');
   1147      var div = document.createElement('div');
   1148      div.appendChild(input);
   1149      document.body.appendChild(div);
   1150      input.type = 'reset';
   1151      input.click();
   1152      // unregister event handler
   1153      document.body.removeEventListener('click', test, false);
   1154      // check that it's removed for good
   1155      input.click();
   1156      // remove the newly added elements
   1157      document.body.removeChild(div);
   1158      assertEquals(count, 1, "capture handler called the wrong number of times");
   1159      assert(ok, "capture handler called incorrectly");
   1160      return 2;
   1161    },
   1162 
   1163    // bucket 3: DOM2 Views, DOM2 Style, and Selectors
   1164    function () {
   1165      // test 33: basic tests for selectors - classes, attributes
   1166      var p;
   1167      var builder = function(doc) {
   1168        p = doc.createElement("p");
   1169        doc.body.appendChild(p);
   1170      };
   1171      selectorTest(function (doc, add, expect) {
   1172        builder(doc);
   1173        p.className = "selectorPingTest";
   1174        var good = add(".selectorPingTest");
   1175        add(".SelectorPingTest");
   1176        add(".selectorpingtest");
   1177        expect(doc.body, 0, "failure 1");
   1178        expect(p, good, "failure 2");
   1179      });
   1180      selectorTest(function (doc, add, expect) {
   1181        builder(doc);
   1182        p.className = 'a\u0020b\u0009c\u000Ad\u000De\u000Cf\u2003g\u3000h';
   1183        var good = add(".a.b.c.d.e.f\\2003g\\3000h");
   1184        expect(p, good, "whitespace error in class processing");
   1185      });
   1186      selectorTest(function (doc, add, expect) {
   1187        builder(doc);
   1188        p.className = "selectorPingTest";
   1189        var good = add("[class=selectorPingTest]");
   1190        add("[class=SelectorPingTest]");
   1191        add("[class=selectorpingtest]");
   1192        expect(doc.body, 0, "failure 3");
   1193        expect(p, good, "class attribute matching failed");
   1194      });
   1195      selectorTest(function (doc, add, expect) {
   1196        builder(doc);
   1197        p.className = "selectorPingTest";
   1198        var good = add("[title=selectorPingTest]");
   1199        add("[title=SelectorPingTest]");
   1200        add("[title=selectorpingtest]");
   1201        expect(doc.body, 0, "failure 4");
   1202        expect(p, 0, "failure 5");
   1203        p.title = "selectorPingTest";
   1204        expect(doc.body, 0, "failure 6");
   1205        expect(p, good, "failure 7");
   1206      });
   1207      selectorTest(function (doc, add, expect) {
   1208        builder(doc);
   1209        p.setAttribute('align', 'right and left');
   1210        var good = add("[align=\"right and left\"]");
   1211        add("[align=left]");
   1212        add("[align=right]");
   1213        expect(p, good, "align attribute mismatch");
   1214      });
   1215      return 3;
   1216    },
   1217    function () {
   1218      // test 34: :lang() and [|=]
   1219      var div1;
   1220      var div2;
   1221      var p;
   1222      var builder = function(doc) {
   1223        div1 = doc.createElement('div');
   1224        div1.setAttribute("lang", "english");
   1225        div1.setAttribute("class", "widget-tree");
   1226        doc.body.appendChild(div1);
   1227        div2 = doc.createElement('div');
   1228        div2.setAttribute("lang", "en-GB");
   1229        div2.setAttribute("class", "WIDGET");
   1230        doc.body.appendChild(div2);
   1231        p = doc.createElement('p');
   1232        div2.appendChild(p);
   1233      };
   1234      selectorTest(function (doc, add, expect) {
   1235        builder(doc);
   1236        var lang_en = add(":lang(en)");
   1237        expect(div1, 0, "lang=english should not be matched by :lang(en)");
   1238        expect(div2, lang_en, "lang=en-GB should be matched by :lang(en)");
   1239        expect(p, lang_en, "descendants inheriting lang=en-GB should be matched by :lang(en)");
   1240      });
   1241      selectorTest(function (doc, add, expect) {
   1242        builder(doc);
   1243        var class_widget = add("[class|=widget]");
   1244        expect(div1, class_widget, "class attribute should be supported by |= attribute selectors");
   1245        expect(div2, 0, "class attribute is case-sensitive");
   1246      });
   1247      return 3;
   1248    },
   1249    function () {
   1250      // test 35: :first-child
   1251      selectorTest(function (doc, add, expect) {
   1252        var notFirst = 0;
   1253        var first = add(":first-child");
   1254        var p1 = doc.createElement("p");
   1255        doc.body.appendChild(doc.createTextNode(" TEST "));
   1256        doc.body.appendChild(p1);
   1257        //expect(doc.documentElement, notFirst, "root element, with no parent node, claims to be a :first-child");
   1258        expect(doc.documentElement.firstChild, first, "first child of root node didn't match :first-child");
   1259        expect(doc.documentElement.firstChild.firstChild, first, "failure 3");
   1260        expect(doc.body, notFirst, "failure 4");
   1261        expect(p1, first, "failure 5");
   1262        var p2 = doc.createElement("p");
   1263        doc.body.appendChild(p2);
   1264        expect(doc.body, notFirst, "failure 6");
   1265        expect(p1, first, "failure 7");
   1266        expect(p2, notFirst, "failure 8");
   1267        var p0 = doc.createElement("p");
   1268        doc.body.insertBefore(p0, p1);
   1269        expect(doc.body, notFirst, "failure 9");
   1270        expect(p0, first, "failure 10");
   1271        expect(p1, notFirst, ":first-child still applies to element that was previously a first child");
   1272        expect(p2, notFirst, "failure 12");
   1273        doc.body.insertBefore(p0, p2);
   1274        expect(doc.body, notFirst, "failure 13");
   1275        expect(p1, first, "failure 14");
   1276        expect(p0, notFirst, "failure 15");
   1277        expect(p2, notFirst, "failure 16");
   1278      });
   1279      return 3;
   1280    },
   1281    function () {
   1282      // test 36: :last-child
   1283      var p1;
   1284      var p2;
   1285      var builder = function(doc) {
   1286        p1 = doc.createElement('p');
   1287        p2 = doc.createElement('p');
   1288        doc.body.appendChild(p1);
   1289        doc.body.appendChild(p2);
   1290      };
   1291      selectorTest(function (doc, add, expect) {
   1292        builder(doc);
   1293        var last = add(":last-child");
   1294        expect(p1, 0, "control test for :last-child failed");
   1295        expect(p2, last, "last child did not match :last-child");
   1296        doc.body.appendChild(p1);
   1297        expect(p2, 0, ":last-child matched element with a following sibling");
   1298        expect(p1, last, "failure 4");
   1299        p1.appendChild(p2);
   1300        expect(p2, last, "failure 5");
   1301        expect(p1, last, "failure 6");
   1302      });
   1303      selectorTest(function (doc, add, expect) {
   1304        builder(doc);
   1305        var last = add(":last-child");
   1306        expect(p1, 0, "failure 7");
   1307        expect(p2, last, "failure 8");
   1308        doc.body.insertBefore(p2, p1);
   1309        expect(p2, 0, "failure 9");
   1310        expect(p1, last, "failure 10");
   1311      });
   1312      selectorTest(function (doc, add, expect) {
   1313        builder(doc);
   1314        var last = add(":last-child");
   1315        expect(p1, 0, "failure 11");
   1316        expect(p2, last, "failure 12");
   1317        doc.body.removeChild(p2);
   1318        expect(p1, last, "failure 13");
   1319        assertEquals(p1.nextSibling, null, "failure 14");
   1320        assertEquals(p2.parentNode, null, "failure 15");
   1321      });
   1322      return 3;
   1323    },
   1324    function () {
   1325      // test 37: :only-child
   1326      var p1;
   1327      var p2;
   1328      var builder = function(doc) {
   1329        p1 = doc.createElement('p');
   1330        p2 = doc.createElement('p');
   1331        doc.body.appendChild(p1);
   1332        doc.body.appendChild(p2);
   1333      };
   1334      selectorTest(function (doc, add, expect) {
   1335        builder(doc);
   1336        var only = add(":only-child");
   1337        expect(p1, 0, "control test for :only-child failed");
   1338        expect(p2, 0, "failure 2");
   1339        doc.body.removeChild(p2);
   1340        expect(p1, only, ":only-child did not match only child");
   1341        p1.appendChild(p2);
   1342        expect(p2, only, "failure 4");
   1343        expect(p1, only, "failure 5");
   1344      });
   1345      selectorTest(function (doc, add, expect) {
   1346        builder(doc);
   1347        var only = add(":only-child");
   1348        expect(p1, 0, "failure 6");
   1349        expect(p2, 0, "failure 7");
   1350        doc.body.removeChild(p1);
   1351        expect(p2, only, "failure 8");
   1352        p2.appendChild(p1);
   1353        expect(p2, only, "failure 9");
   1354        expect(p1, only, "failure 10");
   1355      });
   1356      selectorTest(function (doc, add, expect) {
   1357        builder(doc);
   1358        var only = add(":only-child");
   1359        expect(p1, 0, "failure 11");
   1360        expect(p2, 0, "failure 12");
   1361        var span1 = doc.createElement('span');
   1362        p1.appendChild(span1);
   1363        expect(p1, 0, "failure 13");
   1364        expect(p2, 0, "failure 14");
   1365        expect(span1, only, "failure 15");
   1366        var span2 = doc.createElement('span');
   1367        p1.appendChild(span2);
   1368        expect(p1, 0, "failure 16");
   1369        expect(p2, 0, "failure 17");
   1370        expect(span1, 0, "failure 18");
   1371        expect(span2, 0, "failure 19");
   1372      });
   1373      selectorTest(function (doc, add, expect) {
   1374        builder(doc);
   1375        var only = add(":only-child");
   1376        expect(p1, 0, "failure 20");
   1377        expect(p2, 0, "failure 21");
   1378        var span1 = doc.createElement('span');
   1379        p2.appendChild(span1);
   1380        expect(p1, 0, "failure 22");
   1381        expect(p2, 0, "failure 23");
   1382        expect(span1, only, "failure 24");
   1383        var span2 = doc.createElement('span');
   1384        p2.insertBefore(span2, span1);
   1385        expect(p1, 0, "failure 25");
   1386        expect(p2, 0, "failure 26");
   1387        expect(span1, 0, "failure 27");
   1388        expect(span2, 0, "failure 28");
   1389      });
   1390      return 3;
   1391    },
   1392    function () {
   1393      // test 38: :empty
   1394      selectorTest(function (doc, add, expect) {
   1395        var empty = add(":empty");
   1396        var p = doc.createElement('p');
   1397        doc.body.appendChild(p);
   1398        expect(p, empty, "empty p element didn't match :empty");
   1399        var span = doc.createElement('span');
   1400        p.appendChild(span);
   1401        expect(p, 0, "adding children didn't stop the element matching :empty");
   1402        expect(span, empty, "empty span element didn't match :empty");
   1403        p.removeChild(span);
   1404        expect(p, empty, "removing all children didn't make the element match :empty");
   1405        p.appendChild(doc.createComment("c"));
   1406        p.appendChild(doc.createTextNode(""));
   1407        expect(p, empty, "element with a comment node and an empty text node didn't match :empty");
   1408        p.appendChild(doc.createTextNode(""));
   1409        expect(p, empty, "element with a comment node and two empty text nodes didn't match :empty");
   1410        p.lastChild.data = " ";
   1411        expect(p, 0, "adding text to a text node didn't make the element non-:empty");
   1412        assertEquals(p.childNodes.length, 3, "text nodes may have merged");
   1413 // COMMENTED OUT FOR 2011 UPDATE - replaceWholeText() might go away entirely
   1414 //        p.childNodes[1].replaceWholeText("");
   1415 //        assertEquals(p.childNodes.length, 1, "replaceWholeText('') didn't remove text nodes");
   1416 // REPLACEMENT:
   1417        assertEquals(p.childNodes[1].nodeType, 3, "missing text node before first removal");
   1418        p.removeChild(p.childNodes[1]);
   1419        assertEquals(p.childNodes[1].nodeType, 3, "missing text node before second removal");
   1420        p.removeChild(p.childNodes[1]);
   1421 // END REPLACEMENT TEST
   1422        expect(p, empty, "element with a comment node only didn't match :empty");
   1423        p.appendChild(doc.createElementNS("http://example.com/", "test"));
   1424        expect(p, 0, "adding an element in a namespace didn't make the element non-:empty");
   1425      });
   1426      return 3;
   1427    },
   1428    function () {
   1429      // test 39: :nth-child, :nth-last-child
   1430      var ps;
   1431      var builder = function(doc) {
   1432        ps = [
   1433          doc.createElement('p'),
   1434          doc.createElement('p'),
   1435          doc.createElement('p'),
   1436          doc.createElement('p'),
   1437          doc.createElement('p'),
   1438          doc.createElement('p'),
   1439          doc.createElement('p'),
   1440          doc.createElement('p'),
   1441          doc.createElement('p'),
   1442          doc.createElement('p'),
   1443          doc.createElement('p'),
   1444          doc.createElement('p'),
   1445          doc.createElement('p')
   1446        ];
   1447        for (var i = 0; i < ps.length; i += 1)
   1448          doc.body.appendChild(ps[i]);
   1449      };
   1450      selectorTest(function (doc, add, expect) {
   1451        builder(doc);
   1452        var match = add(":nth-child(odd)");
   1453        for (var i = 0; i < ps.length; i += 1)
   1454          expect(ps[i], i % 2 ? 0 : match, ":nth-child(odd) failed with child " + i);
   1455      });
   1456      selectorTest(function (doc, add, expect) {
   1457        builder(doc);
   1458        var match = add(":nth-child(even)");
   1459        for (var i = 0; i < ps.length; i += 1)
   1460          expect(ps[i], i % 2 ? match : 0 , ":nth-child(even) failed with child " + i);
   1461      });
   1462      selectorTest(function (doc, add, expect) {
   1463        builder(doc);
   1464        var match = add(":nth-child(odd)");
   1465        doc.body.removeChild(ps[5]);
   1466        for (var i = 0; i < 5; i += 1)
   1467          expect(ps[i], i % 2 ? 0 : match, ":nth-child(odd) failed after removal with child " + i);
   1468        for (var i = 6; i < ps.length; i += 1)
   1469          expect(ps[i], i % 2 ? match : 0, ":nth-child(odd) failed after removal with child " + i);
   1470      });
   1471      selectorTest(function (doc, add, expect) {
   1472        builder(doc);
   1473        var match = add(":nth-child(even)");
   1474        doc.body.removeChild(ps[5]);
   1475        for (var i = 0; i < 5; i += 1)
   1476          expect(ps[i], i % 2 ? match : 0, ":nth-child(even) failed after removal with child " + i);
   1477        for (var i = 6; i < ps.length; i += 1)
   1478          expect(ps[i], i % 2 ? 0 : match, ":nth-child(even) failed after removal with child " + i);
   1479      });
   1480      selectorTest(function (doc, add, expect) {
   1481        builder(doc);
   1482        var match = add(":nth-child(-n+3)");
   1483        for (var i = 0; i < 3; i += 1)
   1484          expect(ps[i], match, ":nth-child(-n+3) failed with child " + i);
   1485        for (var i = 3; i < ps.length; i += 1)
   1486          expect(ps[i], 0, ":nth-child(-n+3) failed with child " + i);
   1487      });
   1488      return 3;
   1489    },
   1490    function () {
   1491      // test 40: :first-of-type, :last-of-type, :only-of-type, :nth-of-type, :nth-last-of-type
   1492      var elements;
   1493      var builder = function(doc) {
   1494        elements = [
   1495          doc.createElement('p'),
   1496          doc.createElement('div'),
   1497          doc.createElement('div'),
   1498          doc.createElement('p'),
   1499          doc.createElement('p'),
   1500          doc.createElement('p'),
   1501          doc.createElement('div'),
   1502          doc.createElement('address'),
   1503          doc.createElement('div'),
   1504          doc.createElement('div'),
   1505          doc.createElement('div'),
   1506          doc.createElement('p'),
   1507          doc.createElement('div'),
   1508          doc.createElement('p')
   1509        ];
   1510        for (var i = 0; i < elements.length; i += 1)
   1511          doc.body.appendChild(elements[i]);
   1512      };
   1513      selectorTest(function (doc, add, expect) {
   1514        builder(doc);
   1515        var match = add(":first-of-type");
   1516        var values = [1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0];
   1517        for (var i = 0; i < elements.length; i += 1)
   1518          expect(elements[i], values[i] ? match : 0, "part 1:" + i);
   1519      });
   1520      selectorTest(function (doc, add, expect) {
   1521        builder(doc);
   1522        var match = add(":last-of-type");
   1523        var values = [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1];
   1524        for (var i = 0; i < elements.length; i += 1)
   1525          expect(elements[i], values[i] ? match : 0, "part 2:" + i);
   1526      });
   1527      selectorTest(function (doc, add, expect) {
   1528        builder(doc);
   1529        var match = add(":only-of-type");
   1530        var values = [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0];
   1531        for (var i = 0; i < elements.length; i += 1)
   1532          expect(elements[i], values[i] ? match : 0, "part 3:" + i);
   1533      });
   1534      selectorTest(function (doc, add, expect) {
   1535        builder(doc);
   1536        var match = add(":nth-of-type(3n-1)");
   1537        var values = [0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0];
   1538        for (var i = 0; i < elements.length; i += 1)
   1539          expect(elements[i], values[i] ? match : 0, "part 4:" + i);
   1540      });
   1541      selectorTest(function (doc, add, expect) {
   1542        builder(doc);
   1543        var match = add(":nth-of-type(3n+1)");
   1544        var values = [1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0];
   1545        for (var i = 0; i < elements.length; i += 1)
   1546          expect(elements[i], values[i] ? match : 0, "part 5:" + i);
   1547      });
   1548      selectorTest(function (doc, add, expect) {
   1549        builder(doc);
   1550        var match = add(":nth-last-of-type(2n)");
   1551        var values = [1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0];
   1552        for (var i = 0; i < elements.length; i += 1)
   1553          expect(elements[i], values[i] ? match : 0, "part 6:" + i);
   1554      });
   1555      selectorTest(function (doc, add, expect) {
   1556        builder(doc);
   1557        var match = add(":nth-last-of-type(-5n+3)");
   1558        var values;
   1559        values = [0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0];
   1560        for (var i = 0; i < elements.length; i += 1)
   1561          expect(elements[i], values[i] ? match : 0, "part 7:" + i);
   1562        doc.body.appendChild(doc.createElement('blockquote'));
   1563        values = [0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0];
   1564        for (var i = 0; i < elements.length; i += 1)
   1565          expect(elements[i], values[i] ? match : 0, "part 8:" + i);
   1566        doc.body.appendChild(doc.createElement('div'));
   1567        values = [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0];
   1568        for (var i = 0; i < elements.length; i += 1)
   1569          expect(elements[i], values[i] ? match : 0, "part 9:" + i);
   1570      });
   1571      return 3;
   1572    },
   1573    function () {
   1574      // test 41: :root, :not()
   1575      selectorTest(function (doc, add, expect) {
   1576        var match = add(":not(:root)");
   1577        var p = doc.createElement('p');
   1578        doc.body.appendChild(p);
   1579        expect(doc.documentElement, 0, "root was :not(:root)");
   1580        expect(doc.documentElement.childNodes[0], match,"head was not :not(:root)");
   1581        expect(doc.documentElement.childNodes[1], match,"body was not :not(:root)");
   1582        expect(doc.documentElement.childNodes[0].firstChild, match,"title was not :not(:root)");
   1583        expect(p, match,"p was not :not(:root)");
   1584      });
   1585      return 3;
   1586    },
   1587    function () {
   1588      // test 42: +, ~, >, and ' ' in dynamic situations
   1589      selectorTest(function (doc, add, expect) {
   1590        var div1 = doc.createElement('div');
   1591        div1.id = "div1";
   1592        doc.body.appendChild(div1);
   1593        var div2 = doc.createElement('div');
   1594        doc.body.appendChild(div2);
   1595        var div3 = doc.createElement('div');
   1596        doc.body.appendChild(div3);
   1597        var div31 = doc.createElement('div');
   1598        div3.appendChild(div31);
   1599        var div311 = doc.createElement('div');
   1600        div31.appendChild(div311);
   1601        var div3111 = doc.createElement('div');
   1602        div311.appendChild(div3111);
   1603        var match = add("#div1 ~ div div + div > div");
   1604        expect(div1, 0, "failure 1");
   1605        expect(div2, 0, "failure 2");
   1606        expect(div3, 0, "failure 3");
   1607        expect(div31, 0, "failure 4");
   1608        expect(div311, 0, "failure 5");
   1609        expect(div3111, 0, "failure 6");
   1610        var div310 = doc.createElement('div');
   1611        div31.insertBefore(div310, div311);
   1612        expect(div1, 0, "failure 7");
   1613        expect(div2, 0, "failure 8");
   1614        expect(div3, 0, "failure 9");
   1615        expect(div31, 0, "failure 10");
   1616        expect(div310, 0, "failure 11");
   1617        expect(div311, 0, "failure 12");
   1618        expect(div3111, match, "rule did not start matching after change");
   1619      });
   1620      selectorTest(function (doc, add, expect) {
   1621        var div1 = doc.createElement('div');
   1622        div1.id = "div1";
   1623        doc.body.appendChild(div1);
   1624        var div2 = doc.createElement('div');
   1625        div1.appendChild(div2);
   1626        var div3 = doc.createElement('div');
   1627        div2.appendChild(div3);
   1628        var div4 = doc.createElement('div');
   1629        div3.appendChild(div4);
   1630        var div5 = doc.createElement('div');
   1631        div4.appendChild(div5);
   1632        var div6 = doc.createElement('div');
   1633        div5.appendChild(div6);
   1634        var match = add("#div1 > div div > div");
   1635        expect(div1, 0, "failure 14");
   1636        expect(div2, 0, "failure 15");
   1637        expect(div3, 0, "failure 16");
   1638        expect(div4, match, "failure 17");
   1639        expect(div5, match, "failure 18");
   1640        expect(div6, match, "failure 19");
   1641        var p34 = doc.createElement('p');
   1642        div3.insertBefore(p34, div4);
   1643        p34.insertBefore(div4, null);
   1644        expect(div1, 0, "failure 20");
   1645        expect(div2, 0, "failure 21");
   1646        expect(div3, 0, "failure 22");
   1647        expect(p34, 0, "failure 23");
   1648        expect(div4, 0, "failure 24");
   1649        expect(div5, match, "failure 25");
   1650        expect(div6, match, "failure 26");
   1651      });
   1652      selectorTest(function (doc, add, expect) {
   1653        var div1 = doc.createElement('div');
   1654        div1.id = "div1";
   1655        doc.body.appendChild(div1);
   1656        var div2 = doc.createElement('div');
   1657        div1.appendChild(div2);
   1658        var div3 = doc.createElement('div');
   1659        div2.appendChild(div3);
   1660        var div4 = doc.createElement('div');
   1661        div3.appendChild(div4);
   1662        var div5 = doc.createElement('div');
   1663        div4.appendChild(div5);
   1664        var div6 = doc.createElement('div');
   1665        div5.appendChild(div6);
   1666        var match = add("#div1 > div div > div");
   1667        expect(div1, 0, "failure 27");
   1668        expect(div2, 0, "failure 28");
   1669        expect(div3, 0, "failure 29");
   1670        expect(div4, match, "failure 30");
   1671        expect(div5, match, "failure 31");
   1672        expect(div6, match, "failure 32");
   1673        var p23 = doc.createElement('p');
   1674        div2.insertBefore(p23, div3);
   1675        p23.insertBefore(div3, null);
   1676        expect(div1, 0, "failure 33");
   1677        expect(div2, 0, "failure 34");
   1678        expect(div3, 0, "failure 35");
   1679        expect(p23, 0, "failure 36");
   1680        expect(div4, match, "failure 37");
   1681        expect(div5, match, "failure 38");
   1682        expect(div6, match, "failure 39");
   1683      });
   1684      return 3;
   1685    },
   1686    function () {
   1687      // test 43: :enabled, :disabled, :checked, etc
   1688      selectorTest(function (doc, add, expect) {
   1689        var input = doc.createElement('input');
   1690        input.type = 'checkbox';
   1691        doc.body.appendChild(input);
   1692        var neither = 0;
   1693        var both = add(":checked:enabled");
   1694        var checked = add(":checked");
   1695        var enabled = add(":enabled");
   1696        expect(doc.body, neither, "control failure");
   1697        expect(input, enabled, "input element didn't match :enabled");
   1698        input.click();
   1699        expect(input, both, "input element didn't match :checked");
   1700        input.disabled = true;
   1701        expect(input, checked, "failure 3");
   1702        input.checked = false;
   1703        expect(input, neither, "failure 4");
   1704        expect(doc.body, neither, "failure 5");
   1705      });
   1706      selectorTest(function (doc, add, expect) {
   1707        var input1 = doc.createElement('input');
   1708        input1.type = 'radio';
   1709        input1.name = 'radio';
   1710        doc.body.appendChild(input1);
   1711        var input2 = doc.createElement('input');
   1712        input2.type = 'radio';
   1713        input2.name = 'radio';
   1714        doc.body.appendChild(input2);
   1715        var checked = add(":checked");
   1716        expect(input1, 0, "failure 6");
   1717        expect(input2, 0, "failure 7");
   1718        input2.click();
   1719        expect(input1, 0, "failure 6");
   1720        expect(input2, checked, "failure 7");
   1721        input1.checked = true;
   1722        expect(input1, checked, "failure 8");
   1723        expect(input2, 0, "failure 9");
   1724        input2.setAttribute("checked", "checked"); // sets defaultChecked, doesn't change actual state
   1725        expect(input1, checked, "failure 10");
   1726        expect(input2, 0, "failure 11");
   1727        input1.type = "text";
   1728        expect(input1, 0, "text field matched :checked");
   1729      });
   1730      selectorTest(function (doc, add, expect) {
   1731        var input = doc.createElement('input');
   1732        input.type = 'button';
   1733        doc.body.appendChild(input);
   1734        var neither = 0;
   1735        var enabled = add(":enabled");
   1736        var disabled = add(":disabled");
   1737        add(":enabled:disabled");
   1738        expect(input, enabled, "failure 12");
   1739        input.disabled = true;
   1740        expect(input, disabled, "failure 13");
   1741        input.removeAttribute("disabled");
   1742        expect(input, enabled, "failure 14");
   1743        expect(doc.body, neither, "failure 15");
   1744      });
   1745      return 3;
   1746    },
   1747    function () {
   1748      // test 44: selectors without spaces before a "*"
   1749      selectorTest(function (doc, add, expect) {
   1750        doc.body.className = "test";
   1751        var p = doc.createElement('p');
   1752        p.className = "test";
   1753        doc.body.appendChild(p);
   1754        add("html*.test");
   1755        expect(doc.body, 0, "misparsed selectors");
   1756        expect(p, 0, "really misparsed selectors");
   1757      });
   1758      return 3;
   1759    },
   1760    function () {
   1761      // test 45: cssFloat and the style attribute
   1762      assert(!document.body.style.cssFloat, "body has floatation");
   1763      document.body.setAttribute("style", "float: right");
   1764      assertEquals(document.body.style.cssFloat, "right", "body doesn't have floatation");
   1765      document.body.setAttribute("style", "float: none");
   1766      assertEquals(document.body.style.cssFloat, "none", "body didn't lose floatation");
   1767      return 3;
   1768    },
   1769    function () {
   1770      // test 46: media queries
   1771      var doc = getTestDocument();
   1772      var style = doc.createElement('style');
   1773      style.setAttribute('type', 'text/css');
   1774      style.appendChild(doc.createTextNode('@media all and (min-color: 0) { #a { text-transform: uppercase; } }'));                         // matches
   1775      style.appendChild(doc.createTextNode('@media not all and (min-color: 0) { #b { text-transform: uppercase; } }'));
   1776      style.appendChild(doc.createTextNode('@media only all and (min-color: 0) { #c { text-transform: uppercase; } }'));                    // matches
   1777      style.appendChild(doc.createTextNode('@media (bogus) { #d { text-transform: uppercase; } }'));
   1778      style.appendChild(doc.createTextNode('@media all and (bogus) { #e { text-transform: uppercase; } }'));
   1779      style.appendChild(doc.createTextNode('@media not all and (bogus) { #f { text-transform: uppercase; } }'));                         // commentd out but should not match
   1780      style.appendChild(doc.createTextNode('@media only all and (bogus) { #g { text-transform: uppercase; } }'));
   1781      style.appendChild(doc.createTextNode('@media (bogus), all { #h { text-transform: uppercase; } }'));                                   // matches
   1782      style.appendChild(doc.createTextNode('@media all and (bogus), all { #i { text-transform: uppercase; } }'));                           // matches
   1783      style.appendChild(doc.createTextNode('@media not all and (bogus), all { #j { text-transform: uppercase; } }'));                       // matches
   1784      style.appendChild(doc.createTextNode('@media only all and (bogus), all { #k { text-transform: uppercase; } }'));                      // matches
   1785      style.appendChild(doc.createTextNode('@media all, (bogus) { #l { text-transform: uppercase; } }'));                                   // matches
   1786      style.appendChild(doc.createTextNode('@media all, all and (bogus) { #m { text-transform: uppercase; } }'));                           // matches
   1787      style.appendChild(doc.createTextNode('@media all, not all and (bogus) { #n { text-transform: uppercase; } }'));                       // matches
   1788      style.appendChild(doc.createTextNode('@media all, only all and (bogus) { #o { text-transform: uppercase; } }'));                      // matches
   1789      style.appendChild(doc.createTextNode('@media all and color { #p { text-transform: uppercase; } }'));
   1790      style.appendChild(doc.createTextNode('@media all and min-color: 0 { #q { text-transform: uppercase; } }'));
   1791      style.appendChild(doc.createTextNode('@media all, all and color { #r { text-transform: uppercase; } }'));                          // commented out but should match
   1792      style.appendChild(doc.createTextNode('@media all, all and min-color: 0 { #s { text-transform: uppercase; } }'));                   // commented out but should match
   1793      style.appendChild(doc.createTextNode('@media all and min-color: 0, all { #t { text-transform: uppercase; } }'));                   // commented out but should match
   1794      style.appendChild(doc.createTextNode('@media (max-color: 0) and (max-monochrome: 0) { #u { text-transform: uppercase; } }'));
   1795      style.appendChild(doc.createTextNode('@media (min-color: 1), (min-monochrome: 1) { #v { text-transform: uppercase; } }'));            // matches
   1796      style.appendChild(doc.createTextNode('@media all and (min-color: 0) and (min-monochrome: 0) { #w { text-transform: uppercase; } }')); // matches
   1797      style.appendChild(doc.createTextNode('@media not all and (min-color: 1), not all and (min-monochrome: 1) { #x { text-transform: uppercase; } }')); // matches
   1798      style.appendChild(doc.createTextNode('@media all and (min-height: 1em) and (min-width: 1em) { #y1 { text-transform: uppercase; } }'));
   1799      style.appendChild(doc.createTextNode('@media all and (max-height: 1em) and (min-width: 1em) { #y2 { text-transform: uppercase; } }'));
   1800      style.appendChild(doc.createTextNode('@media all and (min-height: 1em) and (max-width: 1em) { #y3 { text-transform: uppercase; } }'));
   1801      style.appendChild(doc.createTextNode('@media all and (max-height: 1em) and (max-width: 1em) { #y4 { text-transform: uppercase; } }')); // matches
   1802      doc.getElementsByTagName('head')[0].appendChild(style);
   1803      var names = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y1', 'y2', 'y3', 'y4'];
   1804      for (var i in names) {
   1805        var p = doc.createElement('p');
   1806        p.id = names[i];
   1807        doc.body.appendChild(p);
   1808      }
   1809      var count = 0;
   1810      var check = function (c, e) {
   1811        count += 1;
   1812        var p = doc.getElementById(c);
   1813        assertEquals(doc.defaultView.getComputedStyle(p, '').textTransform, e ? 'uppercase' : 'none', "case " + c + " failed (index " + count + ")");
   1814      }
   1815      check('a', true); // 1
   1816      check('b', false);
   1817      check('c', true);
   1818      check('d', false);
   1819      check('e', false);
   1820 /* COMMENTED OUT BECAUSE THE CSSWG KEEP CHANGING THE RIGHT ANSWER FOR THIS CASE
   1821 *      check('f', false);
   1822 */
   1823      check('g', false);
   1824      check('h', true);
   1825      check('i', true);
   1826      check('j', true); // 10
   1827      check('k', true);
   1828      check('l', true);
   1829      check('m', true);
   1830      check('n', true);
   1831      check('o', true);
   1832      check('p', false);
   1833      check('q', false);
   1834 /* COMMENTED OUT BECAUSE THE CSSWG KEEP CHANGING THE RIGHT ANSWER FOR THESE TOO APPARENTLY
   1835 *      check('r', true);
   1836 *      check('s', true);
   1837 *      check('t', true); // 20
   1838 */
   1839      check('u', false);
   1840      check('v', true);
   1841      check('w', true);
   1842      check('x', true);
   1843      // here the viewport is 0x0
   1844      check('y1', false); // 25
   1845      check('y2', false);
   1846      check('y3', false);
   1847      check('y4', true);
   1848      document.getElementById("selectors").setAttribute("style", "height: 100px; width: 100px");
   1849      // now the viewport is more than 1em by 1em
   1850      check('y1', true); // 29
   1851      check('y2', false);
   1852      check('y3', false);
   1853      check('y4', false);
   1854      document.getElementById("selectors").removeAttribute("style");
   1855      // here the viewport is 0x0 again
   1856      check('y1', false); // 33
   1857      check('y2', false);
   1858      check('y3', false);
   1859      check('y4', true);
   1860      return 3;
   1861    },
   1862    function () {
   1863      // test 47: 'cursor' and CSS3 values
   1864      var doc = getTestDocument();
   1865      var style = doc.createElement('style');
   1866      style.setAttribute('type', 'text/css');
   1867      var cursors = ['auto', 'default', 'none', 'context-menu', 'help', 'pointer', 'progress', 'wait', 'cell', 'crosshair', 'text', 'vertical-text', 'alias', 'copy', 'move', 'no-drop', 'not-allowed', 'e-resize', 'n-resize', 'ne-resize', 'nw-resize', 's-resize', 'se-resize', 'sw-resize', 'w-resize', 'ew-resize', 'ns-resize', 'nesw-resize', 'nwse-resize', 'col-resize', 'row-resize', 'all-scroll'];
   1868      for (var i in cursors) {
   1869        var c = cursors[i];
   1870        style.appendChild(doc.createTextNode('#' + c + ' { cursor: ' + c + '; }'));
   1871      }
   1872      style.appendChild(doc.createTextNode('#bogus { cursor: bogus; }'));
   1873      doc.body.previousSibling.appendChild(style);
   1874      doc.body.id = "bogus";
   1875      assertEquals(doc.defaultView.getComputedStyle(doc.body, '').cursor, "auto", "control failed");
   1876      for (var i in cursors) {
   1877        var c = cursors[i];
   1878        doc.body.id = c;
   1879        assertEquals(doc.defaultView.getComputedStyle(doc.body, '').cursor, c, "cursor " + c + " not supported");
   1880      }
   1881      return 3;
   1882    },
   1883    function () {
   1884      // test 48: :link and :visited
   1885      var iframe = document.getElementById("selectors");
   1886      var number = (new Date()).valueOf();
   1887      var a = document.createElement('a');
   1888      a.appendChild(document.createTextNode('YOU SHOULD NOT SEE THIS AT ALL')); // changed text when fixing http://dbaron.org/mozilla/visited-privacy
   1889      a.setAttribute('id', 'linktest');
   1890      a.setAttribute('class', 'pending');
   1891      a.setAttribute('href', iframe.getAttribute('src') + "?" + number);
   1892      document.getElementsByTagName('map')[0].appendChild(a);
   1893      iframe.setAttribute("onload", "document.getElementById('linktest').removeAttribute('class')");
   1894      iframe.src = a.getAttribute("href");
   1895      return 3;
   1896    },
   1897 
   1898    // bucket 4: HTML and the DOM
   1899    // Tables
   1900    function () {
   1901      // test 49: basic table accessor ping test create*, delete*, and *
   1902      // where * is caption, tHead, tFoot.
   1903      var table = document.createElement('table');
   1904      assert(!table.caption, "initially: caption");
   1905      assert(table.tBodies, "initially: tBodies");
   1906      assertEquals(table.tBodies.length, 0, "initially: tBodies.length");
   1907      assert(table.rows, "initially: rows");
   1908      assertEquals(table.rows.length, 0, "initially: rows.length");
   1909      assert(!table.tFoot, "initially: tFoot");
   1910      assert(!table.tHead, "initially: tHead");
   1911      var caption = table.createCaption();
   1912      var thead = table.createTHead();
   1913      var tfoot = table.createTFoot();
   1914      assertEquals(table.caption, caption, "after creation: caption");
   1915      assert(table.tBodies, "after creation: tBodies");
   1916      assertEquals(table.tBodies.length, 0, "after creation: tBodies.length");
   1917      assert(table.rows, "after creation: rows");
   1918      assertEquals(table.rows.length, 0, "after creation: rows.length");
   1919      assertEquals(table.tFoot, tfoot, "after creation: tFoot");
   1920      assertEquals(table.tHead, thead, "after creation: tHead");
   1921      assertEquals(table.childNodes.length, 3, "after creation: childNodes.length");
   1922      table.caption = caption; // no-op
   1923      table.tHead = thead; // no-op
   1924      table.tFoot = tfoot; // no-op
   1925      assertEquals(table.caption, caption, "after setting: caption");
   1926      assert(table.tBodies, "after setting: tBodies");
   1927      assertEquals(table.tBodies.length, 0, "after setting: tBodies.length");
   1928      assert(table.rows, "after setting: rows");
   1929      assertEquals(table.rows.length, 0, "after setting: rows.length");
   1930      assertEquals(table.tFoot, tfoot, "after setting: tFoot");
   1931      assertEquals(table.tHead, thead, "after setting: tHead");
   1932      assertEquals(table.childNodes.length, 3, "after setting: childNodes.length");
   1933      table.deleteCaption();
   1934      table.deleteTHead();
   1935      table.deleteTFoot();
   1936      assert(!table.caption, "after deletion: caption");
   1937      assert(table.tBodies, "after deletion: tBodies");
   1938      assertEquals(table.tBodies.length, 0, "after deletion: tBodies.length");
   1939      assert(table.rows, "after deletion: rows");
   1940      assertEquals(table.rows.length, 0, "after deletion: rows.length");
   1941      assert(!table.tFoot, "after deletion: tFoot");
   1942      assert(!table.tHead, "after deletion: tHead");
   1943      assert(!table.hasChildNodes(), "after deletion: hasChildNodes()");
   1944      assertEquals(table.childNodes.length, 0, "after deletion: childNodes.length");
   1945      return 4;
   1946    },
   1947    function () {
   1948      // test 50: construct a table, and see if the table is as expected
   1949      var table = document.createElement('table');
   1950      table.appendChild(document.createElement('tbody'));
   1951      var tr1 = document.createElement('tr');
   1952      table.appendChild(tr1);
   1953      table.appendChild(document.createElement('caption'));
   1954      table.appendChild(document.createElement('thead'));
   1955      // <table><tbody/><tr/><caption/><thead/>
   1956      table.insertBefore(table.firstChild.nextSibling, null); // move the <tr/> to the end
   1957      // <table><tbody/><caption/><thead/><tr/>
   1958      table.replaceChild(table.firstChild, table.lastChild); // move the <tbody/> to the end and remove the <tr>
   1959      // <table><caption/><thead/><tbody/>
   1960      var tr2 = table.tBodies[0].insertRow(0);
   1961      // <table><caption/><thead/><tbody><tr/><\tbody>     (the '\' is to avoid validation errors)
   1962      assertEquals(table.tBodies[0].rows[0].rowIndex, 0, "rowIndex broken");
   1963      assertEquals(table.tBodies[0].rows[0].sectionRowIndex, 0, "sectionRowIndex broken");
   1964      assertEquals(table.childNodes.length, 3, "wrong number of children");
   1965      assert(table.caption, "caption broken");
   1966      assert(table.tHead, "tHead broken");
   1967      assert(!table.tFoot, "tFoot broken");
   1968      assertEquals(table.tBodies.length, 1, "wrong number of tBodies");
   1969      assertEquals(table.rows.length, 1, "wrong number of rows");
   1970      assert(!tr1.parentNode, "orphan row has unexpected parent");
   1971      assertEquals(table.caption, table.createCaption(), "caption creation failed");
   1972      assertEquals(table.tFoot, null, "table has unexpected footer");
   1973      assertEquals(table.tHead, table.createTHead(), "header creation failed");
   1974      assertEquals(table.createTFoot(), table.tFoot, "footer creation failed");
   1975      // either: <table><caption/><thead/><tbody><tr/><\tbody><tfoot/>
   1976      //     or: <table><caption/><thead/><tfoot/><tbody><tr/><\tbody>
   1977      table.tHead.appendChild(tr1);
   1978      // either: <table><caption/><thead><tr/><\thead><tbody><tr/><\tbody><tfoot/>
   1979      //     or: <table><caption/><thead><tr/><\thead><tfoot/><tbody><tr/><\tbody>
   1980      assertEquals(table.rows[0], table.tHead.firstChild, "top row not in expected position");
   1981      assertEquals(table.rows.length, 2, "wrong number of rows after appending one");
   1982      assertEquals(table.rows[1], table.tBodies[0].firstChild, "second row not in expected position");
   1983      return 4;
   1984    },
   1985    function () {
   1986      // test 51: test the ordering and creation of rows
   1987      var table = document.createElement('table');
   1988      var rows = [
   1989        document.createElement('tr'),    // 0: ends up first child of the tfoot
   1990        document.createElement('tr'),    // 1: goes at the end of the table
   1991        document.createElement('tr'),    // 2: becomes second child of thead
   1992        document.createElement('tr'),    // 3: becomes third child of the thead
   1993        document.createElement('tr'),    // 4: not in the table
   1994        table.insertRow(0),              // 5: not in the table
   1995        table.createTFoot().insertRow(0) // 6: ends up second in the tfoot
   1996      ];
   1997      rows[6].parentNode.appendChild(rows[0]);
   1998      table.appendChild(rows[1]);
   1999      table.insertBefore(document.createElement('thead'), table.firstChild);
   2000      table.firstChild.appendChild(rows[2]);
   2001      rows[2].parentNode.appendChild(rows[3]);
   2002      rows[4].appendChild(rows[5].parentNode);
   2003      table.insertRow(0);
   2004      table.tFoot.appendChild(rows[6]);
   2005      assertEquals(table.rows.length, 6, "wrong number of rows");
   2006      assertEquals(table.getElementsByTagName('tr').length, 6, "wrong number of tr elements");
   2007      assertEquals(table.childNodes.length, 3, "table has wrong number of children");
   2008      assertEquals(table.childNodes[0], table.tHead, "tHead isn't first");
   2009      assertEquals(table.getElementsByTagName('tr')[0], table.tHead.childNodes[0], "first tr isn't in tHead correctly");
   2010      assertEquals(table.getElementsByTagName('tr')[1], table.tHead.childNodes[1], "second tr isn't in tHead correctly");
   2011      assertEquals(table.getElementsByTagName('tr')[1], rows[2], "second tr is the wrong row");
   2012      assertEquals(table.getElementsByTagName('tr')[2], table.tHead.childNodes[2], "third tr isn't in tHead correctly");
   2013      assertEquals(table.getElementsByTagName('tr')[2], rows[3], "third tr is the wrong row");
   2014      assertEquals(table.childNodes[1], table.tFoot, "tFoot isn't second");
   2015      assertEquals(table.getElementsByTagName('tr')[3], table.tFoot.childNodes[0], "fourth tr isn't in tFoot correctly");
   2016      assertEquals(table.getElementsByTagName('tr')[3], rows[0], "fourth tr is the wrong row");
   2017      assertEquals(table.getElementsByTagName('tr')[4], table.tFoot.childNodes[1], "fifth tr isn't in tFoot correctly");
   2018      assertEquals(table.getElementsByTagName('tr')[4], rows[6], "fifth tr is the wrong row");
   2019      assertEquals(table.getElementsByTagName('tr')[5], table.childNodes[2], "sixth tr isn't in tFoot correctly");
   2020      assertEquals(table.getElementsByTagName('tr')[5], rows[1], "sixth tr is the wrong row");
   2021      assertEquals(table.tBodies.length, 0, "non-zero number of tBodies");
   2022      return 4;
   2023    },
   2024 
   2025    // Forms
   2026    function () {
   2027      // test 52: <form> and .elements
   2028      test = document.getElementsByTagName('form')[0];
   2029      assert(test.elements !== test, "form.elements === form");
   2030      assert(test.elements !== test.getAttribute('elements'), "form element has an elements content attribute");
   2031      assertEquals(test.elements.length, 1, "form element has unexpected number of controls");
   2032      assertEquals(test.elements.length, test.length, "form element has inconsistent numbers of controls");
   2033      return 4;
   2034    },
   2035    function () {
   2036      // test 53: changing an <input> dynamically
   2037      var f = document.createElement('form');
   2038      var i = document.createElement('input');
   2039      i.name = 'first';
   2040      i.type = 'text';
   2041      i.value = 'test';
   2042      f.appendChild(i);
   2043      assertEquals(i.getAttribute('name'), 'first', "name attribute wrong");
   2044      assertEquals(i.name, 'first', "name property wrong");
   2045      assertEquals(i.getAttribute('type'), 'text', "type attribute wrong");
   2046      assertEquals(i.type, 'text', "type property wrong");
   2047      assert(!i.hasAttribute('value'), "value attribute wrong");
   2048      assertEquals(i.value, 'test', "value property wrong");
   2049      assertEquals(f.elements.length, 1, "form's elements array has wrong size");
   2050      assertEquals(f.elements[0], i, "form's element array doesn't have input control by index");
   2051      assertEquals(f.elements.first, i, "form's element array doesn't have input control by name");
   2052      assertEquals(f.elements.second, null, "form's element array has unexpected controls by name");
   2053      i.name = 'second';
   2054      i.type = 'password';
   2055      i.value = 'TEST';
   2056      assertEquals(i.getAttribute('name'), 'second', "name attribute wrong after change");
   2057      assertEquals(i.name, 'second', "name property wrong after change");
   2058      assertEquals(i.getAttribute('type'), 'password', "type attribute wrong after change");
   2059      assertEquals(i.type, 'password', "type property wrong after change");
   2060      assert(!i.hasAttribute('value'), "value attribute wrong after change");
   2061      assertEquals(i.value, 'TEST', "value property wrong after change");
   2062      assertEquals(f.elements.length, 1, "form's elements array has wrong size after change");
   2063      assertEquals(f.elements[0], i, "form's element array doesn't have input control by index after change");
   2064      assertEquals(f.elements.second, i, "form's element array doesn't have input control by name after change");
   2065      assertEquals(f.elements.first, null, "form's element array has unexpected controls by name after change");
   2066      return 4;
   2067    },
   2068    function () {
   2069      // test 54: changing a parsed <input>
   2070      var i = document.getElementsByTagName('input')[0];
   2071      // initial values
   2072      assertEquals(i.getAttribute('type'), 'HIDDEN', "input control's type content attribute was wrong");
   2073      assertEquals(i.type, 'hidden', "input control's type DOM attribute was wrong");
   2074      // change values
   2075      i.name = 'test';
   2076      assertEquals(i.parentNode.elements.test, i, "input control's form didn't update");
   2077      // check event handlers
   2078      i.parentNode.action = 'javascript:';
   2079      var called = false;
   2080      i.parentNode.onsubmit = function (arg) {
   2081        arg.preventDefault();
   2082        called = true;
   2083      };
   2084      i.type = 'submit';
   2085      i.click(); // synchronously dispatches a click event to the submit button, which submits the form, which calls onsubmit
   2086      assert(called, "click handler didn't dispatch properly");
   2087      i.type = 'hIdDeN';
   2088      // check numeric attributes
   2089      i.setAttribute('maxLength', '2');
   2090      var s = i.getAttribute('maxLength');
   2091      assert(s.match, "attribute is not a String");
   2092      assert(!s.MIN_VALUE, "attribute is a Number");
   2093      return 4;
   2094    },
   2095    function () {
   2096      // test 55: moved checkboxes should keep their state
   2097      var container = document.getElementsByTagName("iframe")[0];
   2098      var input1 = document.createElement('input');
   2099      container.appendChild(input1);
   2100      input1.type = "checkbox";
   2101      input1.checked = true;
   2102      assert(input1.checked, "checkbox not checked after being checked (inserted first)");
   2103      var input2 = document.createElement('input');
   2104      input2.type = "checkbox";
   2105      container.appendChild(input2);
   2106      input2.checked = true;
   2107      assert(input2.checked, "checkbox not checked after being checked (inserted after type set)");
   2108      var input3 = document.createElement('input');
   2109      input3.type = "checkbox";
   2110      input3.checked = true;
   2111      container.appendChild(input3);
   2112      assert(input3.checked, "checkbox not checked after being checked (inserted after being checked)");
   2113      var target = document.getElementsByTagName("iframe")[1];
   2114      target.appendChild(input1);
   2115      target.appendChild(input2);
   2116      target.appendChild(input3);
   2117      assert(input1.checked, "checkbox 1 not checked after being moved");
   2118      assert(input2.checked, "checkbox 2 not checked after being moved");
   2119      assert(input3.checked, "checkbox 3 not checked after being moved");
   2120      return 4;
   2121    },
   2122    function () {
   2123      // test 56: cloned radio buttons should keep their state
   2124      var form = document.getElementsByTagName("form")[0];
   2125      var input1 = document.createElement('input');
   2126      input1.type = "radio";
   2127      input1.name = "radioGroup1";
   2128      form.appendChild(input1);
   2129      var input2 = input1.cloneNode(true);
   2130      input1.parentNode.appendChild(input2);
   2131      input1.checked = true;
   2132      assert(form.elements.radioGroup1, "radio group absent");
   2133      assert(input1.checked, "first radio button not checked");
   2134      assert(!input2.checked, "second radio button checked");
   2135      input2.checked = true;
   2136      assert(!input1.checked, "first radio button checked");
   2137      assert(input2.checked, "second radio button not checked");
   2138      var input3 = document.createElement('input');
   2139      input3.type = "radio";
   2140      input3.name = "radioGroup2";
   2141      form.appendChild(input3);
   2142      assert(!input3.checked, "third radio button checked");
   2143      input3.checked = true;
   2144      assert(!input1.checked, "first radio button newly checked");
   2145      assert(input2.checked, "second radio button newly not checked");
   2146      assert(input3.checked, "third radio button not checked");
   2147      input1.checked = true;
   2148      assert(input1.checked, "first radio button ended up not checked");
   2149      assert(!input2.checked, "second radio button ended up checked");
   2150      assert(input3.checked, "third radio button ended up not checked");
   2151      input1.parentNode.removeChild(input1);
   2152      input2.parentNode.removeChild(input2);
   2153      input3.parentNode.removeChild(input3);
   2154      return 4;
   2155    },
   2156    function () {
   2157      // test 57: HTMLSelectElement.add()
   2158      var s = document.createElement('select');
   2159      var o = document.createElement('option');
   2160      s.add(o, null);
   2161      assert(s.firstChild === o, "add() didn't add to firstChild");
   2162      assertEquals(s.childNodes.length, 1, "add() didn't add to childNodes");
   2163      assert(s.childNodes[0] === o, "add() didn't add to childNodes correctly");
   2164      assertEquals(s.options.length, 1, "add() didn't add to options");
   2165      assert(s.options[0] === o, "add() didn't add to options correctly");
   2166      return 4;
   2167    },
   2168    function () {
   2169      // test 58: HTMLOptionElement.defaultSelected
   2170      var s = document.createElement('select');
   2171      var o1 = document.createElement('option');
   2172      var o2 = document.createElement('option');
   2173      o2.defaultSelected = true;
   2174      var o3 = document.createElement('option');
   2175      s.appendChild(o1);
   2176      s.appendChild(o2);
   2177      s.appendChild(o3);
   2178      assert(s.options[s.selectedIndex] === o2, "defaultSelected didn't take");
   2179      return 4;
   2180    },
   2181    function () {
   2182      // test 59: attributes of <button> elements
   2183      var button = document.createElement('button');
   2184      assertEquals(button.type, "submit", "<button> doesn't have type=submit");
   2185      button.setAttribute("type", "button");
   2186      assertEquals(button.type, "button", "<button type=button> doesn't have type=button");
   2187      button.removeAttribute("type");
   2188      assertEquals(button.type, "submit", "<button> doesn't have type=submit back");
   2189      button.setAttribute('value', 'apple');
   2190      button.appendChild(document.createTextNode('banana'));
   2191      assertEquals(button.value, 'apple', "wrong button value");
   2192      return 4;
   2193    },
   2194 
   2195    // Misc DOM2 HTML
   2196    function () {
   2197      // test 60: className vs "class" vs attribute nodes
   2198      var span = document.getElementsByTagName('span')[0];
   2199      span.setAttribute('class', 'kittens');
   2200 // COMMENTED OUT FOR 2011 UPDATE - turns out instead of dropping Attr entirely, as Acid3 originally expected, the API is just being refactored
   2201 //      if (!span.getAttributeNode)
   2202 //        return 4; // support for attribute nodes is optional in Acid3, because attribute nodes might be removed from DOM Core in the future.
   2203 //      var attr = span.getAttributeNode('class');
   2204 //      // however, if they're supported, they'd better work:
   2205 //      assert(attr.specified, "attribute not specified");
   2206 //      assertEquals(attr.value, 'kittens', "attribute value wrong");
   2207 //      assertEquals(attr.name, 'class', "attribute name wrong");
   2208 //      attr.value = 'ocelots';
   2209 //      assertEquals(attr.value, 'ocelots', "attribute value wrong");
   2210 //      assertEquals(span.className, 'ocelots', "setting attribute value failed to be reflected in className");
   2211      span.className = 'cats';
   2212 //      assertEquals(attr.ownerElement.getAttribute('class'), 'cats', "setting attribute value failed to be reflected in getAttribute()");
   2213 //      span.removeAttributeNode(attr);
   2214 //      assert(attr.specified, "attribute not specified after removal");
   2215 //      assert(!attr.ownerElement, "attribute still owned after removal");
   2216 //      assert(!span.className, "element had class after removal");
   2217      return 4;
   2218    },
   2219    function () {
   2220      // test 61: className and the class attribute: space preservation
   2221      var p = document.createElement('p');
   2222      assert(!p.hasAttribute('class'), "element had attribute on creation");
   2223      p.setAttribute('class', ' te  st ');
   2224      assert(p.hasAttribute('class'), "element did not have attribute after setting");
   2225      assertEquals(p.getAttribute('class'), ' te  st ', "class attribute's value was wrong");
   2226      assertEquals(p.className, ' te  st ', "className was wrong");
   2227      p.className = p.className.replace(/ /g, '\n');
   2228      assert(p.hasAttribute('class'), "element did not have attribute after replacement");
   2229      assertEquals(p.getAttribute('class'), '\nte\n\nst\n', "class attribute's value was wrong after replacement");
   2230      assertEquals(p.className, '\nte\n\nst\n', "className was wrong after replacement");
   2231      p.className = '';
   2232      assert(p.hasAttribute('class'), "element lost attribute after being set to empty string");
   2233      assertEquals(p.getAttribute('class'), '', "class attribute's value was wrong after being emptied");
   2234      assertEquals(p.className, '', "className was wrong after being emptied");
   2235      return 4;
   2236    },
   2237    function () {
   2238      // test 62: check that DOM attributes and content attributes aren't equivalent
   2239      var test;
   2240      // <div class="">
   2241      test = document.getElementsByTagName('div')[0];
   2242      assertEquals(test.className, 'buckets', "buckets: className wrong");
   2243      assertEquals(test.getAttribute('class'), 'buckets', "buckets: class wrong");
   2244      assert(!test.hasAttribute('className'), "buckets: element has className attribute");
   2245      assert(test.className != test.getAttribute('className'), "buckets: className attribute equals className property");
   2246      assert(!('class' in test), "buckets: element has class property")
   2247      test['class'] = "oil";
   2248      assert(test.className != "oil", "buckets: class property affected className");
   2249      // <label for="">
   2250      test = document.createElement('label');
   2251      test.htmlFor = 'jars';
   2252      assertEquals(test.htmlFor, 'jars', "jars: htmlFor wrong");
   2253      assertEquals(test.getAttribute('for'), 'jars', "jars: for wrong");
   2254      assert(!test.hasAttribute('htmlFor'), "jars: element has htmlFor attribute");
   2255      assert(test.htmlFor != test.getAttribute('htmlFor'), "jars: htmlFor attribute equals htmlFor property");
   2256      test = document.createElement('label');
   2257      test.setAttribute('for', 'pots');
   2258      assertEquals(test.htmlFor, 'pots', "pots: htmlFor wrong");
   2259      assertEquals(test.getAttribute('for'), 'pots', "pots: for wrong");
   2260      assert(!test.hasAttribute('htmlFor'), "pots: element has htmlFor attribute");
   2261      assert(test.htmlFor != test.getAttribute('htmlFor'), "pots: htmlFor attribute equals htmlFor property");
   2262      assert(!('for' in test), "pots: element has for property");
   2263      test['for'] = "oil";
   2264      assert(test.htmlFor != "oil", "pots: for property affected htmlFor");
   2265      // <meta http-equiv="">
   2266      test = document.createElement('meta');
   2267      test.setAttribute('http-equiv', 'boxes');
   2268      assertEquals(test.httpEquiv, 'boxes', "boxes: httpEquiv wrong");
   2269      assertEquals(test.getAttribute('http-equiv'), 'boxes', "boxes: http-equiv wrong");
   2270      assert(!test.hasAttribute('httpEquiv'), "boxes: element has httpEquiv attribute");
   2271      assert(test.httpEquiv != test.getAttribute('httpEquiv'), "boxes: httpEquiv attribute equals httpEquiv property");
   2272      test = document.createElement('meta');
   2273      test.httpEquiv = 'cans';
   2274      assertEquals(test.httpEquiv, 'cans', "cans: httpEquiv wrong");
   2275      assertEquals(test.getAttribute('http-equiv'), 'cans', "cans: http-equiv wrong");
   2276      assert(!test.hasAttribute('httpEquiv'), "cans: element has httpEquiv attribute");
   2277      assert(test.httpEquiv != test.getAttribute('httpEquiv'), "cans: httpEquiv attribute equals httpEquiv property");
   2278      assert(!('http-equiv' in test), "cans: element has http-equiv property");
   2279      test['http-equiv'] = "oil";
   2280      assert(test.httpEquiv != "oil", "cans: http-equiv property affected httpEquiv");
   2281      return 4;
   2282    },
   2283    function () {
   2284      // test 63: attributes of the <area> element
   2285      var area = document.getElementsByTagName('area')[0];
   2286      assertEquals(area.getAttribute('href'), '', "wrong value for href=''");
   2287      assertEquals(area.getAttribute('shape'), 'rect', "wrong value for shape=''");
   2288      assertEquals(area.getAttribute('coords'), '2,2,4,4', "wrong value for coords=''");
   2289      assertEquals(area.getAttribute('alt'), '<\'>', "wrong value for alt=''");
   2290      return 4;
   2291    },
   2292    function () {
   2293      // test 64: more attribute tests
   2294      // attributes of the <object> element
   2295      var obj1 = document.createElement('object');
   2296      obj1.setAttribute('data', 'test.html');
   2297      var obj2 = document.createElement('object');
   2298      obj2.setAttribute('data', './test.html');
   2299      assertEquals(obj1.data, obj2.data, "object elements didn't resolve URIs correctly");
   2300      assert(obj1.data.match(/^http:/), "object.data isn't absolute");
   2301      obj1.appendChild(document.createElement('param'));
   2302      assertEquals(obj1.getElementsByTagName('param').length, 1, "object is missing its only child");
   2303      // non-existent attributes
   2304      var test = document.createElement('p');
   2305      assert(!('TWVvdywgbWV3Li4u' in test), "TWVvdywgbWV3Li4u unexpectedly found");
   2306      assertEquals(test.TWVvdywgbWV3Li4u, undefined, ".TWVvdywgbWV3Li4u wasn't undefined");
   2307      assertEquals(test['TWVvdywgbWV3Li4u'], undefined, "['TWVvdywgbWV3Li4u'] wasn't undefined");
   2308      test.setAttribute('TWVvdywgbWV3Li4u', 'woof');
   2309      assert(!('TWVvdywgbWV3Li4u' in test), "TWVvdywgbWV3Li4u unexpectedly found after setting");
   2310      assertEquals(test.TWVvdywgbWV3Li4u, undefined, ".TWVvdywgbWV3Li4u wasn't undefined after setting");
   2311      assertEquals(test['TWVvdywgbWV3Li4u'], undefined, "['TWVvdywgbWV3Li4u'] wasn't undefined after setting");
   2312      assertEquals(test.getAttribute('TWVvdywgbWV3Li4u'), 'woof', "TWVvdywgbWV3Li4u has wrong value after setting");
   2313      return 4;
   2314    },
   2315 
   2316    // bucket 5: Tests from the Acid3 Competition
   2317    function () {
   2318      // test 65: bring in a couple of SVG files and some HTML files dynamically - preparation for later tests in this bucket
   2319      // NOTE FROM 2011 UPDATE: The svg.xml file still contains the SVG font, but it is no longer used
   2320      kungFuDeathGrip = document.createElement('p');
   2321      kungFuDeathGrip.className = 'removed';
   2322      var iframe, object;
   2323      // svg iframe
   2324      iframe = document.createElement('iframe');
   2325      iframe.onload = function () { kungFuDeathGrip.title += '1' };
   2326      iframe.src = "svg.xml";
   2327      kungFuDeathGrip.appendChild(iframe);
   2328      // object iframe
   2329      object = document.createElement('object');
   2330      object.onload = function () { kungFuDeathGrip.title += '2' };
   2331      object.data = "svg.xml";
   2332      kungFuDeathGrip.appendChild(object);
   2333      // xml iframe
   2334      iframe = document.createElement('iframe');
   2335      iframe.onload = function () { kungFuDeathGrip.title += '3' };
   2336      iframe.src = "empty.xml";
   2337      kungFuDeathGrip.appendChild(iframe);
   2338      // html iframe
   2339      iframe = document.createElement('iframe');
   2340      iframe.onload = function () { kungFuDeathGrip.title += '4' };
   2341      iframe.src = "empty.html";
   2342      kungFuDeathGrip.appendChild(iframe);
   2343      // html iframe
   2344      iframe = document.createElement('iframe');
   2345      iframe.onload = function () { kungFuDeathGrip.title += '5' };
   2346      iframe.src = "xhtml.1";
   2347      kungFuDeathGrip.appendChild(iframe);
   2348      // html iframe
   2349      iframe = document.createElement('iframe');
   2350      iframe.onload = function () { kungFuDeathGrip.title += '6' };
   2351      iframe.src = "xhtml.2";
   2352      kungFuDeathGrip.appendChild(iframe);
   2353      // html iframe
   2354      iframe = document.createElement('iframe');
   2355      iframe.onload = function () { kungFuDeathGrip.title += '7' };
   2356      iframe.src = "xhtml.3";
   2357      kungFuDeathGrip.appendChild(iframe);
   2358      // add the lot to the document
   2359      document.getElementsByTagName('map')[0].appendChild(kungFuDeathGrip);
   2360      return 5;
   2361    },
   2362    function () {
   2363      // test 66: localName on text nodes (and now other things), from Sylvain Pasche
   2364      assertEquals(document.createTextNode("test").localName, null, 'wrong localName for text node');
   2365      assertEquals(document.createComment("test").localName, null, 'wrong localName for comment node');
   2366      assertEquals(document.localName, null, 'wrong localName for document node');
   2367      return 5;
   2368    },
   2369    function () {
   2370 // COMMENTED OUT IN NOV 2013 BECAUSE DOM SPEC REMOVED THIS FEATURE
   2371 //       // test 67: removedNamedItemNS on missing attributes, from Sylvain Pasche
   2372 //       var p = document.createElement("p");
   2373 //       var msg = 'wrong exception raised';
   2374 //       try {
   2375 //         p.attributes.removeNamedItemNS("http://www.example.com/", "absent");
   2376 //         msg = 'no exception raised';
   2377 //       } catch (e) {
   2378 //         if ('code' in e) {
   2379 //           if (e.code == 8)
   2380 //             msg = '';
   2381 //           else
   2382 //             msg += '; code = ' + e.code;
   2383 //         }
   2384 //       }
   2385 //       assert(msg == '', "when calling removeNamedItemNS in a non existent attribute: " + msg);
   2386      return 5;
   2387    },
   2388    function () {
   2389      // test 68: UTF-16 surrogate pairs, from David Chan
   2390      //
   2391      // In The Unicode Standard 5.0, it is explicitly permitted to
   2392      // allow malformed UTF-16, that is, to leave the string alone.
   2393      // (http://www.unicode.org/versions/Unicode5.0.0):
   2394      //
   2395      // section 2.7: "...strings in ... ECMAScript are Unicode 16-bit
   2396      // strings, but are not necessarily well-formed UTF-16
   2397      // sequences.  In normal processing, it can be far more
   2398      // efficient to allow such strings to contain code unit
   2399      // sequences that are not well-formed UTF-16 -- that is,
   2400      // isolated surrogates"
   2401      //
   2402      // On the other hand, if the application wishes to ensure
   2403      // well-formed character sequences, it may not permit the
   2404      // malformed sequence and it must regard the first codepoint as
   2405      // an error:
   2406      //
   2407      // Section 3.2: "C10. When a process interprets a code sequence
   2408      // which purports to be in a Unicode character encoding form, it
   2409      // shall treat ill-formed code unit sequences as an error
   2410      // condition and shall not interpret such sequences as
   2411      // characters.
   2412      // [...]
   2413      // For example, in UTF-8 every code unit of the form 110....2
   2414      // must be followed by a code unit of the form 10......2. A
   2415      // sequence such as 110.....2 0.......2 is ill-formed and must
   2416      // never be generated. When faced with this ill-formed code unit
   2417      // sequence while transforming or interpreting text, a
   2418      // conformant process must treat the first code unit 110.....2
   2419      // as an illegally terminated code unit sequence~Wfor example,
   2420      // by signaling an error, filtering the code unit out, or
   2421      // representing the code unit with a marker such as U+FFFD
   2422      // replacement character."
   2423      //
   2424      // So it would be permitted to do any of the following:
   2425      // 1) Leave the string alone
   2426      // 2) Remove the unpaired surrogate
   2427      // 3) Replace the unpaired surrogate with U+FFFD
   2428      // 4) Throw an exception
   2429 
   2430      try {
   2431        var unpaired = String.fromCharCode(0xd863); // half a surrogate pair
   2432        var before = unpaired + "text";
   2433        var elt = document.createElement("input");
   2434        elt.value = before;
   2435        var after = elt.value;
   2436      }
   2437      catch(ex) {
   2438        return 5; // Unpaired surrogate caused an exception - ok
   2439      }
   2440      if (after == before && before.length == 5)
   2441        return 5; // Unpaired surrogate kept - ok
   2442      if (after == "text")
   2443        return 5; // Unpaired surrogate removed - ok
   2444      var replacement = String.fromCharCode(0xfffd);
   2445      if (after == replacement + "text")
   2446        return 5; // Unpaired surrogate replaced - ok
   2447      fail("Unpaired surrogate handled wrongly (input was '" + before + "', output was '" + after + "')");
   2448    },
   2449    function () {
   2450      // test 69: check that the support files loaded -- preparation for the rest of the tests in this bucket
   2451      assert(!(kungFuDeathGrip == null), "kungFuDeathGrip was null");
   2452      assert(!(kungFuDeathGrip.title == null), "kungFuDeathGrip.title was null");
   2453      if (kungFuDeathGrip.title.length < 7)
   2454        return "retry";
   2455      assert(!(kungFuDeathGrip.firstChild == null), "kungFuDeathGrip.firstChild was null");
   2456      assert(!(kungFuDeathGrip.firstChild.contentDocument == null), "kungFuDeathGrip.firstChild.contentDocument was null");
   2457      assert(!(kungFuDeathGrip.firstChild.contentDocument.getElementsByTagName == null), "kungFuDeathGrip.firstChild.contentDocument.getElementsByTagName was null");
   2458      var t = kungFuDeathGrip.firstChild.contentDocument.getElementsByTagName('text')[0];
   2459      assert(!(t == null), "t was null");
   2460      assert(!(t.parentNode == null), "t.parentNode was null");
   2461      assert(!(t.parentNode.removeChild == null), "t.parentNode.removeChild was null");
   2462      t.parentNode.removeChild(t);
   2463      return 5;
   2464    },
   2465    function () {
   2466      // test 70: XML encoding test
   2467      // the third child in kungFuDeathGrip is an ISO-8859-1 document sent as UTF-8.
   2468      // q.v. XML 1.0, section 4.3.3 Character Encoding in Entities
   2469      // this only tests one of a large number of conditions that should cause fatal errors
   2470      var doc = kungFuDeathGrip.childNodes[2].contentDocument;
   2471      if (!doc)
   2472        return 5;
   2473      if (doc.documentElement.tagName != "root")
   2474        return 5;
   2475      if (doc.documentElement.getElementsByTagName('test').length < 1)
   2476        return 5;
   2477      fail("UTF-8 encoded XML document with invalid character did not have a well-formedness error");
   2478    },
   2479    function () {
   2480      // test 71: HTML parsing, from Simon Pieters and Anne van Kesteren
   2481      var doc = kungFuDeathGrip.childNodes[3].contentDocument;
   2482      assert(doc, "missing document for test");
   2483      try {
   2484        // siblings
   2485        doc.open();
   2486        doc.write("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\"><title><\/title><span><\/span><script type=\"text/javascript\"><\/script>");
   2487        doc.close();
   2488        assertEquals(doc.childNodes.length, 2, "wrong number of children in #document (first test)");
   2489        assertEquals(doc.firstChild.name.toUpperCase(), "HTML", "name wrong (first test)"); // changed 2009-08-13 to add .toUpperCase() for HTML5 compat
   2490        assertEquals(doc.firstChild.publicId, "-//W3C//DTD HTML 4.0 Transitional//EN", "publicId wrong (first test)");
   2491        if ((doc.firstChild.systemId != null) && (doc.firstChild.systemId != ""))
   2492          fail("systemId wrong (first test)");
   2493        if (('internalSubset' in doc.firstChild) || doc.firstChild.internalSubset)
   2494          assertEquals(doc.firstChild.internalSubset, null, "internalSubset wrong (first test)");
   2495        assertEquals(doc.documentElement.childNodes.length, 2, "wrong number of children in HTML (first test)");
   2496        assertEquals(doc.documentElement.firstChild.nodeName, "HEAD", "misplaced HEAD element (first test)");
   2497        assertEquals(doc.documentElement.firstChild.childNodes.length, 1, "wrong number of children in HEAD (first test)");
   2498        assertEquals(doc.documentElement.firstChild.firstChild.tagName, "TITLE", "misplaced TITLE element (first test)");
   2499        assertEquals(doc.documentElement.lastChild.nodeName, "BODY", "misplaced BODY element (first test)");
   2500        assertEquals(doc.documentElement.lastChild.childNodes.length, 2, "wrong number of children in BODY (first test)");
   2501        assertEquals(doc.documentElement.lastChild.firstChild.tagName, "SPAN", "misplaced SPAN element (first test)");
   2502        assertEquals(doc.documentElement.lastChild.lastChild.tagName, "SCRIPT", "misplaced SCRIPT element (first test)");
   2503        // parent/child
   2504        doc.open();
   2505        doc.write("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\"><title><\/title><span><script type=\"text/javascript\"><\/script><\/span>");
   2506        doc.close();
   2507        assertEquals(doc.childNodes.length, 2, "wrong number of children in #document (second test)");
   2508        assertEquals(doc.firstChild.name.toUpperCase(), "HTML", "name wrong (second test)"); // changed 2009-08-13 to add .toUpperCase() for HTML5 compat
   2509        assertEquals(doc.firstChild.publicId, "-//W3C//DTD HTML 4.01 Transitional//EN", "publicId wrong (second test)");
   2510        assertEquals(doc.firstChild.systemId, "http://www.w3.org/TR/html4/loose.dtd", "systemId wrong (second test)");
   2511        if (('internalSubset' in doc.firstChild) || doc.firstChild.internalSubset)
   2512          assertEquals(doc.firstChild.internalSubset, null, "internalSubset wrong (second test)");
   2513        assertEquals(doc.documentElement.childNodes.length, 2, "wrong number of children in HTML (second test)");
   2514        assertEquals(doc.documentElement.firstChild.nodeName, "HEAD", "misplaced HEAD element (second test)");
   2515        assertEquals(doc.documentElement.firstChild.childNodes.length, 1, "wrong number of children in HEAD (second test)");
   2516        assertEquals(doc.documentElement.firstChild.firstChild.tagName, "TITLE", "misplaced TITLE element (second test)");
   2517        assertEquals(doc.documentElement.lastChild.nodeName, "BODY", "misplaced BODY element (second test)");
   2518        assertEquals(doc.documentElement.lastChild.childNodes.length, 1, "wrong number of children in BODY (second test)");
   2519        assertEquals(doc.documentElement.lastChild.firstChild.tagName, "SPAN", "misplaced SPAN element (second test)");
   2520        assertEquals(doc.documentElement.lastChild.firstChild.firstChild.tagName, "SCRIPT", "misplaced SCRIPT element (second test)");
   2521      } finally {
   2522        // prepare the file for the next test
   2523        doc.open();
   2524        doc.write("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"><head><title><\/title><style type=\"text/css\">img { height: 10px; }<\/style><body><p><img src=\"data:image/gif;base64,R0lGODlhAQABAID%2FAMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw%3D%3D\" alt=\"\">");
   2525        doc.close();
   2526      }
   2527      return 5;
   2528    },
   2529    function () {
   2530      // test 72: dynamic modification of <style> blocks' text nodes, from Jonas Sicking and Garret Smith
   2531      var doc = kungFuDeathGrip.childNodes[3].contentDocument;
   2532      assert(doc, "missing document for test");
   2533      assert(doc.images[0], "prerequisite failed: no image");
   2534      assertEquals(doc.images[0].height, 10, "prerequisite failed: style didn't affect image");
   2535      doc.styleSheets[0].ownerNode.firstChild.data = "img { height: 20px; }";
   2536      assertEquals(doc.images[0].height, 20, "change failed to take effect");
   2537      doc.styleSheets[0].ownerNode.appendChild(doc.createTextNode("img { height: 30px; }"));
   2538      assertEquals(doc.images[0].height, 30, "append failed to take effect");
   2539      var rules = doc.styleSheets[0].cssRules; // "All CSS objects in the DOM are "live"" says section 2.1, Overview of the DOM Level 2 CSS Interfaces
   2540      doc.styleSheets[0].insertRule("img { height: 40px; }", 2);
   2541      assertEquals(doc.images[0].height, 40, "insertRule failed to take effect");
   2542      assertEquals(doc.styleSheets[0].cssRules.length, 3, "count of rules is wrong");
   2543      assertEquals(rules.length, 3, "cssRules isn't live");
   2544      // while we're at it, check some other things on doc.styleSheets:
   2545      assert(doc.styleSheets[0].href === null, "internal stylesheet had a URI: " + doc.styleSheets[0].href);
   2546      assert(document.styleSheets[0].href === null, "internal acid3 stylesheet had a URI: " + document.styleSheets[0].href);
   2547      return 5;
   2548    },
   2549    function () {
   2550      // test 73: nested events, from Jonas Sicking
   2551      var doc = kungFuDeathGrip.childNodes[3].contentDocument;
   2552      // implied events
   2553      var up = 0;
   2554      var down = 0;
   2555      var button = doc.createElement("button");
   2556      button.type = "button";
   2557      button.onclick = function () { up += 1; if (up < 10) button.click(); down += up; }; // not called
   2558      button.addEventListener('test', function () { up += 1; var e = doc.createEvent("HTMLEvents"); e.initEvent('test', false, false); if (up < 20) button.dispatchEvent(e); down += up; }, false);
   2559      var evt = doc.createEvent("HTMLEvents");
   2560      evt.initEvent('test', false, false);
   2561      button.dispatchEvent(evt);
   2562      assertEquals(up, 20, "test event handler called the wrong number of times");
   2563      assertEquals(down, 400, "test event handler called in the wrong order");
   2564      return 5;
   2565    },
   2566    function () {
   2567      // test 74: check getSVGDocument(), from Erik Dahlstrom
   2568      // GetSVGDocument[6]: "In the case where an SVG document is
   2569      // embedded by reference, such as when an XHTML document has an
   2570      // 'object' element whose href (or equivalent) attribute
   2571      // references an SVG document (i.e., a document whose MIME type
   2572      // is "image/svg+xml" and whose root element is thus an 'svg'
   2573      // element), the SVG user agent is required to implement the
   2574      // GetSVGDocument interface for the element which references the
   2575      // SVG document (e.g., the HTML 'object' or comparable
   2576      // referencing elements)."
   2577      //
   2578      // [6] http://www.w3.org/TR/SVG11/struct.html#InterfaceGetSVGDocument
   2579      //
   2580      // iframe
   2581      var iframe = kungFuDeathGrip.childNodes[0];
   2582      assert(iframe, "Failed finding svg iframe.");
   2583      assert(iframe.contentDocument, "contentDocument failed for <iframe> referencing an svg document.");
   2584      if (!iframe.getSVGDocument)
   2585        fail("getSVGDocument missing on <iframe> element.");
   2586      assert(iframe.getSVGDocument(), "getSVGDocument failed for <iframe> referencing an svg document.");
   2587      assert(iframe.getSVGDocument() == iframe.contentDocument, "Mismatch between getSVGDocument and contentDocument #1.");
   2588      // object
   2589      var object = kungFuDeathGrip.childNodes[1];
   2590      assert(object, "Failed finding svg object.");
   2591      assert(object.contentDocument, "contentDocument failed for <object> referencing an svg document.");
   2592      if (!object.getSVGDocument)
   2593        fail("getSVGDocument missing on <object> element.");
   2594      assert(object.getSVGDocument(), "getSVGDocument failed for <object> referencing an svg document.");
   2595      assert(object.getSVGDocument() == object.contentDocument, "Mismatch between getSVGDocument and contentDocument #2.");
   2596      return 5;
   2597    },
   2598    function () {
   2599 // PARTS COMMENTED OUT FOR 2011 UPDATE - SVG Fonts, SVG SMIL animation, and XLink have met with some implementor malaise even amongst those that shipped them
   2600 // This affects tests 75 to 79
   2601 //      // test 75: SMIL in SVG, from Erik Dahlstrom
   2602 //      //
   2603 //      // The test begins by creating a few elements, among those is a
   2604 //      // <set> element.  This element is prevented from running by
   2605 //      // setting begin="indefinite", which means that the animation
   2606 //      // doesn't start until the 'beginElement' DOM method is called
   2607 //      // on the <set> element. The animation is a simple animation
   2608 //      // that sets the value of the width attribute to 0. The duration
   2609 //      // of the animation is 'indefinite' which means that the value
   2610 //      // will stay 0 indefinitely. The target of the animation is the
   2611 //      // 'width' attribute of the <rect> element that is the parent of
   2612 //      // the <set> element. When 'width' is 0 the rect is not rendered
   2613 //      // according to the spec[7].
   2614 //      //
   2615 //      // Some properties of the SVGAnimatedLength[2] and SVGLength[8]
   2616 //      // are also inspected. Before the animation starts both baseVal
   2617 //      // and animVal contain the same values[2]. Then the animation is
   2618 //      // started by calling the beginElement method[9]. To make sure
   2619 //      // that time passes between the triggering of the animation and
   2620 //      // the time that the values are read out (in test #66), the
   2621 //      // current time is set to 1000 seconds using the setCurrentTime
   2622 //      // method[10].
   2623 //      //
   2624 //      // [2] http://www.w3.org/TR/SVG11/types.html#InterfaceSVGAnimatedLength
   2625 //      // [7] http://www.w3.org/TR/SVG11/shapes.html#RectElement
   2626 //      // [8] http://www.w3.org/TR/SVG11/types.html#InterfaceSVGLength
   2627 //      // [9] http://www.w3.org/TR/SVG11/animate.html#DOMInterfaces
   2628 //      // [10] http://www.w3.org/TR/SVG11/struct.html#InterfaceSVGSVGElement
   2629 //
   2630      var svgns = "http://www.w3.org/2000/svg";
   2631      var svgdoc = kungFuDeathGrip.firstChild.contentDocument;
   2632      assert(svgdoc, "contentDocument failed on <iframe> for svg document.");
   2633      var svg = svgdoc.documentElement;
   2634      var rect = svgdoc.createElementNS(svgns, "rect");
   2635      rect.setAttribute("fill", "red");
   2636      rect.setAttribute("width", "100");
   2637      rect.setAttribute("height", "100");
   2638      rect.setAttribute("id", "rect");
   2639 //      var anim = svgdoc.createElementNS(svgns, "set");
   2640 //      anim.setAttribute("begin", "indefinite");
   2641 //      anim.setAttribute("to", "0");
   2642 //      anim.setAttribute("attributeName", "width");
   2643 //      anim.setAttribute("dur", "indefinite");
   2644 //      anim.setAttribute("fill", "freeze");
   2645 //      rect.appendChild(anim);
   2646      svg.appendChild(rect);
   2647      assert(rect.width, "SVG DOM interface SVGRectElement not supported.");
   2648 //      assert(rect.width.baseVal, "SVG DOM base type SVGAnimatedLength not supported.");
   2649 //      assert(rect.width.animVal, "SVG DOM base type SVGAnimatedLength not supported.");
   2650 //      assertEquals(SVGLength.SVG_LENGTHTYPE_NUMBER, 1, "Incorrect SVGLength.SVG_LENGTHTYPE_NUMBER constant value.");
   2651 //      assertEquals(rect.width.baseVal.unitType, SVGLength.SVG_LENGTHTYPE_NUMBER, "Incorrect unitType on width attribute.");
   2652      assertEquals(rect.getAttribute("width"), "100", "Incorrect value from getAttribute.");
   2653 //      assertEquals(rect.width.baseVal.valueInSpecifiedUnits, 100, "Incorrect valueInSpecifiedUnits value.");
   2654 //      assertEquals(rect.width.baseVal.value, 100, "Incorrect baseVal value before animation.");
   2655 //      assertEquals(rect.width.animVal.value, 100, "Incorrect animVal value before animation.");
   2656 //      anim.beginElement();
   2657 //      assertEquals(rect.width.baseVal.value, 100, "Incorrect baseVal value after starting animation.");
   2658 //      svg.setCurrentTime(1000); // setting 1 second to make sure that time != 0s when we check the animVal value
   2659 //      // the animation is then tested in the next test
   2660      return 5;
   2661    },
   2662    function () {
   2663 //      // test 76: SMIL in SVG, part 2, from Erik Dahlstrom
   2664 //      //
   2665 //      // About animVal[2]: "If the given attribute or property is
   2666 //      // being animated, contains the current animated value of the
   2667 //      // attribute or property, and both the object itself and its
   2668 //      // contents are readonly. If the given attribute or property is
   2669 //      // not currently being animated, contains the same value as
   2670 //      // 'baseVal'."
   2671 //      //
   2672 //      // Since the duration of the animation is indefinite the value
   2673 //      // is still being animated at the time it's queried. Now since
   2674 //      // the 'width' attribute was animated from its original value of
   2675 //      // "100" to the new value of "0" the animVal property must
   2676 //      // contain the value 0.
   2677 //      //
   2678 //      // [2] http://www.w3.org/TR/SVG11/types.html#InterfaceSVGAnimatedLength
   2679 //
   2680      var svgdoc = kungFuDeathGrip.firstChild.contentDocument;
   2681      assert(svgdoc, "contentDocument failed on <object> for svg document.");
   2682      var rect = svgdoc.getElementById("rect");
   2683      assert(rect, "Failed to find <rect> element in svg document.");
   2684 //      assertEquals(rect.width.animVal.value, 0, "Incorrect animVal value after svg animation.");
   2685      return 5;
   2686    },
   2687    function () {
   2688 //      // test 77: external SVG fonts, from Erik Dahlstrom
   2689 //      //
   2690 //      // SVGFonts are described here[3], and the relevant DOM methods
   2691 //      // used in the test are defined here[4].
   2692 //      //
   2693 //      // Note that in order to be more predictable the svg should be
   2694 //      // visible, so that clause "For non-rendering environments, the
   2695 //      // user agent shall make reasonable assumptions about glyph
   2696 //      // metrics." doesn't influence the results. We use 'opacity:0'
   2697 //      // to hide the SVG, but arguably it's still a "rendering
   2698 //      // environment".
   2699 //      //
   2700 //      // The font-size 4000 was chosen because that matches the
   2701 //      // unitsPerEm value in the svgfont, which makes it easy to check
   2702 //      // the glyph advances since they will then be exactly what was
   2703 //      // specified in the svgfont.
   2704 //      //
   2705 //      // [3] http://www.w3.org/TR/SVG11/fonts.html
   2706 //      // [4] http://www.w3.org/TR/SVG11/text.html#InterfaceSVGTextContentElement
   2707 //
   2708      var svgns = "http://www.w3.org/2000/svg";
   2709 //      var xlinkns = "http://www.w3.org/1999/xlink";
   2710      var svgdoc = kungFuDeathGrip.firstChild.contentDocument;
   2711      assert(svgdoc, "contentDocument failed on <object> for svg document.");
   2712      var svg = svgdoc.documentElement;
   2713      var text = svgdoc.createElementNS(svgns, "text");
   2714      text.setAttribute("y", "1em");
   2715      text.setAttribute("font-size", "4000");
   2716      text.setAttribute("font-family", "ACID3svgfont");
   2717      var textContent = svgdoc.createTextNode("abc");
   2718      text.appendChild(textContent);
   2719      svg.appendChild(text);
   2720      // The font-size 4000 was chosen because that matches the unitsPerEm value in the svgfont,
   2721      // which makes it easy to check the glyph advances since they will then be exactly what was specified in the svgfont.
   2722      assert(text.getNumberOfChars, "SVGTextContentElement.getNumberOfChars() not supported.");
   2723      assertEquals(text.getNumberOfChars(), 3, "getNumberOfChars returned incorrect string length.");
   2724 //      assertEquals(text.getComputedTextLength(), 4711+42+23, "getComputedTextLength failed.");
   2725 //      assertEquals(text.getSubStringLength(0,1), 42, "getSubStringLength #1 failed.");
   2726 //      assertEquals(text.getSubStringLength(0,2), 42+23, "getSubStringLength #2 failed.");
   2727 //      assertEquals(text.getSubStringLength(1,1), 23, "getSubStringLength #3 failed.");
   2728 //      assertEquals(text.getSubStringLength(1,0), 0, "getSubStringLength #4 failed.");
   2729 ///* COMMENTED OUT BECAUSE SVGWG KEEPS CHANGING THIS
   2730 // *     var code = -1000;
   2731 // *     try {
   2732 // *       var sl = text.getSubStringLength(1,3);
   2733 // *     } catch(e) {
   2734 // *       code = e.code;
   2735 // *     }
   2736 // *     assertEquals(code, DOMException.INDEX_SIZE_ERR, "getSubStringLength #1 didn't throw exception.");
   2737 // *     code = -1000;
   2738 // *     try {
   2739 // *       var sl = text.getSubStringLength(0,4);
   2740 // *     } catch(e) {
   2741 // *       code = e.code;
   2742 // *     }
   2743 // *     assertEquals(code, DOMException.INDEX_SIZE_ERR, "getSubStringLength #2 didn't throw exception.");
   2744 // *     code = -1000;
   2745 // *     try {
   2746 // *       var sl = text.getSubStringLength(3,0);
   2747 // *     } catch(e) {
   2748 // *       code = e.code;
   2749 // *     }
   2750 // *     assertEquals(code, DOMException.INDEX_SIZE_ERR, "getSubStringLength #3 didn't throw exception.");
   2751 // */
   2752 //      code = -1000;
   2753 //      try {
   2754 //        var sl = text.getSubStringLength(-17,20);
   2755 //      } catch(e) {
   2756 //        code = 0; // negative values might throw native exception since the api accepts only unsigned values
   2757 //      }
   2758 //      assert(code == 0, "getSubStringLength #4 didn't throw exception.");
   2759 //      assertEquals(text.getStartPositionOfChar(0).x, 0, "getStartPositionOfChar(0).x returned invalid value.");
   2760 //      assertEquals(text.getStartPositionOfChar(1).x, 42, "getStartPositionOfChar(1).x returned invalid value.");
   2761 //      assertEquals(text.getStartPositionOfChar(2).x, 42+23, "getStartPositionOfChar(2).x returned invalid value.");
   2762 //      assertEquals(text.getStartPositionOfChar(0).y, 4000, "getStartPositionOfChar(0).y returned invalid value.");
   2763 //      code = -1000;
   2764 //      try {
   2765 //        var val = text.getStartPositionOfChar(-1);
   2766 //      } catch(e) {
   2767 //        code = 0; // negative values might throw native exception since the api accepts only unsigned values
   2768 //      }
   2769 //      assert(code == 0, "getStartPositionOfChar #1 exception failed.");
   2770 //      code = -1000;
   2771 //      try {
   2772 //        var val = text.getStartPositionOfChar(4);
   2773 //      } catch(e) {
   2774 //        code = e.code;
   2775 //      }
   2776 //      assertEquals(code, DOMException.INDEX_SIZE_ERR, "getStartPositionOfChar #2 exception failed.");
   2777 //      assertEquals(text.getEndPositionOfChar(0).x, 42, "getEndPositionOfChar(0).x returned invalid value.");
   2778 //      assertEquals(text.getEndPositionOfChar(1).x, 42+23, "getEndPositionOfChar(1).x returned invalid value.");
   2779 //      assertEquals(text.getEndPositionOfChar(2).x, 42+23+4711, "getEndPositionOfChar(2).x returned invalid value.");
   2780 //      code = -1000;
   2781 //      try {
   2782 //        var val = text.getEndPositionOfChar(-17);
   2783 //      } catch(e) {
   2784 //        code = 0; // negative values might throw native exception since the api accepts only unsigned values
   2785 //      }
   2786 //      assert(code == 0, "getEndPositionOfChar #1 exception failed.");
   2787 //      code = -1000;
   2788 //      try {
   2789 //        var val = text.getEndPositionOfChar(4);
   2790 //      } catch(e) {
   2791 //        code = e.code;
   2792 //      }
   2793 //      assertEquals(code, DOMException.INDEX_SIZE_ERR, "getEndPositionOfChar #2 exception failed.");
   2794      return 5;
   2795    },
   2796    function () {
   2797 //      // test 78: SVG textPath and getRotationOfChar(), from Erik Dahlstrom
   2798 //      //
   2799 //      // The getRotationOfChar[4] method fetches the midpoint rotation
   2800 //      // of a glyph defined by a character (in this testcase there is
   2801 //      // a simple 1:1 correspondence between the two). The path is
   2802 //      // defined in the svg.xml file, and consists of first a line
   2803 //      // going down, then followed by a line that has a 45 degree
   2804 //      // slope and then followed by a horizontal line. The length of
   2805 //      // each path segment have been paired with the advance of each
   2806 //      // glyph, so that each glyph will be on each of the three
   2807 //      // different path segments (see text on a path layout rules[5]).
   2808 //      // Thus the rotation of the first glyph is 90 degrees, the
   2809 //      // second 45 degrees and the third 0 degrees.
   2810 //      //
   2811 //      // [4] http://www.w3.org/TR/SVG11/text.html#InterfaceSVGTextContentElement
   2812 //      // [5] http://www.w3.org/TR/SVG11/text.html#TextpathLayoutRules
   2813 //
   2814      var svgns = "http://www.w3.org/2000/svg";
   2815 //      var xlinkns = "http://www.w3.org/1999/xlink";
   2816      var svgdoc = kungFuDeathGrip.firstChild.contentDocument;
   2817      assert(svgdoc, "contentDocument failed on <object> for svg document.");
   2818      var svg = svgdoc.documentElement;
   2819 //      var text = svgdoc.createElementNS(svgns, "text");
   2820 //      text.setAttribute("font-size", "4000");
   2821 //      text.setAttribute("font-family", "ACID3svgfont");
   2822 //      var textpath = svgdoc.createElementNS(svgns, "textPath");
   2823 //      textpath.setAttributeNS(xlinkns, "xlink:href", "#path");
   2824 //      var textContent = svgdoc.createTextNode("abc");
   2825 //      textpath.appendChild(textContent);
   2826 //      text.appendChild(textpath);
   2827 //      svg.appendChild(text);
   2828 //      assertEquals(text.getRotationOfChar(0), 90, "getRotationOfChar(0) failed.");
   2829 //      assertEquals(text.getRotationOfChar(1), 45, "getRotationOfChar(1) failed.");
   2830 //      assertEquals(text.getRotationOfChar(2), 0, "getRotationOfChar(2) failed.");
   2831 //      var code = -1000;
   2832 //      try {
   2833 //        var val = text.getRotationOfChar(-1)
   2834 //      } catch(e) {
   2835 //        code = e.code;
   2836 //      }
   2837 //      assertEquals(code, DOMException.INDEX_SIZE_ERR, "getRotationOfChar #1 exception failed.");
   2838 //      code = -1000;
   2839 //      try {
   2840 //        var val = text.getRotationOfChar(4)
   2841 //      } catch(e) {
   2842 //        code = e.code;
   2843 //      }
   2844 //      assertEquals(code, DOMException.INDEX_SIZE_ERR, "getRotationOfChar #2 exception failed.");
   2845      return 5;
   2846    },
   2847    function () {
   2848 //      // test 79: a giant test for <svg:font>, from Cameron McCormack
   2849 //      // This tests various features of SVG fonts from SVG 1.1.  It consists of
   2850 //      // a <text> element with 33 characters, styled using an SVG font that has
   2851 //      // different advance values for each glyph.  The script uses
   2852 //      // SVGTextElementContent.getStartPositionOfChar() to determine where the
   2853 //      // glyph corresponding to each character was placed, and thus to work out
   2854 //      // whether the SVG font was used correctly.
   2855 //      //
   2856 //      // The font uses 100 units per em, and the text is set in 100px.  Since
   2857 //      // font-size gives the size of the em box
   2858 //      // (http://www.w3.org/TR/SVG11/text.html#DOMInterfaces), the scale of the
   2859 //      // coordinate system for the glyphs is the same as the SVG document.
   2860 //      //
   2861 //      // The expectedAdvances array holds the expected advance value for each
   2862 //      // character, and expectedKerning holds the (negative) kerning for each
   2863 //      // character.  getPositionOfChar() returns the actual x coordinate for the
   2864 //      // glyph, corresponding to the given character, and if multiple characters
   2865 //      // correspond to the same glyph, the same position value is returned for
   2866 //      // each of those characters.
   2867 //      //
   2868 //      // Here are the reasonings for the advance/kerning values.  Note that for
   2869 //      // a given character at index i, the expected position is
   2870 //      // sum(expectedAdvances[0:i-1] + expectedKerning[0:i-1]).
   2871 //      //
   2872 //      // char     advance  kerning  reasoning
   2873 //      // -------  -------  -------  --------------------------------------------------
   2874 //      // A        10000    0        Normal character mapping to a single glyph.
   2875 //      // B        0        0        First character of a two character glyph, so the
   2876 //      //                            current position isn't advanced until the second
   2877 //      //                            character.
   2878 //      // C        200      0        Second character of a two character glyph, so now
   2879 //      //                            the position is advanced.
   2880 //      // B        300      0        Although there is a glyph for "BC" in the font,
   2881 //      //                            it appears after the glyph for "B", so the single
   2882 //      //                            character glyph for "B" should be chosen instead.
   2883 //      // D        1100     0        Normal character mapping to a single glyph.
   2884 //      // A        10000    200      Kerning of -200 is specified in the font between
   2885 //      //                            the "A" and "EE" glyphs.
   2886 //      // E        0        0        The first character of a two character glyph "EE".
   2887 //      // E        1300     0        The second character of a two character glyph.
   2888 //      // U        0        0        This is a glyph for the six characters "U+0046",
   2889 //      //                            which happen to look like a valid unicode range.
   2890 //      //                            This tests that the <glyph unicode=""> in the
   2891 //      //                            font matches exact strings rather than a range,
   2892 //      //                            as used in the kerning elements.
   2893 //      // +        0        0        Second character of six character glyph.
   2894 //      // 0        0        0        Third character of six character glyph.
   2895 //      // 0        0        0        Fourth character of six character glyph.
   2896 //      // 4        0        0        Fifth character of six character glyph.
   2897 //      // 6        1700     0        Sixth character of six character glyph.
   2898 //      // U        0        0        The same six character glyph that looks like a
   2899 //      //                            Unicode range.  One of the kerning elements has
   2900 //      //                            u1="U+0046" u2="U+0046", which shouldn't match
   2901 //      //                            this, because those attributes are interpreted
   2902 //      //                            as Unicode ranges if they are, and normal
   2903 //      //                            strings otherwise.  Thus there should be no
   2904 //      //                            kerning between these two glyphs.
   2905 //      // G        2300     200      Kerning is between this character and the next
   2906 //      //                            "G", since there is an <hkern> element that
   2907 //      //                            uses a Unicode range on its u1="" attribute
   2908 //      //                            and a glyph name on its g2="" attribute which
   2909 //      //                            both match "G".
   2910 //      // G        2300     0        Normal character with kerning before it.
   2911 //      // H        3100     0        A glyph with graphical content describing the
   2912 //      //                            glyph, rather than a d="" attribute.
   2913 //      // I        4300     0        Glyphs are checked in document order for one
   2914 //      //                            that matches, but the first glyph with
   2915 //      //                            unicode="I" also has lang="zh", which disqualifies
   2916 //      //                            it.  Thus the second glyph with unicode="I"
   2917 //      //                            is chosen.
   2918 //      // I        4100     0        Since this I has xml:lang="zh" on it in the text,
   2919 //      //                            the first glyph with lang="zh" matches.
   2920 //      // J        4700     -4700    A normal glyph with kerning between the "J" and the
   2921 //      //                            next glyph "A" equal to the advance of the "J"
   2922 //      //                            glyph, so the position should stay the same.
   2923 //      // A        10000    0        Normal glyph with kerning before it.
   2924 //      // K        5900     0        The first glyph with unicode="K" does not match,
   2925 //      //                            since it has orientation="v", so the second
   2926 //      //                            glyph with unicode="K" is chosen.
   2927 //      // <spc>    6100     0        The space character should select the glyph with
   2928 //      //                            unicode=" ", despite it having a misleading
   2929 //      //                            glyph-name="L".
   2930 //      // L        6700     0        The "L" character should select the glyph with
   2931 //      //                            unicode=" ", despite it having a misleading
   2932 //      //                            glyph-name="spacev".
   2933 //      // A        2900     0        An <altGlyph> element is used to select the
   2934 //      //                            glyph for U+10085 instead of the one for "A".
   2935 //      // U+10085  2900     0        Tests glyph selection with a non-plane-0
   2936 //      //                            character.
   2937 //      // A        10000    0        A final normal character.
   2938 //      //
   2939 //      // In addition, the script tests the value returned by
   2940 //      // SVGTextContentElement.getNumberOfChars(), which in this case should be 34.
   2941 //      // If it returned 33, then it incorrectly counted Unicode characters instead
   2942 //      // of UTF-16 codepoints (probably).
   2943 //      //
   2944 //      // See http://www.w3.org/TR/SVG11/fonts.html for a description of the glyph
   2945 //      // matching rules, and http://www.w3.org/TR/SVG11/text.html#DOMInterfaces
   2946 //      // for a description of getStartPositionOfChar() and getNumberOfChars().
   2947 //      //
   2948 //      // Note also that the test uses DOMImplementation.createDocument() to create
   2949 //      // the SVG document.  This seems to cause browsers trouble for the SVG DOM
   2950 //      // interfaces, since the document isn't being "rendered" as it might be
   2951 //      // if it were in an <iframe>.  Changing the test to use an <iframe> will
   2952 //      // at least let you see the main part of the test running.
   2953 //
   2954      var NS = {
   2955        svg: 'http://www.w3.org/2000/svg',
   2956        xml: 'http://www.w3.org/XML/1998/namespace',
   2957 //        xlink: 'http://www.w3.org/1999/xlink'
   2958      };
   2959 
   2960      var doc = kungFuDeathGrip.childNodes[1].contentDocument;
   2961      while (doc.hasChildNodes())
   2962        doc.removeChild(doc.firstChild);
   2963      doc.appendChild(doc.createElementNS(NS.svg, "svg:svg"));
   2964 //
   2965 //      var e = function (n, as, cs) {
   2966 //        var elt = doc.createElementNS(NS.svg, n);
   2967 //        if (as) {
   2968 //          for (var an in as) {
   2969 //            var idx = an.indexOf(':');
   2970 //            var ns = null;
   2971 //            if (idx != -1)
   2972 //              ns = NS[an.substring(0, idx)];
   2973 //            elt.setAttributeNS(ns, an, as[an]);
   2974 //          }
   2975 //        }
   2976 //        if (cs) {
   2977 //          for (var i in cs) {
   2978 //            var c = cs[i];
   2979 //            elt.appendChild(typeof c == 'string' ? doc.createTextNode(c) : c);
   2980 //          }
   2981 //        }
   2982 //        return elt;
   2983 //      }
   2984 //
   2985 //      doc.documentElement.appendChild(e('font', { 'horiz-adv-x': '10000'}, [e('font-face', { 'font-family': 'HCl', 'units-per-em': '100', 'ascent': '1000', 'descent': '500'}), e('missing-glyph', null, [e('path', { 'd': 'M100,0 h800 v-100 h-800 z'})]), e('glyph', { 'unicode': 'A', 'd': 'M100,0 h100 v-100 h-100 z'}), e('glyph', { 'unicode': 'BC', 'd': 'M100,0 h100 v-100 h-100 z', 'horiz-adv-x': '200'}), e('glyph', { 'unicode': 'B', 'd': 'M100,0 h100 v-100 h-100 z', 'horiz-adv-x': '300'}), e('glyph', { 'unicode': 'C', 'd': 'M100,0 h100 v-100 h-100 z', 'horiz-adv-x': '500'}), e('glyph', { 'unicode': 'BD', 'd': 'M100,0 h100 v-100 h-100 z', 'horiz-adv-x': '700'}), e('glyph', { 'unicode': 'D', 'd': 'M100,0 h100 v-100 h-100 z', 'horiz-adv-x': '1100'}), e('glyph', { 'unicode': 'EE', 'd': 'M100,0 h100 v-100 h-100 z', 'horiz-adv-x': '1300',  'glyph-name': 'grapefruit'}), e('glyph', { 'unicode': 'U+0046', 'd': 'M100,0 h100 v-100 h-100 z', 'horiz-adv-x': '1700'}), e('glyph', { 'unicode': 'F', 'd': 'M100,0 h100 v-100 h-100 z', 'horiz-adv-x': '1900'}), e('glyph', { 'unicode': 'G', 'd': 'M100,0 h100 v-100 h-100 z', 'horiz-adv-x': '2300', 'glyph-name': 'gee'}), e('glyph', { 'unicode': '\uD800\uDC85', 'd': 'M100,0 h100 v-100 h-100 z', 'horiz-adv-x': '2900', 'id': 'astral'}), e('glyph', { 'unicode': 'H', 'horiz-adv-x': '3100'}, [e('path', { 'd': 'M100,0 h100 v-100 h-100 z'})]), e('glyph', { 'unicode': 'I', 'd': 'M100,0 h100 v-100 h-100 z', 'horiz-adv-x': '4100', 'lang': 'zh'}), e('glyph', { 'unicode': 'I', 'd': 'M100,0 h100 v-100 h-100 z', 'horiz-adv-x': '4300'}), e('glyph', { 'unicode': 'J', 'd': 'M100,0 h100 v-100 h-100 z', 'horiz-adv-x': '4700'}), e('glyph', { 'unicode': 'K', 'd': 'M100,0 h100 v-100 h-100 z', 'horiz-adv-x': '5300', 'orientation': 'v'}), e('glyph', { 'unicode': 'K', 'd': 'M100,0 h100 v-100 h-100 z', 'horiz-adv-x': '5900'}), e('glyph', { 'unicode': ' ', 'd': 'M100,0 h100 v-100 h-100 z', 'horiz-adv-x': '6100', 'glyph-name': 'L'}), e('glyph', { 'unicode': 'L', 'd': 'M100,0 h100 v-100 h-100 z', 'horiz-adv-x': '6700', 'glyph-name': 'space'}), e('hkern', { 'u1': 'A', 'u2': 'EE', 'k': '1000'}), e('hkern', { 'u1': 'A', 'g2': 'grapefruit', 'k': '-200'}), e('hkern', { 'u1': 'U+0046', 'u2': 'U+0046', 'k': '-200'}), e('hkern', { 'u1': 'U+0047-0047', 'g2': 'gee', 'k': '-200'}), e('hkern', { 'u1': 'J', 'u2': 'A', 'k': '4700'})]));
   2986 //      doc.documentElement.appendChild(e('text', { 'y': '100', 'font-family': 'HCl', 'font-size': '100px', 'letter-spacing': '0px', 'word-spacing': '0px'}, ['ABCBDAEEU+0046U+0046GGHI', e('tspan', { 'xml:lang': 'zh'}, ['I']), 'JAK L', e('altGlyph', { 'xlink:href': '#astral'}, ['A']), '\uD800\uDC85A']));
   2987 //
   2988 //      var t = doc.documentElement.lastChild;
   2989 //
   2990 //      var characterDescriptions = [
   2991 //        "a normal character",
   2992 //        "the first character of a two-character glyph",
   2993 //        "the second character of a two-character glyph",
   2994 //        "a normal character, which shouldn't be the first character of a two-character glyph",
   2995 //        "a normal character, which shouldn't be the second character of a two-character glyph",
   2996 //        "a normal character, which has some kerning after it",
   2997 //        "the first character of a two-character glyph, which has some kerning before it",
   2998 //        "the second character of a two-character glyph, which has some kerning before it",
   2999 //        "the first character of a six-character glyph, which happens to look like a Unicode range, where the range-specified glyph has kerning after it, but this glyph does not",
   3000 //        "the second character of a six-character glyph, which happens to look like a Unicode range, where the range-specified glyph has kerning after it, but this glyph does not",
   3001 //        "the third character of a six-character glyph, which happens to look like a Unicode range, where the range-specified glyph has kerning after it, but this glyph does not",
   3002 //        "the fourth character of a six-character glyph, which happens to look like a Unicode range, where the range-specified glyph has kerning after it, but this glyph does not",
   3003 //        "the fifth character of a six-character glyph, which happens to look like a Unicode range, where the range-specified glyph has kerning after it, but this glyph does not",
   3004 //        "the sixth character of a six-character glyph, which happens to look like a Unicode range, where the range-specified glyph has kerning after it, but this glyph does not",
   3005 //        "the first character of a six-character glyph, which happens to look like a Unicode range, where the range-specified glyph has kerning before it, but this glyph does not",
   3006 //        "the second character of a six-character glyph, which happens to look like a Unicode range, where the range-specified glyph has kerning before it, but this glyph does not",
   3007 //        "the third character of a six-character glyph, which happens to look like a Unicode range, where the range-specified glyph has kerning before it, but this glyph does not",
   3008 //        "the fourth character of a six-character glyph, which happens to look like a Unicode range, where the range-specified glyph has kerning before it, but this glyph does not",
   3009 //        "the fifth character of a six-character glyph, which happens to look like a Unicode range, where the range-specified glyph has kerning before it, but this glyph does not",
   3010 //        "the sixth character of a six-character glyph, which happens to look like a Unicode range, where the range-specified glyph has kerning before it, but this glyph does not",
   3011 //        "a normal character, which has some kerning after it that is specified by glyph name",
   3012 //        "a normal character, which has some kerning before it that is specified by glyph name",
   3013 //        "a normal character, whose glyph is given by child graphical content of the <glyph> element",
   3014 //        "a normal character, whose glyph should not match the one with a lang=\"\" attribute on it",
   3015 //        "a normal character, whose glyph should match the one with a lang=\"\" attribute on it",
   3016 //        "a normal character, which has some kerning after it that is equal to the advance of the character",
   3017 //        "a normal character, which has some kerning before it that is equal to the advance of the previous character",
   3018 //        "a normal character, whose glyph should not match the one with an orientation=\"v\" attribute on it",
   3019 //        "a space character, which has a misleading glyph-name=\"\" attribute",
   3020 //        "a normal character, which has a misleading glyph-name=\"\" attribute",
   3021 //        "a normal character, whose glyph is chosen to be another by using <altGlyph>",
   3022 //        "a character not in Plane 0 (high surrogate pair)",
   3023 //        "a character not in Plane 0 (low surrogate pair)",
   3024 //        "a normal character",
   3025 //      ];
   3026 //
   3027 //      var expectedAdvances = [
   3028 //        10000,       // A
   3029 //        0,           // BC [0]
   3030 //        200,         // BC [1]
   3031 //        300,         // B
   3032 //        1100,        // D
   3033 //        10000,       // A
   3034 //        0,           // EE [0]
   3035 //        1300,        // EE [1]
   3036 //        0,           // U+0046 [0]
   3037 //        0,           // U+0046 [1]
   3038 //        0,           // U+0046 [2]
   3039 //        0,           // U+0046 [3]
   3040 //        0,           // U+0046 [4]
   3041 //        1700,        // U+0046 [5]
   3042 //        0,           // U+0046 [0]
   3043 //        0,           // U+0046 [1]
   3044 //        0,           // U+0046 [2]
   3045 //        0,           // U+0046 [3]
   3046 //        0,           // U+0046 [4]
   3047 //        1700,        // U+0046 [5]
   3048 //        2300,        // G
   3049 //        2300,        // G
   3050 //        3100,        // H
   3051 //        4300,        // I
   3052 //        4100,        // I (zh)
   3053 //        4700,        // J
   3054 //        10000,       // A
   3055 //        5900,        // K
   3056 //        6100,        // <space>
   3057 //        6700,        // L
   3058 //        2900,        // A (using &#x10085; altGlyph)
   3059 //        0,           // &#x10085; high surrogate pair
   3060 //        2900,        // &#x10085; low surrogate pair
   3061 //        10000,       // A
   3062 //      ];
   3063 //
   3064 //      var expectedKerning = [
   3065 //        0,           // A
   3066 //        0,           // BC [0]
   3067 //        0,           // BC [1]
   3068 //        0,           // B
   3069 //        0,           // D
   3070 //        200,         // A
   3071 //        0,           // EE [0]
   3072 //        0,           // EE [1]
   3073 //        0,           // U+0046 [0]
   3074 //        0,           // U+0046 [1]
   3075 //        0,           // U+0046 [2]
   3076 //        0,           // U+0046 [3]
   3077 //        0,           // U+0046 [4]
   3078 //        0,           // U+0046 [5]
   3079 //        0,           // U+0046 [0]
   3080 //        0,           // U+0046 [1]
   3081 //        0,           // U+0046 [2]
   3082 //        0,           // U+0046 [3]
   3083 //        0,           // U+0046 [4]
   3084 //        0,           // U+0046 [5]
   3085 //        200,         // G
   3086 //        0,           // G
   3087 //        0,           // H
   3088 //        0,           // I
   3089 //        0,           // I (zh)
   3090 //        -4700,       // J
   3091 //        0,           // A
   3092 //        0,           // K
   3093 //        0,           // <space>
   3094 //        0,           // L
   3095 //        0,           // A (using &#x10085; altGlyph)
   3096 //        0,           // &#x10085; high surrogate pair
   3097 //        0,           // &#x10085; low surrogate pair
   3098 //        0,           // A
   3099 //      ];
   3100 //
   3101 //      assertEquals(t.getNumberOfChars(), expectedAdvances.length, 'SVGSVGTextElement.getNumberOfChars() incorrect');
   3102 //
   3103 //      var expectedPositions = [0];
   3104 //      for (var i = 0; i < expectedAdvances.length; i++)
   3105 //        expectedPositions.push(expectedPositions[i] + expectedAdvances[i] + expectedKerning[i]);
   3106 //
   3107 //      var actualPositions = [];
   3108 //      for (var i = 0; i < t.getNumberOfChars(); i++)
   3109 //        actualPositions.push(t.getStartPositionOfChar(i).x);
   3110 //      actualPositions.push(t.getEndPositionOfChar(t.getNumberOfChars() - 1).x);
   3111 //
   3112 //      for (var i = 0; i < expectedPositions.length; i++) {
   3113 //        if (expectedPositions[i] != actualPositions[i]) {
   3114 //          var s = "character position " + i + ", which is ";
   3115 //          if (i == 0) {
   3116 //            s += "before " + characterDescriptions[0];
   3117 //          } else if (i == expectedPositions.length - 1) {
   3118 //            s += "after " + characterDescriptions[characterDescriptions.length - 1];
   3119 //          } else {
   3120 //            s += "between " + characterDescriptions[i - 1] + " and " + characterDescriptions[i];
   3121 //          }
   3122 //          s += ", is " + actualPositions[i] + " but should be " + expectedPositions[i] + ".";
   3123 //          fail(s);
   3124 //        }
   3125 //      }
   3126      return 5;
   3127    },
   3128    function () {
   3129      // test 80: remove the iframes and the object
   3130      // (when fixing the test for http://dbaron.org/mozilla/visited-privacy,
   3131      // this section was flipped around so the linktest check is done first;
   3132      // this is to prevent the 'retry' from failing the second time since by
   3133      // then the kungFuDeathGrip has been nullified, if we do it first)
   3134      // first, check that the linktest is loaded
   3135      var a = document.links[1];
   3136      assert(!(a == null), "linktest was null");
   3137      assert(a.textContent == "YOU SHOULD NOT SEE THIS AT ALL", "linktest link couldn't be found"); // changed text when fixing http://dbaron.org/mozilla/visited-privacy
   3138      if (a.hasAttribute('class'))
   3139        return "retry"; // linktest onload didn't fire -- could be a networking issue, check that first
   3140      assert(!(kungFuDeathGrip == null), "kungFuDeathGrip was null");
   3141      assert(!(kungFuDeathGrip.parentNode == null), "kungFuDeathGrip.parentNode was null");
   3142      // ok, now remove the iframes
   3143      kungFuDeathGrip.parentNode.removeChild(kungFuDeathGrip);
   3144      kungFuDeathGrip = null;
   3145      // check that the xhtml files worked right
   3146      assert(notifications['xhtml.1'], "Script in XHTML didn't execute");
   3147      assert(!notifications['xhtml.2'], "XML well-formedness error didn't stop script from executing");
   3148      assert(!notifications['xhtml.3'], "Script executed despite having wrong namespace");
   3149      return 5;
   3150    },
   3151 
   3152    // bucket 6: ECMAScript
   3153    function () {
   3154      // test 81: length of arrays with elisions at end
   3155      var t1 = [,];
   3156      var t2 = [,,];
   3157      assertEquals(t1.length, 1, "[,] doesn't have length 1");
   3158      assertEquals(t2.length, 2, "[,,] doesn't have length 2");
   3159      return 6;
   3160    },
   3161    function () {
   3162      // test 82: length of arrays with elisions in the middle
   3163      var t3 = ['a', , 'c'];
   3164      assertEquals(t3.length, 3, "['a',,'c'] doesn't have length 3");
   3165      assert(0 in t3, "no 0 in t3");
   3166      assert(!(1 in t3), "unexpected 1 in t3");
   3167      assert(2 in t3, "no 2 in t3");
   3168      assertEquals(t3[0], 'a', "t3[0] wrong");
   3169      assertEquals(t3[2], 'c', "t3[2] wrong");
   3170      return 6;
   3171    },
   3172    function () {
   3173      // test 83: array methods
   3174      var x = ['a', 'b', 'c'];
   3175      assertEquals(x.unshift('A', 'B', 'C'), 6, "array.unshift() returned the wrong value");
   3176      var s = x.join(undefined);
   3177      assertEquals(s, 'A,B,C,a,b,c', "array.join(undefined) used wrong separator"); // qv 15.4.4.5:3
   3178      return 6;
   3179    },
   3180    function () {
   3181      // test 84: converting numbers to strings
   3182      assertEquals((0.0).toFixed(4), "0.0000", "toFixed(4) wrong for 0");
   3183      assertEquals((-0.0).toFixed(4), "0.0000", "toFixed(4) wrong for -0");
   3184      assertEquals((0.00006).toFixed(4), "0.0001", "toFixed(4) wrong for 0.00006");
   3185      assertEquals((-0.00006).toFixed(4), "-0.0001", "toFixed(4) wrong for -0.00006");
   3186      assertEquals((0.0).toExponential(4), "0.0000e+0", "toExponential(4) wrong for 0");
   3187      assertEquals((-0.0).toExponential(4), "0.0000e+0", "toExponential(4) wrong for -0");
   3188      var x = 7e-4;
   3189      assertEquals(x.toPrecision(undefined), x.toString(undefined), "toPrecision(undefined) was wrong");
   3190      return 6;
   3191    },
   3192    function () {
   3193      // test 85: strings and string-related operations
   3194      // substr() and negative numbers
   3195      assertEquals("scathing".substr(-7, 3), "cat", "substr() wrong with negative numbers");
   3196      return 6;
   3197    },
   3198    function () {
   3199      // test 86: Date tests -- methods passed no arguments
   3200      var d = new Date();
   3201      assert(isNaN(d.setMilliseconds()), "calling setMilliseconds() with no arguments didn't result in NaN");
   3202      assert(isNaN(d), "date wasn't made NaN");
   3203      assert(isNaN(d.getDay()), "date wasn't made NaN");
   3204      return 6;
   3205    },
   3206    function () {
   3207      // test 87: Date tests -- years
   3208      var d1 = new Date(Date.UTC(99.9, 6));
   3209      assertEquals(d1.getUTCFullYear(), 1999, "Date.UTC() didn't do proper 1900 year offsetting");
   3210      var d2 = new Date(98.9, 6);
   3211      assertEquals(d2.getFullYear(), 1998, "new Date() didn't do proper 1900 year offsetting");
   3212      return 6;
   3213    },
   3214    function () {
   3215      // test 88: ES3 section 7.6:3 (unicode escapes can't be used to put non-identifier characters into identifiers)
   3216      // and there's no other place for them in the syntax (other than strings, of course)
   3217      var ok = false;
   3218      try {
   3219        eval("var test = { };\ntest.i= 0;\ntest.i\\u002b= 1;\ntest.i;\n");
   3220      } catch (e) {
   3221        ok = true;
   3222      }
   3223      assert(ok, "\\u002b was not considered a parse error in script");
   3224      return 6;
   3225    },
   3226    function () {
   3227      // test 89: Regular Expressions
   3228      var ok = true;
   3229      // empty classes in regexps
   3230      try {
   3231        eval("/TA[])]/.exec('TA]')");
   3232        // JS regexps aren't like Perl regexps, if their character
   3233        // classes start with a ] that means they're empty. So this
   3234        // is a syntax error; if we get here it's a bug.
   3235        ok = false;
   3236      } catch (e) { }
   3237      assert(ok, "orphaned bracket not considered parse error in regular expression literal");
   3238      try {
   3239        if (eval("/[]/.exec('')"))
   3240          ok = false;
   3241      } catch (e) {
   3242        ok = false;
   3243      }
   3244      assert(ok, "/[]/ either failed to parse or matched something");
   3245      return 6;
   3246    },
   3247    function () {
   3248      // test 90: Regular Expressions
   3249      // not back references.
   3250      assert(!(/(1)\0(2)/.test("12")), "NUL in regexp incorrectly ignored");
   3251      assert((/(1)\0(2)/.test("1" + "\0" + "2")), "NUL in regexp didn't match correctly");
   3252      assert(!(/(1)\0(2)/.test("1\02")), "octal 2 unexpectedly matched NUL");
   3253      assertEquals(nullInRegexpArgumentResult, "passed", "failed //.test() check"); // nothing to see here, move along now
   3254      // back reference to future capture
   3255      var x = /(\3)(\1)(a)/.exec('cat'); // the \3 matches the empty string, qv. ES3:15.10.2.9
   3256      assert(x, "/(\\3)(\\1)(a)/ failed to match 'cat'");
   3257      assertEquals(x.length, 4, "/(\\3)(\\1)(a)/ failed to return four components");
   3258      assertEquals(x[0], "a", "/(\\3)(\\1)(a)/ failed to find 'a' in 'cat'");
   3259      assert(x[1] === "", "/(\\3)(\\1)(a)/ failed to find '' in 'cat' as first part");
   3260      assert(x[2] === "", "/(\\3)(\\1)(a)/ failed to find '' in 'cat' as second part");
   3261      assertEquals(x[3], "a", "/(\\3)(\\1)(a)/ failed to find 'a' in 'cat' as third part");
   3262      // negative lookahead
   3263      x = /(?!(text))(te.t)/.exec("text testing");
   3264      assertEquals(x.length, 3, "negative lookahead test failed to return the right number of bits");
   3265      assertEquals(x[0], "test", "negative lookahead test failed to find the right text");
   3266      assert(x[1] === undefined, "negative lookahead test failed to return undefined for negative lookahead capture");
   3267      assert(x[2] === "test", "negative lookahead test failed to find the right second capture");
   3268      return 6;
   3269    },
   3270    function () {
   3271      // test 91: check that properties are enumerable by default
   3272      var test = {
   3273        constructor: function() { return 1; },
   3274        toString: function() { return 2; },
   3275        toLocaleString: function() { return 3; },
   3276        valueOf: function() { return 4; },
   3277        hasOwnProperty: function() { return 5; },
   3278        isPrototypeOf: function() { return 6; },
   3279        propertyIsEnumerable: function() { return 7; },
   3280        prototype: function() { return 8; },
   3281        length: function() { return 9; },
   3282        unique: function() { return 10; }
   3283      };
   3284      var results = [];
   3285      for (var property in test)
   3286        results.push([test[property](), property]);
   3287      results.sort(function(a, b) {
   3288        if (a[0] < b[0]) return -1;
   3289        if (a[0] > b[0]) return 1;
   3290        return 0;
   3291      });
   3292      assertEquals(results.length, 10, "missing properties");
   3293      for (var index = 0; index < 10; index += 1)
   3294        assertEquals(results[index][0], index+1, "order wrong at results["+index+"] == ");
   3295      var index = 0;
   3296      assertEquals(results[index++][1], "constructor", "failed to find constructor in expected position");
   3297      assertEquals(results[index++][1], "toString", "failed to find toString in expected position");
   3298      assertEquals(results[index++][1], "toLocaleString", "failed to find toLocaleString in expected position");
   3299      assertEquals(results[index++][1], "valueOf", "failed to find valueOf in expected position");
   3300      assertEquals(results[index++][1], "hasOwnProperty", "failed to find hasOwnProperty in expected position");
   3301      assertEquals(results[index++][1], "isPrototypeOf", "failed to find isPrototypeOf in expected position");
   3302      assertEquals(results[index++][1], "propertyIsEnumerable", "failed to find propertyIsEnumerable in expected position");
   3303      assertEquals(results[index++][1], "prototype", "failed to find prototype in expected position");
   3304      assertEquals(results[index++][1], "length", "failed to find length in expected position");
   3305      assertEquals(results[index++][1], "unique", "failed to find unique in expected position");
   3306      return 6;
   3307    },
   3308    function () {
   3309      // test 92: internal properties of Function objects
   3310      // constructor is not ReadOnly
   3311      var f1 = function () { 1 };
   3312      f1.prototype.constructor = "hello world";
   3313      var f1i = new f1();
   3314      assert(f1i.constructor === "hello world", "Function object's prototype's constructor was ReadOnly");
   3315      // constructor is DontEnum (indeed, no properties at all on a new Function object)
   3316      var f2 = function () { 2 };
   3317      var f2i = new f2();
   3318      var count = 0;
   3319      for (var property in f2i) {
   3320        assert(property != "constructor", "Function object's prototype's constructor was not DontEnum");
   3321        count += 1;
   3322      }
   3323      assertEquals(count, 0, "Function object had unexpected properties");
   3324      // constructor is not DontDelete
   3325      var f3 = function (a, b) { 3 };
   3326      delete f3.prototype.constructor;
   3327      var f3i = new f3();
   3328      assertEquals(f3i.constructor, Object.prototype.constructor, "Function object's prototype's constructor was DontDelete (or got magically replaced)");
   3329      return 6;
   3330    },
   3331    function () {
   3332      // test 93: FunctionExpression semantics
   3333      var functest;
   3334      var vartest = 0;
   3335      var value = (function functest(arg) {
   3336        if (arg)
   3337          return 1;
   3338        vartest = 1;
   3339        functest = function (arg) { return 2; }; // this line does nothing as 'functest' is ReadOnly here
   3340        return functest(true); // this is therefore tail recursion and returns 1
   3341      })(false);
   3342      assertEquals(vartest, 1, "rules in 10.1.4 not followed in FunctionBody");
   3343      assertEquals(value, 1, "semantics of FunctionExpression: function Identifier ... not followed");
   3344      assert(!functest, "Property in step 4 of FunctionExpression: function Identifier ... leaked to parent scope");
   3345      return 6;
   3346    },
   3347    function () {
   3348      // test 94: exception scope
   3349      var test = 'pass';
   3350      try {
   3351        throw 'fail';
   3352      } catch (test) {
   3353        test += 'ing';
   3354      }
   3355      assertEquals(test, 'pass', 'outer scope poisoned by exception catch{} block');
   3356      return 6;
   3357    },
   3358    function () {
   3359      // test 95: types of expressions
   3360      var a = []; var s;
   3361      s = a.length = "2147483648";
   3362      assertEquals(typeof s, "string", "type of |\"2147483648\"| is not string");
   3363      return 6;
   3364    },
   3365    function () {
   3366      // test 96: encodeURI() and encodeURIComponent() and null bytes
   3367      assertEquals(encodeURIComponent(String.fromCharCode(0)), '%00', "encodeURIComponent failed to encode U+0000");
   3368      assertEquals(encodeURI(String.fromCharCode(0)), '%00', "encodeURI failed to encode U+0000");
   3369      return 6;
   3370    },
   3371 
   3372    // URIs
   3373    function () {
   3374      // test 97: data: URI parsing
   3375      assertEquals(d1, "one", "data: failed as escaped");
   3376      assertEquals(d2, "two", "data: failed as base64");
   3377      assertEquals(d3, "three", "data: failed as base64 escaped");
   3378      assertEquals(d4, "four", "data: failed as base64 with spaces");
   3379      assertEquals(d5, "five's", "data: failed with backslash");
   3380      return 7;
   3381    },
   3382 
   3383    // XHTML
   3384    function () {
   3385      // test 98: XHTML and the DOM
   3386      // (special test)
   3387      var doctype = document.implementation.createDocumentType("html", "-//W3C//DTD XHTML 1.0 Strict//EN", "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd");
   3388 // COMMENTED OUT FOR 2011 UPDATE - doctypes are moving towards having an owner, like other nodes
   3389 //      assertEquals(doctype.ownerDocument, null, "doctype's ownerDocument was wrong after creation");
   3390      var doc = document.implementation.createDocument("http://www.w3.org/1999/xhtml", "html", doctype);
   3391      doc.documentElement.appendChild(doc.createElementNS("http://www.w3.org/1999/xhtml", "head"));
   3392      doc.documentElement.appendChild(doc.createElementNS("http://www.w3.org/1999/xhtml", "body"));
   3393      var t = doc.createElementNS("http://www.w3.org/1999/xhtml", "title");
   3394      doc.documentElement.firstChild.appendChild(t);
   3395      // ok we have a conforming XHTML1 doc in |doc| now.
   3396      assertEquals(doctype.ownerDocument, doc, "doctype's ownerDocument didn't change when it was assigned to another document");
   3397      assertEquals(doc.title, "", "document had unexpected title");
   3398      t.textContent = "Sparrow";
   3399      assertEquals(doc.title, "Sparrow", "document.title did not update dynamically");
   3400      doc.body.appendChild(doc.createElementNS("http://www.w3.org/1999/xhtml", "form"));
   3401      assertEquals(doc.forms.length, 1, "document.forms not updated after inserting a form");
   3402      return 7;
   3403    },
   3404 
   3405    // Sanity
   3406    function () {
   3407      // test 99: check for the weirdest bug ever
   3408      var a = document.createElement('a');
   3409      a.setAttribute('href', 'http://www.example.com/');
   3410      a.appendChild(document.createTextNode('www.example.com'));
   3411      a.href = 'http://hixie.ch/';
   3412      assertEquals(a.firstChild.data, "www.example.com", "sanity did not prevail");
   3413      a.href = 'http://damowmow.com/';
   3414      assertEquals(a.firstChild.data, "www.example.com", "final test failed");
   3415      return 7;
   3416    }
   3417 
   3418  ];
   3419  window.parent.postMessage({num_tests: tests.length}, "*");
   3420  var log = '';
   3421  var delay = 10;
   3422  var score = 0, index = 0, retry = 0, errors = 0;
   3423  function update() {
   3424    var span = document.getElementById('score'); // not cached by JS
   3425    span.nextSibling.removeAttribute('class'); // no-op after first loop
   3426    span.nextSibling.nextSibling.firstChild.data = tests.length; // no-op after first loop
   3427    if (index < tests.length) {
   3428      var zeroPaddedIndex = index < 10 ? '0' + index : index;
   3429      try {
   3430        var beforeTest = new Date();
   3431        var result = tests[index]();
   3432        var elapsedTest = new Date() - beforeTest;
   3433        if (result == "retry") {
   3434          // some tests uses this magical mechanism to wait for support files to load
   3435          // we will give this test 500 attempts (5000ms) before aborting
   3436          retry += 1;
   3437          if (retry < 500) {
   3438            setTimeout(update, delay);
   3439            return;
   3440          }
   3441          fail("timeout -- could be a networking issue");
   3442        } else if (result) {
   3443          var bucket = document.getElementById('bucket' + result);
   3444          if (bucket)
   3445            bucket.className += 'P';
   3446          score += 1;
   3447          if (retry > 0) {
   3448            errors += 1;
   3449            log += "Test " + zeroPaddedIndex + " passed, but took " + retry + " attempts (less than perfect).\n";
   3450          } else if (elapsedTest > 33) { // 30fps
   3451            errors += 1;
   3452            log += "Test " + zeroPaddedIndex + " passed, but took " + elapsedTest + "ms (less than 30fps)\n";
   3453          }
   3454        } else {
   3455          fail("no error message");
   3456        }
   3457        window.parent.postMessage({test: index, result: "pass"}, "*");
   3458      } catch (e) {
   3459        var s;
   3460        if (e.message)
   3461          s = e.message.replace(/\s+$/, "");
   3462        else
   3463          s = e;
   3464        errors += 1;
   3465        log += "Test " + zeroPaddedIndex + " failed: " + s + "\n";
   3466        window.parent.postMessage({test: index, result: "fail", message: s}, "*");
   3467      };
   3468      retry = 0;
   3469      index += 1;
   3470      span.firstChild.data = score;
   3471      setTimeout(update, delay);
   3472    } else {
   3473      var endTime = new Date();
   3474      var elapsedTime = ((endTime - startTime) - (delay * tests.length)) / 1000;
   3475      log += "Total elapsed time: " + elapsedTime.toFixed(2) + "s";
   3476      if (errors == 0)
   3477        log += "\nNo JS errors and no timing issues.\nWas the rendering pixel-for-pixel perfect too?";
   3478      document.documentElement.classList.remove("reftest-wait");
   3479    }
   3480  }
   3481  function report(event) {
   3482    // for debugging either click the "A" in "Acid3" (to get an alert) or shift-click it (to get a report)
   3483    if (event.shiftKey) {
   3484      var w = window.open();
   3485      w.document.write('<pre>Failed ' + (tests.length - score) + ' of ' + tests.length + ' tests.\n' +
   3486                       log.replace(/&/g,'&amp;').replace(RegExp('<', 'g'), '&lt;').replace('\0', '\\0') +
   3487                       '<\/pre>');
   3488      w.document.close();
   3489    } else {
   3490      alert('Failed ' + (tests.length - score) + ' test' + (score == 1 ? '' : 's') + '.\n' + log)
   3491    }
   3492  }
   3493 </script>
   3494 <body onload="update()  /* this attribute's value is tested in one of the tests */ ">
   3495  <h1 onclick="report(event)">Acid3</h1>
   3496  <div class="buckets"
   3497   ><p id="bucket1" class="z"></p
   3498   ><p id="bucket2" class="z"></p
   3499   ><p id="bucket3" class="z"></p
   3500   ><p id="bucket4" class="z"></p
   3501   ><p id="bucket5" class="z"></p
   3502   ><p id="bucket6" class="z"></p>
   3503  </div>
   3504  <p id="result"><span id="score">JS</span><span id="slash" class="hidden">/</span><span>?</span></p>
   3505  <!-- The following line is used in a number of the tests. It is done using document.write() to sidestep complaints of validity. -->
   3506  <script type="text/javascript">document.write('<map name=""><area href="" shape="rect" coords="2,2,4,4" alt="<\'>"><iframe src="empty.png">FAIL<\/iframe><iframe src="empty.txt">FAIL<\/iframe><iframe src="empty.html" id="selectors"><\/iframe><form action="" name="form"><input type=HIDDEN><\/form><table><tr><td><p><\/tbody> <\/table><\/map>');</script>
   3507  <p id="instructions">To pass the test,<span></span> a browser must use its default settings, the animation has to be smooth, the score has to end on 100/100, and the final page has to look exactly, pixel for pixel, like <a href="reference.sub.html">this reference rendering</a>.</p>
   3508  <p id="remove-last-child-test">Scripting must be enabled to use this test.</p>
   3509 </body>
   3510 </html>