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