diff --git a/dom/common.js b/dom/common.js index 2cc36bf6a079f1..f75123b055f6f7 100644 --- a/dom/common.js +++ b/dom/common.js @@ -6,370 +6,370 @@ // Everything is done in functions in this test harness, so we have to declare // all the variables before use to make sure they can be reused. var testDiv, paras, detachedDiv, detachedPara1, detachedPara2, - foreignDoc, foreignPara1, foreignPara2, xmlDoc, xmlElement, - detachedXmlElement, detachedTextNode, foreignTextNode, - detachedForeignTextNode, xmlTextNode, detachedXmlTextNode, - processingInstruction, detachedProcessingInstruction, comment, - detachedComment, foreignComment, detachedForeignComment, xmlComment, - detachedXmlComment, docfrag, foreignDocfrag, xmlDocfrag, doctype, - foreignDoctype, xmlDoctype; + foreignDoc, foreignPara1, foreignPara2, xmlDoc, xmlElement, + detachedXmlElement, detachedTextNode, foreignTextNode, + detachedForeignTextNode, xmlTextNode, detachedXmlTextNode, + processingInstruction, detachedProcessingInstruction, comment, + detachedComment, foreignComment, detachedForeignComment, xmlComment, + detachedXmlComment, docfrag, foreignDocfrag, xmlDocfrag, doctype, + foreignDoctype, xmlDoctype; var testRangesShort, testRanges, testPoints, testNodesShort, testNodes; function setupRangeTests() { - testDiv = document.querySelector("#test"); - if (testDiv) { - testDiv.parentNode.removeChild(testDiv); - } - testDiv = document.createElement("div"); - testDiv.id = "test"; - document.body.insertBefore(testDiv, document.body.firstChild); - - paras = []; - paras.push(document.createElement("p")); - paras[0].setAttribute("id", "a"); - // Test some diacritics, to make sure browsers are using code units here - // and not something like grapheme clusters. - paras[0].textContent = "A\u0308b\u0308c\u0308d\u0308e\u0308f\u0308g\u0308h\u0308\n"; - testDiv.appendChild(paras[0]); - - paras.push(document.createElement("p")); - paras[1].setAttribute("id", "b"); - paras[1].setAttribute("style", "display:none"); - paras[1].textContent = "Ijklmnop\n"; - testDiv.appendChild(paras[1]); - - paras.push(document.createElement("p")); - paras[2].setAttribute("id", "c"); - paras[2].textContent = "Qrstuvwx"; - testDiv.appendChild(paras[2]); - - paras.push(document.createElement("p")); - paras[3].setAttribute("id", "d"); - paras[3].setAttribute("style", "display:none"); - paras[3].textContent = "Yzabcdef"; - testDiv.appendChild(paras[3]); - - paras.push(document.createElement("p")); - paras[4].setAttribute("id", "e"); - paras[4].setAttribute("style", "display:none"); - paras[4].textContent = "Ghijklmn"; - testDiv.appendChild(paras[4]); - - detachedDiv = document.createElement("div"); - detachedPara1 = document.createElement("p"); - detachedPara1.appendChild(document.createTextNode("Opqrstuv")); - detachedPara2 = document.createElement("p"); - detachedPara2.appendChild(document.createTextNode("Wxyzabcd")); - detachedDiv.appendChild(detachedPara1); - detachedDiv.appendChild(detachedPara2); - - // Opera doesn't automatically create a doctype for a new HTML document, - // contrary to spec. It also doesn't let you add doctypes to documents - // after the fact through any means I've tried. So foreignDoc in Opera - // will have no doctype, foreignDoctype will be null, and Opera will fail - // some tests somewhat mysteriously as a result. - foreignDoc = document.implementation.createHTMLDocument(""); - foreignPara1 = foreignDoc.createElement("p"); - foreignPara1.appendChild(foreignDoc.createTextNode("Efghijkl")); - foreignPara2 = foreignDoc.createElement("p"); - foreignPara2.appendChild(foreignDoc.createTextNode("Mnopqrst")); - foreignDoc.body.appendChild(foreignPara1); - foreignDoc.body.appendChild(foreignPara2); - - // Now we get to do really silly stuff, which nobody in the universe is - // ever going to actually do, but the spec defines behavior, so too bad. - // Testing is fun! - xmlDoctype = document.implementation.createDocumentType("qorflesnorf", "abcde", "x\"'y"); - xmlDoc = document.implementation.createDocument(null, null, xmlDoctype); - detachedXmlElement = xmlDoc.createElement("everyone-hates-hyphenated-element-names"); - detachedTextNode = document.createTextNode("Uvwxyzab"); - detachedForeignTextNode = foreignDoc.createTextNode("Cdefghij"); - detachedXmlTextNode = xmlDoc.createTextNode("Klmnopqr"); - // PIs only exist in XML documents, so don't bother with document or - // foreignDoc. - detachedProcessingInstruction = xmlDoc.createProcessingInstruction("whippoorwill", "chirp chirp chirp"); - detachedComment = document.createComment("Stuvwxyz"); - // Hurrah, we finally got to "z" at the end! - detachedForeignComment = foreignDoc.createComment("אריה יהודה"); - detachedXmlComment = xmlDoc.createComment("בן חיים אליעזר"); - - // We should also test with document fragments that actually contain stuff - // . . . but, maybe later. - docfrag = document.createDocumentFragment(); - foreignDocfrag = foreignDoc.createDocumentFragment(); - xmlDocfrag = xmlDoc.createDocumentFragment(); - - xmlElement = xmlDoc.createElement("igiveuponcreativenames"); - xmlTextNode = xmlDoc.createTextNode("do re mi fa so la ti"); - xmlElement.appendChild(xmlTextNode); - processingInstruction = xmlDoc.createProcessingInstruction("somePI", 'Did you know that ":syn sync fromstart" is very useful when using vim to edit large amounts of JavaScript embedded in HTML?'); - xmlDoc.appendChild(xmlElement); - xmlDoc.appendChild(processingInstruction); - 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"); - xmlDoc.appendChild(xmlComment); - - comment = document.createComment("Alphabet soup?"); - testDiv.appendChild(comment); - - foreignComment = foreignDoc.createComment('"Commenter" and "commentator" mean different things. I\'ve seen non-native speakers trip up on this.'); - foreignDoc.appendChild(foreignComment); - foreignTextNode = foreignDoc.createTextNode("I admit that I harbor doubts about whether we really need so many things to test, but it's too late to stop now."); - foreignDoc.body.appendChild(foreignTextNode); - - doctype = document.doctype; - foreignDoctype = foreignDoc.doctype; - - testRangesShort = [ - // Various ranges within the text node children of different - // paragraphs. All should be valid. - "[paras[0].firstChild, 0, paras[0].firstChild, 0]", - "[paras[0].firstChild, 0, paras[0].firstChild, 1]", - "[paras[0].firstChild, 2, paras[0].firstChild, 8]", - "[paras[0].firstChild, 2, paras[0].firstChild, 9]", - "[paras[1].firstChild, 0, paras[1].firstChild, 0]", - "[paras[1].firstChild, 2, paras[1].firstChild, 9]", - "[detachedPara1.firstChild, 0, detachedPara1.firstChild, 0]", - "[detachedPara1.firstChild, 2, detachedPara1.firstChild, 8]", - "[foreignPara1.firstChild, 0, foreignPara1.firstChild, 0]", - "[foreignPara1.firstChild, 2, foreignPara1.firstChild, 8]", - // Now try testing some elements, not just text nodes. - "[document.documentElement, 0, document.documentElement, 1]", - "[document.documentElement, 0, document.documentElement, 2]", - "[document.documentElement, 1, document.documentElement, 2]", - "[document.head, 1, document.head, 1]", - "[document.body, 4, document.body, 5]", - "[foreignDoc.documentElement, 0, foreignDoc.documentElement, 1]", - "[paras[0], 0, paras[0], 1]", - "[detachedPara1, 0, detachedPara1, 1]", - // Now try some ranges that span elements. - "[paras[0].firstChild, 0, paras[1].firstChild, 0]", - "[paras[0].firstChild, 0, paras[1].firstChild, 8]", - "[paras[0].firstChild, 3, paras[3], 1]", - // How about something that spans a node and its descendant? - "[paras[0], 0, paras[0].firstChild, 7]", - "[testDiv, 2, paras[4], 1]", - // Then a few more interesting things just for good measure. - "[document, 0, document, 1]", - "[document, 0, document, 2]", - "[comment, 2, comment, 3]", - "[testDiv, 0, comment, 5]", - "[foreignDoc, 1, foreignComment, 2]", - "[foreignDoc.body, 0, foreignTextNode, 36]", - "[xmlDoc, 1, xmlComment, 0]", - "[detachedTextNode, 0, detachedTextNode, 8]", - "[detachedForeignTextNode, 0, detachedForeignTextNode, 8]", - "[detachedXmlTextNode, 0, detachedXmlTextNode, 8]", - "[detachedComment, 3, detachedComment, 4]", - "[detachedForeignComment, 0, detachedForeignComment, 1]", - "[detachedXmlComment, 2, detachedXmlComment, 6]", - "[docfrag, 0, docfrag, 0]", - ]; - - testRanges = testRangesShort.concat([ - "[paras[1].firstChild, 0, paras[1].firstChild, 1]", - "[paras[1].firstChild, 2, paras[1].firstChild, 8]", - "[detachedPara1.firstChild, 0, detachedPara1.firstChild, 1]", - "[foreignPara1.firstChild, 0, foreignPara1.firstChild, 1]", - "[foreignDoc.head, 1, foreignDoc.head, 1]", - "[foreignDoc.body, 0, foreignDoc.body, 0]", - "[paras[0], 0, paras[0], 0]", - "[detachedPara1, 0, detachedPara1, 0]", - "[testDiv, 1, paras[2].firstChild, 5]", - "[document.documentElement, 1, document.body, 0]", - "[foreignDoc.documentElement, 1, foreignDoc.body, 0]", - "[document, 1, document, 2]", - "[paras[2].firstChild, 4, comment, 2]", - "[paras[3], 1, comment, 8]", - "[foreignDoc, 0, foreignDoc, 0]", - "[xmlDoc, 0, xmlDoc, 0]", - "[detachedForeignTextNode, 7, detachedForeignTextNode, 7]", - "[detachedXmlTextNode, 7, detachedXmlTextNode, 7]", - "[detachedComment, 5, detachedComment, 5]", - "[detachedForeignComment, 4, detachedForeignComment, 4]", - "[foreignDocfrag, 0, foreignDocfrag, 0]", - "[xmlDocfrag, 0, xmlDocfrag, 0]", - ]); - - testPoints = [ - // Various positions within the page, some invalid. Remember that - // paras[0] is visible, and paras[1] is display: none. - "[paras[0].firstChild, -1]", - "[paras[0].firstChild, 0]", - "[paras[0].firstChild, 1]", - "[paras[0].firstChild, 2]", - "[paras[0].firstChild, 8]", - "[paras[0].firstChild, 9]", - "[paras[0].firstChild, 10]", - "[paras[0].firstChild, 65535]", - "[paras[1].firstChild, -1]", - "[paras[1].firstChild, 0]", - "[paras[1].firstChild, 1]", - "[paras[1].firstChild, 2]", - "[paras[1].firstChild, 8]", - "[paras[1].firstChild, 9]", - "[paras[1].firstChild, 10]", - "[paras[1].firstChild, 65535]", - "[detachedPara1.firstChild, 0]", - "[detachedPara1.firstChild, 1]", - "[detachedPara1.firstChild, 8]", - "[detachedPara1.firstChild, 9]", - "[foreignPara1.firstChild, 0]", - "[foreignPara1.firstChild, 1]", - "[foreignPara1.firstChild, 8]", - "[foreignPara1.firstChild, 9]", - // Now try testing some elements, not just text nodes. - "[document.documentElement, -1]", - "[document.documentElement, 0]", - "[document.documentElement, 1]", - "[document.documentElement, 2]", - "[document.documentElement, 7]", - "[document.head, 1]", - "[document.body, 3]", - "[foreignDoc.documentElement, 0]", - "[foreignDoc.documentElement, 1]", - "[foreignDoc.head, 0]", - "[foreignDoc.body, 1]", - "[paras[0], 0]", - "[paras[0], 1]", - "[paras[0], 2]", - "[paras[1], 0]", - "[paras[1], 1]", - "[paras[1], 2]", - "[detachedPara1, 0]", - "[detachedPara1, 1]", - "[testDiv, 0]", - "[testDiv, 3]", - // Then a few more interesting things just for good measure. - "[document, -1]", - "[document, 0]", - "[document, 1]", - "[document, 2]", - "[document, 3]", - "[comment, -1]", - "[comment, 0]", - "[comment, 4]", - "[comment, 96]", - "[foreignDoc, 0]", - "[foreignDoc, 1]", - "[foreignComment, 2]", - "[foreignTextNode, 0]", - "[foreignTextNode, 36]", - "[xmlDoc, -1]", - "[xmlDoc, 0]", - "[xmlDoc, 1]", - "[xmlDoc, 5]", - "[xmlComment, 0]", - "[xmlComment, 4]", - "[processingInstruction, 0]", - "[processingInstruction, 5]", - "[processingInstruction, 9]", - "[detachedTextNode, 0]", - "[detachedTextNode, 8]", - "[detachedForeignTextNode, 0]", - "[detachedForeignTextNode, 8]", - "[detachedXmlTextNode, 0]", - "[detachedXmlTextNode, 8]", - "[detachedProcessingInstruction, 12]", - "[detachedComment, 3]", - "[detachedComment, 5]", - "[detachedForeignComment, 0]", - "[detachedForeignComment, 4]", - "[detachedXmlComment, 2]", - "[docfrag, 0]", - "[foreignDocfrag, 0]", - "[xmlDocfrag, 0]", - "[doctype, 0]", - "[doctype, -17]", - "[doctype, 1]", - "[foreignDoctype, 0]", - "[xmlDoctype, 0]", - ]; - - testNodesShort = [ - "paras[0]", - "paras[0].firstChild", - "paras[1].firstChild", - "foreignPara1", - "foreignPara1.firstChild", - "detachedPara1", - "detachedPara1.firstChild", - "document", - "detachedDiv", - "foreignDoc", - "foreignPara2", - "xmlDoc", - "xmlElement", - "detachedTextNode", - "foreignTextNode", - "processingInstruction", - "detachedProcessingInstruction", - "comment", - "detachedComment", - "docfrag", - "doctype", - "foreignDoctype", - ]; - - testNodes = testNodesShort.concat([ - "paras[1]", - "detachedPara2", - "detachedPara2.firstChild", - "testDiv", - "detachedXmlElement", - "detachedForeignTextNode", - "xmlTextNode", - "detachedXmlTextNode", - "xmlComment", - "foreignComment", - "detachedForeignComment", - "detachedXmlComment", - "foreignDocfrag", - "xmlDocfrag", - "xmlDoctype", - ]); + testDiv = document.querySelector("#test"); + if (testDiv) { + testDiv.parentNode.removeChild(testDiv); + } + testDiv = document.createElement("div"); + testDiv.id = "test"; + document.body.insertBefore(testDiv, document.body.firstChild); + + paras = []; + paras.push(document.createElement("p")); + paras[0].setAttribute("id", "a"); + // Test some diacritics, to make sure browsers are using code units here + // and not something like grapheme clusters. + paras[0].textContent = "A\u0308b\u0308c\u0308d\u0308e\u0308f\u0308g\u0308h\u0308\n"; + testDiv.appendChild(paras[0]); + + paras.push(document.createElement("p")); + paras[1].setAttribute("id", "b"); + paras[1].setAttribute("style", "display:none"); + paras[1].textContent = "Ijklmnop\n"; + testDiv.appendChild(paras[1]); + + paras.push(document.createElement("p")); + paras[2].setAttribute("id", "c"); + paras[2].textContent = "Qrstuvwx"; + testDiv.appendChild(paras[2]); + + paras.push(document.createElement("p")); + paras[3].setAttribute("id", "d"); + paras[3].setAttribute("style", "display:none"); + paras[3].textContent = "Yzabcdef"; + testDiv.appendChild(paras[3]); + + paras.push(document.createElement("p")); + paras[4].setAttribute("id", "e"); + paras[4].setAttribute("style", "display:none"); + paras[4].textContent = "Ghijklmn"; + testDiv.appendChild(paras[4]); + + detachedDiv = document.createElement("div"); + detachedPara1 = document.createElement("p"); + detachedPara1.appendChild(document.createTextNode("Opqrstuv")); + detachedPara2 = document.createElement("p"); + detachedPara2.appendChild(document.createTextNode("Wxyzabcd")); + detachedDiv.appendChild(detachedPara1); + detachedDiv.appendChild(detachedPara2); + + // Opera doesn't automatically create a doctype for a new HTML document, + // contrary to spec. It also doesn't let you add doctypes to documents + // after the fact through any means I've tried. So foreignDoc in Opera + // will have no doctype, foreignDoctype will be null, and Opera will fail + // some tests somewhat mysteriously as a result. + foreignDoc = document.implementation.createHTMLDocument(""); + foreignPara1 = foreignDoc.createElement("p"); + foreignPara1.appendChild(foreignDoc.createTextNode("Efghijkl")); + foreignPara2 = foreignDoc.createElement("p"); + foreignPara2.appendChild(foreignDoc.createTextNode("Mnopqrst")); + foreignDoc.body.appendChild(foreignPara1); + foreignDoc.body.appendChild(foreignPara2); + + // Now we get to do really silly stuff, which nobody in the universe is + // ever going to actually do, but the spec defines behavior, so too bad. + // Testing is fun! + xmlDoctype = document.implementation.createDocumentType("qorflesnorf", "abcde", "x\"'y"); + xmlDoc = document.implementation.createDocument(null, null, xmlDoctype); + detachedXmlElement = xmlDoc.createElement("everyone-hates-hyphenated-element-names"); + detachedTextNode = document.createTextNode("Uvwxyzab"); + detachedForeignTextNode = foreignDoc.createTextNode("Cdefghij"); + detachedXmlTextNode = xmlDoc.createTextNode("Klmnopqr"); + // PIs only exist in XML documents, so don't bother with document or + // foreignDoc. + detachedProcessingInstruction = xmlDoc.createProcessingInstruction("whippoorwill", "chirp chirp chirp"); + detachedComment = document.createComment("Stuvwxyz"); + // Hurrah, we finally got to "z" at the end! + detachedForeignComment = foreignDoc.createComment("אריה יהודה"); + detachedXmlComment = xmlDoc.createComment("בן חיים אליעזר"); + + // We should also test with document fragments that actually contain stuff + // . . . but, maybe later. + docfrag = document.createDocumentFragment(); + foreignDocfrag = foreignDoc.createDocumentFragment(); + xmlDocfrag = xmlDoc.createDocumentFragment(); + + xmlElement = xmlDoc.createElement("igiveuponcreativenames"); + xmlTextNode = xmlDoc.createTextNode("do re mi fa so la ti"); + xmlElement.appendChild(xmlTextNode); + processingInstruction = xmlDoc.createProcessingInstruction("somePI", 'Did you know that ":syn sync fromstart" is very useful when using vim to edit large amounts of JavaScript embedded in HTML?'); + xmlDoc.appendChild(xmlElement); + xmlDoc.appendChild(processingInstruction); + 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"); + xmlDoc.appendChild(xmlComment); + + comment = document.createComment("Alphabet soup?"); + testDiv.appendChild(comment); + + foreignComment = foreignDoc.createComment('"Commenter" and "commentator" mean different things. I\'ve seen non-native speakers trip up on this.'); + foreignDoc.appendChild(foreignComment); + foreignTextNode = foreignDoc.createTextNode("I admit that I harbor doubts about whether we really need so many things to test, but it's too late to stop now."); + foreignDoc.body.appendChild(foreignTextNode); + + doctype = document.doctype; + foreignDoctype = foreignDoc.doctype; + + testRangesShort = [ + // Various ranges within the text node children of different + // paragraphs. All should be valid. + "[paras[0].firstChild, 0, paras[0].firstChild, 0]", + "[paras[0].firstChild, 0, paras[0].firstChild, 1]", + "[paras[0].firstChild, 2, paras[0].firstChild, 8]", + "[paras[0].firstChild, 2, paras[0].firstChild, 9]", + "[paras[1].firstChild, 0, paras[1].firstChild, 0]", + "[paras[1].firstChild, 2, paras[1].firstChild, 9]", + "[detachedPara1.firstChild, 0, detachedPara1.firstChild, 0]", + "[detachedPara1.firstChild, 2, detachedPara1.firstChild, 8]", + "[foreignPara1.firstChild, 0, foreignPara1.firstChild, 0]", + "[foreignPara1.firstChild, 2, foreignPara1.firstChild, 8]", + // Now try testing some elements, not just text nodes. + "[document.documentElement, 0, document.documentElement, 1]", + "[document.documentElement, 0, document.documentElement, 2]", + "[document.documentElement, 1, document.documentElement, 2]", + "[document.head, 1, document.head, 1]", + "[document.body, 4, document.body, 5]", + "[foreignDoc.documentElement, 0, foreignDoc.documentElement, 1]", + "[paras[0], 0, paras[0], 1]", + "[detachedPara1, 0, detachedPara1, 1]", + // Now try some ranges that span elements. + "[paras[0].firstChild, 0, paras[1].firstChild, 0]", + "[paras[0].firstChild, 0, paras[1].firstChild, 8]", + "[paras[0].firstChild, 3, paras[3], 1]", + // How about something that spans a node and its descendant? + "[paras[0], 0, paras[0].firstChild, 7]", + "[testDiv, 2, paras[4], 1]", + // Then a few more interesting things just for good measure. + "[document, 0, document, 1]", + "[document, 0, document, 2]", + "[comment, 2, comment, 3]", + "[testDiv, 0, comment, 5]", + "[foreignDoc, 1, foreignComment, 2]", + "[foreignDoc.body, 0, foreignTextNode, 36]", + "[xmlDoc, 1, xmlComment, 0]", + "[detachedTextNode, 0, detachedTextNode, 8]", + "[detachedForeignTextNode, 0, detachedForeignTextNode, 8]", + "[detachedXmlTextNode, 0, detachedXmlTextNode, 8]", + "[detachedComment, 3, detachedComment, 4]", + "[detachedForeignComment, 0, detachedForeignComment, 1]", + "[detachedXmlComment, 2, detachedXmlComment, 6]", + "[docfrag, 0, docfrag, 0]", + ]; + + testRanges = testRangesShort.concat([ + "[paras[1].firstChild, 0, paras[1].firstChild, 1]", + "[paras[1].firstChild, 2, paras[1].firstChild, 8]", + "[detachedPara1.firstChild, 0, detachedPara1.firstChild, 1]", + "[foreignPara1.firstChild, 0, foreignPara1.firstChild, 1]", + "[foreignDoc.head, 1, foreignDoc.head, 1]", + "[foreignDoc.body, 0, foreignDoc.body, 0]", + "[paras[0], 0, paras[0], 0]", + "[detachedPara1, 0, detachedPara1, 0]", + "[testDiv, 1, paras[2].firstChild, 5]", + "[document.documentElement, 1, document.body, 0]", + "[foreignDoc.documentElement, 1, foreignDoc.body, 0]", + "[document, 1, document, 2]", + "[paras[2].firstChild, 4, comment, 2]", + "[paras[3], 1, comment, 8]", + "[foreignDoc, 0, foreignDoc, 0]", + "[xmlDoc, 0, xmlDoc, 0]", + "[detachedForeignTextNode, 7, detachedForeignTextNode, 7]", + "[detachedXmlTextNode, 7, detachedXmlTextNode, 7]", + "[detachedComment, 5, detachedComment, 5]", + "[detachedForeignComment, 4, detachedForeignComment, 4]", + "[foreignDocfrag, 0, foreignDocfrag, 0]", + "[xmlDocfrag, 0, xmlDocfrag, 0]", + ]); + + testPoints = [ + // Various positions within the page, some invalid. Remember that + // paras[0] is visible, and paras[1] is display: none. + "[paras[0].firstChild, -1]", + "[paras[0].firstChild, 0]", + "[paras[0].firstChild, 1]", + "[paras[0].firstChild, 2]", + "[paras[0].firstChild, 8]", + "[paras[0].firstChild, 9]", + "[paras[0].firstChild, 10]", + "[paras[0].firstChild, 65535]", + "[paras[1].firstChild, -1]", + "[paras[1].firstChild, 0]", + "[paras[1].firstChild, 1]", + "[paras[1].firstChild, 2]", + "[paras[1].firstChild, 8]", + "[paras[1].firstChild, 9]", + "[paras[1].firstChild, 10]", + "[paras[1].firstChild, 65535]", + "[detachedPara1.firstChild, 0]", + "[detachedPara1.firstChild, 1]", + "[detachedPara1.firstChild, 8]", + "[detachedPara1.firstChild, 9]", + "[foreignPara1.firstChild, 0]", + "[foreignPara1.firstChild, 1]", + "[foreignPara1.firstChild, 8]", + "[foreignPara1.firstChild, 9]", + // Now try testing some elements, not just text nodes. + "[document.documentElement, -1]", + "[document.documentElement, 0]", + "[document.documentElement, 1]", + "[document.documentElement, 2]", + "[document.documentElement, 7]", + "[document.head, 1]", + "[document.body, 3]", + "[foreignDoc.documentElement, 0]", + "[foreignDoc.documentElement, 1]", + "[foreignDoc.head, 0]", + "[foreignDoc.body, 1]", + "[paras[0], 0]", + "[paras[0], 1]", + "[paras[0], 2]", + "[paras[1], 0]", + "[paras[1], 1]", + "[paras[1], 2]", + "[detachedPara1, 0]", + "[detachedPara1, 1]", + "[testDiv, 0]", + "[testDiv, 3]", + // Then a few more interesting things just for good measure. + "[document, -1]", + "[document, 0]", + "[document, 1]", + "[document, 2]", + "[document, 3]", + "[comment, -1]", + "[comment, 0]", + "[comment, 4]", + "[comment, 96]", + "[foreignDoc, 0]", + "[foreignDoc, 1]", + "[foreignComment, 2]", + "[foreignTextNode, 0]", + "[foreignTextNode, 36]", + "[xmlDoc, -1]", + "[xmlDoc, 0]", + "[xmlDoc, 1]", + "[xmlDoc, 5]", + "[xmlComment, 0]", + "[xmlComment, 4]", + "[processingInstruction, 0]", + "[processingInstruction, 5]", + "[processingInstruction, 9]", + "[detachedTextNode, 0]", + "[detachedTextNode, 8]", + "[detachedForeignTextNode, 0]", + "[detachedForeignTextNode, 8]", + "[detachedXmlTextNode, 0]", + "[detachedXmlTextNode, 8]", + "[detachedProcessingInstruction, 12]", + "[detachedComment, 3]", + "[detachedComment, 5]", + "[detachedForeignComment, 0]", + "[detachedForeignComment, 4]", + "[detachedXmlComment, 2]", + "[docfrag, 0]", + "[foreignDocfrag, 0]", + "[xmlDocfrag, 0]", + "[doctype, 0]", + "[doctype, -17]", + "[doctype, 1]", + "[foreignDoctype, 0]", + "[xmlDoctype, 0]", + ]; + + testNodesShort = [ + "paras[0]", + "paras[0].firstChild", + "paras[1].firstChild", + "foreignPara1", + "foreignPara1.firstChild", + "detachedPara1", + "detachedPara1.firstChild", + "document", + "detachedDiv", + "foreignDoc", + "foreignPara2", + "xmlDoc", + "xmlElement", + "detachedTextNode", + "foreignTextNode", + "processingInstruction", + "detachedProcessingInstruction", + "comment", + "detachedComment", + "docfrag", + "doctype", + "foreignDoctype", + ]; + + testNodes = testNodesShort.concat([ + "paras[1]", + "detachedPara2", + "detachedPara2.firstChild", + "testDiv", + "detachedXmlElement", + "detachedForeignTextNode", + "xmlTextNode", + "detachedXmlTextNode", + "xmlComment", + "foreignComment", + "detachedForeignComment", + "detachedXmlComment", + "foreignDocfrag", + "xmlDocfrag", + "xmlDoctype", + ]); } if ("setup" in window) { - setup(setupRangeTests); + setup(setupRangeTests); } else { - // Presumably we're running from within an iframe or something - setupRangeTests(); + // Presumably we're running from within an iframe or something + setupRangeTests(); } /** * The "length" of a node as defined by the Ranges section of DOM4. */ function nodeLength(node) { - // "The length of a node node depends on node: - // - // "DocumentType - // "0." - if (node.nodeType == Node.DOCUMENT_TYPE_NODE) { - return 0; - } - // "Text - // "ProcessingInstruction - // "Comment - // "Its length attribute value." - // Browsers don't historically support the length attribute on - // ProcessingInstruction, so to avoid spurious failures, do - // node.data.length instead of node.length. - if (node.nodeType == Node.TEXT_NODE || node.nodeType == Node.PROCESSING_INSTRUCTION_NODE || node.nodeType == Node.COMMENT_NODE) { - return node.data.length; - } - // "Any other node - // "Its number of children." - return node.childNodes.length; + // "The length of a node node depends on node: + // + // "DocumentType + // "0." + if (node.nodeType == Node.DOCUMENT_TYPE_NODE) { + return 0; + } + // "Text + // "ProcessingInstruction + // "Comment + // "Its length attribute value." + // Browsers don't historically support the length attribute on + // ProcessingInstruction, so to avoid spurious failures, do + // node.data.length instead of node.length. + if (node.nodeType == Node.TEXT_NODE || node.nodeType == Node.PROCESSING_INSTRUCTION_NODE || node.nodeType == Node.COMMENT_NODE) { + return node.data.length; + } + // "Any other node + // "Its number of children." + return node.childNodes.length; } /** * Returns the furthest ancestor of a Node as defined by the spec. */ function furthestAncestor(node) { - var root = node; - while (root.parentNode != null) { - root = root.parentNode; - } - return root; + var root = node; + while (root.parentNode != null) { + root = root.parentNode; + } + return root; } /** @@ -379,8 +379,8 @@ function furthestAncestor(node) { * Is node1 an ancestor container of node2? */ function isAncestorContainer(node1, node2) { - return node1 == node2 || - (node2.compareDocumentPosition(node1) & Node.DOCUMENT_POSITION_CONTAINS); + return node1 == node2 || + (node2.compareDocumentPosition(node1) & Node.DOCUMENT_POSITION_CONTAINS); } /** @@ -388,10 +388,10 @@ function isAncestorContainer(node1, node2) { * the last Node. */ function nextNode(node) { - if (node.hasChildNodes()) { - return node.firstChild; - } - return nextNodeDescendants(node); + if (node.hasChildNodes()) { + return node.firstChild; + } + return nextNodeDescendants(node); } /** @@ -399,14 +399,14 @@ function nextNode(node) { * the first Node. */ function previousNode(node) { - if (node.previousSibling) { - node = node.previousSibling; - while (node.hasChildNodes()) { - node = node.lastChild; - } - return node; - } - return node.parentNode; + if (node.previousSibling) { + node = node.previousSibling; + while (node.hasChildNodes()) { + node = node.lastChild; + } + return node; + } + return node.parentNode; } /** @@ -414,13 +414,13 @@ function previousNode(node) { * order, or null if node is the last Node or an ancestor of it. */ function nextNodeDescendants(node) { - while (node && !node.nextSibling) { - node = node.parentNode; - } - if (!node) { - return null; - } - return node.nextSibling; + while (node && !node.nextSibling) { + node = node.parentNode; + } + if (!node) { + return null; + } + return node.nextSibling; } /** @@ -428,22 +428,22 @@ function nextNodeDescendants(node) { * Document. */ function ownerDocument(node) { - return node.nodeType == Node.DOCUMENT_NODE - ? node - : node.ownerDocument; + return node.nodeType == Node.DOCUMENT_NODE + ? node + : node.ownerDocument; } /** * Returns true if ancestor is an ancestor of descendant, false otherwise. */ function isAncestor(ancestor, descendant) { - if (!ancestor || !descendant) { - return false; - } - while (descendant && descendant != ancestor) { - descendant = descendant.parentNode; - } - return descendant == ancestor; + if (!ancestor || !descendant) { + return false; + } + while (descendant && descendant != ancestor) { + descendant = descendant.parentNode; + } + return descendant == ancestor; } /** @@ -451,14 +451,14 @@ function isAncestor(ancestor, descendant) { * otherwise. */ function isInclusiveAncestor(ancestor, descendant) { - return ancestor === descendant || isAncestor(ancestor, descendant); + return ancestor === descendant || isAncestor(ancestor, descendant); } /** * Returns true if descendant is a descendant of ancestor, false otherwise. */ function isDescendant(descendant, ancestor) { - return isAncestor(ancestor, descendant); + return isAncestor(ancestor, descendant); } /** @@ -466,7 +466,7 @@ function isDescendant(descendant, ancestor) { * otherwise. */ function isInclusiveDescendant(descendant, ancestor) { - return descendant === ancestor || isDescendant(descendant, ancestor); + return descendant === ancestor || isDescendant(descendant, ancestor); } /** @@ -474,52 +474,52 @@ function isInclusiveDescendant(descendant, ancestor) { * the spec. */ function getPosition(nodeA, offsetA, nodeB, offsetB) { - // "If node A is the same as node B, return equal if offset A equals offset - // B, before if offset A is less than offset B, and after if offset A is - // greater than offset B." - if (nodeA == nodeB) { - if (offsetA == offsetB) { - return "equal"; - } - if (offsetA < offsetB) { - return "before"; - } - if (offsetA > offsetB) { - return "after"; - } - } - - // "If node A is after node B in tree order, compute the position of (node - // B, offset B) relative to (node A, offset A). If it is before, return - // after. If it is after, return before." - if (nodeB.compareDocumentPosition(nodeA) & Node.DOCUMENT_POSITION_FOLLOWING) { - var pos = getPosition(nodeB, offsetB, nodeA, offsetA); - if (pos == "before") { - return "after"; - } - if (pos == "after") { - return "before"; - } - } - - // "If node A is an ancestor of node B:" - if (nodeB.compareDocumentPosition(nodeA) & Node.DOCUMENT_POSITION_CONTAINS) { - // "Let child equal node B." - var child = nodeB; - - // "While child is not a child of node A, set child to its parent." - while (child.parentNode != nodeA) { - child = child.parentNode; - } - - // "If the index of child is less than offset A, return after." - if (indexOf(child) < offsetA) { - return "after"; - } - } - - // "Return before." - return "before"; + // "If node A is the same as node B, return equal if offset A equals offset + // B, before if offset A is less than offset B, and after if offset A is + // greater than offset B." + if (nodeA == nodeB) { + if (offsetA == offsetB) { + return "equal"; + } + if (offsetA < offsetB) { + return "before"; + } + if (offsetA > offsetB) { + return "after"; + } + } + + // "If node A is after node B in tree order, compute the position of (node + // B, offset B) relative to (node A, offset A). If it is before, return + // after. If it is after, return before." + if (nodeB.compareDocumentPosition(nodeA) & Node.DOCUMENT_POSITION_FOLLOWING) { + var pos = getPosition(nodeB, offsetB, nodeA, offsetA); + if (pos == "before") { + return "after"; + } + if (pos == "after") { + return "before"; + } + } + + // "If node A is an ancestor of node B:" + if (nodeB.compareDocumentPosition(nodeA) & Node.DOCUMENT_POSITION_CONTAINS) { + // "Let child equal node B." + var child = nodeB; + + // "While child is not a child of node A, set child to its parent." + while (child.parentNode != nodeA) { + child = child.parentNode; + } + + // "If the index of child is less than offset A, return after." + if (indexOf(child) < offsetA) { + return "after"; + } + } + + // "Return before." + return "before"; } /** @@ -528,12 +528,12 @@ function getPosition(nodeA, offsetA, nodeB, offsetB) { * is after range's start, and (node, length of node) is before range's end." */ function isContained(node, range) { - var pos1 = getPosition(node, 0, range.startContainer, range.startOffset); - var pos2 = getPosition(node, nodeLength(node), range.endContainer, range.endOffset); + var pos1 = getPosition(node, 0, range.startContainer, range.startOffset); + var pos2 = getPosition(node, nodeLength(node), range.endContainer, range.endOffset); - return furthestAncestor(node) == furthestAncestor(range.startContainer) - && pos1 == "after" - && pos2 == "before"; + return furthestAncestor(node) == furthestAncestor(range.startContainer) + && pos1 == "after" + && pos2 == "before"; } /** @@ -542,24 +542,24 @@ function isContained(node, range) { * not its end, or vice versa." */ function isPartiallyContained(node, range) { - var cond1 = isAncestorContainer(node, range.startContainer); - var cond2 = isAncestorContainer(node, range.endContainer); - return (cond1 && !cond2) || (cond2 && !cond1); + var cond1 = isAncestorContainer(node, range.startContainer); + var cond2 = isAncestorContainer(node, range.endContainer); + return (cond1 && !cond2) || (cond2 && !cond1); } /** * Index of a node as defined by the spec. */ function indexOf(node) { - if (!node.parentNode) { - // No preceding sibling nodes, right? - return 0; - } - var i = 0; - while (node != node.parentNode.childNodes[i]) { - i++; - } - return i; + if (!node.parentNode) { + // No preceding sibling nodes, right? + return 0; + } + var i = 0; + while (node != node.parentNode.childNodes[i]) { + i++; + } + return i; } /** @@ -570,258 +570,258 @@ function indexOf(node) { * a spec bug. */ function myExtractContents(range) { - // "If the context object's detached flag is set, raise an - // INVALID_STATE_ERR exception and abort these steps." - try { - range.collapsed; - } catch (e) { - return "INVALID_STATE_ERR"; - } - - // "Let frag be a new DocumentFragment whose ownerDocument is the same as - // the ownerDocument of the context object's start node." - var ownerDoc = range.startContainer.nodeType == Node.DOCUMENT_NODE - ? range.startContainer - : range.startContainer.ownerDocument; - var frag = ownerDoc.createDocumentFragment(); - - // "If the context object's start and end are the same, abort this method, - // returning frag." - if (range.startContainer == range.endContainer - && range.startOffset == range.endOffset) { - return frag; - } - - // "Let original start node, original start offset, original end node, and - // original end offset be the context object's start and end nodes and - // offsets, respectively." - var originalStartNode = range.startContainer; - var originalStartOffset = range.startOffset; - var originalEndNode = range.endContainer; - var originalEndOffset = range.endOffset; - - // "If original start node and original end node are the same, and they are - // a Text or Comment node:" - if (range.startContainer == range.endContainer - && (range.startContainer.nodeType == Node.TEXT_NODE - || range.startContainer.nodeType == Node.COMMENT_NODE)) { - // "Let clone be the result of calling cloneNode(false) on original - // start node." - var clone = originalStartNode.cloneNode(false); - - // "Set the data of clone to the result of calling - // substringData(original start offset, original end offset − original - // start offset) on original start node." - clone.data = originalStartNode.substringData(originalStartOffset, - originalEndOffset - originalStartOffset); - - // "Append clone as the last child of frag." - frag.appendChild(clone); - - // "Call deleteData(original start offset, original end offset − - // original start offset) on original start node." - originalStartNode.deleteData(originalStartOffset, - originalEndOffset - originalStartOffset); - - // "Abort this method, returning frag." - return frag; - } - - // "Let common ancestor equal original start node." - var commonAncestor = originalStartNode; - - // "While common ancestor is not an ancestor container of original end - // node, set common ancestor to its own parent." - while (!isAncestorContainer(commonAncestor, originalEndNode)) { - commonAncestor = commonAncestor.parentNode; - } - - // "If original start node is an ancestor container of original end node, - // let first partially contained child be null." - var firstPartiallyContainedChild; - if (isAncestorContainer(originalStartNode, originalEndNode)) { - firstPartiallyContainedChild = null; - // "Otherwise, let first partially contained child be the first child of - // common ancestor that is partially contained in the context object." - } else { - for (var i = 0; i < commonAncestor.childNodes.length; i++) { - if (isPartiallyContained(commonAncestor.childNodes[i], range)) { - firstPartiallyContainedChild = commonAncestor.childNodes[i]; - break; - } - } - if (!firstPartiallyContainedChild) { - throw "Spec bug: no first partially contained child!"; - } - } - - // "If original end node is an ancestor container of original start node, - // let last partially contained child be null." - var lastPartiallyContainedChild; - if (isAncestorContainer(originalEndNode, originalStartNode)) { - lastPartiallyContainedChild = null; - // "Otherwise, let last partially contained child be the last child of - // common ancestor that is partially contained in the context object." - } else { - for (var i = commonAncestor.childNodes.length - 1; i >= 0; i--) { - if (isPartiallyContained(commonAncestor.childNodes[i], range)) { - lastPartiallyContainedChild = commonAncestor.childNodes[i]; - break; - } - } - if (!lastPartiallyContainedChild) { - throw "Spec bug: no last partially contained child!"; - } - } - - // "Let contained children be a list of all children of common ancestor - // that are contained in the context object, in tree order." - // - // "If any member of contained children is a DocumentType, raise a - // HIERARCHY_REQUEST_ERR exception and abort these steps." - var containedChildren = []; - for (var i = 0; i < commonAncestor.childNodes.length; i++) { - if (isContained(commonAncestor.childNodes[i], range)) { - if (commonAncestor.childNodes[i].nodeType - == Node.DOCUMENT_TYPE_NODE) { - return "HIERARCHY_REQUEST_ERR"; - } - containedChildren.push(commonAncestor.childNodes[i]); - } - } - - // "If original start node is an ancestor container of original end node, - // set new node to original start node and new offset to original start - // offset." - var newNode, newOffset; - if (isAncestorContainer(originalStartNode, originalEndNode)) { - newNode = originalStartNode; - newOffset = originalStartOffset; - // "Otherwise:" - } else { - // "Let reference node equal original start node." - var referenceNode = originalStartNode; - - // "While reference node's parent is not null and is not an ancestor - // container of original end node, set reference node to its parent." - while (referenceNode.parentNode - && !isAncestorContainer(referenceNode.parentNode, originalEndNode)) { - referenceNode = referenceNode.parentNode; - } - - // "Set new node to the parent of reference node, and new offset to one - // plus the index of reference node." - newNode = referenceNode.parentNode; - newOffset = 1 + indexOf(referenceNode); - } - - // "If first partially contained child is a Text or Comment node:" - if (firstPartiallyContainedChild - && (firstPartiallyContainedChild.nodeType == Node.TEXT_NODE - || firstPartiallyContainedChild.nodeType == Node.COMMENT_NODE)) { - // "Let clone be the result of calling cloneNode(false) on original - // start node." - var clone = originalStartNode.cloneNode(false); - - // "Set the data of clone to the result of calling substringData() on - // original start node, with original start offset as the first - // argument and (length of original start node − original start offset) - // as the second." - clone.data = originalStartNode.substringData(originalStartOffset, - nodeLength(originalStartNode) - originalStartOffset); - - // "Append clone as the last child of frag." - frag.appendChild(clone); - - // "Call deleteData() on original start node, with original start - // offset as the first argument and (length of original start node − - // original start offset) as the second." - originalStartNode.deleteData(originalStartOffset, - nodeLength(originalStartNode) - originalStartOffset); - // "Otherwise, if first partially contained child is not null:" - } else if (firstPartiallyContainedChild) { - // "Let clone be the result of calling cloneNode(false) on first - // partially contained child." - var clone = firstPartiallyContainedChild.cloneNode(false); - - // "Append clone as the last child of frag." - frag.appendChild(clone); - - // "Let subrange be a new Range whose start is (original start node, - // original start offset) and whose end is (first partially contained - // child, length of first partially contained child)." - var subrange = ownerDoc.createRange(); - subrange.setStart(originalStartNode, originalStartOffset); - subrange.setEnd(firstPartiallyContainedChild, - nodeLength(firstPartiallyContainedChild)); - - // "Let subfrag be the result of calling extractContents() on - // subrange." - var subfrag = myExtractContents(subrange); - - // "For each child of subfrag, in order, append that child to clone as - // its last child." - for (var i = 0; i < subfrag.childNodes.length; i++) { - clone.appendChild(subfrag.childNodes[i]); - } - } - - // "For each contained child in contained children, append contained child - // as the last child of frag." - for (var i = 0; i < containedChildren.length; i++) { - frag.appendChild(containedChildren[i]); - } - - // "If last partially contained child is a Text or Comment node:" - if (lastPartiallyContainedChild - && (lastPartiallyContainedChild.nodeType == Node.TEXT_NODE - || lastPartiallyContainedChild.nodeType == Node.COMMENT_NODE)) { - // "Let clone be the result of calling cloneNode(false) on original - // end node." - var clone = originalEndNode.cloneNode(false); - - // "Set the data of clone to the result of calling substringData(0, - // original end offset) on original end node." - clone.data = originalEndNode.substringData(0, originalEndOffset); - - // "Append clone as the last child of frag." - frag.appendChild(clone); - - // "Call deleteData(0, original end offset) on original end node." - originalEndNode.deleteData(0, originalEndOffset); - // "Otherwise, if last partially contained child is not null:" - } else if (lastPartiallyContainedChild) { - // "Let clone be the result of calling cloneNode(false) on last - // partially contained child." - var clone = lastPartiallyContainedChild.cloneNode(false); - - // "Append clone as the last child of frag." - frag.appendChild(clone); - - // "Let subrange be a new Range whose start is (last partially - // contained child, 0) and whose end is (original end node, original - // end offset)." - var subrange = ownerDoc.createRange(); - subrange.setStart(lastPartiallyContainedChild, 0); - subrange.setEnd(originalEndNode, originalEndOffset); - - // "Let subfrag be the result of calling extractContents() on - // subrange." - var subfrag = myExtractContents(subrange); - - // "For each child of subfrag, in order, append that child to clone as - // its last child." - for (var i = 0; i < subfrag.childNodes.length; i++) { - clone.appendChild(subfrag.childNodes[i]); - } - } - - // "Set the context object's start and end to (new node, new offset)." - range.setStart(newNode, newOffset); - range.setEnd(newNode, newOffset); - - // "Return frag." - return frag; + // "If the context object's detached flag is set, raise an + // INVALID_STATE_ERR exception and abort these steps." + try { + range.collapsed; + } catch (e) { + return "INVALID_STATE_ERR"; + } + + // "Let frag be a new DocumentFragment whose ownerDocument is the same as + // the ownerDocument of the context object's start node." + var ownerDoc = range.startContainer.nodeType == Node.DOCUMENT_NODE + ? range.startContainer + : range.startContainer.ownerDocument; + var frag = ownerDoc.createDocumentFragment(); + + // "If the context object's start and end are the same, abort this method, + // returning frag." + if (range.startContainer == range.endContainer + && range.startOffset == range.endOffset) { + return frag; + } + + // "Let original start node, original start offset, original end node, and + // original end offset be the context object's start and end nodes and + // offsets, respectively." + var originalStartNode = range.startContainer; + var originalStartOffset = range.startOffset; + var originalEndNode = range.endContainer; + var originalEndOffset = range.endOffset; + + // "If original start node and original end node are the same, and they are + // a Text or Comment node:" + if (range.startContainer == range.endContainer + && (range.startContainer.nodeType == Node.TEXT_NODE + || range.startContainer.nodeType == Node.COMMENT_NODE)) { + // "Let clone be the result of calling cloneNode(false) on original + // start node." + var clone = originalStartNode.cloneNode(false); + + // "Set the data of clone to the result of calling + // substringData(original start offset, original end offset − original + // start offset) on original start node." + clone.data = originalStartNode.substringData(originalStartOffset, + originalEndOffset - originalStartOffset); + + // "Append clone as the last child of frag." + frag.appendChild(clone); + + // "Call deleteData(original start offset, original end offset − + // original start offset) on original start node." + originalStartNode.deleteData(originalStartOffset, + originalEndOffset - originalStartOffset); + + // "Abort this method, returning frag." + return frag; + } + + // "Let common ancestor equal original start node." + var commonAncestor = originalStartNode; + + // "While common ancestor is not an ancestor container of original end + // node, set common ancestor to its own parent." + while (!isAncestorContainer(commonAncestor, originalEndNode)) { + commonAncestor = commonAncestor.parentNode; + } + + // "If original start node is an ancestor container of original end node, + // let first partially contained child be null." + var firstPartiallyContainedChild; + if (isAncestorContainer(originalStartNode, originalEndNode)) { + firstPartiallyContainedChild = null; + // "Otherwise, let first partially contained child be the first child of + // common ancestor that is partially contained in the context object." + } else { + for (var i = 0; i < commonAncestor.childNodes.length; i++) { + if (isPartiallyContained(commonAncestor.childNodes[i], range)) { + firstPartiallyContainedChild = commonAncestor.childNodes[i]; + break; + } + } + if (!firstPartiallyContainedChild) { + throw "Spec bug: no first partially contained child!"; + } + } + + // "If original end node is an ancestor container of original start node, + // let last partially contained child be null." + var lastPartiallyContainedChild; + if (isAncestorContainer(originalEndNode, originalStartNode)) { + lastPartiallyContainedChild = null; + // "Otherwise, let last partially contained child be the last child of + // common ancestor that is partially contained in the context object." + } else { + for (var i = commonAncestor.childNodes.length - 1; i >= 0; i--) { + if (isPartiallyContained(commonAncestor.childNodes[i], range)) { + lastPartiallyContainedChild = commonAncestor.childNodes[i]; + break; + } + } + if (!lastPartiallyContainedChild) { + throw "Spec bug: no last partially contained child!"; + } + } + + // "Let contained children be a list of all children of common ancestor + // that are contained in the context object, in tree order." + // + // "If any member of contained children is a DocumentType, raise a + // HIERARCHY_REQUEST_ERR exception and abort these steps." + var containedChildren = []; + for (var i = 0; i < commonAncestor.childNodes.length; i++) { + if (isContained(commonAncestor.childNodes[i], range)) { + if (commonAncestor.childNodes[i].nodeType + == Node.DOCUMENT_TYPE_NODE) { + return "HIERARCHY_REQUEST_ERR"; + } + containedChildren.push(commonAncestor.childNodes[i]); + } + } + + // "If original start node is an ancestor container of original end node, + // set new node to original start node and new offset to original start + // offset." + var newNode, newOffset; + if (isAncestorContainer(originalStartNode, originalEndNode)) { + newNode = originalStartNode; + newOffset = originalStartOffset; + // "Otherwise:" + } else { + // "Let reference node equal original start node." + var referenceNode = originalStartNode; + + // "While reference node's parent is not null and is not an ancestor + // container of original end node, set reference node to its parent." + while (referenceNode.parentNode + && !isAncestorContainer(referenceNode.parentNode, originalEndNode)) { + referenceNode = referenceNode.parentNode; + } + + // "Set new node to the parent of reference node, and new offset to one + // plus the index of reference node." + newNode = referenceNode.parentNode; + newOffset = 1 + indexOf(referenceNode); + } + + // "If first partially contained child is a Text or Comment node:" + if (firstPartiallyContainedChild + && (firstPartiallyContainedChild.nodeType == Node.TEXT_NODE + || firstPartiallyContainedChild.nodeType == Node.COMMENT_NODE)) { + // "Let clone be the result of calling cloneNode(false) on original + // start node." + var clone = originalStartNode.cloneNode(false); + + // "Set the data of clone to the result of calling substringData() on + // original start node, with original start offset as the first + // argument and (length of original start node − original start offset) + // as the second." + clone.data = originalStartNode.substringData(originalStartOffset, + nodeLength(originalStartNode) - originalStartOffset); + + // "Append clone as the last child of frag." + frag.appendChild(clone); + + // "Call deleteData() on original start node, with original start + // offset as the first argument and (length of original start node − + // original start offset) as the second." + originalStartNode.deleteData(originalStartOffset, + nodeLength(originalStartNode) - originalStartOffset); + // "Otherwise, if first partially contained child is not null:" + } else if (firstPartiallyContainedChild) { + // "Let clone be the result of calling cloneNode(false) on first + // partially contained child." + var clone = firstPartiallyContainedChild.cloneNode(false); + + // "Append clone as the last child of frag." + frag.appendChild(clone); + + // "Let subrange be a new Range whose start is (original start node, + // original start offset) and whose end is (first partially contained + // child, length of first partially contained child)." + var subrange = ownerDoc.createRange(); + subrange.setStart(originalStartNode, originalStartOffset); + subrange.setEnd(firstPartiallyContainedChild, + nodeLength(firstPartiallyContainedChild)); + + // "Let subfrag be the result of calling extractContents() on + // subrange." + var subfrag = myExtractContents(subrange); + + // "For each child of subfrag, in order, append that child to clone as + // its last child." + for (var i = 0; i < subfrag.childNodes.length; i++) { + clone.appendChild(subfrag.childNodes[i]); + } + } + + // "For each contained child in contained children, append contained child + // as the last child of frag." + for (var i = 0; i < containedChildren.length; i++) { + frag.appendChild(containedChildren[i]); + } + + // "If last partially contained child is a Text or Comment node:" + if (lastPartiallyContainedChild + && (lastPartiallyContainedChild.nodeType == Node.TEXT_NODE + || lastPartiallyContainedChild.nodeType == Node.COMMENT_NODE)) { + // "Let clone be the result of calling cloneNode(false) on original + // end node." + var clone = originalEndNode.cloneNode(false); + + // "Set the data of clone to the result of calling substringData(0, + // original end offset) on original end node." + clone.data = originalEndNode.substringData(0, originalEndOffset); + + // "Append clone as the last child of frag." + frag.appendChild(clone); + + // "Call deleteData(0, original end offset) on original end node." + originalEndNode.deleteData(0, originalEndOffset); + // "Otherwise, if last partially contained child is not null:" + } else if (lastPartiallyContainedChild) { + // "Let clone be the result of calling cloneNode(false) on last + // partially contained child." + var clone = lastPartiallyContainedChild.cloneNode(false); + + // "Append clone as the last child of frag." + frag.appendChild(clone); + + // "Let subrange be a new Range whose start is (last partially + // contained child, 0) and whose end is (original end node, original + // end offset)." + var subrange = ownerDoc.createRange(); + subrange.setStart(lastPartiallyContainedChild, 0); + subrange.setEnd(originalEndNode, originalEndOffset); + + // "Let subfrag be the result of calling extractContents() on + // subrange." + var subfrag = myExtractContents(subrange); + + // "For each child of subfrag, in order, append that child to clone as + // its last child." + for (var i = 0; i < subfrag.childNodes.length; i++) { + clone.appendChild(subfrag.childNodes[i]); + } + } + + // "Set the context object's start and end to (new node, new offset)." + range.setStart(newNode, newOffset); + range.setEnd(newNode, newOffset); + + // "Return frag." + return frag; } /** @@ -832,87 +832,87 @@ function myExtractContents(range) { * a spec bug. */ function myInsertNode(range, node) { - // "If the detached flag is set, throw an "InvalidStateError" exception and - // terminate these steps." - // - // Assume that if accessing collapsed throws, it's detached. - try { - range.collapsed; - } catch (e) { - return "INVALID_STATE_ERR"; - } - - // "If start node is a Comment node, or a Text node whose parent is null, - // throw an "HierarchyRequestError" exception and terminate these steps." - if (range.startContainer.nodeType == Node.COMMENT_NODE - || (range.startContainer.nodeType == Node.TEXT_NODE - && !range.startContainer.parentNode)) { - return "HIERARCHY_REQUEST_ERR"; - } - - // "If start node is a Text node, split it with offset context object's - // start offset, and let reference node be the result." - var referenceNode; - if (range.startContainer.nodeType == Node.TEXT_NODE) { - // We aren't testing how ranges vary under mutations, and browsers vary - // in how they mutate for splitText, so let's just force the correct - // way. - var start = [range.startContainer, range.startOffset]; - var end = [range.endContainer, range.endOffset]; - - referenceNode = range.startContainer.splitText(range.startOffset); - - if (start[0] == end[0] - && end[1] > start[1]) { - end[0] = referenceNode; - end[1] -= start[1]; - } else if (end[0] == start[0].parentNode - && end[1] > indexOf(referenceNode)) { - end[1]++; - } - range.setStart(start[0], start[1]); - range.setEnd(end[0], end[1]); - - // "Otherwise, let reference node be the child of start node whose index is - // start offset, or null if there is no such child." - } else { - referenceNode = range.startContainer.childNodes[range.startOffset]; - if (typeof referenceNode == "undefined") { - referenceNode = null; - } - } - - // "If reference node is null, let parent be start node." - var parent_; - if (!referenceNode) { - parent_ = range.startContainer; - - // "Otherwise, let parent be the parent of reference node." - } else { - parent_ = referenceNode.parentNode; - } - - // "Let new offset be the index of reference node, or parent's length if - // reference node is null." - var newOffset = referenceNode ? indexOf(referenceNode) : nodeLength(parent_); - - // "Add node's length to new offset, if node is a DocumentFragment. - // Otherwise add one to new offset." - newOffset += node.nodeType == Node.DOCUMENT_FRAGMENT_NODE - ? nodeLength(node) - : 1; - - // "Pre-insert node into parent before reference node." - try { - parent_.insertBefore(node, referenceNode); - } catch (e) { - return getDomExceptionName(e); - } - - // "If start and end are the same, set end to (parent, new offset)." - if (range.collapsed) { - range.setEnd(parent_, newOffset); - } + // "If the detached flag is set, throw an "InvalidStateError" exception and + // terminate these steps." + // + // Assume that if accessing collapsed throws, it's detached. + try { + range.collapsed; + } catch (e) { + return "INVALID_STATE_ERR"; + } + + // "If start node is a Comment node, or a Text node whose parent is null, + // throw an "HierarchyRequestError" exception and terminate these steps." + if (range.startContainer.nodeType == Node.COMMENT_NODE + || (range.startContainer.nodeType == Node.TEXT_NODE + && !range.startContainer.parentNode)) { + return "HIERARCHY_REQUEST_ERR"; + } + + // "If start node is a Text node, split it with offset context object's + // start offset, and let reference node be the result." + var referenceNode; + if (range.startContainer.nodeType == Node.TEXT_NODE) { + // We aren't testing how ranges vary under mutations, and browsers vary + // in how they mutate for splitText, so let's just force the correct + // way. + var start = [range.startContainer, range.startOffset]; + var end = [range.endContainer, range.endOffset]; + + referenceNode = range.startContainer.splitText(range.startOffset); + + if (start[0] == end[0] + && end[1] > start[1]) { + end[0] = referenceNode; + end[1] -= start[1]; + } else if (end[0] == start[0].parentNode + && end[1] > indexOf(referenceNode)) { + end[1]++; + } + range.setStart(start[0], start[1]); + range.setEnd(end[0], end[1]); + + // "Otherwise, let reference node be the child of start node whose index is + // start offset, or null if there is no such child." + } else { + referenceNode = range.startContainer.childNodes[range.startOffset]; + if (typeof referenceNode == "undefined") { + referenceNode = null; + } + } + + // "If reference node is null, let parent be start node." + var parent_; + if (!referenceNode) { + parent_ = range.startContainer; + + // "Otherwise, let parent be the parent of reference node." + } else { + parent_ = referenceNode.parentNode; + } + + // "Let new offset be the index of reference node, or parent's length if + // reference node is null." + var newOffset = referenceNode ? indexOf(referenceNode) : nodeLength(parent_); + + // "Add node's length to new offset, if node is a DocumentFragment. + // Otherwise add one to new offset." + newOffset += node.nodeType == Node.DOCUMENT_FRAGMENT_NODE + ? nodeLength(node) + : 1; + + // "Pre-insert node into parent before reference node." + try { + parent_.insertBefore(node, referenceNode); + } catch (e) { + return getDomExceptionName(e); + } + + // "If start and end are the same, set end to (parent, new offset)." + if (range.collapsed) { + range.setEnd(parent_, newOffset); + } } /** @@ -921,36 +921,36 @@ function myInsertNode(range, node) { * this to testharness.js? */ function assertNodesEqual(actual, expected, msg) { - if (!actual.isEqualNode(expected)) { - msg = "Actual and expected mismatch for " + msg + ". "; - - while (actual && expected) { - assert_true(actual.nodeType === expected.nodeType - && actual.nodeName === expected.nodeName - && actual.nodeValue === expected.nodeValue - && actual.childNodes.length === expected.childNodes.length, - "First differing node: expected " + format_value(expected) - + ", got " + format_value(actual)); - actual = nextNode(actual); - expected = nextNode(expected); - } - - assert_unreached("DOMs were not equal but we couldn't figure out why"); - } + if (!actual.isEqualNode(expected)) { + msg = "Actual and expected mismatch for " + msg + ". "; + + while (actual && expected) { + assert_true(actual.nodeType === expected.nodeType + && actual.nodeName === expected.nodeName + && actual.nodeValue === expected.nodeValue + && actual.childNodes.length === expected.childNodes.length, + "First differing node: expected " + format_value(expected) + + ", got " + format_value(actual)); + actual = nextNode(actual); + expected = nextNode(expected); + } + + assert_unreached("DOMs were not equal but we couldn't figure out why"); + } } /** * Given a DOMException, return the name (e.g., "HIERARCHY_REQUEST_ERR"). */ function getDomExceptionName(e) { - var ret = null; - for (var prop in e) { - if (/^[A-Z_]+_ERR$/.test(prop) && e[prop] == e.code) { - return prop; - } - } - - throw "Exception seems to not be a DOMException? " + e; + var ret = null; + for (var prop in e) { + if (/^[A-Z_]+_ERR$/.test(prop) && e[prop] == e.code) { + return prop; + } + } + + throw "Exception seems to not be a DOMException? " + e; } /** @@ -958,11 +958,11 @@ function getDomExceptionName(e) { * container, end offset], returns a Range with those endpoints. */ function rangeFromEndpoints(endpoints) { - // If we just use document instead of the ownerDocument of endpoints[0], - // WebKit will throw on setStart/setEnd. This is a WebKit bug, but it's in - // range, not selection, so we don't want to fail anything for it. - var range = ownerDocument(endpoints[0]).createRange(); - range.setStart(endpoints[0], endpoints[1]); - range.setEnd(endpoints[2], endpoints[3]); - return range; + // If we just use document instead of the ownerDocument of endpoints[0], + // WebKit will throw on setStart/setEnd. This is a WebKit bug, but it's in + // range, not selection, so we don't want to fail anything for it. + var range = ownerDocument(endpoints[0]).createRange(); + range.setStart(endpoints[0], endpoints[1]); + range.setEnd(endpoints[2], endpoints[3]); + return range; } diff --git a/dom/nodes/Node-properties.html b/dom/nodes/Node-properties.html index aa44dc23b71079..798ba14d544c61 100644 --- a/dom/nodes/Node-properties.html +++ b/dom/nodes/Node-properties.html @@ -13,10 +13,10 @@ * will be eval()ed, and are mostly global variables defined in common.js. The * values are objects, which maps properties to expected values. So * - * foo: { - * bar: "baz", - * quz: 7, - * }, + * foo: { + * bar: "baz", + * quz: 7, + * }, * * will test that eval("foo.bar") === "baz" and eval("foo.quz") === 7. "foo" * and "bar" could thus be expressions, like "document.documentElement" and @@ -28,661 +28,661 @@ * defined. */ var expected = { - testDiv: { - // Node - nodeType: Node.ELEMENT_NODE, - ownerDocument: document, - parentNode: document.body, - parentElement: document.body, - "childNodes.length": 6, - "childNodes[0]": paras[0], - "childNodes[1]": paras[1], - "childNodes[2]": paras[2], - "childNodes[3]": paras[3], - "childNodes[4]": paras[4], - "childNodes[5]": comment, - previousSibling: null, - nextSibling: document.getElementById("log"), - textContent: "A\u0308b\u0308c\u0308d\u0308e\u0308f\u0308g\u0308h\u0308\nIjklmnop\nQrstuvwxYzabcdefGhijklmn", - - // Element - namespaceURI: "http://www.w3.org/1999/xhtml", - prefix: null, - localName: "div", - tagName: "DIV", - id: "test", - "children[0]": paras[0], - "children[1]": paras[1], - "children[2]": paras[2], - "children[3]": paras[3], - "children[4]": paras[4], - previousElementSibling: null, - // nextSibling isn't explicitly set - //nextElementSibling: , - childElementCount: 5, - }, - detachedDiv: { - // Node - nodeType: Node.ELEMENT_NODE, - ownerDocument: document, - parentNode: null, - parentElement: null, - "childNodes.length": 2, - "childNodes[0]": detachedPara1, - "childNodes[1]": detachedPara2, - previousSibling: null, - nextSibling: null, - textContent: "OpqrstuvWxyzabcd", - - // Element - namespaceURI: "http://www.w3.org/1999/xhtml", - prefix: null, - localName: "div", - tagName: "DIV", - "children[0]": detachedPara1, - "children[1]": detachedPara2, - previousElementSibling: null, - nextElementSibling: null, - childElementCount: 2, - }, - detachedPara1: { - // Node - nodeType: Node.ELEMENT_NODE, - ownerDocument: document, - parentNode: detachedDiv, - parentElement: detachedDiv, - "childNodes.length": 1, - previousSibling: null, - nextSibling: detachedPara2, - textContent: "Opqrstuv", - - // Element - namespaceURI: "http://www.w3.org/1999/xhtml", - prefix: null, - localName: "p", - tagName: "P", - previousElementSibling: null, - nextElementSibling: detachedPara2, - childElementCount: 0, - }, - detachedPara2: { - // Node - nodeType: Node.ELEMENT_NODE, - ownerDocument: document, - parentNode: detachedDiv, - parentElement: detachedDiv, - "childNodes.length": 1, - previousSibling: detachedPara1, - nextSibling: null, - textContent: "Wxyzabcd", - - // Element - namespaceURI: "http://www.w3.org/1999/xhtml", - prefix: null, - localName: "p", - tagName: "P", - previousElementSibling: detachedPara1, - nextElementSibling: null, - childElementCount: 0, - }, - document: { - // Node - nodeType: Node.DOCUMENT_NODE, - "childNodes.length": 2, - "childNodes[0]": document.doctype, - "childNodes[1]": document.documentElement, - - // Document - URL: String(location), - compatMode: "CSS1Compat", - characterSet: "UTF-8", - inputEncoding: "UTF-8", - contentType: "text/html", - doctype: doctype, - //documentElement: , - }, - foreignDoc: { - // Node - nodeType: Node.DOCUMENT_NODE, - "childNodes.length": 3, - "childNodes[0]": foreignDoc.doctype, - "childNodes[1]": foreignDoc.documentElement, - "childNodes[2]": foreignComment, - - // Document - URL: "about:blank", - compatMode: "CSS1Compat", - characterSet: "UTF-8", - inputEncoding: "UTF-8", - contentType: "text/html", - //doctype: , - //documentElement: , - }, - foreignPara1: { - // Node - nodeType: Node.ELEMENT_NODE, - ownerDocument: foreignDoc, - parentNode: foreignDoc.body, - parentElement: foreignDoc.body, - "childNodes.length": 1, - previousSibling: null, - nextSibling: foreignPara2, - textContent: "Efghijkl", - - // Element - namespaceURI: "http://www.w3.org/1999/xhtml", - prefix: null, - localName: "p", - tagName: "P", - previousElementSibling: null, - nextElementSibling: foreignPara2, - childElementCount: 0, - }, - foreignPara2: { - // Node - nodeType: Node.ELEMENT_NODE, - ownerDocument: foreignDoc, - parentNode: foreignDoc.body, - parentElement: foreignDoc.body, - "childNodes.length": 1, - previousSibling: foreignPara1, - nextSibling: foreignTextNode, - textContent: "Mnopqrst", - - // Element - namespaceURI: "http://www.w3.org/1999/xhtml", - prefix: null, - localName: "p", - tagName: "P", - previousElementSibling: foreignPara1, - nextElementSibling: null, - childElementCount: 0, - }, - xmlDoc: { - // Node - nodeType: Node.DOCUMENT_NODE, - "childNodes.length": 4, - "childNodes[0]": xmlDoctype, - "childNodes[1]": xmlElement, - "childNodes[2]": processingInstruction, - "childNodes[3]": xmlComment, - - // Document - URL: "about:blank", - compatMode: "CSS1Compat", - characterSet: "UTF-8", - inputEncoding: "UTF-8", - contentType: "application/xml", - //doctype: , - //documentElement: , - }, - xmlElement: { - // Node - nodeType: Node.ELEMENT_NODE, - ownerDocument: xmlDoc, - parentNode: xmlDoc, - parentElement: null, - "childNodes.length": 1, - "childNodes[0]": xmlTextNode, - previousSibling: xmlDoctype, - nextSibling: processingInstruction, - textContent: "do re mi fa so la ti", - - // Element - namespaceURI: "http://www.w3.org/1999/xhtml", - prefix: null, - localName: "igiveuponcreativenames", - tagName: "igiveuponcreativenames", - previousElementSibling: null, - nextElementSibling: null, - childElementCount: 0, - }, - detachedXmlElement: { - // Node - nodeType: Node.ELEMENT_NODE, - ownerDocument: xmlDoc, - parentNode: null, - parentElement: null, - "childNodes.length": 0, - previousSibling: null, - nextSibling: null, - textContent: "", - - // Element - namespaceURI: "http://www.w3.org/1999/xhtml", - prefix: null, - localName: "everyone-hates-hyphenated-element-names", - tagName: "everyone-hates-hyphenated-element-names", - previousElementSibling: null, - nextElementSibling: null, - childElementCount: 0, - }, - detachedTextNode: { - // Node - nodeType: Node.TEXT_NODE, - ownerDocument: document, - parentNode: null, - parentElement: null, - previousSibling: null, - nextSibling: null, - nodeValue: "Uvwxyzab", - - // Text - wholeText: "Uvwxyzab", - }, - foreignTextNode: { - // Node - nodeType: Node.TEXT_NODE, - ownerDocument: foreignDoc, - parentNode: foreignDoc.body, - parentElement: foreignDoc.body, - previousSibling: foreignPara2, - nextSibling: null, - nodeValue: "I admit that I harbor doubts about whether we really need so many things to test, but it's too late to stop now.", - - // Text - wholeText: "I admit that I harbor doubts about whether we really need so many things to test, but it's too late to stop now.", - }, - detachedForeignTextNode: { - // Node - nodeType: Node.TEXT_NODE, - ownerDocument: foreignDoc, - parentNode: null, - parentElement: null, - previousSibling: null, - nextSibling: null, - nodeValue: "Cdefghij", - - // Text - wholeText: "Cdefghij", - }, - xmlTextNode: { - // Node - nodeType: Node.TEXT_NODE, - ownerDocument: xmlDoc, - parentNode: xmlElement, - parentElement: xmlElement, - previousSibling: null, - nextSibling: null, - nodeValue: "do re mi fa so la ti", - - // Text - wholeText: "do re mi fa so la ti", - }, - detachedXmlTextNode: { - // Node - nodeType: Node.TEXT_NODE, - ownerDocument: xmlDoc, - parentNode: null, - parentElement: null, - previousSibling: null, - nextSibling: null, - nodeValue: "Klmnopqr", - - // Text - wholeText: "Klmnopqr", - }, - processingInstruction: { - // Node - nodeType: Node.PROCESSING_INSTRUCTION_NODE, - ownerDocument: xmlDoc, - parentNode: xmlDoc, - parentElement: null, - previousSibling: xmlElement, - nextSibling: xmlComment, - nodeValue: 'Did you know that ":syn sync fromstart" is very useful when using vim to edit large amounts of JavaScript embedded in HTML?', - - // ProcessingInstruction - target: "somePI", - }, - detachedProcessingInstruction: { - // Node - nodeType: Node.PROCESSING_INSTRUCTION_NODE, - ownerDocument: xmlDoc, - parentNode: null, - parentElement: null, - previousSibling: null, - nextSibling: null, - nodeValue: "chirp chirp chirp", - - // ProcessingInstruction - target: "whippoorwill", - }, - comment: { - // Node - nodeType: Node.COMMENT_NODE, - ownerDocument: document, - parentNode: testDiv, - parentElement: testDiv, - previousSibling: paras[4], - nextSibling: null, - nodeValue: "Alphabet soup?", - }, - detachedComment: { - // Node - nodeType: Node.COMMENT_NODE, - ownerDocument: document, - parentNode: null, - parentElement: null, - previousSibling: null, - nextSibling: null, - nodeValue: "Stuvwxyz", - }, - foreignComment: { - // Node - nodeType: Node.COMMENT_NODE, - ownerDocument: foreignDoc, - parentNode: foreignDoc, - parentElement: null, - previousSibling: foreignDoc.documentElement, - nextSibling: null, - nodeValue: '"Commenter" and "commentator" mean different things. I\'ve seen non-native speakers trip up on this.', - }, - detachedForeignComment: { - // Node - nodeType: Node.COMMENT_NODE, - ownerDocument: foreignDoc, - parentNode: null, - parentElement: null, - previousSibling: null, - nextSibling: null, - nodeValue: "אריה יהודה", - }, - xmlComment: { - // Node - nodeType: Node.COMMENT_NODE, - ownerDocument: xmlDoc, - parentNode: xmlDoc, - parentElement: null, - previousSibling: processingInstruction, - nextSibling: null, - nodeValue: "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", - }, - detachedXmlComment: { - // Node - nodeType: Node.COMMENT_NODE, - ownerDocument: xmlDoc, - parentNode: null, - parentElement: null, - previousSibling: null, - nextSibling: null, - nodeValue: "בן חיים אליעזר", - }, - docfrag: { - // Node - nodeType: Node.DOCUMENT_FRAGMENT_NODE, - ownerDocument: document, - "childNodes.length": 0, - textContent: "", - }, - foreignDocfrag: { - // Node - nodeType: Node.DOCUMENT_FRAGMENT_NODE, - ownerDocument: foreignDoc, - "childNodes.length": 0, - textContent: "", - }, - xmlDocfrag: { - // Node - nodeType: Node.DOCUMENT_FRAGMENT_NODE, - ownerDocument: xmlDoc, - "childNodes.length": 0, - textContent: "", - }, - doctype: { - // Node - nodeType: Node.DOCUMENT_TYPE_NODE, - ownerDocument: document, - parentNode: document, - previousSibling: null, - nextSibling: document.documentElement, - - // DocumentType - name: "html", - publicId: "", - systemId: "", - }, - foreignDoctype: { - // Node - nodeType: Node.DOCUMENT_TYPE_NODE, - ownerDocument: foreignDoc, - parentNode: foreignDoc, - previousSibling: null, - nextSibling: foreignDoc.documentElement, - - // DocumentType - name: "html", - publicId: "", - systemId: "", - }, - xmlDoctype: { - // Node - nodeType: Node.DOCUMENT_TYPE_NODE, - ownerDocument: xmlDoc, - parentNode: xmlDoc, - previousSibling: null, - nextSibling: xmlElement, - - // DocumentType - name: "qorflesnorf", - publicId: "abcde", - systemId: "x\"'y", - }, - "paras[0]": { - // Node - nodeType: Node.ELEMENT_NODE, - ownerDocument: document, - parentNode: testDiv, - parentElement: testDiv, - "childNodes.length": 1, - previousSibling: null, - nextSibling: paras[1], - textContent: "A\u0308b\u0308c\u0308d\u0308e\u0308f\u0308g\u0308h\u0308\n", - - // Element - namespaceURI: "http://www.w3.org/1999/xhtml", - prefix: null, - localName: "p", - tagName: "P", - id: "a", - previousElementSibling: null, - nextElementSibling: paras[1], - childElementCount: 0, - }, - "paras[1]": { - // Node - nodeType: Node.ELEMENT_NODE, - ownerDocument: document, - parentNode: testDiv, - parentElement: testDiv, - "childNodes.length": 1, - previousSibling: paras[0], - nextSibling: paras[2], - textContent: "Ijklmnop\n", - - // Element - namespaceURI: "http://www.w3.org/1999/xhtml", - prefix: null, - localName: "p", - tagName: "P", - id: "b", - previousElementSibling: paras[0], - nextElementSibling: paras[2], - childElementCount: 0, - }, - "paras[2]": { - // Node - nodeType: Node.ELEMENT_NODE, - ownerDocument: document, - parentNode: testDiv, - parentElement: testDiv, - "childNodes.length": 1, - previousSibling: paras[1], - nextSibling: paras[3], - textContent: "Qrstuvwx", - - // Element - namespaceURI: "http://www.w3.org/1999/xhtml", - prefix: null, - localName: "p", - tagName: "P", - id: "c", - previousElementSibling: paras[1], - nextElementSibling: paras[3], - childElementCount: 0, - }, - "paras[3]": { - // Node - nodeType: Node.ELEMENT_NODE, - ownerDocument: document, - parentNode: testDiv, - parentElement: testDiv, - "childNodes.length": 1, - previousSibling: paras[2], - nextSibling: paras[4], - textContent: "Yzabcdef", - - // Element - namespaceURI: "http://www.w3.org/1999/xhtml", - prefix: null, - localName: "p", - tagName: "P", - id: "d", - previousElementSibling: paras[2], - nextElementSibling: paras[4], - childElementCount: 0, - }, - "paras[4]": { - // Node - nodeType: Node.ELEMENT_NODE, - ownerDocument: document, - parentNode: testDiv, - parentElement: testDiv, - "childNodes.length": 1, - previousSibling: paras[3], - nextSibling: comment, - textContent: "Ghijklmn", - - // Element - namespaceURI: "http://www.w3.org/1999/xhtml", - prefix: null, - localName: "p", - tagName: "P", - id: "e", - previousElementSibling: paras[3], - nextElementSibling: null, - childElementCount: 0, - }, + testDiv: { + // Node + nodeType: Node.ELEMENT_NODE, + ownerDocument: document, + parentNode: document.body, + parentElement: document.body, + "childNodes.length": 6, + "childNodes[0]": paras[0], + "childNodes[1]": paras[1], + "childNodes[2]": paras[2], + "childNodes[3]": paras[3], + "childNodes[4]": paras[4], + "childNodes[5]": comment, + previousSibling: null, + nextSibling: document.getElementById("log"), + textContent: "A\u0308b\u0308c\u0308d\u0308e\u0308f\u0308g\u0308h\u0308\nIjklmnop\nQrstuvwxYzabcdefGhijklmn", + + // Element + namespaceURI: "http://www.w3.org/1999/xhtml", + prefix: null, + localName: "div", + tagName: "DIV", + id: "test", + "children[0]": paras[0], + "children[1]": paras[1], + "children[2]": paras[2], + "children[3]": paras[3], + "children[4]": paras[4], + previousElementSibling: null, + // nextSibling isn't explicitly set + //nextElementSibling: , + childElementCount: 5, + }, + detachedDiv: { + // Node + nodeType: Node.ELEMENT_NODE, + ownerDocument: document, + parentNode: null, + parentElement: null, + "childNodes.length": 2, + "childNodes[0]": detachedPara1, + "childNodes[1]": detachedPara2, + previousSibling: null, + nextSibling: null, + textContent: "OpqrstuvWxyzabcd", + + // Element + namespaceURI: "http://www.w3.org/1999/xhtml", + prefix: null, + localName: "div", + tagName: "DIV", + "children[0]": detachedPara1, + "children[1]": detachedPara2, + previousElementSibling: null, + nextElementSibling: null, + childElementCount: 2, + }, + detachedPara1: { + // Node + nodeType: Node.ELEMENT_NODE, + ownerDocument: document, + parentNode: detachedDiv, + parentElement: detachedDiv, + "childNodes.length": 1, + previousSibling: null, + nextSibling: detachedPara2, + textContent: "Opqrstuv", + + // Element + namespaceURI: "http://www.w3.org/1999/xhtml", + prefix: null, + localName: "p", + tagName: "P", + previousElementSibling: null, + nextElementSibling: detachedPara2, + childElementCount: 0, + }, + detachedPara2: { + // Node + nodeType: Node.ELEMENT_NODE, + ownerDocument: document, + parentNode: detachedDiv, + parentElement: detachedDiv, + "childNodes.length": 1, + previousSibling: detachedPara1, + nextSibling: null, + textContent: "Wxyzabcd", + + // Element + namespaceURI: "http://www.w3.org/1999/xhtml", + prefix: null, + localName: "p", + tagName: "P", + previousElementSibling: detachedPara1, + nextElementSibling: null, + childElementCount: 0, + }, + document: { + // Node + nodeType: Node.DOCUMENT_NODE, + "childNodes.length": 2, + "childNodes[0]": document.doctype, + "childNodes[1]": document.documentElement, + + // Document + URL: String(location), + compatMode: "CSS1Compat", + characterSet: "UTF-8", + inputEncoding: "UTF-8", + contentType: "text/html", + doctype: doctype, + //documentElement: , + }, + foreignDoc: { + // Node + nodeType: Node.DOCUMENT_NODE, + "childNodes.length": 3, + "childNodes[0]": foreignDoc.doctype, + "childNodes[1]": foreignDoc.documentElement, + "childNodes[2]": foreignComment, + + // Document + URL: "about:blank", + compatMode: "CSS1Compat", + characterSet: "UTF-8", + inputEncoding: "UTF-8", + contentType: "text/html", + //doctype: , + //documentElement: , + }, + foreignPara1: { + // Node + nodeType: Node.ELEMENT_NODE, + ownerDocument: foreignDoc, + parentNode: foreignDoc.body, + parentElement: foreignDoc.body, + "childNodes.length": 1, + previousSibling: null, + nextSibling: foreignPara2, + textContent: "Efghijkl", + + // Element + namespaceURI: "http://www.w3.org/1999/xhtml", + prefix: null, + localName: "p", + tagName: "P", + previousElementSibling: null, + nextElementSibling: foreignPara2, + childElementCount: 0, + }, + foreignPara2: { + // Node + nodeType: Node.ELEMENT_NODE, + ownerDocument: foreignDoc, + parentNode: foreignDoc.body, + parentElement: foreignDoc.body, + "childNodes.length": 1, + previousSibling: foreignPara1, + nextSibling: foreignTextNode, + textContent: "Mnopqrst", + + // Element + namespaceURI: "http://www.w3.org/1999/xhtml", + prefix: null, + localName: "p", + tagName: "P", + previousElementSibling: foreignPara1, + nextElementSibling: null, + childElementCount: 0, + }, + xmlDoc: { + // Node + nodeType: Node.DOCUMENT_NODE, + "childNodes.length": 4, + "childNodes[0]": xmlDoctype, + "childNodes[1]": xmlElement, + "childNodes[2]": processingInstruction, + "childNodes[3]": xmlComment, + + // Document + URL: "about:blank", + compatMode: "CSS1Compat", + characterSet: "UTF-8", + inputEncoding: "UTF-8", + contentType: "application/xml", + //doctype: , + //documentElement: , + }, + xmlElement: { + // Node + nodeType: Node.ELEMENT_NODE, + ownerDocument: xmlDoc, + parentNode: xmlDoc, + parentElement: null, + "childNodes.length": 1, + "childNodes[0]": xmlTextNode, + previousSibling: xmlDoctype, + nextSibling: processingInstruction, + textContent: "do re mi fa so la ti", + + // Element + namespaceURI: "http://www.w3.org/1999/xhtml", + prefix: null, + localName: "igiveuponcreativenames", + tagName: "igiveuponcreativenames", + previousElementSibling: null, + nextElementSibling: null, + childElementCount: 0, + }, + detachedXmlElement: { + // Node + nodeType: Node.ELEMENT_NODE, + ownerDocument: xmlDoc, + parentNode: null, + parentElement: null, + "childNodes.length": 0, + previousSibling: null, + nextSibling: null, + textContent: "", + + // Element + namespaceURI: "http://www.w3.org/1999/xhtml", + prefix: null, + localName: "everyone-hates-hyphenated-element-names", + tagName: "everyone-hates-hyphenated-element-names", + previousElementSibling: null, + nextElementSibling: null, + childElementCount: 0, + }, + detachedTextNode: { + // Node + nodeType: Node.TEXT_NODE, + ownerDocument: document, + parentNode: null, + parentElement: null, + previousSibling: null, + nextSibling: null, + nodeValue: "Uvwxyzab", + + // Text + wholeText: "Uvwxyzab", + }, + foreignTextNode: { + // Node + nodeType: Node.TEXT_NODE, + ownerDocument: foreignDoc, + parentNode: foreignDoc.body, + parentElement: foreignDoc.body, + previousSibling: foreignPara2, + nextSibling: null, + nodeValue: "I admit that I harbor doubts about whether we really need so many things to test, but it's too late to stop now.", + + // Text + wholeText: "I admit that I harbor doubts about whether we really need so many things to test, but it's too late to stop now.", + }, + detachedForeignTextNode: { + // Node + nodeType: Node.TEXT_NODE, + ownerDocument: foreignDoc, + parentNode: null, + parentElement: null, + previousSibling: null, + nextSibling: null, + nodeValue: "Cdefghij", + + // Text + wholeText: "Cdefghij", + }, + xmlTextNode: { + // Node + nodeType: Node.TEXT_NODE, + ownerDocument: xmlDoc, + parentNode: xmlElement, + parentElement: xmlElement, + previousSibling: null, + nextSibling: null, + nodeValue: "do re mi fa so la ti", + + // Text + wholeText: "do re mi fa so la ti", + }, + detachedXmlTextNode: { + // Node + nodeType: Node.TEXT_NODE, + ownerDocument: xmlDoc, + parentNode: null, + parentElement: null, + previousSibling: null, + nextSibling: null, + nodeValue: "Klmnopqr", + + // Text + wholeText: "Klmnopqr", + }, + processingInstruction: { + // Node + nodeType: Node.PROCESSING_INSTRUCTION_NODE, + ownerDocument: xmlDoc, + parentNode: xmlDoc, + parentElement: null, + previousSibling: xmlElement, + nextSibling: xmlComment, + nodeValue: 'Did you know that ":syn sync fromstart" is very useful when using vim to edit large amounts of JavaScript embedded in HTML?', + + // ProcessingInstruction + target: "somePI", + }, + detachedProcessingInstruction: { + // Node + nodeType: Node.PROCESSING_INSTRUCTION_NODE, + ownerDocument: xmlDoc, + parentNode: null, + parentElement: null, + previousSibling: null, + nextSibling: null, + nodeValue: "chirp chirp chirp", + + // ProcessingInstruction + target: "whippoorwill", + }, + comment: { + // Node + nodeType: Node.COMMENT_NODE, + ownerDocument: document, + parentNode: testDiv, + parentElement: testDiv, + previousSibling: paras[4], + nextSibling: null, + nodeValue: "Alphabet soup?", + }, + detachedComment: { + // Node + nodeType: Node.COMMENT_NODE, + ownerDocument: document, + parentNode: null, + parentElement: null, + previousSibling: null, + nextSibling: null, + nodeValue: "Stuvwxyz", + }, + foreignComment: { + // Node + nodeType: Node.COMMENT_NODE, + ownerDocument: foreignDoc, + parentNode: foreignDoc, + parentElement: null, + previousSibling: foreignDoc.documentElement, + nextSibling: null, + nodeValue: '"Commenter" and "commentator" mean different things. I\'ve seen non-native speakers trip up on this.', + }, + detachedForeignComment: { + // Node + nodeType: Node.COMMENT_NODE, + ownerDocument: foreignDoc, + parentNode: null, + parentElement: null, + previousSibling: null, + nextSibling: null, + nodeValue: "אריה יהודה", + }, + xmlComment: { + // Node + nodeType: Node.COMMENT_NODE, + ownerDocument: xmlDoc, + parentNode: xmlDoc, + parentElement: null, + previousSibling: processingInstruction, + nextSibling: null, + nodeValue: "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", + }, + detachedXmlComment: { + // Node + nodeType: Node.COMMENT_NODE, + ownerDocument: xmlDoc, + parentNode: null, + parentElement: null, + previousSibling: null, + nextSibling: null, + nodeValue: "בן חיים אליעזר", + }, + docfrag: { + // Node + nodeType: Node.DOCUMENT_FRAGMENT_NODE, + ownerDocument: document, + "childNodes.length": 0, + textContent: "", + }, + foreignDocfrag: { + // Node + nodeType: Node.DOCUMENT_FRAGMENT_NODE, + ownerDocument: foreignDoc, + "childNodes.length": 0, + textContent: "", + }, + xmlDocfrag: { + // Node + nodeType: Node.DOCUMENT_FRAGMENT_NODE, + ownerDocument: xmlDoc, + "childNodes.length": 0, + textContent: "", + }, + doctype: { + // Node + nodeType: Node.DOCUMENT_TYPE_NODE, + ownerDocument: document, + parentNode: document, + previousSibling: null, + nextSibling: document.documentElement, + + // DocumentType + name: "html", + publicId: "", + systemId: "", + }, + foreignDoctype: { + // Node + nodeType: Node.DOCUMENT_TYPE_NODE, + ownerDocument: foreignDoc, + parentNode: foreignDoc, + previousSibling: null, + nextSibling: foreignDoc.documentElement, + + // DocumentType + name: "html", + publicId: "", + systemId: "", + }, + xmlDoctype: { + // Node + nodeType: Node.DOCUMENT_TYPE_NODE, + ownerDocument: xmlDoc, + parentNode: xmlDoc, + previousSibling: null, + nextSibling: xmlElement, + + // DocumentType + name: "qorflesnorf", + publicId: "abcde", + systemId: "x\"'y", + }, + "paras[0]": { + // Node + nodeType: Node.ELEMENT_NODE, + ownerDocument: document, + parentNode: testDiv, + parentElement: testDiv, + "childNodes.length": 1, + previousSibling: null, + nextSibling: paras[1], + textContent: "A\u0308b\u0308c\u0308d\u0308e\u0308f\u0308g\u0308h\u0308\n", + + // Element + namespaceURI: "http://www.w3.org/1999/xhtml", + prefix: null, + localName: "p", + tagName: "P", + id: "a", + previousElementSibling: null, + nextElementSibling: paras[1], + childElementCount: 0, + }, + "paras[1]": { + // Node + nodeType: Node.ELEMENT_NODE, + ownerDocument: document, + parentNode: testDiv, + parentElement: testDiv, + "childNodes.length": 1, + previousSibling: paras[0], + nextSibling: paras[2], + textContent: "Ijklmnop\n", + + // Element + namespaceURI: "http://www.w3.org/1999/xhtml", + prefix: null, + localName: "p", + tagName: "P", + id: "b", + previousElementSibling: paras[0], + nextElementSibling: paras[2], + childElementCount: 0, + }, + "paras[2]": { + // Node + nodeType: Node.ELEMENT_NODE, + ownerDocument: document, + parentNode: testDiv, + parentElement: testDiv, + "childNodes.length": 1, + previousSibling: paras[1], + nextSibling: paras[3], + textContent: "Qrstuvwx", + + // Element + namespaceURI: "http://www.w3.org/1999/xhtml", + prefix: null, + localName: "p", + tagName: "P", + id: "c", + previousElementSibling: paras[1], + nextElementSibling: paras[3], + childElementCount: 0, + }, + "paras[3]": { + // Node + nodeType: Node.ELEMENT_NODE, + ownerDocument: document, + parentNode: testDiv, + parentElement: testDiv, + "childNodes.length": 1, + previousSibling: paras[2], + nextSibling: paras[4], + textContent: "Yzabcdef", + + // Element + namespaceURI: "http://www.w3.org/1999/xhtml", + prefix: null, + localName: "p", + tagName: "P", + id: "d", + previousElementSibling: paras[2], + nextElementSibling: paras[4], + childElementCount: 0, + }, + "paras[4]": { + // Node + nodeType: Node.ELEMENT_NODE, + ownerDocument: document, + parentNode: testDiv, + parentElement: testDiv, + "childNodes.length": 1, + previousSibling: paras[3], + nextSibling: comment, + textContent: "Ghijklmn", + + // Element + namespaceURI: "http://www.w3.org/1999/xhtml", + prefix: null, + localName: "p", + tagName: "P", + id: "e", + previousElementSibling: paras[3], + nextElementSibling: null, + childElementCount: 0, + }, }; for (var node in expected) { - // Now we set various default values by node type. - switch (expected[node].nodeType) { - case Node.ELEMENT_NODE: - expected[node].nodeName = expected[node].tagName; - expected[node].nodeValue = null; - expected[node]["children.length"] = expected[node].childElementCount; - - if (expected[node].id === undefined) { - expected[node].id = ""; - } - if (expected[node].className === undefined) { - expected[node].className = ""; - } - - var len = expected[node].childElementCount; - if (len === 0) { - expected[node].firstElementChild = - expected[node].lastElementChild = null; - } else { - // If we have expectations for the first/last child in children, - // use those. Otherwise, at least check that .firstElementChild == - // .children[0] and .lastElementChild == .children[len - 1], even - // if we aren't sure what they should be. - expected[node].firstElementChild = expected[node]["children[0]"] - ? expected[node]["children[0]"] - : eval(node).children[0]; - expected[node].lastElementChild = - expected[node]["children[" + (len - 1) + "]"] - ? expected[node]["children[" + (len - 1) + "]"] - : eval(node).children[len - 1]; - } - break; - - case Node.TEXT_NODE: - expected[node].nodeName = "#text"; - expected[node]["childNodes.length"] = 0; - expected[node].textContent = expected[node].data = - expected[node].nodeValue; - expected[node].length = expected[node].nodeValue.length; - break; - - case Node.PROCESSING_INSTRUCTION_NODE: - expected[node].nodeName = expected[node].target; - expected[node]["childNodes.length"] = 0; - expected[node].textContent = expected[node].data = - expected[node].nodeValue; - expected[node].length = expected[node].nodeValue.length; - break; - - case Node.COMMENT_NODE: - expected[node].nodeName = "#comment"; - expected[node]["childNodes.length"] = 0; - expected[node].textContent = expected[node].data = - expected[node].nodeValue; - expected[node].length = expected[node].nodeValue.length; - break; - - case Node.DOCUMENT_NODE: - expected[node].nodeName = "#document"; - expected[node].ownerDocument = expected[node].parentNode = - expected[node].parentElement = expected[node].previousSibling = - expected[node].nextSibling = expected[node].nodeValue = - expected[node].textContent = null; - expected[node].documentURI = expected[node].URL; - break; - - case Node.DOCUMENT_TYPE_NODE: - expected[node].nodeName = expected[node].name; - expected[node]["childNodes.length"] = 0; - expected[node].parentElement = expected[node].nodeValue = - expected[node].textContent = null; - break; - - case Node.DOCUMENT_FRAGMENT_NODE: - expected[node].nodeName = "#document-fragment"; - expected[node].parentNode = expected[node].parentElement = - expected[node].previousSibling = expected[node].nextSibling = - expected[node].nodeValue = null; - break; - } - - // Now we set some further default values that are independent of node - // type. - var len = expected[node]["childNodes.length"]; - if (len === 0) { - expected[node].firstChild = expected[node].lastChild = null; - } else { - // If we have expectations for the first/last child in childNodes, use - // those. Otherwise, at least check that .firstChild == .childNodes[0] - // and .lastChild == .childNodes[len - 1], even if we aren't sure what - // they should be. - expected[node].firstChild = expected[node]["childNodes[0]"] - ? expected[node]["childNodes[0]"] - : eval(node).childNodes[0]; - expected[node].lastChild = - expected[node]["childNodes[" + (len - 1) + "]"] - ? expected[node]["childNodes[" + (len - 1) + "]"] - : eval(node).childNodes[len - 1]; - } - expected[node]["hasChildNodes()"] = !!expected[node]["childNodes.length"]; - - // Finally, we test! - for (var prop in expected[node]) { - test(function() { - assert_equals(eval(node + "." + prop), expected[node][prop]); - }, node + "." + prop); - } + // Now we set various default values by node type. + switch (expected[node].nodeType) { + case Node.ELEMENT_NODE: + expected[node].nodeName = expected[node].tagName; + expected[node].nodeValue = null; + expected[node]["children.length"] = expected[node].childElementCount; + + if (expected[node].id === undefined) { + expected[node].id = ""; + } + if (expected[node].className === undefined) { + expected[node].className = ""; + } + + var len = expected[node].childElementCount; + if (len === 0) { + expected[node].firstElementChild = + expected[node].lastElementChild = null; + } else { + // If we have expectations for the first/last child in children, + // use those. Otherwise, at least check that .firstElementChild == + // .children[0] and .lastElementChild == .children[len - 1], even + // if we aren't sure what they should be. + expected[node].firstElementChild = expected[node]["children[0]"] + ? expected[node]["children[0]"] + : eval(node).children[0]; + expected[node].lastElementChild = + expected[node]["children[" + (len - 1) + "]"] + ? expected[node]["children[" + (len - 1) + "]"] + : eval(node).children[len - 1]; + } + break; + + case Node.TEXT_NODE: + expected[node].nodeName = "#text"; + expected[node]["childNodes.length"] = 0; + expected[node].textContent = expected[node].data = + expected[node].nodeValue; + expected[node].length = expected[node].nodeValue.length; + break; + + case Node.PROCESSING_INSTRUCTION_NODE: + expected[node].nodeName = expected[node].target; + expected[node]["childNodes.length"] = 0; + expected[node].textContent = expected[node].data = + expected[node].nodeValue; + expected[node].length = expected[node].nodeValue.length; + break; + + case Node.COMMENT_NODE: + expected[node].nodeName = "#comment"; + expected[node]["childNodes.length"] = 0; + expected[node].textContent = expected[node].data = + expected[node].nodeValue; + expected[node].length = expected[node].nodeValue.length; + break; + + case Node.DOCUMENT_NODE: + expected[node].nodeName = "#document"; + expected[node].ownerDocument = expected[node].parentNode = + expected[node].parentElement = expected[node].previousSibling = + expected[node].nextSibling = expected[node].nodeValue = + expected[node].textContent = null; + expected[node].documentURI = expected[node].URL; + break; + + case Node.DOCUMENT_TYPE_NODE: + expected[node].nodeName = expected[node].name; + expected[node]["childNodes.length"] = 0; + expected[node].parentElement = expected[node].nodeValue = + expected[node].textContent = null; + break; + + case Node.DOCUMENT_FRAGMENT_NODE: + expected[node].nodeName = "#document-fragment"; + expected[node].parentNode = expected[node].parentElement = + expected[node].previousSibling = expected[node].nextSibling = + expected[node].nodeValue = null; + break; + } + + // Now we set some further default values that are independent of node + // type. + var len = expected[node]["childNodes.length"]; + if (len === 0) { + expected[node].firstChild = expected[node].lastChild = null; + } else { + // If we have expectations for the first/last child in childNodes, use + // those. Otherwise, at least check that .firstChild == .childNodes[0] + // and .lastChild == .childNodes[len - 1], even if we aren't sure what + // they should be. + expected[node].firstChild = expected[node]["childNodes[0]"] + ? expected[node]["childNodes[0]"] + : eval(node).childNodes[0]; + expected[node].lastChild = + expected[node]["childNodes[" + (len - 1) + "]"] + ? expected[node]["childNodes[" + (len - 1) + "]"] + : eval(node).childNodes[len - 1]; + } + expected[node]["hasChildNodes()"] = !!expected[node]["childNodes.length"]; + + // Finally, we test! + for (var prop in expected[node]) { + test(function() { + assert_equals(eval(node + "." + prop), expected[node][prop]); + }, node + "." + prop); + } } testDiv.parentNode.removeChild(testDiv);