Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1022)

Side by Side Diff: third_party/WebKit/LayoutTests/imported/web-platform-tests/dom/common.js

Issue 1529523002: Import dom/ from web-platform-tests (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: tweak W3CImportExpectations Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 "use strict";
2 // Written by Aryeh Gregor <ayg@aryeh.name>
3
4 // TODO: iframes, contenteditable/designMode
5
6 // Everything is done in functions in this test harness, so we have to declare
7 // all the variables before use to make sure they can be reused.
8 var testDiv, paras, detachedDiv, detachedPara1, detachedPara2,
9 foreignDoc, foreignPara1, foreignPara2, xmlDoc, xmlElement,
10 detachedXmlElement, detachedTextNode, foreignTextNode,
11 detachedForeignTextNode, xmlTextNode, detachedXmlTextNode,
12 processingInstruction, detachedProcessingInstruction, comment,
13 detachedComment, foreignComment, detachedForeignComment, xmlComment,
14 detachedXmlComment, docfrag, foreignDocfrag, xmlDocfrag, doctype,
15 foreignDoctype, xmlDoctype;
16 var testRangesShort, testRanges, testPoints, testNodesShort, testNodes;
17
18 function setupRangeTests() {
19 testDiv = document.querySelector("#test");
20 if (testDiv) {
21 testDiv.parentNode.removeChild(testDiv);
22 }
23 testDiv = document.createElement("div");
24 testDiv.id = "test";
25 document.body.insertBefore(testDiv, document.body.firstChild);
26
27 paras = [];
28 paras.push(document.createElement("p"));
29 paras[0].setAttribute("id", "a");
30 // Test some diacritics, to make sure browsers are using code units here
31 // and not something like grapheme clusters.
32 paras[0].textContent = "A\u0308b\u0308c\u0308d\u0308e\u0308f\u0308g\u0308h\u 0308\n";
33 testDiv.appendChild(paras[0]);
34
35 paras.push(document.createElement("p"));
36 paras[1].setAttribute("id", "b");
37 paras[1].setAttribute("style", "display:none");
38 paras[1].textContent = "Ijklmnop\n";
39 testDiv.appendChild(paras[1]);
40
41 paras.push(document.createElement("p"));
42 paras[2].setAttribute("id", "c");
43 paras[2].textContent = "Qrstuvwx";
44 testDiv.appendChild(paras[2]);
45
46 paras.push(document.createElement("p"));
47 paras[3].setAttribute("id", "d");
48 paras[3].setAttribute("style", "display:none");
49 paras[3].textContent = "Yzabcdef";
50 testDiv.appendChild(paras[3]);
51
52 paras.push(document.createElement("p"));
53 paras[4].setAttribute("id", "e");
54 paras[4].setAttribute("style", "display:none");
55 paras[4].textContent = "Ghijklmn";
56 testDiv.appendChild(paras[4]);
57
58 detachedDiv = document.createElement("div");
59 detachedPara1 = document.createElement("p");
60 detachedPara1.appendChild(document.createTextNode("Opqrstuv"));
61 detachedPara2 = document.createElement("p");
62 detachedPara2.appendChild(document.createTextNode("Wxyzabcd"));
63 detachedDiv.appendChild(detachedPara1);
64 detachedDiv.appendChild(detachedPara2);
65
66 // Opera doesn't automatically create a doctype for a new HTML document,
67 // contrary to spec. It also doesn't let you add doctypes to documents
68 // after the fact through any means I've tried. So foreignDoc in Opera
69 // will have no doctype, foreignDoctype will be null, and Opera will fail
70 // some tests somewhat mysteriously as a result.
71 foreignDoc = document.implementation.createHTMLDocument("");
72 foreignPara1 = foreignDoc.createElement("p");
73 foreignPara1.appendChild(foreignDoc.createTextNode("Efghijkl"));
74 foreignPara2 = foreignDoc.createElement("p");
75 foreignPara2.appendChild(foreignDoc.createTextNode("Mnopqrst"));
76 foreignDoc.body.appendChild(foreignPara1);
77 foreignDoc.body.appendChild(foreignPara2);
78
79 // Now we get to do really silly stuff, which nobody in the universe is
80 // ever going to actually do, but the spec defines behavior, so too bad.
81 // Testing is fun!
82 xmlDoctype = document.implementation.createDocumentType("qorflesnorf", "abcd e", "x\"'y");
83 xmlDoc = document.implementation.createDocument(null, null, xmlDoctype);
84 detachedXmlElement = xmlDoc.createElement("everyone-hates-hyphenated-element -names");
85 detachedTextNode = document.createTextNode("Uvwxyzab");
86 detachedForeignTextNode = foreignDoc.createTextNode("Cdefghij");
87 detachedXmlTextNode = xmlDoc.createTextNode("Klmnopqr");
88 // PIs only exist in XML documents, so don't bother with document or
89 // foreignDoc.
90 detachedProcessingInstruction = xmlDoc.createProcessingInstruction("whippoor will", "chirp chirp chirp");
91 detachedComment = document.createComment("Stuvwxyz");
92 // Hurrah, we finally got to "z" at the end!
93 detachedForeignComment = foreignDoc.createComment("אריה יהודה");
94 detachedXmlComment = xmlDoc.createComment("בן חיים אליעזר");
95
96 // We should also test with document fragments that actually contain stuff
97 // . . . but, maybe later.
98 docfrag = document.createDocumentFragment();
99 foreignDocfrag = foreignDoc.createDocumentFragment();
100 xmlDocfrag = xmlDoc.createDocumentFragment();
101
102 xmlElement = xmlDoc.createElement("igiveuponcreativenames");
103 xmlTextNode = xmlDoc.createTextNode("do re mi fa so la ti");
104 xmlElement.appendChild(xmlTextNode);
105 processingInstruction = xmlDoc.createProcessingInstruction("somePI", 'Did yo u know that ":syn sync fromstart" is very useful when using vim to edit large am ounts of JavaScript embedded in HTML?');
106 xmlDoc.appendChild(xmlElement);
107 xmlDoc.appendChild(processingInstruction);
108 xmlComment = xmlDoc.createComment("I maliciously created a comment that will break incautious XML serializers, but Firefox threw an exception, so all I got was this lousy T-shirt");
109 xmlDoc.appendChild(xmlComment);
110
111 comment = document.createComment("Alphabet soup?");
112 testDiv.appendChild(comment);
113
114 foreignComment = foreignDoc.createComment('"Commenter" and "commentator" mea n different things. I\'ve seen non-native speakers trip up on this.');
115 foreignDoc.appendChild(foreignComment);
116 foreignTextNode = foreignDoc.createTextNode("I admit that I harbor doubts ab out whether we really need so many things to test, but it's too late to stop now .");
117 foreignDoc.body.appendChild(foreignTextNode);
118
119 doctype = document.doctype;
120 foreignDoctype = foreignDoc.doctype;
121
122 testRangesShort = [
123 // Various ranges within the text node children of different
124 // paragraphs. All should be valid.
125 "[paras[0].firstChild, 0, paras[0].firstChild, 0]",
126 "[paras[0].firstChild, 0, paras[0].firstChild, 1]",
127 "[paras[0].firstChild, 2, paras[0].firstChild, 8]",
128 "[paras[0].firstChild, 2, paras[0].firstChild, 9]",
129 "[paras[1].firstChild, 0, paras[1].firstChild, 0]",
130 "[paras[1].firstChild, 2, paras[1].firstChild, 9]",
131 "[detachedPara1.firstChild, 0, detachedPara1.firstChild, 0]",
132 "[detachedPara1.firstChild, 2, detachedPara1.firstChild, 8]",
133 "[foreignPara1.firstChild, 0, foreignPara1.firstChild, 0]",
134 "[foreignPara1.firstChild, 2, foreignPara1.firstChild, 8]",
135 // Now try testing some elements, not just text nodes.
136 "[document.documentElement, 0, document.documentElement, 1]",
137 "[document.documentElement, 0, document.documentElement, 2]",
138 "[document.documentElement, 1, document.documentElement, 2]",
139 "[document.head, 1, document.head, 1]",
140 "[document.body, 4, document.body, 5]",
141 "[foreignDoc.documentElement, 0, foreignDoc.documentElement, 1]",
142 "[paras[0], 0, paras[0], 1]",
143 "[detachedPara1, 0, detachedPara1, 1]",
144 // Now try some ranges that span elements.
145 "[paras[0].firstChild, 0, paras[1].firstChild, 0]",
146 "[paras[0].firstChild, 0, paras[1].firstChild, 8]",
147 "[paras[0].firstChild, 3, paras[3], 1]",
148 // How about something that spans a node and its descendant?
149 "[paras[0], 0, paras[0].firstChild, 7]",
150 "[testDiv, 2, paras[4], 1]",
151 // Then a few more interesting things just for good measure.
152 "[document, 0, document, 1]",
153 "[document, 0, document, 2]",
154 "[comment, 2, comment, 3]",
155 "[testDiv, 0, comment, 5]",
156 "[foreignDoc, 1, foreignComment, 2]",
157 "[foreignDoc.body, 0, foreignTextNode, 36]",
158 "[xmlDoc, 1, xmlComment, 0]",
159 "[detachedTextNode, 0, detachedTextNode, 8]",
160 "[detachedForeignTextNode, 0, detachedForeignTextNode, 8]",
161 "[detachedXmlTextNode, 0, detachedXmlTextNode, 8]",
162 "[detachedComment, 3, detachedComment, 4]",
163 "[detachedForeignComment, 0, detachedForeignComment, 1]",
164 "[detachedXmlComment, 2, detachedXmlComment, 6]",
165 "[docfrag, 0, docfrag, 0]",
166 "[processingInstruction, 0, processingInstruction, 4]",
167 ];
168
169 testRanges = testRangesShort.concat([
170 "[paras[1].firstChild, 0, paras[1].firstChild, 1]",
171 "[paras[1].firstChild, 2, paras[1].firstChild, 8]",
172 "[detachedPara1.firstChild, 0, detachedPara1.firstChild, 1]",
173 "[foreignPara1.firstChild, 0, foreignPara1.firstChild, 1]",
174 "[foreignDoc.head, 1, foreignDoc.head, 1]",
175 "[foreignDoc.body, 0, foreignDoc.body, 0]",
176 "[paras[0], 0, paras[0], 0]",
177 "[detachedPara1, 0, detachedPara1, 0]",
178 "[testDiv, 1, paras[2].firstChild, 5]",
179 "[document.documentElement, 1, document.body, 0]",
180 "[foreignDoc.documentElement, 1, foreignDoc.body, 0]",
181 "[document, 1, document, 2]",
182 "[paras[2].firstChild, 4, comment, 2]",
183 "[paras[3], 1, comment, 8]",
184 "[foreignDoc, 0, foreignDoc, 0]",
185 "[xmlDoc, 0, xmlDoc, 0]",
186 "[detachedForeignTextNode, 7, detachedForeignTextNode, 7]",
187 "[detachedXmlTextNode, 7, detachedXmlTextNode, 7]",
188 "[detachedComment, 5, detachedComment, 5]",
189 "[detachedForeignComment, 4, detachedForeignComment, 4]",
190 "[foreignDocfrag, 0, foreignDocfrag, 0]",
191 "[xmlDocfrag, 0, xmlDocfrag, 0]",
192 ]);
193
194 testPoints = [
195 // Various positions within the page, some invalid. Remember that
196 // paras[0] is visible, and paras[1] is display: none.
197 "[paras[0].firstChild, -1]",
198 "[paras[0].firstChild, 0]",
199 "[paras[0].firstChild, 1]",
200 "[paras[0].firstChild, 2]",
201 "[paras[0].firstChild, 8]",
202 "[paras[0].firstChild, 9]",
203 "[paras[0].firstChild, 10]",
204 "[paras[0].firstChild, 65535]",
205 "[paras[1].firstChild, -1]",
206 "[paras[1].firstChild, 0]",
207 "[paras[1].firstChild, 1]",
208 "[paras[1].firstChild, 2]",
209 "[paras[1].firstChild, 8]",
210 "[paras[1].firstChild, 9]",
211 "[paras[1].firstChild, 10]",
212 "[paras[1].firstChild, 65535]",
213 "[detachedPara1.firstChild, 0]",
214 "[detachedPara1.firstChild, 1]",
215 "[detachedPara1.firstChild, 8]",
216 "[detachedPara1.firstChild, 9]",
217 "[foreignPara1.firstChild, 0]",
218 "[foreignPara1.firstChild, 1]",
219 "[foreignPara1.firstChild, 8]",
220 "[foreignPara1.firstChild, 9]",
221 // Now try testing some elements, not just text nodes.
222 "[document.documentElement, -1]",
223 "[document.documentElement, 0]",
224 "[document.documentElement, 1]",
225 "[document.documentElement, 2]",
226 "[document.documentElement, 7]",
227 "[document.head, 1]",
228 "[document.body, 3]",
229 "[foreignDoc.documentElement, 0]",
230 "[foreignDoc.documentElement, 1]",
231 "[foreignDoc.head, 0]",
232 "[foreignDoc.body, 1]",
233 "[paras[0], 0]",
234 "[paras[0], 1]",
235 "[paras[0], 2]",
236 "[paras[1], 0]",
237 "[paras[1], 1]",
238 "[paras[1], 2]",
239 "[detachedPara1, 0]",
240 "[detachedPara1, 1]",
241 "[testDiv, 0]",
242 "[testDiv, 3]",
243 // Then a few more interesting things just for good measure.
244 "[document, -1]",
245 "[document, 0]",
246 "[document, 1]",
247 "[document, 2]",
248 "[document, 3]",
249 "[comment, -1]",
250 "[comment, 0]",
251 "[comment, 4]",
252 "[comment, 96]",
253 "[foreignDoc, 0]",
254 "[foreignDoc, 1]",
255 "[foreignComment, 2]",
256 "[foreignTextNode, 0]",
257 "[foreignTextNode, 36]",
258 "[xmlDoc, -1]",
259 "[xmlDoc, 0]",
260 "[xmlDoc, 1]",
261 "[xmlDoc, 5]",
262 "[xmlComment, 0]",
263 "[xmlComment, 4]",
264 "[processingInstruction, 0]",
265 "[processingInstruction, 5]",
266 "[processingInstruction, 9]",
267 "[detachedTextNode, 0]",
268 "[detachedTextNode, 8]",
269 "[detachedForeignTextNode, 0]",
270 "[detachedForeignTextNode, 8]",
271 "[detachedXmlTextNode, 0]",
272 "[detachedXmlTextNode, 8]",
273 "[detachedProcessingInstruction, 12]",
274 "[detachedComment, 3]",
275 "[detachedComment, 5]",
276 "[detachedForeignComment, 0]",
277 "[detachedForeignComment, 4]",
278 "[detachedXmlComment, 2]",
279 "[docfrag, 0]",
280 "[foreignDocfrag, 0]",
281 "[xmlDocfrag, 0]",
282 "[doctype, 0]",
283 "[doctype, -17]",
284 "[doctype, 1]",
285 "[foreignDoctype, 0]",
286 "[xmlDoctype, 0]",
287 ];
288
289 testNodesShort = [
290 "paras[0]",
291 "paras[0].firstChild",
292 "paras[1].firstChild",
293 "foreignPara1",
294 "foreignPara1.firstChild",
295 "detachedPara1",
296 "detachedPara1.firstChild",
297 "document",
298 "detachedDiv",
299 "foreignDoc",
300 "foreignPara2",
301 "xmlDoc",
302 "xmlElement",
303 "detachedTextNode",
304 "foreignTextNode",
305 "processingInstruction",
306 "detachedProcessingInstruction",
307 "comment",
308 "detachedComment",
309 "docfrag",
310 "doctype",
311 "foreignDoctype",
312 ];
313
314 testNodes = testNodesShort.concat([
315 "paras[1]",
316 "detachedPara2",
317 "detachedPara2.firstChild",
318 "testDiv",
319 "detachedXmlElement",
320 "detachedForeignTextNode",
321 "xmlTextNode",
322 "detachedXmlTextNode",
323 "xmlComment",
324 "foreignComment",
325 "detachedForeignComment",
326 "detachedXmlComment",
327 "foreignDocfrag",
328 "xmlDocfrag",
329 "xmlDoctype",
330 ]);
331 }
332 if ("setup" in window) {
333 setup(setupRangeTests);
334 } else {
335 // Presumably we're running from within an iframe or something
336 setupRangeTests();
337 }
338
339 /**
340 * The "length" of a node as defined by the Ranges section of DOM4.
341 */
342 function nodeLength(node) {
343 // "The length of a node node depends on node:
344 //
345 // "DocumentType
346 // "0."
347 if (node.nodeType == Node.DOCUMENT_TYPE_NODE) {
348 return 0;
349 }
350 // "Text
351 // "ProcessingInstruction
352 // "Comment
353 // "Its length attribute value."
354 // Browsers don't historically support the length attribute on
355 // ProcessingInstruction, so to avoid spurious failures, do
356 // node.data.length instead of node.length.
357 if (node.nodeType == Node.TEXT_NODE || node.nodeType == Node.PROCESSING_INST RUCTION_NODE || node.nodeType == Node.COMMENT_NODE) {
358 return node.data.length;
359 }
360 // "Any other node
361 // "Its number of children."
362 return node.childNodes.length;
363 }
364
365 /**
366 * Returns the furthest ancestor of a Node as defined by the spec.
367 */
368 function furthestAncestor(node) {
369 var root = node;
370 while (root.parentNode != null) {
371 root = root.parentNode;
372 }
373 return root;
374 }
375
376 /**
377 * "The ancestor containers of a Node are the Node itself and all its
378 * ancestors."
379 *
380 * Is node1 an ancestor container of node2?
381 */
382 function isAncestorContainer(node1, node2) {
383 return node1 == node2 ||
384 (node2.compareDocumentPosition(node1) & Node.DOCUMENT_POSITION_CONTAINS) ;
385 }
386
387 /**
388 * Returns the first Node that's after node in tree order, or null if node is
389 * the last Node.
390 */
391 function nextNode(node) {
392 if (node.hasChildNodes()) {
393 return node.firstChild;
394 }
395 return nextNodeDescendants(node);
396 }
397
398 /**
399 * Returns the last Node that's before node in tree order, or null if node is
400 * the first Node.
401 */
402 function previousNode(node) {
403 if (node.previousSibling) {
404 node = node.previousSibling;
405 while (node.hasChildNodes()) {
406 node = node.lastChild;
407 }
408 return node;
409 }
410 return node.parentNode;
411 }
412
413 /**
414 * Returns the next Node that's after node and all its descendants in tree
415 * order, or null if node is the last Node or an ancestor of it.
416 */
417 function nextNodeDescendants(node) {
418 while (node && !node.nextSibling) {
419 node = node.parentNode;
420 }
421 if (!node) {
422 return null;
423 }
424 return node.nextSibling;
425 }
426
427 /**
428 * Returns the ownerDocument of the Node, or the Node itself if it's a
429 * Document.
430 */
431 function ownerDocument(node) {
432 return node.nodeType == Node.DOCUMENT_NODE
433 ? node
434 : node.ownerDocument;
435 }
436
437 /**
438 * Returns true if ancestor is an ancestor of descendant, false otherwise.
439 */
440 function isAncestor(ancestor, descendant) {
441 if (!ancestor || !descendant) {
442 return false;
443 }
444 while (descendant && descendant != ancestor) {
445 descendant = descendant.parentNode;
446 }
447 return descendant == ancestor;
448 }
449
450 /**
451 * Returns true if ancestor is an inclusive ancestor of descendant, false
452 * otherwise.
453 */
454 function isInclusiveAncestor(ancestor, descendant) {
455 return ancestor === descendant || isAncestor(ancestor, descendant);
456 }
457
458 /**
459 * Returns true if descendant is a descendant of ancestor, false otherwise.
460 */
461 function isDescendant(descendant, ancestor) {
462 return isAncestor(ancestor, descendant);
463 }
464
465 /**
466 * Returns true if descendant is an inclusive descendant of ancestor, false
467 * otherwise.
468 */
469 function isInclusiveDescendant(descendant, ancestor) {
470 return descendant === ancestor || isDescendant(descendant, ancestor);
471 }
472
473 /**
474 * The position of two boundary points relative to one another, as defined by
475 * the spec.
476 */
477 function getPosition(nodeA, offsetA, nodeB, offsetB) {
478 // "If node A is the same as node B, return equal if offset A equals offset
479 // B, before if offset A is less than offset B, and after if offset A is
480 // greater than offset B."
481 if (nodeA == nodeB) {
482 if (offsetA == offsetB) {
483 return "equal";
484 }
485 if (offsetA < offsetB) {
486 return "before";
487 }
488 if (offsetA > offsetB) {
489 return "after";
490 }
491 }
492
493 // "If node A is after node B in tree order, compute the position of (node
494 // B, offset B) relative to (node A, offset A). If it is before, return
495 // after. If it is after, return before."
496 if (nodeB.compareDocumentPosition(nodeA) & Node.DOCUMENT_POSITION_FOLLOWING) {
497 var pos = getPosition(nodeB, offsetB, nodeA, offsetA);
498 if (pos == "before") {
499 return "after";
500 }
501 if (pos == "after") {
502 return "before";
503 }
504 }
505
506 // "If node A is an ancestor of node B:"
507 if (nodeB.compareDocumentPosition(nodeA) & Node.DOCUMENT_POSITION_CONTAINS) {
508 // "Let child equal node B."
509 var child = nodeB;
510
511 // "While child is not a child of node A, set child to its parent."
512 while (child.parentNode != nodeA) {
513 child = child.parentNode;
514 }
515
516 // "If the index of child is less than offset A, return after."
517 if (indexOf(child) < offsetA) {
518 return "after";
519 }
520 }
521
522 // "Return before."
523 return "before";
524 }
525
526 /**
527 * "contained" as defined by DOM Range: "A Node node is contained in a range
528 * range if node's furthest ancestor is the same as range's root, and (node, 0)
529 * is after range's start, and (node, length of node) is before range's end."
530 */
531 function isContained(node, range) {
532 var pos1 = getPosition(node, 0, range.startContainer, range.startOffset);
533 var pos2 = getPosition(node, nodeLength(node), range.endContainer, range.end Offset);
534
535 return furthestAncestor(node) == furthestAncestor(range.startContainer)
536 && pos1 == "after"
537 && pos2 == "before";
538 }
539
540 /**
541 * "partially contained" as defined by DOM Range: "A Node is partially
542 * contained in a range if it is an ancestor container of the range's start but
543 * not its end, or vice versa."
544 */
545 function isPartiallyContained(node, range) {
546 var cond1 = isAncestorContainer(node, range.startContainer);
547 var cond2 = isAncestorContainer(node, range.endContainer);
548 return (cond1 && !cond2) || (cond2 && !cond1);
549 }
550
551 /**
552 * Index of a node as defined by the spec.
553 */
554 function indexOf(node) {
555 if (!node.parentNode) {
556 // No preceding sibling nodes, right?
557 return 0;
558 }
559 var i = 0;
560 while (node != node.parentNode.childNodes[i]) {
561 i++;
562 }
563 return i;
564 }
565
566 /**
567 * extractContents() implementation, following the spec. If an exception is
568 * supposed to be thrown, will return a string with the name (e.g.,
569 * "HIERARCHY_REQUEST_ERR") instead of a document fragment. It might also
570 * return an arbitrary human-readable string if a condition is hit that implies
571 * a spec bug.
572 */
573 function myExtractContents(range) {
574 // "Let frag be a new DocumentFragment whose ownerDocument is the same as
575 // the ownerDocument of the context object's start node."
576 var ownerDoc = range.startContainer.nodeType == Node.DOCUMENT_NODE
577 ? range.startContainer
578 : range.startContainer.ownerDocument;
579 var frag = ownerDoc.createDocumentFragment();
580
581 // "If the context object's start and end are the same, abort this method,
582 // returning frag."
583 if (range.startContainer == range.endContainer
584 && range.startOffset == range.endOffset) {
585 return frag;
586 }
587
588 // "Let original start node, original start offset, original end node, and
589 // original end offset be the context object's start and end nodes and
590 // offsets, respectively."
591 var originalStartNode = range.startContainer;
592 var originalStartOffset = range.startOffset;
593 var originalEndNode = range.endContainer;
594 var originalEndOffset = range.endOffset;
595
596 // "If original start node is original end node, and they are a Text,
597 // ProcessingInstruction, or Comment node:"
598 if (range.startContainer == range.endContainer
599 && (range.startContainer.nodeType == Node.TEXT_NODE
600 || range.startContainer.nodeType == Node.PROCESSING_INSTRUCTION_NODE
601 || range.startContainer.nodeType == Node.COMMENT_NODE)) {
602 // "Let clone be the result of calling cloneNode(false) on original
603 // start node."
604 var clone = originalStartNode.cloneNode(false);
605
606 // "Set the data of clone to the result of calling
607 // substringData(original start offset, original end offset − original
608 // start offset) on original start node."
609 clone.data = originalStartNode.substringData(originalStartOffset,
610 originalEndOffset - originalStartOffset);
611
612 // "Append clone as the last child of frag."
613 frag.appendChild(clone);
614
615 // "Call deleteData(original start offset, original end offset −
616 // original start offset) on original start node."
617 originalStartNode.deleteData(originalStartOffset,
618 originalEndOffset - originalStartOffset);
619
620 // "Abort this method, returning frag."
621 return frag;
622 }
623
624 // "Let common ancestor equal original start node."
625 var commonAncestor = originalStartNode;
626
627 // "While common ancestor is not an ancestor container of original end
628 // node, set common ancestor to its own parent."
629 while (!isAncestorContainer(commonAncestor, originalEndNode)) {
630 commonAncestor = commonAncestor.parentNode;
631 }
632
633 // "If original start node is an ancestor container of original end node,
634 // let first partially contained child be null."
635 var firstPartiallyContainedChild;
636 if (isAncestorContainer(originalStartNode, originalEndNode)) {
637 firstPartiallyContainedChild = null;
638 // "Otherwise, let first partially contained child be the first child of
639 // common ancestor that is partially contained in the context object."
640 } else {
641 for (var i = 0; i < commonAncestor.childNodes.length; i++) {
642 if (isPartiallyContained(commonAncestor.childNodes[i], range)) {
643 firstPartiallyContainedChild = commonAncestor.childNodes[i];
644 break;
645 }
646 }
647 if (!firstPartiallyContainedChild) {
648 throw "Spec bug: no first partially contained child!";
649 }
650 }
651
652 // "If original end node is an ancestor container of original start node,
653 // let last partially contained child be null."
654 var lastPartiallyContainedChild;
655 if (isAncestorContainer(originalEndNode, originalStartNode)) {
656 lastPartiallyContainedChild = null;
657 // "Otherwise, let last partially contained child be the last child of
658 // common ancestor that is partially contained in the context object."
659 } else {
660 for (var i = commonAncestor.childNodes.length - 1; i >= 0; i--) {
661 if (isPartiallyContained(commonAncestor.childNodes[i], range)) {
662 lastPartiallyContainedChild = commonAncestor.childNodes[i];
663 break;
664 }
665 }
666 if (!lastPartiallyContainedChild) {
667 throw "Spec bug: no last partially contained child!";
668 }
669 }
670
671 // "Let contained children be a list of all children of common ancestor
672 // that are contained in the context object, in tree order."
673 //
674 // "If any member of contained children is a DocumentType, raise a
675 // HIERARCHY_REQUEST_ERR exception and abort these steps."
676 var containedChildren = [];
677 for (var i = 0; i < commonAncestor.childNodes.length; i++) {
678 if (isContained(commonAncestor.childNodes[i], range)) {
679 if (commonAncestor.childNodes[i].nodeType
680 == Node.DOCUMENT_TYPE_NODE) {
681 return "HIERARCHY_REQUEST_ERR";
682 }
683 containedChildren.push(commonAncestor.childNodes[i]);
684 }
685 }
686
687 // "If original start node is an ancestor container of original end node,
688 // set new node to original start node and new offset to original start
689 // offset."
690 var newNode, newOffset;
691 if (isAncestorContainer(originalStartNode, originalEndNode)) {
692 newNode = originalStartNode;
693 newOffset = originalStartOffset;
694 // "Otherwise:"
695 } else {
696 // "Let reference node equal original start node."
697 var referenceNode = originalStartNode;
698
699 // "While reference node's parent is not null and is not an ancestor
700 // container of original end node, set reference node to its parent."
701 while (referenceNode.parentNode
702 && !isAncestorContainer(referenceNode.parentNode, originalEndNode)) {
703 referenceNode = referenceNode.parentNode;
704 }
705
706 // "Set new node to the parent of reference node, and new offset to one
707 // plus the index of reference node."
708 newNode = referenceNode.parentNode;
709 newOffset = 1 + indexOf(referenceNode);
710 }
711
712 // "If first partially contained child is a Text, ProcessingInstruction, or
713 // Comment node:"
714 if (firstPartiallyContainedChild
715 && (firstPartiallyContainedChild.nodeType == Node.TEXT_NODE
716 || firstPartiallyContainedChild.nodeType == Node.PROCESSING_INSTRUCTION_NODE
717 || firstPartiallyContainedChild.nodeType == Node.COMMENT_NODE)) {
718 // "Let clone be the result of calling cloneNode(false) on original
719 // start node."
720 var clone = originalStartNode.cloneNode(false);
721
722 // "Set the data of clone to the result of calling substringData() on
723 // original start node, with original start offset as the first
724 // argument and (length of original start node − original start offset)
725 // as the second."
726 clone.data = originalStartNode.substringData(originalStartOffset,
727 nodeLength(originalStartNode) - originalStartOffset);
728
729 // "Append clone as the last child of frag."
730 frag.appendChild(clone);
731
732 // "Call deleteData() on original start node, with original start
733 // offset as the first argument and (length of original start node −
734 // original start offset) as the second."
735 originalStartNode.deleteData(originalStartOffset,
736 nodeLength(originalStartNode) - originalStartOffset);
737 // "Otherwise, if first partially contained child is not null:"
738 } else if (firstPartiallyContainedChild) {
739 // "Let clone be the result of calling cloneNode(false) on first
740 // partially contained child."
741 var clone = firstPartiallyContainedChild.cloneNode(false);
742
743 // "Append clone as the last child of frag."
744 frag.appendChild(clone);
745
746 // "Let subrange be a new Range whose start is (original start node,
747 // original start offset) and whose end is (first partially contained
748 // child, length of first partially contained child)."
749 var subrange = ownerDoc.createRange();
750 subrange.setStart(originalStartNode, originalStartOffset);
751 subrange.setEnd(firstPartiallyContainedChild,
752 nodeLength(firstPartiallyContainedChild));
753
754 // "Let subfrag be the result of calling extractContents() on
755 // subrange."
756 var subfrag = myExtractContents(subrange);
757
758 // "For each child of subfrag, in order, append that child to clone as
759 // its last child."
760 for (var i = 0; i < subfrag.childNodes.length; i++) {
761 clone.appendChild(subfrag.childNodes[i]);
762 }
763 }
764
765 // "For each contained child in contained children, append contained child
766 // as the last child of frag."
767 for (var i = 0; i < containedChildren.length; i++) {
768 frag.appendChild(containedChildren[i]);
769 }
770
771 // "If last partially contained child is a Text, ProcessingInstruction, or
772 // Comment node:"
773 if (lastPartiallyContainedChild
774 && (lastPartiallyContainedChild.nodeType == Node.TEXT_NODE
775 || lastPartiallyContainedChild.nodeType == Node.PROCESSING_INSTRUCTION_NODE
776 || lastPartiallyContainedChild.nodeType == Node.COMMENT_NODE)) {
777 // "Let clone be the result of calling cloneNode(false) on original
778 // end node."
779 var clone = originalEndNode.cloneNode(false);
780
781 // "Set the data of clone to the result of calling substringData(0,
782 // original end offset) on original end node."
783 clone.data = originalEndNode.substringData(0, originalEndOffset);
784
785 // "Append clone as the last child of frag."
786 frag.appendChild(clone);
787
788 // "Call deleteData(0, original end offset) on original end node."
789 originalEndNode.deleteData(0, originalEndOffset);
790 // "Otherwise, if last partially contained child is not null:"
791 } else if (lastPartiallyContainedChild) {
792 // "Let clone be the result of calling cloneNode(false) on last
793 // partially contained child."
794 var clone = lastPartiallyContainedChild.cloneNode(false);
795
796 // "Append clone as the last child of frag."
797 frag.appendChild(clone);
798
799 // "Let subrange be a new Range whose start is (last partially
800 // contained child, 0) and whose end is (original end node, original
801 // end offset)."
802 var subrange = ownerDoc.createRange();
803 subrange.setStart(lastPartiallyContainedChild, 0);
804 subrange.setEnd(originalEndNode, originalEndOffset);
805
806 // "Let subfrag be the result of calling extractContents() on
807 // subrange."
808 var subfrag = myExtractContents(subrange);
809
810 // "For each child of subfrag, in order, append that child to clone as
811 // its last child."
812 for (var i = 0; i < subfrag.childNodes.length; i++) {
813 clone.appendChild(subfrag.childNodes[i]);
814 }
815 }
816
817 // "Set the context object's start and end to (new node, new offset)."
818 range.setStart(newNode, newOffset);
819 range.setEnd(newNode, newOffset);
820
821 // "Return frag."
822 return frag;
823 }
824
825 /**
826 * insertNode() implementation, following the spec. If an exception is meant
827 * to be thrown, will return a string with the expected exception name, for
828 * instance "HIERARCHY_REQUEST_ERR".
829 */
830 function myInsertNode(range, node) {
831 // "If range's start node is a ProcessingInstruction or Comment node, or is
832 // a Text node whose parent is null, or is node, throw an
833 // "HierarchyRequestError" exception and terminate these steps."
834 if (range.startContainer.nodeType == Node.PROCESSING_INSTRUCTION_NODE
835 || range.startContainer.nodeType == Node.COMMENT_NODE
836 || (range.startContainer.nodeType == Node.TEXT_NODE
837 && !range.startContainer.parentNode)
838 || range.startContainer == node) {
839 return "HIERARCHY_REQUEST_ERR";
840 }
841
842 // "Let referenceNode be null."
843 var referenceNode = null;
844
845 // "If range's start node is a Text node, set referenceNode to that Text nod e."
846 if (range.startContainer.nodeType == Node.TEXT_NODE) {
847 referenceNode = range.startContainer;
848
849 // "Otherwise, set referenceNode to the child of start node whose index is
850 // start offset, and null if there is no such child."
851 } else {
852 if (range.startOffset < range.startContainer.childNodes.length) {
853 referenceNode = range.startContainer.childNodes[range.startOffset];
854 } else {
855 referenceNode = null;
856 }
857 }
858
859 // "Let parent be range's start node if referenceNode is null, and
860 // referenceNode's parent otherwise."
861 var parent_ = referenceNode === null ? range.startContainer :
862 referenceNode.parentNode;
863
864 // "Ensure pre-insertion validity of node into parent before
865 // referenceNode."
866 var error = ensurePreInsertionValidity(node, parent_, referenceNode);
867 if (error) {
868 return error;
869 }
870
871 // "If range's start node is a Text node, set referenceNode to the result
872 // of splitting it with offset range's start offset."
873 if (range.startContainer.nodeType == Node.TEXT_NODE) {
874 referenceNode = range.startContainer.splitText(range.startOffset);
875 }
876
877 // "If node is referenceNode, set referenceNode to its next sibling."
878 if (node == referenceNode) {
879 referenceNode = referenceNode.nextSibling;
880 }
881
882 // "If node's parent is not null, remove node from its parent."
883 if (node.parentNode) {
884 node.parentNode.removeChild(node);
885 }
886
887 // "Let newOffset be parent's length if referenceNode is null, and
888 // referenceNode's index otherwise."
889 var newOffset = referenceNode === null ? nodeLength(parent_) :
890 indexOf(referenceNode);
891
892 // "Increase newOffset by node's length if node is a DocumentFragment node,
893 // and one otherwise."
894 newOffset += node.nodeType == Node.DOCUMENT_FRAGMENT_NODE ?
895 nodeLength(node) : 1;
896
897 // "Pre-insert node into parent before referenceNode."
898 parent_.insertBefore(node, referenceNode);
899
900 // "If range's start and end are the same, set range's end to (parent,
901 // newOffset)."
902 if (range.startContainer == range.endContainer
903 && range.startOffset == range.endOffset) {
904 range.setEnd(parent_, newOffset);
905 }
906 }
907
908 // To make filter() calls more readable
909 function isElement(node) {
910 return node.nodeType == Node.ELEMENT_NODE;
911 }
912
913 function isText(node) {
914 return node.nodeType == Node.TEXT_NODE;
915 }
916
917 function isDoctype(node) {
918 return node.nodeType == Node.DOCUMENT_TYPE_NODE;
919 }
920
921 function ensurePreInsertionValidity(node, parent_, child) {
922 // "If parent is not a Document, DocumentFragment, or Element node, throw a
923 // HierarchyRequestError."
924 if (parent_.nodeType != Node.DOCUMENT_NODE
925 && parent_.nodeType != Node.DOCUMENT_FRAGMENT_NODE
926 && parent_.nodeType != Node.ELEMENT_NODE) {
927 return "HIERARCHY_REQUEST_ERR";
928 }
929
930 // "If node is a host-including inclusive ancestor of parent, throw a
931 // HierarchyRequestError."
932 //
933 // XXX Does not account for host
934 if (isInclusiveAncestor(node, parent_)) {
935 return "HIERARCHY_REQUEST_ERR";
936 }
937
938 // "If child is not null and its parent is not parent, throw a NotFoundError
939 // exception."
940 if (child && child.parentNode != parent_) {
941 return "NOT_FOUND_ERR";
942 }
943
944 // "If node is not a DocumentFragment, DocumentType, Element, Text,
945 // ProcessingInstruction, or Comment node, throw a HierarchyRequestError."
946 if (node.nodeType != Node.DOCUMENT_FRAGMENT_NODE
947 && node.nodeType != Node.DOCUMENT_TYPE_NODE
948 && node.nodeType != Node.ELEMENT_NODE
949 && node.nodeType != Node.TEXT_NODE
950 && node.nodeType != Node.PROCESSING_INSTRUCTION_NODE
951 && node.nodeType != Node.COMMENT_NODE) {
952 return "HIERARCHY_REQUEST_ERR";
953 }
954
955 // "If either node is a Text node and parent is a document, or node is a
956 // doctype and parent is not a document, throw a HierarchyRequestError."
957 if ((node.nodeType == Node.TEXT_NODE
958 && parent_.nodeType == Node.DOCUMENT_NODE)
959 || (node.nodeType == Node.DOCUMENT_TYPE_NODE
960 && parent_.nodeType != Node.DOCUMENT_NODE)) {
961 return "HIERARCHY_REQUEST_ERR";
962 }
963
964 // "If parent is a document, and any of the statements below, switched on
965 // node, are true, throw a HierarchyRequestError."
966 if (parent_.nodeType == Node.DOCUMENT_NODE) {
967 switch (node.nodeType) {
968 case Node.DOCUMENT_FRAGMENT_NODE:
969 // "If node has more than one element child or has a Text node
970 // child. Otherwise, if node has one element child and either
971 // parent has an element child, child is a doctype, or child is not
972 // null and a doctype is following child."
973 if ([].filter.call(node.childNodes, isElement).length > 1) {
974 return "HIERARCHY_REQUEST_ERR";
975 }
976
977 if ([].some.call(node.childNodes, isText)) {
978 return "HIERARCHY_REQUEST_ERR";
979 }
980
981 if ([].filter.call(node.childNodes, isElement).length == 1) {
982 if ([].some.call(parent_.childNodes, isElement)) {
983 return "HIERARCHY_REQUEST_ERR";
984 }
985
986 if (child && child.nodeType == Node.DOCUMENT_TYPE_NODE) {
987 return "HIERARCHY_REQUEST_ERR";
988 }
989
990 if (child && [].slice.call(parent_.childNodes, indexOf(child) + 1)
991 .filter(isDoctype)) {
992 return "HIERARCHY_REQUEST_ERR";
993 }
994 }
995 break;
996
997 case Node.ELEMENT_NODE:
998 // "parent has an element child, child is a doctype, or child is
999 // not null and a doctype is following child."
1000 if ([].some.call(parent_.childNodes, isElement)) {
1001 return "HIERARCHY_REQUEST_ERR";
1002 }
1003
1004 if (child.nodeType == Node.DOCUMENT_TYPE_NODE) {
1005 return "HIERARCHY_REQUEST_ERR";
1006 }
1007
1008 if (child && [].slice.call(parent_.childNodes, indexOf(child) + 1)
1009 .filter(isDoctype)) {
1010 return "HIERARCHY_REQUEST_ERR";
1011 }
1012 break;
1013
1014 case Node.DOCUMENT_TYPE_NODE:
1015 // "parent has a doctype child, an element is preceding child, or
1016 // child is null and parent has an element child."
1017 if ([].some.call(parent_.childNodes, isDoctype)) {
1018 return "HIERARCHY_REQUEST_ERR";
1019 }
1020
1021 if (child && [].slice.call(parent_.childNodes, 0, indexOf(child))
1022 .some(isElement)) {
1023 return "HIERARCHY_REQUEST_ERR";
1024 }
1025
1026 if (!child && [].some.call(parent_.childNodes, isElement)) {
1027 return "HIERARCHY_REQUEST_ERR";
1028 }
1029 break;
1030 }
1031 }
1032 }
1033
1034 /**
1035 * Asserts that two nodes are equal, in the sense of isEqualNode(). If they
1036 * aren't, tries to print a relatively informative reason why not. TODO: Move
1037 * this to testharness.js?
1038 */
1039 function assertNodesEqual(actual, expected, msg) {
1040 if (!actual.isEqualNode(expected)) {
1041 msg = "Actual and expected mismatch for " + msg + ". ";
1042
1043 while (actual && expected) {
1044 assert_true(actual.nodeType === expected.nodeType
1045 && actual.nodeName === expected.nodeName
1046 && actual.nodeValue === expected.nodeValue,
1047 "First differing node: expected " + format_value(expected)
1048 + ", got " + format_value(actual) + " [" + msg + "]");
1049 actual = nextNode(actual);
1050 expected = nextNode(expected);
1051 }
1052
1053 assert_unreached("DOMs were not equal but we couldn't figure out why");
1054 }
1055 }
1056
1057 /**
1058 * Given a DOMException, return the name (e.g., "HIERARCHY_REQUEST_ERR").
1059 */
1060 function getDomExceptionName(e) {
1061 var ret = null;
1062 for (var prop in e) {
1063 if (/^[A-Z_]+_ERR$/.test(prop) && e[prop] == e.code) {
1064 return prop;
1065 }
1066 }
1067
1068 throw "Exception seems to not be a DOMException? " + e;
1069 }
1070
1071 /**
1072 * Given an array of endpoint data [start container, start offset, end
1073 * container, end offset], returns a Range with those endpoints.
1074 */
1075 function rangeFromEndpoints(endpoints) {
1076 // If we just use document instead of the ownerDocument of endpoints[0],
1077 // WebKit will throw on setStart/setEnd. This is a WebKit bug, but it's in
1078 // range, not selection, so we don't want to fail anything for it.
1079 var range = ownerDocument(endpoints[0]).createRange();
1080 range.setStart(endpoints[0], endpoints[1]);
1081 range.setEnd(endpoints[2], endpoints[3]);
1082 return range;
1083 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698