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 𐂅 altGlyph) 3059 // 0, // 𐂅 high surrogate pair 3060 // 2900, // 𐂅 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 𐂅 altGlyph) 3096 // 0, // 𐂅 high surrogate pair 3097 // 0, // 𐂅 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,'&').replace(RegExp('<', 'g'), '<').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>