test_selection_changes_with_middle_mouse_button.html (13646B)
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title>Test for selection changes with middle mouse button</title> 5 <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> 6 <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script> 7 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> 8 <style> 9 span, td { 10 white-space: nowrap; 11 } 12 </style> 13 </head> 14 <body> 15 <p id="display"></p> 16 <div id="content" style="display: none;"> 17 18 </div> 19 20 <textarea id="textarea" cols="1" rows="1">copied to clipboard</textarea> 21 22 <div id="container"> 23 <span id="span1">first span.</span> 24 <span id="span2">second span.</span><br> 25 <a id="link" href="#top">link.</a> 26 <table> 27 <tr><td id="td1">first td.</td></tr> 28 <tr><td id="td2">second td.</td></tr> 29 </table> 30 </div> 31 32 <pre id="test"> 33 34 <script class="testbody" type="application/javascript"> 35 SimpleTest.waitForExplicitFinish(); 36 37 const kIsMac = navigator.platform.includes("Mac"); 38 const selection = getSelection(); 39 40 async function doTests(aEnableMiddlePaste, aEditable, aDescription) { 41 await SpecialPowers.pushPrefEnv({"set": [ 42 ["middlemouse.paste", aEnableMiddlePaste], 43 ["middlemouse.contentLoadURL", false], 44 ["general.autoScroll", false], 45 ["general.autoscroll.prevent_to_collapse_selection_by_middle_mouse_down", false], 46 ]}); 47 48 if (aEditable) { 49 document.getElementById("container").setAttribute("contenteditable", "true"); 50 } else { 51 document.getElementById("container").removeAttribute("contenteditable"); 52 } 53 54 let pasteEvents = []; 55 function pasteEventHandler(event) { 56 pasteEvents.push(event); 57 event.preventDefault(); 58 } 59 document.getElementById("container").addEventListener("paste", pasteEventHandler, true); 60 61 // We need to behave as same as Chromium as far as possible. 62 // - middle mouse button down should be collapse selection at the point. 63 // - middle mouse button down can expand only with mouse button down with Shift key. 64 // - middle mouse button shouldn't select table cells. 65 66 function doTest(aMouseDown, aMouseUp, 67 aExpectedSelectionAnchor, aExpectedSelectionFocus, aExpectedPastEventTarget, 68 aAdditionalDescription) { 69 pasteEvents = []; 70 synthesizeMouseAtCenter(aMouseDown.target, 71 { 72 button: 1, 73 type: "mousedown", 74 shiftKey: aMouseDown.shiftKey, 75 ctrlKey: aMouseDown.ctrlKey && !kIsMac, 76 metaKey: aMouseDown.ctrlKey && kIsMac, 77 }); 78 if (aExpectedSelectionAnchor === aExpectedSelectionFocus) { 79 ok(selection.isCollapsed, 80 aDescription + aAdditionalDescription + "Selection should be collapsed at mousedown"); 81 is(selection.focusNode, aExpectedSelectionFocus, 82 aDescription + aAdditionalDescription + "Selection should be collapsed in the node at mousedown"); 83 } else { 84 is(selection.anchorNode, aExpectedSelectionAnchor, 85 aDescription + aAdditionalDescription + "Anchor node of Selection should be previous anchor node"); 86 is(selection.focusNode, aExpectedSelectionFocus, 87 aDescription + aAdditionalDescription + "Focus node of Selection should be the node at mousedown"); 88 } 89 is(pasteEvents.length, 0, 90 aDescription + aAdditionalDescription + "paste event shouldn't be fired when middle mouse button down"); 91 92 if (aMouseDown.target != aMouseUp.target) { 93 synthesizeMouseAtCenter(aMouseUp.target, {type: "mousemove"}); 94 } 95 synthesizeMouseAtCenter(aMouseUp.target, 96 { 97 button: 1, 98 type: "mouseup", 99 shiftKey: aMouseUp.shiftKey, 100 ctrlKey: aMouseUp.ctrlKey && !kIsMac, 101 metaKey: aMouseUp.ctrlKey && kIsMac, 102 }); 103 is(selection.anchorNode, aExpectedSelectionAnchor, 104 aDescription + aAdditionalDescription + "Anchor node of Selection shouldn't be modified at mouseup"); 105 is(selection.focusNode, aExpectedSelectionFocus, 106 aDescription + aAdditionalDescription + "Focus node of Selection shouldn't be modified at mouseup"); 107 if (aEnableMiddlePaste) { 108 if (aExpectedPastEventTarget === null) { 109 is(pasteEvents.length, 0, 110 aDescription + aAdditionalDescription + "paste event shouldn't be fired even when middle mouse button up"); 111 } else { 112 is(pasteEvents.length, 1, 113 aDescription + aAdditionalDescription + "paste event should be fired only once at mouse up"); 114 is(pasteEvents[0]?.target, aExpectedPastEventTarget, 115 aDescription + aAdditionalDescription + "paste event should be fired on start of selection"); 116 } 117 } else { 118 is(pasteEvents.length, 0, 119 aDescription + aAdditionalDescription + "paste event shouldn't be fired when middle mouse button up when middle mouse paste is disabled"); 120 } 121 } 122 123 let span1 = document.getElementById("span1"); 124 let span2 = document.getElementById("span2"); 125 let link = document.getElementById("link"); 126 127 selection.removeAllRanges(); 128 doTest({target: span1}, {target: span1}, 129 span1.firstChild, span1.firstChild, span1, 130 "Clicking span1 when there is no selection: "); 131 doTest({target: span2}, {target: span2}, 132 span2.firstChild, span2.firstChild, span2, 133 "Clicking span2 when selection is collapsed in span1: "); 134 doTest({target: span1}, {target: span2}, 135 span1.firstChild, span1.firstChild, span1, 136 "Dragging from span1 to span2: "); 137 doTest({target: span2}, {target: span1}, 138 span2.firstChild, span2.firstChild, span2, 139 "Dragging from span2 to span1: "); 140 doTest({target: span1, shiftKey: true}, {target: span1, shiftKey: true}, 141 span2.firstChild, span1.firstChild, span1, 142 "Expanding selection with Shift key from span2 to span1: "); 143 selection.collapse(span1.firstChild, 3); 144 if (aEditable) { 145 // Collapse link into editable link. 146 doTest({target: link, shiftKey: true}, {target: link, shiftKey: true}, 147 link.firstChild, link.firstChild, 148 link /* TODO: Perhaps, the "paste" event target should be the link */, 149 "Clicking an editable link with middle-button with Shift key when selection is collapsed in span1: "); 150 } else { 151 // Don't extend selection into a link. 152 link.onauxclick = event => event.preventDefault(); 153 doTest({target: link, shiftKey: true}, {target: link, shiftKey: true}, 154 span1.firstChild, span1.firstChild, 155 null /* due to the call of preventDefault */, 156 "Clicking a link with middle-button with Shift key when selection is collapsed in span1: "); 157 link.onauxclick = null; 158 } 159 // "paste" event should be fired in the "start" of selection. 160 selection.collapse(span1.firstChild, 3); 161 doTest({target: span2, shiftKey: true}, {target: span2, shiftKey: true}, 162 span1.firstChild, span2.firstChild, span1, 163 "Expanding selection with Shift key from span1 to span2: "); 164 // XXX This case is different from Chrome for Linux. 165 // In this case, Chrome does not collapse Selection at mousedown, 166 // but collapse at click. So, if mouseup occurs different element, 167 // Selection isn't modified. 168 selection.selectAllChildren(span1); 169 doTest({target: span1}, {target: span1}, 170 span1.firstChild, span1.firstChild, span1, 171 "Clicking span1 when span1 is selected: "); 172 173 let td1 = document.getElementById("td1"); 174 let td2 = document.getElementById("td2"); 175 176 selection.removeAllRanges(); 177 doTest({target: td1}, {target: td1}, 178 td1.firstChild, td1.firstChild, td1, 179 "Clicking td1 when there is no selection: "); 180 if (aEditable) { 181 // XXX In this case, we don't allow to expand selection with Shift key 182 // click across table cell boundary. 183 doTest({target: td2, shiftKey: true}, {target: td2, shiftKey: true}, 184 td1.firstChild, td1.firstChild, td1, 185 "Expanding selection with Shift key from td1 to td2: "); 186 } else { 187 doTest({target: td2, shiftKey: true}, {target: td2, shiftKey: true}, 188 td1.firstChild, td2.firstChild, td1, 189 "Expanding selection with Shift key from td1 to td2: "); 190 } 191 // Shouldn't select per table cell when the button is middle mouse button. 192 doTest({target: td1, ctrlKey: true}, {target: td1, ctrlKey: true}, 193 td1.firstChild, td1.firstChild, td1, 194 "Click td1 with Control key: "); 195 196 document.getElementById("container").removeEventListener("paste", pasteEventHandler, true); 197 } 198 199 async function runTestPreventingToCollapseSelectionByPrefs() { 200 await SpecialPowers.pushPrefEnv({"set": [ 201 ["middlemouse.contentLoadURL", false], 202 ["general.autoscroll.prevent_to_collapse_selection_by_middle_mouse_down", true], 203 ]}); 204 205 // If middle click paste is disabled and autoscroll is enabled, we should 206 // allow users to prevent to collapse selection by middle mouse down. 207 await SpecialPowers.pushPrefEnv({"set": [ 208 ["middlemouse.paste", false], 209 ["general.autoScroll", true], 210 ]}); 211 212 const container = document.getElementById("container"); 213 container.removeAttribute("contenteditable"); 214 container.getBoundingClientRect(); 215 216 const span1 = container.querySelector("span#span1"); 217 const span2 = container.querySelector("span#span2"); 218 function checkSelectAllChildrenOfSpan2(description) { 219 is( 220 getSelection().focusNode, 221 span2, 222 `Selection shouldn't be collapsed by ${description} (focusNode)` 223 ); 224 is( 225 getSelection().focusOffset, 226 span2.childNodes.length, 227 `Selection shouldn't be collapsed by ${description} (focusOffset)` 228 ); 229 is( 230 getSelection().anchorNode, 231 span2, 232 `Selection shouldn't be collapsed by ${description} (anchorNode)` 233 ); 234 is( 235 getSelection().anchorOffset, 236 0, 237 `Selection shouldn't be collapsed by ${description} (anchorOffset)` 238 ); 239 } 240 241 function checkSelectionCollapsedInSpan1(description) { 242 ok( 243 getSelection().isCollapsed, 244 `Selection should be collapsed into the clicked text node ${description} (isCollapsed)` 245 ); 246 is( 247 getSelection().focusNode, 248 span1.firstChild, 249 `Selection should be collapsed into the clicked text node ${description} (focusNode)` 250 ); 251 is( 252 getSelection().anchorNode, 253 span1.firstChild, 254 `Selection should be collapsed into the clicked text node ${description} (anchorNode)` 255 ); 256 } 257 getSelection().selectAllChildren(span2); 258 synthesizeMouseAtCenter(document.getElementById("span1"), { type: "mousedown", button: 1 }); 259 checkSelectAllChildrenOfSpan2("middle mousedown if the pref is enabled and middle click paste is disabled and autoscroll are enabled"); 260 261 synthesizeMouseAtCenter(document.getElementById("span1"), { type: "mouseup", button: 1 }); 262 checkSelectAllChildrenOfSpan2("middle mouseup if the pref is enabled and middle click paste is disabled and autoscroll are enabled"); 263 264 container.setAttribute("contenteditable", "true"); 265 container.getBoundingClientRect(); 266 getSelection().selectAllChildren(span2); 267 synthesizeMouseAtCenter(document.getElementById("span1"), { type: "mousedown", button: 1 }); 268 checkSelectionCollapsedInSpan1("by middle mousedown if it's editable content"); 269 270 synthesizeMouseAtCenter(document.getElementById("span1"), { type: "mouseup", button: 1 }); 271 checkSelectionCollapsedInSpan1("after middle mouseup if it's editable content"); 272 273 container.removeAttribute("contenteditable"); 274 container.getBoundingClientRect(); 275 await SpecialPowers.pushPrefEnv({"set": [ 276 ["middlemouse.paste", false], 277 ["general.autoScroll", false], 278 ]}); 279 280 getSelection().selectAllChildren(span2); 281 synthesizeMouseAtCenter(document.getElementById("span1"), { type: "mousedown", button: 1 }); 282 checkSelectionCollapsedInSpan1("by middle mousedown if the pref is enabled but autoscroll are disabled"); 283 284 synthesizeMouseAtCenter(document.getElementById("span1"), { type: "mouseup", button: 1 }); 285 checkSelectionCollapsedInSpan1("after middle mousedown if the pref is enabled but autoscroll are disabled"); 286 287 await SpecialPowers.pushPrefEnv({"set": [ 288 ["middlemouse.paste", true], 289 ["general.autoScroll", true], 290 ]}); 291 292 getSelection().selectAllChildren(span2); 293 synthesizeMouseAtCenter(document.getElementById("span1"), { type: "mousedown", button: 1 }); 294 checkSelectionCollapsedInSpan1("by middle mousedown if the pref is enabled but middle mouse paste is enabled"); 295 296 synthesizeMouseAtCenter(document.getElementById("span1"), { type: "mouseup", button: 1 }); 297 checkSelectionCollapsedInSpan1("after middle mousedown if the pref is enabled but middle mouse paste is enabled"); 298 } 299 300 async function runAllTests() { 301 let textarea = document.getElementById("textarea"); 302 textarea.focus(); 303 await new Promise((resolve, reject) => { 304 SimpleTest.waitForClipboard(textarea.value, 305 () => { 306 synthesizeKey("a", {accelKey: true}); 307 synthesizeKey("c", {accelKey: true}); 308 }, 309 () => { 310 ok(true, `Succeeded to copy "${textarea.value}" to clipboard`); 311 textarea.style.display = "none"; 312 resolve(); 313 }, 314 () => { 315 ok(false, `Failed to copy "${textarea.value}" to clipboard`); 316 reject(); 317 }); 318 }); 319 320 await doTests(true, false, "Testing with the middle paste enabled: "); 321 await doTests(false, false, "Testing with the middle paste disabled: "); 322 323 await doTests(true, true, "Testing in editable content with the middle paste enabled: "); 324 await doTests(false, true, "Testing in editable content with the middle paste disabled: "); 325 326 await runTestPreventingToCollapseSelectionByPrefs(); 327 328 SimpleTest.finish(); 329 } 330 331 SimpleTest.waitForFocus(runAllTests); 332 </script> 333 </pre> 334 </body> 335 </html>