selection-preserve.html (6080B)
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <script src='/resources/testharness.js'></script> 5 <script src='/resources/testharnessreport.js'></script> 6 <script src="/resources/testdriver.js"></script> 7 <script src="/resources/testdriver-actions.js"></script> 8 <script src="/resources/testdriver-vendor.js"></script> 9 </head> 10 11 <body> 12 </body> 13 14 <script> 15 'use strict'; 16 17 async function select_range(t, anchorNode, focusNode) { 18 await new test_driver.Actions() 19 .pointerMove(0, 0, {origin: anchorNode}) 20 .pointerDown() 21 .pointerMove(focusNode.clientWidth, focusNode.clientHeight, {origin: focusNode}) 22 .pointerUp() 23 .send(); 24 } 25 26 promise_test(async t => { 27 document.body.innerHTML = ` 28 <div id=old_parent> 29 <span id=notMove>This text does not move</span> 30 <span id=text>Text</span> 31 </div> 32 <div id=new_parent></div>`; 33 getSelection().removeAllRanges(); 34 35 await select_range(t, text, text); 36 37 // XXX This test seems to rely on Chromium internal behavior! 38 // Don't first verify that `getSelection().anchorNode` is the expected 39 // `<span id=text>` node that was selected above. If we did that, then at 40 // least in Chromium browsers, this would generate a new Range capturing the 41 // visual selection, which would be cached to the internal `DOMSelection` 42 // object. Then the move would have two effects: 43 // 44 // 1. The internal visual selection would change as a result of 45 // `moveBefore()` 46 // 2. But the Range representing the visual selection would remain 47 // unchanged, since it was cached to the `DOMSelection` object and not 48 // updated during `moveBefore()`. This means if (a) the visual selection, 49 // and (b) the "API-returned" selection Range are out of sync after 50 // `moveBefore()`, it would be impossible to detect the discrepancy 51 // without a reftest. 52 // 53 // So instead, we will just dive right into `moveBefore()`, and then compute 54 // the selection range *after*, which will accurately represent the internal 55 // visual selection that the user sees, and we can run our assertions on it. 56 new_parent.moveBefore(text, null); 57 assert_equals(getSelection().anchorNode, notMove.firstChild); 58 assert_equals(getSelection().focusNode, notMove.firstChild); 59 }, "moveBefore should not reset selection with preceding text"); 60 61 const kHTML = ` 62 <div id=grandparentDiv> 63 <span id=grandparentParagraph>Grandparent paragraph</span> 64 <div id=parentDiv> 65 <span id=parentParagraph>Parent paragraph</span> 66 <div id=childDiv> 67 <span id=childParagraph1>Child paragraph one</span> 68 <span id=childParagraph2>Paragraph two</span> 69 </div> 70 </div> 71 </div> 72 `; 73 74 // Selection spans parent->child. 75 promise_test(async t => { 76 document.body.innerHTML = kHTML; 77 getSelection().removeAllRanges(); 78 await select_range(t, parentParagraph, childParagraph1); 79 80 grandparentDiv.moveBefore(parentDiv, grandparentParagraph); 81 assert_equals(getSelection().anchorNode, grandparentParagraph.firstChild); 82 assert_equals(getSelection().focusNode, grandparentParagraph.firstChild); 83 }, "moveBefore resets selection that enters a subtree, when the whole " + 84 "selection is moved"); 85 86 // Selection anchor node is moved upwards in the DOM, to suddenly intersect more 87 // nodes. 88 promise_test(async t => { 89 document.body.innerHTML = kHTML; 90 getSelection().removeAllRanges(); 91 await select_range(t, parentParagraph, childParagraph1); 92 93 grandparentDiv.moveBefore(parentParagraph, grandparentParagraph); 94 assert_equals(getSelection().anchorNode, childParagraph1.firstChild); 95 assert_equals(getSelection().focusNode, childParagraph1.firstChild); 96 assert_false(getSelection().getRangeAt(0).intersectsNode(grandparentParagraph)); 97 }, "moveBefore anchor node moved up to expand selection and absorb nodes"); 98 99 // Intersecting nodes are moved *out* of the selection. 100 promise_test(async t => { 101 document.body.innerHTML = kHTML; 102 getSelection().removeAllRanges(); 103 await select_range(t, grandparentParagraph, childParagraph2); 104 105 grandparentDiv.moveBefore(childParagraph1, grandparentParagraph); 106 assert_equals(getSelection().anchorNode, grandparentParagraph.firstChild); 107 assert_equals(getSelection().focusNode, childParagraph2.firstChild); 108 assert_false(getSelection().getRangeAt(0).intersectsNode(childParagraph1)); 109 }, "moveBefore move intersecting nodes out of a selection"); 110 111 // Selection focus node is moved upwards in the DOM, shrinking the selection and 112 // excluding once-intersecting nodes. 113 promise_test(async t => { 114 document.body.innerHTML = kHTML; 115 getSelection().removeAllRanges(); 116 await select_range(t, grandparentParagraph, childParagraph2); 117 118 parentDiv.moveBefore(childDiv, parentParagraph); 119 assert_equals(getSelection().anchorNode, grandparentParagraph.firstChild); 120 assert_equals(getSelection().focusNode, parentParagraph.firstChild); 121 assert_true(getSelection().getRangeAt(0).intersectsNode(parentParagraph)); 122 }, "moveBefore focus node moved up to shrink selection and exclude nodes; " + 123 "focus node gets reset"); 124 125 // Selection focus node is moved upwards in the DOM, shrinking the selection and 126 // excluding once-intersecting nodes. 127 promise_test(async t => { 128 document.body.innerHTML = ` 129 <ul id=list> 130 <li id=i1>One</li> 131 <li id=i2>Two</li> 132 <li id=i3>Three</li> 133 <li id=i4>Four</li> 134 </ul> 135 `; 136 getSelection().removeAllRanges(); 137 await select_range(t, i3, i4); 138 139 // Move the last list item (the selection focus node) to the position before 140 // the first. This resets the range to be collapsed at `i3`. 141 list.moveBefore(i4, i1); 142 assert_equals(getSelection().focusNode, i3.firstChild); 143 assert_equals(getSelection().anchorNode, i3.firstChild); 144 assert_true(getSelection().getRangeAt(0).collapsed, "Range is collased at `i3`"); 145 assert_false(getSelection().getRangeAt(0).intersectsNode(i2), 146 "Range does not intersect node that comes before anchor"); 147 assert_true(getSelection().getRangeAt(0).intersectsNode(i3)); 148 }, "moveBefore selection is not preserved, especially when underlying range " + 149 "gets inverted"); 150 </script> 151 </html>