mirror of
https://github.com/servo/servo.git
synced 2025-08-13 01:15:34 +01:00
Update web-platform-tests to revision 0d318188757a9c996e20b82db201fd04de5aa255
This commit is contained in:
parent
b2a5225831
commit
1a81b18b9f
12321 changed files with 544385 additions and 6 deletions
|
@ -0,0 +1,331 @@
|
|||
<!doctype html>
|
||||
<title>Range.deleteContents() 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"). Only that test will be run. Then you can look at the resulting
|
||||
iframe 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);
|
||||
|
||||
var actualIframe = document.createElement("iframe");
|
||||
actualIframe.style.display = "none";
|
||||
document.body.appendChild(actualIframe);
|
||||
|
||||
var expectedIframe = document.createElement("iframe");
|
||||
expectedIframe.style.display = "none";
|
||||
document.body.appendChild(expectedIframe);
|
||||
|
||||
function restoreIframe(iframe, i) {
|
||||
// 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 = testRanges[i];
|
||||
iframe.contentWindow.run();
|
||||
}
|
||||
|
||||
function myDeleteContents(range) {
|
||||
// "If the context object's start and end are the same, abort this method."
|
||||
if (range.startContainer == range.endContainer
|
||||
&& range.startOffset == range.endOffset) {
|
||||
return;
|
||||
}
|
||||
|
||||
// "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, call deleteData(original start offset, original
|
||||
// end offset − original start offset) on that node, and abort these
|
||||
// steps."
|
||||
if (originalStartNode == originalEndNode
|
||||
&& (range.startContainer.nodeType == Node.TEXT_NODE
|
||||
|| range.startContainer.nodeType == Node.COMMENT_NODE)) {
|
||||
originalStartNode.deleteData(originalStartOffset, originalEndOffset - originalStartOffset);
|
||||
return;
|
||||
}
|
||||
|
||||
// "Let nodes to remove be a list of all the Nodes that are contained in
|
||||
// the context object, in tree order, omitting any Node whose parent is
|
||||
// also contained in the context object."
|
||||
//
|
||||
// We rely on the fact that the contained nodes must lie in tree order
|
||||
// between the start node, and the end node's last descendant (inclusive).
|
||||
var nodesToRemove = [];
|
||||
var stop = nextNodeDescendants(range.endContainer);
|
||||
for (var node = range.startContainer; node != stop; node = nextNode(node)) {
|
||||
if (isContained(node, range)
|
||||
&& !(node.parentNode && isContained(node.parentNode, range))) {
|
||||
nodesToRemove.push(node);
|
||||
}
|
||||
}
|
||||
|
||||
// "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;
|
||||
var newOffset;
|
||||
if (originalStartNode == originalEndNode
|
||||
|| originalEndNode.compareDocumentPosition(originalStartNode) & Node.DOCUMENT_POSITION_CONTAINS) {
|
||||
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
|
||||
&& referenceNode.parentNode != originalEndNode
|
||||
&& !(originalEndNode.compareDocumentPosition(referenceNode.parentNode) & Node.DOCUMENT_POSITION_CONTAINS)) {
|
||||
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 original start node is a Text or Comment node, run deleteData() on
|
||||
// it, with original start offset as the first argument and (length of
|
||||
// original start node − original start offset) as the second."
|
||||
if (originalStartNode.nodeType == Node.TEXT_NODE
|
||||
|| originalStartNode.nodeType == Node.COMMENT_NODE) {
|
||||
originalStartNode.deleteData(originalStartOffset, nodeLength(originalStartNode) - originalStartOffset);
|
||||
}
|
||||
|
||||
// "For each node in nodes to remove, in order, remove node from its
|
||||
// parent."
|
||||
for (var i = 0; i < nodesToRemove.length; i++) {
|
||||
nodesToRemove[i].parentNode.removeChild(nodesToRemove[i]);
|
||||
}
|
||||
|
||||
// "If original end node is a Text or Comment node, run deleteData(0,
|
||||
// original end offset) on it."
|
||||
if (originalEndNode.nodeType == Node.TEXT_NODE
|
||||
|| originalEndNode.nodeType == Node.COMMENT_NODE) {
|
||||
originalEndNode.deleteData(0, originalEndOffset);
|
||||
}
|
||||
|
||||
// "Set the context object's start and end to (new node, new offset)."
|
||||
range.setStart(newNode, newOffset);
|
||||
range.setEnd(newNode, newOffset);
|
||||
}
|
||||
|
||||
function testDeleteContents(i) {
|
||||
restoreIframe(actualIframe, i);
|
||||
restoreIframe(expectedIframe, i);
|
||||
|
||||
var actualRange = actualIframe.contentWindow.testRange;
|
||||
var expectedRange = expectedIframe.contentWindow.testRange;
|
||||
var actualRoots, expectedRoots;
|
||||
|
||||
domTests[i].step(function() {
|
||||
assert_equals(actualIframe.contentWindow.unexpectedException, null,
|
||||
"Unexpected exception thrown when setting up Range for actual deleteContents()");
|
||||
assert_equals(expectedIframe.contentWindow.unexpectedException, null,
|
||||
"Unexpected exception thrown when setting up Range for simulated deleteContents()");
|
||||
assert_equals(typeof actualRange, "object",
|
||||
"typeof Range produced in actual iframe");
|
||||
assert_equals(typeof expectedRange, "object",
|
||||
"typeof Range produced in expected iframe");
|
||||
|
||||
// Just to be pedantic, we'll test not only that the tree we're
|
||||
// modifying is the same in expected vs. actual, but also that all the
|
||||
// nodes originally in it were the same. Typically some nodes will
|
||||
// become detached when the algorithm is run, but they still exist and
|
||||
// references can still be kept to them, so they should also remain the
|
||||
// same.
|
||||
//
|
||||
// We initialize the list to all nodes, and later on remove all the
|
||||
// ones which still have parents, since the parents will presumably be
|
||||
// tested for isEqualNode() and checking the children would be
|
||||
// redundant.
|
||||
var actualAllNodes = [];
|
||||
var node = furthestAncestor(actualRange.startContainer);
|
||||
do {
|
||||
actualAllNodes.push(node);
|
||||
} while (node = nextNode(node));
|
||||
|
||||
var expectedAllNodes = [];
|
||||
var node = furthestAncestor(expectedRange.startContainer);
|
||||
do {
|
||||
expectedAllNodes.push(node);
|
||||
} while (node = nextNode(node));
|
||||
|
||||
actualRange.deleteContents();
|
||||
myDeleteContents(expectedRange);
|
||||
|
||||
actualRoots = [];
|
||||
for (var j = 0; j < actualAllNodes.length; j++) {
|
||||
if (!actualAllNodes[j].parentNode) {
|
||||
actualRoots.push(actualAllNodes[j]);
|
||||
}
|
||||
}
|
||||
|
||||
expectedRoots = [];
|
||||
for (var j = 0; j < expectedAllNodes.length; j++) {
|
||||
if (!expectedAllNodes[j].parentNode) {
|
||||
expectedRoots.push(expectedAllNodes[j]);
|
||||
}
|
||||
}
|
||||
|
||||
for (var j = 0; j < actualRoots.length; j++) {
|
||||
if (!actualRoots[j].isEqualNode(expectedRoots[j])) {
|
||||
var msg = j ? "detached node #" + j : "tree root";
|
||||
msg = "Actual and expected mismatch for " + msg + ". ";
|
||||
|
||||
// Find the specific error
|
||||
var actual = actualRoots[j];
|
||||
var expected = expectedRoots[j];
|
||||
|
||||
while (actual && expected) {
|
||||
assert_equals(actual.nodeType, expected.nodeType,
|
||||
msg + "First difference: differing nodeType");
|
||||
assert_equals(actual.nodeName, expected.nodeName,
|
||||
msg + "First difference: differing nodeName");
|
||||
assert_equals(actual.nodeValue, expected.nodeValue,
|
||||
msg + 'First difference: differing nodeValue (nodeName = "' + actual.nodeName + '")');
|
||||
assert_equals(actual.childNodes.length, expected.childNodes.length,
|
||||
msg + 'First difference: differing number of children (nodeName = "' + actual.nodeName + '")');
|
||||
actual = nextNode(actual);
|
||||
expected = nextNode(expected);
|
||||
}
|
||||
|
||||
assert_unreached("DOMs were not equal but we couldn't figure out why");
|
||||
}
|
||||
|
||||
if (j == 0) {
|
||||
// Clearly something is wrong if the node lists are different
|
||||
// lengths. We want to report this only after we've already
|
||||
// checked the main tree for equality, though, so it doesn't
|
||||
// mask more interesting errors.
|
||||
assert_equals(actualRoots.length, expectedRoots.length,
|
||||
"Actual and expected DOMs were broken up into a different number of pieces by deleteContents() (this probably means you created or detached nodes when you weren't supposed to)");
|
||||
}
|
||||
}
|
||||
});
|
||||
domTests[i].done();
|
||||
|
||||
positionTests[i].step(function() {
|
||||
assert_equals(actualIframe.contentWindow.unexpectedException, null,
|
||||
"Unexpected exception thrown when setting up Range for actual deleteContents()");
|
||||
assert_equals(expectedIframe.contentWindow.unexpectedException, null,
|
||||
"Unexpected exception thrown when setting up Range for simulated deleteContents()");
|
||||
assert_equals(typeof actualRange, "object",
|
||||
"typeof Range produced in actual iframe");
|
||||
assert_equals(typeof expectedRange, "object",
|
||||
"typeof Range produced in expected iframe");
|
||||
assert_true(actualRoots[0].isEqualNode(expectedRoots[0]),
|
||||
"The resulting DOMs were not equal, so comparing positions makes no sense");
|
||||
|
||||
assert_equals(actualRange.startContainer, actualRange.endContainer,
|
||||
"startContainer and endContainer must always be the same after deleteContents()");
|
||||
assert_equals(actualRange.startOffset, actualRange.endOffset,
|
||||
"startOffset and endOffset must always be the same after deleteContents()");
|
||||
assert_equals(expectedRange.startContainer, expectedRange.endContainer,
|
||||
"Test bug! Expected startContainer and endContainer must always be the same after deleteContents()");
|
||||
assert_equals(expectedRange.startOffset, expectedRange.endOffset,
|
||||
"Test bug! Expected startOffset and endOffset must always be the same after deleteContents()");
|
||||
|
||||
assert_equals(actualRange.startOffset, expectedRange.startOffset,
|
||||
"Unexpected startOffset after deleteContents()");
|
||||
// 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 deleteContents(), 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].done();
|
||||
}
|
||||
|
||||
// First test a detached Range, synchronously
|
||||
test(function() {
|
||||
var range = document.createRange();
|
||||
range.detach();
|
||||
range.deleteContents();
|
||||
}, "Detached Range");
|
||||
|
||||
var iStart = 0;
|
||||
var iStop = testRanges.length;
|
||||
|
||||
if (/subtest=[0-9]+/.test(location.search)) {
|
||||
var matches = /subtest=([0-9]+)/.exec(location.search);
|
||||
iStart = Number(matches[1]);
|
||||
iStop = Number(matches[1]) + 1;
|
||||
}
|
||||
|
||||
var domTests = [];
|
||||
var positionTests = [];
|
||||
|
||||
for (var i = iStart; i < iStop; i++) {
|
||||
domTests[i] = async_test("Resulting DOM for range " + i + " " + testRanges[i]);
|
||||
positionTests[i] = async_test("Resulting cursor position for range " + i + " " + testRanges[i]);
|
||||
}
|
||||
|
||||
var referenceDoc = document.implementation.createHTMLDocument("");
|
||||
referenceDoc.removeChild(referenceDoc.documentElement);
|
||||
|
||||
actualIframe.onload = function() {
|
||||
expectedIframe.onload = function() {
|
||||
for (var i = iStart; i < iStop; i++) {
|
||||
testDeleteContents(i);
|
||||
}
|
||||
}
|
||||
expectedIframe.src = "Range-test-iframe.html";
|
||||
referenceDoc.appendChild(actualIframe.contentDocument.documentElement.cloneNode(true));
|
||||
}
|
||||
actualIframe.src = "Range-test-iframe.html";
|
||||
</script>
|
Loading…
Add table
Add a link
Reference in a new issue