test_moving_and_expanding_selection_per_page.html (18871B)
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title>Test for expanding selection per page</title> 5 <script src="/tests/SimpleTest/SimpleTest.js"></script> 6 <script src="/tests/SimpleTest/EventUtils.js"></script> 7 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> 8 </head> 9 <body> 10 11 <pre id="test"> 12 <script class="testbody" type="text/javascript"> 13 SimpleTest.waitForExplicitFinish(); 14 addLoadEvent(() => { 15 open("window_empty_document.html", "_blank", "width=500,height=500"); 16 }); 17 18 async function doTests(aWindow) { 19 // On macOS, there is no shortcut keys to extend selection per page. 20 // Therefore, we need to use nsISelectionController.pageMove() instead. 21 const kUseKeyboardEvent = !navigator.platform.includes("Mac"); 22 let selectionController = SpecialPowers.wrap(aWindow) 23 .docShell 24 .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor) 25 .getInterface(SpecialPowers.Ci.nsISelectionDisplay) 26 .QueryInterface(SpecialPowers.Ci.nsISelectionController); 27 // On Windows, per-page selection to start or end expands selection to same 28 // column of first or last line. On the other platforms, it expands selection 29 // to start or end of first or last line. 30 const kSelectToStartOrEnd = !navigator.platform.includes("Win"); 31 32 await SpecialPowers.pushPrefEnv({"set": [["general.smoothScroll", false]]}); 33 await SimpleTest.promiseFocus(aWindow); 34 35 function getNodeDescription(aNode) { 36 function getElementDescription(aElement) { 37 if (aElement.getAttribute("id") !== null) { 38 return `${aElement.tagName.toLowerCase()}#${aElement.getAttribute("id")}`; 39 } 40 if (aElement.tagName === "BR") { 41 return `${getElementDescription(aElement.previousSibling)} + br`; 42 } 43 return aElement.tagName.toLowerCase(); 44 } 45 switch (aNode.nodeType) { 46 case aNode.TEXT_NODE: 47 return `text node in ${getElementDescription(aNode.parentElement)}`; 48 case aNode.ELEMENT_NODE: 49 return getElementDescription(aNode); 50 default: 51 return "unknown node"; 52 } 53 } 54 55 function doTest(aExpandSelection) { 56 // Note that when neither editor has focus nor in caret mode, key navigation 57 // does not call nsISelectionController::PageMove(). Therefore, in such 58 // cases, you need to call doPageDown() and doPageUp() with setting true 59 // to aUseSelectionController. 60 function doPageDown(aUseSelectionController) { 61 if (kUseKeyboardEvent && !aUseSelectionController) { 62 synthesizeKey("KEY_PageDown", {shiftKey: aExpandSelection}, aWindow); 63 } else { 64 selectionController.pageMove(true, aExpandSelection); 65 } 66 } 67 68 function doPageUp(aUseSelectionController) { 69 if (kUseKeyboardEvent && !aUseSelectionController) { 70 synthesizeKey("KEY_PageUp", {shiftKey: aExpandSelection}, aWindow); 71 } else { 72 selectionController.pageMove(false, aExpandSelection); 73 } 74 } 75 76 let doc = aWindow.document; 77 let body = doc.body; 78 let selection = doc.getSelection(); 79 let container; 80 81 body.innerHTML = '<span id="s1">first line</span><br>' + 82 '<span id="s2">second line</span><br>' + 83 '<span id="s3">last line</span>'; 84 container = doc.documentElement; 85 86 let description = `${aExpandSelection ? "Expanding selection" : "Moving caret"} to forward in non-scrollable body: `; 87 is(container.scrollTop, 0, description + "scrollTop should be 0 at initialization"); 88 selection.collapse(doc.getElementById("s1").firstChild, 3); 89 doPageDown(!aExpandSelection); 90 is(container.scrollTop, 0, description + "this test shouldn't create scrollable document"); 91 let range = selection.getRangeAt(0); 92 if (aExpandSelection) { 93 is(range.startContainer, doc.getElementById("s1").firstChild, 94 `${description} selection should be expanded from the first line (got: ${getNodeDescription(range.startContainer)})`); 95 is(range.startOffset, 3, 96 `${description} selection should be expanded from the first line's 3rd insertion point`); 97 } else { 98 ok(range.collapsed, `${description} selection should be collapsed`); 99 } 100 is(range.endContainer, doc.getElementById("s3").firstChild, 101 `${description} selection should be expanded into the last line (got: ${getNodeDescription(range.endContainer)})`); 102 if (kSelectToStartOrEnd) { 103 is(range.endOffset, range.endContainer.length, 104 `${description} selection should be expanded to end of the last line`); 105 } else { 106 isfuzzy(range.endOffset, 3, 2, 107 `${description} selection should be expanded to around the last line's 3rd insertion point`); 108 } 109 110 description = `${aExpandSelection ? "Expanding selection" : "Moving caret"} to backward in non-scrollable body: `; 111 selection.collapse(doc.getElementById("s3").firstChild, 3); 112 doPageUp(!aExpandSelection); 113 is(container.scrollTop, 0, description + "this test shouldn't create scrollable document"); 114 range = selection.getRangeAt(0); 115 is(range.startContainer, doc.getElementById("s1").firstChild, 116 `${description} selection should be expanded into the first line (got: ${getNodeDescription(range.startContainer)})`); 117 if (kSelectToStartOrEnd) { 118 is(range.startOffset, 0, 119 `${description} selection should be expanded to start of the first line`); 120 } else { 121 isfuzzy(range.startOffset, 3, 2, 122 `${description} selection should be expanded to around the first line's 3rd insertion point`); 123 } 124 if (aExpandSelection) { 125 is(range.endContainer, doc.getElementById("s3").firstChild, 126 `${description} selection should be expanded from the last line (got: ${getNodeDescription(range.endContainer)})`); 127 is(range.endOffset, 3, 128 `${description} selection should be expanded from the last line's 3rd insertion point`); 129 } else { 130 ok(range.collapsed, `${description} selection should be collapsed`); 131 } 132 133 body.innerHTML = '<span id="s1">first line in the body</span>' + 134 '<div id="d1" style="height: 2em; line-height: 1em; overflow: auto;">' + 135 '<span id="s2">first line</span><br>' + 136 '<span id="s3">second line</span><br>' + 137 '<span id="s4">third line</span><br>' + 138 '<span id="s5">last line</span>' + 139 "</div>" + 140 '<span id="s6">last line in the body</span>'; 141 container = doc.getElementById("d1"); 142 143 description = `${aExpandSelection ? "Expanding selection" : "Moving caret"} to forward in scrollable area in the body: `; 144 is(container.scrollTop, 0, description + "scrollTop should be 0 at initialization"); 145 selection.collapse(doc.getElementById("s2").firstChild, 3); 146 doPageDown(!aExpandSelection); 147 isnot(container.scrollTop, 0, description + "should be scrolled down"); 148 range = selection.getRangeAt(0); 149 if (aExpandSelection) { 150 is(range.startContainer, doc.getElementById("s2").firstChild, 151 `${description} selection should be expanded from the first line (got: ${getNodeDescription(range.startContainer)})`); 152 is(range.startOffset, 3, 153 `${description} selection should be expanded from the first line's 3rd insertion point`); 154 } else { 155 ok(range.collapsed, `${description} selection should be collapsed`); 156 } 157 is(range.endContainer, doc.getElementById("s4").firstChild, 158 `${description} selection should be expanded into the 3rd line (got: ${getNodeDescription(range.endContainer)})`); 159 isfuzzy(range.endOffset, 3, 2, 160 `${description} selection should be expanded to around the 3rd line's 3rd insertion point`); 161 162 description = `${aExpandSelection ? "Expanding selection" : "Moving caret"} to backward in scrollable area in the body: `; 163 selection.collapse(doc.getElementById("s4").firstChild, 3); 164 let previousScrollTop = container.scrollTop; 165 doPageUp(!aExpandSelection); 166 ok(container.scrollTop < previousScrollTop, description + "should be scrolled up"); 167 range = selection.getRangeAt(0); 168 is(range.startContainer, doc.getElementById("s2").firstChild, 169 `${description} selection should be expanded into the first line (got: ${getNodeDescription(range.startContainer)})`); 170 isfuzzy(range.startOffset, 3, 2, 171 `${description} selection should be expanded to around the first line's 3rd insertion point`); 172 if (aExpandSelection) { 173 is(range.endContainer, doc.getElementById("s4").firstChild, 174 `${description} selection should be expanded from the 3rd line (got: ${getNodeDescription(range.endContainer)})`); 175 is(range.endOffset, 3, 176 `${description} selection should be expanded from the 3rd line's 3rd insertion point`); 177 } else { 178 ok(range.collapsed, `${description} selection should be collapsed`); 179 } 180 181 body.innerHTML = '<span id="s1">first line in the body</span>' + 182 '<div id="d1" contenteditable style="height: 2em; line-height: 1em; overflow: auto;">' + 183 '<span id="s2">first line</span><br>' + 184 '<span id="s3">second line</span><br>' + 185 '<span id="s4">third line</span><br>' + 186 '<span id="s5">last line</span>' + 187 "</div>" + 188 '<span id="s6">last line in the body</span>'; 189 container = doc.getElementById("d1"); 190 191 description = `${aExpandSelection ? "Expanding selection" : "Moving caret"} to forward in scrollable editable div in the body: `; 192 is(container.scrollTop, 0, description + "scrollTop should be 0 at initialization"); 193 selection.collapse(doc.getElementById("s2").firstChild, 3); 194 doPageDown(); 195 isnot(container.scrollTop, 0, description + "should be scrolled down"); 196 range = selection.getRangeAt(0); 197 if (aExpandSelection) { 198 is(range.startContainer, doc.getElementById("s2").firstChild, 199 `${description} selection should be expanded from the first line (got: ${getNodeDescription(range.startContainer)})`); 200 is(range.startOffset, 3, 201 `${description} selection should be expanded from the first line's 3rd insertion point`); 202 } else { 203 ok(range.collapsed, `${description} selection should be collapsed`); 204 } 205 is(range.endContainer, doc.getElementById("s4").firstChild, 206 `${description} selection should be expanded into the 3rd line (got: ${getNodeDescription(range.endContainer)})`); 207 isfuzzy(range.endOffset, 3, 2, 208 `${description} selection should be expanded to around the 3rd line's 3rd insertion point`); 209 210 description = `${aExpandSelection ? "Expanding selection" : "Moving caret"} to backward in scrollable editable div in the body: `; 211 selection.collapse(doc.getElementById("s4").firstChild, 3); 212 previousScrollTop = container.scrollTop; 213 doPageUp(); 214 ok(container.scrollTop < previousScrollTop, description + "should be scrolled up"); 215 range = selection.getRangeAt(0); 216 is(range.startContainer, doc.getElementById("s2").firstChild, 217 `${description} selection should be expanded into the first line (got: ${getNodeDescription(range.startContainer)})`); 218 isfuzzy(range.startOffset, 3, 2, 219 `${description} selection should be expanded to around the first line's 3rd insertion point`); 220 if (aExpandSelection) { 221 is(range.endContainer, doc.getElementById("s4").firstChild, 222 `${description} selection should be expanded from the 3rd line (got: ${getNodeDescription(range.endContainer)})`); 223 is(range.endOffset, 3, 224 `${description} selection should be expanded from the 3rd line's 3rd insertion point`); 225 } else { 226 ok(range.collapsed, `${description} selection should be collapsed`); 227 } 228 229 body.innerHTML = '<span id="s1">first line in the body</span>' + 230 '<div id="d1" contenteditable>' + 231 '<span id="s2">first line</span><br>' + 232 '<span id="s3">second line</span><br>' + 233 '<span id="s4">third line</span><br>' + 234 '<span id="s5">last line</span>' + 235 "</div>" + 236 '<span id="s6">last line in the body</span>'; 237 container = doc.getElementById("d1"); 238 239 description = `${aExpandSelection ? "Expanding selection" : "Moving caret"} to forward in non-scrollable editable div in the body: `; 240 is(container.scrollTop, 0, description + "scrollTop should be 0 at initialization"); 241 selection.collapse(doc.getElementById("s2").firstChild, 3); 242 doPageDown(); 243 is(container.scrollTop, 0, description + "editable div shouldn't be scrollable"); 244 range = selection.getRangeAt(0); 245 if (aExpandSelection) { 246 is(range.startContainer, doc.getElementById("s2").firstChild, 247 `${description} selection should be expanded from the first line (got: ${getNodeDescription(range.startContainer)})`); 248 is(range.startOffset, 3, 249 `${description} selection should be expanded from the first line's 3rd insertion point`); 250 } else { 251 ok(range.collapsed, `${description} selection should be collapsed`); 252 } 253 is(range.endContainer, doc.getElementById("s5").firstChild, 254 `${description} selection should be expanded into the last line (got: ${getNodeDescription(range.endContainer)})`); 255 if (kSelectToStartOrEnd) { 256 is(range.endOffset, range.endContainer.length, 257 `${description} selection should be expanded to end of the last line`); 258 } else { 259 isfuzzy(range.endOffset, 3, 2, 260 `${description} selection should be expanded to around the last line's 3rd insertion point`); 261 } 262 263 description = `${aExpandSelection ? "Expanding selection" : "Moving caret"} to backward in non-scrollable editable div in the body: `; 264 selection.collapse(doc.getElementById("s5").firstChild, 3); 265 doPageUp(); 266 is(container.scrollTop, 0, description + "editable div shouldn't be scrollable"); 267 range = selection.getRangeAt(0); 268 is(range.startContainer, doc.getElementById("s2").firstChild, 269 `${description} selection should be expanded into the first line (got: ${getNodeDescription(range.startContainer)})`); 270 if (kSelectToStartOrEnd) { 271 is(range.startOffset, 0, 272 `${description} selection should be expanded to start of the first line`); 273 } else { 274 isfuzzy(range.startOffset, 3, 2, 275 `${description} selection should be expanded to around the first line's 3rd insertion point`); 276 } 277 if (aExpandSelection) { 278 is(range.endContainer, doc.getElementById("s5").firstChild, 279 `${description} selection should be expanded from the last line (got: ${getNodeDescription(range.endContainer)})`); 280 is(range.endOffset, 3, 281 `${description} selection should be expanded from the last line's 3rd insertion point`); 282 } else { 283 ok(range.collapsed, `${description} selection should be collapsed`); 284 } 285 286 body.innerHTML = '<span id="s1">first line in the body</span>' + 287 '<div id="d1" contenteditable>' + 288 '<span id="s2">first editable line</span><br>' + 289 '<div id="d2" style="height: 3em; line-height: 1em; overflow: auto;">' + 290 '<span id="s3">first line</span><br>' + 291 '<span id="s4">second line</span>' + 292 "</div>" + 293 '<span id="s5">last editable line</span>' + 294 "</div>" + 295 '<span id="s6">last line in the body</span>'; 296 container = doc.getElementById("d2"); 297 298 description = `${aExpandSelection ? "Expanding selection" : "Moving caret"} to forward in scrollable div (but not scrollable along y-axis) in the editable div: `; 299 is(container.scrollTop, 0, description + "scrollTop should be 0 at initialization"); 300 selection.collapse(doc.getElementById("s3").firstChild, 3); 301 doPageDown(); 302 is(container.scrollTop, 0, description + "scrollable div in the editable div (but not scrollable along y-axis) shouldn't be scrollable"); 303 range = selection.getRangeAt(0); 304 if (aExpandSelection) { 305 is(range.startContainer, doc.getElementById("s3").firstChild, 306 `${description} selection should be expanded from the first line (got: ${getNodeDescription(range.startContainer)})`); 307 is(range.startOffset, 3, 308 `${description} selection should be expanded from the first line's 3rd insertion point`); 309 } else { 310 ok(range.collapsed, `${description} selection should be collapsed`); 311 } 312 is(range.endContainer, doc.getElementById("s5").firstChild, 313 `${description} selection should be expanded into the last editable line (got: ${getNodeDescription(range.endContainer)})`); 314 if (kSelectToStartOrEnd) { 315 is(range.endOffset, range.endContainer.length, 316 `${description} selection should be expanded to end of the last editable line`); 317 } else { 318 isfuzzy(range.endOffset, 3, 2, 319 `${description} selection should be expanded to around the last editable line's 3rd insertion point`); 320 } 321 322 description = `${aExpandSelection ? "Expanding selection" : "Moving caret"} to backward in scrollable div (but not scrollable along y-axis) in the editable div: `; 323 selection.collapse(doc.getElementById("s4").firstChild, 3); 324 doPageUp(); 325 is(container.scrollTop, 0, description + "scrollable div (but not scrollable along y-axis) in the editable div shouldn't be scrollable"); 326 range = selection.getRangeAt(0); 327 is(range.startContainer, doc.getElementById("s2").firstChild, 328 `${description} selection should be expanded into the first editable line (got: ${getNodeDescription(range.startContainer)})`); 329 if (kSelectToStartOrEnd) { 330 is(range.startOffset, 0, 331 `${description} selection should be expanded to start of the first editable line`); 332 } else { 333 isfuzzy(range.startOffset, 3, 2, 334 `${description} selection should be expanded to around the first editable line's 3rd insertion point`); 335 } 336 if (aExpandSelection) { 337 is(range.endContainer, doc.getElementById("s4").firstChild, 338 `${description} selection should be expanded from the last line (got: ${getNodeDescription(range.endContainer)})`); 339 is(range.endOffset, 3, 340 `${description} selection should be expanded from the last line's 3rd insertion point`); 341 } else { 342 ok(range.collapsed, `${description} selection should be collapsed`); 343 } 344 } 345 346 doTest(false); 347 doTest(true); 348 349 aWindow.close(); 350 SimpleTest.finish(); 351 } 352 </script> 353 </html>