input-events-delete-selection.html (8159B)
1 <!DOCTYPE html> 2 <meta charset="utf-8" /> 3 <title> 4 Input Event tests for deletion with non-collapsed selection 5 </title> 6 <script src="/resources/testharness.js"></script> 7 <script src="/resources/testharnessreport.js"></script> 8 <script src="/resources/testdriver.js"></script> 9 <script src="/resources/testdriver-vendor.js"></script> 10 <script src="/resources/testdriver-actions.js"></script> 11 <div id="rich" contenteditable></div> 12 <script> 13 let inputEventsLog = []; 14 const rich = document.getElementById("rich"); 15 const isMacOS = navigator.platform.indexOf("Mac") === 0; 16 const MODIFIER_KEY = isMacOS ? "\uE00A" : "\uE009"; // Alt on Mac, Ctrl on others 17 18 function log(event) { 19 inputEventsLog.push({ 20 type: event.type, 21 inputType: event.inputType, 22 data: event.data, 23 }); 24 } 25 26 function resetRich() { 27 inputEventsLog = []; 28 rich.innerHTML = ""; 29 } 30 31 function selectText(startNode, startOffset, endNode, endOffset) { 32 const selection = window.getSelection(); 33 const range = document.createRange(); 34 range.setStart(startNode, startOffset); 35 range.setEnd(endNode, endOffset); 36 selection.removeAllRanges(); 37 selection.addRange(range); 38 } 39 40 rich.addEventListener("beforeinput", log); 41 rich.addEventListener("input", log); 42 43 // Test Ctrl+Backspace with non-collapsed selection 44 promise_test(async function () { 45 this.add_cleanup(resetRich); 46 rich.innerHTML = "hello world"; 47 rich.focus(); 48 49 const textNode = rich.firstChild; 50 // Select "or" in "world" 51 selectText(textNode, 6, textNode, 8); 52 53 // Simulate Ctrl+Backspace (Alt+Backspace on Mac) 54 await new test_driver.Actions() 55 .keyDown(MODIFIER_KEY) 56 .keyDown("\uE003") // Backspace 57 .keyUp("\uE003") 58 .keyUp(MODIFIER_KEY) 59 .send(); 60 61 assert_equals(inputEventsLog.length, 2, "Should have 2 events"); 62 const [beforeInputEvent, inputEvent] = inputEventsLog; 63 assert_equals(beforeInputEvent.type, "beforeinput"); 64 assert_equals(inputEvent.type, "input"); 65 // When deleting a selection, inputType should be deleteContentBackward 66 assert_equals( 67 beforeInputEvent.inputType, 68 "deleteContentBackward", 69 "Ctrl+Backspace with selection should report deleteContentBackward" 70 ); 71 assert_equals( 72 inputEvent.inputType, 73 "deleteContentBackward", 74 "Ctrl+Backspace with selection should report deleteContentBackward" 75 ); 76 }, "Ctrl+Backspace with non-collapsed selection should report deleteContentBackward"); 77 78 // Test Ctrl+Backspace with caret (collapsed selection) 79 promise_test(async function () { 80 this.add_cleanup(resetRich); 81 rich.innerHTML = "hello world"; 82 rich.focus(); 83 84 const textNode = rich.firstChild; 85 // Place caret after "world" 86 selectText(textNode, 11, textNode, 11); 87 88 // Simulate Ctrl+Backspace (Alt+Backspace on Mac) 89 await new test_driver.Actions() 90 .keyDown(MODIFIER_KEY) 91 .keyDown("\uE003") // Backspace 92 .keyUp("\uE003") 93 .keyUp(MODIFIER_KEY) 94 .send(); 95 96 assert_equals(inputEventsLog.length, 2, "Should have 2 events"); 97 const [beforeInputEvent, inputEvent] = inputEventsLog; 98 assert_equals(beforeInputEvent.type, "beforeinput"); 99 assert_equals(inputEvent.type, "input"); 100 // With caret, should be deleteWordBackward 101 assert_equals( 102 beforeInputEvent.inputType, 103 "deleteWordBackward", 104 "Ctrl+Backspace with caret should report deleteWordBackward" 105 ); 106 assert_equals( 107 inputEvent.inputType, 108 "deleteWordBackward", 109 "Ctrl+Backspace with caret should report deleteWordBackward" 110 ); 111 }, "Ctrl+Backspace with caret should report deleteWordBackward"); 112 113 // Test Ctrl+Delete with non-collapsed selection 114 promise_test(async function () { 115 this.add_cleanup(resetRich); 116 rich.innerHTML = "hello world"; 117 rich.focus(); 118 119 const textNode = rich.firstChild; 120 // Select "ell" in "hello" 121 selectText(textNode, 1, textNode, 4); 122 123 // Simulate Ctrl+Delete (Alt+Delete on Mac) 124 await new test_driver.Actions() 125 .keyDown(MODIFIER_KEY) 126 .keyDown("\uE017") // Delete 127 .keyUp("\uE017") 128 .keyUp(MODIFIER_KEY) 129 .send(); 130 131 assert_equals(inputEventsLog.length, 2, "Should have 2 events"); 132 const [beforeInputEvent, inputEvent] = inputEventsLog; 133 assert_equals(beforeInputEvent.type, "beforeinput"); 134 assert_equals(inputEvent.type, "input"); 135 // When deleting a selection, inputType should be deleteContentForward 136 assert_equals( 137 beforeInputEvent.inputType, 138 "deleteContentForward", 139 "Ctrl+Delete with selection should report deleteContentForward" 140 ); 141 assert_equals( 142 inputEvent.inputType, 143 "deleteContentForward", 144 "Ctrl+Delete with selection should report deleteContentForward" 145 ); 146 }, "Ctrl+Delete with non-collapsed selection should report deleteContentForward"); 147 148 // Test Ctrl+Delete with caret (collapsed selection) 149 promise_test(async function () { 150 this.add_cleanup(resetRich); 151 rich.innerHTML = "hello world"; 152 rich.focus(); 153 154 const textNode = rich.firstChild; 155 // Place caret before "world" 156 selectText(textNode, 6, textNode, 6); 157 158 // Simulate Ctrl+Delete (Alt+Delete on Mac) 159 await new test_driver.Actions() 160 .keyDown(MODIFIER_KEY) 161 .keyDown("\uE017") // Delete 162 .keyUp("\uE017") 163 .keyUp(MODIFIER_KEY) 164 .send(); 165 166 assert_equals(inputEventsLog.length, 2, "Should have 2 events"); 167 const [beforeInputEvent, inputEvent] = inputEventsLog; 168 assert_equals(beforeInputEvent.type, "beforeinput"); 169 assert_equals(inputEvent.type, "input"); 170 // With caret, should be deleteWordForward 171 assert_equals( 172 beforeInputEvent.inputType, 173 "deleteWordForward", 174 "Ctrl+Delete with caret should report deleteWordForward" 175 ); 176 assert_equals( 177 inputEvent.inputType, 178 "deleteWordForward", 179 "Ctrl+Delete with caret should report deleteWordForward" 180 ); 181 }, "Ctrl+Delete with caret should report deleteWordForward"); 182 183 // Test regular Backspace with non-collapsed selection (should still be deleteContentBackward) 184 promise_test(async function () { 185 this.add_cleanup(resetRich); 186 rich.innerHTML = "hello world"; 187 rich.focus(); 188 189 const textNode = rich.firstChild; 190 // Select "lo wo" 191 selectText(textNode, 3, textNode, 8); 192 193 // Simulate Backspace (without Ctrl) 194 await test_driver.send_keys(rich, "\uE003"); // Backspace 195 196 assert_equals(inputEventsLog.length, 2, "Should have 2 events"); 197 const [beforeInputEvent, inputEvent] = inputEventsLog; 198 assert_equals(beforeInputEvent.type, "beforeinput"); 199 assert_equals(inputEvent.type, "input"); 200 assert_equals( 201 beforeInputEvent.inputType, 202 "deleteContentBackward", 203 "Regular Backspace with selection should report deleteContentBackward" 204 ); 205 assert_equals( 206 inputEvent.inputType, 207 "deleteContentBackward", 208 "Regular Backspace with selection should report deleteContentBackward" 209 ); 210 }, "Regular Backspace with non-collapsed selection should report deleteContentBackward"); 211 212 // Test regular Delete with non-collapsed selection (should be deleteContentForward) 213 promise_test(async function () { 214 this.add_cleanup(resetRich); 215 rich.innerHTML = "hello world"; 216 rich.focus(); 217 218 const textNode = rich.firstChild; 219 // Select "lo wo" 220 selectText(textNode, 3, textNode, 8); 221 222 // Simulate Delete (without Ctrl) 223 await test_driver.send_keys(rich, "\uE017"); // Delete 224 225 assert_equals(inputEventsLog.length, 2, "Should have 2 events"); 226 const [beforeInputEvent, inputEvent] = inputEventsLog; 227 assert_equals(beforeInputEvent.type, "beforeinput"); 228 assert_equals(inputEvent.type, "input"); 229 assert_equals( 230 beforeInputEvent.inputType, 231 "deleteContentForward", 232 "Regular Delete with selection should report deleteContentForward" 233 ); 234 assert_equals( 235 inputEvent.inputType, 236 "deleteContentForward", 237 "Regular Delete with selection should report deleteContentForward" 238 ); 239 }, "Regular Delete with non-collapsed selection should report deleteContentForward"); 240 </script>