Selection-getComposedRanges-range-update.html (12100B)
1 <!DOCTYPE html> 2 <html> 3 <body> 4 <meta name="assert" content="Selection's composed range should be updated when its associated legacy uncomposed range changes"> 5 <link rel="help" href="https://w3c.github.io/selection-api/#dom-selection-getcomposedranges"> 6 <script src="/resources/testharness.js"></script> 7 <script src="/resources/testharnessreport.js"></script> 8 9 <div id="light">Start outside shadow DOM</div> 10 <div id="outerHost">outerHost 11 <template shadowrootmode="open"> 12 <slot></slot> 13 <div id="innerHost">innerHost 14 <template shadowrootmode="open"> 15 <slot></slot> 16 </template> 17 </div> 18 </template> 19 </div> 20 <div id="lightEnd">End outside shadow DOM</div> 21 22 <script> 23 24 const selection = getSelection(); 25 const outerHost = document.getElementById('outerHost') 26 const outerRoot = outerHost.shadowRoot; 27 const innerHost = outerRoot.getElementById('innerHost'); 28 const innerRoot = innerHost.shadowRoot; 29 30 test(() => { 31 // Setting a selction crossing to shadow tree 32 selection.setBaseAndExtent(light.firstChild, 10, innerHost.firstChild, 5); 33 assert_throws_dom("INDEX_SIZE_ERR", function () { selection.getRangeAt(0) }); 34 }, 'If selection crosses shadow boundaries, getRangeAt(0) should throw an IndexSizeError because the end is not in the document tree.'); 35 36 test(() => { 37 // Setting a selection within light tree 38 selection.setBaseAndExtent(light.firstChild, 10, lightEnd.firstChild, 20); 39 const liveRange = selection.getRangeAt(0); 40 const newSpan = document.createElement("span"); 41 liveRange.setStart(newSpan, 0); 42 43 assert_true(liveRange.collapsed); 44 assert_equals(liveRange.startContainer, newSpan); 45 assert_equals(liveRange.startOffset, 0); 46 47 assert_true(selection.isCollapsed); 48 assert_equals(selection.anchorNode, null); 49 assert_equals(selection.anchorOffset, 0); 50 51 assert_throws_dom("INDEX_SIZE_ERR", function () { selection.getRangeAt(0) }); 52 assert_equals(selection.getComposedRanges({ shadowRoots: [outerRoot, innerRoot] }).length, 0); 53 54 }, 'modify getRangeAt() range: setStart() to disconnected node will collapse and remove the live range from the selection.'); 55 56 test(() => { 57 // Setting a selection within light tree 58 selection.setBaseAndExtent(light.firstChild, 10, light.firstChild, 20); 59 const liveRange = selection.getRangeAt(0); 60 let composedRange = selection.getComposedRanges({ shadowRoots: [outerRoot, innerRoot] })[0]; 61 62 assert_equals(liveRange.startContainer, light.firstChild); 63 assert_equals(liveRange.startOffset, 10); 64 assert_equals(liveRange.endContainer, light.firstChild); 65 assert_equals(liveRange.endOffset, 20); 66 67 assert_equals(selection.anchorNode, light.firstChild); 68 assert_equals(selection.anchorOffset, 10); 69 assert_equals(selection.focusNode, light.firstChild); 70 assert_equals(selection.focusOffset, 20); 71 72 assert_equals(composedRange.startContainer, light.firstChild); 73 assert_equals(composedRange.startOffset, 10); 74 assert_equals(composedRange.endContainer, light.firstChild); 75 assert_equals(composedRange.endOffset, 20); 76 77 liveRange.setEnd(innerHost.firstChild, 5); 78 composedRange = selection.getComposedRanges({ shadowRoots: [outerRoot, innerRoot] })[0]; 79 80 assert_true(liveRange.collapsed); 81 assert_equals(liveRange.startContainer, innerHost.firstChild); 82 assert_equals(liveRange.startOffset, 5); 83 84 assert_true(selection.isCollapsed); 85 assert_equals(selection.anchorNode, innerHost.firstChild); 86 assert_equals(selection.anchorOffset, 5); 87 88 assert_equals(composedRange.startContainer, light.firstChild); 89 assert_equals(composedRange.startOffset, 10); 90 assert_equals(composedRange.endContainer, innerHost.firstChild); 91 assert_equals(composedRange.endOffset, 5); 92 }, 'modify getRangeAt() range: setEnd() crosses shadow boundary into the shadow DOM and after start, which collapses live range. Composed selection range is not collapsed.'); 93 94 test(() => { 95 // Setting a selection within light tree 96 selection.setBaseAndExtent(lightEnd.firstChild, 10, lightEnd.firstChild, 20); 97 const liveRange = selection.getRangeAt(0); 98 let composedRange = selection.getComposedRanges({ shadowRoots: [outerRoot, innerRoot] })[0]; 99 100 assert_equals(liveRange.startContainer, lightEnd.firstChild); 101 assert_equals(liveRange.startOffset, 10); 102 assert_equals(liveRange.endContainer, lightEnd.firstChild); 103 assert_equals(liveRange.endOffset, 20); 104 105 assert_equals(selection.anchorNode, lightEnd.firstChild); 106 assert_equals(selection.anchorOffset, 10); 107 assert_equals(selection.focusNode, lightEnd.firstChild); 108 assert_equals(selection.focusOffset, 20); 109 110 assert_equals(composedRange.startContainer, lightEnd.firstChild); 111 assert_equals(composedRange.startOffset, 10); 112 assert_equals(composedRange.endContainer, lightEnd.firstChild); 113 assert_equals(composedRange.endOffset, 20); 114 115 liveRange.setStart(innerHost.firstChild, 5); 116 composedRange = selection.getComposedRanges({ shadowRoots: [outerRoot, innerRoot] })[0]; 117 118 assert_true(liveRange.collapsed); 119 assert_equals(liveRange.startContainer, innerHost.firstChild); 120 assert_equals(liveRange.startOffset, 5); 121 122 assert_true(selection.isCollapsed); 123 assert_equals(selection.anchorNode, innerHost.firstChild); 124 assert_equals(selection.anchorOffset, 5); 125 126 assert_equals(composedRange.startContainer, innerHost.firstChild); 127 assert_equals(composedRange.startOffset, 5); 128 assert_equals(composedRange.endContainer, lightEnd.firstChild); 129 assert_equals(composedRange.endOffset, 20); 130 }, 'modify getRangeAt() range: setStart() crosses shadow boundary into the shadow DOM and before end, which collapses live range. Composed selection range is not collapsed.'); 131 132 test(() => { 133 // Setting a selection within light tree 134 selection.setBaseAndExtent(light.firstChild, 10, light.firstChild, 20); 135 const liveRange = selection.getRangeAt(0); 136 let composedRange = selection.getComposedRanges({ shadowRoots: [outerRoot, innerRoot] })[0]; 137 138 assert_equals(liveRange.startContainer, light.firstChild); 139 assert_equals(liveRange.startOffset, 10); 140 assert_equals(liveRange.endContainer, light.firstChild); 141 assert_equals(liveRange.endOffset, 20); 142 143 assert_equals(selection.anchorNode, light.firstChild); 144 assert_equals(selection.anchorOffset, 10); 145 assert_equals(selection.focusNode, light.firstChild); 146 assert_equals(selection.focusOffset, 20); 147 148 assert_equals(composedRange.startContainer, light.firstChild); 149 assert_equals(composedRange.startOffset, 10); 150 assert_equals(composedRange.endContainer, light.firstChild); 151 assert_equals(composedRange.endOffset, 20); 152 153 liveRange.setStart(innerHost.firstChild, 5); 154 composedRange = selection.getComposedRanges({ shadowRoots: [outerRoot, innerRoot] })[0]; 155 156 assert_true(liveRange.collapsed); 157 assert_equals(liveRange.startContainer, innerHost.firstChild); 158 assert_equals(liveRange.startOffset, 5); 159 160 assert_true(selection.isCollapsed); 161 assert_equals(selection.anchorNode, innerHost.firstChild); 162 assert_equals(selection.anchorOffset, 5); 163 164 assert_true(composedRange.collapsed); 165 assert_equals(composedRange.startContainer, innerHost.firstChild); 166 assert_equals(composedRange.startOffset, 5); 167 }, 'modify getRangeAt() range: setStart() crosses shadow boundary into the shadow DOM and after end, which collapses both live range and composed selection range.'); 168 169 test(() => { 170 // Setting a selection within light tree 171 selection.setBaseAndExtent(light.firstChild, 10, lightEnd.firstChild, 20); 172 const liveRange = selection.getRangeAt(0); 173 liveRange.selectNode(innerHost); 174 const composedRange = selection.getComposedRanges({ shadowRoots: [outerRoot, innerRoot] })[0]; 175 176 assert_equals(liveRange.startContainer, outerRoot); 177 assert_equals(liveRange.startOffset, 3); 178 assert_equals(liveRange.endContainer, outerRoot); 179 assert_equals(liveRange.endOffset, 4); 180 181 assert_equals(selection.anchorNode, outerRoot); 182 assert_equals(selection.anchorOffset, 3); 183 assert_equals(selection.focusNode, outerRoot); 184 assert_equals(selection.focusOffset, 4); 185 186 assert_equals(composedRange.startContainer, outerRoot); 187 assert_equals(composedRange.startOffset, 3); 188 assert_equals(composedRange.endContainer, outerRoot); 189 assert_equals(composedRange.endOffset, 4); 190 }, 'modify getRangeAt() range: selectNode() innerHost for all ranges.'); 191 192 test(() => { 193 // Setting a selection within light tree 194 selection.setBaseAndExtent(light.firstChild, 10, lightEnd.firstChild, 20); 195 const liveRange = selection.getRangeAt(0); 196 liveRange.collapse(); 197 const composedRange = selection.getComposedRanges({ shadowRoots: [outerRoot, innerRoot] })[0]; 198 199 assert_true(liveRange.collapsed); 200 assert_equals(liveRange.startContainer, lightEnd.firstChild); 201 assert_equals(liveRange.startOffset, 20); 202 203 assert_true(selection.isCollapsed); 204 assert_equals(selection.anchorNode, lightEnd.firstChild); 205 assert_equals(selection.anchorOffset, 20); 206 207 assert_true(composedRange.collapsed); 208 assert_equals(composedRange.startContainer, lightEnd.firstChild); 209 assert_equals(composedRange.startOffset, 20); 210 }, 'modify getRangeAt() range: collapse() collapses all ranges.'); 211 212 test(() => { 213 // Step 1: Creating a live range and only setting its end/anchor 214 selection.removeAllRanges(); 215 const liveRange = document.createRange(); 216 liveRange.setEnd(innerHost.firstChild, 5); 217 const composedRanges = selection.getComposedRanges({ shadowRoots: [outerRoot, innerRoot] }); 218 219 assert_true(liveRange.collapsed); 220 assert_equals(liveRange.startContainer, innerHost.firstChild); 221 assert_equals(liveRange.startOffset, 5); 222 223 assert_true(selection.isCollapsed); 224 assert_equals(selection.anchorNode, null); 225 assert_equals(selection.anchorOffset, 0); 226 227 assert_equals(composedRanges.length, 0, 'range is not added to selection yet.'); 228 229 // Step 2: Add range to selection so range API updates will change selection 230 selection.addRange(liveRange); 231 const composedRange = selection.getComposedRanges({ shadowRoots: [outerRoot, innerRoot] })[0]; 232 233 assert_true(liveRange.collapsed); 234 assert_equals(liveRange.endContainer, innerHost.firstChild); 235 assert_equals(liveRange.endOffset, 5); 236 237 assert_true(selection.isCollapsed); 238 assert_equals(selection.anchorNode, innerHost.firstChild); 239 assert_equals(selection.anchorOffset, 5); 240 241 assert_true(composedRange.collapsed); 242 assert_equals(composedRange.startContainer, innerHost.firstChild); 243 assert_equals(composedRange.startOffset, 5); 244 }, 'modify createRange() range: adding to selection sets the selection'); 245 246 test(() => { 247 // Step 1: Creating a live range and only setting its end/anchor 248 selection.removeAllRanges(); 249 const liveRange = document.createRange(); 250 // Add range to selection so range API updates will change selection 251 selection.addRange(liveRange); 252 liveRange.setEnd(innerHost.firstChild, 5); 253 let composedRange = selection.getComposedRanges({ shadowRoots: [outerRoot, innerRoot] })[0]; 254 255 assert_true(liveRange.collapsed); 256 assert_equals(liveRange.startContainer, innerHost.firstChild); 257 assert_equals(liveRange.startOffset, 5); 258 259 assert_true(selection.isCollapsed); 260 assert_equals(selection.anchorNode, innerHost.firstChild); 261 assert_equals(selection.anchorOffset, 5); 262 263 assert_equals(composedRange.startContainer, document); 264 assert_equals(composedRange.startOffset, 0); 265 assert_equals(composedRange.endContainer, innerHost.firstChild); 266 assert_equals(composedRange.endOffset, 5); 267 268 // Step 2: Update the live range by setting its start/focus 269 liveRange.setStart(light.firstChild, 10); 270 composedRange = selection.getComposedRanges({ shadowRoots: [outerRoot, innerRoot] })[0]; 271 272 assert_true(liveRange.collapsed); 273 assert_equals(liveRange.startContainer, light.firstChild); 274 assert_equals(liveRange.startOffset, 10); 275 276 assert_true(selection.isCollapsed); 277 assert_equals(selection.anchorNode, light.firstChild); 278 assert_equals(selection.anchorOffset, 10); 279 280 assert_equals(composedRange.startContainer, light.firstChild); 281 assert_equals(composedRange.startOffset, 10); 282 assert_equals(composedRange.endContainer, innerHost.firstChild); 283 assert_equals(composedRange.endOffset, 5); 284 }, 'modify createRange() range: added to selection before setStart/setEnd calls.'); 285 </script>