blob: 71224bd3cebe0a770810e73ca2b50ce686159ff9 [file] [log] [blame]
eric@webkit.orga7f130f2008-03-28 09:23:39 +00001<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
2<html>
3 <title>The Acid3 Test</title>
4 <script type="text/javascript">
rniwa@webkit.org14a295a2012-06-13 07:49:51 +00005 if (window.testRunner) {
6 testRunner.waitUntilDone();
7 testRunner.keepWebHistory();
commit-queue@webkit.org0b4112d2012-05-25 21:41:04 +00008 }
eric@webkit.orgaadb79d2010-06-11 09:10:18 +00009
eric@webkit.orga7f130f2008-03-28 09:23:39 +000010 var startTime = new Date();
11 </script>
12 <style type="text/css">
13
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; }
17
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 */
eric@webkit.orgaadb79d2010-06-11 09:10:18 +000021 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 */
eric@webkit.orga7f130f2008-03-28 09:23:39 +000022 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); }
eric@webkit.orgaadb79d2010-06-11 09:10:18 +000031 map::after { position: absolute; top: 18px; left: 638px; content: "X"; background: fuchsia; color: white; font: 20px/1 AcidAhemTest; }
eric@webkit.orga7f130f2008-03-28 09:23:39 +000032 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; }
35
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; }
39
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 */
44
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; }
52
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; }
65
66 /* The line-height for the .bucket div is worked out as follows:
67 *
68 * The div.bucket element has a line box with a few
69 * inline-blocks. Each inline-block consists of:
70 *
71 * 2.0em vertical-align from baseline to bottom of inline-block
72 * 1px bottom border
73 * 1.0em bottom padding
74 * 1.0em top padding
75 * 1px top border
76 *
77 * The biggest inline-block has font-size: 40px.
78 *
79 * Thus the distance from the baseline to the top of the biggest
80 * inline-block is (2em+1em+1em)*2em*20px+2px = 162px.
81 *
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.
85 *
86 * (Why use line-height:0 and font-size:0? Well:
87 *
88 * The div.bucket line box would have a height that is the maximum
89 * of the following two sums:
90 *
91 * 1: half-leading + font descent at 1em + font ascent at 1em + half-leading
92 * 2: half-leading + font descent at 1em + 162px
93 *
94 * Now the half-leading is (line-height - (font-ascent + font-descent))/2, so that is really:
95 *
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
98 *
99 * Which simplify to:
100 *
101 * 1: line-height
102 * 2: line-height/2 + (font descent - font-ascent)/2 + 162px
103 *
104 * So if the following expression is true:
105 *
106 * line-height > line-height/2 + (font descent - font-ascent)/2 + 162px
107 *
108 * That is, if this is true:
109 *
110 * line-height > font descent - font-ascent + 324px
111 *
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).
119 *
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
123 * installed.
124 *
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.
128 *
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.
132 *
133 */
134
135 /* rules specific to the tests below */
136 #instructions:last-child { white-space: pre-wrap; white-space: x-bogus; }
eric@webkit.orgaadb79d2010-06-11 09:10:18 +0000137 /* 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; }
eric@webkit.orga7f130f2008-03-28 09:23:39 +0000143 #\ { 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; }
145 </style>
146
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) -->
149
150 <!-- the next five script blocks are part of one of the tests -->
151 <script type="text/javascript">
152 var d1 = "fail";
153 var d2 = "fail";
154 var d3 = "fail";
155 var d4 = "fail";
156 var d5 = "fail";
157 </script>
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>
163
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";
167 </script>
168
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;
175 }
176 function fail(message) {
177 throw { message: message };
178 }
179 function assert(condition, message) {
180 if (!condition)
181 fail(message);
182 }
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);
188 }
189 }
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'));
198 return doc;
199 }
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);
205 var ruleCount = 0;
206 tester(doc, function (selector) {
207 ruleCount += 1;
208 style.appendChild(doc.createTextNode(selector + " { z-index: " + ruleCount + "; }\n"));
209 return ruleCount;
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);
214 });
215 }
216 var kungFuDeathGrip = null; // used to hold things from test to test
217 var tests = [
218
219 // there are 6 buckets with 16 tests each, plus four special tests (0, 97, 98, and 99).
220
221 // Remove the "JS required" message and the <script> element in the <body>
222 function () {
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
225 // removing script:
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");
234 return 7;
235 },
236
237 // bucket 1: DOM Traversal, DOM Range, HTTP
238 // DOM Traversal
239 function () {
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)
242 var iteration = 0;
243 var exception = "Roses";
244 var test = function(node) {
245 iteration += 1;
246 switch (iteration) {
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
249 default: throw 0;
250 };
251 };
252 var check = function(o, method) {
253 var ok = false;
254 try {
255 o[method]();
256 } catch (e) {
257 if (e === exception)
258 ok = true;
259 }
260 assert(ok, "method " + o + "." + method + "() didn't forward exception");
261 };
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
283 return 1;
284 },
285 function () {
286 // test 2: Removing nodes during iteration
287 var count = 0;
288 var expect = function(n, node1, node2) {
289 count += 1;
290 assert(n == count, "reached expectation " + n + " when expecting expectation " + count);
291 assertEquals(node1, node2, "expectation " + count + " failed");
292 };
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'));
298 var callCount = 0;
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
309 ];
310 var i = doc.createNodeIterator(doc.documentElement.lastChild, 0xFFFFFFFF, function (node) { return filterFunctions[callCount++](node); }, true);
311 // * B 1 2 3 4
312 expect(2, i.nextNode(), doc.body); // filter 0
313 // [B] * 1 2 3 4
314 expect(4, i.nextNode(), t1); // filter 1
315 // B [1] * 2 3 4
316 expect(6, i.nextNode(), t2); // filter 2
317 // B 1 [2] * 3 4
318 expect(8, i.nextNode(), t3); // filter 3
319 // B 1 2 [3] *
320 doc.body.appendChild(t4);
321 // B 1 2 [3] * 4
322 expect(10, i.nextNode(), t4); // filter 4
323 // B 1 2 3 [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
328 // B 1 2 * [3]
329 expect(15, i.previousNode(), t2); // filter 7
330 // B 1 * (2) [3]
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
335 // being traversed."
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."
339 // B 1 * [3]
340 expect(17, i.previousNode(), t1); // filter 8
341 // B [1] * 3
342 return 1;
343 },
344 function () {
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;
350 }
351 var count = 0;
352 var test = function() {
353 if (count > 3 && count < 12)
354 doc.body.appendChild(doc.body.firstChild);
355 count += 1;
356 return (count % 2 == 0) ? 1 : 2;
357 };
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");
367 return 1;
368 },
369 function () {
370 // test 4: ignoring whitespace text nodes with node iterators
371 var count = 0;
372 var expect = function(node1, node2) {
373 count += 1;
374 assertEquals(node1, node2, "expectation " + count + " failed");
375 };
376 var allButWS = function (node) {
377 if (node.nodeType == 3 && node.data.match(/^\s*$/))
378 return 2;
379 return 1;
380 };
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);
462 return 1;
463 },
464 function () {
465 // test 5: ignoring whitespace text nodes with tree walkers
466 var count = 0;
467 var expect = function(node1, node2) {
468 count += 1;
469 assertEquals(node1, node2, "expectation " + count + " failed");
470 };
471 var allButWS = function (node) {
472 if (node.nodeType == 3 && node.data.match(/^\s*$/))
473 return 3;
474 return 1;
475 };
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);
500 return 1;
501 },
502 function () {
503 // test 6: walking outside a tree
504 var doc = getTestDocument();
505 var p = doc.createElement('p');
506 doc.body.appendChild(p);
507 var b = doc.body;
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");
517 p.appendChild(b);
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");
521 return 1;
522 },
523
524 // DOM Range
525 function () {
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");
eric@webkit.orgaadb79d2010-06-11 09:10:18 +0000542 var endOffset = r.endOffset;
eric@webkit.orga7f130f2008-03-28 09:23:39 +0000543 r.insertNode(document.createComment("commented inserted to test ranges"));
eric@webkit.orgaadb79d2010-06-11 09:10:18 +0000544 r.setEnd(r.endContainer, endOffset + 1); // added to work around spec bug that smaug is blocking the errata for
eric@webkit.orga7f130f2008-03-28 09:23:39 +0000545 try {
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");
552 } finally {
553 document.removeChild(document.firstChild);
554 }
555 return 1;
556 },
557 function () {
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();
569 r.setStart(e2, 0);
570 r.setEnd(e3, 0);
571 assert(!r.collapsed, "non-empty range claims to be collapsed");
572 r.setEnd(e1, 0);
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()");
584 r.setEndAfter(root);
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()");
590 r.setStartAfter(e2);
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()");
596 var msg = '';
597 try {
598 r.setEndBefore(doc);
599 msg = "no exception thrown for setEndBefore() the document itself";
600 } catch (e) {
arv@chromium.org0e413152012-12-03 14:28:52 +0000601// 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';
604// else
605// if (e.INVALID_NODE_TYPE_ERR != 2)
606// msg = 'RangeException has no INVALID_NODE_TYPE_ERR';
607// else
608// if ("INVALID_ACCESS_ERR" in e)
609// msg = 'RangeException has DOMException constants';
610// else
611 if (e.code != e.INVALID_NODE_TYPE_ERR)
eric@webkit.orga7f130f2008-03-28 09:23:39 +0000612 msg = 'wrong exception raised from setEndBefore()';
613 }
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()");
620 r.collapse(false);
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()");
632 r.selectNode(e2);
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()");
638 return 1;
639 },
640 function () {
641 // test 9: extractContents() in a Document
642 var doc = getTestDocument();
643 var h1 = doc.createElement('h1');
644 var t1 = doc.createTextNode('Hello ');
645 h1.appendChild(t1);
646 var em = doc.createElement('em');
647 var t2 = doc.createTextNode('Wonderful');
648 em.appendChild(t2);
649 h1.appendChild(em);
650 var t3 = doc.createTextNode(' Kitty');
651 h1.appendChild(t3);
652 doc.body.appendChild(h1);
653 var p = doc.createElement('p');
654 var t4 = doc.createTextNode('How are you?');
655 p.appendChild(t4);
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");
660 r.setStart(t2, 6);
661 r.setEnd(p, 0);
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");
683 return 1;
684 },
685 function () {
686 // test 10: Ranges and Attribute Nodes
arv@chromium.org0e413152012-12-03 14:28:52 +0000687// 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");
703// e.id = 'result';
eric@webkit.orga7f130f2008-03-28 09:23:39 +0000704 return 1;
705 },
706 function () {
707 // test 11: Ranges and Comments
708 var msg;
709 var doc = getTestDocument();
710 var c1 = doc.createComment("11111");
711 doc.appendChild(c1);
712 var r = doc.createRange();
713 r.selectNode(c1);
714 msg = 'wrong exception raised';
715 try {
716 r.surroundContents(doc.createElement('a'));
717 msg = 'no exception raised';
718 } catch (e) {
719 if ('code' in e)
720 msg += '; code = ' + e.code;
arv@chromium.org0e413152012-12-03 14:28:52 +0000721 if (e.code == 3) // HIERARCHY_REQUEST_ERR
eric@webkit.orga7f130f2008-03-28 09:23:39 +0000722 msg = '';
723 }
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);
729 r.setStart(c2, 2);
730 r.setEnd(c3, 3);
731 var msg = 'wrong exception raised';
732 try {
733 r.surroundContents(doc.createElement('a'));
734 msg = 'no exception raised';
735 } catch (e) {
arv@chromium.org0e413152012-12-03 14:28:52 +0000736// COMMENTED OUT FOR 2011 UPDATE - DOM Core changes the exception from RangeException.BAD_BOUNDARYPOINTS_ERR (1) to DOMException.INVALID_STATE_ERR (11)
737// if ('code' in e)
738// msg += '; code = ' + e.code;
739// if (e.code == 1)
eric@webkit.orga7f130f2008-03-28 09:23:39 +0000740 msg = '';
741 }
742 assert(msg == '', "when trying to surround two halves of comment: " + msg);
743 assertEquals(r.toString(), "", "comments returned text");
744 return 1;
745 },
746 function () {
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');
751 p.appendChild(t1);
752 var t2 = doc.createTextNode('ABCDE');
753 p.appendChild(t2);
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
780 // the start.
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() + "'");
783 return 1;
784 },
785 function () {
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");
807 return 1;
808 },
809
810 // HTTP
811 function () {
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');
818 if (ps.length > 0) {
819 if (ps[0].firstChild && ps[0].firstChild.data && ps[0].firstChild.data == 'FAIL')
820 fail("PNG was parsed as HTML.");
821 }
822 }
823 return 1;
824 },
825 function () {
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');
832 if (ps.length > 0) {
833 if (ps[0].firstChild && ps[0].firstChild.data && ps[0].firstChild.data == 'FAIL')
834 fail("text/plain file was parsed as HTML");
835 }
836 }
837 return 1;
838 },
839 function () {
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";
847 oB.appendChild(oC);
848 oC.data = "resources/acid3/support-c.png";
849 oA.appendChild(oB);
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)
853 return 1;
854 },
855
856 // bucket 2: DOM2 Core and DOM2 Events
857 // Core
858 function () {
859 // test 17: hasAttribute
860 // missing attribute
861 assert(!document.getElementsByTagName('map')[0].hasAttribute('id'), "hasAttribute failure for 'id' on map");
862 // implied attribute
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");
867 return 2;
868 },
869 function () {
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");
arv@chromium.org0e413152012-12-03 14:28:52 +0000873// 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
eric@webkit.orga7f130f2008-03-28 09:23:39 +0000876 assertEquals(document.getElementById('score').firstChild.nodeType, 3, "text node nodeType wrong");
877 assertEquals(document.firstChild.nodeType, 10, "DOCTYPE nodeType wrong");
878 return 2;
879 },
880 function () {
881 // test 19: value of constants
882 var e = null;
883 try {
884 document.body.appendChild(document.documentElement);
885 // raises a HIERARCHY_REQUEST_ERR
886 } catch (err) {
887 e = err;
888 }
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()");
894 return 2;
895 },
896 function () {
897 // test 20: nulls bytes in various places
898 assert(!document.getElementById('bucket1\0error'), "null in getElementById() probably terminated string");
899 var ok = true;
900 try {
901 document.createElement('form\0div');
902 ok = false;
903 } catch (e) {
904 if (e.code != 5)
905 ok = false;
906 }
907 assert(ok, "didn't raise the right exception for null byte in createElement()");
908 return 2;
909 },
910 function () {
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");
918 return 2;
919 },
920 function () {
921 // test 22: createElement() with invalid tag names
922 var test = function (name) {
923 var result;
924 try {
925 var div = document.createElement(name);
926 } catch (e) {
927 result = e;
928 }
929 assert(result, "no exception for createElement('" + name + "')");
930 assertEquals(result.code, 5, "wrong exception for createElement('" + name + "')"); // INVALID_CHARACTER_ERR
931 }
932 test('<div>');
933 test('0div');
934 test('di v');
935 test('di<v');
936 test('-div');
937 test('.div');
938 return 2;
939 },
940 function () {
941 // test 23: createElementNS() with invalid tag names
942 var test = function (name, ns, code) {
943 var result;
944 try {
945 var div = document.createElementNS(ns, name);
946 } catch (e) {
947 result = e;
948 }
949 assert(result, "no exception for createElementNS('" + ns + "', '" + name + "')");
950 assertEquals(result.code, code, "wrong exception for createElementNS('" + ns + "', '" + name + "')");
951 }
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)
970 return 2;
971 },
972 function () {
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");
975 return 2;
976 },
977 function () {
978 // test 25: test namespace checking in createDocumentType, and
979 // check that exceptions that are thrown are DOMException objects
980 var message = "";
981 try {
982 document.implementation.createDocumentType('a:', '', ''); /* doesn't contain an illegal character; is malformed */
983 message = "failed to raise exception";
984 } catch (e) {
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";
989 }
990 if (message)
991 fail(message);
992 return 2;
993 },
994 function () {
995 // test 26: check that document tree survives while still accessible
996 var d;
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
1011 d = null;
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");
eric@webkit.orgaadb79d2010-06-11 09:10:18 +00001017 var loops = new Date().valueOf() * 2.813435e-9 - 2412; // increases linearly over time
eric@webkit.orga7f130f2008-03-28 09:23:39 +00001018 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...
1021 d = new Date();
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);
1025 d = d.parentNode;
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);
1030 }
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");
1037 return 2;
1038 },
1039 function () {
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");
1052 return 2;
1053 },
1054 function () {
1055 // test 28: getElementById()
1056 // ...and name=""
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'));
1061 div.id = " ";
1062 document.body.appendChild(div); // it's hidden by CSS
1063 assert(div === document.getElementById(" "), "getElementById() didn't return the right element");
1064 return 2;
1065 },
1066 function () {
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");
1074 return 2;
1075 },
1076
1077 // Events
1078 function () {
1079 // test 30: dispatchEvent()
1080 var count = 0;
1081 var ok = true;
1082 var test = function (event) {
1083 if (event.detail != 6)
1084 ok = false;
1085 count++;
1086 };
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");
1100 return 2;
1101 },
1102 function () {
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:
1110 var ok = true;
1111 var captureCount = 0;
1112 var testCapture = function (event) {
1113 ok = ok &&
1114 (event.type == 'click') &&
1115 (event.target == input) &&
1116 (event.currentTarget == div) &&
1117 (event.eventPhase == 1) &&
1118 (event.bubbles) &&
1119 (event.cancelable);
1120 captureCount++;
1121 event.stopPropagation(); // this shouldn't stop it from firing both times on the div element
1122 };
1123 var testBubble = function (event) {
1124 ok = false;
1125 };
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';
1132 input.click();
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");
1138 return 2;
1139 },
1140 function () {
1141 // test 32: events bubbling through Document node
1142 // event handler:
1143 var ok = true;
1144 var count = 0;
1145 var test = function (event) {
1146 count += 1;
1147 if (event.eventPhase != 3)
1148 ok = false;
1149 }
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';
1158 input.click();
1159 // unregister event handler
1160 document.body.removeEventListener('click', test, false);
1161 // check that it's removed for good
1162 input.click();
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");
1167 return 2;
1168 },
1169
1170 // bucket 3: DOM2 Views, DOM2 Style, and Selectors
1171 function () {
1172 // test 33: basic tests for selectors - classes, attributes
1173 var p;
1174 var builder = function(doc) {
1175 p = doc.createElement("p");
1176 doc.body.appendChild(p);
1177 };
1178 selectorTest(function (doc, add, expect) {
1179 builder(doc);
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");
1186 });
1187 selectorTest(function (doc, add, expect) {
1188 builder(doc);
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");
1192 });
1193 selectorTest(function (doc, add, expect) {
1194 builder(doc);
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");
1201 });
1202 selectorTest(function (doc, add, expect) {
1203 builder(doc);
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");
1213 });
1214 selectorTest(function (doc, add, expect) {
1215 builder(doc);
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");
1221 });
1222 return 3;
1223 },
1224 function () {
1225 // test 34: :lang() and [|=]
1226 var div1;
1227 var div2;
1228 var p;
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);
1240 };
1241 selectorTest(function (doc, add, expect) {
1242 builder(doc);
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)");
1247 });
1248 selectorTest(function (doc, add, expect) {
1249 builder(doc);
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");
1253 });
1254 return 3;
1255 },
1256 function () {
1257 // test 35: :first-child
1258 selectorTest(function (doc, add, expect) {
1259 var notFirst = 0;
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");
1285 });
1286 return 3;
1287 },
1288 function () {
1289 // test 36: :last-child
1290 var p1;
1291 var p2;
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);
1297 };
1298 selectorTest(function (doc, add, expect) {
1299 builder(doc);
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");
1306 p1.appendChild(p2);
1307 expect(p2, last, "failure 5");
1308 expect(p1, last, "failure 6");
1309 });
1310 selectorTest(function (doc, add, expect) {
1311 builder(doc);
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");
1318 });
1319 selectorTest(function (doc, add, expect) {
1320 builder(doc);
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");
1328 });
1329 return 3;
1330 },
1331 function () {
1332 // test 37: :only-child
1333 var p1;
1334 var p2;
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);
1340 };
1341 selectorTest(function (doc, add, expect) {
1342 builder(doc);
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");
1348 p1.appendChild(p2);
1349 expect(p2, only, "failure 4");
1350 expect(p1, only, "failure 5");
1351 });
1352 selectorTest(function (doc, add, expect) {
1353 builder(doc);
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");
1359 p2.appendChild(p1);
1360 expect(p2, only, "failure 9");
1361 expect(p1, only, "failure 10");
1362 });
1363 selectorTest(function (doc, add, expect) {
1364 builder(doc);
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");
1379 });
1380 selectorTest(function (doc, add, expect) {
1381 builder(doc);
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");
1396 });
1397 return 3;
1398 },
1399 function () {
1400 // test 38: :empty
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");
arv@chromium.org0e413152012-12-03 14:28:52 +00001420// 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");
1423// REPLACEMENT:
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
eric@webkit.orga7f130f2008-03-28 09:23:39 +00001429 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");
1432 });
1433 return 3;
1434 },
1435 function () {
1436 // test 39: :nth-child, :nth-last-child
1437 var ps;
1438 var builder = function(doc) {
1439 ps = [
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')
1453 ];
1454 for (var i = 0; i < ps.length; i += 1)
1455 doc.body.appendChild(ps[i]);
1456 };
1457 selectorTest(function (doc, add, expect) {
1458 builder(doc);
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);
1462 });
1463 selectorTest(function (doc, add, expect) {
1464 builder(doc);
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);
1468 });
1469 selectorTest(function (doc, add, expect) {
1470 builder(doc);
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);
1477 });
1478 selectorTest(function (doc, add, expect) {
1479 builder(doc);
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);
1486 });
1487 selectorTest(function (doc, add, expect) {
1488 builder(doc);
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);
1494 });
1495 return 3;
1496 },
1497 function () {
1498 // test 40: :first-of-type, :last-of-type, :only-of-type, :nth-of-type, :nth-last-of-type
1499 var elements;
1500 var builder = function(doc) {
1501 elements = [
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')
1516 ];
1517 for (var i = 0; i < elements.length; i += 1)
1518 doc.body.appendChild(elements[i]);
1519 };
1520 selectorTest(function (doc, add, expect) {
1521 builder(doc);
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);
1526 });
1527 selectorTest(function (doc, add, expect) {
1528 builder(doc);
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);
1533 });
1534 selectorTest(function (doc, add, expect) {
1535 builder(doc);
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);
1540 });
1541 selectorTest(function (doc, add, expect) {
1542 builder(doc);
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);
1547 });
1548 selectorTest(function (doc, add, expect) {
1549 builder(doc);
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);
1554 });
1555 selectorTest(function (doc, add, expect) {
1556 builder(doc);
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);
1561 });
1562 selectorTest(function (doc, add, expect) {
1563 builder(doc);
1564 var match = add(":nth-last-of-type(-5n+3)");
1565 var values;
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);
1577 });
1578 return 3;
1579 },
1580 function () {
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)");
1591 });
1592 return 3;
1593 },
1594 function () {
1595 // test 42: +, ~, >, and ' ' in dynamic situations
1596 selectorTest(function (doc, add, expect) {
1597 var div1 = doc.createElement('div');
1598 div1.id = "div1";
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");
1626 });
1627 selectorTest(function (doc, add, expect) {
1628 var div1 = doc.createElement('div');
1629 div1.id = "div1";
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");
1658 });
1659 selectorTest(function (doc, add, expect) {
1660 var div1 = doc.createElement('div');
1661 div1.id = "div1";
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");
1690 });
1691 return 3;
1692 },
1693 function () {
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);
1699 var neither = 0;
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");
1705 input.click();
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");
1712 });
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");
1725 input2.click();
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");
1736 });
1737 selectorTest(function (doc, add, expect) {
1738 var input = doc.createElement('input');
1739 input.type = 'button';
1740 doc.body.appendChild(input);
1741 var neither = 0;
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");
1751 });
1752 return 3;
1753 },
1754 function () {
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);
1761 add("html*.test");
1762 expect(doc.body, 0, "misparsed selectors");
1763 expect(p, 0, "really misparsed selectors");
1764 });
1765 return 3;
1766 },
1767 function () {
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");
1774 return 3;
1775 },
1776 function () {
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; } }'));
eric@webkit.orgaadb79d2010-06-11 09:10:18 +00001786 style.appendChild(doc.createTextNode('@media not all and (bogus) { #f { text-transform: uppercase; } }')); // commentd out but should not match
eric@webkit.orga7f130f2008-03-28 09:23:39 +00001787 style.appendChild(doc.createTextNode('@media only all and (bogus) { #g { text-transform: uppercase; } }'));
weinig@apple.comeec87e02008-04-22 03:33:12 +00001788 style.appendChild(doc.createTextNode('@media (bogus), all { #h { text-transform: uppercase; } }')); // matches
eric@webkit.orga7f130f2008-03-28 09:23:39 +00001789 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
weinig@apple.comeec87e02008-04-22 03:33:12 +00001792 style.appendChild(doc.createTextNode('@media all, (bogus) { #l { text-transform: uppercase; } }')); // matches
eric@webkit.orga7f130f2008-03-28 09:23:39 +00001793 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; } }'));
eric@webkit.orgaadb79d2010-06-11 09:10:18 +00001798 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
weinig@apple.comeec87e02008-04-22 03:33:12 +00001801 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
eric@webkit.orga7f130f2008-03-28 09:23:39 +00001805 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');
1813 p.id = names[i];
1814 doc.body.appendChild(p);
1815 }
1816 var count = 0;
1817 var check = function (c, e) {
1818 count += 1;
1819 var p = doc.getElementById(c);
1820 assertEquals(doc.defaultView.getComputedStyle(p, '').textTransform, e ? 'uppercase' : 'none', "case " + c + " failed (index " + count + ")");
1821 }
1822 check('a', true); // 1
1823 check('b', false);
1824 check('c', true);
1825 check('d', false);
1826 check('e', false);
eric@webkit.orgaadb79d2010-06-11 09:10:18 +00001827/* COMMENTED OUT BECAUSE THE CSSWG KEEP CHANGING THE RIGHT ANSWER FOR THIS CASE
1828 * check('f', false);
1829 */
eric@webkit.orga7f130f2008-03-28 09:23:39 +00001830 check('g', false);
weinig@apple.comeec87e02008-04-22 03:33:12 +00001831 check('h', true);
eric@webkit.orga7f130f2008-03-28 09:23:39 +00001832 check('i', true);
1833 check('j', true); // 10
1834 check('k', true);
weinig@apple.comeec87e02008-04-22 03:33:12 +00001835 check('l', true);
eric@webkit.orga7f130f2008-03-28 09:23:39 +00001836 check('m', true);
1837 check('n', true);
1838 check('o', true);
1839 check('p', false);
1840 check('q', false);
eric@webkit.orgaadb79d2010-06-11 09:10:18 +00001841/* COMMENTED OUT BECAUSE THE CSSWG KEEP CHANGING THE RIGHT ANSWER FOR THESE TOO APPARENTLY
1842 * check('r', true);
1843 * check('s', true);
1844 * check('t', true); // 20
1845 */
eric@webkit.orga7f130f2008-03-28 09:23:39 +00001846 check('u', false);
weinig@apple.comeec87e02008-04-22 03:33:12 +00001847 check('v', true);
eric@webkit.orga7f130f2008-03-28 09:23:39 +00001848 check('w', true);
1849 check('x', true);
1850 // here the viewport is 0x0
1851 check('y1', false); // 25
1852 check('y2', false);
1853 check('y3', false);
1854 check('y4', true);
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
1858 check('y2', false);
1859 check('y3', false);
1860 check('y4', false);
1861 document.getElementById("selectors").removeAttribute("style");
1862 // here the viewport is 0x0 again
1863 check('y1', false); // 33
1864 check('y2', false);
1865 check('y3', false);
1866 check('y4', true);
1867 return 3;
1868 },
1869 function () {
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) {
1876 var c = cursors[i];
1877 style.appendChild(doc.createTextNode('#' + c + ' { cursor: ' + c + '; }'));
1878 }
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) {
1884 var c = cursors[i];
1885 doc.body.id = c;
1886 assertEquals(doc.defaultView.getComputedStyle(doc.body, '').cursor, c, "cursor " + c + " not supported");
1887 }
1888 return 3;
1889 },
1890 function () {
1891 // test 48: :link and :visited
1892 var iframe = document.getElementById("selectors");
1893 var number = (new Date()).valueOf();
1894 var a = document.createElement('a');
eric@webkit.orgaadb79d2010-06-11 09:10:18 +00001895 a.appendChild(document.createTextNode('YOU SHOULD NOT SEE THIS AT ALL')); // changed text when fixing http://dbaron.org/mozilla/visited-privacy
eric@webkit.orga7f130f2008-03-28 09:23:39 +00001896 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");
1902 return 3;
1903 },
1904
1905 // bucket 4: HTML and the DOM
1906 // Tables
1907 function () {
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");
1952 return 4;
1953 },
1954 function () {
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");
1990 return 4;
1991 },
1992 function () {
1993 // test 51: test the ordering and creation of rows
1994 var table = document.createElement('table');
1995 var rows = [
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
2003 ];
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);
2010 table.insertRow(0);
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");
2029 return 4;
2030 },
2031
2032 // Forms
2033 function () {
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");
2040 return 4;
2041 },
2042 function () {
2043 // test 53: changing an <input> dynamically
2044 var f = document.createElement('form');
2045 var i = document.createElement('input');
2046 i.name = 'first';
2047 i.type = 'text';
2048 i.value = 'test';
2049 f.appendChild(i);
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");
2060 i.name = 'second';
2061 i.type = 'password';
2062 i.value = 'TEST';
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");
2073 return 4;
2074 },
2075 function () {
2076 // test 54: changing a parsed <input>
2077 var i = document.getElementsByTagName('input')[0];
2078 // initial values
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");
2081 // change values
2082 i.name = 'test';
2083 assertEquals(i.parentNode.elements.test, i, "input control's form didn't update");
2084 // check event handlers
2085 i.parentNode.action = 'javascript:';
2086 var called = false;
2087 i.parentNode.onsubmit = function (arg) {
2088 arg.preventDefault();
2089 called = true;
2090 };
2091 i.type = 'submit';
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");
2094 i.type = 'hIdDeN';
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");
2100 return 4;
2101 },
2102 function () {
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");
2127 return 4;
2128 },
2129 function () {
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);
2161 return 4;
2162 },
2163 function () {
2164 // test 57: HTMLSelectElement.add()
2165 var s = document.createElement('select');
2166 var o = document.createElement('option');
2167 s.add(o, null);
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");
2173 return 4;
2174 },
2175 function () {
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');
2182 s.appendChild(o1);
2183 s.appendChild(o2);
2184 s.appendChild(o3);
2185 assert(s.options[s.selectedIndex] === o2, "defaultSelected didn't take");
2186 return 4;
2187 },
2188 function () {
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");
2199 return 4;
2200 },
2201
2202 // Misc DOM2 HTML
2203 function () {
2204 // test 60: className vs "class" vs attribute nodes
2205 var span = document.getElementsByTagName('span')[0];
2206 span.setAttribute('class', 'kittens');
arv@chromium.org0e413152012-12-03 14:28:52 +00002207// 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");
eric@webkit.orga7f130f2008-03-28 09:23:39 +00002218 span.className = 'cats';
arv@chromium.org0e413152012-12-03 14:28:52 +00002219// 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");
eric@webkit.orga7f130f2008-03-28 09:23:39 +00002224 return 4;
2225 },
2226 function () {
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");
2238 p.className = '';
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");
2242 return 4;
2243 },
2244 function () {
2245 // test 62: check that DOM attributes and content attributes aren't equivalent
2246 var test;
2247 // <div class="">
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");
2256 // <label for="">
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");
2288 return 4;
2289 },
2290 function () {
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=''");
2297 return 4;
2298 },
2299 function () {
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");
2320 return 4;
2321 },
2322
2323 // bucket 5: Tests from the Acid3 Competition
2324 function () {
2325 // test 65: bring in a couple of SVG files and some HTML files dynamically - preparation for later tests in this bucket
arv@chromium.org0e413152012-12-03 14:28:52 +00002326 // NOTE FROM 2011 UPDATE: The svg.xml file still contains the SVG font, but it is no longer used
eric@webkit.orga7f130f2008-03-28 09:23:39 +00002327 kungFuDeathGrip = document.createElement('p');
2328 kungFuDeathGrip.className = 'removed';
2329 var iframe, object;
2330 // svg iframe
2331 iframe = document.createElement('iframe');
2332 iframe.onload = function () { kungFuDeathGrip.title += '1' };
2333 iframe.src = "resources/acid3/svg.xml";
2334 kungFuDeathGrip.appendChild(iframe);
2335 // object iframe
2336 object = document.createElement('object');
2337 object.onload = function () { kungFuDeathGrip.title += '2' };
2338 object.data = "resources/acid3/svg.xml";
2339 kungFuDeathGrip.appendChild(object);
2340 // xml iframe
2341 iframe = document.createElement('iframe');
2342 iframe.onload = function () { kungFuDeathGrip.title += '3' };
2343 iframe.src = "resources/acid3/empty.xml";
2344 kungFuDeathGrip.appendChild(iframe);
2345 // html iframe
2346 iframe = document.createElement('iframe');
2347 iframe.onload = function () { kungFuDeathGrip.title += '4' };
2348 iframe.src = "resources/acid3/empty.html";
2349 kungFuDeathGrip.appendChild(iframe);
2350 // html iframe
2351 iframe = document.createElement('iframe');
2352 iframe.onload = function () { kungFuDeathGrip.title += '5' };
2353 iframe.src = "resources/acid3/xhtml.1";
2354 kungFuDeathGrip.appendChild(iframe);
2355 // html iframe
2356 iframe = document.createElement('iframe');
2357 iframe.onload = function () { kungFuDeathGrip.title += '6' };
2358 iframe.src = "resources/acid3/xhtml.2";
2359 kungFuDeathGrip.appendChild(iframe);
2360 // html 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);
2367 return 5;
2368 },
2369 function () {
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');
2374 return 5;
2375 },
2376 function () {
2377 // test 67: removedNamedItemNS on missing attributes, from Sylvain Pasche
2378 var p = document.createElement("p");
2379 var msg = 'wrong exception raised';
2380 try {
2381 p.attributes.removeNamedItemNS("http://www.example.com/", "absent");
2382 msg = 'no exception raised';
2383 } catch (e) {
2384 if ('code' in e) {
2385 if (e.code == 8)
2386 msg = '';
2387 else
2388 msg += '; code = ' + e.code;
2389 }
2390 }
2391 assert(msg == '', "when calling removeNamedItemNS in a non existent attribute: " + msg);
2392 return 5;
2393 },
2394 function () {
2395 // test 68: UTF-16 surrogate pairs, from David Chan
2396 //
2397 // In The Unicode Standard 5.0, it is explicitly permitted to
2398 // allow malformed UTF-16, that is, to leave the string alone.
2399 // (http://www.unicode.org/versions/Unicode5.0.0):
2400 //
2401 // section 2.7: "...strings in ... ECMAScript are Unicode 16-bit
2402 // strings, but are not necessarily well-formed UTF-16
2403 // sequences. In normal processing, it can be far more
2404 // efficient to allow such strings to contain code unit
2405 // sequences that are not well-formed UTF-16 -- that is,
2406 // isolated surrogates"
2407 //
2408 // On the other hand, if the application wishes to ensure
2409 // well-formed character sequences, it may not permit the
2410 // malformed sequence and it must regard the first codepoint as
2411 // an error:
2412 //
2413 // Section 3.2: "C10. When a process interprets a code sequence
2414 // which purports to be in a Unicode character encoding form, it
2415 // shall treat ill-formed code unit sequences as an error
2416 // condition and shall not interpret such sequences as
2417 // characters.
2418 // [...]
2419 // For example, in UTF-8 every code unit of the form 110....2
2420 // must be followed by a code unit of the form 10......2. A
2421 // sequence such as 110.....2 0.......2 is ill-formed and must
2422 // never be generated. When faced with this ill-formed code unit
2423 // sequence while transforming or interpreting text, a
2424 // conformant process must treat the first code unit 110.....2
2425 // as an illegally terminated code unit sequence~Wfor example,
2426 // by signaling an error, filtering the code unit out, or
2427 // representing the code unit with a marker such as U+FFFD
2428 // replacement character."
2429 //
2430 // So it would be permitted to do any of the following:
2431 // 1) Leave the string alone
2432 // 2) Remove the unpaired surrogate
2433 // 3) Replace the unpaired surrogate with U+FFFD
2434 // 4) Throw an exception
2435
2436 try {
2437 var unpaired = String.fromCharCode(0xd863); // half a surrogate pair
2438 var before = unpaired + "text";
2439 var elt = document.createElement("input");
2440 elt.value = before;
2441 var after = elt.value;
2442 }
2443 catch(ex) {
2444 return 5; // Unpaired surrogate caused an exception - ok
2445 }
2446 if (after == before && before.length == 5)
2447 return 5; // Unpaired surrogate kept - ok
2448 if (after == "text")
2449 return 5; // Unpaired surrogate removed - ok
2450 var replacement = String.fromCharCode(0xfffd);
2451 if (after == replacement + "text")
2452 return 5; // Unpaired surrogate replaced - ok
2453 fail("Unpaired surrogate handled wrongly (input was '" + before + "', output was '" + after + "')");
2454 },
2455 function () {
2456 // test 69: check that the support files loaded -- preparation for the rest of the tests in this bucket
2457 assert(!(kungFuDeathGrip == null), "kungFuDeathGrip was null");
2458 assert(!(kungFuDeathGrip.title == null), "kungFuDeathGrip.title was null");
2459 if (kungFuDeathGrip.title.length < 7)
2460 return "retry";
2461 assert(!(kungFuDeathGrip.firstChild == null), "kungFuDeathGrip.firstChild was null");
2462 assert(!(kungFuDeathGrip.firstChild.contentDocument == null), "kungFuDeathGrip.firstChild.contentDocument was null");
2463 assert(!(kungFuDeathGrip.firstChild.contentDocument.getElementsByTagName == null), "kungFuDeathGrip.firstChild.contentDocument.getElementsByTagName was null");
2464 var t = kungFuDeathGrip.firstChild.contentDocument.getElementsByTagName('text')[0];
2465 assert(!(t == null), "t was null");
2466 assert(!(t.parentNode == null), "t.parentNode was null");
2467 assert(!(t.parentNode.removeChild == null), "t.parentNode.removeChild was null");
2468 t.parentNode.removeChild(t);
2469 return 5;
2470 },
2471 function () {
2472 // test 70: XML encoding test
2473 // the third child in kungFuDeathGrip is an ISO-8859-1 document sent as UTF-8.
2474 // q.v. XML 1.0, section 4.3.3 Character Encoding in Entities
2475 // this only tests one of a large number of conditions that should cause fatal errors
2476 var doc = kungFuDeathGrip.childNodes[2].contentDocument;
2477 if (!doc)
2478 return 5;
2479 if (doc.documentElement.tagName != "root")
2480 return 5;
2481 if (doc.documentElement.getElementsByTagName('test').length < 1)
2482 return 5;
2483 fail("UTF-8 encoded XML document with invalid character did not have a well-formedness error");
2484 },
2485 function () {
2486 // test 71: HTML parsing, from Simon Pieters and Anne van Kesteren
2487 var doc = kungFuDeathGrip.childNodes[3].contentDocument;
2488 assert(doc, "missing document for test");
2489 try {
2490 // siblings
2491 doc.open();
2492 doc.write("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\"><title><\/title><span><\/span><script type=\"text/javascript\"><\/script>");
2493 doc.close();
2494 assertEquals(doc.childNodes.length, 2, "wrong number of children in #document (first test)");
eric@webkit.orgaadb79d2010-06-11 09:10:18 +00002495 assertEquals(doc.firstChild.name.toUpperCase(), "HTML", "name wrong (first test)"); // changed 2009-08-13 to add .toUpperCase() for HTML5 compat
eric@webkit.orga7f130f2008-03-28 09:23:39 +00002496 assertEquals(doc.firstChild.publicId, "-//W3C//DTD HTML 4.0 Transitional//EN", "publicId wrong (first test)");
2497 if ((doc.firstChild.systemId != null) && (doc.firstChild.systemId != ""))
2498 fail("systemId wrong (first test)");
eric@webkit.orgaadb79d2010-06-11 09:10:18 +00002499 if (('internalSubset' in doc.firstChild) || doc.firstChild.internalSubset)
2500 assertEquals(doc.firstChild.internalSubset, null, "internalSubset wrong (first test)");
eric@webkit.orga7f130f2008-03-28 09:23:39 +00002501 assertEquals(doc.documentElement.childNodes.length, 2, "wrong number of children in HTML (first test)");
2502 assertEquals(doc.documentElement.firstChild.nodeName, "HEAD", "misplaced HEAD element (first test)");
2503 assertEquals(doc.documentElement.firstChild.childNodes.length, 1, "wrong number of children in HEAD (first test)");
2504 assertEquals(doc.documentElement.firstChild.firstChild.tagName, "TITLE", "misplaced TITLE element (first test)");
2505 assertEquals(doc.documentElement.lastChild.nodeName, "BODY", "misplaced BODY element (first test)");
2506 assertEquals(doc.documentElement.lastChild.childNodes.length, 2, "wrong number of children in BODY (first test)");
2507 assertEquals(doc.documentElement.lastChild.firstChild.tagName, "SPAN", "misplaced SPAN element (first test)");
2508 assertEquals(doc.documentElement.lastChild.lastChild.tagName, "SCRIPT", "misplaced SCRIPT element (first test)");
2509 // parent/child
2510 doc.open();
2511 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>");
2512 doc.close();
eric@webkit.orgaadb79d2010-06-11 09:10:18 +00002513 assertEquals(doc.childNodes.length, 2, "wrong number of children in #document (second test)");
2514 assertEquals(doc.firstChild.name.toUpperCase(), "HTML", "name wrong (second test)"); // changed 2009-08-13 to add .toUpperCase() for HTML5 compat
eric@webkit.orga7f130f2008-03-28 09:23:39 +00002515 assertEquals(doc.firstChild.publicId, "-//W3C//DTD HTML 4.01 Transitional//EN", "publicId wrong (second test)");
2516 assertEquals(doc.firstChild.systemId, "http://www.w3.org/TR/html4/loose.dtd", "systemId wrong (second test)");
eric@webkit.orgaadb79d2010-06-11 09:10:18 +00002517 if (('internalSubset' in doc.firstChild) || doc.firstChild.internalSubset)
2518 assertEquals(doc.firstChild.internalSubset, null, "internalSubset wrong (second test)");
eric@webkit.orga7f130f2008-03-28 09:23:39 +00002519 assertEquals(doc.documentElement.childNodes.length, 2, "wrong number of children in HTML (second test)");
2520 assertEquals(doc.documentElement.firstChild.nodeName, "HEAD", "misplaced HEAD element (second test)");
2521 assertEquals(doc.documentElement.firstChild.childNodes.length, 1, "wrong number of children in HEAD (second test)");
2522 assertEquals(doc.documentElement.firstChild.firstChild.tagName, "TITLE", "misplaced TITLE element (second test)");
2523 assertEquals(doc.documentElement.lastChild.nodeName, "BODY", "misplaced BODY element (second test)");
2524 assertEquals(doc.documentElement.lastChild.childNodes.length, 1, "wrong number of children in BODY (second test)");
2525 assertEquals(doc.documentElement.lastChild.firstChild.tagName, "SPAN", "misplaced SPAN element (second test)");
2526 assertEquals(doc.documentElement.lastChild.firstChild.firstChild.tagName, "SCRIPT", "misplaced SCRIPT element (second test)");
2527 } finally {
2528 // prepare the file for the next test
2529 doc.open();
2530 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=\"\">");
2531 doc.close();
2532 }
2533 return 5;
2534 },
2535 function () {
2536 // test 72: dynamic modification of <style> blocks' text nodes, from Jonas Sicking and Garret Smith
2537 var doc = kungFuDeathGrip.childNodes[3].contentDocument;
2538 assert(doc, "missing document for test");
2539 assert(doc.images[0], "prerequisite failed: no image");
2540 assertEquals(doc.images[0].height, 10, "prerequisite failed: style didn't affect image");
2541 doc.styleSheets[0].ownerNode.firstChild.data = "img { height: 20px; }";
2542 assertEquals(doc.images[0].height, 20, "change failed to take effect");
2543 doc.styleSheets[0].ownerNode.appendChild(doc.createTextNode("img { height: 30px; }"));
2544 assertEquals(doc.images[0].height, 30, "append failed to take effect");
2545 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
2546 doc.styleSheets[0].insertRule("img { height: 40px; }", 2);
2547 assertEquals(doc.images[0].height, 40, "insertRule failed to take effect");
2548 assertEquals(doc.styleSheets[0].cssRules.length, 3, "count of rules is wrong");
2549 assertEquals(rules.length, 3, "cssRules isn't live");
2550 // while we're at it, check some other things on doc.styleSheets:
2551 assert(doc.styleSheets[0].href === null, "internal stylesheet had a URI: " + doc.styleSheets[0].href);
2552 assert(document.styleSheets[0].href === null, "internal acid3 stylesheet had a URI: " + document.styleSheets[0].href);
2553 return 5;
2554 },
2555 function () {
2556 // test 73: nested events, from Jonas Sicking
2557 var doc = kungFuDeathGrip.childNodes[3].contentDocument;
2558 // implied events
2559 var up = 0;
2560 var down = 0;
2561 var button = doc.createElement("button");
2562 button.type = "button";
eric@webkit.orgaadb79d2010-06-11 09:10:18 +00002563 button.onclick = function () { up += 1; if (up < 10) button.click(); down += up; }; // not called
eric@webkit.orga7f130f2008-03-28 09:23:39 +00002564 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);
2565 var evt = doc.createEvent("HTMLEvents");
2566 evt.initEvent('test', false, false);
2567 button.dispatchEvent(evt);
2568 assertEquals(up, 20, "test event handler called the wrong number of times");
2569 assertEquals(down, 400, "test event handler called in the wrong order");
2570 return 5;
2571 },
2572 function () {
2573 // test 74: check getSVGDocument(), from Erik Dahlstrom
2574 // GetSVGDocument[6]: "In the case where an SVG document is
2575 // embedded by reference, such as when an XHTML document has an
2576 // 'object' element whose href (or equivalent) attribute
2577 // references an SVG document (i.e., a document whose MIME type
2578 // is "image/svg+xml" and whose root element is thus an 'svg'
2579 // element), the SVG user agent is required to implement the
2580 // GetSVGDocument interface for the element which references the
2581 // SVG document (e.g., the HTML 'object' or comparable
2582 // referencing elements)."
2583 //
2584 // [6] http://www.w3.org/TR/SVG11/struct.html#InterfaceGetSVGDocument
2585 //
2586 // iframe
2587 var iframe = kungFuDeathGrip.childNodes[0];
2588 assert(iframe, "Failed finding svg iframe.");
2589 assert(iframe.contentDocument, "contentDocument failed for <iframe> referencing an svg document.");
2590 if (!iframe.getSVGDocument)
2591 fail("getSVGDocument missing on <iframe> element.");
2592 assert(iframe.getSVGDocument(), "getSVGDocument failed for <iframe> referencing an svg document.");
2593 assert(iframe.getSVGDocument() == iframe.contentDocument, "Mismatch between getSVGDocument and contentDocument #1.");
2594 // object
2595 var object = kungFuDeathGrip.childNodes[1];
2596 assert(object, "Failed finding svg object.");
2597 assert(object.contentDocument, "contentDocument failed for <object> referencing an svg document.");
2598 if (!object.getSVGDocument)
2599 fail("getSVGDocument missing on <object> element.");
2600 assert(object.getSVGDocument(), "getSVGDocument failed for <object> referencing an svg document.");
2601 assert(object.getSVGDocument() == object.contentDocument, "Mismatch between getSVGDocument and contentDocument #2.");
2602 return 5;
2603 },
2604 function () {
arv@chromium.org0e413152012-12-03 14:28:52 +00002605// 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
2606// This affects tests 75 to 79
2607// // test 75: SMIL in SVG, from Erik Dahlstrom
2608// //
2609// // The test begins by creating a few elements, among those is a
2610// // <set> element. This element is prevented from running by
2611// // setting begin="indefinite", which means that the animation
2612// // doesn't start until the 'beginElement' DOM method is called
2613// // on the <set> element. The animation is a simple animation
2614// // that sets the value of the width attribute to 0. The duration
2615// // of the animation is 'indefinite' which means that the value
2616// // will stay 0 indefinitely. The target of the animation is the
2617// // 'width' attribute of the <rect> element that is the parent of
2618// // the <set> element. When 'width' is 0 the rect is not rendered
2619// // according to the spec[7].
2620// //
2621// // Some properties of the SVGAnimatedLength[2] and SVGLength[8]
2622// // are also inspected. Before the animation starts both baseVal
2623// // and animVal contain the same values[2]. Then the animation is
2624// // started by calling the beginElement method[9]. To make sure
2625// // that time passes between the triggering of the animation and
2626// // the time that the values are read out (in test #66), the
2627// // current time is set to 1000 seconds using the setCurrentTime
2628// // method[10].
2629// //
2630// // [2] http://www.w3.org/TR/SVG11/types.html#InterfaceSVGAnimatedLength
2631// // [7] http://www.w3.org/TR/SVG11/shapes.html#RectElement
2632// // [8] http://www.w3.org/TR/SVG11/types.html#InterfaceSVGLength
2633// // [9] http://www.w3.org/TR/SVG11/animate.html#DOMInterfaces
2634// // [10] http://www.w3.org/TR/SVG11/struct.html#InterfaceSVGSVGElement
2635//
eric@webkit.orga7f130f2008-03-28 09:23:39 +00002636 var svgns = "http://www.w3.org/2000/svg";
2637 var svgdoc = kungFuDeathGrip.firstChild.contentDocument;
2638 assert(svgdoc, "contentDocument failed on <iframe> for svg document.");
2639 var svg = svgdoc.documentElement;
2640 var rect = svgdoc.createElementNS(svgns, "rect");
2641 rect.setAttribute("fill", "red");
2642 rect.setAttribute("width", "100");
2643 rect.setAttribute("height", "100");
2644 rect.setAttribute("id", "rect");
arv@chromium.org0e413152012-12-03 14:28:52 +00002645// var anim = svgdoc.createElementNS(svgns, "set");
2646// anim.setAttribute("begin", "indefinite");
2647// anim.setAttribute("to", "0");
2648// anim.setAttribute("attributeName", "width");
2649// anim.setAttribute("dur", "indefinite");
2650// anim.setAttribute("fill", "freeze");
2651// rect.appendChild(anim);
eric@webkit.orga7f130f2008-03-28 09:23:39 +00002652 svg.appendChild(rect);
2653 assert(rect.width, "SVG DOM interface SVGRectElement not supported.");
arv@chromium.org0e413152012-12-03 14:28:52 +00002654// assert(rect.width.baseVal, "SVG DOM base type SVGAnimatedLength not supported.");
2655// assert(rect.width.animVal, "SVG DOM base type SVGAnimatedLength not supported.");
2656// assertEquals(SVGLength.SVG_LENGTHTYPE_NUMBER, 1, "Incorrect SVGLength.SVG_LENGTHTYPE_NUMBER constant value.");
2657// assertEquals(rect.width.baseVal.unitType, SVGLength.SVG_LENGTHTYPE_NUMBER, "Incorrect unitType on width attribute.");
eric@webkit.orga7f130f2008-03-28 09:23:39 +00002658 assertEquals(rect.getAttribute("width"), "100", "Incorrect value from getAttribute.");
arv@chromium.org0e413152012-12-03 14:28:52 +00002659// assertEquals(rect.width.baseVal.valueInSpecifiedUnits, 100, "Incorrect valueInSpecifiedUnits value.");
2660// assertEquals(rect.width.baseVal.value, 100, "Incorrect baseVal value before animation.");
2661// assertEquals(rect.width.animVal.value, 100, "Incorrect animVal value before animation.");
2662// anim.beginElement();
2663// assertEquals(rect.width.baseVal.value, 100, "Incorrect baseVal value after starting animation.");
2664// svg.setCurrentTime(1000); // setting 1 second to make sure that time != 0s when we check the animVal value
2665// // the animation is then tested in the next test
eric@webkit.orga7f130f2008-03-28 09:23:39 +00002666 return 5;
2667 },
2668 function () {
arv@chromium.org0e413152012-12-03 14:28:52 +00002669// // test 76: SMIL in SVG, part 2, from Erik Dahlstrom
2670// //
2671// // About animVal[2]: "If the given attribute or property is
2672// // being animated, contains the current animated value of the
2673// // attribute or property, and both the object itself and its
2674// // contents are readonly. If the given attribute or property is
2675// // not currently being animated, contains the same value as
2676// // 'baseVal'."
2677// //
2678// // Since the duration of the animation is indefinite the value
2679// // is still being animated at the time it's queried. Now since
2680// // the 'width' attribute was animated from its original value of
2681// // "100" to the new value of "0" the animVal property must
2682// // contain the value 0.
2683// //
2684// // [2] http://www.w3.org/TR/SVG11/types.html#InterfaceSVGAnimatedLength
2685//
eric@webkit.orga7f130f2008-03-28 09:23:39 +00002686 var svgdoc = kungFuDeathGrip.firstChild.contentDocument;
2687 assert(svgdoc, "contentDocument failed on <object> for svg document.");
2688 var rect = svgdoc.getElementById("rect");
2689 assert(rect, "Failed to find <rect> element in svg document.");
arv@chromium.org0e413152012-12-03 14:28:52 +00002690// assertEquals(rect.width.animVal.value, 0, "Incorrect animVal value after svg animation.");
eric@webkit.orga7f130f2008-03-28 09:23:39 +00002691 return 5;
2692 },
2693 function () {
arv@chromium.org0e413152012-12-03 14:28:52 +00002694// // test 77: external SVG fonts, from Erik Dahlstrom
2695// //
2696// // SVGFonts are described here[3], and the relevant DOM methods
2697// // used in the test are defined here[4].
2698// //
2699// // Note that in order to be more predictable the svg should be
2700// // visible, so that clause "For non-rendering environments, the
2701// // user agent shall make reasonable assumptions about glyph
2702// // metrics." doesn't influence the results. We use 'opacity:0'
2703// // to hide the SVG, but arguably it's still a "rendering
2704// // environment".
2705// //
2706// // The font-size 4000 was chosen because that matches the
2707// // unitsPerEm value in the svgfont, which makes it easy to check
2708// // the glyph advances since they will then be exactly what was
2709// // specified in the svgfont.
2710// //
2711// // [3] http://www.w3.org/TR/SVG11/fonts.html
2712// // [4] http://www.w3.org/TR/SVG11/text.html#InterfaceSVGTextContentElement
2713//
eric@webkit.orga7f130f2008-03-28 09:23:39 +00002714 var svgns = "http://www.w3.org/2000/svg";
arv@chromium.org0e413152012-12-03 14:28:52 +00002715// var xlinkns = "http://www.w3.org/1999/xlink";
eric@webkit.orga7f130f2008-03-28 09:23:39 +00002716 var svgdoc = kungFuDeathGrip.firstChild.contentDocument;
2717 assert(svgdoc, "contentDocument failed on <object> for svg document.");
2718 var svg = svgdoc.documentElement;
2719 var text = svgdoc.createElementNS(svgns, "text");
2720 text.setAttribute("y", "1em");
2721 text.setAttribute("font-size", "4000");
2722 text.setAttribute("font-family", "ACID3svgfont");
2723 var textContent = svgdoc.createTextNode("abc");
2724 text.appendChild(textContent);
2725 svg.appendChild(text);
2726 // The font-size 4000 was chosen because that matches the unitsPerEm value in the svgfont,
2727 // which makes it easy to check the glyph advances since they will then be exactly what was specified in the svgfont.
2728 assert(text.getNumberOfChars, "SVGTextContentElement.getNumberOfChars() not supported.");
2729 assertEquals(text.getNumberOfChars(), 3, "getNumberOfChars returned incorrect string length.");
arv@chromium.org0e413152012-12-03 14:28:52 +00002730// assertEquals(text.getComputedTextLength(), 4711+42+23, "getComputedTextLength failed.");
2731// assertEquals(text.getSubStringLength(0,1), 42, "getSubStringLength #1 failed.");
2732// assertEquals(text.getSubStringLength(0,2), 42+23, "getSubStringLength #2 failed.");
2733// assertEquals(text.getSubStringLength(1,1), 23, "getSubStringLength #3 failed.");
2734// assertEquals(text.getSubStringLength(1,0), 0, "getSubStringLength #4 failed.");
2735///* COMMENTED OUT BECAUSE SVGWG KEEPS CHANGING THIS
2736// * var code = -1000;
2737// * try {
2738// * var sl = text.getSubStringLength(1,3);
2739// * } catch(e) {
2740// * code = e.code;
2741// * }
2742// * assertEquals(code, DOMException.INDEX_SIZE_ERR, "getSubStringLength #1 didn't throw exception.");
2743// * code = -1000;
2744// * try {
2745// * var sl = text.getSubStringLength(0,4);
2746// * } catch(e) {
2747// * code = e.code;
2748// * }
2749// * assertEquals(code, DOMException.INDEX_SIZE_ERR, "getSubStringLength #2 didn't throw exception.");
2750// * code = -1000;
2751// * try {
2752// * var sl = text.getSubStringLength(3,0);
2753// * } catch(e) {
2754// * code = e.code;
2755// * }
2756// * assertEquals(code, DOMException.INDEX_SIZE_ERR, "getSubStringLength #3 didn't throw exception.");
2757// */
2758// code = -1000;
2759// try {
2760// var sl = text.getSubStringLength(-17,20);
2761// } catch(e) {
2762// code = 0; // negative values might throw native exception since the api accepts only unsigned values
2763// }
2764// assert(code == 0, "getSubStringLength #4 didn't throw exception.");
2765// assertEquals(text.getStartPositionOfChar(0).x, 0, "getStartPositionOfChar(0).x returned invalid value.");
2766// assertEquals(text.getStartPositionOfChar(1).x, 42, "getStartPositionOfChar(1).x returned invalid value.");
2767// assertEquals(text.getStartPositionOfChar(2).x, 42+23, "getStartPositionOfChar(2).x returned invalid value.");
2768// assertEquals(text.getStartPositionOfChar(0).y, 4000, "getStartPositionOfChar(0).y returned invalid value.");
2769// code = -1000;
2770// try {
2771// var val = text.getStartPositionOfChar(-1);
2772// } catch(e) {
2773// code = 0; // negative values might throw native exception since the api accepts only unsigned values
2774// }
2775// assert(code == 0, "getStartPositionOfChar #1 exception failed.");
2776// code = -1000;
2777// try {
2778// var val = text.getStartPositionOfChar(4);
2779// } catch(e) {
2780// code = e.code;
2781// }
2782// assertEquals(code, DOMException.INDEX_SIZE_ERR, "getStartPositionOfChar #2 exception failed.");
2783// assertEquals(text.getEndPositionOfChar(0).x, 42, "getEndPositionOfChar(0).x returned invalid value.");
2784// assertEquals(text.getEndPositionOfChar(1).x, 42+23, "getEndPositionOfChar(1).x returned invalid value.");
2785// assertEquals(text.getEndPositionOfChar(2).x, 42+23+4711, "getEndPositionOfChar(2).x returned invalid value.");
2786// code = -1000;
2787// try {
2788// var val = text.getEndPositionOfChar(-17);
2789// } catch(e) {
2790// code = 0; // negative values might throw native exception since the api accepts only unsigned values
2791// }
2792// assert(code == 0, "getEndPositionOfChar #1 exception failed.");
2793// code = -1000;
2794// try {
2795// var val = text.getEndPositionOfChar(4);
2796// } catch(e) {
2797// code = e.code;
2798// }
2799// assertEquals(code, DOMException.INDEX_SIZE_ERR, "getEndPositionOfChar #2 exception failed.");
eric@webkit.orga7f130f2008-03-28 09:23:39 +00002800 return 5;
2801 },
2802 function () {
arv@chromium.org0e413152012-12-03 14:28:52 +00002803// // test 78: SVG textPath and getRotationOfChar(), from Erik Dahlstrom
2804// //
2805// // The getRotationOfChar[4] method fetches the midpoint rotation
2806// // of a glyph defined by a character (in this testcase there is
2807// // a simple 1:1 correspondence between the two). The path is
2808// // defined in the resources/acid3/svg.xml file, and consists of first a line
2809// // going down, then followed by a line that has a 45 degree
2810// // slope and then followed by a horizontal line. The length of
2811// // each path segment have been paired with the advance of each
2812// // glyph, so that each glyph will be on each of the three
2813// // different path segments (see text on a path layout rules[5]).
2814// // Thus the rotation of the first glyph is 90 degrees, the
2815// // second 45 degrees and the third 0 degrees.
2816// //
2817// // [4] http://www.w3.org/TR/SVG11/text.html#InterfaceSVGTextContentElement
2818// // [5] http://www.w3.org/TR/SVG11/text.html#TextpathLayoutRules
2819//
eric@webkit.orga7f130f2008-03-28 09:23:39 +00002820 var svgns = "http://www.w3.org/2000/svg";
arv@chromium.org0e413152012-12-03 14:28:52 +00002821// var xlinkns = "http://www.w3.org/1999/xlink";
eric@webkit.orga7f130f2008-03-28 09:23:39 +00002822 var svgdoc = kungFuDeathGrip.firstChild.contentDocument;
2823 assert(svgdoc, "contentDocument failed on <object> for svg document.");
2824 var svg = svgdoc.documentElement;
arv@chromium.org0e413152012-12-03 14:28:52 +00002825// var text = svgdoc.createElementNS(svgns, "text");
2826// text.setAttribute("font-size", "4000");
2827// text.setAttribute("font-family", "ACID3svgfont");
2828// var textpath = svgdoc.createElementNS(svgns, "textPath");
2829// textpath.setAttributeNS(xlinkns, "xlink:href", "#path");
2830// var textContent = svgdoc.createTextNode("abc");
2831// textpath.appendChild(textContent);
2832// text.appendChild(textpath);
2833// svg.appendChild(text);
2834// assertEquals(text.getRotationOfChar(0), 90, "getRotationOfChar(0) failed.");
2835// assertEquals(text.getRotationOfChar(1), 45, "getRotationOfChar(1) failed.");
2836// assertEquals(text.getRotationOfChar(2), 0, "getRotationOfChar(2) failed.");
2837// var code = -1000;
2838// try {
2839// var val = text.getRotationOfChar(-1)
2840// } catch(e) {
2841// code = e.code;
2842// }
2843// assertEquals(code, DOMException.INDEX_SIZE_ERR, "getRotationOfChar #1 exception failed.");
2844// code = -1000;
2845// try {
2846// var val = text.getRotationOfChar(4)
2847// } catch(e) {
2848// code = e.code;
2849// }
2850// assertEquals(code, DOMException.INDEX_SIZE_ERR, "getRotationOfChar #2 exception failed.");
eric@webkit.orga7f130f2008-03-28 09:23:39 +00002851 return 5;
2852 },
2853 function () {
arv@chromium.org0e413152012-12-03 14:28:52 +00002854// // test 79: a giant test for <svg:font>, from Cameron McCormack
2855// // This tests various features of SVG fonts from SVG 1.1. It consists of
2856// // a <text> element with 33 characters, styled using an SVG font that has
2857// // different advance values for each glyph. The script uses
2858// // SVGTextElementContent.getStartPositionOfChar() to determine where the
2859// // glyph corresponding to each character was placed, and thus to work out
2860// // whether the SVG font was used correctly.
2861// //
2862// // The font uses 100 units per em, and the text is set in 100px. Since
2863// // font-size gives the size of the em box
2864// // (http://www.w3.org/TR/SVG11/text.html#DOMInterfaces), the scale of the
2865// // coordinate system for the glyphs is the same as the SVG document.
2866// //
2867// // The expectedAdvances array holds the expected advance value for each
2868// // character, and expectedKerning holds the (negative) kerning for each
2869// // character. getPositionOfChar() returns the actual x coordinate for the
2870// // glyph, corresponding to the given character, and if multiple characters
2871// // correspond to the same glyph, the same position value is returned for
2872// // each of those characters.
2873// //
2874// // Here are the reasonings for the advance/kerning values. Note that for
2875// // a given character at index i, the expected position is
2876// // sum(expectedAdvances[0:i-1] + expectedKerning[0:i-1]).
2877// //
2878// // char advance kerning reasoning
2879// // ------- ------- ------- --------------------------------------------------
2880// // A 10000 0 Normal character mapping to a single glyph.
2881// // B 0 0 First character of a two character glyph, so the
2882// // current position isn't advanced until the second
2883// // character.
2884// // C 200 0 Second character of a two character glyph, so now
2885// // the position is advanced.
2886// // B 300 0 Although there is a glyph for "BC" in the font,
2887// // it appears after the glyph for "B", so the single
2888// // character glyph for "B" should be chosen instead.
2889// // D 1100 0 Normal character mapping to a single glyph.
2890// // A 10000 200 Kerning of -200 is specified in the font between
2891// // the "A" and "EE" glyphs.
2892// // E 0 0 The first character of a two character glyph "EE".
2893// // E 1300 0 The second character of a two character glyph.
2894// // U 0 0 This is a glyph for the six characters "U+0046",
2895// // which happen to look like a valid unicode range.
2896// // This tests that the <glyph unicode=""> in the
2897// // font matches exact strings rather than a range,
2898// // as used in the kerning elements.
2899// // + 0 0 Second character of six character glyph.
2900// // 0 0 0 Third character of six character glyph.
2901// // 0 0 0 Fourth character of six character glyph.
2902// // 4 0 0 Fifth character of six character glyph.
2903// // 6 1700 0 Sixth character of six character glyph.
2904// // U 0 0 The same six character glyph that looks like a
2905// // Unicode range. One of the kerning elements has
2906// // u1="U+0046" u2="U+0046", which shouldn't match
2907// // this, because those attributes are interpreted
2908// // as Unicode ranges if they are, and normal
2909// // strings otherwise. Thus there should be no
2910// // kerning between these two glyphs.
2911// // G 2300 200 Kerning is between this character and the next
2912// // "G", since there is an <hkern> element that
2913// // uses a Unicode range on its u1="" attribute
2914// // and a glyph name on its g2="" attribute which
2915// // both match "G".
2916// // G 2300 0 Normal character with kerning before it.
2917// // H 3100 0 A glyph with graphical content describing the
2918// // glyph, rather than a d="" attribute.
2919// // I 4300 0 Glyphs are checked in document order for one
2920// // that matches, but the first glyph with
2921// // unicode="I" also has lang="zh", which disqualifies
2922// // it. Thus the second glyph with unicode="I"
2923// // is chosen.
2924// // I 4100 0 Since this I has xml:lang="zh" on it in the text,
2925// // the first glyph with lang="zh" matches.
2926// // J 4700 -4700 A normal glyph with kerning between the "J" and the
2927// // next glyph "A" equal to the advance of the "J"
2928// // glyph, so the position should stay the same.
2929// // A 10000 0 Normal glyph with kerning before it.
2930// // K 5900 0 The first glyph with unicode="K" does not match,
2931// // since it has orientation="v", so the second
2932// // glyph with unicode="K" is chosen.
2933// // <spc> 6100 0 The space character should select the glyph with
2934// // unicode=" ", despite it having a misleading
2935// // glyph-name="L".
2936// // L 6700 0 The "L" character should select the glyph with
2937// // unicode=" ", despite it having a misleading
2938// // glyph-name="spacev".
2939// // A 2900 0 An <altGlyph> element is used to select the
2940// // glyph for U+10085 instead of the one for "A".
2941// // U+10085 2900 0 Tests glyph selection with a non-plane-0
2942// // character.
2943// // A 10000 0 A final normal character.
2944// //
2945// // In addition, the script tests the value returned by
2946// // SVGTextContentElement.getNumberOfChars(), which in this case should be 34.
2947// // If it returned 33, then it incorrectly counted Unicode characters instead
2948// // of UTF-16 codepoints (probably).
2949// //
2950// // See http://www.w3.org/TR/SVG11/fonts.html for a description of the glyph
2951// // matching rules, and http://www.w3.org/TR/SVG11/text.html#DOMInterfaces
2952// // for a description of getStartPositionOfChar() and getNumberOfChars().
2953// //
2954// // Note also that the test uses DOMImplementation.createDocument() to create
2955// // the SVG document. This seems to cause browsers trouble for the SVG DOM
2956// // interfaces, since the document isn't being "rendered" as it might be
2957// // if it were in an <iframe>. Changing the test to use an <iframe> will
2958// // at least let you see the main part of the test running.
2959//
eric@webkit.orga7f130f2008-03-28 09:23:39 +00002960 var NS = {
2961 svg: 'http://www.w3.org/2000/svg',
2962 xml: 'http://www.w3.org/XML/1998/namespace',
arv@chromium.org0e413152012-12-03 14:28:52 +00002963// xlink: 'http://www.w3.org/1999/xlink'
eric@webkit.orga7f130f2008-03-28 09:23:39 +00002964 };
2965
2966 var doc = kungFuDeathGrip.childNodes[1].contentDocument;
2967 while (doc.hasChildNodes())
2968 doc.removeChild(doc.firstChild);
2969 doc.appendChild(doc.createElementNS(NS.svg, "svg:svg"));
arv@chromium.org0e413152012-12-03 14:28:52 +00002970//
2971// var e = function (n, as, cs) {
2972// var elt = doc.createElementNS(NS.svg, n);
2973// if (as) {
2974// for (var an in as) {
2975// var idx = an.indexOf(':');
2976// var ns = null;
2977// if (idx != -1)
2978// ns = NS[an.substring(0, idx)];
2979// elt.setAttributeNS(ns, an, as[an]);
2980// }
2981// }
2982// if (cs) {
2983// for (var i in cs) {
2984// var c = cs[i];
2985// elt.appendChild(typeof c == 'string' ? doc.createTextNode(c) : c);
2986// }
2987// }
2988// return elt;
2989// }
2990//
2991// 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'})]));
2992// 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']));
2993//
2994// var t = doc.documentElement.lastChild;
2995//
2996// var characterDescriptions = [
2997// "a normal character",
2998// "the first character of a two-character glyph",
2999// "the second character of a two-character glyph",
3000// "a normal character, which shouldn't be the first character of a two-character glyph",
3001// "a normal character, which shouldn't be the second character of a two-character glyph",
3002// "a normal character, which has some kerning after it",
3003// "the first character of a two-character glyph, which has some kerning before it",
3004// "the second character of a two-character glyph, which has some kerning before it",
3005// "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",
3006// "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",
3007// "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",
3008// "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",
3009// "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",
3010// "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",
3011// "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",
3012// "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",
3013// "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",
3014// "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",
3015// "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",
3016// "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",
3017// "a normal character, which has some kerning after it that is specified by glyph name",
3018// "a normal character, which has some kerning before it that is specified by glyph name",
3019// "a normal character, whose glyph is given by child graphical content of the <glyph> element",
3020// "a normal character, whose glyph should not match the one with a lang=\"\" attribute on it",
3021// "a normal character, whose glyph should match the one with a lang=\"\" attribute on it",
3022// "a normal character, which has some kerning after it that is equal to the advance of the character",
3023// "a normal character, which has some kerning before it that is equal to the advance of the previous character",
3024// "a normal character, whose glyph should not match the one with an orientation=\"v\" attribute on it",
3025// "a space character, which has a misleading glyph-name=\"\" attribute",
3026// "a normal character, which has a misleading glyph-name=\"\" attribute",
3027// "a normal character, whose glyph is chosen to be another by using <altGlyph>",
3028// "a character not in Plane 0 (high surrogate pair)",
3029// "a character not in Plane 0 (low surrogate pair)",
3030// "a normal character",
3031// ];
3032//
3033// var expectedAdvances = [
3034// 10000, // A
3035// 0, // BC [0]
3036// 200, // BC [1]
3037// 300, // B
3038// 1100, // D
3039// 10000, // A
3040// 0, // EE [0]
3041// 1300, // EE [1]
3042// 0, // U+0046 [0]
3043// 0, // U+0046 [1]
3044// 0, // U+0046 [2]
3045// 0, // U+0046 [3]
3046// 0, // U+0046 [4]
3047// 1700, // U+0046 [5]
3048// 0, // U+0046 [0]
3049// 0, // U+0046 [1]
3050// 0, // U+0046 [2]
3051// 0, // U+0046 [3]
3052// 0, // U+0046 [4]
3053// 1700, // U+0046 [5]
3054// 2300, // G
3055// 2300, // G
3056// 3100, // H
3057// 4300, // I
3058// 4100, // I (zh)
3059// 4700, // J
3060// 10000, // A
3061// 5900, // K
3062// 6100, // <space>
3063// 6700, // L
3064// 2900, // A (using &#x10085; altGlyph)
3065// 0, // &#x10085; high surrogate pair
3066// 2900, // &#x10085; low surrogate pair
3067// 10000, // A
3068// ];
3069//
3070// var expectedKerning = [
3071// 0, // A
3072// 0, // BC [0]
3073// 0, // BC [1]
3074// 0, // B
3075// 0, // D
3076// 200, // A
3077// 0, // EE [0]
3078// 0, // EE [1]
3079// 0, // U+0046 [0]
3080// 0, // U+0046 [1]
3081// 0, // U+0046 [2]
3082// 0, // U+0046 [3]
3083// 0, // U+0046 [4]
3084// 0, // U+0046 [5]
3085// 0, // U+0046 [0]
3086// 0, // U+0046 [1]
3087// 0, // U+0046 [2]
3088// 0, // U+0046 [3]
3089// 0, // U+0046 [4]
3090// 0, // U+0046 [5]
3091// 200, // G
3092// 0, // G
3093// 0, // H
3094// 0, // I
3095// 0, // I (zh)
3096// -4700, // J
3097// 0, // A
3098// 0, // K
3099// 0, // <space>
3100// 0, // L
3101// 0, // A (using &#x10085; altGlyph)
3102// 0, // &#x10085; high surrogate pair
3103// 0, // &#x10085; low surrogate pair
3104// 0, // A
3105// ];
3106//
3107// assertEquals(t.getNumberOfChars(), expectedAdvances.length, 'SVGSVGTextElement.getNumberOfChars() incorrect');
3108//
3109// var expectedPositions = [0];
3110// for (var i = 0; i < expectedAdvances.length; i++)
3111// expectedPositions.push(expectedPositions[i] + expectedAdvances[i] + expectedKerning[i]);
3112//
3113// var actualPositions = [];
3114// for (var i = 0; i < t.getNumberOfChars(); i++)
3115// actualPositions.push(t.getStartPositionOfChar(i).x);
3116// actualPositions.push(t.getEndPositionOfChar(t.getNumberOfChars() - 1).x);
3117//
3118// for (var i = 0; i < expectedPositions.length; i++) {
3119// if (expectedPositions[i] != actualPositions[i]) {
3120// var s = "character position " + i + ", which is ";
3121// if (i == 0) {
3122// s += "before " + characterDescriptions[0];
3123// } else if (i == expectedPositions.length - 1) {
3124// s += "after " + characterDescriptions[characterDescriptions.length - 1];
3125// } else {
3126// s += "between " + characterDescriptions[i - 1] + " and " + characterDescriptions[i];
3127// }
3128// s += ", is " + actualPositions[i] + " but should be " + expectedPositions[i] + ".";
3129// fail(s);
3130// }
3131// }
eric@webkit.orga7f130f2008-03-28 09:23:39 +00003132 return 5;
3133 },
3134 function () {
3135 // test 80: remove the iframes and the object
eric@webkit.orgaadb79d2010-06-11 09:10:18 +00003136 // (when fixing the test for http://dbaron.org/mozilla/visited-privacy,
3137 // this section was flipped around so the linktest check is done first;
3138 // this is to prevent the 'retry' from failing the second time since by
3139 // then the kungFuDeathGrip has been nullified, if we do it first)
3140 // first, check that the linktest is loaded
3141 var a = document.links[1];
3142 assert(!(a == null), "linktest was null");
3143 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
3144 if (a.hasAttribute('class'))
3145 return "retry"; // linktest onload didn't fire -- could be a networking issue, check that first
eric@webkit.orga7f130f2008-03-28 09:23:39 +00003146 assert(!(kungFuDeathGrip == null), "kungFuDeathGrip was null");
3147 assert(!(kungFuDeathGrip.parentNode == null), "kungFuDeathGrip.parentNode was null");
eric@webkit.orgaadb79d2010-06-11 09:10:18 +00003148 // ok, now remove the iframes
eric@webkit.orga7f130f2008-03-28 09:23:39 +00003149 kungFuDeathGrip.parentNode.removeChild(kungFuDeathGrip);
3150 kungFuDeathGrip = null;
3151 // check that the xhtml files worked right
3152 assert(notifications['xhtml.1'], "Script in XHTML didn't execute");
3153 assert(!notifications['xhtml.2'], "XML well-formedness error didn't stop script from executing");
3154 assert(!notifications['xhtml.3'], "Script executed despite having wrong namespace");
eric@webkit.orga7f130f2008-03-28 09:23:39 +00003155 return 5;
3156 },
3157
3158 // bucket 6: ECMAScript
3159 function () {
3160 // test 81: length of arrays with elisions at end
3161 var t1 = [,];
3162 var t2 = [,,];
3163 assertEquals(t1.length, 1, "[,] doesn't have length 1");
3164 assertEquals(t2.length, 2, "[,,] doesn't have length 2");
3165 return 6;
3166 },
3167 function () {
3168 // test 82: length of arrays with elisions in the middle
3169 var t3 = ['a', , 'c'];
3170 assertEquals(t3.length, 3, "['a',,'c'] doesn't have length 3");
3171 assert(0 in t3, "no 0 in t3");
3172 assert(!(1 in t3), "unexpected 1 in t3");
3173 assert(2 in t3, "no 2 in t3");
3174 assertEquals(t3[0], 'a', "t3[0] wrong");
3175 assertEquals(t3[2], 'c', "t3[2] wrong");
3176 return 6;
3177 },
3178 function () {
3179 // test 83: array methods
3180 var x = ['a', 'b', 'c'];
3181 assertEquals(x.unshift('A', 'B', 'C'), 6, "array.unshift() returned the wrong value");
3182 var s = x.join(undefined);
3183 assertEquals(s, 'A,B,C,a,b,c', "array.join(undefined) used wrong separator"); // qv 15.4.4.5:3
3184 return 6;
3185 },
3186 function () {
3187 // test 84: converting numbers to strings
3188 assertEquals((0.0).toFixed(4), "0.0000", "toFixed(4) wrong for 0");
3189 assertEquals((-0.0).toFixed(4), "0.0000", "toFixed(4) wrong for -0");
3190 assertEquals((0.00006).toFixed(4), "0.0001", "toFixed(4) wrong for 0.00006");
3191 assertEquals((-0.00006).toFixed(4), "-0.0001", "toFixed(4) wrong for -0.00006");
3192 assertEquals((0.0).toExponential(4), "0.0000e+0", "toExponential(4) wrong for 0");
3193 assertEquals((-0.0).toExponential(4), "0.0000e+0", "toExponential(4) wrong for -0");
3194 var x = 7e-4;
3195 assertEquals(x.toPrecision(undefined), x.toString(undefined), "toPrecision(undefined) was wrong");
3196 return 6;
3197 },
3198 function () {
3199 // test 85: strings and string-related operations
3200 // substr() and negative numbers
3201 assertEquals("scathing".substr(-7, 3), "cat", "substr() wrong with negative numbers");
3202 return 6;
3203 },
3204 function () {
3205 // test 86: Date tests -- methods passed no arguments
3206 var d = new Date();
3207 assert(isNaN(d.setMilliseconds()), "calling setMilliseconds() with no arguments didn't result in NaN");
3208 assert(isNaN(d), "date wasn't made NaN");
3209 assert(isNaN(d.getDay()), "date wasn't made NaN");
3210 return 6;
3211 },
3212 function () {
3213 // test 87: Date tests -- years
3214 var d1 = new Date(Date.UTC(99.9, 6));
3215 assertEquals(d1.getUTCFullYear(), 1999, "Date.UTC() didn't do proper 1900 year offsetting");
3216 var d2 = new Date(98.9, 6);
3217 assertEquals(d2.getFullYear(), 1998, "new Date() didn't do proper 1900 year offsetting");
3218 return 6;
3219 },
3220 function () {
3221 // test 88: ES3 section 7.6:3 (unicode escapes can't be used to put non-identifier characters into identifiers)
3222 // and there's no other place for them in the syntax (other than strings, of course)
3223 var ok = false;
3224 try {
3225 eval("var test = { };\ntest.i= 0;\ntest.i\\u002b= 1;\ntest.i;\n");
3226 } catch (e) {
3227 ok = true;
3228 }
3229 assert(ok, "\\u002b was not considered a parse error in script");
3230 return 6;
3231 },
3232 function () {
3233 // test 89: Regular Expressions
3234 var ok = true;
3235 // empty classes in regexps
3236 try {
3237 eval("/TA[])]/.exec('TA]')");
3238 // JS regexps aren't like Perl regexps, if their character
3239 // classes start with a ] that means they're empty. So this
3240 // is a syntax error; if we get here it's a bug.
3241 ok = false;
3242 } catch (e) { }
3243 assert(ok, "orphaned bracket not considered parse error in regular expression literal");
3244 try {
3245 if (eval("/[]/.exec('')"))
3246 ok = false;
3247 } catch (e) {
3248 ok = false;
3249 }
3250 assert(ok, "/[]/ either failed to parse or matched something");
3251 return 6;
3252 },
3253 function () {
3254 // test 90: Regular Expressions
3255 // not back references.
3256 assert(!(/(1)\0(2)/.test("12")), "NUL in regexp incorrectly ignored");
3257 assert((/(1)\0(2)/.test("1" + "\0" + "2")), "NUL in regexp didn't match correctly");
3258 assert(!(/(1)\0(2)/.test("1\02")), "octal 2 unexpectedly matched NUL");
3259 assertEquals(nullInRegexpArgumentResult, "passed", "failed //.test() check"); // nothing to see here, move along now
3260 // back reference to future capture
3261 var x = /(\3)(\1)(a)/.exec('cat'); // the \3 matches the empty string, qv. ES3:15.10.2.9
3262 assert(x, "/(\\3)(\\1)(a)/ failed to match 'cat'");
3263 assertEquals(x.length, 4, "/(\\3)(\\1)(a)/ failed to return four components");
3264 assertEquals(x[0], "a", "/(\\3)(\\1)(a)/ failed to find 'a' in 'cat'");
3265 assert(x[1] === "", "/(\\3)(\\1)(a)/ failed to find '' in 'cat' as first part");
3266 assert(x[2] === "", "/(\\3)(\\1)(a)/ failed to find '' in 'cat' as second part");
3267 assertEquals(x[3], "a", "/(\\3)(\\1)(a)/ failed to find 'a' in 'cat' as third part");
3268 // negative lookahead
3269 x = /(?!(text))(te.t)/.exec("text testing");
3270 assertEquals(x.length, 3, "negative lookahead test failed to return the right number of bits");
3271 assertEquals(x[0], "test", "negative lookahead test failed to find the right text");
3272 assert(x[1] === undefined, "negative lookahead test failed to return undefined for negative lookahead capture");
3273 assert(x[2] === "test", "negative lookahead test failed to find the right second capture");
3274 return 6;
3275 },
3276 function () {
3277 // test 91: check that properties are enumerable by default
3278 var test = {
3279 constructor: function() { return 1; },
3280 toString: function() { return 2; },
3281 toLocaleString: function() { return 3; },
3282 valueOf: function() { return 4; },
3283 hasOwnProperty: function() { return 5; },
3284 isPrototypeOf: function() { return 6; },
3285 propertyIsEnumerable: function() { return 7; },
3286 prototype: function() { return 8; },
3287 length: function() { return 9; },
3288 unique: function() { return 10; }
3289 };
3290 var results = [];
3291 for (var property in test)
3292 results.push([test[property](), property]);
3293 results.sort(function(a, b) {
3294 if (a[0] < b[0]) return -1;
3295 if (a[0] > b[0]) return 1;
3296 return 0;
3297 });
3298 assertEquals(results.length, 10, "missing properties");
3299 for (var index = 0; index < 10; index += 1)
3300 assertEquals(results[index][0], index+1, "order wrong at results["+index+"] == ");
3301 var index = 0;
3302 assertEquals(results[index++][1], "constructor", "failed to find constructor in expected position");
3303 assertEquals(results[index++][1], "toString", "failed to find toString in expected position");
3304 assertEquals(results[index++][1], "toLocaleString", "failed to find toLocaleString in expected position");
3305 assertEquals(results[index++][1], "valueOf", "failed to find valueOf in expected position");
3306 assertEquals(results[index++][1], "hasOwnProperty", "failed to find hasOwnProperty in expected position");
3307 assertEquals(results[index++][1], "isPrototypeOf", "failed to find isPrototypeOf in expected position");
3308 assertEquals(results[index++][1], "propertyIsEnumerable", "failed to find propertyIsEnumerable in expected position");
3309 assertEquals(results[index++][1], "prototype", "failed to find prototype in expected position");
3310 assertEquals(results[index++][1], "length", "failed to find length in expected position");
3311 assertEquals(results[index++][1], "unique", "failed to find unique in expected position");
3312 return 6;
3313 },
3314 function () {
3315 // test 92: internal properties of Function objects
3316 // constructor is not ReadOnly
3317 var f1 = function () { 1 };
3318 f1.prototype.constructor = "hello world";
3319 var f1i = new f1();
3320 assert(f1i.constructor === "hello world", "Function object's prototype's constructor was ReadOnly");
3321 // constructor is DontEnum (indeed, no properties at all on a new Function object)
3322 var f2 = function () { 2 };
3323 var f2i = new f2();
3324 var count = 0;
3325 for (var property in f2i) {
3326 assert(property != "constructor", "Function object's prototype's constructor was not DontEnum");
3327 count += 1;
3328 }
3329 assertEquals(count, 0, "Function object had unexpected properties");
3330 // constructor is not DontDelete
3331 var f3 = function (a, b) { 3 };
3332 delete f3.prototype.constructor;
3333 var f3i = new f3();
3334 assertEquals(f3i.constructor, Object.prototype.constructor, "Function object's prototype's constructor was DontDelete (or got magically replaced)");
3335 return 6;
3336 },
3337 function () {
3338 // test 93: FunctionExpression semantics
3339 var functest;
3340 var vartest = 0;
3341 var value = (function functest(arg) {
3342 if (arg)
3343 return 1;
3344 vartest = 1;
3345 functest = function (arg) { return 2; }; // this line does nothing as 'functest' is ReadOnly here
3346 return functest(true); // this is therefore tail recursion and returns 1
3347 })(false);
3348 assertEquals(vartest, 1, "rules in 10.1.4 not followed in FunctionBody");
3349 assertEquals(value, 1, "semantics of FunctionExpression: function Identifier ... not followed");
3350 assert(!functest, "Property in step 4 of FunctionExpression: function Identifier ... leaked to parent scope");
3351 return 6;
3352 },
3353 function () {
3354 // test 94: exception scope
3355 var test = 'pass';
3356 try {
3357 throw 'fail';
3358 } catch (test) {
3359 test += 'ing';
3360 }
3361 assertEquals(test, 'pass', 'outer scope poisoned by exception catch{} block');
3362 return 6;
3363 },
3364 function () {
3365 // test 95: types of expressions
3366 var a = []; var s;
3367 s = a.length = "2147483648";
3368 assertEquals(typeof s, "string", "type of |\"2147483648\"| is not string");
3369 return 6;
3370 },
3371 function () {
3372 // test 96: encodeURI() and encodeURIComponent() and null bytes
3373 assertEquals(encodeURIComponent(String.fromCharCode(0)), '%00', "encodeURIComponent failed to encode U+0000");
3374 assertEquals(encodeURI(String.fromCharCode(0)), '%00', "encodeURI failed to encode U+0000");
3375 return 6;
3376 },
3377
3378 // URIs
3379 function () {
3380 // test 97: data: URI parsing
3381 assertEquals(d1, "one", "data: failed as escaped");
3382 assertEquals(d2, "two", "data: failed as base64");
3383 assertEquals(d3, "three", "data: failed as base64 escaped");
3384 assertEquals(d4, "four", "data: failed as base64 with spaces");
3385 assertEquals(d5, "five's", "data: failed with backslash");
3386 return 7;
3387 },
3388
3389 // XHTML
3390 function () {
3391 // test 98: XHTML and the DOM
3392 // (special test)
3393 var doctype = document.implementation.createDocumentType("html", "-//W3C//DTD XHTML 1.0 Strict//EN", "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd");
arv@chromium.org0e413152012-12-03 14:28:52 +00003394// COMMENTED OUT FOR 2011 UPDATE - doctypes are moving towards having an owner, like other nodes
3395// assertEquals(doctype.ownerDocument, null, "doctype's ownerDocument was wrong after creation");
eric@webkit.orga7f130f2008-03-28 09:23:39 +00003396 var doc = document.implementation.createDocument("http://www.w3.org/1999/xhtml", "html", doctype);
3397 doc.documentElement.appendChild(doc.createElementNS("http://www.w3.org/1999/xhtml", "head"));
3398 doc.documentElement.appendChild(doc.createElementNS("http://www.w3.org/1999/xhtml", "body"));
3399 var t = doc.createElementNS("http://www.w3.org/1999/xhtml", "title");
3400 doc.documentElement.firstChild.appendChild(t);
3401 // ok we have a conforming XHTML1 doc in |doc| now.
3402 assertEquals(doctype.ownerDocument, doc, "doctype's ownerDocument didn't change when it was assigned to another document");
3403 assertEquals(doc.title, "", "document had unexpected title");
3404 t.textContent = "Sparrow";
3405 assertEquals(doc.title, "Sparrow", "document.title did not update dynamically");
3406 doc.body.appendChild(doc.createElementNS("http://www.w3.org/1999/xhtml", "form"));
3407 assertEquals(doc.forms.length, 1, "document.forms not updated after inserting a form");
3408 return 7;
3409 },
3410
3411 // Sanity
3412 function () {
3413 // test 99: check for the weirdest bug ever
3414 var a = document.createElement('a');
3415 a.setAttribute('href', 'http://www.example.com/');
3416 a.appendChild(document.createTextNode('www.example.com'));
3417 a.href = 'http://hixie.ch/';
3418 assertEquals(a.firstChild.data, "www.example.com", "sanity did not prevail");
3419 a.href = 'http://damowmow.com/';
3420 assertEquals(a.firstChild.data, "www.example.com", "final test failed");
3421 return 7;
3422 }
3423
3424 ];
3425 var log = '';
3426 var delay = 10;
3427 var score = 0, index = 0, retry = 0, errors = 0;
3428 function update() {
3429 var span = document.getElementById('score'); // not cached by JS
3430 span.nextSibling.removeAttribute('class'); // no-op after first loop
3431 span.nextSibling.nextSibling.firstChild.data = tests.length; // no-op after first loop
3432 if (index < tests.length) {
3433 var zeroPaddedIndex = index < 10 ? '0' + index : index;
3434 try {
3435 var beforeTest = new Date();
3436 var result = tests[index]();
3437 var elapsedTest = new Date() - beforeTest;
3438 if (result == "retry") {
3439 // some tests uses this magical mechanism to wait for support files to load
cdumez@apple.com43a8ea52017-05-31 23:42:27 +00003440 // we will give this test 1000 attempts (10000ms) before aborting
eric@webkit.orga7f130f2008-03-28 09:23:39 +00003441 retry += 1;
cdumez@apple.com43a8ea52017-05-31 23:42:27 +00003442 if (retry < 1000) {
eric@webkit.orga7f130f2008-03-28 09:23:39 +00003443 setTimeout(update, delay);
3444 return;
3445 }
3446 fail("timeout -- could be a networking issue");
3447 } else if (result) {
3448 var bucket = document.getElementById('bucket' + result);
3449 if (bucket)
3450 bucket.className += 'P';
3451 score += 1;
3452 if (retry > 0) {
3453 errors += 1;
cdumez@apple.com371f9792017-04-24 23:43:24 +00003454 // Commented out as this would make the layout test flaky.
3455 // log += "Test " + zeroPaddedIndex + " passed, but took " + retry + " attempts (less than perfect).\n";
eric@webkit.orga7f130f2008-03-28 09:23:39 +00003456 } else if (elapsedTest > 33) { // 30fps
3457 errors += 1;
cdumez@apple.com371f9792017-04-24 23:43:24 +00003458 // Commented out as this would make the layout test flaky.
3459 // log += "Test " + zeroPaddedIndex + " passed, but took " + elapsedTest + "ms (less than 30fps)\n";
eric@webkit.orga7f130f2008-03-28 09:23:39 +00003460 }
3461 } else {
3462 fail("no error message");
3463 }
3464 } catch (e) {
3465 var s;
3466 if (e.message)
3467 s = e.message.replace(/\s+$/, "");
3468 else
3469 s = e;
3470 errors += 1;
3471 log += "Test " + zeroPaddedIndex + " failed: " + s + "\n";
3472 };
3473 retry = 0;
3474 index += 1;
3475 span.firstChild.data = score;
3476 setTimeout(update, delay);
3477 } else {
3478 var endTime = new Date();
3479 var elapsedTime = ((endTime - startTime) - (delay * tests.length)) / 1000;
cdumez@apple.com371f9792017-04-24 23:43:24 +00003480 // Commented out as this would make the layout test flaky.
3481 // log += "Total elapsed time: " + elapsedTime.toFixed(2) + "s";
eric@webkit.orga7f130f2008-03-28 09:23:39 +00003482 if (errors == 0)
eric@webkit.orgaadb79d2010-06-11 09:10:18 +00003483 log += "\nNo JS errors and no timing issues.\nWas the rendering pixel-for-pixel perfect too?";
3484
rniwa@webkit.org14a295a2012-06-13 07:49:51 +00003485 if (window.testRunner) {
mitz@apple.com73534e32008-07-25 21:42:18 +00003486 if (score < 100)
3487 alert(log);
rniwa@webkit.org14a295a2012-06-13 07:49:51 +00003488 testRunner.notifyDone();
mitz@apple.com73534e32008-07-25 21:42:18 +00003489 }
eric@webkit.orga7f130f2008-03-28 09:23:39 +00003490 }
3491 }
3492 function report(event) {
3493 // for debugging either click the "A" in "Acid3" (to get an alert) or shift-click it (to get a report)
3494 if (event.shiftKey) {
3495 var w = window.open();
3496 w.document.write('<pre>Failed ' + (tests.length - score) + ' of ' + tests.length + ' tests.\n' +
3497 log.replace(/&/g,'&amp;').replace(RegExp('<', 'g'), '&lt;').replace('\0', '\\0') +
3498 '<\/pre>');
3499 w.document.close();
3500 } else {
3501 alert('Failed ' + (tests.length - score) + ' test' + (score == 1 ? '' : 's') + '.\n' + log)
3502 }
3503 }
3504 </script>
3505 <body onload="update() /* this attribute's value is tested in one of the tests */ ">
3506 <h1 onclick="report(event)">Acid3</h1>
3507 <div class="buckets"
3508 ><p id="bucket1" class="z"></p
3509 ><p id="bucket2" class="z"></p
3510 ><p id="bucket3" class="z"></p
3511 ><p id="bucket4" class="z"></p
3512 ><p id="bucket5" class="z"></p
3513 ><p id="bucket6" class="z"></p>
3514 </div>
3515 <p id="result"><span id="score">JS</span><span id="slash" class="hidden">/</span><span>?</span></p>
3516 <!-- The following line is used in a number of the tests. It is done using document.write() to sidestep complaints of validity. -->
3517 <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>
eric@webkit.orgaadb79d2010-06-11 09:10:18 +00003518 <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>
eric@webkit.orga7f130f2008-03-28 09:23:39 +00003519 <p id="remove-last-child-test">Scripting must be enabled to use this test.</p>
3520 </body>
3521</html>