mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-09-03 08:08:43 +00:00
LibWeb: Don't neglect DOM range updates on CharacterData changes
Regressed in 036327332f
.
This commit moves the optimization a little later in replaceData(),
still avoiding relayout (the important part).
Recovers 480 points on WPT. :^)
This commit is contained in:
parent
4396dbb88e
commit
d8f95c5050
Notes:
github-actions[bot]
2025-02-21 10:54:57 +00:00
Author: https://github.com/awesomekling
Commit: d8f95c5050
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3652
4 changed files with 3757 additions and 5 deletions
|
@ -0,0 +1,14 @@
|
|||
<!doctype html>
|
||||
<title>Range mutation tests - update data by IDL attributes</title>
|
||||
<link rel="author" title="Aryeh Gregor" href=ayg@aryeh.name>
|
||||
<meta name=timeout content=long>
|
||||
|
||||
<div id=log></div>
|
||||
<script src="../../resources/testharness.js"></script>
|
||||
<script src="../../resources/testharnessreport.js"></script>
|
||||
<script src="../common.js"></script>
|
||||
<script src="Range-mutations.js"></script>
|
||||
<script>
|
||||
doTests(dataChangeTests, function(params) { return params[0] + "." + eval(params[1]) + " " + eval(params[2]) + ' ' + params[3] }, testDataChange);
|
||||
testDiv.style.display = "none";
|
||||
</script>
|
921
Tests/LibWeb/Text/input/wpt-import/dom/ranges/Range-mutations.js
Normal file
921
Tests/LibWeb/Text/input/wpt-import/dom/ranges/Range-mutations.js
Normal file
|
@ -0,0 +1,921 @@
|
|||
"use strict";
|
||||
|
||||
// These tests probably use too much abstraction and too little copy-paste.
|
||||
// Reader beware.
|
||||
//
|
||||
// TODO:
|
||||
//
|
||||
// * Lots and lots and lots more different types of ranges
|
||||
// * insertBefore() with DocumentFragments
|
||||
// * Fill out other insert/remove tests
|
||||
// * normalize() (https://www.w3.org/Bugs/Public/show_bug.cgi?id=13843)
|
||||
|
||||
// Give a textual description of the range we're testing, for the test names.
|
||||
function describeRange(startContainer, startOffset, endContainer, endOffset) {
|
||||
if (startContainer == endContainer && startOffset == endOffset) {
|
||||
return "range collapsed at (" + startContainer + ", " + startOffset + ")";
|
||||
} else if (startContainer == endContainer) {
|
||||
return "range on " + startContainer + " from " + startOffset + " to " + endOffset;
|
||||
} else {
|
||||
return "range from (" + startContainer + ", " + startOffset + ") to (" + endContainer + ", " + endOffset + ")";
|
||||
}
|
||||
}
|
||||
|
||||
// Lists of the various types of nodes we'll want to use. We use strings that
|
||||
// we can later eval(), so that we can produce legible test names.
|
||||
var textNodes = [
|
||||
"paras[0].firstChild",
|
||||
"paras[1].firstChild",
|
||||
"foreignTextNode",
|
||||
"xmlTextNode",
|
||||
"detachedTextNode",
|
||||
"detachedForeignTextNode",
|
||||
"detachedXmlTextNode",
|
||||
];
|
||||
var commentNodes = [
|
||||
"comment",
|
||||
"foreignComment",
|
||||
"xmlComment",
|
||||
"detachedComment",
|
||||
"detachedForeignComment",
|
||||
"detachedXmlComment",
|
||||
];
|
||||
var characterDataNodes = textNodes.concat(commentNodes);
|
||||
|
||||
// This function is slightly scary, but it works well enough, so . . .
|
||||
// sourceTests is an array of test data that will be altered in mysterious ways
|
||||
// before being passed off to doTest, descFn is something that takes an element
|
||||
// of sourceTests and produces the first part of a human-readable description
|
||||
// of the test, testFn is the function that doTest will call to do the actual
|
||||
// work and tell it what results to expect.
|
||||
function doTests(sourceTests, descFn, testFn) {
|
||||
var tests = [];
|
||||
for (var i = 0; i < sourceTests.length; i++) {
|
||||
var params = sourceTests[i];
|
||||
var len = params.length;
|
||||
tests.push([
|
||||
descFn(params) + ", with unselected " + describeRange(params[len - 4], params[len - 3], params[len - 2], params[len - 1]),
|
||||
// The closure here ensures that the params that testFn get are the
|
||||
// current version of params, not the version from the last
|
||||
// iteration of this loop. We test that none of the parameters
|
||||
// evaluate to undefined to catch bugs in our eval'ing, like
|
||||
// mistyping a property name.
|
||||
function(params) { return function() {
|
||||
var evaledParams = params.map(eval);
|
||||
for (var i = 0; i < evaledParams.length; i++) {
|
||||
assert_not_equals(typeof evaledParams[i], "undefined",
|
||||
"Test bug: " + params[i] + " is undefined");
|
||||
}
|
||||
return testFn.apply(null, evaledParams);
|
||||
} }(params),
|
||||
false,
|
||||
params[len - 4],
|
||||
params[len - 3],
|
||||
params[len - 2],
|
||||
params[len - 1]
|
||||
]);
|
||||
tests.push([
|
||||
descFn(params) + ", with selected " + describeRange(params[len - 4], params[len - 3], params[len - 2], params[len - 1]),
|
||||
function(params) { return function(selectedRange) {
|
||||
var evaledParams = params.slice(0, len - 4).map(eval);
|
||||
for (var i = 0; i < evaledParams.length; i++) {
|
||||
assert_not_equals(typeof evaledParams[i], "undefined",
|
||||
"Test bug: " + params[i] + " is undefined");
|
||||
}
|
||||
// Override input range with the one that was actually selected when computing the expected result.
|
||||
evaledParams = evaledParams.concat([selectedRange.startContainer, selectedRange.startOffset, selectedRange.endContainer, selectedRange.endOffset]);
|
||||
return testFn.apply(null, evaledParams);
|
||||
} }(params),
|
||||
true,
|
||||
params[len - 4],
|
||||
params[len - 3],
|
||||
params[len - 2],
|
||||
params[len - 1]
|
||||
]);
|
||||
}
|
||||
generate_tests(doTest, tests);
|
||||
}
|
||||
|
||||
// Set up the range, call the callback function to do the DOM modification and
|
||||
// tell us what to expect. The callback function needs to return a
|
||||
// four-element array with the expected start/end containers/offsets, and
|
||||
// receives no arguments. useSelection tells us whether the Range should be
|
||||
// added to a Selection and the Selection tested to ensure that the mutation
|
||||
// affects user selections as well as other ranges; every test is run with this
|
||||
// both false and true, because when it's set to true WebKit and Opera fail all
|
||||
// tests' sanity checks, which is unhelpful. The last four parameters just
|
||||
// tell us what range to build.
|
||||
function doTest(callback, useSelection, startContainer, startOffset, endContainer, endOffset) {
|
||||
// Recreate all the test nodes in case they were altered by the last test
|
||||
// run.
|
||||
setupRangeTests();
|
||||
startContainer = eval(startContainer);
|
||||
startOffset = eval(startOffset);
|
||||
endContainer = eval(endContainer);
|
||||
endOffset = eval(endOffset);
|
||||
|
||||
var ownerDoc = startContainer.nodeType == Node.DOCUMENT_NODE
|
||||
? startContainer
|
||||
: startContainer.ownerDocument;
|
||||
var range = ownerDoc.createRange();
|
||||
range.setStart(startContainer, startOffset);
|
||||
range.setEnd(endContainer, endOffset);
|
||||
|
||||
if (useSelection) {
|
||||
getSelection().removeAllRanges();
|
||||
getSelection().addRange(range);
|
||||
|
||||
// Some browsers refuse to add a range unless it results in an actual visible selection.
|
||||
if (!getSelection().rangeCount)
|
||||
return;
|
||||
|
||||
// Override range with the one that was actually selected as it differs in some browsers.
|
||||
range = getSelection().getRangeAt(0);
|
||||
}
|
||||
|
||||
var expected = callback(range);
|
||||
|
||||
assert_equals(range.startContainer, expected[0],
|
||||
"Wrong start container");
|
||||
assert_equals(range.startOffset, expected[1],
|
||||
"Wrong start offset");
|
||||
assert_equals(range.endContainer, expected[2],
|
||||
"Wrong end container");
|
||||
assert_equals(range.endOffset, expected[3],
|
||||
"Wrong end offset");
|
||||
}
|
||||
|
||||
|
||||
// Now we get to the specific tests.
|
||||
|
||||
function testSplitText(oldNode, offset, startContainer, startOffset, endContainer, endOffset) {
|
||||
// Save these for later
|
||||
var originalStartOffset = startOffset;
|
||||
var originalEndOffset = endOffset;
|
||||
var originalLength = oldNode.length;
|
||||
|
||||
var newNode;
|
||||
try {
|
||||
newNode = oldNode.splitText(offset);
|
||||
} catch (e) {
|
||||
// Should only happen if offset is negative
|
||||
return [startContainer, startOffset, endContainer, endOffset];
|
||||
}
|
||||
|
||||
// First we adjust for replacing data:
|
||||
//
|
||||
// "Replace data with offset offset, count count, and data the empty
|
||||
// string."
|
||||
//
|
||||
// That translates to offset = offset, count = originalLength - offset,
|
||||
// data = "". node is oldNode.
|
||||
//
|
||||
// "For every boundary point whose node is node, and whose offset is
|
||||
// greater than offset but less than or equal to offset plus count, set its
|
||||
// offset to offset."
|
||||
if (startContainer == oldNode
|
||||
&& startOffset > offset
|
||||
&& startOffset <= originalLength) {
|
||||
startOffset = offset;
|
||||
}
|
||||
|
||||
if (endContainer == oldNode
|
||||
&& endOffset > offset
|
||||
&& endOffset <= originalLength) {
|
||||
endOffset = offset;
|
||||
}
|
||||
|
||||
// "For every boundary point whose node is node, and whose offset is
|
||||
// greater than offset plus count, add the length of data to its offset,
|
||||
// then subtract count from it."
|
||||
//
|
||||
// Can't happen: offset plus count is originalLength.
|
||||
|
||||
// Now we insert a node, if oldNode's parent isn't null: "For each boundary
|
||||
// point whose node is the new parent of the affected node and whose offset
|
||||
// is greater than the new index of the affected node, add one to the
|
||||
// boundary point's offset."
|
||||
if (startContainer == oldNode.parentNode
|
||||
&& startOffset > 1 + indexOf(oldNode)) {
|
||||
startOffset++;
|
||||
}
|
||||
|
||||
if (endContainer == oldNode.parentNode
|
||||
&& endOffset > 1 + indexOf(oldNode)) {
|
||||
endOffset++;
|
||||
}
|
||||
|
||||
// Finally, the splitText stuff itself:
|
||||
//
|
||||
// "If parent is not null, run these substeps:
|
||||
//
|
||||
// * "For each range whose start node is node and start offset is greater
|
||||
// than offset, set its start node to new node and decrease its start
|
||||
// offset by offset.
|
||||
//
|
||||
// * "For each range whose end node is node and end offset is greater
|
||||
// than offset, set its end node to new node and decrease its end offset
|
||||
// by offset.
|
||||
//
|
||||
// * "For each range whose start node is parent and start offset is equal
|
||||
// to the index of node + 1, increase its start offset by one.
|
||||
//
|
||||
// * "For each range whose end node is parent and end offset is equal to
|
||||
// the index of node + 1, increase its end offset by one."
|
||||
if (oldNode.parentNode) {
|
||||
if (startContainer == oldNode && originalStartOffset > offset) {
|
||||
startContainer = newNode;
|
||||
startOffset = originalStartOffset - offset;
|
||||
}
|
||||
|
||||
if (endContainer == oldNode && originalEndOffset > offset) {
|
||||
endContainer = newNode;
|
||||
endOffset = originalEndOffset - offset;
|
||||
}
|
||||
|
||||
if (startContainer == oldNode.parentNode
|
||||
&& startOffset == 1 + indexOf(oldNode)) {
|
||||
startOffset++;
|
||||
}
|
||||
|
||||
if (endContainer == oldNode.parentNode
|
||||
&& endOffset == 1 + indexOf(oldNode)) {
|
||||
endOffset++;
|
||||
}
|
||||
}
|
||||
|
||||
return [startContainer, startOffset, endContainer, endOffset];
|
||||
}
|
||||
|
||||
// The offset argument is unsigned, so per WebIDL -1 should wrap to 4294967295,
|
||||
// which is probably longer than the length, so it should throw an exception.
|
||||
// This is no different from the other cases where the offset is longer than
|
||||
// the length, and the wrapping complicates my testing slightly, so I won't
|
||||
// bother testing negative values here or in other cases.
|
||||
var splitTextTests = [];
|
||||
for (var i = 0; i < textNodes.length; i++) {
|
||||
var node = textNodes[i];
|
||||
splitTextTests.push([node, 376, node, 0, node, 1]);
|
||||
splitTextTests.push([node, 0, node, 0, node, 0]);
|
||||
splitTextTests.push([node, 1, node, 1, node, 1]);
|
||||
splitTextTests.push([node, node + ".length", node, node + ".length", node, node + ".length"]);
|
||||
splitTextTests.push([node, 1, node, 1, node, 3]);
|
||||
splitTextTests.push([node, 2, node, 1, node, 3]);
|
||||
splitTextTests.push([node, 3, node, 1, node, 3]);
|
||||
}
|
||||
|
||||
splitTextTests.push(
|
||||
["paras[0].firstChild", 1, "paras[0]", 0, "paras[0]", 0],
|
||||
["paras[0].firstChild", 1, "paras[0]", 0, "paras[0]", 1],
|
||||
["paras[0].firstChild", 1, "paras[0]", 1, "paras[0]", 1],
|
||||
["paras[0].firstChild", 1, "paras[0].firstChild", 1, "paras[0]", 1],
|
||||
["paras[0].firstChild", 2, "paras[0].firstChild", 1, "paras[0]", 1],
|
||||
["paras[0].firstChild", 3, "paras[0].firstChild", 1, "paras[0]", 1],
|
||||
["paras[0].firstChild", 1, "paras[0]", 0, "paras[0].firstChild", 3],
|
||||
["paras[0].firstChild", 2, "paras[0]", 0, "paras[0].firstChild", 3],
|
||||
["paras[0].firstChild", 3, "paras[0]", 0, "paras[0].firstChild", 3]
|
||||
);
|
||||
|
||||
|
||||
function testReplaceDataAlgorithm(node, offset, count, data, callback, startContainer, startOffset, endContainer, endOffset) {
|
||||
// Mutation works the same any time DOM Core's "replace data" algorithm is
|
||||
// invoked. node, offset, count, data are as in that algorithm. The
|
||||
// callback is what does the actual setting. Not to be confused with
|
||||
// testReplaceData, which tests the replaceData() method.
|
||||
|
||||
// Barring any provision to the contrary, the containers and offsets must
|
||||
// not change.
|
||||
var expectedStartContainer = startContainer;
|
||||
var expectedStartOffset = startOffset;
|
||||
var expectedEndContainer = endContainer;
|
||||
var expectedEndOffset = endOffset;
|
||||
|
||||
var originalParent = node.parentNode;
|
||||
var originalData = node.data;
|
||||
|
||||
var exceptionThrown = false;
|
||||
try {
|
||||
callback();
|
||||
} catch (e) {
|
||||
// Should only happen if offset is greater than length
|
||||
exceptionThrown = true;
|
||||
}
|
||||
|
||||
assert_equals(node.parentNode, originalParent,
|
||||
"Sanity check failed: changing data changed the parent");
|
||||
|
||||
// "User agents must run the following steps whenever they replace data of
|
||||
// a CharacterData node, as though they were written in the specification
|
||||
// for that algorithm after all other steps. In particular, the steps must
|
||||
// not be executed if the algorithm threw an exception."
|
||||
if (exceptionThrown) {
|
||||
assert_equals(node.data, originalData,
|
||||
"Sanity check failed: exception thrown but data changed");
|
||||
} else {
|
||||
assert_equals(node.data,
|
||||
originalData.substr(0, offset) + data + originalData.substr(offset + count),
|
||||
"Sanity check failed: data not changed as expected");
|
||||
}
|
||||
|
||||
// "For every boundary point whose node is node, and whose offset is
|
||||
// greater than offset but less than or equal to offset plus count, set
|
||||
// its offset to offset."
|
||||
if (!exceptionThrown
|
||||
&& startContainer == node
|
||||
&& startOffset > offset
|
||||
&& startOffset <= offset + count) {
|
||||
expectedStartOffset = offset;
|
||||
}
|
||||
|
||||
if (!exceptionThrown
|
||||
&& endContainer == node
|
||||
&& endOffset > offset
|
||||
&& endOffset <= offset + count) {
|
||||
expectedEndOffset = offset;
|
||||
}
|
||||
|
||||
// "For every boundary point whose node is node, and whose offset is
|
||||
// greater than offset plus count, add the length of data to its offset,
|
||||
// then subtract count from it."
|
||||
if (!exceptionThrown
|
||||
&& startContainer == node
|
||||
&& startOffset > offset + count) {
|
||||
expectedStartOffset += data.length - count;
|
||||
}
|
||||
|
||||
if (!exceptionThrown
|
||||
&& endContainer == node
|
||||
&& endOffset > offset + count) {
|
||||
expectedEndOffset += data.length - count;
|
||||
}
|
||||
|
||||
return [expectedStartContainer, expectedStartOffset, expectedEndContainer, expectedEndOffset];
|
||||
}
|
||||
|
||||
function testInsertData(node, offset, data, startContainer, startOffset, endContainer, endOffset) {
|
||||
return testReplaceDataAlgorithm(node, offset, 0, data,
|
||||
function() { node.insertData(offset, data) },
|
||||
startContainer, startOffset, endContainer, endOffset);
|
||||
}
|
||||
|
||||
var insertDataTests = [];
|
||||
for (var i = 0; i < characterDataNodes.length; i++) {
|
||||
var node = characterDataNodes[i];
|
||||
insertDataTests.push([node, 376, '"foo"', node, 0, node, 1]);
|
||||
insertDataTests.push([node, 0, '"foo"', node, 0, node, 0]);
|
||||
insertDataTests.push([node, 1, '"foo"', node, 1, node, 1]);
|
||||
insertDataTests.push([node, node + ".length", '"foo"', node, node + ".length", node, node + ".length"]);
|
||||
insertDataTests.push([node, 1, '"foo"', node, 1, node, 3]);
|
||||
insertDataTests.push([node, 2, '"foo"', node, 1, node, 3]);
|
||||
insertDataTests.push([node, 3, '"foo"', node, 1, node, 3]);
|
||||
|
||||
insertDataTests.push([node, 376, '""', node, 0, node, 1]);
|
||||
insertDataTests.push([node, 0, '""', node, 0, node, 0]);
|
||||
insertDataTests.push([node, 1, '""', node, 1, node, 1]);
|
||||
insertDataTests.push([node, node + ".length", '""', node, node + ".length", node, node + ".length"]);
|
||||
insertDataTests.push([node, 1, '""', node, 1, node, 3]);
|
||||
insertDataTests.push([node, 2, '""', node, 1, node, 3]);
|
||||
insertDataTests.push([node, 3, '""', node, 1, node, 3]);
|
||||
}
|
||||
|
||||
insertDataTests.push(
|
||||
["paras[0].firstChild", 1, '"foo"', "paras[0]", 0, "paras[0]", 0],
|
||||
["paras[0].firstChild", 1, '"foo"', "paras[0]", 0, "paras[0]", 1],
|
||||
["paras[0].firstChild", 1, '"foo"', "paras[0]", 1, "paras[0]", 1],
|
||||
["paras[0].firstChild", 1, '"foo"', "paras[0].firstChild", 1, "paras[0]", 1],
|
||||
["paras[0].firstChild", 2, '"foo"', "paras[0].firstChild", 1, "paras[0]", 1],
|
||||
["paras[0].firstChild", 3, '"foo"', "paras[0].firstChild", 1, "paras[0]", 1],
|
||||
["paras[0].firstChild", 1, '"foo"', "paras[0]", 0, "paras[0].firstChild", 3],
|
||||
["paras[0].firstChild", 2, '"foo"', "paras[0]", 0, "paras[0].firstChild", 3],
|
||||
["paras[0].firstChild", 3, '"foo"', "paras[0]", 0, "paras[0].firstChild", 3]
|
||||
);
|
||||
|
||||
|
||||
function testAppendData(node, data, startContainer, startOffset, endContainer, endOffset) {
|
||||
return testReplaceDataAlgorithm(node, node.length, 0, data,
|
||||
function() { node.appendData(data) },
|
||||
startContainer, startOffset, endContainer, endOffset);
|
||||
}
|
||||
|
||||
var appendDataTests = [];
|
||||
for (var i = 0; i < characterDataNodes.length; i++) {
|
||||
var node = characterDataNodes[i];
|
||||
appendDataTests.push([node, '"foo"', node, 0, node, 1]);
|
||||
appendDataTests.push([node, '"foo"', node, 0, node, 0]);
|
||||
appendDataTests.push([node, '"foo"', node, 1, node, 1]);
|
||||
appendDataTests.push([node, '"foo"', node, 0, node, node + ".length"]);
|
||||
appendDataTests.push([node, '"foo"', node, 1, node, node + ".length"]);
|
||||
appendDataTests.push([node, '"foo"', node, node + ".length", node, node + ".length"]);
|
||||
appendDataTests.push([node, '"foo"', node, 1, node, 3]);
|
||||
|
||||
appendDataTests.push([node, '""', node, 0, node, 1]);
|
||||
appendDataTests.push([node, '""', node, 0, node, 0]);
|
||||
appendDataTests.push([node, '""', node, 1, node, 1]);
|
||||
appendDataTests.push([node, '""', node, 0, node, node + ".length"]);
|
||||
appendDataTests.push([node, '""', node, 1, node, node + ".length"]);
|
||||
appendDataTests.push([node, '""', node, node + ".length", node, node + ".length"]);
|
||||
appendDataTests.push([node, '""', node, 1, node, 3]);
|
||||
}
|
||||
|
||||
appendDataTests.push(
|
||||
["paras[0].firstChild", '""', "paras[0]", 0, "paras[0]", 0],
|
||||
["paras[0].firstChild", '""', "paras[0]", 0, "paras[0]", 1],
|
||||
["paras[0].firstChild", '""', "paras[0]", 1, "paras[0]", 1],
|
||||
["paras[0].firstChild", '""', "paras[0].firstChild", 1, "paras[0]", 1],
|
||||
["paras[0].firstChild", '""', "paras[0]", 0, "paras[0].firstChild", 3],
|
||||
|
||||
["paras[0].firstChild", '"foo"', "paras[0]", 0, "paras[0]", 0],
|
||||
["paras[0].firstChild", '"foo"', "paras[0]", 0, "paras[0]", 1],
|
||||
["paras[0].firstChild", '"foo"', "paras[0]", 1, "paras[0]", 1],
|
||||
["paras[0].firstChild", '"foo"', "paras[0].firstChild", 1, "paras[0]", 1],
|
||||
["paras[0].firstChild", '"foo"', "paras[0]", 0, "paras[0].firstChild", 3]
|
||||
);
|
||||
|
||||
|
||||
function testDeleteData(node, offset, count, startContainer, startOffset, endContainer, endOffset) {
|
||||
return testReplaceDataAlgorithm(node, offset, count, "",
|
||||
function() { node.deleteData(offset, count) },
|
||||
startContainer, startOffset, endContainer, endOffset);
|
||||
}
|
||||
|
||||
var deleteDataTests = [];
|
||||
for (var i = 0; i < characterDataNodes.length; i++) {
|
||||
var node = characterDataNodes[i];
|
||||
deleteDataTests.push([node, 376, 2, node, 0, node, 1]);
|
||||
deleteDataTests.push([node, 0, 2, node, 0, node, 0]);
|
||||
deleteDataTests.push([node, 1, 2, node, 1, node, 1]);
|
||||
deleteDataTests.push([node, node + ".length", 2, node, node + ".length", node, node + ".length"]);
|
||||
deleteDataTests.push([node, 1, 2, node, 1, node, 3]);
|
||||
deleteDataTests.push([node, 2, 2, node, 1, node, 3]);
|
||||
deleteDataTests.push([node, 3, 2, node, 1, node, 3]);
|
||||
|
||||
deleteDataTests.push([node, 376, 0, node, 0, node, 1]);
|
||||
deleteDataTests.push([node, 0, 0, node, 0, node, 0]);
|
||||
deleteDataTests.push([node, 1, 0, node, 1, node, 1]);
|
||||
deleteDataTests.push([node, node + ".length", 0, node, node + ".length", node, node + ".length"]);
|
||||
deleteDataTests.push([node, 1, 0, node, 1, node, 3]);
|
||||
deleteDataTests.push([node, 2, 0, node, 1, node, 3]);
|
||||
deleteDataTests.push([node, 3, 0, node, 1, node, 3]);
|
||||
|
||||
deleteDataTests.push([node, 376, 631, node, 0, node, 1]);
|
||||
deleteDataTests.push([node, 0, 631, node, 0, node, 0]);
|
||||
deleteDataTests.push([node, 1, 631, node, 1, node, 1]);
|
||||
deleteDataTests.push([node, node + ".length", 631, node, node + ".length", node, node + ".length"]);
|
||||
deleteDataTests.push([node, 1, 631, node, 1, node, 3]);
|
||||
deleteDataTests.push([node, 2, 631, node, 1, node, 3]);
|
||||
deleteDataTests.push([node, 3, 631, node, 1, node, 3]);
|
||||
}
|
||||
|
||||
deleteDataTests.push(
|
||||
["paras[0].firstChild", 1, 2, "paras[0]", 0, "paras[0]", 0],
|
||||
["paras[0].firstChild", 1, 2, "paras[0]", 0, "paras[0]", 1],
|
||||
["paras[0].firstChild", 1, 2, "paras[0]", 1, "paras[0]", 1],
|
||||
["paras[0].firstChild", 1, 2, "paras[0].firstChild", 1, "paras[0]", 1],
|
||||
["paras[0].firstChild", 2, 2, "paras[0].firstChild", 1, "paras[0]", 1],
|
||||
["paras[0].firstChild", 3, 2, "paras[0].firstChild", 1, "paras[0]", 1],
|
||||
["paras[0].firstChild", 1, 2, "paras[0]", 0, "paras[0].firstChild", 3],
|
||||
["paras[0].firstChild", 2, 2, "paras[0]", 0, "paras[0].firstChild", 3],
|
||||
["paras[0].firstChild", 3, 2, "paras[0]", 0, "paras[0].firstChild", 3]
|
||||
);
|
||||
|
||||
|
||||
function testReplaceData(node, offset, count, data, startContainer, startOffset, endContainer, endOffset) {
|
||||
return testReplaceDataAlgorithm(node, offset, count, data,
|
||||
function() { node.replaceData(offset, count, data) },
|
||||
startContainer, startOffset, endContainer, endOffset);
|
||||
}
|
||||
|
||||
var replaceDataTests = [];
|
||||
for (var i = 0; i < characterDataNodes.length; i++) {
|
||||
var node = characterDataNodes[i];
|
||||
replaceDataTests.push([node, 376, 0, '"foo"', node, 0, node, 1]);
|
||||
replaceDataTests.push([node, 0, 0, '"foo"', node, 0, node, 0]);
|
||||
replaceDataTests.push([node, 1, 0, '"foo"', node, 1, node, 1]);
|
||||
replaceDataTests.push([node, node + ".length", 0, '"foo"', node, node + ".length", node, node + ".length"]);
|
||||
replaceDataTests.push([node, 1, 0, '"foo"', node, 1, node, 3]);
|
||||
replaceDataTests.push([node, 2, 0, '"foo"', node, 1, node, 3]);
|
||||
replaceDataTests.push([node, 3, 0, '"foo"', node, 1, node, 3]);
|
||||
|
||||
replaceDataTests.push([node, 376, 0, '""', node, 0, node, 1]);
|
||||
replaceDataTests.push([node, 0, 0, '""', node, 0, node, 0]);
|
||||
replaceDataTests.push([node, 1, 0, '""', node, 1, node, 1]);
|
||||
replaceDataTests.push([node, node + ".length", 0, '""', node, node + ".length", node, node + ".length"]);
|
||||
replaceDataTests.push([node, 1, 0, '""', node, 1, node, 3]);
|
||||
replaceDataTests.push([node, 2, 0, '""', node, 1, node, 3]);
|
||||
replaceDataTests.push([node, 3, 0, '""', node, 1, node, 3]);
|
||||
|
||||
replaceDataTests.push([node, 376, 1, '"foo"', node, 0, node, 1]);
|
||||
replaceDataTests.push([node, 0, 1, '"foo"', node, 0, node, 0]);
|
||||
replaceDataTests.push([node, 1, 1, '"foo"', node, 1, node, 1]);
|
||||
replaceDataTests.push([node, node + ".length", 1, '"foo"', node, node + ".length", node, node + ".length"]);
|
||||
replaceDataTests.push([node, 1, 1, '"foo"', node, 1, node, 3]);
|
||||
replaceDataTests.push([node, 2, 1, '"foo"', node, 1, node, 3]);
|
||||
replaceDataTests.push([node, 3, 1, '"foo"', node, 1, node, 3]);
|
||||
|
||||
replaceDataTests.push([node, 376, 1, '""', node, 0, node, 1]);
|
||||
replaceDataTests.push([node, 0, 1, '""', node, 0, node, 0]);
|
||||
replaceDataTests.push([node, 1, 1, '""', node, 1, node, 1]);
|
||||
replaceDataTests.push([node, node + ".length", 1, '""', node, node + ".length", node, node + ".length"]);
|
||||
replaceDataTests.push([node, 1, 1, '""', node, 1, node, 3]);
|
||||
replaceDataTests.push([node, 2, 1, '""', node, 1, node, 3]);
|
||||
replaceDataTests.push([node, 3, 1, '""', node, 1, node, 3]);
|
||||
|
||||
replaceDataTests.push([node, 376, 47, '"foo"', node, 0, node, 1]);
|
||||
replaceDataTests.push([node, 0, 47, '"foo"', node, 0, node, 0]);
|
||||
replaceDataTests.push([node, 1, 47, '"foo"', node, 1, node, 1]);
|
||||
replaceDataTests.push([node, node + ".length", 47, '"foo"', node, node + ".length", node, node + ".length"]);
|
||||
replaceDataTests.push([node, 1, 47, '"foo"', node, 1, node, 3]);
|
||||
replaceDataTests.push([node, 2, 47, '"foo"', node, 1, node, 3]);
|
||||
replaceDataTests.push([node, 3, 47, '"foo"', node, 1, node, 3]);
|
||||
|
||||
replaceDataTests.push([node, 376, 47, '""', node, 0, node, 1]);
|
||||
replaceDataTests.push([node, 0, 47, '""', node, 0, node, 0]);
|
||||
replaceDataTests.push([node, 1, 47, '""', node, 1, node, 1]);
|
||||
replaceDataTests.push([node, node + ".length", 47, '""', node, node + ".length", node, node + ".length"]);
|
||||
replaceDataTests.push([node, 1, 47, '""', node, 1, node, 3]);
|
||||
replaceDataTests.push([node, 2, 47, '""', node, 1, node, 3]);
|
||||
replaceDataTests.push([node, 3, 47, '""', node, 1, node, 3]);
|
||||
}
|
||||
|
||||
replaceDataTests.push(
|
||||
["paras[0].firstChild", 1, 0, '"foo"', "paras[0]", 0, "paras[0]", 0],
|
||||
["paras[0].firstChild", 1, 0, '"foo"', "paras[0]", 0, "paras[0]", 1],
|
||||
["paras[0].firstChild", 1, 0, '"foo"', "paras[0]", 1, "paras[0]", 1],
|
||||
["paras[0].firstChild", 1, 0, '"foo"', "paras[0].firstChild", 1, "paras[0]", 1],
|
||||
["paras[0].firstChild", 2, 0, '"foo"', "paras[0].firstChild", 1, "paras[0]", 1],
|
||||
["paras[0].firstChild", 3, 0, '"foo"', "paras[0].firstChild", 1, "paras[0]", 1],
|
||||
["paras[0].firstChild", 1, 0, '"foo"', "paras[0]", 0, "paras[0].firstChild", 3],
|
||||
["paras[0].firstChild", 2, 0, '"foo"', "paras[0]", 0, "paras[0].firstChild", 3],
|
||||
["paras[0].firstChild", 3, 0, '"foo"', "paras[0]", 0, "paras[0].firstChild", 3],
|
||||
|
||||
["paras[0].firstChild", 1, 1, '"foo"', "paras[0]", 0, "paras[0]", 0],
|
||||
["paras[0].firstChild", 1, 1, '"foo"', "paras[0]", 0, "paras[0]", 1],
|
||||
["paras[0].firstChild", 1, 1, '"foo"', "paras[0]", 1, "paras[0]", 1],
|
||||
["paras[0].firstChild", 1, 1, '"foo"', "paras[0].firstChild", 1, "paras[0]", 1],
|
||||
["paras[0].firstChild", 2, 1, '"foo"', "paras[0].firstChild", 1, "paras[0]", 1],
|
||||
["paras[0].firstChild", 3, 1, '"foo"', "paras[0].firstChild", 1, "paras[0]", 1],
|
||||
["paras[0].firstChild", 1, 1, '"foo"', "paras[0]", 0, "paras[0].firstChild", 3],
|
||||
["paras[0].firstChild", 2, 1, '"foo"', "paras[0]", 0, "paras[0].firstChild", 3],
|
||||
["paras[0].firstChild", 3, 1, '"foo"', "paras[0]", 0, "paras[0].firstChild", 3],
|
||||
|
||||
["paras[0].firstChild", 1, 47, '"foo"', "paras[0]", 0, "paras[0]", 0],
|
||||
["paras[0].firstChild", 1, 47, '"foo"', "paras[0]", 0, "paras[0]", 1],
|
||||
["paras[0].firstChild", 1, 47, '"foo"', "paras[0]", 1, "paras[0]", 1],
|
||||
["paras[0].firstChild", 1, 47, '"foo"', "paras[0].firstChild", 1, "paras[0]", 1],
|
||||
["paras[0].firstChild", 2, 47, '"foo"', "paras[0].firstChild", 1, "paras[0]", 1],
|
||||
["paras[0].firstChild", 3, 47, '"foo"', "paras[0].firstChild", 1, "paras[0]", 1],
|
||||
["paras[0].firstChild", 1, 47, '"foo"', "paras[0]", 0, "paras[0].firstChild", 3],
|
||||
["paras[0].firstChild", 2, 47, '"foo"', "paras[0]", 0, "paras[0].firstChild", 3],
|
||||
["paras[0].firstChild", 3, 47, '"foo"', "paras[0]", 0, "paras[0].firstChild", 3]
|
||||
);
|
||||
|
||||
|
||||
// There are lots of ways to set data, so we pass a callback that does the
|
||||
// actual setting.
|
||||
function testDataChange(node, attr, op, rval, startContainer, startOffset, endContainer, endOffset) {
|
||||
return testReplaceDataAlgorithm(node, 0, node.length, op == "=" ? rval : node[attr] + rval,
|
||||
function() {
|
||||
if (op == "=") {
|
||||
node[attr] = rval;
|
||||
} else if (op == "+=") {
|
||||
node[attr] += rval;
|
||||
} else {
|
||||
throw "Unknown op " + op;
|
||||
}
|
||||
},
|
||||
startContainer, startOffset, endContainer, endOffset);
|
||||
}
|
||||
|
||||
var dataChangeTests = [];
|
||||
var dataChangeTestAttrs = ["data", "textContent", "nodeValue"];
|
||||
for (var i = 0; i < characterDataNodes.length; i++) {
|
||||
var node = characterDataNodes[i];
|
||||
var dataChangeTestRanges = [
|
||||
[node, 0, node, 0],
|
||||
[node, 0, node, 1],
|
||||
[node, 1, node, 1],
|
||||
[node, 0, node, node + ".length"],
|
||||
[node, 1, node, node + ".length"],
|
||||
[node, node + ".length", node, node + ".length"],
|
||||
];
|
||||
|
||||
for (var j = 0; j < dataChangeTestRanges.length; j++) {
|
||||
for (var k = 0; k < dataChangeTestAttrs.length; k++) {
|
||||
dataChangeTests.push([
|
||||
node,
|
||||
'"' + dataChangeTestAttrs[k] + '"',
|
||||
'"="',
|
||||
'""',
|
||||
].concat(dataChangeTestRanges[j]));
|
||||
|
||||
dataChangeTests.push([
|
||||
node,
|
||||
'"' + dataChangeTestAttrs[k] + '"',
|
||||
'"="',
|
||||
'"foo"',
|
||||
].concat(dataChangeTestRanges[j]));
|
||||
|
||||
dataChangeTests.push([
|
||||
node,
|
||||
'"' + dataChangeTestAttrs[k] + '"',
|
||||
'"="',
|
||||
node + "." + dataChangeTestAttrs[k],
|
||||
].concat(dataChangeTestRanges[j]));
|
||||
|
||||
dataChangeTests.push([
|
||||
node,
|
||||
'"' + dataChangeTestAttrs[k] + '"',
|
||||
'"+="',
|
||||
'""',
|
||||
].concat(dataChangeTestRanges[j]));
|
||||
|
||||
dataChangeTests.push([
|
||||
node,
|
||||
'"' + dataChangeTestAttrs[k] + '"',
|
||||
'"+="',
|
||||
'"foo"',
|
||||
].concat(dataChangeTestRanges[j]));
|
||||
|
||||
dataChangeTests.push([
|
||||
node,
|
||||
'"' + dataChangeTestAttrs[k] + '"',
|
||||
'"+="',
|
||||
node + "." + dataChangeTestAttrs[k]
|
||||
].concat(dataChangeTestRanges[j]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Now we test node insertions and deletions, as opposed to just data changes.
|
||||
// To avoid loads of repetition, we define modifyForRemove() and
|
||||
// modifyForInsert().
|
||||
|
||||
// If we were to remove removedNode from its parent, what would the boundary
|
||||
// point [node, offset] become? Returns [new node, new offset]. Must be
|
||||
// called BEFORE the node is actually removed, so its parent is not null. (If
|
||||
// the parent is null, it will do nothing.)
|
||||
function modifyForRemove(removedNode, point) {
|
||||
var oldParent = removedNode.parentNode;
|
||||
var oldIndex = indexOf(removedNode);
|
||||
if (!oldParent) {
|
||||
return point;
|
||||
}
|
||||
|
||||
// "For each boundary point whose node is removed node or a descendant of
|
||||
// it, set the boundary point to (old parent, old index)."
|
||||
if (point[0] == removedNode || isDescendant(point[0], removedNode)) {
|
||||
return [oldParent, oldIndex];
|
||||
}
|
||||
|
||||
// "For each boundary point whose node is old parent and whose offset is
|
||||
// greater than old index, subtract one from its offset."
|
||||
if (point[0] == oldParent && point[1] > oldIndex) {
|
||||
return [point[0], point[1] - 1];
|
||||
}
|
||||
|
||||
return point;
|
||||
}
|
||||
|
||||
// Update the given boundary point [node, offset] to account for the fact that
|
||||
// insertedNode was just inserted into its current position. This must be
|
||||
// called AFTER insertedNode was already inserted.
|
||||
function modifyForInsert(insertedNode, point) {
|
||||
// "For each boundary point whose node is the new parent of the affected
|
||||
// node and whose offset is greater than the new index of the affected
|
||||
// node, add one to the boundary point's offset."
|
||||
if (point[0] == insertedNode.parentNode && point[1] > indexOf(insertedNode)) {
|
||||
return [point[0], point[1] + 1];
|
||||
}
|
||||
|
||||
return point;
|
||||
}
|
||||
|
||||
|
||||
function testInsertBefore(newParent, affectedNode, refNode, startContainer, startOffset, endContainer, endOffset) {
|
||||
var expectedStart = [startContainer, startOffset];
|
||||
var expectedEnd = [endContainer, endOffset];
|
||||
|
||||
expectedStart = modifyForRemove(affectedNode, expectedStart);
|
||||
expectedEnd = modifyForRemove(affectedNode, expectedEnd);
|
||||
|
||||
try {
|
||||
newParent.insertBefore(affectedNode, refNode);
|
||||
} catch (e) {
|
||||
// For our purposes, assume that DOM Core is true -- i.e., ignore
|
||||
// mutation events and similar.
|
||||
return [startContainer, startOffset, endContainer, endOffset];
|
||||
}
|
||||
|
||||
expectedStart = modifyForInsert(affectedNode, expectedStart);
|
||||
expectedEnd = modifyForInsert(affectedNode, expectedEnd);
|
||||
|
||||
return expectedStart.concat(expectedEnd);
|
||||
}
|
||||
|
||||
var insertBeforeTests = [
|
||||
// Moving a node to its current position
|
||||
["testDiv", "paras[0]", "paras[1]", "paras[0]", 0, "paras[0]", 0],
|
||||
["testDiv", "paras[0]", "paras[1]", "paras[0]", 0, "paras[0]", 1],
|
||||
["testDiv", "paras[0]", "paras[1]", "paras[0]", 1, "paras[0]", 1],
|
||||
["testDiv", "paras[0]", "paras[1]", "testDiv", 0, "testDiv", 2],
|
||||
["testDiv", "paras[0]", "paras[1]", "testDiv", 1, "testDiv", 1],
|
||||
["testDiv", "paras[0]", "paras[1]", "testDiv", 1, "testDiv", 2],
|
||||
["testDiv", "paras[0]", "paras[1]", "testDiv", 2, "testDiv", 2],
|
||||
|
||||
// Stuff that actually moves something. Note that paras[0] and paras[1]
|
||||
// are both children of testDiv.
|
||||
["paras[0]", "paras[1]", "paras[0].firstChild", "paras[0]", 0, "paras[0]", 0],
|
||||
["paras[0]", "paras[1]", "paras[0].firstChild", "paras[0]", 0, "paras[0]", 1],
|
||||
["paras[0]", "paras[1]", "paras[0].firstChild", "paras[0]", 1, "paras[0]", 1],
|
||||
["paras[0]", "paras[1]", "paras[0].firstChild", "testDiv", 0, "testDiv", 1],
|
||||
["paras[0]", "paras[1]", "paras[0].firstChild", "testDiv", 0, "testDiv", 2],
|
||||
["paras[0]", "paras[1]", "paras[0].firstChild", "testDiv", 1, "testDiv", 1],
|
||||
["paras[0]", "paras[1]", "paras[0].firstChild", "testDiv", 1, "testDiv", 2],
|
||||
["paras[0]", "paras[1]", "null", "paras[0]", 0, "paras[0]", 0],
|
||||
["paras[0]", "paras[1]", "null", "paras[0]", 0, "paras[0]", 1],
|
||||
["paras[0]", "paras[1]", "null", "paras[0]", 1, "paras[0]", 1],
|
||||
["paras[0]", "paras[1]", "null", "testDiv", 0, "testDiv", 1],
|
||||
["paras[0]", "paras[1]", "null", "testDiv", 0, "testDiv", 2],
|
||||
["paras[0]", "paras[1]", "null", "testDiv", 1, "testDiv", 1],
|
||||
["paras[0]", "paras[1]", "null", "testDiv", 1, "testDiv", 2],
|
||||
["foreignDoc", "detachedComment", "foreignDoc.documentElement", "foreignDoc", 0, "foreignDoc", 0],
|
||||
["foreignDoc", "detachedComment", "foreignDoc.documentElement", "foreignDoc", 0, "foreignDoc", 1],
|
||||
["foreignDoc", "detachedComment", "foreignDoc.documentElement", "foreignDoc", 0, "foreignDoc", 2],
|
||||
["foreignDoc", "detachedComment", "foreignDoc.documentElement", "foreignDoc", 1, "foreignDoc", 1],
|
||||
["foreignDoc", "detachedComment", "foreignDoc.doctype", "foreignDoc", 0, "foreignDoc", 0],
|
||||
["foreignDoc", "detachedComment", "foreignDoc.doctype", "foreignDoc", 0, "foreignDoc", 1],
|
||||
["foreignDoc", "detachedComment", "foreignDoc.doctype", "foreignDoc", 0, "foreignDoc", 2],
|
||||
["foreignDoc", "detachedComment", "foreignDoc.doctype", "foreignDoc", 1, "foreignDoc", 1],
|
||||
["foreignDoc", "detachedComment", "null", "foreignDoc", 0, "foreignDoc", 1],
|
||||
["paras[0]", "xmlTextNode", "paras[0].firstChild", "paras[0]", 0, "paras[0]", 0],
|
||||
["paras[0]", "xmlTextNode", "paras[0].firstChild", "paras[0]", 0, "paras[0]", 1],
|
||||
["paras[0]", "xmlTextNode", "paras[0].firstChild", "paras[0]", 1, "paras[0]", 1],
|
||||
|
||||
// Stuff that throws exceptions
|
||||
["paras[0]", "paras[0]", "paras[0].firstChild", "paras[0]", 0, "paras[0]", 1],
|
||||
["paras[0]", "testDiv", "paras[0].firstChild", "paras[0]", 0, "paras[0]", 1],
|
||||
["paras[0]", "document", "paras[0].firstChild", "paras[0]", 0, "paras[0]", 1],
|
||||
["paras[0]", "foreignDoc", "paras[0].firstChild", "paras[0]", 0, "paras[0]", 1],
|
||||
["paras[0]", "document.doctype", "paras[0].firstChild", "paras[0]", 0, "paras[0]", 1],
|
||||
];
|
||||
|
||||
|
||||
function testReplaceChild(newParent, newChild, oldChild, startContainer, startOffset, endContainer, endOffset) {
|
||||
var expectedStart = [startContainer, startOffset];
|
||||
var expectedEnd = [endContainer, endOffset];
|
||||
|
||||
expectedStart = modifyForRemove(oldChild, expectedStart);
|
||||
expectedEnd = modifyForRemove(oldChild, expectedEnd);
|
||||
|
||||
if (newChild != oldChild) {
|
||||
// Don't do this twice, if they're the same!
|
||||
expectedStart = modifyForRemove(newChild, expectedStart);
|
||||
expectedEnd = modifyForRemove(newChild, expectedEnd);
|
||||
}
|
||||
|
||||
try {
|
||||
newParent.replaceChild(newChild, oldChild);
|
||||
} catch (e) {
|
||||
return [startContainer, startOffset, endContainer, endOffset];
|
||||
}
|
||||
|
||||
expectedStart = modifyForInsert(newChild, expectedStart);
|
||||
expectedEnd = modifyForInsert(newChild, expectedEnd);
|
||||
|
||||
return expectedStart.concat(expectedEnd);
|
||||
}
|
||||
|
||||
var replaceChildTests = [
|
||||
// Moving a node to its current position. Doesn't match most browsers'
|
||||
// behavior, but we probably want to keep the spec the same anyway:
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=647603
|
||||
["testDiv", "paras[0]", "paras[0]", "paras[0]", 0, "paras[0]", 0],
|
||||
["testDiv", "paras[0]", "paras[0]", "paras[0]", 0, "paras[0]", 1],
|
||||
["testDiv", "paras[0]", "paras[0]", "paras[0]", 1, "paras[0]", 1],
|
||||
["testDiv", "paras[0]", "paras[0]", "testDiv", 0, "testDiv", 2],
|
||||
["testDiv", "paras[0]", "paras[0]", "testDiv", 1, "testDiv", 1],
|
||||
["testDiv", "paras[0]", "paras[0]", "testDiv", 1, "testDiv", 2],
|
||||
["testDiv", "paras[0]", "paras[0]", "testDiv", 2, "testDiv", 2],
|
||||
|
||||
// Stuff that actually moves something.
|
||||
["paras[0]", "paras[1]", "paras[0].firstChild", "paras[0]", 0, "paras[0]", 0],
|
||||
["paras[0]", "paras[1]", "paras[0].firstChild", "paras[0]", 0, "paras[0]", 1],
|
||||
["paras[0]", "paras[1]", "paras[0].firstChild", "paras[0]", 1, "paras[0]", 1],
|
||||
["paras[0]", "paras[1]", "paras[0].firstChild", "testDiv", 0, "testDiv", 1],
|
||||
["paras[0]", "paras[1]", "paras[0].firstChild", "testDiv", 0, "testDiv", 2],
|
||||
["paras[0]", "paras[1]", "paras[0].firstChild", "testDiv", 1, "testDiv", 1],
|
||||
["paras[0]", "paras[1]", "paras[0].firstChild", "testDiv", 1, "testDiv", 2],
|
||||
["foreignDoc", "detachedComment", "foreignDoc.documentElement", "foreignDoc", 0, "foreignDoc", 0],
|
||||
["foreignDoc", "detachedComment", "foreignDoc.documentElement", "foreignDoc", 0, "foreignDoc", 1],
|
||||
["foreignDoc", "detachedComment", "foreignDoc.documentElement", "foreignDoc", 0, "foreignDoc", 2],
|
||||
["foreignDoc", "detachedComment", "foreignDoc.documentElement", "foreignDoc", 1, "foreignDoc", 1],
|
||||
["foreignDoc", "detachedComment", "foreignDoc.doctype", "foreignDoc", 0, "foreignDoc", 0],
|
||||
["foreignDoc", "detachedComment", "foreignDoc.doctype", "foreignDoc", 0, "foreignDoc", 1],
|
||||
["foreignDoc", "detachedComment", "foreignDoc.doctype", "foreignDoc", 0, "foreignDoc", 2],
|
||||
["foreignDoc", "detachedComment", "foreignDoc.doctype", "foreignDoc", 1, "foreignDoc", 1],
|
||||
["paras[0]", "xmlTextNode", "paras[0].firstChild", "paras[0]", 0, "paras[0]", 0],
|
||||
["paras[0]", "xmlTextNode", "paras[0].firstChild", "paras[0]", 0, "paras[0]", 1],
|
||||
["paras[0]", "xmlTextNode", "paras[0].firstChild", "paras[0]", 1, "paras[0]", 1],
|
||||
|
||||
// Stuff that throws exceptions
|
||||
["paras[0]", "paras[0]", "paras[0].firstChild", "paras[0]", 0, "paras[0]", 1],
|
||||
["paras[0]", "testDiv", "paras[0].firstChild", "paras[0]", 0, "paras[0]", 1],
|
||||
["paras[0]", "document", "paras[0].firstChild", "paras[0]", 0, "paras[0]", 1],
|
||||
["paras[0]", "foreignDoc", "paras[0].firstChild", "paras[0]", 0, "paras[0]", 1],
|
||||
["paras[0]", "document.doctype", "paras[0].firstChild", "paras[0]", 0, "paras[0]", 1],
|
||||
];
|
||||
|
||||
|
||||
function testAppendChild(newParent, affectedNode, startContainer, startOffset, endContainer, endOffset) {
|
||||
var expectedStart = [startContainer, startOffset];
|
||||
var expectedEnd = [endContainer, endOffset];
|
||||
|
||||
expectedStart = modifyForRemove(affectedNode, expectedStart);
|
||||
expectedEnd = modifyForRemove(affectedNode, expectedEnd);
|
||||
|
||||
try {
|
||||
newParent.appendChild(affectedNode);
|
||||
} catch (e) {
|
||||
return [startContainer, startOffset, endContainer, endOffset];
|
||||
}
|
||||
|
||||
// These two lines will actually never do anything, if you think about it,
|
||||
// but let's leave them in so correctness is more obvious.
|
||||
expectedStart = modifyForInsert(affectedNode, expectedStart);
|
||||
expectedEnd = modifyForInsert(affectedNode, expectedEnd);
|
||||
|
||||
return expectedStart.concat(expectedEnd);
|
||||
}
|
||||
|
||||
var appendChildTests = [
|
||||
// Moving a node to its current position
|
||||
["testDiv", "testDiv.lastChild", "testDiv.lastChild", 0, "testDiv.lastChild", 0],
|
||||
["testDiv", "testDiv.lastChild", "testDiv.lastChild", 0, "testDiv.lastChild", 1],
|
||||
["testDiv", "testDiv.lastChild", "testDiv.lastChild", 1, "testDiv.lastChild", 1],
|
||||
["testDiv", "testDiv.lastChild", "testDiv", "testDiv.childNodes.length - 2", "testDiv", "testDiv.childNodes.length"],
|
||||
["testDiv", "testDiv.lastChild", "testDiv", "testDiv.childNodes.length - 2", "testDiv", "testDiv.childNodes.length - 1"],
|
||||
["testDiv", "testDiv.lastChild", "testDiv", "testDiv.childNodes.length - 1", "testDiv", "testDiv.childNodes.length"],
|
||||
["testDiv", "testDiv.lastChild", "testDiv", "testDiv.childNodes.length - 1", "testDiv", "testDiv.childNodes.length - 1"],
|
||||
["testDiv", "testDiv.lastChild", "testDiv", "testDiv.childNodes.length", "testDiv", "testDiv.childNodes.length"],
|
||||
["detachedDiv", "detachedDiv.lastChild", "detachedDiv.lastChild", 0, "detachedDiv.lastChild", 0],
|
||||
["detachedDiv", "detachedDiv.lastChild", "detachedDiv.lastChild", 0, "detachedDiv.lastChild", 1],
|
||||
["detachedDiv", "detachedDiv.lastChild", "detachedDiv.lastChild", 1, "detachedDiv.lastChild", 1],
|
||||
["detachedDiv", "detachedDiv.lastChild", "detachedDiv", "detachedDiv.childNodes.length - 2", "detachedDiv", "detachedDiv.childNodes.length"],
|
||||
["detachedDiv", "detachedDiv.lastChild", "detachedDiv", "detachedDiv.childNodes.length - 2", "detachedDiv", "detachedDiv.childNodes.length - 1"],
|
||||
["detachedDiv", "detachedDiv.lastChild", "detachedDiv", "detachedDiv.childNodes.length - 1", "detachedDiv", "detachedDiv.childNodes.length"],
|
||||
["detachedDiv", "detachedDiv.lastChild", "detachedDiv", "detachedDiv.childNodes.length - 1", "detachedDiv", "detachedDiv.childNodes.length - 1"],
|
||||
["detachedDiv", "detachedDiv.lastChild", "detachedDiv", "detachedDiv.childNodes.length", "detachedDiv", "detachedDiv.childNodes.length"],
|
||||
|
||||
// Stuff that actually moves something
|
||||
["paras[0]", "paras[1]", "paras[0]", 0, "paras[0]", 0],
|
||||
["paras[0]", "paras[1]", "paras[0]", 0, "paras[0]", 1],
|
||||
["paras[0]", "paras[1]", "paras[0]", 1, "paras[0]", 1],
|
||||
["paras[0]", "paras[1]", "testDiv", 0, "testDiv", 1],
|
||||
["paras[0]", "paras[1]", "testDiv", 0, "testDiv", 2],
|
||||
["paras[0]", "paras[1]", "testDiv", 1, "testDiv", 1],
|
||||
["paras[0]", "paras[1]", "testDiv", 1, "testDiv", 2],
|
||||
["foreignDoc", "detachedComment", "foreignDoc", "foreignDoc.childNodes.length - 1", "foreignDoc", "foreignDoc.childNodes.length"],
|
||||
["foreignDoc", "detachedComment", "foreignDoc", "foreignDoc.childNodes.length - 1", "foreignDoc", "foreignDoc.childNodes.length - 1"],
|
||||
["foreignDoc", "detachedComment", "foreignDoc", "foreignDoc.childNodes.length", "foreignDoc", "foreignDoc.childNodes.length"],
|
||||
["foreignDoc", "detachedComment", "detachedComment", 0, "detachedComment", 5],
|
||||
["paras[0]", "xmlTextNode", "paras[0]", 0, "paras[0]", 0],
|
||||
["paras[0]", "xmlTextNode", "paras[0]", 0, "paras[0]", 1],
|
||||
["paras[0]", "xmlTextNode", "paras[0]", 1, "paras[0]", 1],
|
||||
|
||||
// Stuff that throws exceptions
|
||||
["paras[0]", "paras[0]", "paras[0]", 0, "paras[0]", 1],
|
||||
["paras[0]", "testDiv", "paras[0]", 0, "paras[0]", 1],
|
||||
["paras[0]", "document", "paras[0]", 0, "paras[0]", 1],
|
||||
["paras[0]", "foreignDoc", "paras[0]", 0, "paras[0]", 1],
|
||||
["paras[0]", "document.doctype", "paras[0]", 0, "paras[0]", 1],
|
||||
];
|
||||
|
||||
|
||||
function testRemoveChild(affectedNode, startContainer, startOffset, endContainer, endOffset) {
|
||||
var expectedStart = [startContainer, startOffset];
|
||||
var expectedEnd = [endContainer, endOffset];
|
||||
|
||||
expectedStart = modifyForRemove(affectedNode, expectedStart);
|
||||
expectedEnd = modifyForRemove(affectedNode, expectedEnd);
|
||||
|
||||
// We don't test cases where the parent is wrong, so this should never
|
||||
// throw an exception.
|
||||
affectedNode.parentNode.removeChild(affectedNode);
|
||||
|
||||
return expectedStart.concat(expectedEnd);
|
||||
}
|
||||
|
||||
var removeChildTests = [
|
||||
["paras[0]", "paras[0]", 0, "paras[0]", 0],
|
||||
["paras[0]", "paras[0]", 0, "paras[0]", 1],
|
||||
["paras[0]", "paras[0]", 1, "paras[0]", 1],
|
||||
["paras[0]", "testDiv", 0, "testDiv", 0],
|
||||
["paras[0]", "testDiv", 0, "testDiv", 1],
|
||||
["paras[0]", "testDiv", 1, "testDiv", 1],
|
||||
["paras[0]", "testDiv", 0, "testDiv", 2],
|
||||
["paras[0]", "testDiv", 1, "testDiv", 2],
|
||||
["paras[0]", "testDiv", 2, "testDiv", 2],
|
||||
|
||||
["foreignDoc.documentElement", "foreignDoc", 0, "foreignDoc", "foreignDoc.childNodes.length"],
|
||||
];
|
Loading…
Add table
Add a link
Reference in a new issue