| OLD | NEW |
| (Empty) |
| 1 <!doctype html> | |
| 2 <title>Range mutation tests</title> | |
| 3 <link rel="author" title="Aryeh Gregor" href=ayg@aryeh.name> | |
| 4 <meta name=timeout content=long> | |
| 5 | |
| 6 <div id=log></div> | |
| 7 <script src=/resources/testharness.js></script> | |
| 8 <script src=/resources/testharnessreport.js></script> | |
| 9 <script src=../common.js></script> | |
| 10 <script> | |
| 11 "use strict"; | |
| 12 | |
| 13 // These tests probably use too much abstraction and too little copy-paste. | |
| 14 // Reader beware. | |
| 15 // | |
| 16 // TODO: | |
| 17 // | |
| 18 // * Lots and lots and lots more different types of ranges | |
| 19 // * insertBefore() with DocumentFragments | |
| 20 // * Fill out other insert/remove tests | |
| 21 // * normalize() (https://www.w3.org/Bugs/Public/show_bug.cgi?id=13843) | |
| 22 | |
| 23 // Give a textual description of the range we're testing, for the test names. | |
| 24 function describeRange(startContainer, startOffset, endContainer, endOffset) { | |
| 25 if (startContainer == endContainer && startOffset == endOffset) { | |
| 26 return "range collapsed at (" + startContainer + ", " + startOffset + ")"; | |
| 27 } else if (startContainer == endContainer) { | |
| 28 return "range on " + startContainer + " from " + startOffset + " to " + endO
ffset; | |
| 29 } else { | |
| 30 return "range from (" + startContainer + ", " + startOffset + ") to (" + end
Container + ", " + endOffset + ")"; | |
| 31 } | |
| 32 } | |
| 33 | |
| 34 // Lists of the various types of nodes we'll want to use. We use strings that | |
| 35 // we can later eval(), so that we can produce legible test names. | |
| 36 var textNodes = [ | |
| 37 "paras[0].firstChild", | |
| 38 "paras[1].firstChild", | |
| 39 "foreignTextNode", | |
| 40 "xmlTextNode", | |
| 41 "detachedTextNode", | |
| 42 "detachedForeignTextNode", | |
| 43 "detachedXmlTextNode", | |
| 44 ]; | |
| 45 var commentNodes = [ | |
| 46 "comment", | |
| 47 "foreignComment", | |
| 48 "xmlComment", | |
| 49 "detachedComment", | |
| 50 "detachedForeignComment", | |
| 51 "detachedXmlComment", | |
| 52 ]; | |
| 53 var characterDataNodes = textNodes.concat(commentNodes); | |
| 54 | |
| 55 // This function is slightly scary, but it works well enough, so . . . | |
| 56 // sourceTests is an array of test data that will be altered in mysterious ways | |
| 57 // before being passed off to doTest, descFn is something that takes an element | |
| 58 // of sourceTests and produces the first part of a human-readable description | |
| 59 // of the test, testFn is the function that doTest will call to do the actual | |
| 60 // work and tell it what results to expect. | |
| 61 function doTests(sourceTests, descFn, testFn) { | |
| 62 var tests = []; | |
| 63 for (var i = 0; i < sourceTests.length; i++) { | |
| 64 var params = sourceTests[i]; | |
| 65 var len = params.length; | |
| 66 tests.push([ | |
| 67 descFn(params) + ", with unselected " + describeRange(params[len - 4], par
ams[len - 3], params[len - 2], params[len - 1]), | |
| 68 // The closure here ensures that the params that testFn get are the | |
| 69 // current version of params, not the version from the last | |
| 70 // iteration of this loop. We test that none of the parameters | |
| 71 // evaluate to undefined to catch bugs in our eval'ing, like | |
| 72 // mistyping a property name. | |
| 73 function(params) { return function() { | |
| 74 var evaledParams = params.map(eval); | |
| 75 for (var i = 0; i < evaledParams.length; i++) { | |
| 76 assert_true(typeof evaledParams[i] != "undefined", | |
| 77 "Test bug: " + params[i] + " is undefined"); | |
| 78 } | |
| 79 return testFn.apply(null, evaledParams); | |
| 80 } }(params), | |
| 81 false, | |
| 82 params[len - 4], | |
| 83 params[len - 3], | |
| 84 params[len - 2], | |
| 85 params[len - 1] | |
| 86 ]); | |
| 87 tests.push([ | |
| 88 descFn(params) + ", with selected " + describeRange(params[len - 4], param
s[len - 3], params[len - 2], params[len - 1]), | |
| 89 function(params) { return function(selectedRange) { | |
| 90 var evaledParams = params.slice(0, len - 4).map(eval); | |
| 91 for (var i = 0; i < evaledParams.length; i++) { | |
| 92 assert_true(typeof evaledParams[i] != "undefined", | |
| 93 "Test bug: " + params[i] + " is undefined"); | |
| 94 } | |
| 95 // Override input range with the one that was actually selected when com
puting the expected result. | |
| 96 evaledParams = evaledParams.concat([selectedRange.startContainer, select
edRange.startOffset, selectedRange.endContainer, selectedRange.endOffset]); | |
| 97 return testFn.apply(null, evaledParams); | |
| 98 } }(params), | |
| 99 true, | |
| 100 params[len - 4], | |
| 101 params[len - 3], | |
| 102 params[len - 2], | |
| 103 params[len - 1] | |
| 104 ]); | |
| 105 } | |
| 106 generate_tests(doTest, tests); | |
| 107 } | |
| 108 | |
| 109 // Set up the range, call the callback function to do the DOM modification and | |
| 110 // tell us what to expect. The callback function needs to return a | |
| 111 // four-element array with the expected start/end containers/offsets, and | |
| 112 // receives no arguments. useSelection tells us whether the Range should be | |
| 113 // added to a Selection and the Selection tested to ensure that the mutation | |
| 114 // affects user selections as well as other ranges; every test is run with this | |
| 115 // both false and true, because when it's set to true WebKit and Opera fail all | |
| 116 // tests' sanity checks, which is unhelpful. The last four parameters just | |
| 117 // tell us what range to build. | |
| 118 function doTest(callback, useSelection, startContainer, startOffset, endContaine
r, endOffset) { | |
| 119 // Recreate all the test nodes in case they were altered by the last test | |
| 120 // run. | |
| 121 setupRangeTests(); | |
| 122 startContainer = eval(startContainer); | |
| 123 startOffset = eval(startOffset); | |
| 124 endContainer = eval(endContainer); | |
| 125 endOffset = eval(endOffset); | |
| 126 | |
| 127 var ownerDoc = startContainer.nodeType == Node.DOCUMENT_NODE | |
| 128 ? startContainer | |
| 129 : startContainer.ownerDocument; | |
| 130 var range = ownerDoc.createRange(); | |
| 131 range.setStart(startContainer, startOffset); | |
| 132 range.setEnd(endContainer, endOffset); | |
| 133 | |
| 134 if (useSelection) { | |
| 135 getSelection().removeAllRanges(); | |
| 136 getSelection().addRange(range); | |
| 137 | |
| 138 // Some browsers refuse to add a range unless it results in an actual visibl
e selection. | |
| 139 if (!getSelection().rangeCount) | |
| 140 return; | |
| 141 | |
| 142 // Override range with the one that was actually selected as it differs in s
ome browsers. | |
| 143 range = getSelection().getRangeAt(0); | |
| 144 } | |
| 145 | |
| 146 var expected = callback(range); | |
| 147 | |
| 148 assert_equals(range.startContainer, expected[0], | |
| 149 "Wrong start container"); | |
| 150 assert_equals(range.startOffset, expected[1], | |
| 151 "Wrong start offset"); | |
| 152 assert_equals(range.endContainer, expected[2], | |
| 153 "Wrong end container"); | |
| 154 assert_equals(range.endOffset, expected[3], | |
| 155 "Wrong end offset"); | |
| 156 } | |
| 157 | |
| 158 | |
| 159 // Now we get to the specific tests. | |
| 160 | |
| 161 function testSplitText(oldNode, offset, startContainer, startOffset, endContaine
r, endOffset) { | |
| 162 // Save these for later | |
| 163 var originalStartOffset = startOffset; | |
| 164 var originalEndOffset = endOffset; | |
| 165 var originalLength = oldNode.length; | |
| 166 | |
| 167 var newNode; | |
| 168 try { | |
| 169 newNode = oldNode.splitText(offset); | |
| 170 } catch (e) { | |
| 171 // Should only happen if offset is negative | |
| 172 return [startContainer, startOffset, endContainer, endOffset]; | |
| 173 } | |
| 174 | |
| 175 // First we adjust for replacing data: | |
| 176 // | |
| 177 // "Replace data with offset offset, count count, and data the empty | |
| 178 // string." | |
| 179 // | |
| 180 // That translates to offset = offset, count = originalLength - offset, | |
| 181 // data = "". node is oldNode. | |
| 182 // | |
| 183 // "For every boundary point whose node is node, and whose offset is | |
| 184 // greater than offset but less than or equal to offset plus count, set its | |
| 185 // offset to offset." | |
| 186 if (startContainer == oldNode | |
| 187 && startOffset > offset | |
| 188 && startOffset <= originalLength) { | |
| 189 startOffset = offset; | |
| 190 } | |
| 191 | |
| 192 if (endContainer == oldNode | |
| 193 && endOffset > offset | |
| 194 && endOffset <= originalLength) { | |
| 195 endOffset = offset; | |
| 196 } | |
| 197 | |
| 198 // "For every boundary point whose node is node, and whose offset is | |
| 199 // greater than offset plus count, add the length of data to its offset, | |
| 200 // then subtract count from it." | |
| 201 // | |
| 202 // Can't happen: offset plus count is originalLength. | |
| 203 | |
| 204 // Now we insert a node, if oldNode's parent isn't null: "For each boundary | |
| 205 // point whose node is the new parent of the affected node and whose offset | |
| 206 // is greater than the new index of the affected node, add one to the | |
| 207 // boundary point's offset." | |
| 208 if (startContainer == oldNode.parentNode | |
| 209 && startOffset > 1 + indexOf(oldNode)) { | |
| 210 startOffset++; | |
| 211 } | |
| 212 | |
| 213 if (endContainer == oldNode.parentNode | |
| 214 && endOffset > 1 + indexOf(oldNode)) { | |
| 215 endOffset++; | |
| 216 } | |
| 217 | |
| 218 // Finally, the splitText stuff itself: | |
| 219 // | |
| 220 // "If parent is not null, run these substeps: | |
| 221 // | |
| 222 // * "For each range whose start node is node and start offset is greater | |
| 223 // than offset, set its start node to new node and decrease its start | |
| 224 // offset by offset. | |
| 225 // | |
| 226 // * "For each range whose end node is node and end offset is greater | |
| 227 // than offset, set its end node to new node and decrease its end offset | |
| 228 // by offset. | |
| 229 // | |
| 230 // * "For each range whose start node is parent and start offset is equal | |
| 231 // to the index of node + 1, increase its start offset by one. | |
| 232 // | |
| 233 // * "For each range whose end node is parent and end offset is equal to | |
| 234 // the index of node + 1, increase its end offset by one." | |
| 235 if (oldNode.parentNode) { | |
| 236 if (startContainer == oldNode && originalStartOffset > offset) { | |
| 237 startContainer = newNode; | |
| 238 startOffset = originalStartOffset - offset; | |
| 239 } | |
| 240 | |
| 241 if (endContainer == oldNode && originalEndOffset > offset) { | |
| 242 endContainer = newNode; | |
| 243 endOffset = originalEndOffset - offset; | |
| 244 } | |
| 245 | |
| 246 if (startContainer == oldNode.parentNode | |
| 247 && startOffset == 1 + indexOf(oldNode)) { | |
| 248 startOffset++; | |
| 249 } | |
| 250 | |
| 251 if (endContainer == oldNode.parentNode | |
| 252 && endOffset == 1 + indexOf(oldNode)) { | |
| 253 endOffset++; | |
| 254 } | |
| 255 } | |
| 256 | |
| 257 return [startContainer, startOffset, endContainer, endOffset]; | |
| 258 } | |
| 259 | |
| 260 // The offset argument is unsigned, so per WebIDL -1 should wrap to 4294967295, | |
| 261 // which is probably longer than the length, so it should throw an exception. | |
| 262 // This is no different from the other cases where the offset is longer than | |
| 263 // the length, and the wrapping complicates my testing slightly, so I won't | |
| 264 // bother testing negative values here or in other cases. | |
| 265 var splitTextTests = []; | |
| 266 for (var i = 0; i < textNodes.length; i++) { | |
| 267 var node = textNodes[i]; | |
| 268 splitTextTests.push([node, 376, node, 0, node, 1]); | |
| 269 splitTextTests.push([node, 0, node, 0, node, 0]); | |
| 270 splitTextTests.push([node, 1, node, 1, node, 1]); | |
| 271 splitTextTests.push([node, node + ".length", node, node + ".length", node, nod
e + ".length"]); | |
| 272 splitTextTests.push([node, 1, node, 1, node, 3]); | |
| 273 splitTextTests.push([node, 2, node, 1, node, 3]); | |
| 274 splitTextTests.push([node, 3, node, 1, node, 3]); | |
| 275 } | |
| 276 | |
| 277 splitTextTests.push( | |
| 278 ["paras[0].firstChild", 1, "paras[0]", 0, "paras[0]", 0], | |
| 279 ["paras[0].firstChild", 1, "paras[0]", 0, "paras[0]", 1], | |
| 280 ["paras[0].firstChild", 1, "paras[0]", 1, "paras[0]", 1], | |
| 281 ["paras[0].firstChild", 1, "paras[0].firstChild", 1, "paras[0]", 1], | |
| 282 ["paras[0].firstChild", 2, "paras[0].firstChild", 1, "paras[0]", 1], | |
| 283 ["paras[0].firstChild", 3, "paras[0].firstChild", 1, "paras[0]", 1], | |
| 284 ["paras[0].firstChild", 1, "paras[0]", 0, "paras[0].firstChild", 3], | |
| 285 ["paras[0].firstChild", 2, "paras[0]", 0, "paras[0].firstChild", 3], | |
| 286 ["paras[0].firstChild", 3, "paras[0]", 0, "paras[0].firstChild", 3] | |
| 287 ); | |
| 288 | |
| 289 | |
| 290 function testReplaceDataAlgorithm(node, offset, count, data, callback, startCont
ainer, startOffset, endContainer, endOffset) { | |
| 291 // Mutation works the same any time DOM Core's "replace data" algorithm is | |
| 292 // invoked. node, offset, count, data are as in that algorithm. The | |
| 293 // callback is what does the actual setting. Not to be confused with | |
| 294 // testReplaceData, which tests the replaceData() method. | |
| 295 | |
| 296 // Barring any provision to the contrary, the containers and offsets must | |
| 297 // not change. | |
| 298 var expectedStartContainer = startContainer; | |
| 299 var expectedStartOffset = startOffset; | |
| 300 var expectedEndContainer = endContainer; | |
| 301 var expectedEndOffset = endOffset; | |
| 302 | |
| 303 var originalParent = node.parentNode; | |
| 304 var originalData = node.data; | |
| 305 | |
| 306 var exceptionThrown = false; | |
| 307 try { | |
| 308 callback(); | |
| 309 } catch (e) { | |
| 310 // Should only happen if offset is greater than length | |
| 311 exceptionThrown = true; | |
| 312 } | |
| 313 | |
| 314 assert_equals(node.parentNode, originalParent, | |
| 315 "Sanity check failed: changing data changed the parent"); | |
| 316 | |
| 317 // "User agents must run the following steps whenever they replace data of | |
| 318 // a CharacterData node, as though they were written in the specification | |
| 319 // for that algorithm after all other steps. In particular, the steps must | |
| 320 // not be executed if the algorithm threw an exception." | |
| 321 if (exceptionThrown) { | |
| 322 assert_equals(node.data, originalData, | |
| 323 "Sanity check failed: exception thrown but data changed"); | |
| 324 } else { | |
| 325 assert_equals(node.data, | |
| 326 originalData.substr(0, offset) + data + originalData.substr(offset + count
), | |
| 327 "Sanity check failed: data not changed as expected"); | |
| 328 } | |
| 329 | |
| 330 // "For every boundary point whose node is node, and whose offset is | |
| 331 // greater than offset but less than or equal to offset plus count, set | |
| 332 // its offset to offset." | |
| 333 if (!exceptionThrown | |
| 334 && startContainer == node | |
| 335 && startOffset > offset | |
| 336 && startOffset <= offset + count) { | |
| 337 expectedStartOffset = offset; | |
| 338 } | |
| 339 | |
| 340 if (!exceptionThrown | |
| 341 && endContainer == node | |
| 342 && endOffset > offset | |
| 343 && endOffset <= offset + count) { | |
| 344 expectedEndOffset = offset; | |
| 345 } | |
| 346 | |
| 347 // "For every boundary point whose node is node, and whose offset is | |
| 348 // greater than offset plus count, add the length of data to its offset, | |
| 349 // then subtract count from it." | |
| 350 if (!exceptionThrown | |
| 351 && startContainer == node | |
| 352 && startOffset > offset + count) { | |
| 353 expectedStartOffset += data.length - count; | |
| 354 } | |
| 355 | |
| 356 if (!exceptionThrown | |
| 357 && endContainer == node | |
| 358 && endOffset > offset + count) { | |
| 359 expectedEndOffset += data.length - count; | |
| 360 } | |
| 361 | |
| 362 return [expectedStartContainer, expectedStartOffset, expectedEndContainer, exp
ectedEndOffset]; | |
| 363 } | |
| 364 | |
| 365 function testInsertData(node, offset, data, startContainer, startOffset, endCont
ainer, endOffset) { | |
| 366 return testReplaceDataAlgorithm(node, offset, 0, data, | |
| 367 function() { node.insertData(offset, data) }, | |
| 368 startContainer, startOffset, endContainer, endOffset); | |
| 369 } | |
| 370 | |
| 371 var insertDataTests = []; | |
| 372 for (var i = 0; i < characterDataNodes.length; i++) { | |
| 373 var node = characterDataNodes[i]; | |
| 374 insertDataTests.push([node, 376, '"foo"', node, 0, node, 1]); | |
| 375 insertDataTests.push([node, 0, '"foo"', node, 0, node, 0]); | |
| 376 insertDataTests.push([node, 1, '"foo"', node, 1, node, 1]); | |
| 377 insertDataTests.push([node, node + ".length", '"foo"', node, node + ".length",
node, node + ".length"]); | |
| 378 insertDataTests.push([node, 1, '"foo"', node, 1, node, 3]); | |
| 379 insertDataTests.push([node, 2, '"foo"', node, 1, node, 3]); | |
| 380 insertDataTests.push([node, 3, '"foo"', node, 1, node, 3]); | |
| 381 | |
| 382 insertDataTests.push([node, 376, '""', node, 0, node, 1]); | |
| 383 insertDataTests.push([node, 0, '""', node, 0, node, 0]); | |
| 384 insertDataTests.push([node, 1, '""', node, 1, node, 1]); | |
| 385 insertDataTests.push([node, node + ".length", '""', node, node + ".length", no
de, node + ".length"]); | |
| 386 insertDataTests.push([node, 1, '""', node, 1, node, 3]); | |
| 387 insertDataTests.push([node, 2, '""', node, 1, node, 3]); | |
| 388 insertDataTests.push([node, 3, '""', node, 1, node, 3]); | |
| 389 } | |
| 390 | |
| 391 insertDataTests.push( | |
| 392 ["paras[0].firstChild", 1, '"foo"', "paras[0]", 0, "paras[0]", 0], | |
| 393 ["paras[0].firstChild", 1, '"foo"', "paras[0]", 0, "paras[0]", 1], | |
| 394 ["paras[0].firstChild", 1, '"foo"', "paras[0]", 1, "paras[0]", 1], | |
| 395 ["paras[0].firstChild", 1, '"foo"', "paras[0].firstChild", 1, "paras[0]", 1], | |
| 396 ["paras[0].firstChild", 2, '"foo"', "paras[0].firstChild", 1, "paras[0]", 1], | |
| 397 ["paras[0].firstChild", 3, '"foo"', "paras[0].firstChild", 1, "paras[0]", 1], | |
| 398 ["paras[0].firstChild", 1, '"foo"', "paras[0]", 0, "paras[0].firstChild", 3], | |
| 399 ["paras[0].firstChild", 2, '"foo"', "paras[0]", 0, "paras[0].firstChild", 3], | |
| 400 ["paras[0].firstChild", 3, '"foo"', "paras[0]", 0, "paras[0].firstChild", 3] | |
| 401 ); | |
| 402 | |
| 403 | |
| 404 function testAppendData(node, data, startContainer, startOffset, endContainer, e
ndOffset) { | |
| 405 return testReplaceDataAlgorithm(node, node.length, 0, data, | |
| 406 function() { node.appendData(data) }, | |
| 407 startContainer, startOffset, endContainer, endOffset); | |
| 408 } | |
| 409 | |
| 410 var appendDataTests = []; | |
| 411 for (var i = 0; i < characterDataNodes.length; i++) { | |
| 412 var node = characterDataNodes[i]; | |
| 413 appendDataTests.push([node, '"foo"', node, 0, node, 1]); | |
| 414 appendDataTests.push([node, '"foo"', node, 0, node, 0]); | |
| 415 appendDataTests.push([node, '"foo"', node, 1, node, 1]); | |
| 416 appendDataTests.push([node, '"foo"', node, 0, node, node + ".length"]); | |
| 417 appendDataTests.push([node, '"foo"', node, 1, node, node + ".length"]); | |
| 418 appendDataTests.push([node, '"foo"', node, node + ".length", node, node + ".le
ngth"]); | |
| 419 appendDataTests.push([node, '"foo"', node, 1, node, 3]); | |
| 420 | |
| 421 appendDataTests.push([node, '""', node, 0, node, 1]); | |
| 422 appendDataTests.push([node, '""', node, 0, node, 0]); | |
| 423 appendDataTests.push([node, '""', node, 1, node, 1]); | |
| 424 appendDataTests.push([node, '""', node, 0, node, node + ".length"]); | |
| 425 appendDataTests.push([node, '""', node, 1, node, node + ".length"]); | |
| 426 appendDataTests.push([node, '""', node, node + ".length", node, node + ".lengt
h"]); | |
| 427 appendDataTests.push([node, '""', node, 1, node, 3]); | |
| 428 } | |
| 429 | |
| 430 appendDataTests.push( | |
| 431 ["paras[0].firstChild", '""', "paras[0]", 0, "paras[0]", 0], | |
| 432 ["paras[0].firstChild", '""', "paras[0]", 0, "paras[0]", 1], | |
| 433 ["paras[0].firstChild", '""', "paras[0]", 1, "paras[0]", 1], | |
| 434 ["paras[0].firstChild", '""', "paras[0].firstChild", 1, "paras[0]", 1], | |
| 435 ["paras[0].firstChild", '""', "paras[0]", 0, "paras[0].firstChild", 3], | |
| 436 | |
| 437 ["paras[0].firstChild", '"foo"', "paras[0]", 0, "paras[0]", 0], | |
| 438 ["paras[0].firstChild", '"foo"', "paras[0]", 0, "paras[0]", 1], | |
| 439 ["paras[0].firstChild", '"foo"', "paras[0]", 1, "paras[0]", 1], | |
| 440 ["paras[0].firstChild", '"foo"', "paras[0].firstChild", 1, "paras[0]", 1], | |
| 441 ["paras[0].firstChild", '"foo"', "paras[0]", 0, "paras[0].firstChild", 3] | |
| 442 ); | |
| 443 | |
| 444 | |
| 445 function testDeleteData(node, offset, count, startContainer, startOffset, endCon
tainer, endOffset) { | |
| 446 return testReplaceDataAlgorithm(node, offset, count, "", | |
| 447 function() { node.deleteData(offset, count) }, | |
| 448 startContainer, startOffset, endContainer, endOffset); | |
| 449 } | |
| 450 | |
| 451 var deleteDataTests = []; | |
| 452 for (var i = 0; i < characterDataNodes.length; i++) { | |
| 453 var node = characterDataNodes[i]; | |
| 454 deleteDataTests.push([node, 376, 2, node, 0, node, 1]); | |
| 455 deleteDataTests.push([node, 0, 2, node, 0, node, 0]); | |
| 456 deleteDataTests.push([node, 1, 2, node, 1, node, 1]); | |
| 457 deleteDataTests.push([node, node + ".length", 2, node, node + ".length", node,
node + ".length"]); | |
| 458 deleteDataTests.push([node, 1, 2, node, 1, node, 3]); | |
| 459 deleteDataTests.push([node, 2, 2, node, 1, node, 3]); | |
| 460 deleteDataTests.push([node, 3, 2, node, 1, node, 3]); | |
| 461 | |
| 462 deleteDataTests.push([node, 376, 0, node, 0, node, 1]); | |
| 463 deleteDataTests.push([node, 0, 0, node, 0, node, 0]); | |
| 464 deleteDataTests.push([node, 1, 0, node, 1, node, 1]); | |
| 465 deleteDataTests.push([node, node + ".length", 0, node, node + ".length", node,
node + ".length"]); | |
| 466 deleteDataTests.push([node, 1, 0, node, 1, node, 3]); | |
| 467 deleteDataTests.push([node, 2, 0, node, 1, node, 3]); | |
| 468 deleteDataTests.push([node, 3, 0, node, 1, node, 3]); | |
| 469 | |
| 470 deleteDataTests.push([node, 376, 631, node, 0, node, 1]); | |
| 471 deleteDataTests.push([node, 0, 631, node, 0, node, 0]); | |
| 472 deleteDataTests.push([node, 1, 631, node, 1, node, 1]); | |
| 473 deleteDataTests.push([node, node + ".length", 631, node, node + ".length", nod
e, node + ".length"]); | |
| 474 deleteDataTests.push([node, 1, 631, node, 1, node, 3]); | |
| 475 deleteDataTests.push([node, 2, 631, node, 1, node, 3]); | |
| 476 deleteDataTests.push([node, 3, 631, node, 1, node, 3]); | |
| 477 } | |
| 478 | |
| 479 deleteDataTests.push( | |
| 480 ["paras[0].firstChild", 1, 2, "paras[0]", 0, "paras[0]", 0], | |
| 481 ["paras[0].firstChild", 1, 2, "paras[0]", 0, "paras[0]", 1], | |
| 482 ["paras[0].firstChild", 1, 2, "paras[0]", 1, "paras[0]", 1], | |
| 483 ["paras[0].firstChild", 1, 2, "paras[0].firstChild", 1, "paras[0]", 1], | |
| 484 ["paras[0].firstChild", 2, 2, "paras[0].firstChild", 1, "paras[0]", 1], | |
| 485 ["paras[0].firstChild", 3, 2, "paras[0].firstChild", 1, "paras[0]", 1], | |
| 486 ["paras[0].firstChild", 1, 2, "paras[0]", 0, "paras[0].firstChild", 3], | |
| 487 ["paras[0].firstChild", 2, 2, "paras[0]", 0, "paras[0].firstChild", 3], | |
| 488 ["paras[0].firstChild", 3, 2, "paras[0]", 0, "paras[0].firstChild", 3] | |
| 489 ); | |
| 490 | |
| 491 | |
| 492 function testReplaceData(node, offset, count, data, startContainer, startOffset,
endContainer, endOffset) { | |
| 493 return testReplaceDataAlgorithm(node, offset, count, data, | |
| 494 function() { node.replaceData(offset, count, data) }, | |
| 495 startContainer, startOffset, endContainer, endOffset); | |
| 496 } | |
| 497 | |
| 498 var replaceDataTests = []; | |
| 499 for (var i = 0; i < characterDataNodes.length; i++) { | |
| 500 var node = characterDataNodes[i]; | |
| 501 replaceDataTests.push([node, 376, 0, '"foo"', node, 0, node, 1]); | |
| 502 replaceDataTests.push([node, 0, 0, '"foo"', node, 0, node, 0]); | |
| 503 replaceDataTests.push([node, 1, 0, '"foo"', node, 1, node, 1]); | |
| 504 replaceDataTests.push([node, node + ".length", 0, '"foo"', node, node + ".leng
th", node, node + ".length"]); | |
| 505 replaceDataTests.push([node, 1, 0, '"foo"', node, 1, node, 3]); | |
| 506 replaceDataTests.push([node, 2, 0, '"foo"', node, 1, node, 3]); | |
| 507 replaceDataTests.push([node, 3, 0, '"foo"', node, 1, node, 3]); | |
| 508 | |
| 509 replaceDataTests.push([node, 376, 0, '""', node, 0, node, 1]); | |
| 510 replaceDataTests.push([node, 0, 0, '""', node, 0, node, 0]); | |
| 511 replaceDataTests.push([node, 1, 0, '""', node, 1, node, 1]); | |
| 512 replaceDataTests.push([node, node + ".length", 0, '""', node, node + ".length"
, node, node + ".length"]); | |
| 513 replaceDataTests.push([node, 1, 0, '""', node, 1, node, 3]); | |
| 514 replaceDataTests.push([node, 2, 0, '""', node, 1, node, 3]); | |
| 515 replaceDataTests.push([node, 3, 0, '""', node, 1, node, 3]); | |
| 516 | |
| 517 replaceDataTests.push([node, 376, 1, '"foo"', node, 0, node, 1]); | |
| 518 replaceDataTests.push([node, 0, 1, '"foo"', node, 0, node, 0]); | |
| 519 replaceDataTests.push([node, 1, 1, '"foo"', node, 1, node, 1]); | |
| 520 replaceDataTests.push([node, node + ".length", 1, '"foo"', node, node + ".leng
th", node, node + ".length"]); | |
| 521 replaceDataTests.push([node, 1, 1, '"foo"', node, 1, node, 3]); | |
| 522 replaceDataTests.push([node, 2, 1, '"foo"', node, 1, node, 3]); | |
| 523 replaceDataTests.push([node, 3, 1, '"foo"', node, 1, node, 3]); | |
| 524 | |
| 525 replaceDataTests.push([node, 376, 1, '""', node, 0, node, 1]); | |
| 526 replaceDataTests.push([node, 0, 1, '""', node, 0, node, 0]); | |
| 527 replaceDataTests.push([node, 1, 1, '""', node, 1, node, 1]); | |
| 528 replaceDataTests.push([node, node + ".length", 1, '""', node, node + ".length"
, node, node + ".length"]); | |
| 529 replaceDataTests.push([node, 1, 1, '""', node, 1, node, 3]); | |
| 530 replaceDataTests.push([node, 2, 1, '""', node, 1, node, 3]); | |
| 531 replaceDataTests.push([node, 3, 1, '""', node, 1, node, 3]); | |
| 532 | |
| 533 replaceDataTests.push([node, 376, 47, '"foo"', node, 0, node, 1]); | |
| 534 replaceDataTests.push([node, 0, 47, '"foo"', node, 0, node, 0]); | |
| 535 replaceDataTests.push([node, 1, 47, '"foo"', node, 1, node, 1]); | |
| 536 replaceDataTests.push([node, node + ".length", 47, '"foo"', node, node + ".len
gth", node, node + ".length"]); | |
| 537 replaceDataTests.push([node, 1, 47, '"foo"', node, 1, node, 3]); | |
| 538 replaceDataTests.push([node, 2, 47, '"foo"', node, 1, node, 3]); | |
| 539 replaceDataTests.push([node, 3, 47, '"foo"', node, 1, node, 3]); | |
| 540 | |
| 541 replaceDataTests.push([node, 376, 47, '""', node, 0, node, 1]); | |
| 542 replaceDataTests.push([node, 0, 47, '""', node, 0, node, 0]); | |
| 543 replaceDataTests.push([node, 1, 47, '""', node, 1, node, 1]); | |
| 544 replaceDataTests.push([node, node + ".length", 47, '""', node, node + ".length
", node, node + ".length"]); | |
| 545 replaceDataTests.push([node, 1, 47, '""', node, 1, node, 3]); | |
| 546 replaceDataTests.push([node, 2, 47, '""', node, 1, node, 3]); | |
| 547 replaceDataTests.push([node, 3, 47, '""', node, 1, node, 3]); | |
| 548 } | |
| 549 | |
| 550 replaceDataTests.push( | |
| 551 ["paras[0].firstChild", 1, 0, '"foo"', "paras[0]", 0, "paras[0]", 0], | |
| 552 ["paras[0].firstChild", 1, 0, '"foo"', "paras[0]", 0, "paras[0]", 1], | |
| 553 ["paras[0].firstChild", 1, 0, '"foo"', "paras[0]", 1, "paras[0]", 1], | |
| 554 ["paras[0].firstChild", 1, 0, '"foo"', "paras[0].firstChild", 1, "paras[0]", 1
], | |
| 555 ["paras[0].firstChild", 2, 0, '"foo"', "paras[0].firstChild", 1, "paras[0]", 1
], | |
| 556 ["paras[0].firstChild", 3, 0, '"foo"', "paras[0].firstChild", 1, "paras[0]", 1
], | |
| 557 ["paras[0].firstChild", 1, 0, '"foo"', "paras[0]", 0, "paras[0].firstChild", 3
], | |
| 558 ["paras[0].firstChild", 2, 0, '"foo"', "paras[0]", 0, "paras[0].firstChild", 3
], | |
| 559 ["paras[0].firstChild", 3, 0, '"foo"', "paras[0]", 0, "paras[0].firstChild", 3
], | |
| 560 | |
| 561 ["paras[0].firstChild", 1, 1, '"foo"', "paras[0]", 0, "paras[0]", 0], | |
| 562 ["paras[0].firstChild", 1, 1, '"foo"', "paras[0]", 0, "paras[0]", 1], | |
| 563 ["paras[0].firstChild", 1, 1, '"foo"', "paras[0]", 1, "paras[0]", 1], | |
| 564 ["paras[0].firstChild", 1, 1, '"foo"', "paras[0].firstChild", 1, "paras[0]", 1
], | |
| 565 ["paras[0].firstChild", 2, 1, '"foo"', "paras[0].firstChild", 1, "paras[0]", 1
], | |
| 566 ["paras[0].firstChild", 3, 1, '"foo"', "paras[0].firstChild", 1, "paras[0]", 1
], | |
| 567 ["paras[0].firstChild", 1, 1, '"foo"', "paras[0]", 0, "paras[0].firstChild", 3
], | |
| 568 ["paras[0].firstChild", 2, 1, '"foo"', "paras[0]", 0, "paras[0].firstChild", 3
], | |
| 569 ["paras[0].firstChild", 3, 1, '"foo"', "paras[0]", 0, "paras[0].firstChild", 3
], | |
| 570 | |
| 571 ["paras[0].firstChild", 1, 47, '"foo"', "paras[0]", 0, "paras[0]", 0], | |
| 572 ["paras[0].firstChild", 1, 47, '"foo"', "paras[0]", 0, "paras[0]", 1], | |
| 573 ["paras[0].firstChild", 1, 47, '"foo"', "paras[0]", 1, "paras[0]", 1], | |
| 574 ["paras[0].firstChild", 1, 47, '"foo"', "paras[0].firstChild", 1, "paras[0]",
1], | |
| 575 ["paras[0].firstChild", 2, 47, '"foo"', "paras[0].firstChild", 1, "paras[0]",
1], | |
| 576 ["paras[0].firstChild", 3, 47, '"foo"', "paras[0].firstChild", 1, "paras[0]",
1], | |
| 577 ["paras[0].firstChild", 1, 47, '"foo"', "paras[0]", 0, "paras[0].firstChild",
3], | |
| 578 ["paras[0].firstChild", 2, 47, '"foo"', "paras[0]", 0, "paras[0].firstChild",
3], | |
| 579 ["paras[0].firstChild", 3, 47, '"foo"', "paras[0]", 0, "paras[0].firstChild",
3] | |
| 580 ); | |
| 581 | |
| 582 | |
| 583 // There are lots of ways to set data, so we pass a callback that does the | |
| 584 // actual setting. | |
| 585 function testDataChange(node, attr, op, rval, startContainer, startOffset, endCo
ntainer, endOffset) { | |
| 586 return testReplaceDataAlgorithm(node, 0, node.length, op == "=" ? rval : node[
attr] + rval, | |
| 587 function() { | |
| 588 if (op == "=") { | |
| 589 node[attr] = rval; | |
| 590 } else if (op == "+=") { | |
| 591 node[attr] += rval; | |
| 592 } else { | |
| 593 throw "Unknown op " + op; | |
| 594 } | |
| 595 }, | |
| 596 startContainer, startOffset, endContainer, endOffset); | |
| 597 } | |
| 598 | |
| 599 var dataChangeTests = []; | |
| 600 var dataChangeTestAttrs = ["data", "textContent", "nodeValue"]; | |
| 601 for (var i = 0; i < characterDataNodes.length; i++) { | |
| 602 var node = characterDataNodes[i]; | |
| 603 var dataChangeTestRanges = [ | |
| 604 [node, 0, node, 0], | |
| 605 [node, 0, node, 1], | |
| 606 [node, 1, node, 1], | |
| 607 [node, 0, node, node + ".length"], | |
| 608 [node, 1, node, node + ".length"], | |
| 609 [node, node + ".length", node, node + ".length"], | |
| 610 ]; | |
| 611 | |
| 612 for (var j = 0; j < dataChangeTestRanges.length; j++) { | |
| 613 for (var k = 0; k < dataChangeTestAttrs.length; k++) { | |
| 614 dataChangeTests.push([ | |
| 615 node, | |
| 616 '"' + dataChangeTestAttrs[k] + '"', | |
| 617 '"="', | |
| 618 '""', | |
| 619 ].concat(dataChangeTestRanges[j])); | |
| 620 | |
| 621 dataChangeTests.push([ | |
| 622 node, | |
| 623 '"' + dataChangeTestAttrs[k] + '"', | |
| 624 '"="', | |
| 625 '"foo"', | |
| 626 ].concat(dataChangeTestRanges[j])); | |
| 627 | |
| 628 dataChangeTests.push([ | |
| 629 node, | |
| 630 '"' + dataChangeTestAttrs[k] + '"', | |
| 631 '"="', | |
| 632 node + "." + dataChangeTestAttrs[k], | |
| 633 ].concat(dataChangeTestRanges[j])); | |
| 634 | |
| 635 dataChangeTests.push([ | |
| 636 node, | |
| 637 '"' + dataChangeTestAttrs[k] + '"', | |
| 638 '"+="', | |
| 639 '""', | |
| 640 ].concat(dataChangeTestRanges[j])); | |
| 641 | |
| 642 dataChangeTests.push([ | |
| 643 node, | |
| 644 '"' + dataChangeTestAttrs[k] + '"', | |
| 645 '"+="', | |
| 646 '"foo"', | |
| 647 ].concat(dataChangeTestRanges[j])); | |
| 648 | |
| 649 dataChangeTests.push([ | |
| 650 node, | |
| 651 '"' + dataChangeTestAttrs[k] + '"', | |
| 652 '"+="', | |
| 653 node + "." + dataChangeTestAttrs[k] | |
| 654 ].concat(dataChangeTestRanges[j])); | |
| 655 } | |
| 656 } | |
| 657 } | |
| 658 | |
| 659 | |
| 660 // Now we test node insertions and deletions, as opposed to just data changes. | |
| 661 // To avoid loads of repetition, we define modifyForRemove() and | |
| 662 // modifyForInsert(). | |
| 663 | |
| 664 // If we were to remove removedNode from its parent, what would the boundary | |
| 665 // point [node, offset] become? Returns [new node, new offset]. Must be | |
| 666 // called BEFORE the node is actually removed, so its parent is not null. (If | |
| 667 // the parent is null, it will do nothing.) | |
| 668 function modifyForRemove(removedNode, point) { | |
| 669 var oldParent = removedNode.parentNode; | |
| 670 var oldIndex = indexOf(removedNode); | |
| 671 if (!oldParent) { | |
| 672 return point; | |
| 673 } | |
| 674 | |
| 675 // "For each boundary point whose node is removed node or a descendant of | |
| 676 // it, set the boundary point to (old parent, old index)." | |
| 677 if (point[0] == removedNode || isDescendant(point[0], removedNode)) { | |
| 678 return [oldParent, oldIndex]; | |
| 679 } | |
| 680 | |
| 681 // "For each boundary point whose node is old parent and whose offset is | |
| 682 // greater than old index, subtract one from its offset." | |
| 683 if (point[0] == oldParent && point[1] > oldIndex) { | |
| 684 return [point[0], point[1] - 1]; | |
| 685 } | |
| 686 | |
| 687 return point; | |
| 688 } | |
| 689 | |
| 690 // Update the given boundary point [node, offset] to account for the fact that | |
| 691 // insertedNode was just inserted into its current position. This must be | |
| 692 // called AFTER insertedNode was already inserted. | |
| 693 function modifyForInsert(insertedNode, point) { | |
| 694 // "For each boundary point whose node is the new parent of the affected | |
| 695 // node and whose offset is greater than the new index of the affected | |
| 696 // node, add one to the boundary point's offset." | |
| 697 if (point[0] == insertedNode.parentNode && point[1] > indexOf(insertedNode)) { | |
| 698 return [point[0], point[1] + 1]; | |
| 699 } | |
| 700 | |
| 701 return point; | |
| 702 } | |
| 703 | |
| 704 | |
| 705 function testInsertBefore(newParent, affectedNode, refNode, startContainer, star
tOffset, endContainer, endOffset) { | |
| 706 var expectedStart = [startContainer, startOffset]; | |
| 707 var expectedEnd = [endContainer, endOffset]; | |
| 708 | |
| 709 expectedStart = modifyForRemove(affectedNode, expectedStart); | |
| 710 expectedEnd = modifyForRemove(affectedNode, expectedEnd); | |
| 711 | |
| 712 try { | |
| 713 newParent.insertBefore(affectedNode, refNode); | |
| 714 } catch (e) { | |
| 715 // For our purposes, assume that DOM Core is true -- i.e., ignore | |
| 716 // mutation events and similar. | |
| 717 return [startContainer, startOffset, endContainer, endOffset]; | |
| 718 } | |
| 719 | |
| 720 expectedStart = modifyForInsert(affectedNode, expectedStart); | |
| 721 expectedEnd = modifyForInsert(affectedNode, expectedEnd); | |
| 722 | |
| 723 return expectedStart.concat(expectedEnd); | |
| 724 } | |
| 725 | |
| 726 var insertBeforeTests = [ | |
| 727 // Moving a node to its current position | |
| 728 ["testDiv", "paras[0]", "paras[1]", "paras[0]", 0, "paras[0]", 0], | |
| 729 ["testDiv", "paras[0]", "paras[1]", "paras[0]", 0, "paras[0]", 1], | |
| 730 ["testDiv", "paras[0]", "paras[1]", "paras[0]", 1, "paras[0]", 1], | |
| 731 ["testDiv", "paras[0]", "paras[1]", "testDiv", 0, "testDiv", 2], | |
| 732 ["testDiv", "paras[0]", "paras[1]", "testDiv", 1, "testDiv", 1], | |
| 733 ["testDiv", "paras[0]", "paras[1]", "testDiv", 1, "testDiv", 2], | |
| 734 ["testDiv", "paras[0]", "paras[1]", "testDiv", 2, "testDiv", 2], | |
| 735 | |
| 736 // Stuff that actually moves something. Note that paras[0] and paras[1] | |
| 737 // are both children of testDiv. | |
| 738 ["paras[0]", "paras[1]", "paras[0].firstChild", "paras[0]", 0, "paras[0]", 0], | |
| 739 ["paras[0]", "paras[1]", "paras[0].firstChild", "paras[0]", 0, "paras[0]", 1], | |
| 740 ["paras[0]", "paras[1]", "paras[0].firstChild", "paras[0]", 1, "paras[0]", 1], | |
| 741 ["paras[0]", "paras[1]", "paras[0].firstChild", "testDiv", 0, "testDiv", 1], | |
| 742 ["paras[0]", "paras[1]", "paras[0].firstChild", "testDiv", 0, "testDiv", 2], | |
| 743 ["paras[0]", "paras[1]", "paras[0].firstChild", "testDiv", 1, "testDiv", 1], | |
| 744 ["paras[0]", "paras[1]", "paras[0].firstChild", "testDiv", 1, "testDiv", 2], | |
| 745 ["paras[0]", "paras[1]", "null", "paras[0]", 0, "paras[0]", 0], | |
| 746 ["paras[0]", "paras[1]", "null", "paras[0]", 0, "paras[0]", 1], | |
| 747 ["paras[0]", "paras[1]", "null", "paras[0]", 1, "paras[0]", 1], | |
| 748 ["paras[0]", "paras[1]", "null", "testDiv", 0, "testDiv", 1], | |
| 749 ["paras[0]", "paras[1]", "null", "testDiv", 0, "testDiv", 2], | |
| 750 ["paras[0]", "paras[1]", "null", "testDiv", 1, "testDiv", 1], | |
| 751 ["paras[0]", "paras[1]", "null", "testDiv", 1, "testDiv", 2], | |
| 752 ["foreignDoc", "detachedComment", "foreignDoc.documentElement", "foreignDoc",
0, "foreignDoc", 0], | |
| 753 ["foreignDoc", "detachedComment", "foreignDoc.documentElement", "foreignDoc",
0, "foreignDoc", 1], | |
| 754 ["foreignDoc", "detachedComment", "foreignDoc.documentElement", "foreignDoc",
0, "foreignDoc", 2], | |
| 755 ["foreignDoc", "detachedComment", "foreignDoc.documentElement", "foreignDoc",
1, "foreignDoc", 1], | |
| 756 ["foreignDoc", "detachedComment", "foreignDoc.doctype", "foreignDoc", 0, "fore
ignDoc", 0], | |
| 757 ["foreignDoc", "detachedComment", "foreignDoc.doctype", "foreignDoc", 0, "fore
ignDoc", 1], | |
| 758 ["foreignDoc", "detachedComment", "foreignDoc.doctype", "foreignDoc", 0, "fore
ignDoc", 2], | |
| 759 ["foreignDoc", "detachedComment", "foreignDoc.doctype", "foreignDoc", 1, "fore
ignDoc", 1], | |
| 760 ["foreignDoc", "detachedComment", "null", "foreignDoc", 0, "foreignDoc", 1], | |
| 761 ["paras[0]", "xmlTextNode", "paras[0].firstChild", "paras[0]", 0, "paras[0]",
0], | |
| 762 ["paras[0]", "xmlTextNode", "paras[0].firstChild", "paras[0]", 0, "paras[0]",
1], | |
| 763 ["paras[0]", "xmlTextNode", "paras[0].firstChild", "paras[0]", 1, "paras[0]",
1], | |
| 764 | |
| 765 // Stuff that throws exceptions | |
| 766 ["paras[0]", "paras[0]", "paras[0].firstChild", "paras[0]", 0, "paras[0]", 1], | |
| 767 ["paras[0]", "testDiv", "paras[0].firstChild", "paras[0]", 0, "paras[0]", 1], | |
| 768 ["paras[0]", "document", "paras[0].firstChild", "paras[0]", 0, "paras[0]", 1], | |
| 769 ["paras[0]", "foreignDoc", "paras[0].firstChild", "paras[0]", 0, "paras[0]", 1
], | |
| 770 ["paras[0]", "document.doctype", "paras[0].firstChild", "paras[0]", 0, "paras[
0]", 1], | |
| 771 ]; | |
| 772 | |
| 773 | |
| 774 function testReplaceChild(newParent, newChild, oldChild, startContainer, startOf
fset, endContainer, endOffset) { | |
| 775 var expectedStart = [startContainer, startOffset]; | |
| 776 var expectedEnd = [endContainer, endOffset]; | |
| 777 | |
| 778 expectedStart = modifyForRemove(oldChild, expectedStart); | |
| 779 expectedEnd = modifyForRemove(oldChild, expectedEnd); | |
| 780 | |
| 781 if (newChild != oldChild) { | |
| 782 // Don't do this twice, if they're the same! | |
| 783 expectedStart = modifyForRemove(newChild, expectedStart); | |
| 784 expectedEnd = modifyForRemove(newChild, expectedEnd); | |
| 785 } | |
| 786 | |
| 787 try { | |
| 788 newParent.replaceChild(newChild, oldChild); | |
| 789 } catch (e) { | |
| 790 return [startContainer, startOffset, endContainer, endOffset]; | |
| 791 } | |
| 792 | |
| 793 expectedStart = modifyForInsert(newChild, expectedStart); | |
| 794 expectedEnd = modifyForInsert(newChild, expectedEnd); | |
| 795 | |
| 796 return expectedStart.concat(expectedEnd); | |
| 797 } | |
| 798 | |
| 799 var replaceChildTests = [ | |
| 800 // Moving a node to its current position. Doesn't match most browsers' | |
| 801 // behavior, but we probably want to keep the spec the same anyway: | |
| 802 // https://bugzilla.mozilla.org/show_bug.cgi?id=647603 | |
| 803 ["testDiv", "paras[0]", "paras[0]", "paras[0]", 0, "paras[0]", 0], | |
| 804 ["testDiv", "paras[0]", "paras[0]", "paras[0]", 0, "paras[0]", 1], | |
| 805 ["testDiv", "paras[0]", "paras[0]", "paras[0]", 1, "paras[0]", 1], | |
| 806 ["testDiv", "paras[0]", "paras[0]", "testDiv", 0, "testDiv", 2], | |
| 807 ["testDiv", "paras[0]", "paras[0]", "testDiv", 1, "testDiv", 1], | |
| 808 ["testDiv", "paras[0]", "paras[0]", "testDiv", 1, "testDiv", 2], | |
| 809 ["testDiv", "paras[0]", "paras[0]", "testDiv", 2, "testDiv", 2], | |
| 810 | |
| 811 // Stuff that actually moves something. | |
| 812 ["paras[0]", "paras[1]", "paras[0].firstChild", "paras[0]", 0, "paras[0]", 0], | |
| 813 ["paras[0]", "paras[1]", "paras[0].firstChild", "paras[0]", 0, "paras[0]", 1], | |
| 814 ["paras[0]", "paras[1]", "paras[0].firstChild", "paras[0]", 1, "paras[0]", 1], | |
| 815 ["paras[0]", "paras[1]", "paras[0].firstChild", "testDiv", 0, "testDiv", 1], | |
| 816 ["paras[0]", "paras[1]", "paras[0].firstChild", "testDiv", 0, "testDiv", 2], | |
| 817 ["paras[0]", "paras[1]", "paras[0].firstChild", "testDiv", 1, "testDiv", 1], | |
| 818 ["paras[0]", "paras[1]", "paras[0].firstChild", "testDiv", 1, "testDiv", 2], | |
| 819 ["foreignDoc", "detachedComment", "foreignDoc.documentElement", "foreignDoc",
0, "foreignDoc", 0], | |
| 820 ["foreignDoc", "detachedComment", "foreignDoc.documentElement", "foreignDoc",
0, "foreignDoc", 1], | |
| 821 ["foreignDoc", "detachedComment", "foreignDoc.documentElement", "foreignDoc",
0, "foreignDoc", 2], | |
| 822 ["foreignDoc", "detachedComment", "foreignDoc.documentElement", "foreignDoc",
1, "foreignDoc", 1], | |
| 823 ["foreignDoc", "detachedComment", "foreignDoc.doctype", "foreignDoc", 0, "fore
ignDoc", 0], | |
| 824 ["foreignDoc", "detachedComment", "foreignDoc.doctype", "foreignDoc", 0, "fore
ignDoc", 1], | |
| 825 ["foreignDoc", "detachedComment", "foreignDoc.doctype", "foreignDoc", 0, "fore
ignDoc", 2], | |
| 826 ["foreignDoc", "detachedComment", "foreignDoc.doctype", "foreignDoc", 1, "fore
ignDoc", 1], | |
| 827 ["paras[0]", "xmlTextNode", "paras[0].firstChild", "paras[0]", 0, "paras[0]",
0], | |
| 828 ["paras[0]", "xmlTextNode", "paras[0].firstChild", "paras[0]", 0, "paras[0]",
1], | |
| 829 ["paras[0]", "xmlTextNode", "paras[0].firstChild", "paras[0]", 1, "paras[0]",
1], | |
| 830 | |
| 831 // Stuff that throws exceptions | |
| 832 ["paras[0]", "paras[0]", "paras[0].firstChild", "paras[0]", 0, "paras[0]", 1], | |
| 833 ["paras[0]", "testDiv", "paras[0].firstChild", "paras[0]", 0, "paras[0]", 1], | |
| 834 ["paras[0]", "document", "paras[0].firstChild", "paras[0]", 0, "paras[0]", 1], | |
| 835 ["paras[0]", "foreignDoc", "paras[0].firstChild", "paras[0]", 0, "paras[0]", 1
], | |
| 836 ["paras[0]", "document.doctype", "paras[0].firstChild", "paras[0]", 0, "paras[
0]", 1], | |
| 837 ]; | |
| 838 | |
| 839 | |
| 840 function testAppendChild(newParent, affectedNode, startContainer, startOffset, e
ndContainer, endOffset) { | |
| 841 var expectedStart = [startContainer, startOffset]; | |
| 842 var expectedEnd = [endContainer, endOffset]; | |
| 843 | |
| 844 expectedStart = modifyForRemove(affectedNode, expectedStart); | |
| 845 expectedEnd = modifyForRemove(affectedNode, expectedEnd); | |
| 846 | |
| 847 try { | |
| 848 newParent.appendChild(affectedNode); | |
| 849 } catch (e) { | |
| 850 return [startContainer, startOffset, endContainer, endOffset]; | |
| 851 } | |
| 852 | |
| 853 // These two lines will actually never do anything, if you think about it, | |
| 854 // but let's leave them in so correctness is more obvious. | |
| 855 expectedStart = modifyForInsert(affectedNode, expectedStart); | |
| 856 expectedEnd = modifyForInsert(affectedNode, expectedEnd); | |
| 857 | |
| 858 return expectedStart.concat(expectedEnd); | |
| 859 } | |
| 860 | |
| 861 var appendChildTests = [ | |
| 862 // Moving a node to its current position | |
| 863 ["testDiv", "testDiv.lastChild", "testDiv.lastChild", 0, "testDiv.lastChild",
0], | |
| 864 ["testDiv", "testDiv.lastChild", "testDiv.lastChild", 0, "testDiv.lastChild",
1], | |
| 865 ["testDiv", "testDiv.lastChild", "testDiv.lastChild", 1, "testDiv.lastChild",
1], | |
| 866 ["testDiv", "testDiv.lastChild", "testDiv", "testDiv.childNodes.length - 2", "
testDiv", "testDiv.childNodes.length"], | |
| 867 ["testDiv", "testDiv.lastChild", "testDiv", "testDiv.childNodes.length - 2", "
testDiv", "testDiv.childNodes.length - 1"], | |
| 868 ["testDiv", "testDiv.lastChild", "testDiv", "testDiv.childNodes.length - 1", "
testDiv", "testDiv.childNodes.length"], | |
| 869 ["testDiv", "testDiv.lastChild", "testDiv", "testDiv.childNodes.length - 1", "
testDiv", "testDiv.childNodes.length - 1"], | |
| 870 ["testDiv", "testDiv.lastChild", "testDiv", "testDiv.childNodes.length", "test
Div", "testDiv.childNodes.length"], | |
| 871 ["detachedDiv", "detachedDiv.lastChild", "detachedDiv.lastChild", 0, "detached
Div.lastChild", 0], | |
| 872 ["detachedDiv", "detachedDiv.lastChild", "detachedDiv.lastChild", 0, "detached
Div.lastChild", 1], | |
| 873 ["detachedDiv", "detachedDiv.lastChild", "detachedDiv.lastChild", 1, "detached
Div.lastChild", 1], | |
| 874 ["detachedDiv", "detachedDiv.lastChild", "detachedDiv", "detachedDiv.childNode
s.length - 2", "detachedDiv", "detachedDiv.childNodes.length"], | |
| 875 ["detachedDiv", "detachedDiv.lastChild", "detachedDiv", "detachedDiv.childNode
s.length - 2", "detachedDiv", "detachedDiv.childNodes.length - 1"], | |
| 876 ["detachedDiv", "detachedDiv.lastChild", "detachedDiv", "detachedDiv.childNode
s.length - 1", "detachedDiv", "detachedDiv.childNodes.length"], | |
| 877 ["detachedDiv", "detachedDiv.lastChild", "detachedDiv", "detachedDiv.childNode
s.length - 1", "detachedDiv", "detachedDiv.childNodes.length - 1"], | |
| 878 ["detachedDiv", "detachedDiv.lastChild", "detachedDiv", "detachedDiv.childNode
s.length", "detachedDiv", "detachedDiv.childNodes.length"], | |
| 879 | |
| 880 // Stuff that actually moves something | |
| 881 ["paras[0]", "paras[1]", "paras[0]", 0, "paras[0]", 0], | |
| 882 ["paras[0]", "paras[1]", "paras[0]", 0, "paras[0]", 1], | |
| 883 ["paras[0]", "paras[1]", "paras[0]", 1, "paras[0]", 1], | |
| 884 ["paras[0]", "paras[1]", "testDiv", 0, "testDiv", 1], | |
| 885 ["paras[0]", "paras[1]", "testDiv", 0, "testDiv", 2], | |
| 886 ["paras[0]", "paras[1]", "testDiv", 1, "testDiv", 1], | |
| 887 ["paras[0]", "paras[1]", "testDiv", 1, "testDiv", 2], | |
| 888 ["foreignDoc", "detachedComment", "foreignDoc", "foreignDoc.childNodes.length
- 1", "foreignDoc", "foreignDoc.childNodes.length"], | |
| 889 ["foreignDoc", "detachedComment", "foreignDoc", "foreignDoc.childNodes.length
- 1", "foreignDoc", "foreignDoc.childNodes.length - 1"], | |
| 890 ["foreignDoc", "detachedComment", "foreignDoc", "foreignDoc.childNodes.length"
, "foreignDoc", "foreignDoc.childNodes.length"], | |
| 891 ["foreignDoc", "detachedComment", "detachedComment", 0, "detachedComment", 5], | |
| 892 ["paras[0]", "xmlTextNode", "paras[0]", 0, "paras[0]", 0], | |
| 893 ["paras[0]", "xmlTextNode", "paras[0]", 0, "paras[0]", 1], | |
| 894 ["paras[0]", "xmlTextNode", "paras[0]", 1, "paras[0]", 1], | |
| 895 | |
| 896 // Stuff that throws exceptions | |
| 897 ["paras[0]", "paras[0]", "paras[0]", 0, "paras[0]", 1], | |
| 898 ["paras[0]", "testDiv", "paras[0]", 0, "paras[0]", 1], | |
| 899 ["paras[0]", "document", "paras[0]", 0, "paras[0]", 1], | |
| 900 ["paras[0]", "foreignDoc", "paras[0]", 0, "paras[0]", 1], | |
| 901 ["paras[0]", "document.doctype", "paras[0]", 0, "paras[0]", 1], | |
| 902 ]; | |
| 903 | |
| 904 | |
| 905 function testRemoveChild(affectedNode, startContainer, startOffset, endContainer
, endOffset) { | |
| 906 var expectedStart = [startContainer, startOffset]; | |
| 907 var expectedEnd = [endContainer, endOffset]; | |
| 908 | |
| 909 expectedStart = modifyForRemove(affectedNode, expectedStart); | |
| 910 expectedEnd = modifyForRemove(affectedNode, expectedEnd); | |
| 911 | |
| 912 // We don't test cases where the parent is wrong, so this should never | |
| 913 // throw an exception. | |
| 914 affectedNode.parentNode.removeChild(affectedNode); | |
| 915 | |
| 916 return expectedStart.concat(expectedEnd); | |
| 917 } | |
| 918 | |
| 919 var removeChildTests = [ | |
| 920 ["paras[0]", "paras[0]", 0, "paras[0]", 0], | |
| 921 ["paras[0]", "paras[0]", 0, "paras[0]", 1], | |
| 922 ["paras[0]", "paras[0]", 1, "paras[0]", 1], | |
| 923 ["paras[0]", "testDiv", 0, "testDiv", 0], | |
| 924 ["paras[0]", "testDiv", 0, "testDiv", 1], | |
| 925 ["paras[0]", "testDiv", 1, "testDiv", 1], | |
| 926 ["paras[0]", "testDiv", 0, "testDiv", 2], | |
| 927 ["paras[0]", "testDiv", 1, "testDiv", 2], | |
| 928 ["paras[0]", "testDiv", 2, "testDiv", 2], | |
| 929 | |
| 930 ["foreignDoc.documentElement", "foreignDoc", 0, "foreignDoc", "foreignDoc.chil
dNodes.length"], | |
| 931 ]; | |
| 932 | |
| 933 | |
| 934 // Finally run everything. All grouped together at the end so that I can | |
| 935 // easily comment out some of them, so I don't have to wait for all test types | |
| 936 // to debug only some of them. | |
| 937 doTests(splitTextTests, function(params) { return params[0] + ".splitText(" + pa
rams[1] + ")" }, testSplitText); | |
| 938 doTests(insertDataTests, function(params) { return params[0] + ".insertData(" +
params[1] + ", " + params[2] + ")" }, testInsertData); | |
| 939 doTests(appendDataTests, function(params) { return params[0] + ".appendData(" +
params[1] + ")" }, testAppendData); | |
| 940 doTests(deleteDataTests, function(params) { return params[0] + ".deleteData(" +
params[1] + ", " + params[2] + ")" }, testDeleteData); | |
| 941 doTests(replaceDataTests, function(params) { return params[0] + ".replaceData("
+ params[1] + ", " + params[2] + ", " + params[3] + ")" }, testReplaceData); | |
| 942 doTests(dataChangeTests, function(params) { return params[0] + "." + eval(params
[1]) + " " + eval(params[2]) + ' ' + params[3] }, testDataChange); | |
| 943 doTests(insertBeforeTests, function(params) { return params[0] + ".insertBefore(
" + params[1] + ", " + params[2] + ")" }, testInsertBefore); | |
| 944 doTests(replaceChildTests, function(params) { return params[0] + ".replaceChild(
" + params[1] + ", " + params[2] + ")" }, testReplaceChild); | |
| 945 doTests(appendChildTests, function(params) { return params[0] + ".appendChild("
+ params[1] + ")" }, testAppendChild); | |
| 946 doTests(removeChildTests, function(params) { return params[0] + ".parentNode.rem
oveChild(" + params[0] + ")" }, testRemoveChild); | |
| 947 | |
| 948 | |
| 949 testDiv.style.display = "none"; | |
| 950 </script> | |
| OLD | NEW |