test_selection_after_right_click.html (18793B)
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8" /> 5 <title>Tests for checking the behavior of right click clicking outside selection</title> 6 <script src="/tests/SimpleTest/SimpleTest.js"></script> 7 <script src="/tests/SimpleTest/EventUtils.js"></script> 8 <link rel="stylesheet" href="/tests/SimpleTest/test.css" /> 9 </head> 10 <body> 11 <div>Non editable text</div> 12 <div contenteditable>Editable text</div> 13 <input value="input value" style="width: 100%" /> 14 <iframe srcdoc="<span>abc</span>"></iframe> 15 <script> 16 SimpleTest.waitForExplicitFinish(); 17 SimpleTest.waitForFocus(async () => { 18 function getRangeDescription(range) { 19 function getNodeDescription(node) { 20 if (!node) { 21 return "null"; 22 } 23 switch (node.nodeType) { 24 case Node.TEXT_NODE: 25 case Node.COMMENT_NODE: 26 case Node.CDATA_SECTION_NODE: 27 return `${node.nodeName} "${node.data}"`; 28 case Node.ELEMENT_NODE: 29 return `<${node.nodeName.toLowerCase()}>`; 30 default: 31 return `${node.nodeName}`; 32 } 33 } 34 if (range === null) { 35 return "null"; 36 } 37 if (range === undefined) { 38 return "undefined"; 39 } 40 return range.startContainer == range.endContainer && 41 range.startOffset == range.endOffset 42 ? `(${getNodeDescription(range.startContainer)}, ${range.startOffset})` 43 : `(${getNodeDescription(range.startContainer)}, ${ 44 range.startOffset 45 }) - (${getNodeDescription(range.endContainer)}, ${range.endOffset})`; 46 } 47 48 document.addEventListener( 49 "contextmenu", 50 event => event.preventDefault(), 51 { capture: true } 52 ); 53 const nonEditableDiv = document.querySelector("div"); 54 const editableDiv = document.querySelector("div[contenteditable]"); 55 const input = document.querySelector("input"); 56 57 // See also modifying-selection-with-non-primary-mouse-button.tentative.html?secondary for the basic behavior check. 58 await SpecialPowers.pushPrefEnv({ 59 set: [ 60 ["ui.mouse.right_click.collapse_selection.stop_if_non_collapsed_selection", false], 61 ["ui.mouse.right_click.collapse_selection.stop_if_non_editable_node", false], 62 ["ui.mouse.right_click.move_caret.stop_if_in_focused_editable_node", false], 63 ], 64 }); 65 66 getSelection().setBaseAndExtent( 67 nonEditableDiv.firstChild, "Non editable ".length, 68 nonEditableDiv.firstChild, "Non editable text".length 69 ); 70 synthesizeMouse(nonEditableDiv, 10, 10, {button: 2}); 71 ok( 72 getSelection().isCollapsed, 73 `Selection should be collapsed by a right click when stop_if_non_collapsed_selection pref is false in non-editable text node (${ 74 getRangeDescription(getSelection().getRangeAt(0)) 75 })` 76 ); 77 78 getSelection().setBaseAndExtent( 79 editableDiv.firstChild, "Editable ".length, 80 editableDiv.firstChild, "Editable text".length 81 ); 82 synthesizeMouse(editableDiv, 10, 10, {button: 2}); 83 ok( 84 getSelection().isCollapsed, 85 `Selection should be collapsed by a right click when stop_if_non_collapsed_selection pref is false in editable text node (${ 86 getRangeDescription(getSelection().getRangeAt(0)) 87 })` 88 ); 89 90 input.focus(); 91 input.setSelectionRange("input ".length, "input value".length); 92 synthesizeMouse(input, 10, 10, {button: 2}); 93 ok( 94 input.selectionStart == input.selectionEnd, 95 `Selection in <input> should be collapsed by a right click when stop_if_non_collapsed_selection pref is false (got: ${input.selectionStart} - ${input.selectionEnd})` 96 ); 97 input.blur(); 98 99 await SpecialPowers.pushPrefEnv({ 100 set: [ 101 ["ui.mouse.right_click.collapse_selection.stop_if_non_collapsed_selection", false], 102 ["ui.mouse.right_click.collapse_selection.stop_if_non_editable_node", true], 103 ["ui.mouse.right_click.move_caret.stop_if_in_focused_editable_node", false], 104 ], 105 }); 106 107 getSelection().setBaseAndExtent( 108 nonEditableDiv.firstChild, "Non editable ".length, 109 nonEditableDiv.firstChild, "Non editable text".length 110 ); 111 synthesizeMouse(nonEditableDiv, 10, 10, {button: 2}); 112 ok( 113 !getSelection().isCollapsed, 114 `Selection should not be collapsed by a right click when stop_if_non_editable_node pref is true in non-editable text node (${ 115 getRangeDescription(getSelection().getRangeAt(0)) 116 })` 117 ); 118 119 getSelection().setBaseAndExtent( 120 editableDiv.firstChild, "Editable ".length, 121 editableDiv.firstChild, "Editable text".length 122 ); 123 synthesizeMouse(editableDiv, 10, 10, {button: 2}); 124 ok( 125 getSelection().isCollapsed, 126 `Selection should be collapsed by a right click even when stop_if_non_editable_node pref is true in editable text node (${ 127 getRangeDescription(getSelection().getRangeAt(0)) 128 })` 129 ); 130 131 input.focus(); 132 input.setSelectionRange("input ".length, "input value".length); 133 synthesizeMouse(input, 10, 10, {button: 2}); 134 ok( 135 input.selectionStart == input.selectionEnd, 136 `Selection in <input> should be collapsed by a right click even when stop_if_non_editable_node pref is true (got: ${input.selectionStart} - ${input.selectionEnd})` 137 ); 138 input.blur(); 139 140 await SpecialPowers.pushPrefEnv({ 141 set: [ 142 ["ui.mouse.right_click.collapse_selection.stop_if_non_collapsed_selection", true], 143 ["ui.mouse.right_click.collapse_selection.stop_if_non_editable_node", false], 144 ["ui.mouse.right_click.move_caret.stop_if_in_focused_editable_node", false], 145 ], 146 }); 147 148 getSelection().setBaseAndExtent( 149 nonEditableDiv.firstChild, "Non editable ".length, 150 nonEditableDiv.firstChild, "Non editable text".length 151 ); 152 synthesizeMouse(nonEditableDiv, 10, 10, {button: 2}); 153 ok( 154 !getSelection().isCollapsed, 155 `Selection should not be collapsed by a right click when stop_if_non_collapsed_selection pref is true in non-editable text node (${ 156 getRangeDescription(getSelection().getRangeAt(0)) 157 })` 158 ); 159 160 getSelection().setBaseAndExtent( 161 editableDiv.firstChild, "Editable ".length, 162 editableDiv.firstChild, "Editable text".length 163 ); 164 synthesizeMouse(editableDiv, 10, 10, {button: 2}); 165 ok( 166 !getSelection().isCollapsed, 167 `Selection should be collapsed by a right click when stop_if_non_collapsed_selection pref is true in editable text node (${ 168 getRangeDescription(getSelection().getRangeAt(0)) 169 })` 170 ); 171 172 input.focus(); 173 input.setSelectionRange("input ".length, "input value".length); 174 synthesizeMouse(input, 10, 10, {button: 2}); 175 ok( 176 input.selectionStart != input.selectionEnd, 177 `Selection in <input> should not be collapsed by a right click when stop_if_non_collapsed_selection pref is true (got: ${input.selectionStart} - ${input.selectionEnd})` 178 ); 179 input.blur(); 180 181 // When the right click changes the selection ancestor limit, it should be 182 // handled correctly after nsIFrame does nothing. 183 getSelection().setBaseAndExtent( 184 nonEditableDiv.firstChild, "Non editable ".length, 185 nonEditableDiv.firstChild, "Non editable text".length 186 ); 187 synthesizeMouse(editableDiv, 10, 10, {button: 2}); 188 is( 189 document.activeElement, 190 editableDiv, 191 "Right clicking in editable <div> when selection selects some text outside the <div> should move focus into the editor" 192 ); 193 ok( 194 getSelection().focusNode == editableDiv.firstChild && 195 getSelection().focusOffset > 0, 196 `Right clicking in editable <div> when selection selects some text outside the <div> should not cause collapsing selection to start of the editor (${ 197 getRangeDescription(getSelection().getRangeAt(0)) 198 })` 199 ); 200 201 getSelection().setBaseAndExtent( 202 editableDiv.firstChild, "Editable ".length, 203 editableDiv.firstChild, "Editable text".length 204 ); 205 synthesizeMouse(nonEditableDiv, 10, 10, {button: 2}); 206 isnot( 207 document.activeElement, 208 editableDiv, 209 "Right clicking outside the editable <div> should blur from it" 210 ); 211 ok( 212 getSelection().focusNode == nonEditableDiv.firstChild && 213 getSelection().focusOffset > 0, 214 `Right clicking outside the editable <div> should collapse selection at the clicked content (${ 215 getRangeDescription(getSelection().getRangeAt(0)) 216 })` 217 ); 218 219 // If clicking in a selection range, the range should never be collapsed. 220 const iframe = document.querySelector("iframe"); 221 const doc = iframe.contentDocument; 222 doc.addEventListener("contextmenu", event => event.preventDefault(), {capture: true}); 223 224 await SpecialPowers.pushPrefEnv({ 225 set: [ 226 ["ui.mouse.right_click.collapse_selection.stop_if_non_collapsed_selection", false], 227 ["ui.mouse.right_click.collapse_selection.stop_if_non_editable_node", false], 228 ["ui.mouse.right_click.move_caret.stop_if_in_focused_editable_node", false], 229 ], 230 }); 231 iframe.focus(); 232 doc.getSelection().selectAllChildren(doc.body); 233 synthesizeMouseAtCenter(doc.body.firstChild, {button: 2}, iframe.contentWindow); 234 ok( 235 !doc.getSelection().isCollapsed, 236 "Right click in a selection range should not cause collapsing selection" 237 ); 238 iframe.blur(); 239 doc.activeElement?.blur(); 240 241 doc.documentElement.setAttribute("contenteditable", ""); 242 iframe.focus(); 243 doc.getSelection().selectAllChildren(doc.body); 244 synthesizeMouseAtCenter(doc.body.firstChild, {button: 2}, iframe.contentWindow); 245 ok( 246 !doc.getSelection().isCollapsed, 247 "Right click in a selection range in a editable-root-element should not cause collapsing selection" 248 ); 249 iframe.blur(); 250 doc.activeElement?.blur(); 251 252 await SpecialPowers.pushPrefEnv({ 253 set: [ 254 ["ui.mouse.right_click.collapse_selection.stop_if_non_collapsed_selection", true], 255 ["ui.mouse.right_click.collapse_selection.stop_if_non_editable_node", false], 256 ["ui.mouse.right_click.move_caret.stop_if_in_focused_editable_node", false], 257 ], 258 }); 259 doc.designMode = "on"; 260 iframe.focus(); 261 doc.getSelection().selectAllChildren(doc.body); 262 synthesizeMouseAtCenter(doc.body.firstChild, {button: 2}, iframe.contentWindow); 263 ok( 264 !doc.getSelection().isCollapsed, 265 "Right click in a selection range in a sub-editable-document should not cause collapsing selection" 266 ); 267 doc.designMode = "off"; 268 269 // If selection is collapsed and `ui.mouse.right_click.move_caret.stop_if_in_focused_editable_node` is 270 // false, the caret should be moved at clicked point. 271 await SpecialPowers.pushPrefEnv({ 272 set: [ 273 ["ui.mouse.right_click.collapse_selection.stop_if_non_collapsed_selection", true], 274 ["ui.mouse.right_click.collapse_selection.stop_if_non_editable_node", false], 275 ["ui.mouse.right_click.move_caret.stop_if_in_focused_editable_node", false], 276 ], 277 }); 278 editableDiv.innerHTML = "<b>Bold text</b><i>Italic text</i>"; 279 editableDiv.focus(); 280 getSelection().collapse( 281 editableDiv.querySelector("b").firstChild, 282 "Bold".length 283 ); 284 synthesizeMouseAtCenter(editableDiv.querySelector("i"), {button: 2}); 285 ok( 286 getSelection().isCollapsed, 287 "Right clicking in editable <div> should keep Selection collapsed if the pref is unset" 288 ); 289 is( 290 getSelection().focusNode.parentNode, 291 editableDiv.querySelector("i"), 292 `Right clicking in editable <div> should collapse Selection at the clicked point (${ 293 getRangeDescription(getSelection().getRangeAt(0)) 294 }) if the pref is unset` 295 ); 296 297 input.focus(); 298 input.setSelectionRange(0, 0); 299 synthesizeMouseAtCenter(input, {button: 2}); 300 ok( 301 input.selectionStart == input.selectionEnd, 302 "Right click in <input> should keep Selection collapsed if the pref is unset" 303 ); 304 ok( 305 input.selectionStart > 0, 306 "Right click in <input> should move caret if the pref is unset" 307 ); 308 input.blur(); 309 310 // If selection is collapsed and `ui.mouse.right_click.move_caret.stop_if_in_focused_editable_node` is 311 // true, the caret should not be moved at clicked point unless focus is changed by the click. 312 await SpecialPowers.pushPrefEnv({ 313 set: [ 314 ["ui.mouse.right_click.collapse_selection.stop_if_non_collapsed_selection", true], 315 ["ui.mouse.right_click.collapse_selection.stop_if_non_editable_node", false], 316 ["ui.mouse.right_click.move_caret.stop_if_in_focused_editable_node", true], 317 ], 318 }); 319 editableDiv.innerHTML = "<b>Bold text</b><i>Italic text</i>"; 320 editableDiv.focus(); 321 getSelection().collapse( 322 editableDiv.querySelector("b").firstChild, 323 "Bold".length 324 ); 325 synthesizeMouseAtCenter(editableDiv.querySelector("i"), {button: 2}); 326 ok( 327 getSelection().isCollapsed, 328 "Right clicking in editable <div> should keep Selection collapsed even if the pref is set" 329 ); 330 is( 331 getSelection().focusNode.parentNode, 332 editableDiv.querySelector("b"), 333 `Right clicking in editable <div> should not move caret to the clicked point (${ 334 getRangeDescription(getSelection().getRangeAt(0)) 335 }) if the pref is set` 336 ); 337 338 input.focus(); 339 input.setSelectionRange(0, 0); 340 synthesizeMouseAtCenter(input, {button: 2}); 341 ok( 342 input.selectionStart == input.selectionEnd, 343 "Right click in <input> should keep Selection collapsed even if the pref is set" 344 ); 345 is( 346 input.selectionStart, 347 0, 348 "Right click in <input> should not move caret if the pref is set" 349 ); 350 input.blur(); 351 352 input.setSelectionRange(0, 0); 353 editableDiv.focus(); 354 synthesizeMouseAtCenter(input, {button: 2}); 355 ok( 356 input.selectionStart == input.selectionEnd, 357 "Right click in <input> which is not focused should keep Selection collapsed" 358 ); 359 ok( 360 input.selectionStart > 0, 361 "Right click in <input> which is not focused should move caret" 362 ); 363 364 editableDiv.focus(); 365 getSelection().collapse( 366 editableDiv.querySelector("b").firstChild, 367 "Bold".length 368 ); 369 input.focus(); 370 synthesizeMouseAtCenter(editableDiv.querySelector("i"), {button: 2}); 371 ok( 372 getSelection().isCollapsed, 373 "Right clicking in editable <div> which is not focused should collapse selection" 374 ); 375 is( 376 getSelection().focusNode.parentNode, 377 editableDiv.querySelector("i"), 378 `Right clicking in editable <div> which is not focused should collapse Selection at the clicked point (${ 379 getRangeDescription(getSelection().getRangeAt(0)) 380 })` 381 ); 382 383 // If Shift + right click should forcibly open context menu, users may want the click to work as 384 // same as without Shift. 385 await SpecialPowers.pushPrefEnv({ 386 set: [ 387 ["ui.mouse.right_click.collapse_selection.stop_if_non_collapsed_selection", true], 388 ["ui.mouse.right_click.collapse_selection.stop_if_non_editable_node", false], 389 ["ui.mouse.right_click.move_caret.stop_if_in_focused_editable_node", false], 390 ["dom.event.contextmenu.shift_suppresses_event", true], 391 ], 392 }); 393 nonEditableDiv.innerHTML = "<b>bold</b> <i>italic</i>"; 394 getSelection().collapse(nonEditableDiv.querySelector("b").firstChild, 0); 395 synthesizeMouseAtCenter(nonEditableDiv.querySelector("i"), {shiftKey: true, button: 2}); 396 ok( 397 getSelection().isCollapsed, 398 `Selection should be collapsed by a Shift + right click on non-editable node when it does not open context menu (${ 399 getRangeDescription(getSelection().getRangeAt(0)) 400 })` 401 ); 402 is( 403 getSelection().focusNode, 404 nonEditableDiv.querySelector("i").firstChild, 405 `Selection should be collapsed at the click point by a Shift + right click on non-editable node when it does not open context menu (${ 406 getRangeDescription(getSelection().getRangeAt(0)) 407 })` 408 ); 409 410 editableDiv.innerHTML = "<b>bold</b> <i>italic</i>"; 411 getSelection().collapse(editableDiv.querySelector("b").firstChild, 0); 412 synthesizeMouseAtCenter(editableDiv.querySelector("i"), {shiftKey: true, button: 2}); 413 ok( 414 getSelection().isCollapsed, 415 `Selection should be collapsed by a Shift + right click on editable node when it does not open context menu (${ 416 getRangeDescription(getSelection().getRangeAt(0)) 417 })` 418 ); 419 is( 420 getSelection().focusNode, 421 editableDiv.querySelector("i").firstChild, 422 `Selection should be collapsed at the click point by a Shift + right click on editable node when it does not open context menu (${ 423 getRangeDescription(getSelection().getRangeAt(0)) 424 })` 425 ); 426 427 input.focus(); 428 input.setSelectionRange(0, 0); 429 synthesizeMouseAtCenter(input, {shiftKey: true, button: 2}); 430 ok( 431 input.selectionStart == input.selectionEnd, 432 `Selection in <input> should be collapsed by a Shift + right click when it does not open context menu (got: ${ 433 input.selectionStart 434 } - ${input.selectionEnd})` 435 ); 436 isnot( 437 input.selectionStart, 438 0, 439 `Selection in <input> should be collapsed at the click point by a Shift + right click when it does not open context menu (got: ${ 440 input.selectionStart 441 } - ${input.selectionEnd})` 442 ); 443 input.blur(); 444 445 // If Shift + right click should open context menu, users may want the click to work as 446 // same as a left click. 447 await SpecialPowers.pushPrefEnv({ 448 set: [ 449 ["ui.mouse.right_click.collapse_selection.stop_if_non_collapsed_selection", true], 450 ["ui.mouse.right_click.collapse_selection.stop_if_non_editable_node", false], 451 ["ui.mouse.right_click.move_caret.stop_if_in_focused_editable_node", false], 452 ["dom.event.contextmenu.shift_suppresses_event", false], 453 ], 454 }); 455 nonEditableDiv.innerHTML = "<b>bold</b> <i>italic</i>"; 456 getSelection().collapse(nonEditableDiv.querySelector("b").firstChild, 0); 457 synthesizeMouseAtCenter(nonEditableDiv.querySelector("i"), {shiftKey: true, button: 2}); 458 is( 459 getRangeDescription(getSelection().getRangeAt(0)), 460 getRangeDescription({ 461 startContainer: nonEditableDiv.querySelector("b").firstChild, 462 startOffset: 0, 463 endContainer: nonEditableDiv.querySelector("i").firstChild, 464 endOffset: getSelection().focusOffset, 465 }), 466 `Selection should be extended by a Shift + right click on non-editable node when it should open context menu` 467 ); 468 469 editableDiv.innerHTML = "<b>bold</b> <i>italic</i>"; 470 getSelection().collapse(editableDiv.querySelector("b").firstChild, 0); 471 synthesizeMouseAtCenter(editableDiv.querySelector("i"), {shiftKey: true, button: 2}); 472 is( 473 getRangeDescription(getSelection().getRangeAt(0)), 474 getRangeDescription({ 475 startContainer: editableDiv.querySelector("b").firstChild, 476 startOffset: 0, 477 endContainer: editableDiv.querySelector("i").firstChild, 478 endOffset: getSelection().focusOffset, 479 }), 480 `Selection should be extended by a Shift + right click on editable node when it should open context menu` 481 ); 482 483 input.focus(); 484 input.setSelectionRange(0, 0); 485 synthesizeMouseAtCenter(input, {shiftKey: true, button: 2}); 486 isnot( 487 input.selectionStart, 488 input.selectionEnd, 489 `Selection in <input> should be extended by a Shift + right click when it should open context menu (got: ${ 490 input.selectionStart 491 } - ${input.selectionEnd})` 492 ); 493 input.blur(); 494 495 SimpleTest.finish(); 496 }); 497 </script> 498 </body> 499 </html>