Range_setEnd.html (24081B)
1 <!doctype html> 2 <meta charset=utf-8> 3 <title>focus move tests caused by a call of Range.setEnd(), Range.setEndAfter() and Range.setEndBefore()</title> 4 <script src="/resources/testharness.js"></script> 5 <script src="/resources/testharnessreport.js"></script> 6 <body> 7 <div style="height: 3000px;">Spacer to check whether or not page was scrolled down to focused editor</div> 8 <p id="staticBefore">static text</p> 9 <div id="editor" contenteditable><p>content of editor</p></div> 10 <div id="outerEditor" contenteditable 11 ><p>content of outer editor</p><div id="staticInEditor" contenteditable="false" 12 ><p>static content of outer editor</p><div id="innerEditor" contenteditable 13 ><p>content of inner editor</p></div></div></div> 14 <p id="staticAfter">static text</p> 15 <p><a id="anchor" href="about:blank">anchor</a></p> 16 <script> 17 "use strict"; 18 19 var staticBefore = { 20 element: document.getElementById("staticBefore"), 21 textNode: document.getElementById("staticBefore").firstChild, 22 textLength: document.getElementById("staticBefore").firstChild.length 23 }; 24 var editor = { 25 element: document.getElementById("editor"), 26 textNode: document.getElementById("editor").firstChild.firstChild, 27 textLength: document.getElementById("editor").firstChild.firstChild.length 28 }; 29 var outerEditor = { 30 element: document.getElementById("outerEditor"), 31 textNode: document.getElementById("outerEditor").firstChild.firstChild, 32 textLength: document.getElementById("outerEditor").firstChild.firstChild.length 33 }; 34 var staticInEditor = { 35 element: document.getElementById("staticInEditor"), 36 textNode: document.getElementById("staticInEditor").firstChild, 37 textLength: document.getElementById("staticInEditor").firstChild.length 38 }; 39 var innerEditor = { 40 element: document.getElementById("innerEditor"), 41 textNode: document.getElementById("innerEditor").firstChild.firstChild, 42 textLength: document.getElementById("innerEditor").firstChild.firstChild.length 43 }; 44 var staticAfter = { 45 element: document.getElementById("staticAfter"), 46 textNode: document.getElementById("staticAfter").firstChild, 47 textLength: document.getElementById("staticAfter").firstChild.length 48 }; 49 var anchor = { 50 element: document.getElementById("anchor"), 51 textNode: document.getElementById("anchor").firstChild, 52 textLength: document.getElementById("anchor").firstChild.length 53 }; 54 55 function resetFocusAndSelectionRange(aFocus) 56 { 57 document.getSelection().removeAllRanges(); 58 if (document.activeElement) { 59 document.activeElement.blur(); 60 } 61 if (aFocus) { 62 aFocus.element.focus(); 63 document.getSelection().collapse(aFocus.textNode, 0); 64 } else { 65 document.getSelection().collapse(staticBefore.textNode, 0); 66 } 67 document.documentElement.scrollTop = 0; 68 } 69 70 function setEnd(aNode, aOffset) 71 { 72 document.getSelection().getRangeAt(0).setEnd(aNode, aOffset); 73 } 74 75 function setEndBefore(aNode, aOffset) 76 { 77 document.getSelection().getRangeAt(0).setEndBefore(aNode); 78 } 79 80 function setEndAfter(aNode, aOffset) 81 { 82 document.getSelection().getRangeAt(0).setEndAfter(aNode); 83 } 84 85 // Range.setEnd*() should work same as collapse if specified end position is before its start. 86 [{ func: setEnd, doingDescription: "Range.setEnd()" }, 87 { func: setEndBefore, doingDescription: "Range.setEndBefore()" }].forEach((aTest, aIndex, aArray)=>{ 88 test(function() { 89 resetFocusAndSelectionRange(editor); 90 document.getSelection().selectAllChildren(editor.textNode); 91 aTest.func(staticBefore.textNode, 0); 92 assert_equals(document.activeElement, editor.element); 93 assert_equals(document.documentElement.scrollTop, 0); 94 }, "Active element should be 'editor' after " + aTest.doingDescription + " with start of the first text node of 'staticBefore' (before the selection) when active element is 'editor'"); 95 test(function() { 96 resetFocusAndSelectionRange(outerEditor); 97 document.getSelection().selectAllChildren(outerEditor.textNode); 98 aTest.func(editor.textNode, 0); 99 assert_equals(document.activeElement, editor.element); 100 assert_equals(document.documentElement.scrollTop, 0); 101 }, "Active element should be 'editor' after " + aTest.doingDescription + " with start of the first text node of 'editor' (before the selection) when active element is 'outerEditor'"); 102 test(function() { 103 resetFocusAndSelectionRange(); 104 document.getSelection().selectAllChildren(staticInEditor.textNode); 105 aTest.func(outerEditor.textNode, 0); 106 assert_equals(document.activeElement, outerEditor.element); 107 assert_equals(document.documentElement.scrollTop, 0); 108 }, "Active element should be 'outerEditor' after " + aTest.doingDescription + " with start of the first text node of 'outerEditor' (before the selection) when active element is the <body>"); 109 test(function() { 110 resetFocusAndSelectionRange(innerEditor); 111 document.getSelection().selectAllChildren(innerEditor.textNode); 112 aTest.func(outerEditor.textNode, 0); 113 assert_equals(document.activeElement, outerEditor.element); 114 assert_equals(document.documentElement.scrollTop, 0); 115 }, "Active element should be 'outerEditor' after " + aTest.doingDescription + " with start of the first text node of 'outerEditor' (before the selection) when active element is 'innerEditor'"); 116 test(function() { 117 resetFocusAndSelectionRange(anchor); 118 document.getSelection().selectAllChildren(anchor.textNode); 119 aTest.func(staticAfter.textNode, 0); 120 assert_equals(document.activeElement, anchor.element); 121 assert_equals(document.documentElement.scrollTop, 0); 122 }, "Active element should be 'anchor' after " + aTest.doingDescription + " with start of the first text node of 'staticAfter' (before the selection) when active element is 'anchor'"); 123 124 test(function() { 125 resetFocusAndSelectionRange(); 126 document.getSelection().collapse(staticBefore.textNode, staticBefore.textLength); 127 aTest.func(staticBefore.textNode, 0); 128 assert_equals(document.activeElement, document.body); 129 assert_equals(document.documentElement.scrollTop, 0); 130 }, "Active element should be the <body> after " + aTest.doingDescription + " with start of the first text node of 'staticBefore' (before the collapsed selection) when active element is the <body>"); 131 test(function() { 132 resetFocusAndSelectionRange(editor); 133 document.getSelection().collapse(editor.textNode, editor.textLength); 134 aTest.func(staticBefore.textNode, 0); 135 assert_equals(document.activeElement, editor.element); 136 assert_equals(document.documentElement.scrollTop, 0); 137 }, "Active element should be 'editor' after " + aTest.doingDescription + " with start of the first text node of 'staticBefore' (before the collapsed selection) when active element is 'editor'"); 138 test(function() { 139 resetFocusAndSelectionRange(outerEditor); 140 document.getSelection().collapse(outerEditor.textNode, outerEditor.textLength); 141 aTest.func(editor.textNode, 0); 142 assert_equals(document.activeElement, editor.element); 143 assert_equals(document.documentElement.scrollTop, 0); 144 }, "Active element should be 'editor' after " + aTest.doingDescription + " with start of the first text node of 'editor' (before the collapsed selection) when active element is 'outerEditor'"); 145 test(function() { 146 resetFocusAndSelectionRange(); 147 document.getSelection().collapse(staticInEditor.textNode, staticInEditor.textLength); 148 aTest.func(outerEditor.textNode, 0); 149 assert_equals(document.activeElement, outerEditor.element); 150 assert_equals(document.documentElement.scrollTop, 0); 151 }, "Active element should be 'outerEditor' after " + aTest.doingDescription + " with start of the first text node of 'outerEditor' (before the collapsed selection) when active element is the <body> and selection is in 'staticInEditor'"); 152 test(function() { 153 resetFocusAndSelectionRange(innerEditor); 154 document.getSelection().collapse(innerEditor.textNode, innerEditor.textLength); 155 aTest.func(outerEditor.textNode, 0); 156 assert_equals(document.activeElement, outerEditor.element); 157 assert_equals(document.documentElement.scrollTop, 0); 158 }, "Active element should be 'outerEditor' after " + aTest.doingDescription + " with start of the first text node of 'outerEditor' (before the collapsed selection) when active element is 'innerEditor'"); 159 test(function() { 160 resetFocusAndSelectionRange(anchor); 161 document.getSelection().collapse(anchor.textNode, anchor.textLength); 162 aTest.func(staticAfter.textNode, 0); 163 assert_equals(document.activeElement, anchor.element); 164 assert_equals(document.documentElement.scrollTop, 0); 165 }, "Active element should be 'anchor' after " + aTest.doingDescription + " with start of the first text node of 'staticAfter' (before the collapsed selection) when active element is 'anchor'"); 166 test(function() { 167 resetFocusAndSelectionRange(); 168 document.getSelection().collapse(anchor.textNode, anchor.textLength); 169 aTest.func(anchor.textNode, 0); 170 assert_equals(document.activeElement, document.body); 171 assert_equals(document.documentElement.scrollTop, 0); 172 }, "Active element should be the <body> after " + aTest.doingDescription + " with start of the first text node of 'anchor' (before the collapsed selection) when active element is the <body>"); 173 174 test(function() { 175 resetFocusAndSelectionRange(); 176 document.getSelection().setBaseAndExtent(staticBefore.textNode, staticBefore.textLength, 177 editor.textNode, editor.textLength); 178 aTest.func(staticBefore.textNode, 0); 179 assert_equals(document.activeElement, document.body); 180 assert_equals(document.documentElement.scrollTop, 0); 181 }, "Active element should be the <body> after " + aTest.doingDescription + " with start of the first text node of 'staticBefore' (before the selection, between end of the first text node of 'staticBefore' and end of the first text node of 'editor') when active element is the <body>"); 182 test(function() { 183 resetFocusAndSelectionRange(); 184 document.getSelection().setBaseAndExtent(editor.textNode, editor.textLength, 185 outerEditor.textNode, outerEditor.textLength); 186 aTest.func(editor.textNode, 0); 187 assert_equals(document.activeElement, editor.element); 188 assert_equals(document.documentElement.scrollTop, 0); 189 }, "Active element should be 'editor' after " + aTest.doingDescription + " with start of the first text node of 'editor' (before the selection, between end of the first text node of 'editor' and end of the first text node of 'outerEditor') when active element is the <body>"); 190 test(function() { 191 resetFocusAndSelectionRange(outerEditor); 192 document.getSelection().setBaseAndExtent(outerEditor.textNode, outerEditor.textLength, 193 innerEditor.textNode, innerEditor.textLength); 194 aTest.func(outerEditor.textNode, 0); 195 assert_equals(document.activeElement, outerEditor.element); 196 assert_equals(document.documentElement.scrollTop, 0); 197 }, "Active element should be 'outerEditor' after " + aTest.doingDescription + " with start of the first text node of 'outerEditor' (before the selection, between end of the first text node of 'outerEditor' and end of the first text node of 'innerEditor') when active element is 'outerEditor'"); 198 test(function() { 199 resetFocusAndSelectionRange(); 200 document.getSelection().setBaseAndExtent(staticInEditor.textNode, staticInEditor.textLength, 201 innerEditor.textNode, innerEditor.textLength); 202 aTest.func(staticInEditor.textNode, 0); 203 assert_equals(document.activeElement, document.body); 204 assert_equals(document.documentElement.scrollTop, 0); 205 }, "Active element should be the <body> after " + aTest.doingDescription + " with start of the first text node of 'staticInEditor' (before the selection, between end of the first text node of 'staticInEditor' and end of the first text node of 'innerEditor') when active element is the <body>"); 206 }); 207 208 test(function() { 209 resetFocusAndSelectionRange(); 210 document.getSelection().selectAllChildren(staticAfter.textNode); 211 setEndAfter(innerEditor.textNode); 212 assert_equals(document.activeElement, innerEditor.element); 213 assert_equals(document.documentElement.scrollTop, 0); 214 }, "Active element should be 'innerEditor' after Selection.setEndAfter() with the first text node of 'innerEditor' (before the selection) when active element is the <body> and selection is in 'staticAfter'"); 215 test(function() { 216 resetFocusAndSelectionRange(innerEditor); 217 document.getSelection().selectAllChildren(innerEditor.textNode); 218 setEndAfter(staticInEditor.textNode); 219 assert_equals(document.activeElement, innerEditor.element); 220 assert_equals(document.documentElement.scrollTop, 0); 221 }, "Active element should be 'innerEditor' after Selection.setEndAfter() with the first text node of 'staticInEditor' (before the selection) when active element is 'innerEditor'"); 222 test(function() { 223 resetFocusAndSelectionRange(); 224 document.getSelection().selectAllChildren(staticInEditor.textNode); 225 setEndAfter(outerEditor.textNode); 226 assert_equals(document.activeElement, outerEditor.element); 227 assert_equals(document.documentElement.scrollTop, 0); 228 }, "Active element should be 'outerEditor' after Selection.setEndAfter() with the first text node of 'outerEditor' (before the selection) when active element is the <body> and selection is in 'staticInEditor'"); 229 test(function() { 230 resetFocusAndSelectionRange(innerEditor); 231 document.getSelection().selectAllChildren(innerEditor.textNode); 232 setEndAfter(outerEditor.textNode); 233 assert_equals(document.activeElement, outerEditor.element); 234 assert_equals(document.documentElement.scrollTop, 0); 235 }, "Active element should be 'outerEditor' after Selection.setEndAfter() with the first text node of 'outerEditor' (before the selection) when active element is 'innerEditor'"); 236 test(function() { 237 resetFocusAndSelectionRange(outerEditor); 238 document.getSelection().selectAllChildren(outerEditor.textNode); 239 setEndAfter(editor.textNode); 240 assert_equals(document.activeElement, editor.element); 241 assert_equals(document.documentElement.scrollTop, 0); 242 }, "Active element should be 'editor' after Selection.setEndAfter() with the first text node of 'editor' (before the selection) when active element is 'outerEditor'"); 243 test(function() { 244 resetFocusAndSelectionRange(editor); 245 document.getSelection().selectAllChildren(editor.textNode); 246 setEndAfter(staticBefore.textNode); 247 assert_equals(document.activeElement, editor.element); 248 assert_equals(document.documentElement.scrollTop, 0); 249 }, "Active element should be 'editor' after Selection.setEndAfter() with the first text node of 'staticBefore' (before the selection) when active element is 'editor'"); 250 test(function() { 251 resetFocusAndSelectionRange(anchor); 252 document.getSelection().selectAllChildren(anchor.textNode); 253 setEndAfter(staticBefore.textNode); 254 assert_equals(document.activeElement, anchor.element); 255 assert_equals(document.documentElement.scrollTop, 0); 256 }, "Active element should be 'anchor' after Selection.setEndAfter() with the first text node of 'staticBefore' (before the selection) when active element is 'anchor'"); 257 258 // Range.setEnd*() should blur focused editing host when it expands selection to outside of it. 259 [{ func: setEnd, doingDescription: "Range.setEnd()" }, 260 { func: setEndAfter, doingDescription: "Range.setEndAfter()" }, 261 { func: setEndBefore, doingDescription: "Range.setEndBefore()" }].forEach((aTest, aIndex, aArray)=>{ 262 test(function() { 263 resetFocusAndSelectionRange(editor); 264 document.getSelection().collapse(editor.textNode, 0); 265 aTest.func(outerEditor.textNode, 0); 266 assert_equals(document.activeElement, editor.element); 267 assert_equals(document.documentElement.scrollTop, 0); 268 }, "Active element should be 'editor' after " + aTest.doingDescription + " with start of the first text node of 'outerEditor' (after end of the collapsed selection) when active element is 'editor'"); 269 test(function() { 270 resetFocusAndSelectionRange(outerEditor); 271 document.getSelection().collapse(outerEditor.textNode, 0); 272 aTest.func(staticInEditor.textNode, 0); 273 assert_equals(document.activeElement, outerEditor.element); 274 assert_equals(document.documentElement.scrollTop, 0); 275 }, "Active element should be 'outerEditor' after " + aTest.doingDescription + " with start of the first text node of 'outerEditor' (after end of the collapsed selection) when active element is 'outerEditor'"); 276 test(function() { 277 resetFocusAndSelectionRange(outerEditor); 278 document.getSelection().collapse(outerEditor.textNode, 0); 279 aTest.func(innerEditor.textNode, 0); 280 assert_equals(document.activeElement, outerEditor.element); 281 assert_equals(document.documentElement.scrollTop, 0); 282 }, "Active element should be 'outerEditor' after " + aTest.doingDescription + " with start of the first text node of 'innerEditor' (after end of the collapsed selection) when active element is 'outerEditor'"); 283 test(function() { 284 resetFocusAndSelectionRange(outerEditor); 285 document.getSelection().collapse(outerEditor.textNode, 0); 286 aTest.func(staticAfter.textNode, 0); 287 assert_equals(document.activeElement, outerEditor.element); 288 assert_equals(document.documentElement.scrollTop, 0); 289 }, "Active element should be 'outerEditor' after " + aTest.doingDescription + " with start of the first text node of 'staticAfter' (after end of the collapsed selection) when active element is 'outerEditor'"); 290 test(function() { 291 resetFocusAndSelectionRange(editor); 292 document.getSelection().selectAllChildren(editor.textNode); 293 aTest.func(outerEditor.textNode, 0); 294 assert_equals(document.activeElement, editor.element); 295 assert_equals(document.documentElement.scrollTop, 0); 296 }, "Active element should be 'editor' after " + aTest.doingDescription + " with start of the first text node of 'outerEditor' (after end of the selection) when active element is 'editor'"); 297 test(function() { 298 resetFocusAndSelectionRange(outerEditor); 299 document.getSelection().selectAllChildren(outerEditor.textNode); 300 aTest.func(staticInEditor.textNode, 0); 301 assert_equals(document.activeElement, outerEditor.element); 302 assert_equals(document.documentElement.scrollTop, 0); 303 }, "Active element should be 'outerEditor' after " + aTest.doingDescription + " with start of the first text node of 'outerEditor' (after end of the selection) when active element is 'outerEditor'"); 304 test(function() { 305 resetFocusAndSelectionRange(outerEditor); 306 document.getSelection().selectAllChildren(outerEditor.textNode); 307 aTest.func(innerEditor.textNode, 0); 308 assert_equals(document.activeElement, outerEditor.element); 309 assert_equals(document.documentElement.scrollTop, 0); 310 }, "Active element should be 'outerEditor' after " + aTest.doingDescription + " with start of the first text node of 'innerEditor' (after end of the selection) when active element is 'outerEditor'"); 311 test(function() { 312 resetFocusAndSelectionRange(outerEditor); 313 document.getSelection().selectAllChildren(outerEditor.textNode); 314 aTest.func(staticAfter.textNode, 0); 315 assert_equals(document.activeElement, outerEditor.element); 316 assert_equals(document.documentElement.scrollTop, 0); 317 }, "Active element should be 'outerEditor' after " + aTest.doingDescription + " with start of the first text node of 'staticAfter' (after end of the selection) when active element is 'outerEditor'"); 318 }); 319 320 // Range.setEnd*() should move focus to an editing host when the range is shrunken into it. 321 [{ func: setEnd, doingDescription: "Range.setEnd()" }, 322 { func: setEndAfter, doingDescription: "Range.setEndAfter()" }].forEach((aTest, aIndex, aArray)=>{ 323 test(function() { 324 resetFocusAndSelectionRange(); 325 document.getSelection().setBaseAndExtent(editor.textNode, 0, 326 outerEditor.textNode, outerEditor.textLength); 327 aTest.func(editor.textNode, editor.textLength); 328 assert_equals(document.activeElement, editor.element); 329 assert_equals(document.documentElement.scrollTop, 0); 330 }, "Active element should be 'editor' after " + aTest.doingDescription + " with end of the first text node of 'editor' (shrunken into 'editor') when active element is the <body>"); 331 test(function() { 332 resetFocusAndSelectionRange(outerEditor); 333 document.getSelection().setBaseAndExtent(outerEditor.textNode, 0, 334 staticInEditor.textNode, staticInEditor.textLength); 335 aTest.func(outerEditor.textNode, outerEditor.textLength); 336 assert_equals(document.activeElement, outerEditor.element); 337 assert_equals(document.documentElement.scrollTop, 0); 338 }, "Active element should be 'outerEditor' after " + aTest.doingDescription + " with end of the first text node of 'outerEditor' (shrunken into 'outerEditor') when active element is 'outerEditor' and selection end is in 'staticInEditor'"); 339 test(function() { 340 resetFocusAndSelectionRange(outerEditor); 341 document.getSelection().setBaseAndExtent(outerEditor.textNode, 0, 342 innerEditor.textNode, innerEditor.textLength); 343 aTest.func(outerEditor.textNode, outerEditor.textLength); 344 assert_equals(document.activeElement, outerEditor.element); 345 assert_equals(document.documentElement.scrollTop, 0); 346 }, "Active element should be 'outerEditor' after " + aTest.doingDescription + " with end of the first text node of 'outerEditor' (shrunken into 'outerEditor') when active element is 'outerEditor' and selection end is in 'innerEditor'"); 347 test(function() { 348 resetFocusAndSelectionRange(); 349 document.getSelection().setBaseAndExtent(innerEditor.textNode, 0, 350 staticAfter.textNode, staticAfter.textLength); 351 aTest.func(innerEditor.textNode, innerEditor.textLength); 352 assert_equals(document.activeElement, innerEditor.element); 353 assert_equals(document.documentElement.scrollTop, 0); 354 }, "Active element should be 'innerEditor' after " + aTest.doingDescription + " with end of the first text node of 'innerEditor' (shrunken into 'innerEditor') when active element is 'innerEditor' and selection end is in 'staticAfter'"); 355 test(function() { 356 resetFocusAndSelectionRange(); 357 document.getSelection().setBaseAndExtent(staticInEditor.textNode, 0, 358 innerEditor.textNode, innerEditor.textLength); 359 aTest.func(staticInEditor.textNode, staticInEditor.textLength); 360 assert_equals(document.activeElement, document.body); 361 assert_equals(document.documentElement.scrollTop, 0); 362 }, "Active element should be the <body> after " + aTest.doingDescription + " with end of the first text node of 'staticInEditor' (shrunken into 'staticInEditor') when active element is the <body>"); 363 }); 364 </script>