input-events-cut-paste.html (5408B)
1 <!DOCTYPE html> 2 <meta charset="utf-8"> 3 <title>Cut and Paste should trigger corresponding InputEvent</title> 4 <script src="/resources/testharness.js"></script> 5 <script src="/resources/testharnessreport.js"></script> 6 <script src="/resources/testdriver.js"></script> 7 <script src="/resources/testdriver-actions.js"></script> 8 <script src="/resources/testdriver-vendor.js"></script> 9 <script type="text/javascript" src="pointerevent_support.js"></script> 10 <p>To manually run this test, please follow the steps below:<br/> 11 1. Select 'plain' => Cut (e.g. Ctrl/Cmd-X) => Paste (e.g. Ctrl/Cmd-V).<br/> 12 2. Select 'rich' => Cut => Paste.<br/> 13 3. Select 'prevent' => Paste.<br/> 14 4. Select 'prevent' => Cut => Select 'normal' => Paste.<br/> 15 <br/> 16 If a "PASS" result appears the test passes, otherwise it fails</p> 17 <textarea id="test1_plain">plain</textarea> 18 <p id="test2_editable" contenteditable><b>rich</b></p> 19 <p id="test3_editable_prevent" contenteditable>prevent</p> 20 <p id="test3_editable_normal" contenteditable>normal</p> 21 <script> 22 23 function resolveWhen(condition) { 24 return new Promise((resolve, reject) => { 25 function tick() { 26 if (condition()) 27 resolve(); 28 else 29 requestAnimationFrame(tick.bind(this)); 30 } 31 tick(); 32 }); 33 } 34 35 let modifier_key = "\uE009"; 36 if(navigator.platform.includes('Mac')) 37 modifier_key = "\uE03D"; 38 const commands = { 39 COPY: 'copy', 40 CUT: 'cut', 41 PASTE: 'paste', 42 } 43 function selectTextCommand(target, command) { 44 let command_key = ""; 45 if(command == "copy") 46 command_key = "c"; 47 else if (command == "cut") 48 command_key = "x"; 49 else if (command == "paste") 50 command_key = "v"; 51 return new test_driver.Actions() 52 .pointerMove(0, 0, {origin: target}) 53 .pointerDown() 54 .pointerUp() 55 .addTick() 56 .keyDown(modifier_key) 57 .keyDown("a") 58 .keyUp("a") 59 .keyDown(command_key) 60 .keyUp(command_key) 61 .keyUp(modifier_key) 62 .send(); 63 } 64 65 function selectTextCutAndPaste(target1, target2) { 66 return selectTextCommand(target1, commands.CUT).then(() => { 67 return selectTextCommand(target2, commands.PASTE); 68 }) 69 } 70 71 promise_test(async test => { 72 const expectedEventLog = [ 73 'cut-[null]', 'beforeinput-deleteByCut', 'input-deleteByCut', 74 'paste-[null]', 'beforeinput-insertFromPaste', 'input-insertFromPaste']; 75 const actualEventLog = []; 76 const text1 = document.getElementById("test1_plain"); 77 78 for (let eventType of ['beforeinput', 'input', 'cut', 'paste']) { 79 text1.addEventListener(eventType, test.step_func(function() { 80 if (event.type === 'beforeinput' && event.inputType === 'insertFromPaste') { 81 assert_equals(event.data, 'plain'); 82 assert_equals(event.dataTransfer, null); 83 } 84 85 actualEventLog.push(`${event.type}-${event.inputType || '[null]'}`); 86 })); 87 } 88 await selectTextCutAndPaste(text1, text1); 89 await resolveWhen(() => { return actualEventLog.length == expectedEventLog.length }); 90 assert_array_equals(actualEventLog, expectedEventLog, 91 `Expected: ${expectedEventLog}; Actual: ${actualEventLog}.`); 92 }, 'Event order and data on textarea.'); 93 94 promise_test(async test => { 95 const expectedEventLog = [ 96 'cut-[null]', 'beforeinput-deleteByCut', 'input-deleteByCut', 97 'paste-[null]', 'beforeinput-insertFromPaste', 'input-insertFromPaste']; 98 const actualEventLog = []; 99 const text2 = document.getElementById("test2_editable"); 100 101 for (let eventType of ['beforeinput', 'input', 'cut', 'paste']) { 102 text2.addEventListener(eventType, test.step_func(function() { 103 if (event.type === 'beforeinput' && event.inputType === 'insertFromPaste') { 104 assert_equals(event.data, null); 105 assert_equals(event.dataTransfer.getData('text/plain'), 'rich'); 106 assert_regexp_match(event.dataTransfer.getData('text/html'), /<b.*>rich<\/b>$/); 107 } 108 109 actualEventLog.push(`${event.type}-${event.inputType || '[null]'}`); 110 })); 111 } 112 await selectTextCutAndPaste(text2, text2); 113 await resolveWhen(() => { return actualEventLog.length == expectedEventLog.length }); 114 assert_array_equals(actualEventLog, expectedEventLog, 115 `Expected: ${expectedEventLog}; Actual: ${actualEventLog}.`); 116 }, 'Event order and dataTransfer on contenteditable.'); 117 118 promise_test(async test => { 119 const prevent = document.getElementById('test3_editable_prevent'); 120 const normal = document.getElementById('test3_editable_normal'); 121 prevent.addEventListener('beforeinput', test.step_func(function() { 122 if (event.inputType === 'deleteByCut' || 123 event.inputType === 'insertFromPaste') { 124 event.preventDefault(); 125 } 126 })); 127 128 normal.addEventListener('input', test.step_func(function() { 129 if (event.inputType === 'insertFromPaste') { 130 assert_equals(prevent.textContent, 'prevent'); 131 assert_equals(normal.textContent, 'prevent'); 132 } 133 })); 134 135 await selectTextCommand(prevent, commands.PASTE); 136 await selectTextCutAndPaste(prevent, normal); 137 await resolveWhen(() => { return normal.textContent == 'prevent' }); 138 assert_equals(prevent.textContent, 'prevent'); 139 assert_equals(normal.textContent, 'prevent'); 140 }, 'preventDefault() should prevent DOM modification but allow clipboard updates.'); 141 </script>