Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(35)

Unified Diff: third_party/WebKit/LayoutTests/fast/events/inputevents/inputevent-drag-drop.html

Issue 2374743002: [InputEvent] Support |deleteByDrag|, |insertFromDrop| and fire in sequential order (Closed)
Patch Set: Yosin's review 2 Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/LayoutTests/fast/events/inputevents/inputevent-drag-drop.html
diff --git a/third_party/WebKit/LayoutTests/fast/events/inputevents/inputevent-drag-drop.html b/third_party/WebKit/LayoutTests/fast/events/inputevents/inputevent-drag-drop.html
new file mode 100644
index 0000000000000000000000000000000000000000..56eb083951fadec452d1c82307c33375eced044b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/events/inputevents/inputevent-drag-drop.html
@@ -0,0 +1,293 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>InputEvent: beforeinput for Drag and Drop</title>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<style>
+div, img {
+ width: 100px;
+ height: 100px;
+}
+</style>
+</head>
+<body>
+<div id="editable1" contenteditable><img id="img" src="../resources/greenbox.png"></div>
+<div id="editable2" contenteditable></div>
+<textarea id="textarea1">Text</textarea>
+<textarea id="barrier"></textarea>
+<script>
+function simulateDragDrop(dragElement, dropElement) {
+ eventSender.mouseMoveTo(dragElement.offsetLeft + dragElement.offsetWidth / 2,
+ dragElement.offsetTop + dragElement.offsetHeight / 2);
+ eventSender.mouseDown();
+ eventSender.leapForward(600);
+ eventSender.mouseMoveTo(dropElement.offsetLeft + dropElement.offsetWidth / 2,
+ dropElement.offsetTop + dropElement.offsetHeight / 2);
+ eventSender.mouseUp();
+}
+
+function assertCleanInitialDOM(logInfo) {
+ const editable1 = document.getElementById('editable1');
+ const editable2 = document.getElementById('editable2');
+ const textarea1 = document.getElementById('textarea1');
+ assert_equals(editable1.children.length, 1, `${logInfo}, DOM is dirty`);
+ assert_equals(editable1.children[0].tagName, 'IMG', `${logInfo}, DOM is dirty`);
+ assert_equals(editable2.children.length, 0, `${logInfo}, DOM is dirty`);
+ assert_equals(textarea1.value, 'Text', `${logInfo}, DOM is dirty`);
+}
+
+test(function() {
+ assertCleanInitialDOM();
+ assert_not_equals(window.eventSender, undefined, 'This test requires eventSender.');
+ assert_not_equals(window.testRunner, undefined, 'This test requires testRunner.');
+
+ const editable1 = document.getElementById('editable1');
+ const editable2 = document.getElementById('editable2');
+ const textarea1 = document.getElementById('textarea1');
+
+ function preventDeleteByDragListener(event) {
+ if (event.inputType == 'deleteByDrag')
+ event.preventDefault();
+ }
+
+ function preventInsertFromDropListener(event) {
+ if (event.inputType == 'insertFromDrop')
+ event.preventDefault();
+ }
+
+ const undoBarrier = document.getElementById('barrier');
+ undoBarrier.focus();
+ document.execCommand('insertText', false, 'abc');
+ function assertBarrierUnchanged() {
+ assert_equals(undoBarrier.value, 'abc');
+ }
+
+ // Normally Drag&Drop requires a single Undo.
+ simulateDragDrop(editable1, editable2);
+ testRunner.execCommand('undo');
+ assertCleanInitialDOM('Normal Drag&Drop');
+ assertBarrierUnchanged();
+
+ // Canceling |DeleteByDrag|, still require a single Undo.
+ editable1.addEventListener('beforeinput', preventDeleteByDragListener);
+ simulateDragDrop(editable1, editable2);
+ testRunner.execCommand('undo');
+ assertCleanInitialDOM('Canceling |DeleteByDrag|');
+ editable1.removeEventListener('beforeinput', preventDeleteByDragListener);
+ assertBarrierUnchanged();
+
+ // Canceling |InsertFromDrop|, still require a single Undo.
+ editable2.addEventListener('beforeinput', preventInsertFromDropListener);
+ simulateDragDrop(editable1, editable2);
+ testRunner.execCommand('undo');
+ assertCleanInitialDOM('Canceling |InsertFromDrop|');
+ editable2.removeEventListener('beforeinput', preventInsertFromDropListener);
+ assertBarrierUnchanged();
+
+ // Canceling both, shouldn't create undo entry.
+ editable1.addEventListener('beforeinput', preventDeleteByDragListener);
+ editable2.addEventListener('beforeinput', preventInsertFromDropListener);
+ simulateDragDrop(editable1, editable2);
+ assertCleanInitialDOM('Canceling both');
+ testRunner.execCommand('undo');
+ assert_equals(undoBarrier.value, '');
+ testRunner.execCommand('redo');
+ assertBarrierUnchanged();
+ editable1.removeEventListener('beforeinput', preventDeleteByDragListener);
+ editable2.removeEventListener('beforeinput', preventInsertFromDropListener);
+
+ // Two Drag&Drop, cancel first |InsertFromDrop| and second |DeleteByDrag|, should still create 2 undo entries.
+ editable2.addEventListener('beforeinput', preventInsertFromDropListener);
+ simulateDragDrop(editable1, editable2);
+ editable2.removeEventListener('beforeinput', preventInsertFromDropListener);
+ textarea1.addEventListener('beforeinput', preventDeleteByDragListener);
+ textarea1.select();
+ simulateDragDrop(textarea1, editable2);
+ textarea1.removeEventListener('beforeinput', preventDeleteByDragListener);
+ assert_equals(editable1.children.length, 0);
+ assert_equals(editable2.innerHTML, 'Text');
+ assert_equals(textarea1.value, 'Text');
+ // First undo.
+ testRunner.execCommand('undo');
+ assert_equals(editable1.children.length, 0);
+ assert_equals(editable2.innerHTML, '');
+ assert_equals(textarea1.value, 'Text');
+ // Second undo.
+ testRunner.execCommand('undo');
+ assert_equals(editable1.children.length, 1);
+ assert_equals(editable2.innerHTML, '');
+ assert_equals(textarea1.value, 'Text');
+ // More undo should reach to |undoBarrier|.
+ assertBarrierUnchanged();
+ testRunner.execCommand('undo');
+ assert_equals(undoBarrier.value, '');
+ testRunner.execCommand('redo');
+}, 'Testing Drag and Drop, preventDefault() and Undo entry');
+
+test(function() {
+ assertCleanInitialDOM();
+ assert_not_equals(window.eventSender, undefined, 'This test requires eventSender.');
+ assert_not_equals(window.testRunner, undefined, 'This test requires testRunner.');
+
+ const editable1 = document.getElementById('editable1');
+ const editable2 = document.getElementById('editable2');
+ var eventOrderRecorder = [];
+ document.addEventListener('beforeinput', event =>
+ eventOrderRecorder.push(`beforeinput:${event.target.id}:${event.inputType}`));
+ document.addEventListener('input', event =>
+ eventOrderRecorder.push(`input:${event.target.id}:${event.inputType}`));
+ ['drop', 'dragend'].forEach(eventType => document.addEventListener(
+ eventType, () => eventOrderRecorder.push(`${event.target.id}:${eventType}`)));
+
+ function testDragDropEventOrder(dragElement, dropElement, expectedOrder) {
+ assert_equals(dragElement.children.length, 1);
+ eventOrderRecorder = [];
+ simulateDragDrop(dragElement, dropElement);
+ assert_array_equals(eventOrderRecorder, expectedOrder,
+ `Testing drag ${dragElement.id} onto ${dropElement.id} actual order: ${eventOrderRecorder}`);
+ }
+
+ // Test Drag and Drop.
+ testDragDropEventOrder(editable1, editable2,
+ ['editable2:drop', 'beforeinput:img:deleteByDrag', 'input:editable1:deleteByDrag',
+ 'beforeinput:editable2:insertFromDrop', 'input:editable2:insertFromDrop', 'editable1:dragend']);
+ testRunner.execCommand('undo');
+}, 'Testing Drag and Drop event order');
+
+test(function() {
+ assertCleanInitialDOM();
+ assert_not_equals(window.eventSender, undefined, 'This test requires eventSender.');
+ assert_not_equals(window.testRunner, undefined, 'This test requires testRunner.');
+
+ const editable1 = document.getElementById('editable1');
+ const editable2 = document.getElementById('editable2');
+ var lastPlainTextData = {};
+ var lastHTMLData = {};
+ document.addEventListener('beforeinput', event => {
+ lastPlainTextData[event.inputType] = event.dataTransfer ? event.dataTransfer.getData('text/plain') : null;
+ lastHTMLData[event.inputType] = event.dataTransfer ? event.dataTransfer.getData('text/html') : null;
+ });
+
+ function testDragDropDataTransfer(inputType, dragElement, dropElement, expectedPlainText, expectedHTML) {
+ assert_equals(dragElement.children.length, 1);
+ lastPlainTextData = {};
+ lastHTMLData = {};
+ simulateDragDrop(dragElement, dropElement);
+ assert_equals(lastPlainTextData[inputType], expectedPlainText,
+ `Testing '${inputType}' getData('text/plain')`);
+ if (expectedHTML && expectedHTML.test) {
+ assert_regexp_match(lastHTMLData[inputType], expectedHTML,
+ `Testing '${inputType}' getData('text/html')`);
+ } else {
+ assert_equals(lastHTMLData[inputType], expectedHTML,
+ `Testing '${inputType}' getData('text/html')`);
+ }
+ }
+
+ // Test Drag and Drop.
+ testDragDropDataTransfer('deleteByDrag', editable1, editable2, null, null);
+ testRunner.execCommand('undo');
+ testDragDropDataTransfer('insertFromDrop', editable1, editable2, '', /^<img.*greenbox\.png".*>$/);
+ testRunner.execCommand('undo');
+}, 'Testing Drag and Drop dataTransfer');
+
+test(function() {
+ assertCleanInitialDOM();
+ assert_not_equals(window.eventSender, undefined, 'This test requires eventSender.');
+ assert_not_equals(window.testRunner, undefined, 'This test requires testRunner.');
+
+ const editable1 = document.getElementById('editable1');
+ const editable2 = document.getElementById('editable2');
+ var inputTypesToPrevent = [];
+ document.addEventListener('beforeinput', event => {
+ if (inputTypesToPrevent.indexOf(event.inputType) != -1)
+ event.preventDefault();
+ });
+
+ function testDragDropPreventDefault(preventDefaultTypes, dragElement, dropElement, expectedDragElementChildren, expectedDropElementChildren) {
+ assert_equals(dragElement.children.length, 1);
+ inputTypesToPrevent = preventDefaultTypes;
+ simulateDragDrop(dragElement, dropElement);
+ assert_equals(dragElement.children.length, expectedDragElementChildren,
+ 'Testing preventDefault() on ${preventDefaultTypes} ${dragElement.id} children count');
+ assert_equals(dropElement.children.length, expectedDropElementChildren,
+ 'Testing preventDefault() on ${preventDefaultTypes} ${dropElement.id} children count');
+ inputTypesToPrevent = [];
+ }
+
+ // Preventing single 'beforeinput' will only cancel DOM update for one event,
+ // the remaining DOM update will still update undo stack.
+ testDragDropPreventDefault(['deleteByDrag'], editable1, editable2, 1, 1);
+ testRunner.execCommand('undo');
+ testDragDropPreventDefault(['insertFromDrop'], editable1, editable2, 0, 0);
+ testRunner.execCommand('undo');
+
+ // Adding 'insertHTML' command to undo stack.
+ editable2.focus();
+ document.execCommand('insertHTML', false, '<b>B</b><i>i</i>');
+ assert_equals(editable2.children.length, 2,
+ '"editable2" should have 2 children after "insertHTML" command');
+ // Canceling both |deleteByDrag| and |insertFromDrop| won't modify undo stack.
+ testDragDropPreventDefault(['deleteByDrag', 'insertFromDrop'], editable1, editable2, 1, 2);
+ // |undo| will undo last 'insertHTML' command.
+ testRunner.execCommand('undo');
+ assert_equals(editable2.children.length, 0,
+ '"editable2" should have 0 children after undo "insertHTML"');
+}, 'Testing Drag and Drop preventDefault()');
+
+test(function() {
+ assertCleanInitialDOM();
+ assert_not_equals(window.eventSender, undefined, 'This test requires eventSender.');
+ assert_not_equals(window.testRunner, undefined, 'This test requires testRunner.');
+
+ const editable1 = document.getElementById('editable1');
+ const editable2 = document.getElementById('editable2');
+ var eventOrderRecorder = [];
+ [editable1, editable2].forEach(editable => {
+ editable.addEventListener('beforeinput', event =>
+ eventOrderRecorder.push(`beforeinput:${editable.id}:${event.inputType}`));
+ editable.addEventListener('input', event =>
+ eventOrderRecorder.push(`input:${editable.id}:${event.inputType}`));
+ editable.addEventListener('drop', event =>
+ eventOrderRecorder.push(`${editable.id}:drop`));
+ editable.addEventListener('dragend', event =>
+ eventOrderRecorder.push(`${editable.id}:dragend`));
+ });
+
+ function testDragDropEventOrder(dragElement, dropElement, expectedOrder) {
+ assert_equals(dragElement.children.length, 1);
+ eventOrderRecorder = [];
+ simulateDragDrop(dragElement, dropElement);
+ assert_array_equals(eventOrderRecorder, expectedOrder,
+ `Testing drag ${dragElement.id} onto ${dropElement.id} actual order: ${eventOrderRecorder}`);
+ }
+
+ function removeEditable1Listener() {
+ editable1.remove();
+ }
+
+ function removeEditable2Listener() {
+ editable2.remove();
+ }
+
+ // Testing remove drop target, |editable2| won't get 'beforeinput' as it's disconnected.
+ editable1.addEventListener('beforeinput', removeEditable2Listener);
+ testDragDropEventOrder(editable1, editable2,
+ ['editable2:drop', 'beforeinput:editable1:deleteByDrag', 'input:editable1:deleteByDrag', 'editable1:dragend']);
+ editable1.removeEventListener('beforeinput', removeEditable2Listener);
+ testRunner.execCommand('undo');
+ document.body.appendChild(editable2);
+
+ // Testing remove drag target, |editable1| won't receive DOM updates after disconnected.
+ editable1.addEventListener('beforeinput', removeEditable1Listener);
+ testDragDropEventOrder(editable1, editable2,
+ ['editable2:drop', 'beforeinput:editable1:deleteByDrag', 'beforeinput:editable2:insertFromDrop',
+ 'input:editable2:insertFromDrop', 'editable1:dragend']);
+ editable1.removeEventListener('beforeinput', removeEditable1Listener);
+ testRunner.execCommand('undo');
+ document.body.appendChild(editable1);
+}, 'Testing element removed by event handler');
+</script>
+</body>
+</html>

Powered by Google App Engine
This is Rietveld 408576698