modifying-selection-with-non-primary-mouse-button.tentative.html (9690B)
1 <!doctype html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <meta name="variant" content="?middle"> 6 <meta name="variant" content="?secondary"> 7 <title>Testing default action of `mousedown` of middle button and `mouseup` of middle/secondary button</title> 8 <script src="/resources/testharness.js"></script> 9 <script src="/resources/testharnessreport.js"></script> 10 <script src="/resources/testdriver.js"></script> 11 <script src="/resources/testdriver-vendor.js"></script> 12 <script src="/resources/testdriver-actions.js"></script> 13 <style> 14 span { 15 white-space: nowrap; 16 } 17 </style> 18 </head> 19 <body> 20 <div contenteditable></div> 21 <script> 22 "use strict"; 23 24 const button = location.search.substr(1); 25 26 function getButtonType(actions) { 27 return button == "middle" ? actions.ButtonType.MIDDLE : actions.ButtonType.RIGHT; 28 } 29 30 var editor = document.querySelector("div[contenteditable]"); 31 var span1, span2, link; 32 var selection = getSelection(); 33 34 function preventDefault(event) { 35 event.preventDefault(); 36 } 37 editor.addEventListener("paste", preventDefault, {capture: true}); 38 document.addEventListener("contextmenu", preventDefault, {capture: true}); 39 40 function resetEditor() { 41 editor.innerHTML = 42 '<span id="span1">first span.</span><br><span id="span2">second span.</span><br><a id="link" href="#top">link.</a>'; 43 span1 = document.getElementById("span1"); 44 span2 = document.getElementById("span2"); 45 link = document.getElementById("link"); 46 } 47 48 promise_test(async () => { 49 resetEditor(); 50 editor.blur(); 51 let actions = new test_driver.Actions(); 52 await actions 53 .pointerMove(0, 0) 54 .pointerMove(0, 0, {origin: span1}) 55 .pointerDown({button: getButtonType(actions)}) 56 .pointerUp({button: getButtonType(actions)}) 57 .send(); 58 59 assert_equals(document.activeElement, editor, 60 "The clicked editor should get focus"); 61 assert_true(selection.isCollapsed, 62 `Selection should be collapsed after ${button} button click`); 63 assert_equals(selection.focusNode, span1.firstChild, 64 `Selection should be collapsed in the first <span> element which was clicked by ${button} button`); 65 }, `${button} click should set focus to clicked editable element and collapse selection around the clicked point`); 66 67 promise_test(async () => { 68 resetEditor(); 69 editor.focus(); 70 selection.collapse(span1.firstChild, 2); 71 let actions = new test_driver.Actions(); 72 await actions 73 .pointerMove(0, 0) 74 .pointerMove(0, 0, {origin: span2}) 75 .pointerDown({button: getButtonType(actions)}) 76 .pointerUp({button: getButtonType(actions)}) 77 .send(); 78 79 assert_equals(document.activeElement, editor, 80 "The clicked editor should keep having focus"); 81 assert_true(selection.isCollapsed, 82 `Selection should be collapsed after ${button} button click`); 83 assert_equals(selection.focusNode, span2.firstChild, 84 `Selection should be collapsed in the second <span> element which was clicked by ${button} button`); 85 }, `${button} click should move caret in an editable element`); 86 87 promise_test(async () => { 88 resetEditor(); 89 editor.focus(); 90 selection.collapse(span1.firstChild, 2); 91 addEventListener("mousedown", preventDefault); 92 let actions = new test_driver.Actions(); 93 await actions 94 .pointerMove(0, 0) 95 .pointerMove(0, 0, {origin: span2}) 96 .pointerDown({button: getButtonType(actions)}) 97 .pointerUp({button: getButtonType(actions)}) 98 .send(); 99 removeEventListener("mousedown", preventDefault); 100 101 assert_equals(selection.focusNode, span1.firstChild, 102 "Selection should keep collapsed selection in the first <span> element"); 103 assert_equals(selection.focusOffset, 2, 104 "Selection should keep collapsed selection at 2 of the first <span> element"); 105 }, `${button} click shouldn't move caret in an editable element if the default of mousedown event is prevented`); 106 107 promise_test(async () => { 108 resetEditor(); 109 editor.focus(); 110 selection.collapse(span1.firstChild, 2); 111 addEventListener("pointerdown", preventDefault); 112 let actions = new test_driver.Actions(); 113 await actions 114 .pointerMove(0, 0) 115 .pointerMove(0, 0, {origin: span2}) 116 .pointerDown({button: getButtonType(actions)}) 117 .pointerUp({button: getButtonType(actions)}) 118 .send(); 119 removeEventListener("pointerdown", preventDefault); 120 121 assert_equals(selection.focusNode, span1.firstChild, 122 "Selection should keep collapsed selection in the first <span> element"); 123 assert_equals(selection.focusOffset, 2, 124 "Selection should keep collapsed selection at 2 of the first <span> element"); 125 }, `${button} click shouldn't move caret in an editable element if the default of pointerdown event is prevented`); 126 127 promise_test(async () => { 128 resetEditor(); 129 editor.focus(); 130 selection.collapse(span1.firstChild, 2); 131 let contextmenuFired = false; 132 function onContextMenu() { 133 contextmenuFired = true; 134 } 135 document.addEventListener("contextmenu", onContextMenu, {capture: true}); 136 let actions = new test_driver.Actions(); 137 await actions 138 .pointerMove(0, 0) 139 .pointerMove(0, 0, {origin: span2}) 140 .keyDown("\uE008") 141 .pointerDown({button: getButtonType(actions)}) 142 .pointerUp({button: getButtonType(actions)}) 143 .keyUp("\uE008") 144 .send(); 145 document.removeEventListener("contextmenu", onContextMenu, {capture: true}); 146 147 if (button != "secondary" || contextmenuFired) { 148 assert_equals(selection.anchorNode, span1.firstChild, 149 "Selection#anchorNode should keep in the first <span> element"); 150 assert_equals(selection.anchorOffset, 2, 151 "Selection#anchorNode should keep at 2 of the first <span> element"); 152 assert_equals(selection.focusNode, span2.firstChild, 153 `Selection#focusNode should be in the second <span> element which was clicked by ${button} button`); 154 } else { 155 // Special case for Firefox. Firefox users can forcibly open context menu 156 // with pressing shift key. In this case, users may want the secondary 157 // button click to work as without Shift key press. 158 assert_true(selection.isCollapsed, 159 `Selection should be collapsed after ${button} button click because contextmenu was not opened with Shift key`); 160 assert_equals(selection.focusNode, span2.firstChild, 161 `Selection should be collapsed in the second <span> element which was clicked by ${ 162 button 163 } button because contextmenu was not opened with Shift key`); 164 } 165 }, `Shift + ${button} click should extend the selection`); 166 167 promise_test(async () => { 168 resetEditor(); 169 editor.focus(); 170 selection.collapse(span1.firstChild, 2); 171 let actions = new test_driver.Actions(); 172 await actions 173 .pointerMove(0, 0) 174 .pointerMove(0, 0, {origin: link}) 175 .keyDown("\uE008") 176 .pointerDown({button: getButtonType(actions)}) 177 .pointerUp({button: getButtonType(actions)}) 178 .keyUp("\uE008") 179 .send(); 180 181 assert_equals(selection.focusNode, link.firstChild, 182 `Selection#focusNode should be in the <a href> element which was clicked by ${button} button`); 183 assert_true(selection.isCollapsed, 184 "Selection#isCollapsed should be true"); 185 }, `Shift + ${button} click in a link shouldn't extend the selection`); 186 187 promise_test(async () => { 188 resetEditor(); 189 editor.focus(); 190 selection.collapse(span1.firstChild, 2); 191 editor.addEventListener("pointerdown", () => { 192 assert_true(selection.isCollapsed, 193 "Selection shouldn't be modified before pointerdown event"); 194 assert_equals(selection.focusNode, span1.firstChild, 195 "Selection should stay in the first <span> element when pointerdown event is fired (focusNode)"); 196 assert_equals(selection.focusOffset, 2, 197 "Selection should stay in the first <span> element when pointerdown event is fired (focusOffset)"); 198 }, {once: true}); 199 editor.addEventListener("mousedown", () => { 200 assert_true(selection.isCollapsed, 201 "Selection shouldn't be modified before mousedown event"); 202 assert_equals(selection.focusNode, span1.firstChild, 203 "Selection should stay in the first <span> element when mousedown event is fired (focusNode)"); 204 assert_equals(selection.focusOffset, 2, 205 "Selection should stay in the first <span> element when mousedown event is fired (focusOffset)"); 206 }, {once: true}); 207 editor.addEventListener("pointerup", () => { 208 assert_true(selection.isCollapsed, 209 "Selection should be collapsed before pointerup event"); 210 assert_equals(selection.focusNode, span2.firstChild, 211 `selection should be collapsed in the second <span> element which was clicked by ${button} button before pointerup event`); 212 }, {once: true}); 213 let focusOffsetAtMouseUp; 214 editor.addEventListener("mouseup", () => { 215 assert_true(selection.isCollapsed, 216 "Selection should be collapsed before mouseup event"); 217 assert_equals(selection.focusNode, span2.firstChild, 218 `Selection should be collapsed in the second <span> element which was clicked by ${button} button before mouseup event`); 219 focusOffsetAtMouseUp = selection.focusOffset; 220 }, {once: true}); 221 let actions = new test_driver.Actions(); 222 await actions 223 .pointerMove(0, 0) 224 .pointerMove(0, 0, {origin: span2}) 225 .pointerDown({button: getButtonType(actions)}) 226 .pointerMove(0, 0, {origin: span1}) 227 .pointerUp({button: getButtonType(actions)}) 228 .send(); 229 230 assert_true(selection.isCollapsed, 231 `Selection shouldn't be extended by pointer moves during pressing ${button} button`); 232 assert_equals(selection.focusNode, span2.firstChild, 233 "Selection#focusNode should stay in the second <span> element"); 234 assert_equals(selection.focusOffset, focusOffsetAtMouseUp, 235 "Selection#focusOffset should stay in the second <span> element"); 236 }, `${button} mouse button down should move caret, but its button up shouldn't move caret`); 237 238 </script> 239 </body> 240 </html>