Title: [262507] trunk
Revision
262507
Author
[email protected]
Date
2020-06-03 12:41:50 -0700 (Wed, 03 Jun 2020)

Log Message

dataTransfer.types is empty when handling the "dragstart" event
https://bugs.webkit.org/show_bug.cgi?id=212685
<rdar://problem/61368402>

Reviewed by Andy Estes.

Source/WebCore:

Implements several currently stubbed methods on StaticPasteboard, so that the DataTransfer provided to the page
on the "dragstart" event contains the DOM-exposed data types that will be written to the system pasteboard. This
includes "text/html", "text/plain", and "text/uri-list".

Tests:  DragAndDropTests.DataTransferTypesOnDragStartForTextSelection
        DragAndDropTests.DataTransferTypesOnDragStartForImage
        DragAndDropTests.DataTransferTypesOnDragStartForLink

...as well as several existing tests in DragAndDropTestsIOS.mm that attempt to set pasteboard data during the
dragstart event:

        DragAndDropTests.DataTransferSanitizeHTML
        DragAndDropTests.DataTransferSetDataCannotWritePlatformTypes
        DragAndDropTests.DataTransferSetDataInvalidURL
        DragAndDropTests.DataTransferSetDataUnescapedURL
        DragAndDropTests.DataTransferSetDataValidURL

* dom/DataTransfer.cpp:
(WebCore::DataTransfer::commitToPasteboard):

Only commit data to the native pasteboard if the page actually tried to write or modify the data. This allows us
to preserve existing behavior by allowing DragController to write dragged data to the pasteboard normally in the
case where the page didn't specify any custom data. In the case where the page does specify custom data, we will
write this custom data *in addition* to any default data that was written to the static pasteboard. While this
is a departure from our current behavior (which is to treat the pasteboard as a blank slate that contains only
whatever custom data was provided by the page), it matches behavior in both Chrome and Firefox, and is likely
more compatible with webpages that don't have UA-specific logic targeting WebKit.

* editing/cocoa/EditorCocoa.mm:
(WebCore::Editor::writeSelectionToPasteboard):

Avoid calling into the injected bundle (as well as writing a few particular non-web-exposed types, such as web
archive data) in the case where we're writing to a static pasteboard (there's no point in doing this for the
static pasteboard, and in the worst case, it could confuse some internal clients).

* editing/ios/EditorIOS.mm:
(WebCore::Editor::writeImageToPasteboard): Ditto.
* editing/mac/EditorMac.mm:
(WebCore::Editor::writeImageToPasteboard):

Ditto. But additionally, introduce a markup string to PasteboardImage, so that we will expose the "text/html"
type when starting a drag on an image element.

* page/DragController.cpp:
(WebCore::DragController::startDrag):

Only attempt to call into `Pasteboard::writeTrustworthyWebURLsPboardType` in the case where the pasteboard
supports this type (i.e. on macOS). This fixes an existing assertion that was hit by my new API test, which
attempts to override the contents of the pasteboard with custom data while starting a drag on a link.

* page/EventHandler.cpp:
(WebCore::EventHandler::handleDrag):

Since the StaticPasteboard contains data before the page has written anything, don't use `Pasteboard::hasData()`
to determine whether there's custom data; instead, use the new `hasNonDefaultData()` method on
`StaticPasteboard` (see below).

* platform/Pasteboard.cpp:
(WebCore::Pasteboard::canWriteTrustworthyWebURLsPboardType):

On non-macOS ports, return false.

* platform/Pasteboard.h:
* platform/StaticPasteboard.cpp:
(WebCore::StaticPasteboard::hasNonDefaultData const):

Keep track of whether the page attempted to stage any custom data during "dragstart" by maintaining the set of
types written by the page, via calls to `writeString()` and similar. I'm using a set of types here instead of a
simple `bool` flag to ensure correctness in the case where the page adds a type, and then later removes that
same custom type, such that there is no longer non-default data.

(WebCore::StaticPasteboard::writeString):
(WebCore::StaticPasteboard::writeData):
(WebCore::StaticPasteboard::writeStringInCustomData):
(WebCore::StaticPasteboard::clear):

See above.

(WebCore::StaticPasteboard::writeMarkup):
(WebCore::StaticPasteboard::writePlainText):
(WebCore::StaticPasteboard::write):

Implement these methods by writing to the `PasteboardCustomData`. These methods are invoked by our own code
rather than the bindings, and should only be used to stage default data types when starting a drag.

* platform/StaticPasteboard.h:
* platform/mac/PasteboardMac.mm:
(WebCore::Pasteboard::write):
(WebCore::Pasteboard::canWriteTrustworthyWebURLsPboardType):

Tools:

Adds new API tests and test infrastructure to verify that DataTransfer types and data are accessible during
the "dragstart" event. See below for more details.

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WebKitCocoa/DragAndDropTests.mm:
(-[TestWKWebView selectElementWithID:]):
(-[DragAndDropSimulator dragFromElementWithID:to:]):

Add a few (very specialized) helper methods to assist with simulating drags over the various elements in the new
drag and drop test harness page below (dragstart-data.html).

(runDragStartDataTestCase):

Test the following scenarios (split between three API tests) by dumping the resulting DataTransfer types and
their data strings, and observing the results:

- Dragging a normal text selection.
- Dragging a normal text selection, and then adding a URL string.
- Dragging a normal text selection, and then adding a custom pasteboard type.
- Dragging a normal text selection, but then replacing the data with just a URL string.
- Dragging a normal text selection, but then replacing the data with just a custom data type.
- Dragging an image element.
- Dragging an image element, and then overriding the plain text data.
- Dragging a link (anchor element).
- Dragging a link, and then adding a custom type.

* TestWebKitAPI/Tests/WebKitCocoa/dragstart-data.html: Added.

Add a new test harness to help test DataTransfer types when starting a drag. This test page can also be used as
a manual test harness, by simply opening the test page, starting drags on the various elements and observing the
output in the textarea.

* TestWebKitAPI/Tests/WebKitCocoa/dump-datatransfer-types.html:

Tweak this test page to replace the DataTransfer with custom data (rather than simply append it) by calling
`DataTransfer.clearData()` prior to writing the custom types.

* TestWebKitAPI/Tests/ios/DragAndDropTestsIOS.mm:
* TestWebKitAPI/cocoa/TestWKWebView.h:
* TestWebKitAPI/cocoa/TestWKWebView.mm:
(-[TestWKWebViewHostWindow initWithWebView:contentRect:styleMask:backing:defer:]):

Add a `__weak` reference on TestWKWebViewHostWindow back to the TestWKWebView, so that we can consult
`-eventTimestamp` when synthesizing mouse events on macOS during API tests.

(-[TestWKWebViewHostWindow _mouseDownAtPoint:simulatePressure:clickCount:]):
(-[TestWKWebViewHostWindow _mouseUpAtPoint:clickCount:]):
(-[TestWKWebViewHostWindow initWithWebView:frame:]):
(-[TestWKWebView _setUpTestWindow:]):
(-[TestWKWebView setEventTimestampOffset:]):
(-[TestWKWebView eventTimestamp]):

Add a mechanism to offset synthetic event timestamps by a given time interval (i.e. the event timestamp offset).

(-[TestWKWebView mouseMoveToPoint:withFlags:]):
(-[TestWKWebView _mouseEventWithType:atLocation:]):
(-[TestWKWebView typeCharacter:]):
* TestWebKitAPI/mac/DragAndDropSimulatorMac.mm:
(-[DragAndDropSimulator runFrom:to:]):

While simulating drag and drop on macOS, use `-setEventTimestampOffset:` to "leap forward" in time, so that the
150 millisecond delay when dragging a text selection doesn't prevent drags from beginning.

Modified Paths

Added Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (262506 => 262507)


--- trunk/Source/WebCore/ChangeLog	2020-06-03 19:25:43 UTC (rev 262506)
+++ trunk/Source/WebCore/ChangeLog	2020-06-03 19:41:50 UTC (rev 262507)
@@ -1,3 +1,101 @@
+2020-06-03  Wenson Hsieh  <[email protected]>
+
+        dataTransfer.types is empty when handling the "dragstart" event
+        https://bugs.webkit.org/show_bug.cgi?id=212685
+        <rdar://problem/61368402>
+
+        Reviewed by Andy Estes.
+
+        Implements several currently stubbed methods on StaticPasteboard, so that the DataTransfer provided to the page
+        on the "dragstart" event contains the DOM-exposed data types that will be written to the system pasteboard. This
+        includes "text/html", "text/plain", and "text/uri-list".
+
+        Tests:  DragAndDropTests.DataTransferTypesOnDragStartForTextSelection
+                DragAndDropTests.DataTransferTypesOnDragStartForImage
+                DragAndDropTests.DataTransferTypesOnDragStartForLink
+
+        ...as well as several existing tests in DragAndDropTestsIOS.mm that attempt to set pasteboard data during the
+        dragstart event:
+
+                DragAndDropTests.DataTransferSanitizeHTML
+                DragAndDropTests.DataTransferSetDataCannotWritePlatformTypes
+                DragAndDropTests.DataTransferSetDataInvalidURL
+                DragAndDropTests.DataTransferSetDataUnescapedURL
+                DragAndDropTests.DataTransferSetDataValidURL
+
+        * dom/DataTransfer.cpp:
+        (WebCore::DataTransfer::commitToPasteboard):
+
+        Only commit data to the native pasteboard if the page actually tried to write or modify the data. This allows us
+        to preserve existing behavior by allowing DragController to write dragged data to the pasteboard normally in the
+        case where the page didn't specify any custom data. In the case where the page does specify custom data, we will
+        write this custom data *in addition* to any default data that was written to the static pasteboard. While this
+        is a departure from our current behavior (which is to treat the pasteboard as a blank slate that contains only
+        whatever custom data was provided by the page), it matches behavior in both Chrome and Firefox, and is likely
+        more compatible with webpages that don't have UA-specific logic targeting WebKit.
+
+        * editing/cocoa/EditorCocoa.mm:
+        (WebCore::Editor::writeSelectionToPasteboard):
+
+        Avoid calling into the injected bundle (as well as writing a few particular non-web-exposed types, such as web
+        archive data) in the case where we're writing to a static pasteboard (there's no point in doing this for the
+        static pasteboard, and in the worst case, it could confuse some internal clients).
+
+        * editing/ios/EditorIOS.mm:
+        (WebCore::Editor::writeImageToPasteboard): Ditto.
+        * editing/mac/EditorMac.mm:
+        (WebCore::Editor::writeImageToPasteboard):
+
+        Ditto. But additionally, introduce a markup string to PasteboardImage, so that we will expose the "text/html"
+        type when starting a drag on an image element.
+
+        * page/DragController.cpp:
+        (WebCore::DragController::startDrag):
+
+        Only attempt to call into `Pasteboard::writeTrustworthyWebURLsPboardType` in the case where the pasteboard
+        supports this type (i.e. on macOS). This fixes an existing assertion that was hit by my new API test, which
+        attempts to override the contents of the pasteboard with custom data while starting a drag on a link.
+
+        * page/EventHandler.cpp:
+        (WebCore::EventHandler::handleDrag):
+
+        Since the StaticPasteboard contains data before the page has written anything, don't use `Pasteboard::hasData()`
+        to determine whether there's custom data; instead, use the new `hasNonDefaultData()` method on
+        `StaticPasteboard` (see below).
+
+        * platform/Pasteboard.cpp:
+        (WebCore::Pasteboard::canWriteTrustworthyWebURLsPboardType):
+
+        On non-macOS ports, return false.
+
+        * platform/Pasteboard.h:
+        * platform/StaticPasteboard.cpp:
+        (WebCore::StaticPasteboard::hasNonDefaultData const):
+
+        Keep track of whether the page attempted to stage any custom data during "dragstart" by maintaining the set of
+        types written by the page, via calls to `writeString()` and similar. I'm using a set of types here instead of a
+        simple `bool` flag to ensure correctness in the case where the page adds a type, and then later removes that
+        same custom type, such that there is no longer non-default data.
+
+        (WebCore::StaticPasteboard::writeString):
+        (WebCore::StaticPasteboard::writeData):
+        (WebCore::StaticPasteboard::writeStringInCustomData):
+        (WebCore::StaticPasteboard::clear):
+
+        See above.
+
+        (WebCore::StaticPasteboard::writeMarkup):
+        (WebCore::StaticPasteboard::writePlainText):
+        (WebCore::StaticPasteboard::write):
+
+        Implement these methods by writing to the `PasteboardCustomData`. These methods are invoked by our own code
+        rather than the bindings, and should only be used to stage default data types when starting a drag.
+
+        * platform/StaticPasteboard.h:
+        * platform/mac/PasteboardMac.mm:
+        (WebCore::Pasteboard::write):
+        (WebCore::Pasteboard::canWriteTrustworthyWebURLsPboardType):
+
 2020-06-03  Jer Noble  <[email protected]>
 
         Crash with uncaught exception: *** -[AVSampleBufferAudioRenderer enqueueSampleBuffer:] Sample buffer has media type 'vide' instead of 'soun'

Modified: trunk/Source/WebCore/dom/DataTransfer.cpp (262506 => 262507)


--- trunk/Source/WebCore/dom/DataTransfer.cpp	2020-06-03 19:25:43 UTC (rev 262506)
+++ trunk/Source/WebCore/dom/DataTransfer.cpp	2020-06-03 19:41:50 UTC (rev 262507)
@@ -424,8 +424,8 @@
 void DataTransfer::commitToPasteboard(Pasteboard& nativePasteboard)
 {
     ASSERT(is<StaticPasteboard>(*m_pasteboard) && !is<StaticPasteboard>(nativePasteboard));
-    PasteboardCustomData customData = downcast<StaticPasteboard>(*m_pasteboard).takeCustomData();
-    if (!customData.hasData()) {
+    auto& staticPasteboard = downcast<StaticPasteboard>(*m_pasteboard);
+    if (!staticPasteboard.hasNonDefaultData()) {
         // We clear the platform pasteboard here to ensure that the pasteboard doesn't contain any data
         // that may have been written before starting the drag or copying, and after ending the last
         // drag session or paste. After pushing the static pasteboard's contents to the platform, the
@@ -434,6 +434,7 @@
         return;
     }
 
+    auto customData = staticPasteboard.takeCustomData();
     if (RuntimeEnabledFeatures::sharedFeatures().customPasteboardDataEnabled()) {
         customData.setOrigin(m_originIdentifier);
         nativePasteboard.writeCustomData({ customData });

Modified: trunk/Source/WebCore/editing/cocoa/EditorCocoa.mm (262506 => 262507)


--- trunk/Source/WebCore/editing/cocoa/EditorCocoa.mm	2020-06-03 19:25:43 UTC (rev 262506)
+++ trunk/Source/WebCore/editing/cocoa/EditorCocoa.mm	2020-06-03 19:41:50 UTC (rev 262507)
@@ -119,13 +119,15 @@
     PasteboardWebContent content;
     content.contentOrigin = m_document.originIdentifierForPasteboard();
     content.canSmartCopyOrDelete = canSmartCopyOrDelete();
-    content.dataInWebArchiveFormat = selectionInWebArchiveFormat();
-    content.dataInRTFDFormat = [string containsAttachments] ? dataInRTFDFormat(string.get()) : nullptr;
-    content.dataInRTFFormat = dataInRTFFormat(string.get());
-    content.dataInAttributedStringFormat = archivedDataForAttributedString(string.get());
+    if (!pasteboard.isStatic()) {
+        content.dataInWebArchiveFormat = selectionInWebArchiveFormat();
+        content.dataInRTFDFormat = [string containsAttachments] ? dataInRTFDFormat(string.get()) : nullptr;
+        content.dataInRTFFormat = dataInRTFFormat(string.get());
+        content.dataInAttributedStringFormat = archivedDataForAttributedString(string.get());
+        client()->getClientPasteboardDataForRange(selectedRange().get(), content.clientTypes, content.clientData);
+    }
     content.dataInHTMLFormat = selectionInHTMLFormat();
     content.dataInStringFormat = stringSelectionForPasteboardWithImageAltText();
-    client()->getClientPasteboardDataForRange(selectedRange().get(), content.clientTypes, content.clientData);
 
     pasteboard.write(content);
 }

Modified: trunk/Source/WebCore/editing/ios/EditorIOS.mm (262506 => 262507)


--- trunk/Source/WebCore/editing/ios/EditorIOS.mm	2020-06-03 19:25:43 UTC (rev 262506)
+++ trunk/Source/WebCore/editing/ios/EditorIOS.mm	2020-06-03 19:41:50 UTC (rev 262507)
@@ -206,10 +206,12 @@
     pasteboardImage.resourceMIMEType = pasteboard.resourceMIMEType(cachedImage->response().mimeType());
     pasteboardImage.resourceData = cachedImage->resourceBuffer();
 
-    Position beforeImagePosition(&imageElement, Position::PositionIsBeforeAnchor);
-    Position afterImagePosition(&imageElement, Position::PositionIsAfterAnchor);
-    auto imageRange = Range::create(imageElement.document(), beforeImagePosition, afterImagePosition);
-    client()->getClientPasteboardDataForRange(imageRange.ptr(), pasteboardImage.clientTypes, pasteboardImage.clientData);
+    if (!pasteboard.isStatic()) {
+        Position beforeImagePosition(&imageElement, Position::PositionIsBeforeAnchor);
+        Position afterImagePosition(&imageElement, Position::PositionIsAfterAnchor);
+        auto imageRange = Range::create(imageElement.document(), beforeImagePosition, afterImagePosition);
+        client()->getClientPasteboardDataForRange(imageRange.ptr(), pasteboardImage.clientTypes, pasteboardImage.clientData);
+    }
 
     pasteboard.write(pasteboardImage);
 }

Modified: trunk/Source/WebCore/editing/mac/EditorMac.mm (262506 => 262507)


--- trunk/Source/WebCore/editing/mac/EditorMac.mm	2020-06-03 19:25:43 UTC (rev 262506)
+++ trunk/Source/WebCore/editing/mac/EditorMac.mm	2020-06-03 19:41:50 UTC (rev 262507)
@@ -250,7 +250,14 @@
         return;
     ASSERT(cachedImage);
 
-    pasteboardImage.dataInWebArchiveFormat = imageInWebArchiveFormat(imageElement);
+    if (!pasteboard.isStatic())
+        pasteboardImage.dataInWebArchiveFormat = imageInWebArchiveFormat(imageElement);
+
+    if (auto imageRange = makeRangeSelectingNode(imageElement)) {
+        auto serializeComposedTree = m_document.settings().selectionAcrossShadowBoundariesEnabled() ? SerializeComposedTree::Yes : SerializeComposedTree::No;
+        pasteboardImage.dataInHTMLFormat = serializePreservingVisualAppearance(VisibleSelection { *imageRange }, ResolveURLs::YesExcludingLocalFileURLsForPrivacy, serializeComposedTree);
+    }
+
     pasteboardImage.url.url = ""
     pasteboardImage.url.title = title;
     pasteboardImage.url.userVisibleForm = WTF::userVisibleString(pasteboardImage.url.url);

Modified: trunk/Source/WebCore/page/DragController.cpp (262506 => 262507)


--- trunk/Source/WebCore/page/DragController.cpp	2020-06-03 19:25:43 UTC (rev 262506)
+++ trunk/Source/WebCore/page/DragController.cpp	2020-06-03 19:41:50 UTC (rev 262507)
@@ -1123,7 +1123,7 @@
                 src.editor().copyURL(linkURL, textContentWithSimplifiedWhiteSpace, dataTransfer.pasteboard());
             else
                 pasteboardWriterData.setURLData(src.editor().pasteboardWriterURL(linkURL, textContentWithSimplifiedWhiteSpace));
-        } else {
+        } else if (dataTransfer.pasteboard().canWriteTrustworthyWebURLsPboardType()) {
             // Make sure the pasteboard also contains trustworthy link data
             // but don't overwrite more general pasteboard types.
             PasteboardURL pasteboardURL;

Modified: trunk/Source/WebCore/page/EventHandler.cpp (262506 => 262507)


--- trunk/Source/WebCore/page/EventHandler.cpp	2020-06-03 19:25:43 UTC (rev 262506)
+++ trunk/Source/WebCore/page/EventHandler.cpp	2020-06-03 19:41:50 UTC (rev 262507)
@@ -3872,13 +3872,14 @@
     invalidateDataTransfer();
 
     dragState().dataTransfer = DataTransfer::createForDrag();
-    HasNonDefaultPasteboardData hasNonDefaultPasteboardData = HasNonDefaultPasteboardData::No;
+    auto hasNonDefaultPasteboardData = HasNonDefaultPasteboardData::No;
     
     if (dragState().shouldDispatchEvents) {
         ASSERT(dragState().source);
         auto dragStartDataTransfer = DataTransfer::createForDragStartEvent(dragState().source->document());
         m_mouseDownMayStartDrag = dispatchDragStartEventOnSourceElement(dragStartDataTransfer);
-        hasNonDefaultPasteboardData = dragStartDataTransfer->pasteboard().hasData() ? HasNonDefaultPasteboardData::Yes : HasNonDefaultPasteboardData::No;
+        if (downcast<StaticPasteboard>(dragStartDataTransfer->pasteboard()).hasNonDefaultData())
+            hasNonDefaultPasteboardData = HasNonDefaultPasteboardData::Yes;
         dragState().dataTransfer->moveDragState(WTFMove(dragStartDataTransfer));
 
         if (dragState().source && dragState().type == DragSourceActionDHTML && !dragState().dataTransfer->hasDragImage()) {

Modified: trunk/Source/WebCore/platform/Pasteboard.cpp (262506 => 262507)


--- trunk/Source/WebCore/platform/Pasteboard.cpp	2020-06-03 19:25:43 UTC (rev 262506)
+++ trunk/Source/WebCore/platform/Pasteboard.cpp	2020-06-03 19:41:50 UTC (rev 262507)
@@ -103,4 +103,13 @@
     return { };
 }
 
+#if !PLATFORM(MAC)
+
+bool Pasteboard::canWriteTrustworthyWebURLsPboardType()
+{
+    return false;
+}
+
+#endif
+
 };

Modified: trunk/Source/WebCore/platform/Pasteboard.h (262506 => 262507)


--- trunk/Source/WebCore/platform/Pasteboard.h	2020-06-03 19:25:43 UTC (rev 262506)
+++ trunk/Source/WebCore/platform/Pasteboard.h	2020-06-03 19:41:50 UTC (rev 262507)
@@ -118,6 +118,7 @@
     RefPtr<Image> image;
 #if PLATFORM(MAC)
     RefPtr<SharedBuffer> dataInWebArchiveFormat;
+    String dataInHTMLFormat;
 #endif
 #if !PLATFORM(WIN)
     PasteboardURL url;
@@ -214,6 +215,8 @@
     virtual WEBCORE_EXPORT void read(PasteboardWebContentReader&, WebContentReadingPolicy = WebContentReadingPolicy::AnyType, Optional<size_t> itemIndex = WTF::nullopt);
     virtual WEBCORE_EXPORT void read(PasteboardFileReader&, Optional<size_t> itemIndex = WTF::nullopt);
 
+    static bool canWriteTrustworthyWebURLsPboardType();
+
     virtual WEBCORE_EXPORT void write(const Color&);
     virtual WEBCORE_EXPORT void write(const PasteboardURL&);
     virtual WEBCORE_EXPORT void writeTrustworthyWebURLsPboardType(const PasteboardURL&);
@@ -222,7 +225,7 @@
 
     virtual WEBCORE_EXPORT void writeCustomData(const Vector<PasteboardCustomData>&);
 
-    enum class FileContentState { NoFileOrImageData, InMemoryImage, MayContainFilePaths };
+    enum class FileContentState : uint8_t { NoFileOrImageData, InMemoryImage, MayContainFilePaths };
     virtual WEBCORE_EXPORT FileContentState fileContentState();
     virtual WEBCORE_EXPORT bool canSmartReplace();
 

Modified: trunk/Source/WebCore/platform/StaticPasteboard.cpp (262506 => 262507)


--- trunk/Source/WebCore/platform/StaticPasteboard.cpp	2020-06-03 19:25:43 UTC (rev 262506)
+++ trunk/Source/WebCore/platform/StaticPasteboard.cpp	2020-06-03 19:41:50 UTC (rev 262507)
@@ -58,28 +58,39 @@
     return m_customData.readStringInCustomData(type);
 }
 
+bool StaticPasteboard::hasNonDefaultData() const
+{
+    return !m_nonDefaultDataTypes.isEmpty();
+}
+
 void StaticPasteboard::writeString(const String& type, const String& value)
 {
+    m_nonDefaultDataTypes.add(type);
     m_customData.writeString(type, value);
 }
 
 void StaticPasteboard::writeData(const String& type, Ref<SharedBuffer>&& data)
 {
+    m_nonDefaultDataTypes.add(type);
     m_customData.writeData(type, WTFMove(data));
 }
 
 void StaticPasteboard::writeStringInCustomData(const String& type, const String& value)
 {
+    m_nonDefaultDataTypes.add(type);
     m_customData.writeStringInCustomData(type, value);
 }
 
 void StaticPasteboard::clear()
 {
+    m_nonDefaultDataTypes.clear();
+    m_fileContentState = Pasteboard::FileContentState::NoFileOrImageData;
     m_customData.clear();
 }
 
 void StaticPasteboard::clear(const String& type)
 {
+    m_nonDefaultDataTypes.remove(type);
     m_customData.clear(type);
 }
 
@@ -88,4 +99,60 @@
     return std::exchange(m_customData, { });
 }
 
+void StaticPasteboard::writeMarkup(const String& markup)
+{
+    m_customData.writeString("text/html"_s, markup);
 }
+
+void StaticPasteboard::writePlainText(const String& text, SmartReplaceOption)
+{
+    m_customData.writeString("text/plain"_s, text);
+}
+
+void StaticPasteboard::write(const PasteboardURL& url)
+{
+    m_customData.writeString("text/uri-list"_s, url.url.string());
+}
+
+void StaticPasteboard::write(const PasteboardImage& image)
+{
+    // FIXME: This should ideally remember the image data, so that when this StaticPasteboard
+    // is committed to the native pasteboard, we'll preserve the image as well. For now, stick
+    // with our existing behavior, which prevents image data from being copied in the case where
+    // any non-default data was written by the page.
+    m_fileContentState = Pasteboard::FileContentState::InMemoryImage;
+
+#if PLATFORM(MAC)
+    if (!image.dataInHTMLFormat.isEmpty())
+        writeMarkup(image.dataInHTMLFormat);
+#else
+    UNUSED_PARAM(image);
+#endif
+
+#if !PLATFORM(WIN)
+    if (Pasteboard::canExposeURLToDOMWhenPasteboardContainsFiles(image.url.url.string()))
+        write(image.url);
+#endif
+}
+
+void StaticPasteboard::write(const PasteboardWebContent& content)
+{
+    String markup;
+    String text;
+
+#if PLATFORM(COCOA)
+    markup = content.dataInHTMLFormat;
+    text = content.dataInStringFormat;
+#elif PLATFORM(GTK) || USE(LIBWPE)
+    markup = content.markup;
+    text = content.text;
+#endif
+
+    if (!markup.isEmpty())
+        writeMarkup(markup);
+
+    if (!text.isEmpty())
+        writePlainText(text, SmartReplaceOption::CannotSmartReplace);
+}
+
+}

Modified: trunk/Source/WebCore/platform/StaticPasteboard.h (262506 => 262507)


--- trunk/Source/WebCore/platform/StaticPasteboard.h	2020-06-03 19:25:43 UTC (rev 262506)
+++ trunk/Source/WebCore/platform/StaticPasteboard.h	2020-06-03 19:41:50 UTC (rev 262507)
@@ -41,6 +41,7 @@
 
     PasteboardCustomData takeCustomData();
 
+    bool hasNonDefaultData() const;
     bool isStatic() const final { return true; }
 
     bool hasData() final;
@@ -59,18 +60,17 @@
     void read(PasteboardPlainText&, PlainTextURLReadingPolicy = PlainTextURLReadingPolicy::AllowURL, Optional<size_t> = WTF::nullopt) final { }
     void read(PasteboardWebContentReader&, WebContentReadingPolicy, Optional<size_t> = WTF::nullopt) final { }
 
-    void write(const PasteboardURL&) final { }
-    void write(const PasteboardImage&) final { }
-    void write(const PasteboardWebContent&) final { }
+    void write(const PasteboardURL&) final;
+    void write(const PasteboardImage&) final;
+    void write(const PasteboardWebContent&) final;
+    void writeMarkup(const String&) final;
+    void writePlainText(const String&, SmartReplaceOption) final;
 
     void writeCustomData(const Vector<PasteboardCustomData>&) final { }
 
-    Pasteboard::FileContentState fileContentState() final { return FileContentState::NoFileOrImageData; }
+    Pasteboard::FileContentState fileContentState() final { return m_fileContentState; }
     bool canSmartReplace() final { return false; }
 
-    void writeMarkup(const String&) final { }
-    void writePlainText(const String&, SmartReplaceOption) final { }
-
 #if ENABLE(DRAG_SUPPORT)
     void setDragImage(DragImage, const IntPoint&) final { }
 #endif
@@ -77,6 +77,8 @@
 
 private:
     PasteboardCustomData m_customData;
+    HashSet<String> m_nonDefaultDataTypes;
+    Pasteboard::FileContentState m_fileContentState { Pasteboard::FileContentState::NoFileOrImageData };
 };
 
 }

Modified: trunk/Source/WebCore/platform/mac/PasteboardMac.mm (262506 => 262507)


--- trunk/Source/WebCore/platform/mac/PasteboardMac.mm	2020-06-03 19:25:43 UTC (rev 262506)
+++ trunk/Source/WebCore/platform/mac/PasteboardMac.mm	2020-06-03 19:41:50 UTC (rev 262507)
@@ -281,6 +281,8 @@
         m_changeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(archiveData.get(), WebArchivePboardType, m_pasteboardName);
         m_changeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(archiveData.get(), kUTTypeWebArchive, m_pasteboardName);
     }
+    if (!pasteboardImage.dataInHTMLFormat.isEmpty())
+        m_changeCount = platformStrategies()->pasteboardStrategy()->setStringForType(pasteboardImage.dataInHTMLFormat, legacyHTMLPasteboardType(), m_pasteboardName);
     writeFileWrapperAsRTFDAttachment(fileWrapper(pasteboardImage), m_pasteboardName, m_changeCount);
 }
 
@@ -789,6 +791,11 @@
 }
 #endif
 
+bool Pasteboard::canWriteTrustworthyWebURLsPboardType()
+{
+    return true;
 }
 
+}
+
 #endif // PLATFORM(MAC)

Modified: trunk/Tools/ChangeLog (262506 => 262507)


--- trunk/Tools/ChangeLog	2020-06-03 19:25:43 UTC (rev 262506)
+++ trunk/Tools/ChangeLog	2020-06-03 19:41:50 UTC (rev 262507)
@@ -1,3 +1,74 @@
+2020-06-03  Wenson Hsieh  <[email protected]>
+
+        dataTransfer.types is empty when handling the "dragstart" event
+        https://bugs.webkit.org/show_bug.cgi?id=212685
+        <rdar://problem/61368402>
+
+        Reviewed by Andy Estes.
+
+        Adds new API tests and test infrastructure to verify that DataTransfer types and data are accessible during
+        the "dragstart" event. See below for more details.
+
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WebKitCocoa/DragAndDropTests.mm:
+        (-[TestWKWebView selectElementWithID:]):
+        (-[DragAndDropSimulator dragFromElementWithID:to:]):
+
+        Add a few (very specialized) helper methods to assist with simulating drags over the various elements in the new
+        drag and drop test harness page below (dragstart-data.html).
+
+        (runDragStartDataTestCase):
+
+        Test the following scenarios (split between three API tests) by dumping the resulting DataTransfer types and
+        their data strings, and observing the results:
+
+        - Dragging a normal text selection.
+        - Dragging a normal text selection, and then adding a URL string.
+        - Dragging a normal text selection, and then adding a custom pasteboard type.
+        - Dragging a normal text selection, but then replacing the data with just a URL string.
+        - Dragging a normal text selection, but then replacing the data with just a custom data type.
+        - Dragging an image element.
+        - Dragging an image element, and then overriding the plain text data.
+        - Dragging a link (anchor element).
+        - Dragging a link, and then adding a custom type.
+
+        * TestWebKitAPI/Tests/WebKitCocoa/dragstart-data.html: Added.
+
+        Add a new test harness to help test DataTransfer types when starting a drag. This test page can also be used as
+        a manual test harness, by simply opening the test page, starting drags on the various elements and observing the
+        output in the textarea.
+
+        * TestWebKitAPI/Tests/WebKitCocoa/dump-datatransfer-types.html:
+
+        Tweak this test page to replace the DataTransfer with custom data (rather than simply append it) by calling
+        `DataTransfer.clearData()` prior to writing the custom types.
+
+        * TestWebKitAPI/Tests/ios/DragAndDropTestsIOS.mm:
+        * TestWebKitAPI/cocoa/TestWKWebView.h:
+        * TestWebKitAPI/cocoa/TestWKWebView.mm:
+        (-[TestWKWebViewHostWindow initWithWebView:contentRect:styleMask:backing:defer:]):
+
+        Add a `__weak` reference on TestWKWebViewHostWindow back to the TestWKWebView, so that we can consult
+        `-eventTimestamp` when synthesizing mouse events on macOS during API tests.
+
+        (-[TestWKWebViewHostWindow _mouseDownAtPoint:simulatePressure:clickCount:]):
+        (-[TestWKWebViewHostWindow _mouseUpAtPoint:clickCount:]):
+        (-[TestWKWebViewHostWindow initWithWebView:frame:]):
+        (-[TestWKWebView _setUpTestWindow:]):
+        (-[TestWKWebView setEventTimestampOffset:]):
+        (-[TestWKWebView eventTimestamp]):
+
+        Add a mechanism to offset synthetic event timestamps by a given time interval (i.e. the event timestamp offset).
+
+        (-[TestWKWebView mouseMoveToPoint:withFlags:]):
+        (-[TestWKWebView _mouseEventWithType:atLocation:]):
+        (-[TestWKWebView typeCharacter:]):
+        * TestWebKitAPI/mac/DragAndDropSimulatorMac.mm:
+        (-[DragAndDropSimulator runFrom:to:]):
+
+        While simulating drag and drop on macOS, use `-setEventTimestampOffset:` to "leap forward" in time, so that the
+        150 millisecond delay when dragging a text selection doesn't prevent drags from beginning.
+
 2020-06-03  Sihui Liu  <[email protected]>
 
         Text manipulation sometimes fails to replace text in attributes

Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (262506 => 262507)


--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj	2020-06-03 19:25:43 UTC (rev 262506)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj	2020-06-03 19:41:50 UTC (rev 262507)
@@ -1102,6 +1102,7 @@
 		F469FB241F01804B00401539 /* contenteditable-and-target.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F469FB231F01803500401539 /* contenteditable-and-target.html */; };
 		F46A095A1ED8A6E600D4AA55 /* apple.gif in Copy Resources */ = {isa = PBXBuildFile; fileRef = F47D30EB1ED28619000482E1 /* apple.gif */; };
 		F46A095B1ED8A6E600D4AA55 /* gif-and-file-input.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F47D30ED1ED28A6C000482E1 /* gif-and-file-input.html */; };
+		F46BD56924870643008282D6 /* dragstart-data.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F46BD5682487062D008282D6 /* dragstart-data.html */; };
 		F47728991E4AE3C1007ABF6A /* full-page-contenteditable.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F47728981E4AE3AD007ABF6A /* full-page-contenteditable.html */; };
 		F47DFB2621A878DF00021FB6 /* data-detectors.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F47DFB2421A8704A00021FB6 /* data-detectors.html */; };
 		F4811E5921940BDE00A5E0FD /* WKWebViewEditActions.mm in Sources */ = {isa = PBXBuildFile; fileRef = F4811E5821940B4400A5E0FD /* WKWebViewEditActions.mm */; };
@@ -1318,6 +1319,7 @@
 				5714ECBB1CA8BFE400051AC8 /* DownloadRequestOriginalURLFrame.html in Copy Resources */,
 				F4A32EC41F05F3850047C544 /* dragstart-change-selection-offscreen.html in Copy Resources */,
 				F4D5E4E81F0C5D38008C1A49 /* dragstart-clear-selection.html in Copy Resources */,
+				F46BD56924870643008282D6 /* dragstart-data.html in Copy Resources */,
 				F4194AD11F5A320100ADD83F /* drop-targets.html in Copy Resources */,
 				2EB29D5E1F762DB90023A5F1 /* dump-datatransfer-types.html in Copy Resources */,
 				A155022C1E050D0300A24C57 /* duplicate-completion-handler-calls.html in Copy Resources */,
@@ -2767,6 +2769,7 @@
 		F46849BD1EEF58E400B937FE /* UIPasteboardTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = UIPasteboardTests.mm; sourceTree = "<group>"; };
 		F46849BF1EEF5EDC00B937FE /* rich-and-plain-text.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "rich-and-plain-text.html"; sourceTree = "<group>"; };
 		F469FB231F01803500401539 /* contenteditable-and-target.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "contenteditable-and-target.html"; sourceTree = "<group>"; };
+		F46BD5682487062D008282D6 /* dragstart-data.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "dragstart-data.html"; sourceTree = "<group>"; };
 		F47728981E4AE3AD007ABF6A /* full-page-contenteditable.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "full-page-contenteditable.html"; sourceTree = "<group>"; };
 		F47D30EB1ED28619000482E1 /* apple.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = apple.gif; sourceTree = "<group>"; };
 		F47D30ED1ED28A6C000482E1 /* gif-and-file-input.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "gif-and-file-input.html"; sourceTree = "<group>"; };
@@ -3560,6 +3563,7 @@
 				5714ECBA1CA8BFD100051AC8 /* DownloadRequestOriginalURLFrame.html */,
 				F4A32EC31F05F3780047C544 /* dragstart-change-selection-offscreen.html */,
 				F4D5E4E71F0C5D27008C1A49 /* dragstart-clear-selection.html */,
+				F46BD5682487062D008282D6 /* dragstart-data.html */,
 				F4194AD01F5A2EA500ADD83F /* drop-targets.html */,
 				2EB29D5D1F762DA50023A5F1 /* dump-datatransfer-types.html */,
 				A155022B1E050BC500A24C57 /* duplicate-completion-handler-calls.html */,

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/DragAndDropTests.mm (262506 => 262507)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/DragAndDropTests.mm	2020-06-03 19:25:43 UTC (rev 262506)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/DragAndDropTests.mm	2020-06-03 19:41:50 UTC (rev 262507)
@@ -24,6 +24,7 @@
  */
 
 #import "config.h"
+#import "Test.h"
 
 #if ENABLE(DRAG_SUPPORT) && !PLATFORM(MACCATALYST)
 
@@ -36,6 +37,33 @@
 #import <MobileCoreServices/MobileCoreServices.h>
 #endif
 
+@implementation TestWKWebView (DragAndDropTests)
+
+- (void)selectElementWithID:(NSString *)elementID
+{
+    [self evaluateJavaScript:[NSString stringWithFormat:@"getSelection().selectAllChildren(document.getElementById('%@'))", elementID] completionHandler:nil];
+    [self waitForNextPresentationUpdate];
+}
+
+@end
+
+@implementation DragAndDropSimulator (DragAndDropTests)
+
+- (void)dragFromElementWithID:(NSString *)elementID to:(CGPoint)destination
+{
+    NSArray<NSNumber *> *rectValues = [self.webView objectByEvaluatingJavaScript:[NSString stringWithFormat:@"(() => {"
+        "    const element = document.getElementById('%@');"
+        "    const bounds = element.getBoundingClientRect();"
+        "    return [bounds.left, bounds.top, bounds.width, bounds.height];"
+        "})();", elementID]];
+
+    auto bounds = CGRectMake(rectValues[0].floatValue, rectValues[1].floatValue, rectValues[2].floatValue, rectValues[3].floatValue);
+    auto midPoint = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
+    [self runFrom:midPoint to:destination];
+}
+
+@end
+
 TEST(DragAndDropTests, ModernWebArchiveType)
 {
     NSData *markupData = [@"<strong><i>Hello world</i></strong>" dataUsingEncoding:NSUTF8StringEncoding];
@@ -160,6 +188,127 @@
     EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"observedMousedown"].boolValue);
 }
 
+struct DragStartData {
+    BOOL containsFile { NO };
+    NSString *text { nil };
+    NSString *url { nil };
+    NSString *html { nil };
+    NSDictionary<NSString *, NSString *> *customData { nil };
+};
+
+static DragStartData runDragStartDataTestCase(DragAndDropSimulator *simulator, NSString *elementID)
+{
+    auto webView = [simulator webView];
+    NSString *tagName = [webView stringByEvaluatingJavaScript:[NSString stringWithFormat:@"document.getElementById('%@').tagName", elementID]];
+    if (![tagName isEqualToString:@"IMG"] && ![tagName isEqualToString:@"A"])
+        [webView selectElementWithID:elementID];
+
+    [simulator dragFromElementWithID:elementID to:CGPointMake(400, 400)];
+    RetainPtr<NSMutableDictionary<NSString *, NSString *>> allData = adoptNS([[webView objectByEvaluatingJavaScript:@"allData"] mutableCopy]);
+    DragStartData result;
+    result.text = [allData objectForKey:@"text/plain"];
+    result.url = "" objectForKey:@"text/uri-list"];
+    result.html = [allData objectForKey:@"text/html"];
+    result.containsFile = !![allData objectForKey:@"Files"];
+    [allData removeObjectForKey:@"text/plain"];
+    [allData removeObjectForKey:@"text/uri-list"];
+    [allData removeObjectForKey:@"text/html"];
+    [allData removeObjectForKey:@"Files"];
+    if ([allData count])
+        result.customData = allData.autorelease();
+    return result;
+}
+
+TEST(DragAndDropTests, DataTransferTypesOnDragStartForTextSelection)
+{
+    auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebViewFrame:CGRectMake(0, 0, 500, 500)]);
+    [[simulator webView] synchronouslyLoadTestPageNamed:@"dragstart-data"];
+
+    auto result = runDragStartDataTestCase(simulator.get(), @"regular");
+    EXPECT_WK_STREQ("Regular text", result.text);
+    EXPECT_NULL(result.url);
+    EXPECT_TRUE([result.html containsString:@"Regular text"]);
+    EXPECT_NULL(result.customData);
+    EXPECT_FALSE(result.containsFile);
+
+    result = runDragStartDataTestCase(simulator.get(), @"regular-url");
+    EXPECT_WK_STREQ("Regular text + URL data", result.text);
+    EXPECT_WK_STREQ("https://www.google.com", result.url);
+    EXPECT_TRUE([result.html containsString:@"Regular text + URL data"]);
+    EXPECT_NULL(result.customData);
+    EXPECT_FALSE(result.containsFile);
+
+    result = runDragStartDataTestCase(simulator.get(), @"regular-custom");
+    EXPECT_WK_STREQ("Regular text + custom data", result.text);
+    EXPECT_NULL(result.url);
+    EXPECT_TRUE([result.html containsString:@"Regular text + custom data"]);
+    EXPECT_WK_STREQ("Hello world", result.customData[@"text/foo"]);
+    EXPECT_FALSE(result.containsFile);
+
+    result = runDragStartDataTestCase(simulator.get(), @"url");
+    EXPECT_NULL(result.text);
+    EXPECT_WK_STREQ("https://www.google.com", result.url);
+    EXPECT_NULL([result.html containsString:@"Regular text + custom data"]);
+    EXPECT_NULL(result.customData);
+    EXPECT_FALSE(result.containsFile);
+
+    result = runDragStartDataTestCase(simulator.get(), @"custom");
+    EXPECT_NULL(result.text);
+    EXPECT_NULL(result.url);
+    EXPECT_NULL(result.html);
+    EXPECT_WK_STREQ("Hello world", result.customData[@"text/foo"]);
+    EXPECT_FALSE(result.containsFile);
+}
+
+TEST(DragAndDropTests, DataTransferTypesOnDragStartForImage)
+{
+    auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebViewFrame:CGRectMake(0, 0, 500, 500)]);
+    [[simulator webView] synchronouslyLoadTestPageNamed:@"dragstart-data"];
+
+    auto result = runDragStartDataTestCase(simulator.get(), @"image");
+    EXPECT_NULL(result.text);
+    // The URL should be nil here because the image source is a file URL, so we shoud avoid exposing it to the page.
+    EXPECT_NULL(result.url);
+#if PLATFORM(MAC)
+    EXPECT_TRUE([result.html containsString:@"<img"]);
+#else
+    EXPECT_NULL(result.html);
+#endif
+    EXPECT_NULL(result.customData);
+    EXPECT_TRUE(result.containsFile);
+
+    result = runDragStartDataTestCase(simulator.get(), @"image-text");
+    EXPECT_WK_STREQ("This is an image of Cupertino.", result.text);
+    EXPECT_NULL(result.url);
+#if PLATFORM(MAC)
+    EXPECT_TRUE([result.html containsString:@"<img"]);
+#else
+    EXPECT_NULL(result.html);
+#endif
+    EXPECT_NULL(result.customData);
+    EXPECT_TRUE(result.containsFile);
+}
+
+TEST(DragAndDropTests, DataTransferTypesOnDragStartForLink)
+{
+    auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebViewFrame:CGRectMake(0, 0, 500, 800)]);
+    [[simulator webView] synchronouslyLoadTestPageNamed:@"dragstart-data"];
+
+    auto result = runDragStartDataTestCase(simulator.get(), @"link");
+    EXPECT_NULL(result.text);
+    EXPECT_WK_STREQ("https://www.apple.com/", result.url);
+    EXPECT_NULL(result.html);
+    EXPECT_NULL(result.customData);
+    EXPECT_FALSE(result.containsFile);
+
+    result = runDragStartDataTestCase(simulator.get(), @"link-custom");
+    EXPECT_NULL(result.text);
+    EXPECT_WK_STREQ("https://www.apple.com/", result.url);
+    EXPECT_NULL(result.html);
+    EXPECT_WK_STREQ("bar", result.customData[@"text/foo"]);
+    EXPECT_FALSE(result.containsFile);
+}
+
 #if ENABLE(INPUT_TYPE_COLOR)
 
 TEST(DragAndDropTests, ColorInputToColorInput)

Added: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/dragstart-data.html (0 => 262507)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/dragstart-data.html	                        (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/dragstart-data.html	2020-06-03 19:41:50 UTC (rev 262507)
@@ -0,0 +1,109 @@
+<!DOCTYPE html>
+<html>
+<meta name="viewport" content="width=device-width">
+<head>
+<style>
+body {
+    margin: 0;
+    padding: 0;
+}
+
+h1, a, p {
+    font-size: 20px;
+    font-family: system-ui;
+}
+
+h1 {
+    user-select: all;
+    -webkit-user-select: all;
+}
+
+a {
+    display: inline-block;
+    text-decoration: none;
+}
+
+a:active {
+    color: blue;
+}
+
+textarea {
+    margin: 1em;
+    width: calc(100% - 2em);
+    font-size: 16px;
+    font-family: monospace;
+}
+</style>
+</head>
+<body>
+    <h1 id="regular">Regular text</h1>
+    <h1 id="regular-url">Regular text + URL data</h1>
+    <h1 id="regular-custom">Regular text + custom data</h1>
+    <h1 id="url">URL data only</h1>
+    <h1 id="custom">Custom data only</h1>
+    <img id="image" src=""
+    <img id="image-text" src=""
+    <a id="link" href="" Homepage</a>
+    <a id="link-custom" href="" Homepage</a>
+    <textarea rows="20" readonly id="text-output"></textarea>
+</body>
+<script>
+window.allData = {};
+const textOutput = document.getElementById("text-output");
+
+function collectDataForEvent(event, element)
+{
+    allData = {};
+    for (let type of event.dataTransfer.types)
+        allData[type] = event.dataTransfer.getData(type);
+    textOutput.value = JSON.stringify(allData, null, 4);
+}
+
+const regular = document.getElementById("regular");
+regular.addEventListener("dragstart", event => collectDataForEvent(event, regular));
+
+const regularURL = document.getElementById("regular-url");
+regularURL.addEventListener("dragstart", event => {
+    event.dataTransfer.setData("text/uri-list", "https://www.google.com");
+    collectDataForEvent(event, regularURL);
+});
+
+const regularCustom = document.getElementById("regular-custom");
+regularCustom.addEventListener("dragstart", event => {
+    event.dataTransfer.setData("text/foo", "Hello world");
+    collectDataForEvent(event, regularCustom);
+});
+
+const url = ""
+url.addEventListener("dragstart", event => {
+    event.dataTransfer.clearData();
+    event.dataTransfer.setData("text/uri-list", "https://www.google.com");
+    collectDataForEvent(event, url);
+});
+
+const custom = document.getElementById("custom");
+custom.addEventListener("dragstart", event => {
+    event.dataTransfer.clearData();
+    event.dataTransfer.setData("text/foo", "Hello world");
+    collectDataForEvent(event, custom);
+});
+
+const image = document.getElementById("image");
+image.addEventListener("dragstart", event => collectDataForEvent(event, image));
+
+const imageText = document.getElementById("image-text");
+imageText.addEventListener("dragstart", event => {
+    event.dataTransfer.setData("text/plain", "This is an image of Cupertino.");
+    collectDataForEvent(event, imageText);
+});
+
+const link = document.getElementById("link");
+link.addEventListener("dragstart", event => collectDataForEvent(event, link));
+
+const linkCustom = document.getElementById("link-custom");
+linkCustom.addEventListener("dragstart", event => {
+    event.dataTransfer.setData("text/foo", "bar");
+    collectDataForEvent(event, linkCustom);
+});
+</script>
+</html>

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/dump-datatransfer-types.html (262506 => 262507)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/dump-datatransfer-types.html	2020-06-03 19:25:43 UTC (rev 262506)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/dump-datatransfer-types.html	2020-06-03 19:41:50 UTC (rev 262507)
@@ -100,6 +100,7 @@
         return true;
 
     const pasteboard = event.dataTransfer || event.clipboardData;
+    pasteboard.clearData();
     for (const type in customData)
         pasteboard.setData(type, customData[type]);
 

Modified: trunk/Tools/TestWebKitAPI/Tests/ios/DragAndDropTestsIOS.mm (262506 => 262507)


--- trunk/Tools/TestWebKitAPI/Tests/ios/DragAndDropTestsIOS.mm	2020-06-03 19:25:43 UTC (rev 262506)
+++ trunk/Tools/TestWebKitAPI/Tests/ios/DragAndDropTestsIOS.mm	2020-06-03 19:41:50 UTC (rev 262507)
@@ -78,7 +78,7 @@
     return [NSData dataWithContentsOfURL:zipFileURL];
 }
 
-@implementation TestWKWebView (DragAndDropTests)
+@implementation TestWKWebView (DragAndDropTestsIOS)
 
 - (BOOL)editorContainsImageElement
 {

Modified: trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.h (262506 => 262507)


--- trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.h	2020-06-03 19:25:43 UTC (rev 262506)
+++ trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.h	2020-06-03 19:41:50 UTC (rev 262507)
@@ -130,6 +130,8 @@
 - (NSWindow *)hostWindow;
 - (void)typeCharacter:(char)character;
 - (void)waitForPendingMouseEvents;
+- (void)setEventTimestampOffset:(NSTimeInterval)offset;
+@property (nonatomic, readonly) NSTimeInterval eventTimestamp;
 @end
 #endif
 

Modified: trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.mm (262506 => 262507)


--- trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.mm	2020-06-03 19:25:43 UTC (rev 262506)
+++ trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.mm	2020-06-03 19:41:50 UTC (rev 262507)
@@ -271,9 +271,11 @@
 
 @implementation TestWKWebViewHostWindow {
     BOOL _forceKeyWindow;
+    __weak TestWKWebView *_webView;
 }
 
 #if PLATFORM(MAC)
+
 static int gEventNumber = 1;
 
 NSEventMask __simulated_forceClickAssociatedEventsMask(id self, SEL _cmd)
@@ -281,6 +283,13 @@
     return NSEventMaskPressure | NSEventMaskLeftMouseDown | NSEventMaskLeftMouseUp | NSEventMaskLeftMouseDragged;
 }
 
+- (instancetype)initWithWebView:(TestWKWebView *)webView contentRect:(NSRect)contentRect styleMask:(NSWindowStyleMask)style backing:(NSBackingStoreType)backingStoreType defer:(BOOL)flag
+{
+    if (self = [super initWithContentRect:contentRect styleMask:style backing:backingStoreType defer:flag])
+        _webView = webView;
+    return self;
+}
+
 - (void)_mouseDownAtPoint:(NSPoint)point simulatePressure:(BOOL)simulatePressure clickCount:(NSUInteger)clickCount
 {
     NSEventType mouseEventType = NSEventTypeLeftMouseDown;
@@ -289,7 +298,7 @@
     if (simulatePressure)
         modifierFlags |= NSEventMaskPressure;
 
-    NSEvent *event = [NSEvent mouseEventWithType:mouseEventType location:point modifierFlags:modifierFlags timestamp:GetCurrentEventTime() windowNumber:self.windowNumber context:[NSGraphicsContext currentContext] eventNumber:++gEventNumber clickCount:clickCount pressure:simulatePressure];
+    NSEvent *event = [NSEvent mouseEventWithType:mouseEventType location:point modifierFlags:modifierFlags timestamp:_webView.eventTimestamp windowNumber:self.windowNumber context:[NSGraphicsContext currentContext] eventNumber:++gEventNumber clickCount:clickCount pressure:simulatePressure];
     if (!simulatePressure) {
         [self sendEvent:event];
         return;
@@ -309,10 +318,11 @@
 
 - (void)_mouseUpAtPoint:(NSPoint)point clickCount:(NSUInteger)clickCount
 {
-    [self sendEvent:[NSEvent mouseEventWithType:NSEventTypeLeftMouseUp location:point modifierFlags:0 timestamp:GetCurrentEventTime() windowNumber:self.windowNumber context:[NSGraphicsContext currentContext] eventNumber:++gEventNumber clickCount:clickCount pressure:0]];
+    [self sendEvent:[NSEvent mouseEventWithType:NSEventTypeLeftMouseUp location:point modifierFlags:0 timestamp:_webView.eventTimestamp windowNumber:self.windowNumber context:[NSGraphicsContext currentContext] eventNumber:++gEventNumber clickCount:clickCount pressure:0]];
 }
-#endif // PLATFORM(MAC)
 
+#endif
+
 - (BOOL)isKeyWindow
 {
     return _forceKeyWindow || [super isKeyWindow];
@@ -319,6 +329,7 @@
 }
 
 #if PLATFORM(IOS_FAMILY)
+
 static NeverDestroyed<RetainPtr<UIWindow>> gOverriddenApplicationKeyWindow;
 static NeverDestroyed<std::unique_ptr<InstanceMethodSwizzler>> gApplicationKeyWindowSwizzler;
 static NeverDestroyed<std::unique_ptr<InstanceMethodSwizzler>> gSharedApplicationSwizzler;
@@ -345,6 +356,14 @@
 {
     return gOverriddenApplicationKeyWindow.get().get();
 }
+
+- (instancetype)initWithWebView:(TestWKWebView *)webView frame:(CGRect)frame
+{
+    if (self = [super initWithFrame:frame])
+        _webView = webView;
+    return self;
+}
+
 #endif
 
 - (void)makeKeyWindow
@@ -388,6 +407,9 @@
     std::unique_ptr<ClassMethodSwizzler> _sharedCalloutBarSwizzler;
     InputSessionChangeCount _inputSessionChangeCount;
 #endif
+#if PLATFORM(MAC)
+    NSTimeInterval _eventTimestampOffset;
+#endif
 }
 
 - (instancetype)initWithFrame:(CGRect)frame
@@ -437,7 +459,7 @@
 - (void)_setUpTestWindow:(NSRect)frame
 {
 #if PLATFORM(MAC)
-    _hostWindow = adoptNS([[TestWKWebViewHostWindow alloc] initWithContentRect:frame styleMask:(NSWindowStyleMaskBorderless | NSWindowStyleMaskMiniaturizable) backing:NSBackingStoreBuffered defer:NO]);
+    _hostWindow = adoptNS([[TestWKWebViewHostWindow alloc] initWithWebView:self contentRect:frame styleMask:(NSWindowStyleMaskBorderless | NSWindowStyleMaskMiniaturizable) backing:NSBackingStoreBuffered defer:NO]);
     [_hostWindow setFrameOrigin:NSMakePoint(0, 0)];
     [_hostWindow setIsVisible:YES];
     [_hostWindow contentView].wantsLayer = YES;
@@ -444,7 +466,7 @@
     [[_hostWindow contentView] addSubview:self];
     [_hostWindow makeKeyAndOrderFront:self];
 #else
-    _hostWindow = adoptNS([[TestWKWebViewHostWindow alloc] initWithFrame:frame]);
+    _hostWindow = adoptNS([[TestWKWebViewHostWindow alloc] initWithWebView:self frame:frame]);
     [_hostWindow setHidden:NO];
     [_hostWindow addSubview:self];
 #endif
@@ -684,7 +706,19 @@
 #endif
 
 #if PLATFORM(MAC)
+
 @implementation TestWKWebView (MacOnly)
+
+- (void)setEventTimestampOffset:(NSTimeInterval)offset
+{
+    _eventTimestampOffset += offset;
+}
+
+- (NSTimeInterval)eventTimestamp
+{
+    return GetCurrentEventTime() + _eventTimestampOffset;
+}
+
 - (void)mouseDownAtPoint:(NSPoint)pointInWindow simulatePressure:(BOOL)simulatePressure
 {
     [_hostWindow _mouseDownAtPoint:pointInWindow simulatePressure:simulatePressure clickCount:1];
@@ -697,7 +731,7 @@
 
 - (void)mouseMoveToPoint:(NSPoint)pointInWindow withFlags:(NSEventModifierFlags)flags
 {
-    [self mouseMoved:[self _mouseEventWithType:NSEventTypeMouseMoved atLocation:pointInWindow flags:flags timestamp:GetCurrentEventTime() clickCount:0]];
+    [self mouseMoved:[self _mouseEventWithType:NSEventTypeMouseMoved atLocation:pointInWindow flags:flags timestamp:self.eventTimestamp clickCount:0]];
 }
 
 - (void)sendClicksAtPoint:(NSPoint)pointInWindow numberOfClicks:(NSUInteger)numberOfClicks
@@ -725,7 +759,7 @@
 
 - (NSEvent *)_mouseEventWithType:(NSEventType)type atLocation:(NSPoint)pointInWindow
 {
-    return [self _mouseEventWithType:type atLocation:pointInWindow flags:0 timestamp:GetCurrentEventTime() clickCount:0];
+    return [self _mouseEventWithType:type atLocation:pointInWindow flags:0 timestamp:self.eventTimestamp clickCount:0];
 }
 
 - (NSEvent *)_mouseEventWithType:(NSEventType)type atLocation:(NSPoint)locationInWindow flags:(NSEventModifierFlags)flags timestamp:(NSTimeInterval)timestamp clickCount:(NSUInteger)clickCount
@@ -749,8 +783,8 @@
     NSString *characterAsString = [NSString stringWithFormat:@"%c" , character];
     NSEventType keyDownEventType = NSEventTypeKeyDown;
     NSEventType keyUpEventType = NSEventTypeKeyUp;
-    [self keyDown:[NSEvent keyEventWithType:keyDownEventType location:NSZeroPoint modifierFlags:0 timestamp:GetCurrentEventTime() windowNumber:[_hostWindow windowNumber] context:nil characters:characterAsString charactersIgnoringModifiers:characterAsString isARepeat:NO keyCode:character]];
-    [self keyUp:[NSEvent keyEventWithType:keyUpEventType location:NSZeroPoint modifierFlags:0 timestamp:GetCurrentEventTime() windowNumber:[_hostWindow windowNumber] context:nil characters:characterAsString charactersIgnoringModifiers:characterAsString isARepeat:NO keyCode:character]];
+    [self keyDown:[NSEvent keyEventWithType:keyDownEventType location:NSZeroPoint modifierFlags:0 timestamp:self.eventTimestamp windowNumber:[_hostWindow windowNumber] context:nil characters:characterAsString charactersIgnoringModifiers:characterAsString isARepeat:NO keyCode:character]];
+    [self keyUp:[NSEvent keyEventWithType:keyUpEventType location:NSZeroPoint modifierFlags:0 timestamp:self.eventTimestamp windowNumber:[_hostWindow windowNumber] context:nil characters:characterAsString charactersIgnoringModifiers:characterAsString isARepeat:NO keyCode:character]];
 }
 
 - (void)waitForPendingMouseEvents
@@ -763,6 +797,7 @@
 }
 
 @end
+
 #endif // PLATFORM(MAC)
 
 #if PLATFORM(IOS_FAMILY)

Modified: trunk/Tools/TestWebKitAPI/mac/DragAndDropSimulatorMac.mm (262506 => 262507)


--- trunk/Tools/TestWebKitAPI/mac/DragAndDropSimulatorMac.mm	2020-06-03 19:25:43 UTC (rev 262506)
+++ trunk/Tools/TestWebKitAPI/mac/DragAndDropSimulatorMac.mm	2020-06-03 19:41:50 UTC (rev 262507)
@@ -177,6 +177,8 @@
     [_webView mouseEnterAtPoint:_startLocationInWindow];
     [_webView mouseMoveToPoint:_startLocationInWindow withFlags:0];
     [_webView mouseDownAtPoint:_startLocationInWindow simulatePressure:NO];
+    // Make sure that we exceed the minimum 150ms delay between handling mousedown and drag when dragging a text selection.
+    [_webView setEventTimestampOffset:0.25];
     [_webView mouseDragToPoint:[self locationInViewForCurrentProgress]];
     [_webView waitForPendingMouseEvents];
 
@@ -186,6 +188,7 @@
     [_webView waitForPendingMouseEvents];
 
     TestWebKitAPI::Util::run(&_doneWaitingForDrop);
+    [_webView setEventTimestampOffset:0];
 }
 
 - (void)beginDraggingSessionInWebView:(DragAndDropTestWKWebView *)webView withItems:(NSArray<NSDraggingItem *> *)items source:(id<NSDraggingSource>)source
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to