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