diff options
Diffstat (limited to 'testing/web-platform/tests/dom/ranges/Range-surroundContents.html')
-rw-r--r-- | testing/web-platform/tests/dom/ranges/Range-surroundContents.html | 324 |
1 files changed, 324 insertions, 0 deletions
diff --git a/testing/web-platform/tests/dom/ranges/Range-surroundContents.html b/testing/web-platform/tests/dom/ranges/Range-surroundContents.html new file mode 100644 index 000000000..e8cc11b24 --- /dev/null +++ b/testing/web-platform/tests/dom/ranges/Range-surroundContents.html @@ -0,0 +1,324 @@ +<!doctype html> +<meta charset=utf-8> +<title>Range.surroundContents() tests</title> +<link rel="author" title="Aryeh Gregor" href=ayg@aryeh.name> +<meta name=timeout content=long> +<p>To debug test failures, add a query parameter "subtest" with the test id (like +"?subtest=5,16"). Only that test will be run. Then you can look at the resulting +iframes in the DOM. +<div id=log></div> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src=../common.js></script> +<script> +"use strict"; + +testDiv.parentNode.removeChild(testDiv); + +function mySurroundContents(range, newParent) { + try { + // "If a non-Text node is partially contained in the context object, + // throw a "InvalidStateError" exception and terminate these steps." + var node = range.commonAncestorContainer; + var stop = nextNodeDescendants(node); + for (; node != stop; node = nextNode(node)) { + if (node.nodeType != Node.TEXT_NODE + && isPartiallyContained(node, range)) { + return "INVALID_STATE_ERR"; + } + } + + // "If newParent is a Document, DocumentType, or DocumentFragment node, + // throw an "InvalidNodeTypeError" exception and terminate these + // steps." + if (newParent.nodeType == Node.DOCUMENT_NODE + || newParent.nodeType == Node.DOCUMENT_TYPE_NODE + || newParent.nodeType == Node.DOCUMENT_FRAGMENT_NODE) { + return "INVALID_NODE_TYPE_ERR"; + } + + // "Call extractContents() on the context object, and let fragment be + // the result." + var fragment = myExtractContents(range); + if (typeof fragment == "string") { + return fragment; + } + + // "While newParent has children, remove its first child." + while (newParent.childNodes.length) { + newParent.removeChild(newParent.firstChild); + } + + // "Call insertNode(newParent) on the context object." + var ret = myInsertNode(range, newParent); + if (typeof ret == "string") { + return ret; + } + + // "Call appendChild(fragment) on newParent." + newParent.appendChild(fragment); + + // "Call selectNode(newParent) on the context object." + // + // We just reimplement this in-place. + if (!newParent.parentNode) { + return "INVALID_NODE_TYPE_ERR"; + } + var index = indexOf(newParent); + range.setStart(newParent.parentNode, index); + range.setEnd(newParent.parentNode, index + 1); + } catch (e) { + return getDomExceptionName(e); + } +} + +function restoreIframe(iframe, i, j) { + // Most of this function is designed to work around the fact that Opera + // doesn't let you add a doctype to a document that no longer has one, in + // any way I can figure out. I eventually compromised on something that + // will still let Opera pass most tests that don't actually involve + // doctypes. + while (iframe.contentDocument.firstChild + && iframe.contentDocument.firstChild.nodeType != Node.DOCUMENT_TYPE_NODE) { + iframe.contentDocument.removeChild(iframe.contentDocument.firstChild); + } + + while (iframe.contentDocument.lastChild + && iframe.contentDocument.lastChild.nodeType != Node.DOCUMENT_TYPE_NODE) { + iframe.contentDocument.removeChild(iframe.contentDocument.lastChild); + } + + if (!iframe.contentDocument.firstChild) { + // This will throw an exception in Opera if we reach here, which is why + // I try to avoid it. It will never happen in a browser that obeys the + // spec, so it's really just insurance. I don't think it actually gets + // hit by anything. + iframe.contentDocument.appendChild(iframe.contentDocument.implementation.createDocumentType("html", "", "")); + } + iframe.contentDocument.appendChild(referenceDoc.documentElement.cloneNode(true)); + iframe.contentWindow.setupRangeTests(); + iframe.contentWindow.testRangeInput = testRangesShort[i]; + iframe.contentWindow.testNodeInput = testNodesShort[j]; + iframe.contentWindow.run(); +} + +function testSurroundContents(i, j) { + var actualRange; + var expectedRange; + var actualNode; + var expectedNode; + var actualRoots = []; + var expectedRoots = []; + + domTests[i][j].step(function() { + restoreIframe(actualIframe, i, j); + restoreIframe(expectedIframe, i, j); + + actualRange = actualIframe.contentWindow.testRange; + expectedRange = expectedIframe.contentWindow.testRange; + actualNode = actualIframe.contentWindow.testNode; + expectedNode = expectedIframe.contentWindow.testNode; + + assert_equals(actualIframe.contentWindow.unexpectedException, null, + "Unexpected exception thrown when setting up Range for actual surroundContents()"); + assert_equals(expectedIframe.contentWindow.unexpectedException, null, + "Unexpected exception thrown when setting up Range for simulated surroundContents()"); + assert_equals(typeof actualRange, "object", + "typeof Range produced in actual iframe"); + assert_false(actualRange === null, + "Range produced in actual iframe was null"); + assert_equals(typeof expectedRange, "object", + "typeof Range produced in expected iframe"); + assert_false(expectedRange === null, + "Range produced in expected iframe was null"); + assert_equals(typeof actualNode, "object", + "typeof Node produced in actual iframe"); + assert_false(actualNode === null, + "Node produced in actual iframe was null"); + assert_equals(typeof expectedNode, "object", + "typeof Node produced in expected iframe"); + assert_false(expectedNode === null, + "Node produced in expected iframe was null"); + + // We want to test that the trees containing the ranges are equal, and + // also the trees containing the moved nodes. These might not be the + // same, if we're inserting a node from a detached tree or a different + // document. + actualRoots.push(furthestAncestor(actualRange.startContainer)); + expectedRoots.push(furthestAncestor(expectedRange.startContainer)); + + if (furthestAncestor(actualNode) != actualRoots[0]) { + actualRoots.push(furthestAncestor(actualNode)); + } + if (furthestAncestor(expectedNode) != expectedRoots[0]) { + expectedRoots.push(furthestAncestor(expectedNode)); + } + + assert_equals(actualRoots.length, expectedRoots.length, + "Either the actual node and actual range are in the same tree but the expected are in different trees, or vice versa"); + + // This doctype stuff is to work around the fact that Opera 11.00 will + // move around doctypes within a document, even to totally invalid + // positions, but it won't allow a new doctype to be added to a + // document in any way I can figure out. So if we try moving a doctype + // to some invalid place, in Opera it will actually succeed, and then + // restoreIframe() will remove the doctype along with the root element, + // and then nothing can re-add the doctype. So instead, we catch it + // during the test itself and move it back to the right place while we + // still can. + // + // I spent *way* too much time debugging and working around this bug. + var actualDoctype = actualIframe.contentDocument.doctype; + var expectedDoctype = expectedIframe.contentDocument.doctype; + + var result; + try { + result = mySurroundContents(expectedRange, expectedNode); + } catch (e) { + if (expectedDoctype != expectedIframe.contentDocument.firstChild) { + expectedIframe.contentDocument.insertBefore(expectedDoctype, expectedIframe.contentDocument.firstChild); + } + throw e; + } + if (typeof result == "string") { + assert_throws(result, function() { + try { + actualRange.surroundContents(actualNode); + } catch (e) { + if (expectedDoctype != expectedIframe.contentDocument.firstChild) { + expectedIframe.contentDocument.insertBefore(expectedDoctype, expectedIframe.contentDocument.firstChild); + } + if (actualDoctype != actualIframe.contentDocument.firstChild) { + actualIframe.contentDocument.insertBefore(actualDoctype, actualIframe.contentDocument.firstChild); + } + throw e; + } + }, "A " + result + " must be thrown in this case"); + // Don't return, we still need to test DOM equality + } else { + try { + actualRange.surroundContents(actualNode); + } catch (e) { + if (expectedDoctype != expectedIframe.contentDocument.firstChild) { + expectedIframe.contentDocument.insertBefore(expectedDoctype, expectedIframe.contentDocument.firstChild); + } + if (actualDoctype != actualIframe.contentDocument.firstChild) { + actualIframe.contentDocument.insertBefore(actualDoctype, actualIframe.contentDocument.firstChild); + } + throw e; + } + } + + for (var k = 0; k < actualRoots.length; k++) { + assertNodesEqual(actualRoots[k], expectedRoots[k], k ? "moved node's tree root" : "range's tree root"); + } + }); + domTests[i][j].done(); + + positionTests[i][j].step(function() { + assert_equals(actualIframe.contentWindow.unexpectedException, null, + "Unexpected exception thrown when setting up Range for actual surroundContents()"); + assert_equals(expectedIframe.contentWindow.unexpectedException, null, + "Unexpected exception thrown when setting up Range for simulated surroundContents()"); + assert_equals(typeof actualRange, "object", + "typeof Range produced in actual iframe"); + assert_false(actualRange === null, + "Range produced in actual iframe was null"); + assert_equals(typeof expectedRange, "object", + "typeof Range produced in expected iframe"); + assert_false(expectedRange === null, + "Range produced in expected iframe was null"); + assert_equals(typeof actualNode, "object", + "typeof Node produced in actual iframe"); + assert_false(actualNode === null, + "Node produced in actual iframe was null"); + assert_equals(typeof expectedNode, "object", + "typeof Node produced in expected iframe"); + assert_false(expectedNode === null, + "Node produced in expected iframe was null"); + + for (var k = 0; k < actualRoots.length; k++) { + assertNodesEqual(actualRoots[k], expectedRoots[k], k ? "moved node's tree root" : "range's tree root"); + } + + assert_equals(actualRange.startOffset, expectedRange.startOffset, + "Unexpected startOffset after surroundContents()"); + assert_equals(actualRange.endOffset, expectedRange.endOffset, + "Unexpected endOffset after surroundContents()"); + // How do we decide that the two nodes are equal, since they're in + // different trees? Since the DOMs are the same, it's enough to check + // that the index in the parent is the same all the way up the tree. + // But we can first cheat by just checking they're actually equal. + assert_true(actualRange.startContainer.isEqualNode(expectedRange.startContainer), + "Unexpected startContainer after surroundContents(), expected " + + expectedRange.startContainer.nodeName.toLowerCase() + " but got " + + actualRange.startContainer.nodeName.toLowerCase()); + var currentActual = actualRange.startContainer; + var currentExpected = expectedRange.startContainer; + var actual = ""; + var expected = ""; + while (currentActual && currentExpected) { + actual = indexOf(currentActual) + "-" + actual; + expected = indexOf(currentExpected) + "-" + expected; + + currentActual = currentActual.parentNode; + currentExpected = currentExpected.parentNode; + } + actual = actual.substr(0, actual.length - 1); + expected = expected.substr(0, expected.length - 1); + assert_equals(actual, expected, + "startContainer superficially looks right but is actually the wrong node if you trace back its index in all its ancestors (I'm surprised this actually happened"); + }); + positionTests[i][j].done(); +} + +var iStart = 0; +var iStop = testRangesShort.length; +var jStart = 0; +var jStop = testNodesShort.length; + +if (/subtest=[0-9]+,[0-9]+/.test(location.search)) { + var matches = /subtest=([0-9]+),([0-9]+)/.exec(location.search); + iStart = Number(matches[1]); + iStop = Number(matches[1]) + 1; + jStart = Number(matches[2]) + 0; + jStop = Number(matches[2]) + 1; +} + +var domTests = []; +var positionTests = []; +for (var i = iStart; i < iStop; i++) { + domTests[i] = []; + positionTests[i] = []; + for (var j = jStart; j < jStop; j++) { + domTests[i][j] = async_test(i + "," + j + ": resulting DOM for range " + testRangesShort[i] + ", node " + testNodesShort[j]); + positionTests[i][j] = async_test(i + "," + j + ": resulting range position for range " + testRangesShort[i] + ", node " + testNodesShort[j]); + } +} + +var actualIframe = document.createElement("iframe"); +actualIframe.style.display = "none"; +actualIframe.id = "actual"; +document.body.appendChild(actualIframe); + +var expectedIframe = document.createElement("iframe"); +expectedIframe.style.display = "none"; +expectedIframe.id = "expected"; +document.body.appendChild(expectedIframe); + +var referenceDoc = document.implementation.createHTMLDocument(""); +referenceDoc.removeChild(referenceDoc.documentElement); + +actualIframe.onload = function() { + expectedIframe.onload = function() { + for (var i = iStart; i < iStop; i++) { + for (var j = jStart; j < jStop; j++) { + testSurroundContents(i, j); + } + } + } + expectedIframe.src = "Range-test-iframe.html"; + referenceDoc.appendChild(actualIframe.contentDocument.documentElement.cloneNode(true)); +} +actualIframe.src = "Range-test-iframe.html"; +</script> |