Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(21)

Side by Side Diff: third_party/WebKit/LayoutTests/imported/wpt/dom/ranges/Range-cloneContents.html

Issue 2482213003: Import WPT tests which require non-test HTML resources. (Closed)
Patch Set: Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 <!doctype html>
2 <title>Range.cloneContents() tests</title>
3 <link rel="author" title="Aryeh Gregor" href=ayg@aryeh.name>
4 <meta name=timeout content=long>
5 <p>To debug test failures, add a query parameter "subtest" with the test id (lik e
6 "?subtest=5"). Only that test will be run. Then you can look at the resulting
7 iframe in the DOM.
8 <div id=log></div>
9 <script src=/resources/testharness.js></script>
10 <script src=/resources/testharnessreport.js></script>
11 <script src=../common.js></script>
12 <script>
13 "use strict";
14
15 testDiv.parentNode.removeChild(testDiv);
16
17 var actualIframe = document.createElement("iframe");
18 actualIframe.style.display = "none";
19 document.body.appendChild(actualIframe);
20
21 var expectedIframe = document.createElement("iframe");
22 expectedIframe.style.display = "none";
23 document.body.appendChild(expectedIframe);
24
25 function myCloneContents(range) {
26 // "Let frag be a new DocumentFragment whose ownerDocument is the same as
27 // the ownerDocument of the context object's start node."
28 var ownerDoc = range.startContainer.nodeType == Node.DOCUMENT_NODE
29 ? range.startContainer
30 : range.startContainer.ownerDocument;
31 var frag = ownerDoc.createDocumentFragment();
32
33 // "If the context object's start and end are the same, abort this method,
34 // returning frag."
35 if (range.startContainer == range.endContainer
36 && range.startOffset == range.endOffset) {
37 return frag;
38 }
39
40 // "Let original start node, original start offset, original end node, and
41 // original end offset be the context object's start and end nodes and
42 // offsets, respectively."
43 var originalStartNode = range.startContainer;
44 var originalStartOffset = range.startOffset;
45 var originalEndNode = range.endContainer;
46 var originalEndOffset = range.endOffset;
47
48 // "If original start node and original end node are the same, and they are
49 // a Text, ProcessingInstruction, or Comment node:"
50 if (range.startContainer == range.endContainer
51 && (range.startContainer.nodeType == Node.TEXT_NODE
52 || range.startContainer.nodeType == Node.COMMENT_NODE
53 || range.startContainer.nodeType == Node.PROCESSING_INSTRUCTION_NODE)) {
54 // "Let clone be the result of calling cloneNode(false) on original
55 // start node."
56 var clone = originalStartNode.cloneNode(false);
57
58 // "Set the data of clone to the result of calling
59 // substringData(original start offset, original end offset − original
60 // start offset) on original start node."
61 clone.data = originalStartNode.substringData(originalStartOffset,
62 originalEndOffset - originalStartOffset);
63
64 // "Append clone as the last child of frag."
65 frag.appendChild(clone);
66
67 // "Abort this method, returning frag."
68 return frag;
69 }
70
71 // "Let common ancestor equal original start node."
72 var commonAncestor = originalStartNode;
73
74 // "While common ancestor is not an ancestor container of original end
75 // node, set common ancestor to its own parent."
76 while (!isAncestorContainer(commonAncestor, originalEndNode)) {
77 commonAncestor = commonAncestor.parentNode;
78 }
79
80 // "If original start node is an ancestor container of original end node,
81 // let first partially contained child be null."
82 var firstPartiallyContainedChild;
83 if (isAncestorContainer(originalStartNode, originalEndNode)) {
84 firstPartiallyContainedChild = null;
85 // "Otherwise, let first partially contained child be the first child of
86 // common ancestor that is partially contained in the context object."
87 } else {
88 for (var i = 0; i < commonAncestor.childNodes.length; i++) {
89 if (isPartiallyContained(commonAncestor.childNodes[i], range)) {
90 firstPartiallyContainedChild = commonAncestor.childNodes[i];
91 break;
92 }
93 }
94 if (!firstPartiallyContainedChild) {
95 throw "Spec bug: no first partially contained child!";
96 }
97 }
98
99 // "If original end node is an ancestor container of original start node,
100 // let last partially contained child be null."
101 var lastPartiallyContainedChild;
102 if (isAncestorContainer(originalEndNode, originalStartNode)) {
103 lastPartiallyContainedChild = null;
104 // "Otherwise, let last partially contained child be the last child of
105 // common ancestor that is partially contained in the context object."
106 } else {
107 for (var i = commonAncestor.childNodes.length - 1; i >= 0; i--) {
108 if (isPartiallyContained(commonAncestor.childNodes[i], range)) {
109 lastPartiallyContainedChild = commonAncestor.childNodes[i];
110 break;
111 }
112 }
113 if (!lastPartiallyContainedChild) {
114 throw "Spec bug: no last partially contained child!";
115 }
116 }
117
118 // "Let contained children be a list of all children of common ancestor
119 // that are contained in the context object, in tree order."
120 //
121 // "If any member of contained children is a DocumentType, raise a
122 // HIERARCHY_REQUEST_ERR exception and abort these steps."
123 var containedChildren = [];
124 for (var i = 0; i < commonAncestor.childNodes.length; i++) {
125 if (isContained(commonAncestor.childNodes[i], range)) {
126 if (commonAncestor.childNodes[i].nodeType
127 == Node.DOCUMENT_TYPE_NODE) {
128 return "HIERARCHY_REQUEST_ERR";
129 }
130 containedChildren.push(commonAncestor.childNodes[i]);
131 }
132 }
133
134 // "If first partially contained child is a Text, ProcessingInstruction, or Co mment node:"
135 if (firstPartiallyContainedChild
136 && (firstPartiallyContainedChild.nodeType == Node.TEXT_NODE
137 || firstPartiallyContainedChild.nodeType == Node.COMMENT_NODE
138 || firstPartiallyContainedChild.nodeType == Node.PROCESSING_INSTRUCTION_NODE)) {
139 // "Let clone be the result of calling cloneNode(false) on original
140 // start node."
141 var clone = originalStartNode.cloneNode(false);
142
143 // "Set the data of clone to the result of calling substringData() on
144 // original start node, with original start offset as the first
145 // argument and (length of original start node − original start offset)
146 // as the second."
147 clone.data = originalStartNode.substringData(originalStartOffset,
148 nodeLength(originalStartNode) - originalStartOffset);
149
150 // "Append clone as the last child of frag."
151 frag.appendChild(clone);
152 // "Otherwise, if first partially contained child is not null:"
153 } else if (firstPartiallyContainedChild) {
154 // "Let clone be the result of calling cloneNode(false) on first
155 // partially contained child."
156 var clone = firstPartiallyContainedChild.cloneNode(false);
157
158 // "Append clone as the last child of frag."
159 frag.appendChild(clone);
160
161 // "Let subrange be a new Range whose start is (original start node,
162 // original start offset) and whose end is (first partially contained
163 // child, length of first partially contained child)."
164 var subrange = ownerDoc.createRange();
165 subrange.setStart(originalStartNode, originalStartOffset);
166 subrange.setEnd(firstPartiallyContainedChild,
167 nodeLength(firstPartiallyContainedChild));
168
169 // "Let subfrag be the result of calling cloneContents() on
170 // subrange."
171 var subfrag = myCloneContents(subrange);
172
173 // "For each child of subfrag, in order, append that child to clone as
174 // its last child."
175 for (var i = 0; i < subfrag.childNodes.length; i++) {
176 clone.appendChild(subfrag.childNodes[i]);
177 }
178 }
179
180 // "For each contained child in contained children:"
181 for (var i = 0; i < containedChildren.length; i++) {
182 // "Let clone be the result of calling cloneNode(true) of contained
183 // child."
184 var clone = containedChildren[i].cloneNode(true);
185
186 // "Append clone as the last child of frag."
187 frag.appendChild(clone);
188 }
189
190 // "If last partially contained child is a Text, ProcessingInstruction, or Com ment node:"
191 if (lastPartiallyContainedChild
192 && (lastPartiallyContainedChild.nodeType == Node.TEXT_NODE
193 || lastPartiallyContainedChild.nodeType == Node.COMMENT_NODE
194 || lastPartiallyContainedChild.nodeType == Node.PROCESSING_INSTRUCTION_NODE)) {
195 // "Let clone be the result of calling cloneNode(false) on original
196 // end node."
197 var clone = originalEndNode.cloneNode(false);
198
199 // "Set the data of clone to the result of calling substringData(0,
200 // original end offset) on original end node."
201 clone.data = originalEndNode.substringData(0, originalEndOffset);
202
203 // "Append clone as the last child of frag."
204 frag.appendChild(clone);
205 // "Otherwise, if last partially contained child is not null:"
206 } else if (lastPartiallyContainedChild) {
207 // "Let clone be the result of calling cloneNode(false) on last
208 // partially contained child."
209 var clone = lastPartiallyContainedChild.cloneNode(false);
210
211 // "Append clone as the last child of frag."
212 frag.appendChild(clone);
213
214 // "Let subrange be a new Range whose start is (last partially
215 // contained child, 0) and whose end is (original end node, original
216 // end offset)."
217 var subrange = ownerDoc.createRange();
218 subrange.setStart(lastPartiallyContainedChild, 0);
219 subrange.setEnd(originalEndNode, originalEndOffset);
220
221 // "Let subfrag be the result of calling cloneContents() on
222 // subrange."
223 var subfrag = myCloneContents(subrange);
224
225 // "For each child of subfrag, in order, append that child to clone as
226 // its last child."
227 for (var i = 0; i < subfrag.childNodes.length; i++) {
228 clone.appendChild(subfrag.childNodes[i]);
229 }
230 }
231
232 // "Return frag."
233 return frag;
234 }
235
236 function restoreIframe(iframe, i) {
237 // Most of this function is designed to work around the fact that Opera
238 // doesn't let you add a doctype to a document that no longer has one, in
239 // any way I can figure out. I eventually compromised on something that
240 // will still let Opera pass most tests that don't actually involve
241 // doctypes.
242 while (iframe.contentDocument.firstChild
243 && iframe.contentDocument.firstChild.nodeType != Node.DOCUMENT_TYPE_NODE) {
244 iframe.contentDocument.removeChild(iframe.contentDocument.firstChild);
245 }
246
247 while (iframe.contentDocument.lastChild
248 && iframe.contentDocument.lastChild.nodeType != Node.DOCUMENT_TYPE_NODE) {
249 iframe.contentDocument.removeChild(iframe.contentDocument.lastChild);
250 }
251
252 if (!iframe.contentDocument.firstChild) {
253 // This will throw an exception in Opera if we reach here, which is why
254 // I try to avoid it. It will never happen in a browser that obeys the
255 // spec, so it's really just insurance. I don't think it actually gets
256 // hit by anything.
257 iframe.contentDocument.appendChild(iframe.contentDocument.implementation.cre ateDocumentType("html", "", ""));
258 }
259 iframe.contentDocument.appendChild(referenceDoc.documentElement.cloneNode(true ));
260 iframe.contentWindow.setupRangeTests();
261 iframe.contentWindow.testRangeInput = testRanges[i];
262 iframe.contentWindow.run();
263 }
264
265 function testCloneContents(i) {
266 restoreIframe(actualIframe, i);
267 restoreIframe(expectedIframe, i);
268
269 var actualRange = actualIframe.contentWindow.testRange;
270 var expectedRange = expectedIframe.contentWindow.testRange;
271 var actualFrag, expectedFrag;
272 var actualRoots, expectedRoots;
273
274 domTests[i].step(function() {
275 assert_equals(actualIframe.contentWindow.unexpectedException, null,
276 "Unexpected exception thrown when setting up Range for actual cloneContent s()");
277 assert_equals(expectedIframe.contentWindow.unexpectedException, null,
278 "Unexpected exception thrown when setting up Range for simulated cloneCont ents()");
279 assert_equals(typeof actualRange, "object",
280 "typeof Range produced in actual iframe");
281 assert_equals(typeof expectedRange, "object",
282 "typeof Range produced in expected iframe");
283
284 // NOTE: We could just assume that cloneContents() doesn't change
285 // anything. That would simplify these tests, taken in isolation. But
286 // once we've already set up the whole apparatus for extractContents()
287 // and deleteContents(), we just reuse it here, on the theory of "why
288 // not test some more stuff if it's easy to do".
289 //
290 // Just to be pedantic, we'll test not only that the tree we're
291 // modifying is the same in expected vs. actual, but also that all the
292 // nodes originally in it were the same. Typically some nodes will
293 // become detached when the algorithm is run, but they still exist and
294 // references can still be kept to them, so they should also remain the
295 // same.
296 //
297 // We initialize the list to all nodes, and later on remove all the
298 // ones which still have parents, since the parents will presumably be
299 // tested for isEqualNode() and checking the children would be
300 // redundant.
301 var actualAllNodes = [];
302 var node = furthestAncestor(actualRange.startContainer);
303 do {
304 actualAllNodes.push(node);
305 } while (node = nextNode(node));
306
307 var expectedAllNodes = [];
308 var node = furthestAncestor(expectedRange.startContainer);
309 do {
310 expectedAllNodes.push(node);
311 } while (node = nextNode(node));
312
313 expectedFrag = myCloneContents(expectedRange);
314 if (typeof expectedFrag == "string") {
315 assert_throws(expectedFrag, function() {
316 actualRange.cloneContents();
317 });
318 } else {
319 actualFrag = actualRange.cloneContents();
320 }
321
322 actualRoots = [];
323 for (var j = 0; j < actualAllNodes.length; j++) {
324 if (!actualAllNodes[j].parentNode) {
325 actualRoots.push(actualAllNodes[j]);
326 }
327 }
328
329 expectedRoots = [];
330 for (var j = 0; j < expectedAllNodes.length; j++) {
331 if (!expectedAllNodes[j].parentNode) {
332 expectedRoots.push(expectedAllNodes[j]);
333 }
334 }
335
336 for (var j = 0; j < actualRoots.length; j++) {
337 assertNodesEqual(actualRoots[j], expectedRoots[j], j ? "detached node #" + j : "tree root");
338
339 if (j == 0) {
340 // Clearly something is wrong if the node lists are different
341 // lengths. We want to report this only after we've already
342 // checked the main tree for equality, though, so it doesn't
343 // mask more interesting errors.
344 assert_equals(actualRoots.length, expectedRoots.length,
345 "Actual and expected DOMs were broken up into a different number of pi eces by cloneContents() (this probably means you created or detached nodes when you weren't supposed to)");
346 }
347 }
348 });
349 domTests[i].done();
350
351 positionTests[i].step(function() {
352 assert_equals(actualIframe.contentWindow.unexpectedException, null,
353 "Unexpected exception thrown when setting up Range for actual cloneContent s()");
354 assert_equals(expectedIframe.contentWindow.unexpectedException, null,
355 "Unexpected exception thrown when setting up Range for simulated cloneCont ents()");
356 assert_equals(typeof actualRange, "object",
357 "typeof Range produced in actual iframe");
358 assert_equals(typeof expectedRange, "object",
359 "typeof Range produced in expected iframe");
360
361 assert_true(actualRoots[0].isEqualNode(expectedRoots[0]),
362 "The resulting DOMs were not equal, so comparing positions makes no sense" );
363
364 if (typeof expectedFrag == "string") {
365 // It's no longer true that, e.g., startContainer and endContainer
366 // must always be the same
367 return;
368 }
369
370 assert_equals(actualRange.startOffset, expectedRange.startOffset,
371 "Unexpected startOffset after cloneContents()");
372 // How do we decide that the two nodes are equal, since they're in
373 // different trees? Since the DOMs are the same, it's enough to check
374 // that the index in the parent is the same all the way up the tree.
375 // But we can first cheat by just checking they're actually equal.
376 assert_true(actualRange.startContainer.isEqualNode(expectedRange.startContai ner),
377 "Unexpected startContainer after cloneContents(), expected " +
378 expectedRange.startContainer.nodeName.toLowerCase() + " but got " +
379 actualRange.startContainer.nodeName.toLowerCase());
380 var currentActual = actualRange.startContainer;
381 var currentExpected = expectedRange.startContainer;
382 var actual = "";
383 var expected = "";
384 while (currentActual && currentExpected) {
385 actual = indexOf(currentActual) + "-" + actual;
386 expected = indexOf(currentExpected) + "-" + expected;
387
388 currentActual = currentActual.parentNode;
389 currentExpected = currentExpected.parentNode;
390 }
391 actual = actual.substr(0, actual.length - 1);
392 expected = expected.substr(0, expected.length - 1);
393 assert_equals(actual, expected,
394 "startContainer superficially looks right but is actually the wrong node i f you trace back its index in all its ancestors (I'm surprised this actually hap pened");
395 });
396 positionTests[i].done();
397
398 fragTests[i].step(function() {
399 assert_equals(actualIframe.contentWindow.unexpectedException, null,
400 "Unexpected exception thrown when setting up Range for actual cloneContent s()");
401 assert_equals(expectedIframe.contentWindow.unexpectedException, null,
402 "Unexpected exception thrown when setting up Range for simulated cloneCont ents()");
403 assert_equals(typeof actualRange, "object",
404 "typeof Range produced in actual iframe");
405 assert_equals(typeof expectedRange, "object",
406 "typeof Range produced in expected iframe");
407
408 if (typeof expectedFrag == "string") {
409 // Comparing makes no sense
410 return;
411 }
412 assertNodesEqual(actualFrag, expectedFrag,
413 "returned fragment");
414 });
415 fragTests[i].done();
416 }
417
418 // First test a Range that has the no-op detach() called on it, synchronously
419 test(function() {
420 var range = document.createRange();
421 range.detach();
422 assert_array_equals(range.cloneContents().childNodes, []);
423 }, "Range.detach()");
424
425 var iStart = 0;
426 var iStop = testRanges.length;
427
428 if (/subtest=[0-9]+/.test(location.search)) {
429 var matches = /subtest=([0-9]+)/.exec(location.search);
430 iStart = Number(matches[1]);
431 iStop = Number(matches[1]) + 1;
432 }
433
434 var domTests = [];
435 var positionTests = [];
436 var fragTests = [];
437
438 for (var i = iStart; i < iStop; i++) {
439 domTests[i] = async_test("Resulting DOM for range " + i + " " + testRanges[i]) ;
440 positionTests[i] = async_test("Resulting cursor position for range " + i + " " + testRanges[i]);
441 fragTests[i] = async_test("Returned fragment for range " + i + " " + testRange s[i]);
442 }
443
444 var referenceDoc = document.implementation.createHTMLDocument("");
445 referenceDoc.removeChild(referenceDoc.documentElement);
446
447 actualIframe.onload = function() {
448 expectedIframe.onload = function() {
449 for (var i = iStart; i < iStop; i++) {
450 testCloneContents(i);
451 }
452 }
453 expectedIframe.src = "Range-test-iframe.html";
454 referenceDoc.appendChild(actualIframe.contentDocument.documentElement.cloneNod e(true));
455 }
456 actualIframe.src = "Range-test-iframe.html";
457 </script>
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698