dragenter and dragleave shouldn't use the same data transfer object
authorrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 8 Oct 2017 10:08:52 +0000 (10:08 +0000)
committerrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 8 Oct 2017 10:08:52 +0000 (10:08 +0000)
https://bugs.webkit.org/show_bug.cgi?id=178056

Reviewed by Darin Adler.

Source/WebCore:

This patch fixes the bug that we were using a single DataTransfer to fire dragleave and dragenter events
when the drag target moves from one element to another.

It alos refactors DragController and EventHandler code so that the construction of DataTransfer object
happens in EventHandler instead of DragController, and extracts createForUpdatingDropTarget out of
createForDrop to have a better encapsulation over the data store mode.

drag related functions in EventHandler now takes std::unique_ptr<Pasteboard>&&, drag operation mask set
by the drag source, and a boolean indicating whether this drag & drop is for files or not. updateDragAndDrop
takes a closure which makes a pasteboard because it has to create two instances of DataTransfer one for
dragleave event and another one for dragenter event in some cases.

Test: editing/pasteboard/data-transfer-is-unique-for-dragenter-and-dragleave.html

* dom/DataTransfer.cpp:
(WebCore::DataTransfer::createForDrop): Now takes Pasteboard instead of DragData.
(WebCore::DataTransfer::createForUpdatingDropTarget): Extracted out of createForDrop. Moved the code to
use Readonly mode in dashboad here from createDataTransferToUpdateDrag in DragController.cpp.
* dom/DataTransfer.h:
* page/DragController.cpp:
(WebCore::createDataTransferToUpdateDrag): Deleted.
(WebCore::DragController::dragExited):
(WebCore::DragController::performDragOperation):
(WebCore::DragController::tryDHTMLDrag):
* page/EventHandler.cpp:
(WebCore::EventHandler::dispatchDragEvent): Made this fucntion take DataTransfer& instead of DataTransfer*.
(WebCore::findDropZone): Ditto.
(WebCore::EventHandler::dispatchDragEnterOrDragOverEvent): Added.
(WebCore::EventHandler::updateDragAndDrop):
(WebCore::EventHandler::cancelDragAndDrop):
(WebCore::EventHandler::performDragAndDrop):
(WebCore::EventHandler::dispatchDragSrcEvent):
(WebCore::EventHandler::dispatchDragStartEventOnSourceElement):
* page/EventHandler.h:

LayoutTests:

Added a regression test for checking the uniqueness of dataTransfer object for dragenter and dragleave events.
Unfortunately, the test is only runnable in Mac WebKit1 port due to the lack of support in WebKitTestRunner.

* TestExpectations:
* editing/pasteboard/data-transfer-is-unique-for-dragenter-and-dragleave-expected.txt: Added.
* editing/pasteboard/data-transfer-is-unique-for-dragenter-and-dragleave.html: Added.
* platform/mac-wk1/TestExpectations:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@223031 268f45cc-cd09-0410-ab3c-d52691b4dbfc

12 files changed:
LayoutTests/ChangeLog
LayoutTests/TestExpectations
LayoutTests/editing/pasteboard/data-transfer-is-unique-for-dragenter-and-dragleave-expected.txt [new file with mode: 0644]
LayoutTests/editing/pasteboard/data-transfer-is-unique-for-dragenter-and-dragleave.html [new file with mode: 0644]
LayoutTests/platform/mac-wk1/TestExpectations
LayoutTests/platform/win/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/dom/DataTransfer.cpp
Source/WebCore/dom/DataTransfer.h
Source/WebCore/page/DragController.cpp
Source/WebCore/page/EventHandler.cpp
Source/WebCore/page/EventHandler.h

index d772ffa..d35a586 100644 (file)
@@ -1,3 +1,18 @@
+2017-10-08  Ryosuke Niwa  <rniwa@webkit.org>
+
+        dragenter and dragleave shouldn't use the same data transfer object
+        https://bugs.webkit.org/show_bug.cgi?id=178056
+
+        Reviewed by Darin Adler.
+
+        Added a regression test for checking the uniqueness of dataTransfer object for dragenter and dragleave events.
+        Unfortunately, the test is only runnable in Mac WebKit1 port due to the lack of support in WebKitTestRunner.
+
+        * TestExpectations:
+        * editing/pasteboard/data-transfer-is-unique-for-dragenter-and-dragleave-expected.txt: Added.
+        * editing/pasteboard/data-transfer-is-unique-for-dragenter-and-dragleave.html: Added.
+        * platform/mac-wk1/TestExpectations:
+
 2017-10-08  Jer Noble  <jer.noble@apple.com>
 
         SourceBuffer remove throws out way more content than requested
index 25b34e4..38b97c5 100644 (file)
@@ -73,6 +73,7 @@ editing/pasteboard/data-transfer-get-data-on-drop-custom.html [ Skip ]
 editing/pasteboard/data-transfer-get-data-on-drop-plain-text.html [ Skip ]
 editing/pasteboard/data-transfer-get-data-on-drop-rich-text.html [ Skip ]
 editing/pasteboard/data-transfer-get-data-on-drop-url.html [ Skip ]
+editing/pasteboard/data-transfer-is-unique-for-dragenter-and-dragleave.html [ Skip ]
 editing/pasteboard/drag-end-crash-accessing-item-list.html [ Skip ]
 editing/pasteboard/data-transfer-item-list-add-file-on-drag.html [ Skip ]
 
diff --git a/LayoutTests/editing/pasteboard/data-transfer-is-unique-for-dragenter-and-dragleave-expected.txt b/LayoutTests/editing/pasteboard/data-transfer-is-unique-for-dragenter-and-dragleave-expected.txt
new file mode 100644 (file)
index 0000000..2f69b1b
--- /dev/null
@@ -0,0 +1,10 @@
+PASS dragstart for source had an unique dataTransfer object
+PASS dragenter for passover had an unique dataTransfer object
+PASS dragenter for destination had an unique dataTransfer object
+PASS dragleave for passover had an unique dataTransfer object
+PASS dragover for destination had an unique dataTransfer object
+PASS drop for destination had an unique dataTransfer object
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/editing/pasteboard/data-transfer-is-unique-for-dragenter-and-dragleave.html b/LayoutTests/editing/pasteboard/data-transfer-is-unique-for-dragenter-and-dragleave.html
new file mode 100644 (file)
index 0000000..2ffbc36
--- /dev/null
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<html>
+<body>
+<style>
+#container > div { width: 100px; height: 50px; margin: 0px; }
+</style>
+<p id="description"></p>
+<div id="container">
+<div draggable="true" id="source" style="background: #9cf">Drag this</div>
+<div id="passover" style="background: #ff9">Drag over here</div>
+<div id="destination" style="background: #fc9">And drop here</div>
+</div>
+<div id="console"></div>
+<script src="../../resources/js-test-pre.js"></script>
+<script>
+
+var jsTestIsAsync = true;
+
+source.addEventListener("dragstart", (event) => {
+    event.dataTransfer.effectAllowed = 'copy';
+    event.dataTransfer.setData('text', 'hello, world');
+    dataTransferMap.clear();
+    testUniquenessAndAdd(event.dataTransfer, 'source', 'dragstart');
+});
+
+const dataTransferMap = new Map;
+function testUniquenessAndAdd(dataTransfer, label, eventType) {
+    const existingEntry = dataTransferMap.get(dataTransfer);
+    if (existingEntry) {
+        testFailed(`${eventType} for ${label} had the same dataTransfer object as ${existingEntry.eventType} for ${existingEntry.label}`);
+        return;
+    }
+    testPassed(`${eventType} for ${label} had an unique dataTransfer object`);
+    dataTransferMap.set(dataTransfer, {label, eventType});
+}
+
+passover.addEventListener("dragenter", (event) => {
+    testUniquenessAndAdd(event.dataTransfer, 'passover', 'dragenter');
+});
+passover.addEventListener("dragleave", (event) => {
+    testUniquenessAndAdd(event.dataTransfer, 'passover', 'dragleave');
+});
+destination.addEventListener("dragenter", (event) => {
+    testUniquenessAndAdd(event.dataTransfer, 'destination', 'dragenter');
+});
+destination.addEventListener("dragleave", (event) => {
+    testUniquenessAndAdd(event.dataTransfer, 'destination', 'dragleave');
+});
+destination.addEventListener("dragover", (event) => {
+    testUniquenessAndAdd(event.dataTransfer, 'destination', 'dragover');
+    event.preventDefault();
+});
+destination.addEventListener("drop", (event) => {
+    testUniquenessAndAdd(event.dataTransfer, 'destination', 'drop');
+    finishJSTest();
+});
+
+if (window.eventSender) {
+    eventSender.mouseMoveTo(source.offsetLeft + 10, source.offsetTop + 10);
+    eventSender.mouseDown();
+    eventSender.leapForward(500);
+    eventSender.mouseMoveTo(passover.offsetLeft + 10, passover.offsetTop + 10);
+    eventSender.leapForward(100);
+    eventSender.mouseMoveTo(destination.offsetLeft + 10, destination.offsetTop + 10);
+    eventSender.mouseUp();
+    document.getElementById('container').remove();
+}
+</script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
index 256f765..3e6f73e 100644 (file)
@@ -11,6 +11,7 @@ editing/pasteboard/data-transfer-get-data-on-drop-custom.html [ Pass ]
 editing/pasteboard/data-transfer-get-data-on-drop-plain-text.html [ Pass ]
 editing/pasteboard/data-transfer-get-data-on-drop-rich-text.html [ Pass ]
 editing/pasteboard/data-transfer-get-data-on-drop-url.html [ Pass ]
+editing/pasteboard/data-transfer-is-unique-for-dragenter-and-dragleave.html [ Pass ]
 editing/pasteboard/drag-end-crash-accessing-item-list.html [ Pass ]
 editing/pasteboard/data-transfer-item-list-add-file-on-drag.html [ Pass ]
 
index 194387d..fa1a976 100644 (file)
@@ -1155,6 +1155,7 @@ editing/pasteboard/paste-global-selection.html [ Skip ]
 webkit.org/b/116564 editing/pasteboard/copy-without-selection.html [ Skip ]
 editing/pasteboard/smart-paste-004.html [ Failure ]
 # TODO Drag & Drop doesn't work correctly in DRT <rdar://5621244>
+editing/pasteboard/data-transfer-is-unique-for-dragenter-and-dragleave.html [ Skip ]
 editing/pasteboard/drag-and-drop-objectimage-contenteditable.html [ Skip ]
 editing/pasteboard/drag-image-in-about-blank-frame.html [ Skip ]
 editing/pasteboard/drop-file-svg.html [ Skip ]
index 9b29ea1..27efb37 100644 (file)
@@ -1,3 +1,45 @@
+2017-10-08  Ryosuke Niwa  <rniwa@webkit.org>
+
+        dragenter and dragleave shouldn't use the same data transfer object
+        https://bugs.webkit.org/show_bug.cgi?id=178056
+
+        Reviewed by Darin Adler.
+
+        This patch fixes the bug that we were using a single DataTransfer to fire dragleave and dragenter events
+        when the drag target moves from one element to another.
+
+        It alos refactors DragController and EventHandler code so that the construction of DataTransfer object
+        happens in EventHandler instead of DragController, and extracts createForUpdatingDropTarget out of
+        createForDrop to have a better encapsulation over the data store mode.
+
+        drag related functions in EventHandler now takes std::unique_ptr<Pasteboard>&&, drag operation mask set
+        by the drag source, and a boolean indicating whether this drag & drop is for files or not. updateDragAndDrop
+        takes a closure which makes a pasteboard because it has to create two instances of DataTransfer one for
+        dragleave event and another one for dragenter event in some cases.
+
+        Test: editing/pasteboard/data-transfer-is-unique-for-dragenter-and-dragleave.html
+
+        * dom/DataTransfer.cpp:
+        (WebCore::DataTransfer::createForDrop): Now takes Pasteboard instead of DragData.
+        (WebCore::DataTransfer::createForUpdatingDropTarget): Extracted out of createForDrop. Moved the code to
+        use Readonly mode in dashboad here from createDataTransferToUpdateDrag in DragController.cpp.
+        * dom/DataTransfer.h:
+        * page/DragController.cpp:
+        (WebCore::createDataTransferToUpdateDrag): Deleted.
+        (WebCore::DragController::dragExited):
+        (WebCore::DragController::performDragOperation):
+        (WebCore::DragController::tryDHTMLDrag):
+        * page/EventHandler.cpp:
+        (WebCore::EventHandler::dispatchDragEvent): Made this fucntion take DataTransfer& instead of DataTransfer*.
+        (WebCore::findDropZone): Ditto.
+        (WebCore::EventHandler::dispatchDragEnterOrDragOverEvent): Added.
+        (WebCore::EventHandler::updateDragAndDrop):
+        (WebCore::EventHandler::cancelDragAndDrop):
+        (WebCore::EventHandler::performDragAndDrop):
+        (WebCore::EventHandler::dispatchDragSrcEvent):
+        (WebCore::EventHandler::dispatchDragStartEventOnSourceElement):
+        * page/EventHandler.h:
+
 2017-10-08  Jer Noble  <jer.noble@apple.com>
 
         SourceBuffer remove throws out way more content than requested
index 59d7433..b0d9df4 100644 (file)
@@ -337,10 +337,25 @@ Ref<DataTransfer> DataTransfer::createForDragStartEvent()
     return adoptRef(*new DataTransfer(StoreMode::ReadWrite, std::make_unique<StaticPasteboard>(), Type::DragAndDropData));
 }
 
-Ref<DataTransfer> DataTransfer::createForDrop(StoreMode accessMode, const DragData& dragData)
+Ref<DataTransfer> DataTransfer::createForDrop(std::unique_ptr<Pasteboard>&& pasteboard, DragOperation sourceOperation, bool draggingFiles)
 {
-    auto type = dragData.containsFiles() ? Type::DragAndDropFiles : Type::DragAndDropData;
-    return adoptRef(*new DataTransfer(accessMode, Pasteboard::createForDragAndDrop(dragData), type));
+    auto dataTransfer = adoptRef(*new DataTransfer(DataTransfer::StoreMode::Readonly, WTFMove(pasteboard), draggingFiles ? Type::DragAndDropFiles : Type::DragAndDropData));
+    dataTransfer->setSourceOperation(sourceOperation);
+    return dataTransfer;
+}
+
+Ref<DataTransfer> DataTransfer::createForUpdatingDropTarget(Document& document, std::unique_ptr<Pasteboard>&& pasteboard, DragOperation sourceOperation, bool draggingFiles)
+{
+    auto mode = DataTransfer::StoreMode::Protected;
+#if ENABLE(DASHBOARD_SUPPORT)
+    if (document.settings().usesDashboardBackwardCompatibilityMode() && document.securityOrigin().isLocal())
+        mode = DataTransfer::StoreMode::Readonly;
+#else
+    UNUSED_PARAM(document);
+#endif
+    auto dataTransfer = adoptRef(*new DataTransfer(mode, WTFMove(pasteboard), draggingFiles ? Type::DragAndDropFiles : Type::DragAndDropData));
+    dataTransfer->setSourceOperation(sourceOperation);
+    return dataTransfer;
 }
 
 void DataTransfer::setDragImage(Element* element, int x, int y)
index 8400d7a..abbede1 100644 (file)
@@ -32,6 +32,7 @@ namespace WebCore {
 
 class CachedImage;
 class DataTransferItemList;
+class Document;
 class DragData;
 class DragImageLoader;
 class Element;
@@ -83,7 +84,8 @@ public:
 #if ENABLE(DRAG_SUPPORT)
     static Ref<DataTransfer> createForDrag();
     static Ref<DataTransfer> createForDragStartEvent();
-    static Ref<DataTransfer> createForDrop(StoreMode, const DragData&);
+    static Ref<DataTransfer> createForDrop(std::unique_ptr<Pasteboard>&&, DragOperation, bool draggingFiles);
+    static Ref<DataTransfer> createForUpdatingDropTarget(Document&, std::unique_ptr<Pasteboard>&&, DragOperation, bool draggingFiles);
 
     bool dropEffectIsUninitialized() const { return m_dropEffect == "uninitialized"; }
 
index 3f8a206..a47e139 100644 (file)
@@ -220,31 +220,11 @@ DragOperation DragController::dragEntered(const DragData& dragData)
     return dragEnteredOrUpdated(dragData);
 }
 
-static Ref<DataTransfer> createDataTransferToUpdateDrag(const DragData& dragData, const Settings& settings, RefPtr<Document>&& documentUnderMouse)
-{
-    auto accessMode = DataTransfer::StoreMode::Protected;
-
-#if ENABLE(DASHBOARD_SUPPORT)
-    if (settings.usesDashboardBackwardCompatibilityMode() && (!documentUnderMouse || documentUnderMouse->securityOrigin().isLocal()))
-        accessMode = DataTransfer::StoreMode::Readonly;
-#else
-    UNUSED_PARAM(settings);
-    UNUSED_PARAM(documentUnderMouse);
-#endif
-
-    auto dataTransfer = DataTransfer::createForDrop(accessMode, dragData);
-    dataTransfer->setSourceOperation(dragData.draggingSourceOperationMask());
-
-    return dataTransfer;
-}
-
 void DragController::dragExited(const DragData& dragData)
 {
-    if (RefPtr<FrameView> v = m_page.mainFrame().view()) {
-        auto dataTransfer = createDataTransferToUpdateDrag(dragData, m_page.mainFrame().settings(), m_documentUnderMouse.copyRef());
-        m_page.mainFrame().eventHandler().cancelDragAndDrop(createMouseEvent(dragData), dataTransfer);
-        dataTransfer->makeInvalidForSecurity();
-    }
+    auto& mainFrame = m_page.mainFrame();
+    if (mainFrame.view())
+        mainFrame.eventHandler().cancelDragAndDrop(createMouseEvent(dragData), Pasteboard::createForDragAndDrop(dragData), dragData.draggingSourceOperationMask(), dragData.containsFiles());
     mouseMovedIntoDocument(nullptr);
     if (m_fileInputElementUnderMouse)
         m_fileInputElementUnderMouse->setCanReceiveDroppedFiles(false);
@@ -282,13 +262,8 @@ bool DragController::performDragOperation(const DragData& dragData)
         m_client.willPerformDragDestinationAction(DragDestinationActionDHTML, dragData);
         Ref<MainFrame> mainFrame(m_page.mainFrame());
         bool preventedDefault = false;
-        if (mainFrame->view()) {
-            // Sending an event can result in the destruction of the view and part.
-            auto dataTransfer = DataTransfer::createForDrop(DataTransfer::StoreMode::Readonly, dragData);
-            dataTransfer->setSourceOperation(dragData.draggingSourceOperationMask());
-            preventedDefault = mainFrame->eventHandler().performDragAndDrop(createMouseEvent(dragData), dataTransfer);
-            dataTransfer->makeInvalidForSecurity();
-        }
+        if (mainFrame->view())
+            preventedDefault = mainFrame->eventHandler().performDragAndDrop(createMouseEvent(dragData), Pasteboard::createForDragAndDrop(dragData), dragData.draggingSourceOperationMask(), dragData.containsFiles());
         if (preventedDefault) {
             clearDragCaret();
             m_documentUnderMouse = nullptr;
@@ -709,24 +684,18 @@ bool DragController::tryDHTMLDrag(const DragData& dragData, DragOperation& opera
     if (!viewProtector)
         return false;
 
-    auto dataTransfer = createDataTransferToUpdateDrag(dragData, mainFrame->settings(), m_documentUnderMouse.copyRef());
-
-    PlatformMouseEvent event = createMouseEvent(dragData);
-    if (!mainFrame->eventHandler().updateDragAndDrop(event, dataTransfer)) {
-        dataTransfer->makeInvalidForSecurity();
+    DragOperation sourceOperation = dragData.draggingSourceOperationMask();
+    auto targetResponse = mainFrame->eventHandler().updateDragAndDrop(createMouseEvent(dragData), [&dragData]() { return Pasteboard::createForDragAndDrop(dragData); }, sourceOperation, dragData.containsFiles());
+    if (!targetResponse.accept)
         return false;
-    }
 
-    operation = dataTransfer->destinationOperation();
-    DragOperation srcOpMask = dragData.draggingSourceOperationMask();
-    if (dataTransfer->dropEffectIsUninitialized())
-        operation = defaultOperationForDrag(srcOpMask);
-    else if (!(srcOpMask & operation)) {
-        // The element picked an operation which is not supported by the source
+    if (!targetResponse.operation)
+        operation = defaultOperationForDrag(sourceOperation);
+    else if (!(sourceOperation & targetResponse.operation.value())) // The element picked an operation which is not supported by the source
         operation = DragOperationNone;
-    }
+    else
+        operation = targetResponse.operation.value();
 
-    dataTransfer->makeInvalidForSecurity();
     return true;
 }
 
index 23d47c5..4173979 100644 (file)
@@ -2180,7 +2180,7 @@ bool EventHandler::handlePasteGlobalSelection(const PlatformMouseEvent& platform
 
 #if ENABLE(DRAG_SUPPORT)
 
-bool EventHandler::dispatchDragEvent(const AtomicString& eventType, Element& dragTarget, const PlatformMouseEvent& event, DataTransfer* dataTransfer)
+bool EventHandler::dispatchDragEvent(const AtomicString& eventType, Element& dragTarget, const PlatformMouseEvent& event, DataTransfer& dataTransfer)
 {
     Ref<Frame> protectedFrame(m_frame);
     FrameView* view = m_frame.view();
@@ -2197,7 +2197,7 @@ bool EventHandler::dispatchDragEvent(const AtomicString& eventType, Element& dra
         event.movementDelta().x(), event.movementDelta().y(),
 #endif
         event.ctrlKey(), event.altKey(), event.shiftKey(), event.metaKey(),
-        0, 0, event.force(), NoTap, dataTransfer);
+        0, 0, event.force(), NoTap, &dataTransfer);
 
     dragTarget.dispatchEvent(me);
     return me->defaultPrevented();
@@ -2248,10 +2248,9 @@ static bool hasDropZoneType(DataTransfer& dataTransfer, const String& keyword)
     return false;
 }
 
-static bool findDropZone(Node* target, DataTransfer* dataTransfer)
+static bool findDropZone(Node& target, DataTransfer& dataTransfer)
 {
-    ASSERT(target);
-    Element* element = is<Element>(*target) ? downcast<Element>(target) : target->parentElement();
+    RefPtr<Element> element = is<Element>(target) ? &downcast<Element>(target) : target.parentElement();
     for (; element; element = element->parentElement()) {
         SpaceSplitString keywords(element->attributeWithoutSynchronization(webkitdropzoneAttr), true);
         bool matched = false;
@@ -2262,26 +2261,36 @@ static bool findDropZone(Node* target, DataTransfer* dataTransfer)
                 if (dragOperation == DragOperationNone)
                     dragOperation = op;
             } else
-                matched = matched || hasDropZoneType(*dataTransfer, keywords[i].string());
+                matched = matched || hasDropZoneType(dataTransfer, keywords[i].string());
             if (matched && dragOperation != DragOperationNone)
                 break;
         }
         if (matched) {
-            dataTransfer->setDropEffect(convertDragOperationToDropZoneOperation(dragOperation));
+            dataTransfer.setDropEffect(convertDragOperationToDropZoneOperation(dragOperation));
             return true;
         }
     }
     return false;
 }
-    
-bool EventHandler::updateDragAndDrop(const PlatformMouseEvent& event, DataTransfer& dataTransfer)
-{
-    Ref<Frame> protectedFrame(m_frame);
 
-    bool accept = false;
+EventHandler::DragTargetResponse EventHandler::dispatchDragEnterOrDragOverEvent(const AtomicString& eventType, Element& target, const PlatformMouseEvent& event,
+    std::unique_ptr<Pasteboard>&& pasteboard, DragOperation sourceOperation, bool draggingFiles)
+{
+    auto dataTransfer = DataTransfer::createForUpdatingDropTarget(target.document(), WTFMove(pasteboard), sourceOperation, draggingFiles);
+    bool accept = dispatchDragEvent(eventType, target, event, dataTransfer.get());
+    if (!accept)
+        accept = findDropZone(target, dataTransfer);
+    dataTransfer->makeInvalidForSecurity();
+    if (accept && !dataTransfer->dropEffectIsUninitialized())
+        return { true, dataTransfer->destinationOperation() };
+    return { accept, std::nullopt };
+}
 
+EventHandler::DragTargetResponse EventHandler::updateDragAndDrop(const PlatformMouseEvent& event, const std::function<std::unique_ptr<Pasteboard>()>& makePasteboard, DragOperation sourceOperation, bool draggingFiles)
+{
+    Ref<Frame> protectedFrame(m_frame);
     if (!m_frame.view())
-        return false;
+        return { };
 
     HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowUserAgentShadowContent);
     MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, event);
@@ -2297,6 +2306,7 @@ bool EventHandler::updateDragAndDrop(const PlatformMouseEvent& event, DataTransf
 
     m_autoscrollController->updateDragAndDrop(newTarget.get(), event.position(), event.timestamp());
 
+    DragTargetResponse response;
     if (m_dragTarget != newTarget) {
         // FIXME: this ordering was explicitly chosen to match WinIE. However,
         // it is sometimes incorrect when dragging within subframes, as seen with
@@ -2306,23 +2316,25 @@ bool EventHandler::updateDragAndDrop(const PlatformMouseEvent& event, DataTransf
         Frame* targetFrame;
         if (targetIsFrame(newTarget.get(), targetFrame)) {
             if (targetFrame)
-                accept = targetFrame->eventHandler().updateDragAndDrop(event, dataTransfer);
+                response = targetFrame->eventHandler().updateDragAndDrop(event, makePasteboard, sourceOperation, draggingFiles);
         } else if (newTarget) {
             // As per section 7.9.4 of the HTML 5 spec., we must always fire a drag event before firing a dragenter, dragleave, or dragover event.
             if (dragState().source && dragState().shouldDispatchEvents) {
                 // for now we don't care if event handler cancels default behavior, since there is none
                 dispatchDragSrcEvent(eventNames().dragEvent, event);
             }
-            accept = dispatchDragEvent(eventNames().dragenterEvent, *newTarget, event, &dataTransfer);
-            if (!accept)
-                accept = findDropZone(newTarget.get(), &dataTransfer);
+            response = dispatchDragEnterOrDragOverEvent(eventNames().dragenterEvent, *newTarget, event, makePasteboard(), sourceOperation, draggingFiles);
         }
 
         if (targetIsFrame(m_dragTarget.get(), targetFrame)) {
+            // FIXME: Recursing again here doesn't make sense if the newTarget and m_dragTarget were in the same frame.
             if (targetFrame)
-                accept = targetFrame->eventHandler().updateDragAndDrop(event, dataTransfer);
-        } else if (m_dragTarget)
-            dispatchDragEvent(eventNames().dragleaveEvent, *m_dragTarget, event, &dataTransfer);
+                response = targetFrame->eventHandler().updateDragAndDrop(event, makePasteboard, sourceOperation, draggingFiles);
+        } else if (m_dragTarget) {
+            auto dataTransfer = DataTransfer::createForUpdatingDropTarget(m_dragTarget->document(), makePasteboard(), sourceOperation, draggingFiles);
+            dispatchDragEvent(eventNames().dragleaveEvent, *m_dragTarget, event, dataTransfer.get());
+            dataTransfer->makeInvalidForSecurity();
+        }
 
         if (newTarget) {
             // We do not explicitly call dispatchDragEvent here because it could ultimately result in the appearance that
@@ -2333,40 +2345,41 @@ bool EventHandler::updateDragAndDrop(const PlatformMouseEvent& event, DataTransf
         Frame* targetFrame;
         if (targetIsFrame(newTarget.get(), targetFrame)) {
             if (targetFrame)
-                accept = targetFrame->eventHandler().updateDragAndDrop(event, dataTransfer);
+                response = targetFrame->eventHandler().updateDragAndDrop(event, makePasteboard, sourceOperation, draggingFiles);
         } else if (newTarget) {
             // Note, when dealing with sub-frames, we may need to fire only a dragover event as a drag event may have been fired earlier.
             if (!m_shouldOnlyFireDragOverEvent && dragState().source && dragState().shouldDispatchEvents) {
                 // for now we don't care if event handler cancels default behavior, since there is none
                 dispatchDragSrcEvent(eventNames().dragEvent, event);
             }
-            accept = dispatchDragEvent(eventNames().dragoverEvent, *newTarget, event, &dataTransfer);
-            if (!accept)
-                accept = findDropZone(newTarget.get(), &dataTransfer);
+            response = dispatchDragEnterOrDragOverEvent(eventNames().dragoverEvent, *newTarget, event, makePasteboard(), sourceOperation, draggingFiles);
             m_shouldOnlyFireDragOverEvent = false;
         }
     }
     m_dragTarget = WTFMove(newTarget);
-    return accept;
+    return response;
 }
 
-void EventHandler::cancelDragAndDrop(const PlatformMouseEvent& event, DataTransfer& dataTransfer)
+void EventHandler::cancelDragAndDrop(const PlatformMouseEvent& event, std::unique_ptr<Pasteboard>&& pasteboard, DragOperation sourceOperation, bool draggingFiles)
 {
     Ref<Frame> protectedFrame(m_frame);
 
     Frame* targetFrame;
     if (targetIsFrame(m_dragTarget.get(), targetFrame)) {
         if (targetFrame)
-            targetFrame->eventHandler().cancelDragAndDrop(event, dataTransfer);
+            targetFrame->eventHandler().cancelDragAndDrop(event, WTFMove(pasteboard), sourceOperation, draggingFiles);
     } else if (m_dragTarget) {
         if (dragState().source && dragState().shouldDispatchEvents)
             dispatchDragSrcEvent(eventNames().dragEvent, event);
-        dispatchDragEvent(eventNames().dragleaveEvent, *m_dragTarget, event, &dataTransfer);
+
+        auto dataTransfer = DataTransfer::createForUpdatingDropTarget(m_dragTarget->document(), WTFMove(pasteboard), sourceOperation, draggingFiles);
+        dispatchDragEvent(eventNames().dragleaveEvent, *m_dragTarget, event, dataTransfer.get());
+        dataTransfer->makeInvalidForSecurity();
     }
     clearDragState();
 }
 
-bool EventHandler::performDragAndDrop(const PlatformMouseEvent& event, DataTransfer& dataTransfer)
+bool EventHandler::performDragAndDrop(const PlatformMouseEvent& event, std::unique_ptr<Pasteboard>&& pasteboard, DragOperation sourceOperation, bool draggingFiles)
 {
     Ref<Frame> protectedFrame(m_frame);
 
@@ -2374,9 +2387,12 @@ bool EventHandler::performDragAndDrop(const PlatformMouseEvent& event, DataTrans
     bool preventedDefault = false;
     if (targetIsFrame(m_dragTarget.get(), targetFrame)) {
         if (targetFrame)
-            preventedDefault = targetFrame->eventHandler().performDragAndDrop(event, dataTransfer);
-    } else if (m_dragTarget)
-        preventedDefault = dispatchDragEvent(eventNames().dropEvent, *m_dragTarget, event, &dataTransfer);
+            preventedDefault = targetFrame->eventHandler().performDragAndDrop(event, WTFMove(pasteboard), sourceOperation, draggingFiles);
+    } else if (m_dragTarget) {
+        auto dataTransfer = DataTransfer::createForDrop(WTFMove(pasteboard), sourceOperation, draggingFiles);
+        preventedDefault = dispatchDragEvent(eventNames().dropEvent, *m_dragTarget, event, dataTransfer);
+        dataTransfer->makeInvalidForSecurity();
+    }
     clearDragState();
     return preventedDefault;
 }
@@ -3545,12 +3561,13 @@ void EventHandler::updateDragStateAfterEditDragIfNeeded(Element& rootEditableEle
 // Return value indicates if we should continue "default processing", i.e., whether event handler canceled.
 bool EventHandler::dispatchDragSrcEvent(const AtomicString& eventType, const PlatformMouseEvent& event)
 {
-    return !dispatchDragEvent(eventType, *dragState().source, event, dragState().dataTransfer.get());
+    ASSERT(dragState().dataTransfer);
+    return !dispatchDragEvent(eventType, *dragState().source, event, *dragState().dataTransfer);
 }
 
 bool EventHandler::dispatchDragStartEventOnSourceElement(DataTransfer& dataTransfer)
 {
-    return !dispatchDragEvent(eventNames().dragstartEvent, *dragState().source, m_mouseDown, &dataTransfer) && !m_frame.selection().selection().isInPasswordField();
+    return !dispatchDragEvent(eventNames().dragstartEvent, *dragState().source, m_mouseDown, dataTransfer) && !m_frame.selection().selection().isInPasswordField();
 }
     
 static bool ExactlyOneBitSet(DragSourceAction n)
index 2de5acf..62a6b67 100644 (file)
@@ -75,6 +75,7 @@ class HitTestResult;
 class KeyboardEvent;
 class MouseEventWithHitTestResults;
 class Node;
+class Pasteboard;
 class PlatformGestureEvent;
 class PlatformKeyboardEvent;
 class PlatformTouchEvent;
@@ -155,9 +156,13 @@ public:
     WEBCORE_EXPORT void setCapturingMouseEventsElement(Element*);
 
 #if ENABLE(DRAG_SUPPORT)
-    bool updateDragAndDrop(const PlatformMouseEvent&, DataTransfer&);
-    void cancelDragAndDrop(const PlatformMouseEvent&, DataTransfer&);
-    bool performDragAndDrop(const PlatformMouseEvent&, DataTransfer&);
+    struct DragTargetResponse {
+        bool accept { false };
+        std::optional<DragOperation> operation;
+    };
+    DragTargetResponse updateDragAndDrop(const PlatformMouseEvent&, const std::function<std::unique_ptr<Pasteboard>()>&, DragOperation sourceOperation, bool draggingFiles);
+    void cancelDragAndDrop(const PlatformMouseEvent&, std::unique_ptr<Pasteboard>&&, DragOperation, bool draggingFiles);
+    bool performDragAndDrop(const PlatformMouseEvent&, std::unique_ptr<Pasteboard>&&, DragOperation, bool draggingFiles);
     void updateDragStateAfterEditDragIfNeeded(Element& rootEditableElement);
     RefPtr<Element> draggedElement() const;
 #endif
@@ -392,7 +397,8 @@ private:
     bool dispatchMouseEvent(const AtomicString& eventType, Node* target, bool cancelable, int clickCount, const PlatformMouseEvent&, bool setUnder);
 
 #if ENABLE(DRAG_SUPPORT)
-    bool dispatchDragEvent(const AtomicString& eventType, Element& target, const PlatformMouseEvent&, DataTransfer*);
+    bool dispatchDragEvent(const AtomicString& eventType, Element& target, const PlatformMouseEvent&, DataTransfer&);
+    DragTargetResponse dispatchDragEnterOrDragOverEvent(const AtomicString& eventType, Element& target, const PlatformMouseEvent&, std::unique_ptr<Pasteboard>&& , DragOperation, bool draggingFiles);
     void invalidateDataTransfer();
 
     bool handleDrag(const MouseEventWithHitTestResults&, CheckDragHysteresis);