Drag event DataTransfer has unexpected types "dyn.ah62d4..."
authorwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 28 Sep 2017 02:29:16 +0000 (02:29 +0000)
committerwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 28 Sep 2017 02:29:16 +0000 (02:29 +0000)
https://bugs.webkit.org/show_bug.cgi?id=172526
<rdar://problem/32396081>

Reviewed by Ryosuke Niwa.

Source/WebCore:

Currently, the pasteboard types we expose to web content are simply the types that appear on the platform
pasteboard (i.e. the general NSPasteboard on Mac, and either the general UIPasteboard or a UIDragSession's
NSItemProviders on iOS). This leads to DataTransfer.types exposing many private pasteboard types written by apps
around the system to the page, such as dynamic UTIs, CorePasteboardFlavorTypes, or the "Apple WebKit dummy
pasteboard type". These are not only confusing and not useful for web content (since they mostly hold values of
empty string anyways), but can additionally pose privacy concerns by exposing information meant only for native
applications to unvetted web content.

To address this problem, other browsers (e.g. Chrome and Firefox on Mac) white-list MIME types in DataTransfer's
list of types. By default, when dragging or copying, these are "text/plain", "text/html" and "text/uri-list".
However, this policy alone is insufficient, because the page may also supply its own types, in which case our
naive whitelist would prevent us from delivering them to the page. To address this additional constraint, both
Chrome and Firefox write any custom data supplied by the page to custom pasteboard UTIs
(org.chromium.drag-dummy-type and org.mozilla.custom-clipdata, respectively). The corresponding data is a map
of custom UTI => custom data supplied by the page; upon drop or paste, this mapping is consulted if the page
calls getData() with a custom UTI.

This patch adopts this same approach in WebKit, and introduces the com.apple.WebKit.custom-pasteboard-data UTI
(refer to per-method comments below for more information). These changes are covered by 18 new layout and API
tests, as well as existing drag-and-drop tests.

Tests: editing/pasteboard/data-transfer-get-data-on-drop-custom.html
       editing/pasteboard/data-transfer-get-data-on-drop-plain-text.html
       editing/pasteboard/data-transfer-get-data-on-drop-rich-text.html
       editing/pasteboard/data-transfer-get-data-on-drop-url.html
       editing/pasteboard/data-transfer-get-data-on-paste-custom.html
       editing/pasteboard/data-transfer-get-data-on-paste-plain-text.html
       editing/pasteboard/data-transfer-get-data-on-paste-rich-text.html
       DataInteractionTests.DataTransferGetDataWhenDroppingPlainText
       DataInteractionTests.DataTransferGetDataWhenDroppingCustomData
       DataInteractionTests.DataTransferGetDataWhenDroppingURL
       DataInteractionTests.DataTransferGetDataWhenDroppingImageWithFileURL
       DataInteractionTests.DataTransferGetDataWhenDroppingRespectsPresentationStyle
       DataInteractionTests.DataTransferSetDataCannotWritePlatformTypes
       DataInteractionTests.DataTransferGetDataCannotReadPrivatePlatformTypes
       UIPasteboardTests.DataTransferGetDataWhenPastingURL
       UIPasteboardTests.DataTransferGetDataWhenPastingPlatformRepresentations
       UIPasteboardTests.DataTransferSetDataCannotWritePlatformTypes
       UIPasteboardTests.DataTransferGetDataCannotReadPrivatePlatformTypes

* CMakeLists.txt:

Add Pasteboard.cpp to the WebCore CMakeList.

* WebCore.xcodeproj/project.pbxproj:
* dom/DataTransfer.cpp:
(WebCore::DataTransfer::getData const):
(WebCore::DataTransfer::createForDragStartEvent):

Make a new static helper function to create a StaticPasteboard-backed DataTransfer when dispatching a dragstart
event. Any data supplied by the page will be written to the static pasteboard of this DataTransfer.

(WebCore::DataTransfer::moveDragState):

Add a new helper on DataTransfer to transfer the data required to initiate a drag from one DataTransfer to
another. This is used in EventHandler to transfer the contents of the temporary DataTransfer modified by the
page during the dragstart event over to the DataTransfer used for the rest of the drag initiation codepath,
which is actually connected to the platform. This includes committing the contents of the other
DataTransfer's StaticPasteboard to the new platform-connected Pasteboard.

(WebCore::DataTransfer::hasDragImage const):
* dom/DataTransfer.h:
* editing/cocoa/EditorCocoa.mm:
(WebCore::Editor::selectionInHTMLFormat):
(WebCore::Editor::writeSelectionToPasteboard):
(WebCore::Editor::writeSelection):

Write an additional HTML markup string on iOS. We already do this for Mac, but this data previously had no use
on iOS. This is needed for to vend the "text/html" representation to the page on iOS when pasting.

* editing/mac/EditorMac.mm:
(WebCore::Editor::selectionInHTMLFormat): Deleted.
* editing/wpe/EditorWPE.cpp:
(WebCore::createFragmentFromPasteboardData):
* page/EventHandler.cpp:
(WebCore::EventHandler::dispatchDragStartEventOnSourceElement):

Renamed from dispatchDragStartEvent to dispatchDragStartEventOnSourceElement. Additionally, simplified the logic
significantly, so that we now just check to see if the StaticPasteboard exposed to the page has any data,
instead of using platform-dependent logic to compare changeCounts. We can do this because StaticPasteboard is
guaranteed to only contain content that the page has supplied during the dragstart event, since it is empty
upon initialization and cannot be written to by the rest of the platform.

(WebCore::EventHandler::handleDrag):

Tweak dispatchDragStartEvent to take a DataTransfer to expose to bindings; at the call site in handleDrag,
create a new DataTransfer backed by a StaticPasteboard that the page may mutate over the course of the dragstart
event. After dispatching to the page, move the dragging information present on the drag start DataTransfer over
to the DragState's DataTransfer. If the drag image has not been set, compute and set the default drag image
element on the DragState's DataTransfer.

(WebCore::EventHandler::dispatchDragStartEvent): Deleted.
* page/EventHandler.h:
* page/Settings.cpp:
(WebCore::Settings::customPasteboardDataEnabled):
* page/Settings.h:
(WebCore::Settings::setCustomPasteboardDataEnabled):
* page/ios/EventHandlerIOS.mm:
(WebCore::EventHandler::createDraggingDataTransfer const): Deleted.
* page/mac/EventHandlerMac.mm:
(WebCore::EventHandler::createDraggingDataTransfer const): Deleted.
* page/win/EventHandlerWin.cpp:
(WebCore::EventHandler::createDraggingDataTransfer const): Deleted.

Remove this helper on both iOS and Mac. This only called createForDrag() before, with the addition of clearing
the platform pasteboard prior to returning. Now that a StaticPasteboard is used when starting a drag, we clear
out the platform pasteboard in platform-invariant code in EventHandler::handleDrag, so these helpers are no
longer useful.

* platform/Pasteboard.cpp: Added.
(WebCore::isSafeTypeForDOMToReadAndWrite):

Add a new helper to determine whether or not a pasteboard type is one of the standard DOM-exposed types. These
are "text/plain", "text/html" and "text/uri-list".

(WebCore::sharedBufferFromCustomData):
(WebCore::customDataFromSharedBuffer):

Add helper methods to serialize and deserialize custom data. The serialized data blob consists of: (1)
versioning information, (2) a dictionary mapping each custom type to a value, and (3) a full list of types
written by the page, in the order they were written.

* platform/Pasteboard.h:

Rename Pasteboard::readString to Pasteboard::readStringForBindings, to reflect that the string being read and
the given type are exposed to and from the DOM.

* platform/PasteboardStrategy.h:
* platform/PasteboardWriterData.h:
* platform/PlatformPasteboard.h:
* platform/StaticPasteboard.cpp:

Split m_stringContents out into m_platformData and m_customData. The former contains type-to-data entries for
the DOM-exposed types, while the second contains entries that don't belong in the former.

(WebCore::StaticPasteboard::hasData):
(WebCore::StaticPasteboard::readStringForBindings):
(WebCore::StaticPasteboard::writeString):
(WebCore::StaticPasteboard::clear):
(WebCore::StaticPasteboard::commitToPasteboard):

Rather than propagate each entry to the client layer one at a time, populate a new PasteboardCustomData struct
and send it to the client layer in one go. This new struct consists of an array of types in the order they were
written by the page, a dictionary of public types (types we want to write directly to the platform pasteboard)
and private types (custom types we want to package under our custom data blob).

(WebCore::StaticPasteboard::readString): Deleted.
* platform/StaticPasteboard.h:
* platform/glib/EventHandlerGLib.cpp:
(WebCore::EventHandler::createDraggingDataTransfer const): Deleted.
* platform/gtk/PasteboardGtk.cpp:
(WebCore::Pasteboard::readStringForBindings):
(WebCore::Pasteboard::writeCustomData):
(WebCore::Pasteboard::readString): Deleted.
* platform/gtk/PlatformPasteboardGtk.cpp:
(WebCore::PlatformPasteboard::typesSafeForDOMToReadAndWrite const):
(WebCore::PlatformPasteboard::write):

Add stub implementations for new custom pasteboard data methods.

* platform/ios/AbstractPasteboard.h:
* platform/ios/PasteboardIOS.mm:
(WebCore::Pasteboard::writeCustomData):

Add new plumbing to ship a custom data (PasteboardCustomData) struct from WebCore to the client layer.

(WebCore::cocoaTypeFromHTMLClipboardType):
(WebCore::readPlatformValueAsString):
(WebCore::Pasteboard::readStringForBindings):
(WebCore::Pasteboard::types):

Rewritten to ask the client layer for DOM-exposed types rather than all types, in the case where custom
pasteboard data is enabled in Settings.

(WebCore::Pasteboard::readString): Deleted.
* platform/ios/PlatformPasteboardIOS.mm:
(WebCore::PlatformPasteboard::bufferForType):
(WebCore::PlatformPasteboard::getPathnamesForType const):
(WebCore::PlatformPasteboard::numberOfFiles const):
(WebCore::PlatformPasteboard::platformPasteboardTypeForSafeTypeForDOMToReadAndWrite):

Add a new helper to map DOM-safe pasteboard types to their platform counterparts.

(WebCore::PlatformPasteboard::write):
(WebCore::safeTypeForDOMToReadAndWriteForPlatformType):

Add a new helper to map platform pasteboard types to their DOM-safe counterparts.

(WebCore::PlatformPasteboard::typesSafeForDOMToReadAndWrite const):

Fetch a list of DOM-exposed types. On iOS, for drag and drop, we have the additional constraint of not being
able to read any data before the drop happens. This is problematic, since the page needs to know the list of
types during 'dragover' events. To support this, we instead keep the array of types in the teamData property of
the generated item provider, which is immediately available, even when dragging across different apps. Note that
we still need to check if the pasteboard contains the full custom data blob here to handle the case where we
copy on Mac and perform a continuity paste on iOS, since teamData does not exist on Mac.

(WebCore::PlatformPasteboard::readString):

Tweak to account for how UIPasteboard may return data blobs when reading values.

(WebCore::PlatformPasteboard::getPathnamesForType): Deleted.
(WebCore::PlatformPasteboard::numberOfFiles): Deleted.
* platform/ios/WebItemProviderPasteboard.h:
* platform/ios/WebItemProviderPasteboard.mm:
(-[WebItemProviderRegistrationInfoList itemProvider]):
(+[WebItemProviderLoadResult loadResultWithItemProvider:typesToLoad:]):
(-[WebItemProviderLoadResult initWithItemProvider:typesToLoad:]):
(-[WebItemProviderLoadResult typesToLoad]):
(-[WebItemProviderLoadResult setFileURL:forType:]):
(-[WebItemProviderLoadResult itemProvider]):
(-[WebItemProviderPasteboard setItemProviders:]):
(-[WebItemProviderPasteboard dataForPasteboardType:]):
(-[WebItemProviderPasteboard typeIdentifiersToLoadForRegisteredTypeIdentfiers:]):
(-[WebItemProviderPasteboard doAfterLoadingProvidedContentIntoFileURLs:synchronousTimeout:]):
(+[WebItemProviderLoadResult emptyLoadResult]): Deleted.
(+[WebItemProviderLoadResult loadResultWithFileURLMap:presentationStyle:]): Deleted.
(-[WebItemProviderLoadResult initWithFileURLMap:presentationStyle:]): Deleted.
(-[WebItemProviderPasteboard typeIdentifierToLoadForRegisteredTypeIdentfiers:]): Deleted.

In the case of drag and drop on iOS, we cannot load any data prior to performing the drop; additionally, any
attempts to load data immediately after the drop is performed in the UI process will fail. This means any and
all data that the web process may require in the future when handling the drop must be loaded out of the item
providers and saved when the drop is being handled in the UI process.

Currently, we only load the highest fidelity type we can handle (or, if we don't know what we can handle, we
select the highest fidelity representation conforming to "public.content"). This is a problematic for supporting
DataTransfer.getData() on drop on iOS, because the page can ask for any of the three web-exposed types. To
address this, we refactor WebItemProviderPasteboard to support loading multiple representations per item being
dropped. At minimum, we will load anything conforming to "public.html", "public.plain-text", "public.url", and
the new "com.apple.WebKit.custom-pasteboard-data" so we have means to answer any question that the page could
ask via DataTransfer.getData(). We additonally load the highest fidelity supported (or content-conformant) type,
if it has not already been loaded as a result of the former.

To make this possible, we refactor WebItemProviderLoadResult to take an item provider and a list of types to
load. -doAfterLoadingProvidedContentIntoFileURLs:synchronousTimeout: then creates a list of load results and
uses each one to represent the results of loading data from its item provider (i.e. a map of UTI => file URL).

* platform/mac/PasteboardMac.mm:
(WebCore::Pasteboard::writeCustomData):
(WebCore::cocoaTypeFromHTMLClipboardType):
(WebCore::readPlatformValueAsString):
(WebCore::Pasteboard::readStringForBindings):

Change readStringForBindings (formerly, readString) so that if support for custom pasteboard data is enabled,
we only allow reading directly from the platform pasteboard if the given type is DOM-safe; otherwise, we consult
the custom data blob, if it exists. Otherwise, if support for custom pasteboard data is disabled, we fall back
to our current behavior.

(WebCore::Pasteboard::types):
(WebCore::Pasteboard::readString): Deleted.
* platform/mac/PlatformPasteboardMac.mm:
(WebCore::PlatformPasteboard::numberOfFiles const):
(WebCore::PlatformPasteboard::getPathnamesForType const):
(WebCore::PlatformPasteboard::stringForType):
(WebCore::safeTypeForDOMToReadAndWriteForPlatformType):
(WebCore::PlatformPasteboard::typesSafeForDOMToReadAndWrite const):

Add support for reading DOM-exposed types and fetching DOM-exposed data off of the pasteboard. The overall idea
here is similar to iOS, though implementation details vary (e.g. no item provider support).

(WebCore::PlatformPasteboard::write):
(WebCore::PlatformPasteboard::platformPasteboardTypeForSafeTypeForDOMToReadAndWrite):
(WebCore::PlatformPasteboard::numberOfFiles): Deleted.
(WebCore::PlatformPasteboard::getPathnamesForType): Deleted.
* platform/win/PasteboardWin.cpp:
(WebCore::Pasteboard::readStringForBindings):
(WebCore::Pasteboard::writeCustomData):
(WebCore::Pasteboard::readString): Deleted.
* platform/wpe/PasteboardWPE.cpp:
(WebCore::Pasteboard::readStringForBindings):
(WebCore::Pasteboard::writeCustomData):
(WebCore::Pasteboard::readString): Deleted.
* platform/wpe/PlatformPasteboardWPE.cpp:
(WebCore::PlatformPasteboard::typesSafeForDOMToReadAndWrite const):
(WebCore::PlatformPasteboard::write):
* testing/InternalSettings.cpp:
(WebCore::InternalSettings::Backup::Backup):
(WebCore::InternalSettings::Backup::restoreTo):
(WebCore::InternalSettings::setCustomPasteboardDataEnabled):

Add a new internal settings hook for layout tests to opt in to using custom pasteboard data. By default, custom
pasteboard data is enabled only in Safari, or on applications linked on or after certain releases of iOS and
macOS.

* testing/InternalSettings.h:
* testing/InternalSettings.idl:

Source/WebKit:

Add boilerplate plumbing and encoder/decoder support for new pasteboard codepaths. See WebCore ChangeLog for
more details.

* Scripts/webkit/messages.py:
(headers_for_type):
* Shared/WebCoreArgumentCoders.cpp:
(IPC::ArgumentCoder<PasteboardCustomData>::encode):
(IPC::ArgumentCoder<PasteboardCustomData>::decode):

Add encoder/decoder support for PasteboardCustomData.

(IPC::ArgumentCoder<PasteboardWebContent>::encode):
(IPC::ArgumentCoder<PasteboardWebContent>::decode):

Encode and decode dataInHTMLFormat.

* Shared/WebCoreArgumentCoders.h:
* UIProcess/Cocoa/WebPasteboardProxyCocoa.mm:
(WebKit::WebPasteboardProxy::typesSafeForDOMToReadAndWrite):
(WebKit::WebPasteboardProxy::writeCustomData):
* UIProcess/WebPasteboardProxy.cpp:
(WebKit::WebPasteboardProxy::typesSafeForDOMToReadAndWrite):
(WebKit::WebPasteboardProxy::writeCustomData):
* UIProcess/WebPasteboardProxy.h:
* UIProcess/WebPasteboardProxy.messages.in:
* WebProcess/WebCoreSupport/WebPlatformStrategies.cpp:
(WebKit::WebPlatformStrategies::typesSafeForDOMToReadAndWrite):
(WebKit::WebPlatformStrategies::writeCustomData):
* WebProcess/WebCoreSupport/WebPlatformStrategies.h:

Source/WebKitLegacy/mac:

Adjust for changes in WebCore. See WebCore ChangeLog for more details.

* WebCoreSupport/WebPlatformStrategies.h:
* WebCoreSupport/WebPlatformStrategies.mm:
(WebPlatformStrategies::webExposedTypes):
(WebPlatformStrategies::writeCustomData):

Tools:

Adds new API tests on iOS to cover various cases of using DataTransfer.setData, DataTransfer.getData, and
DataTransfer.types, as well as their interaction with platform objects (source NSItemProviders in the case of
drag and drop, and the general UIPasteboard for copy and paste).

* TestWebKitAPI/PlatformUtilities.h:
* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WebKitCocoa/dump-datatransfer-types.html: Added.

Introduce a new API test harness that both drag-and-drop and copy-and-paste tests use to dump DataTransfer's
web-exposed types and values.

* TestWebKitAPI/Tests/ios/DataInteractionTests.mm:
(checkFirstTypeIsPresentAndSecondTypeIsMissing):
(checkJSONWithLogging):
(TestWebKitAPI::TEST):
(checkTypeIdentifierAndIsNotOtherTypeIdentifier): Deleted.
* TestWebKitAPI/Tests/ios/UIPasteboardTests.mm:
(TestWebKitAPI::checkJSONWithLogging):
(TestWebKitAPI::setUpWebViewForPasteboardTests):
(TestWebKitAPI::TEST):
* TestWebKitAPI/cocoa/PlatformUtilitiesCocoa.mm:
(TestWebKitAPI::Util::jsonMatchesExpectedValues):

LayoutTests:

Add new layout tests on Mac and iOS to test various cases of using DataTransfer.setData, DataTransfer.getData,
and DataTransfer.types for drag-and-drop (tests for Mac WK1 only) and copy-and-paste (all platforms).

* TestExpectations:
* editing/pasteboard/data-transfer-get-data-on-drop-custom-expected.txt: Added.
* editing/pasteboard/data-transfer-get-data-on-drop-custom.html: Added.
* editing/pasteboard/data-transfer-get-data-on-drop-plain-text-expected.txt: Added.
* editing/pasteboard/data-transfer-get-data-on-drop-plain-text.html: Added.
* editing/pasteboard/data-transfer-get-data-on-drop-rich-text-expected.txt: Added.
* editing/pasteboard/data-transfer-get-data-on-drop-rich-text.html: Added.
* editing/pasteboard/data-transfer-get-data-on-drop-url-expected.txt: Added.
* editing/pasteboard/data-transfer-get-data-on-drop-url.html: Added.
* editing/pasteboard/data-transfer-get-data-on-paste-custom-expected.txt: Added.
* editing/pasteboard/data-transfer-get-data-on-paste-custom.html: Added.
* editing/pasteboard/data-transfer-get-data-on-paste-plain-text-expected.txt: Added.
* editing/pasteboard/data-transfer-get-data-on-paste-plain-text.html: Added.
* editing/pasteboard/data-transfer-get-data-on-paste-rich-text-expected.txt: Added.
* editing/pasteboard/data-transfer-get-data-on-paste-rich-text.html: Added.
* platform/ios-simulator-wk1/TestExpectations:

Mark new copy and paste tests as [ Pass ], since editing/pasteboard/ is skipped by default for iOS WK1.

* platform/ios-wk1/editing/pasteboard/data-transfer-get-data-on-paste-rich-text-expected.txt: Added.
* platform/ios-wk2/editing/pasteboard/data-transfer-get-data-on-paste-rich-text-expected.txt: Added.

Add iOS-specific baselines. This is due to the generated HTML markup for "text/html" being slightly different
when pasting.

* platform/mac-wk1/TestExpectations:

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

78 files changed:
LayoutTests/ChangeLog
LayoutTests/TestExpectations
LayoutTests/editing/pasteboard/data-transfer-get-data-on-drop-custom-expected.txt [new file with mode: 0644]
LayoutTests/editing/pasteboard/data-transfer-get-data-on-drop-custom.html [new file with mode: 0644]
LayoutTests/editing/pasteboard/data-transfer-get-data-on-drop-plain-text-expected.txt [new file with mode: 0644]
LayoutTests/editing/pasteboard/data-transfer-get-data-on-drop-plain-text.html [new file with mode: 0644]
LayoutTests/editing/pasteboard/data-transfer-get-data-on-drop-rich-text-expected.txt [new file with mode: 0644]
LayoutTests/editing/pasteboard/data-transfer-get-data-on-drop-rich-text.html [new file with mode: 0644]
LayoutTests/editing/pasteboard/data-transfer-get-data-on-drop-url-expected.txt [new file with mode: 0644]
LayoutTests/editing/pasteboard/data-transfer-get-data-on-drop-url.html [new file with mode: 0644]
LayoutTests/editing/pasteboard/data-transfer-get-data-on-paste-custom-expected.txt [new file with mode: 0644]
LayoutTests/editing/pasteboard/data-transfer-get-data-on-paste-custom.html [new file with mode: 0644]
LayoutTests/editing/pasteboard/data-transfer-get-data-on-paste-plain-text-expected.txt [new file with mode: 0644]
LayoutTests/editing/pasteboard/data-transfer-get-data-on-paste-plain-text.html [new file with mode: 0644]
LayoutTests/editing/pasteboard/data-transfer-get-data-on-paste-rich-text-expected.txt [new file with mode: 0644]
LayoutTests/editing/pasteboard/data-transfer-get-data-on-paste-rich-text.html [new file with mode: 0644]
LayoutTests/platform/ios-simulator-wk1/TestExpectations
LayoutTests/platform/ios-wk1/editing/pasteboard/data-transfer-get-data-on-paste-rich-text-expected.txt [new file with mode: 0644]
LayoutTests/platform/ios-wk2/editing/pasteboard/data-transfer-get-data-on-paste-rich-text-expected.txt [new file with mode: 0644]
LayoutTests/platform/mac-wk1/TestExpectations
Source/WebCore/CMakeLists.txt
Source/WebCore/ChangeLog
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/dom/DataTransfer.cpp
Source/WebCore/dom/DataTransfer.h
Source/WebCore/editing/cocoa/EditorCocoa.mm
Source/WebCore/editing/mac/EditorMac.mm
Source/WebCore/editing/wpe/EditorWPE.cpp
Source/WebCore/page/EventHandler.cpp
Source/WebCore/page/EventHandler.h
Source/WebCore/page/Settings.cpp
Source/WebCore/page/Settings.h
Source/WebCore/page/ios/EventHandlerIOS.mm
Source/WebCore/page/mac/EventHandlerMac.mm
Source/WebCore/page/win/EventHandlerWin.cpp
Source/WebCore/platform/Pasteboard.cpp [new file with mode: 0644]
Source/WebCore/platform/Pasteboard.h
Source/WebCore/platform/PasteboardStrategy.h
Source/WebCore/platform/PasteboardWriterData.h
Source/WebCore/platform/PlatformPasteboard.h
Source/WebCore/platform/StaticPasteboard.cpp
Source/WebCore/platform/StaticPasteboard.h
Source/WebCore/platform/glib/EventHandlerGLib.cpp
Source/WebCore/platform/gtk/PasteboardGtk.cpp
Source/WebCore/platform/gtk/PlatformPasteboardGtk.cpp
Source/WebCore/platform/ios/AbstractPasteboard.h
Source/WebCore/platform/ios/PasteboardIOS.mm
Source/WebCore/platform/ios/PlatformPasteboardIOS.mm
Source/WebCore/platform/ios/WebItemProviderPasteboard.h
Source/WebCore/platform/ios/WebItemProviderPasteboard.mm
Source/WebCore/platform/mac/PasteboardMac.mm
Source/WebCore/platform/mac/PlatformPasteboardMac.mm
Source/WebCore/platform/win/PasteboardWin.cpp
Source/WebCore/platform/wpe/PasteboardWPE.cpp
Source/WebCore/platform/wpe/PlatformPasteboardWPE.cpp
Source/WebCore/testing/InternalSettings.cpp
Source/WebCore/testing/InternalSettings.h
Source/WebCore/testing/InternalSettings.idl
Source/WebKit/ChangeLog
Source/WebKit/Scripts/webkit/messages.py
Source/WebKit/Shared/WebCoreArgumentCoders.cpp
Source/WebKit/Shared/WebCoreArgumentCoders.h
Source/WebKit/UIProcess/Cocoa/WebPasteboardProxyCocoa.mm
Source/WebKit/UIProcess/WebPasteboardProxy.cpp
Source/WebKit/UIProcess/WebPasteboardProxy.h
Source/WebKit/UIProcess/WebPasteboardProxy.messages.in
Source/WebKit/WebProcess/WebCoreSupport/WebPlatformStrategies.cpp
Source/WebKit/WebProcess/WebCoreSupport/WebPlatformStrategies.h
Source/WebKitLegacy/mac/ChangeLog
Source/WebKitLegacy/mac/WebCoreSupport/WebPlatformStrategies.h
Source/WebKitLegacy/mac/WebCoreSupport/WebPlatformStrategies.mm
Tools/ChangeLog
Tools/TestWebKitAPI/PlatformUtilities.h
Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
Tools/TestWebKitAPI/Tests/WebKitCocoa/dump-datatransfer-types.html [new file with mode: 0644]
Tools/TestWebKitAPI/Tests/ios/DataInteractionTests.mm
Tools/TestWebKitAPI/Tests/ios/UIPasteboardTests.mm
Tools/TestWebKitAPI/cocoa/PlatformUtilitiesCocoa.mm

index db22ade..1d1d20e 100644 (file)
@@ -1,3 +1,41 @@
+2017-09-27  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        Drag event DataTransfer has unexpected types "dyn.ah62d4..."
+        https://bugs.webkit.org/show_bug.cgi?id=172526
+        <rdar://problem/32396081>
+
+        Reviewed by Ryosuke Niwa.
+
+        Add new layout tests on Mac and iOS to test various cases of using DataTransfer.setData, DataTransfer.getData,
+        and DataTransfer.types for drag-and-drop (tests for Mac WK1 only) and copy-and-paste (all platforms).
+
+        * TestExpectations:
+        * editing/pasteboard/data-transfer-get-data-on-drop-custom-expected.txt: Added.
+        * editing/pasteboard/data-transfer-get-data-on-drop-custom.html: Added.
+        * editing/pasteboard/data-transfer-get-data-on-drop-plain-text-expected.txt: Added.
+        * editing/pasteboard/data-transfer-get-data-on-drop-plain-text.html: Added.
+        * editing/pasteboard/data-transfer-get-data-on-drop-rich-text-expected.txt: Added.
+        * editing/pasteboard/data-transfer-get-data-on-drop-rich-text.html: Added.
+        * editing/pasteboard/data-transfer-get-data-on-drop-url-expected.txt: Added.
+        * editing/pasteboard/data-transfer-get-data-on-drop-url.html: Added.
+        * editing/pasteboard/data-transfer-get-data-on-paste-custom-expected.txt: Added.
+        * editing/pasteboard/data-transfer-get-data-on-paste-custom.html: Added.
+        * editing/pasteboard/data-transfer-get-data-on-paste-plain-text-expected.txt: Added.
+        * editing/pasteboard/data-transfer-get-data-on-paste-plain-text.html: Added.
+        * editing/pasteboard/data-transfer-get-data-on-paste-rich-text-expected.txt: Added.
+        * editing/pasteboard/data-transfer-get-data-on-paste-rich-text.html: Added.
+        * platform/ios-simulator-wk1/TestExpectations:
+
+        Mark new copy and paste tests as [ Pass ], since editing/pasteboard/ is skipped by default for iOS WK1.
+
+        * platform/ios-wk1/editing/pasteboard/data-transfer-get-data-on-paste-rich-text-expected.txt: Added.
+        * platform/ios-wk2/editing/pasteboard/data-transfer-get-data-on-paste-rich-text-expected.txt: Added.
+
+        Add iOS-specific baselines. This is due to the generated HTML markup for "text/html" being slightly different
+        when pasting.
+
+        * platform/mac-wk1/TestExpectations:
+
 2017-09-27  Zalan Bujtas  <zalan@apple.com>
 
         Deferred image size change makes image-load-on-delay.html flaky.
index ce62316..dc92265 100644 (file)
@@ -67,6 +67,10 @@ fast/events/autoscroll-main-document.html [ Skip ]
 
 # Drag and drop via EventSender is only supported on Mac WK1
 editing/pasteboard/drag-drop-href-as-text-data.html [ Skip ]
+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 ]
 
 # Only iOS supports QuickLook
 quicklook [ Skip ]
diff --git a/LayoutTests/editing/pasteboard/data-transfer-get-data-on-drop-custom-expected.txt b/LayoutTests/editing/pasteboard/data-transfer-get-data-on-drop-custom-expected.txt
new file mode 100644 (file)
index 0000000..9991433
--- /dev/null
@@ -0,0 +1,21 @@
+Rich text
+{
+    "dragover": {
+        "text/plain": "",
+        "foo/🤔👌🙃": "",
+        "text/html": "",
+        "bar/מקור השם עברית": "",
+        "text/uri-list": "",
+        "baz/年年年": "",
+        "text/rtf": ""
+    },
+    "drop": {
+        "text/plain": "ben bitdiddle",
+        "foo/🤔👌🙃": "🤔👌🙃",
+        "text/html": "<b>年年年</b>",
+        "bar/מקור השם עברית": "<i>מקור השם עברית</i>",
+        "text/uri-list": "https://www.apple.com/",
+        "baz/年年年": "https://www.webkit.org",
+        "text/rtf": "AAAAAAAAAAA"
+    }
+}
diff --git a/LayoutTests/editing/pasteboard/data-transfer-get-data-on-drop-custom.html b/LayoutTests/editing/pasteboard/data-transfer-get-data-on-drop-custom.html
new file mode 100644 (file)
index 0000000..e2e1a79
--- /dev/null
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<html>
+<style>
+html, body {
+    margin: 0;
+    font-family: -apple-system;
+}
+
+#source, #destination {
+    width: 100%;
+    margin: 0;
+}
+
+#destination {
+    border: 1px blue green;
+    height: 1024px;
+}
+
+#source {
+    font-size: 150px;
+    white-space: nowrap;
+    height: 200px;
+}
+</style>
+<body>
+    <div draggable="true" id="source"><strong style="color: purple">Rich text</strong></div>
+    <div id="destination" contenteditable></div>
+    <pre id="output"></pre>
+</body>
+<script>
+
+// The contents of this `result` dictionary will contain a map of {event type => {MIME type => data}}.
+result = {};
+
+function updateResultWithEvent(event) {
+    const eventData = {};
+    for (const type of event.dataTransfer.types)
+        eventData[type] = event.dataTransfer.getData(type);
+    result[event.type] = eventData;
+    if (event.type === "drop")
+        output.textContent = JSON.stringify(result, null, "    ");
+    event.preventDefault();
+}
+
+function setCustomData(event) {
+    event.dataTransfer.setData("text/plain", "ben bitdiddle");
+    event.dataTransfer.setData("foo/🤔👌🙃", "🤔👌🙃");
+    event.dataTransfer.setData("text/html", "<b>年年年</b>");
+    event.dataTransfer.setData("bar/מקור השם עברית", `<i>מקור השם עברית</i>`);
+    event.dataTransfer.setData("text/uri-list", "https://www.apple.com");
+    event.dataTransfer.setData("baz/年年年", "https://www.webkit.org");
+    event.dataTransfer.setData("text/rtf", "AAAAAAAAAAA");
+}
+
+source.addEventListener("dragstart", setCustomData);
+destination.addEventListener("dragover", updateResultWithEvent);
+destination.addEventListener("drop", updateResultWithEvent);
+
+if (window.testRunner && window.eventSender && window.internals) {
+    internals.settings.setCustomPasteboardDataEnabled(true);
+    testRunner.dumpAsText();
+    eventSender.mouseMoveTo(100, 100);
+    eventSender.mouseDown();
+    eventSender.leapForward(500);
+    eventSender.mouseMoveTo(100, 400);
+    eventSender.mouseUp();
+}
+</script>
+</html>
diff --git a/LayoutTests/editing/pasteboard/data-transfer-get-data-on-drop-plain-text-expected.txt b/LayoutTests/editing/pasteboard/data-transfer-get-data-on-drop-plain-text-expected.txt
new file mode 100644 (file)
index 0000000..97b229b
--- /dev/null
@@ -0,0 +1,9 @@
+
+{
+    "dragover": {
+        "text/plain": ""
+    },
+    "drop": {
+        "text/plain": "Plain text"
+    }
+}
diff --git a/LayoutTests/editing/pasteboard/data-transfer-get-data-on-drop-plain-text.html b/LayoutTests/editing/pasteboard/data-transfer-get-data-on-drop-plain-text.html
new file mode 100644 (file)
index 0000000..0c849e2
--- /dev/null
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html>
+<style>
+html, body {
+    margin: 0;
+    font-family: -apple-system;
+}
+
+#source, #destination {
+    width: 100%;
+    margin: 0;
+}
+
+#destination {
+    border: 1px blue green;
+    height: 1024px;
+}
+
+#source {
+    font-size: 150px;
+    white-space: nowrap;
+    height: 200px;
+}
+</style>
+<body>
+    <textarea id="source">Plain text</textarea>
+    <div id="destination" contenteditable></div>
+    <pre id="output"></pre>
+</body>
+<script>
+
+// The contents of this `result` dictionary will contain a map of {event type => {MIME type => data}}.
+result = {};
+
+function updateResultWithEvent(event) {
+    const eventData = {};
+    for (const type of event.dataTransfer.types)
+        eventData[type] = event.dataTransfer.getData(type);
+    result[event.type] = eventData;
+    if (event.type === "drop")
+        output.textContent = JSON.stringify(result, null, "    ");
+    event.preventDefault();
+}
+
+destination.addEventListener("dragover", updateResultWithEvent);
+destination.addEventListener("drop", updateResultWithEvent);
+
+source.setSelectionRange(0, source.value.length);
+
+if (window.testRunner && window.eventSender && window.internals) {
+    internals.settings.setCustomPasteboardDataEnabled(true);
+    testRunner.dumpAsText();
+    eventSender.mouseMoveTo(100, 100);
+    eventSender.mouseDown();
+    eventSender.leapForward(500);
+    eventSender.mouseMoveTo(100, 400);
+    eventSender.mouseUp();
+}
+</script>
+</html>
diff --git a/LayoutTests/editing/pasteboard/data-transfer-get-data-on-drop-rich-text-expected.txt b/LayoutTests/editing/pasteboard/data-transfer-get-data-on-drop-rich-text-expected.txt
new file mode 100644 (file)
index 0000000..cec5b62
--- /dev/null
@@ -0,0 +1,11 @@
+Rich text
+{
+    "dragover": {
+        "text/html": "",
+        "text/plain": ""
+    },
+    "drop": {
+        "text/html": "<strong style=\"font-family: -apple-system; font-size: 150px; font-style: normal; font-variant-caps: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: nowrap; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; color: purple;\">Rich text</strong>",
+        "text/plain": "Rich text"
+    }
+}
diff --git a/LayoutTests/editing/pasteboard/data-transfer-get-data-on-drop-rich-text.html b/LayoutTests/editing/pasteboard/data-transfer-get-data-on-drop-rich-text.html
new file mode 100644 (file)
index 0000000..decbb68
--- /dev/null
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html>
+<style>
+html, body {
+    margin: 0;
+    font-family: -apple-system;
+}
+
+#source, #destination {
+    width: 100%;
+    margin: 0;
+}
+
+#destination {
+    border: 1px blue green;
+    height: 1024px;
+}
+
+#source {
+    font-size: 150px;
+    white-space: nowrap;
+    height: 200px;
+}
+</style>
+<body>
+    <div id="source"><strong style="color: purple">Rich text</strong></div>
+    <div id="destination" contenteditable></div>
+    <pre id="output"></pre>
+</body>
+<script>
+
+// The contents of this `result` dictionary will contain a map of {event type => {MIME type => data}}.
+result = {};
+
+function updateResultWithEvent(event) {
+    const eventData = {};
+    for (const type of event.dataTransfer.types)
+        eventData[type] = event.dataTransfer.getData(type);
+    result[event.type] = eventData;
+    if (event.type === "drop")
+        output.textContent = JSON.stringify(result, null, "    ");
+    event.preventDefault();
+}
+
+destination.addEventListener("dragover", updateResultWithEvent);
+destination.addEventListener("drop", updateResultWithEvent);
+
+getSelection().setBaseAndExtent(source, 0, source, 1);
+
+if (window.testRunner && window.eventSender && window.internals) {
+    internals.settings.setCustomPasteboardDataEnabled(true);
+    testRunner.dumpAsText();
+    eventSender.mouseMoveTo(100, 100);
+    eventSender.mouseDown();
+    eventSender.leapForward(500);
+    eventSender.mouseMoveTo(100, 400);
+    eventSender.mouseUp();
+}
+</script>
+</html>
diff --git a/LayoutTests/editing/pasteboard/data-transfer-get-data-on-drop-url-expected.txt b/LayoutTests/editing/pasteboard/data-transfer-get-data-on-drop-url-expected.txt
new file mode 100644 (file)
index 0000000..3a897fe
--- /dev/null
@@ -0,0 +1,11 @@
+Apple dot com
+{
+    "dragover": {
+        "text/uri-list": "",
+        "text/plain": ""
+    },
+    "drop": {
+        "text/uri-list": "https://www.apple.com/",
+        "text/plain": "https://www.apple.com/"
+    }
+}
diff --git a/LayoutTests/editing/pasteboard/data-transfer-get-data-on-drop-url.html b/LayoutTests/editing/pasteboard/data-transfer-get-data-on-drop-url.html
new file mode 100644 (file)
index 0000000..fdf25f2
--- /dev/null
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+<style>
+html, body {
+    margin: 0;
+    font-family: -apple-system;
+}
+
+#source, #destination {
+    width: 100%;
+    margin: 0;
+}
+
+#destination {
+    border: 1px blue green;
+    height: 1024px;
+}
+
+#source {
+    font-size: 150px;
+    white-space: nowrap;
+    height: 200px;
+}
+</style>
+<body>
+    <a id="source" href="https://www.apple.com">Apple dot com</a>
+    <div id="destination" contenteditable></div>
+    <pre id="output"></pre>
+</body>
+<script>
+
+// The contents of this `result` dictionary will contain a map of {event type => {MIME type => data}}.
+result = {};
+
+function updateResultWithEvent(event) {
+    const eventData = {};
+    for (const type of event.dataTransfer.types)
+        eventData[type] = event.dataTransfer.getData(type);
+    result[event.type] = eventData;
+    if (event.type === "drop")
+        output.textContent = JSON.stringify(result, null, "    ");
+    event.preventDefault();
+}
+
+destination.addEventListener("dragover", updateResultWithEvent);
+destination.addEventListener("drop", updateResultWithEvent);
+
+if (window.testRunner && window.eventSender && window.internals) {
+    internals.settings.setCustomPasteboardDataEnabled(true);
+    testRunner.dumpAsText();
+    eventSender.mouseMoveTo(100, 100);
+    eventSender.mouseDown();
+    eventSender.leapForward(500);
+    eventSender.mouseMoveTo(100, 400);
+    eventSender.mouseUp();
+}
+</script>
+</html>
diff --git a/LayoutTests/editing/pasteboard/data-transfer-get-data-on-paste-custom-expected.txt b/LayoutTests/editing/pasteboard/data-transfer-get-data-on-paste-custom-expected.txt
new file mode 100644 (file)
index 0000000..8607a4a
--- /dev/null
@@ -0,0 +1,12 @@
+Rich text
+{
+    "paste": {
+        "text/plain": "ben bitdiddle",
+        "foo/🤔👌🙃": "🤔👌🙃",
+        "text/html": "<b>年年年</b>",
+        "bar/מקור השם עברית": "<i>מקור השם עברית</i>",
+        "text/uri-list": "https://www.apple.com/",
+        "baz/年年年": "https://www.webkit.org",
+        "text/rtf": "AAAAAAAAAAA"
+    }
+}
diff --git a/LayoutTests/editing/pasteboard/data-transfer-get-data-on-paste-custom.html b/LayoutTests/editing/pasteboard/data-transfer-get-data-on-paste-custom.html
new file mode 100644 (file)
index 0000000..4290ce6
--- /dev/null
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<html>
+<style>
+html, body {
+    margin: 0;
+    font-family: -apple-system;
+}
+
+#source, #destination {
+    width: 100%;
+    margin: 0;
+}
+
+#destination {
+    border: 1px blue green;
+    height: 1024px;
+}
+
+#source {
+    font-size: 150px;
+    white-space: nowrap;
+    height: 200px;
+}
+</style>
+<body>
+    <div id="source">Rich text</div>
+    <div id="destination" contenteditable></div>
+    <pre id="output"></pre>
+</body>
+<script>
+
+// The contents of this `result` dictionary will contain a map of {event type => {MIME type => data}}.
+result = {};
+
+function updateResultWithEvent(event) {
+    const eventData = {};
+    for (const type of event.clipboardData.types)
+        eventData[type] = event.clipboardData.getData(type);
+    result[event.type] = eventData;
+    output.textContent = JSON.stringify(result, null, "    ");
+    event.preventDefault();
+}
+
+function setCustomData(event) {
+    event.clipboardData.setData("text/plain", "ben bitdiddle");
+    event.clipboardData.setData("foo/🤔👌🙃", "🤔👌🙃");
+    event.clipboardData.setData("text/html", "<b>年年年</b>");
+    event.clipboardData.setData("bar/מקור השם עברית", `<i>מקור השם עברית</i>`);
+    event.clipboardData.setData("text/uri-list", "https://www.apple.com");
+    event.clipboardData.setData("baz/年年年", "https://www.webkit.org");
+    event.clipboardData.setData("text/rtf", "AAAAAAAAAAA");
+    event.preventDefault();
+}
+
+getSelection().setBaseAndExtent(source, 0, source, 1);
+source.addEventListener("copy", setCustomData);
+destination.addEventListener("paste", updateResultWithEvent);
+
+if (window.testRunner && window.internals) {
+    internals.settings.setCustomPasteboardDataEnabled(true);
+    testRunner.dumpAsText();
+    testRunner.execCommand("Copy");
+    destination.focus();
+    testRunner.execCommand("Paste");
+}
+</script>
+</html>
diff --git a/LayoutTests/editing/pasteboard/data-transfer-get-data-on-paste-plain-text-expected.txt b/LayoutTests/editing/pasteboard/data-transfer-get-data-on-paste-plain-text-expected.txt
new file mode 100644 (file)
index 0000000..1520f0a
--- /dev/null
@@ -0,0 +1,6 @@
+
+{
+    "paste": {
+        "text/plain": "Plain text"
+    }
+}
diff --git a/LayoutTests/editing/pasteboard/data-transfer-get-data-on-paste-plain-text.html b/LayoutTests/editing/pasteboard/data-transfer-get-data-on-paste-plain-text.html
new file mode 100644 (file)
index 0000000..6430e3c
--- /dev/null
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<html>
+<style>
+html, body {
+    margin: 0;
+    font-family: -apple-system;
+}
+
+#source, #destination {
+    width: 100%;
+    margin: 0;
+}
+
+#destination {
+    border: 1px blue green;
+    height: 1024px;
+}
+
+#source {
+    font-size: 150px;
+    white-space: nowrap;
+    height: 200px;
+}
+</style>
+<body>
+    <textarea id="source">Plain text</textarea>
+    <div id="destination" contenteditable></div>
+    <pre id="output"></pre>
+</body>
+<script>
+
+// The contents of this `result` dictionary will contain a map of {event type => {MIME type => data}}.
+result = {};
+
+function updateResultWithEvent(event) {
+    const eventData = {};
+    for (const type of event.clipboardData.types)
+        eventData[type] = event.clipboardData.getData(type);
+    result[event.type] = eventData;
+    output.textContent = JSON.stringify(result, null, "    ");
+    event.preventDefault();
+}
+
+source.setSelectionRange(0, source.value.length);
+destination.addEventListener("paste", updateResultWithEvent);
+
+if (window.testRunner && window.internals) {
+    internals.settings.setCustomPasteboardDataEnabled(true);
+    testRunner.dumpAsText();
+    testRunner.execCommand("Copy");
+    destination.focus();
+    testRunner.execCommand("Paste");
+}
+</script>
+</html>
diff --git a/LayoutTests/editing/pasteboard/data-transfer-get-data-on-paste-rich-text-expected.txt b/LayoutTests/editing/pasteboard/data-transfer-get-data-on-paste-rich-text-expected.txt
new file mode 100644 (file)
index 0000000..d4b004f
--- /dev/null
@@ -0,0 +1,7 @@
+Rich text
+{
+    "paste": {
+        "text/html": "<span style=\"caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0); font-family: -apple-system; font-size: 150px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: nowrap; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; display: inline !important; float: none;\">Rich text</span>",
+        "text/plain": "Rich text"
+    }
+}
diff --git a/LayoutTests/editing/pasteboard/data-transfer-get-data-on-paste-rich-text.html b/LayoutTests/editing/pasteboard/data-transfer-get-data-on-paste-rich-text.html
new file mode 100644 (file)
index 0000000..69bb7c0
--- /dev/null
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<html>
+<style>
+html, body {
+    margin: 0;
+    font-family: -apple-system;
+}
+
+#source, #destination {
+    width: 100%;
+    margin: 0;
+}
+
+#destination {
+    border: 1px blue green;
+    height: 1024px;
+}
+
+#source {
+    font-size: 150px;
+    white-space: nowrap;
+    height: 200px;
+}
+</style>
+<body>
+    <div id="source">Rich text</div>
+    <div id="destination" contenteditable></div>
+    <pre id="output"></pre>
+</body>
+<script>
+
+// The contents of this `result` dictionary will contain a map of {event type => {MIME type => data}}.
+result = {};
+
+function updateResultWithEvent(event) {
+    const eventData = {};
+    for (const type of event.clipboardData.types)
+        eventData[type] = event.clipboardData.getData(type);
+    result[event.type] = eventData;
+    output.textContent = JSON.stringify(result, null, "    ");
+    event.preventDefault();
+}
+
+getSelection().setBaseAndExtent(source, 0, source, 1);
+destination.addEventListener("paste", updateResultWithEvent);
+
+if (window.testRunner && window.internals) {
+    internals.settings.setCustomPasteboardDataEnabled(true);
+    testRunner.dumpAsText();
+    testRunner.execCommand("Copy");
+    destination.focus();
+    testRunner.execCommand("Paste");
+}
+</script>
+</html>
index 51c63a6..a7d77c6 100644 (file)
@@ -7,3 +7,6 @@ fast/events/offsetX-offsetY.html [ Timeout ]
 
 # Untriage pasteboard test failures
 editing/pasteboard/
+editing/pasteboard/data-transfer-get-data-on-paste-custom.html [ Pass ]
+editing/pasteboard/data-transfer-get-data-on-paste-plain-text.html [ Pass ]
+editing/pasteboard/data-transfer-get-data-on-paste-rich-text.html [ Pass ]
diff --git a/LayoutTests/platform/ios-wk1/editing/pasteboard/data-transfer-get-data-on-paste-rich-text-expected.txt b/LayoutTests/platform/ios-wk1/editing/pasteboard/data-transfer-get-data-on-paste-rich-text-expected.txt
new file mode 100644 (file)
index 0000000..b3de879
--- /dev/null
@@ -0,0 +1,7 @@
+Rich text
+{
+    "paste": {
+        "text/plain": "Rich text",
+        "text/html": "<span style=\"caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0); font-family: -apple-system; font-size: 150px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: nowrap; widows: auto; word-spacing: 0px; -webkit-tap-highlight-color: rgba(26, 26, 26, 0.301961); -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; display: inline !important; float: none;\">Rich text</span>"
+    }
+}
diff --git a/LayoutTests/platform/ios-wk2/editing/pasteboard/data-transfer-get-data-on-paste-rich-text-expected.txt b/LayoutTests/platform/ios-wk2/editing/pasteboard/data-transfer-get-data-on-paste-rich-text-expected.txt
new file mode 100644 (file)
index 0000000..263144b
--- /dev/null
@@ -0,0 +1,7 @@
+Rich text
+{
+    "paste": {
+        "text/plain": "Rich text",
+        "text/html": "<span style=\"caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0); font-family: -apple-system; font-size: 150px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: nowrap; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; display: inline !important; float: none;\">Rich text</span>"
+    }
+}
index 8037333..974a806 100644 (file)
@@ -7,6 +7,10 @@
 
 fast/forms/attributed-strings.html [ Pass ]
 editing/pasteboard/drag-drop-href-as-text-data.html [ Pass ]
+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 ]
 
 #//////////////////////////////////////////////////////////////////////////////////////////
 # End platform-specific directories.
index 206328f..1e39d29 100644 (file)
@@ -2330,6 +2330,7 @@ set(WebCore_SOURCES
     platform/MIMETypeRegistry.cpp
     platform/MainThreadSharedTimer.cpp
     platform/NotImplemented.cpp
+    platform/Pasteboard.cpp
     platform/PasteboardWriterData.cpp
     platform/PlatformSpeechSynthesisUtterance.cpp
     platform/PlatformSpeechSynthesisVoice.cpp
index acdc197..80396b9 100644 (file)
@@ -1,3 +1,299 @@
+2017-09-27  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        Drag event DataTransfer has unexpected types "dyn.ah62d4..."
+        https://bugs.webkit.org/show_bug.cgi?id=172526
+        <rdar://problem/32396081>
+
+        Reviewed by Ryosuke Niwa.
+
+        Currently, the pasteboard types we expose to web content are simply the types that appear on the platform
+        pasteboard (i.e. the general NSPasteboard on Mac, and either the general UIPasteboard or a UIDragSession's
+        NSItemProviders on iOS). This leads to DataTransfer.types exposing many private pasteboard types written by apps
+        around the system to the page, such as dynamic UTIs, CorePasteboardFlavorTypes, or the "Apple WebKit dummy
+        pasteboard type". These are not only confusing and not useful for web content (since they mostly hold values of
+        empty string anyways), but can additionally pose privacy concerns by exposing information meant only for native
+        applications to unvetted web content.
+
+        To address this problem, other browsers (e.g. Chrome and Firefox on Mac) white-list MIME types in DataTransfer's
+        list of types. By default, when dragging or copying, these are "text/plain", "text/html" and "text/uri-list".
+        However, this policy alone is insufficient, because the page may also supply its own types, in which case our
+        naive whitelist would prevent us from delivering them to the page. To address this additional constraint, both
+        Chrome and Firefox write any custom data supplied by the page to custom pasteboard UTIs
+        (org.chromium.drag-dummy-type and org.mozilla.custom-clipdata, respectively). The corresponding data is a map
+        of custom UTI => custom data supplied by the page; upon drop or paste, this mapping is consulted if the page
+        calls getData() with a custom UTI.
+
+        This patch adopts this same approach in WebKit, and introduces the com.apple.WebKit.custom-pasteboard-data UTI
+        (refer to per-method comments below for more information). These changes are covered by 18 new layout and API
+        tests, as well as existing drag-and-drop tests.
+
+        Tests: editing/pasteboard/data-transfer-get-data-on-drop-custom.html
+               editing/pasteboard/data-transfer-get-data-on-drop-plain-text.html
+               editing/pasteboard/data-transfer-get-data-on-drop-rich-text.html
+               editing/pasteboard/data-transfer-get-data-on-drop-url.html
+               editing/pasteboard/data-transfer-get-data-on-paste-custom.html
+               editing/pasteboard/data-transfer-get-data-on-paste-plain-text.html
+               editing/pasteboard/data-transfer-get-data-on-paste-rich-text.html
+               DataInteractionTests.DataTransferGetDataWhenDroppingPlainText
+               DataInteractionTests.DataTransferGetDataWhenDroppingCustomData
+               DataInteractionTests.DataTransferGetDataWhenDroppingURL
+               DataInteractionTests.DataTransferGetDataWhenDroppingImageWithFileURL
+               DataInteractionTests.DataTransferGetDataWhenDroppingRespectsPresentationStyle
+               DataInteractionTests.DataTransferSetDataCannotWritePlatformTypes
+               DataInteractionTests.DataTransferGetDataCannotReadPrivatePlatformTypes
+               UIPasteboardTests.DataTransferGetDataWhenPastingURL
+               UIPasteboardTests.DataTransferGetDataWhenPastingPlatformRepresentations
+               UIPasteboardTests.DataTransferSetDataCannotWritePlatformTypes
+               UIPasteboardTests.DataTransferGetDataCannotReadPrivatePlatformTypes
+
+        * CMakeLists.txt:
+
+        Add Pasteboard.cpp to the WebCore CMakeList.
+
+        * WebCore.xcodeproj/project.pbxproj:
+        * dom/DataTransfer.cpp:
+        (WebCore::DataTransfer::getData const):
+        (WebCore::DataTransfer::createForDragStartEvent):
+
+        Make a new static helper function to create a StaticPasteboard-backed DataTransfer when dispatching a dragstart
+        event. Any data supplied by the page will be written to the static pasteboard of this DataTransfer.
+
+        (WebCore::DataTransfer::moveDragState):
+
+        Add a new helper on DataTransfer to transfer the data required to initiate a drag from one DataTransfer to
+        another. This is used in EventHandler to transfer the contents of the temporary DataTransfer modified by the
+        page during the dragstart event over to the DataTransfer used for the rest of the drag initiation codepath,
+        which is actually connected to the platform. This includes committing the contents of the other
+        DataTransfer's StaticPasteboard to the new platform-connected Pasteboard.
+
+        (WebCore::DataTransfer::hasDragImage const):
+        * dom/DataTransfer.h:
+        * editing/cocoa/EditorCocoa.mm:
+        (WebCore::Editor::selectionInHTMLFormat):
+        (WebCore::Editor::writeSelectionToPasteboard):
+        (WebCore::Editor::writeSelection):
+
+        Write an additional HTML markup string on iOS. We already do this for Mac, but this data previously had no use
+        on iOS. This is needed for to vend the "text/html" representation to the page on iOS when pasting.
+
+        * editing/mac/EditorMac.mm:
+        (WebCore::Editor::selectionInHTMLFormat): Deleted.
+        * editing/wpe/EditorWPE.cpp:
+        (WebCore::createFragmentFromPasteboardData):
+        * page/EventHandler.cpp:
+        (WebCore::EventHandler::dispatchDragStartEventOnSourceElement):
+
+        Renamed from dispatchDragStartEvent to dispatchDragStartEventOnSourceElement. Additionally, simplified the logic
+        significantly, so that we now just check to see if the StaticPasteboard exposed to the page has any data,
+        instead of using platform-dependent logic to compare changeCounts. We can do this because StaticPasteboard is
+        guaranteed to only contain content that the page has supplied during the dragstart event, since it is empty
+        upon initialization and cannot be written to by the rest of the platform.
+
+        (WebCore::EventHandler::handleDrag):
+
+        Tweak dispatchDragStartEvent to take a DataTransfer to expose to bindings; at the call site in handleDrag,
+        create a new DataTransfer backed by a StaticPasteboard that the page may mutate over the course of the dragstart
+        event. After dispatching to the page, move the dragging information present on the drag start DataTransfer over
+        to the DragState's DataTransfer. If the drag image has not been set, compute and set the default drag image
+        element on the DragState's DataTransfer.
+
+        (WebCore::EventHandler::dispatchDragStartEvent): Deleted.
+        * page/EventHandler.h:
+        * page/Settings.cpp:
+        (WebCore::Settings::customPasteboardDataEnabled):
+        * page/Settings.h:
+        (WebCore::Settings::setCustomPasteboardDataEnabled):
+        * page/ios/EventHandlerIOS.mm:
+        (WebCore::EventHandler::createDraggingDataTransfer const): Deleted.
+        * page/mac/EventHandlerMac.mm:
+        (WebCore::EventHandler::createDraggingDataTransfer const): Deleted.
+        * page/win/EventHandlerWin.cpp:
+        (WebCore::EventHandler::createDraggingDataTransfer const): Deleted.
+
+        Remove this helper on both iOS and Mac. This only called createForDrag() before, with the addition of clearing
+        the platform pasteboard prior to returning. Now that a StaticPasteboard is used when starting a drag, we clear
+        out the platform pasteboard in platform-invariant code in EventHandler::handleDrag, so these helpers are no
+        longer useful.
+
+        * platform/Pasteboard.cpp: Added.
+        (WebCore::isSafeTypeForDOMToReadAndWrite):
+
+        Add a new helper to determine whether or not a pasteboard type is one of the standard DOM-exposed types. These
+        are "text/plain", "text/html" and "text/uri-list".
+
+        (WebCore::sharedBufferFromCustomData):
+        (WebCore::customDataFromSharedBuffer):
+
+        Add helper methods to serialize and deserialize custom data. The serialized data blob consists of: (1)
+        versioning information, (2) a dictionary mapping each custom type to a value, and (3) a full list of types
+        written by the page, in the order they were written.
+
+        * platform/Pasteboard.h:
+
+        Rename Pasteboard::readString to Pasteboard::readStringForBindings, to reflect that the string being read and
+        the given type are exposed to and from the DOM.
+
+        * platform/PasteboardStrategy.h:
+        * platform/PasteboardWriterData.h:
+        * platform/PlatformPasteboard.h:
+        * platform/StaticPasteboard.cpp:
+
+        Split m_stringContents out into m_platformData and m_customData. The former contains type-to-data entries for
+        the DOM-exposed types, while the second contains entries that don't belong in the former.
+
+        (WebCore::StaticPasteboard::hasData):
+        (WebCore::StaticPasteboard::readStringForBindings):
+        (WebCore::StaticPasteboard::writeString):
+        (WebCore::StaticPasteboard::clear):
+        (WebCore::StaticPasteboard::commitToPasteboard):
+
+        Rather than propagate each entry to the client layer one at a time, populate a new PasteboardCustomData struct
+        and send it to the client layer in one go. This new struct consists of an array of types in the order they were
+        written by the page, a dictionary of public types (types we want to write directly to the platform pasteboard)
+        and private types (custom types we want to package under our custom data blob).
+
+        (WebCore::StaticPasteboard::readString): Deleted.
+        * platform/StaticPasteboard.h:
+        * platform/glib/EventHandlerGLib.cpp:
+        (WebCore::EventHandler::createDraggingDataTransfer const): Deleted.
+        * platform/gtk/PasteboardGtk.cpp:
+        (WebCore::Pasteboard::readStringForBindings):
+        (WebCore::Pasteboard::writeCustomData):
+        (WebCore::Pasteboard::readString): Deleted.
+        * platform/gtk/PlatformPasteboardGtk.cpp:
+        (WebCore::PlatformPasteboard::typesSafeForDOMToReadAndWrite const):
+        (WebCore::PlatformPasteboard::write):
+
+        Add stub implementations for new custom pasteboard data methods.
+
+        * platform/ios/AbstractPasteboard.h:
+        * platform/ios/PasteboardIOS.mm:
+        (WebCore::Pasteboard::writeCustomData):
+
+        Add new plumbing to ship a custom data (PasteboardCustomData) struct from WebCore to the client layer.
+
+        (WebCore::cocoaTypeFromHTMLClipboardType):
+        (WebCore::readPlatformValueAsString):
+        (WebCore::Pasteboard::readStringForBindings):
+        (WebCore::Pasteboard::types):
+
+        Rewritten to ask the client layer for DOM-exposed types rather than all types, in the case where custom
+        pasteboard data is enabled in Settings.
+
+        (WebCore::Pasteboard::readString): Deleted.
+        * platform/ios/PlatformPasteboardIOS.mm:
+        (WebCore::PlatformPasteboard::bufferForType):
+        (WebCore::PlatformPasteboard::getPathnamesForType const):
+        (WebCore::PlatformPasteboard::numberOfFiles const):
+        (WebCore::PlatformPasteboard::platformPasteboardTypeForSafeTypeForDOMToReadAndWrite):
+
+        Add a new helper to map DOM-safe pasteboard types to their platform counterparts.
+
+        (WebCore::PlatformPasteboard::write):
+        (WebCore::safeTypeForDOMToReadAndWriteForPlatformType):
+
+        Add a new helper to map platform pasteboard types to their DOM-safe counterparts.
+
+        (WebCore::PlatformPasteboard::typesSafeForDOMToReadAndWrite const):
+
+        Fetch a list of DOM-exposed types. On iOS, for drag and drop, we have the additional constraint of not being
+        able to read any data before the drop happens. This is problematic, since the page needs to know the list of
+        types during 'dragover' events. To support this, we instead keep the array of types in the teamData property of
+        the generated item provider, which is immediately available, even when dragging across different apps. Note that
+        we still need to check if the pasteboard contains the full custom data blob here to handle the case where we
+        copy on Mac and perform a continuity paste on iOS, since teamData does not exist on Mac.
+
+        (WebCore::PlatformPasteboard::readString):
+
+        Tweak to account for how UIPasteboard may return data blobs when reading values.
+
+        (WebCore::PlatformPasteboard::getPathnamesForType): Deleted.
+        (WebCore::PlatformPasteboard::numberOfFiles): Deleted.
+        * platform/ios/WebItemProviderPasteboard.h:
+        * platform/ios/WebItemProviderPasteboard.mm:
+        (-[WebItemProviderRegistrationInfoList itemProvider]):
+        (+[WebItemProviderLoadResult loadResultWithItemProvider:typesToLoad:]):
+        (-[WebItemProviderLoadResult initWithItemProvider:typesToLoad:]):
+        (-[WebItemProviderLoadResult typesToLoad]):
+        (-[WebItemProviderLoadResult setFileURL:forType:]):
+        (-[WebItemProviderLoadResult itemProvider]):
+        (-[WebItemProviderPasteboard setItemProviders:]):
+        (-[WebItemProviderPasteboard dataForPasteboardType:]):
+        (-[WebItemProviderPasteboard typeIdentifiersToLoadForRegisteredTypeIdentfiers:]):
+        (-[WebItemProviderPasteboard doAfterLoadingProvidedContentIntoFileURLs:synchronousTimeout:]):
+        (+[WebItemProviderLoadResult emptyLoadResult]): Deleted.
+        (+[WebItemProviderLoadResult loadResultWithFileURLMap:presentationStyle:]): Deleted.
+        (-[WebItemProviderLoadResult initWithFileURLMap:presentationStyle:]): Deleted.
+        (-[WebItemProviderPasteboard typeIdentifierToLoadForRegisteredTypeIdentfiers:]): Deleted.
+
+        In the case of drag and drop on iOS, we cannot load any data prior to performing the drop; additionally, any
+        attempts to load data immediately after the drop is performed in the UI process will fail. This means any and
+        all data that the web process may require in the future when handling the drop must be loaded out of the item
+        providers and saved when the drop is being handled in the UI process.
+
+        Currently, we only load the highest fidelity type we can handle (or, if we don't know what we can handle, we
+        select the highest fidelity representation conforming to "public.content"). This is a problematic for supporting
+        DataTransfer.getData() on drop on iOS, because the page can ask for any of the three web-exposed types. To
+        address this, we refactor WebItemProviderPasteboard to support loading multiple representations per item being
+        dropped. At minimum, we will load anything conforming to "public.html", "public.plain-text", "public.url", and
+        the new "com.apple.WebKit.custom-pasteboard-data" so we have means to answer any question that the page could
+        ask via DataTransfer.getData(). We additonally load the highest fidelity supported (or content-conformant) type,
+        if it has not already been loaded as a result of the former.
+
+        To make this possible, we refactor WebItemProviderLoadResult to take an item provider and a list of types to
+        load. -doAfterLoadingProvidedContentIntoFileURLs:synchronousTimeout: then creates a list of load results and
+        uses each one to represent the results of loading data from its item provider (i.e. a map of UTI => file URL).
+
+        * platform/mac/PasteboardMac.mm:
+        (WebCore::Pasteboard::writeCustomData):
+        (WebCore::cocoaTypeFromHTMLClipboardType):
+        (WebCore::readPlatformValueAsString):
+        (WebCore::Pasteboard::readStringForBindings):
+
+        Change readStringForBindings (formerly, readString) so that if support for custom pasteboard data is enabled,
+        we only allow reading directly from the platform pasteboard if the given type is DOM-safe; otherwise, we consult
+        the custom data blob, if it exists. Otherwise, if support for custom pasteboard data is disabled, we fall back
+        to our current behavior.
+
+        (WebCore::Pasteboard::types):
+        (WebCore::Pasteboard::readString): Deleted.
+        * platform/mac/PlatformPasteboardMac.mm:
+        (WebCore::PlatformPasteboard::numberOfFiles const):
+        (WebCore::PlatformPasteboard::getPathnamesForType const):
+        (WebCore::PlatformPasteboard::stringForType):
+        (WebCore::safeTypeForDOMToReadAndWriteForPlatformType):
+        (WebCore::PlatformPasteboard::typesSafeForDOMToReadAndWrite const):
+
+        Add support for reading DOM-exposed types and fetching DOM-exposed data off of the pasteboard. The overall idea
+        here is similar to iOS, though implementation details vary (e.g. no item provider support).
+
+        (WebCore::PlatformPasteboard::write):
+        (WebCore::PlatformPasteboard::platformPasteboardTypeForSafeTypeForDOMToReadAndWrite):
+        (WebCore::PlatformPasteboard::numberOfFiles): Deleted.
+        (WebCore::PlatformPasteboard::getPathnamesForType): Deleted.
+        * platform/win/PasteboardWin.cpp:
+        (WebCore::Pasteboard::readStringForBindings):
+        (WebCore::Pasteboard::writeCustomData):
+        (WebCore::Pasteboard::readString): Deleted.
+        * platform/wpe/PasteboardWPE.cpp:
+        (WebCore::Pasteboard::readStringForBindings):
+        (WebCore::Pasteboard::writeCustomData):
+        (WebCore::Pasteboard::readString): Deleted.
+        * platform/wpe/PlatformPasteboardWPE.cpp:
+        (WebCore::PlatformPasteboard::typesSafeForDOMToReadAndWrite const):
+        (WebCore::PlatformPasteboard::write):
+        * testing/InternalSettings.cpp:
+        (WebCore::InternalSettings::Backup::Backup):
+        (WebCore::InternalSettings::Backup::restoreTo):
+        (WebCore::InternalSettings::setCustomPasteboardDataEnabled):
+
+        Add a new internal settings hook for layout tests to opt in to using custom pasteboard data. By default, custom
+        pasteboard data is enabled only in Safari, or on applications linked on or after certain releases of iOS and
+        macOS.
+
+        * testing/InternalSettings.h:
+        * testing/InternalSettings.idl:
+
 2017-09-27  Zalan Bujtas  <zalan@apple.com>
 
         Deferred image size change makes image-load-on-delay.html flaky.
index 7998dc0..6905c23 100644 (file)
                2EDEF1F7121B0EFC00726DB2 /* BlobRegistryImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 2EDEF1F1121B0EFC00726DB2 /* BlobRegistryImpl.h */; settings = {ATTRIBUTES = (Private, ); }; };
                2EDF369C122C94B4002F7D4E /* FileReaderSync.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2EDF369A122C94B4002F7D4E /* FileReaderSync.cpp */; };
                2EDF369D122C94B4002F7D4E /* FileReaderSync.h in Headers */ = {isa = PBXBuildFile; fileRef = 2EDF369B122C94B4002F7D4E /* FileReaderSync.h */; };
+               2EE02A1F1F7324280006AF72 /* Pasteboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2EE02A1E1F7324280006AF72 /* Pasteboard.cpp */; };
                2EEEE55C1B66A047008E2CBC /* WheelEventDeltaFilterMac.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2EEEE55B1B66A047008E2CBC /* WheelEventDeltaFilterMac.mm */; };
                2EF1BFEA121C9F4200C27627 /* FileStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2EF1BFE8121C9F4200C27627 /* FileStream.cpp */; };
                2EF1BFEB121C9F4200C27627 /* FileStream.h in Headers */ = {isa = PBXBuildFile; fileRef = 2EF1BFE9121C9F4200C27627 /* FileStream.h */; settings = {ATTRIBUTES = (Private, ); }; };
                2EDEF1F1121B0EFC00726DB2 /* BlobRegistryImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BlobRegistryImpl.h; sourceTree = "<group>"; };
                2EDF369A122C94B4002F7D4E /* FileReaderSync.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FileReaderSync.cpp; sourceTree = "<group>"; };
                2EDF369B122C94B4002F7D4E /* FileReaderSync.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FileReaderSync.h; sourceTree = "<group>"; };
+               2EE02A1E1F7324280006AF72 /* Pasteboard.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Pasteboard.cpp; sourceTree = "<group>"; };
                2EEEE55B1B66A047008E2CBC /* WheelEventDeltaFilterMac.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WheelEventDeltaFilterMac.mm; sourceTree = "<group>"; };
                2EF1BFE8121C9F4200C27627 /* FileStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FileStream.cpp; sourceTree = "<group>"; };
                2EF1BFE9121C9F4200C27627 /* FileStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FileStream.h; sourceTree = "<group>"; };
                                E1513D501677F08800149FCB /* NotImplemented.cpp */,
                                98EB1F941313FE0500D0E1EA /* NotImplemented.h */,
                                4184F5151EAF059800F18BF0 /* OrientationNotifier.h */,
+                               2EE02A1E1F7324280006AF72 /* Pasteboard.cpp */,
                                4B2708C50AF19EE40065127F /* Pasteboard.h */,
                                C5F765B414E1D414006C899B /* PasteboardStrategy.h */,
                                1AF5E4D21E56735A004A1F01 /* PasteboardWriterData.cpp */,
                                BCEA4813097D93020094C9E4 /* RenderBlockLineLayout.cpp */,
                                BCEA4822097D93020094C9E4 /* RenderBox.cpp */,
                                BCEA4823097D93020094C9E4 /* RenderBox.h */,
+                               BCEB179B143379F50052EAE9 /* RenderBoxFragmentInfo.h */,
                                BC96DB450F3A882200573CB3 /* RenderBoxModelObject.cpp */,
                                BC96DB420F3A880E00573CB3 /* RenderBoxModelObject.h */,
-                               BCEB179B143379F50052EAE9 /* RenderBoxFragmentInfo.h */,
                                BCEA4826097D93020094C9E4 /* RenderButton.cpp */,
                                BCEA4827097D93020094C9E4 /* RenderButton.h */,
                                B56579B41824D12A00E79F23 /* RenderChildIterator.h */,
                                066C772F0AB603FD00238CC4 /* RenderFileUploadControl.h */,
                                53C8298B13D8D92700DE2DEB /* RenderFlexibleBox.cpp */,
                                53C8298C13D8D92700DE2DEB /* RenderFlexibleBox.h */,
+                               D70AD65513E1342B005B50B4 /* RenderFragmentContainer.cpp */,
+                               D70AD65613E1342B005B50B4 /* RenderFragmentContainer.h */,
+                               BCE93F461517C6D5008CCF74 /* RenderFragmentContainerSet.cpp */,
+                               BCE93F441517C567008CCF74 /* RenderFragmentContainerSet.h */,
                                508CCA4E13CF106B003151F3 /* RenderFragmentedFlow.cpp */,
                                508CCA4D13CF106B003151F3 /* RenderFragmentedFlow.h */,
                                A871DECC0A1530C700B12A68 /* RenderFrame.cpp */,
                                ADE16736181050C300463A2E /* RenderPtr.h */,
                                5A574F22131DB93900471B88 /* RenderQuote.cpp */,
                                5A574F23131DB93900471B88 /* RenderQuote.h */,
-                               D70AD65513E1342B005B50B4 /* RenderFragmentContainer.cpp */,
-                               D70AD65613E1342B005B50B4 /* RenderFragmentContainer.h */,
-                               BCE93F461517C6D5008CCF74 /* RenderFragmentContainerSet.cpp */,
-                               BCE93F441517C567008CCF74 /* RenderFragmentContainerSet.h */,
                                A871DFDE0A15376B00B12A68 /* RenderReplaced.cpp */,
                                A871DFDF0A15376B00B12A68 /* RenderReplaced.h */,
                                BCA846D40DC67A350026C309 /* RenderReplica.cpp */,
                                BCEA4860097D93020094C9E4 /* RenderBlock.h in Headers */,
                                BC10D76817D8EE71005E2626 /* RenderBlockFlow.h in Headers */,
                                BCEA4862097D93020094C9E4 /* RenderBox.h in Headers */,
-                               BC96DB430F3A880E00573CB3 /* RenderBoxModelObject.h in Headers */,
                                BCEB179C143379F50052EAE9 /* RenderBoxFragmentInfo.h in Headers */,
+                               BC96DB430F3A880E00573CB3 /* RenderBoxModelObject.h in Headers */,
                                BCEA4866097D93020094C9E4 /* RenderButton.h in Headers */,
                                B56579B51824D12A00E79F23 /* RenderChildIterator.h in Headers */,
                                BCE4413412F748E2009B84B8 /* RenderCombineText.h in Headers */,
                                0F5B7A5510F65D7A00376302 /* RenderEmbeddedObject.h in Headers */,
                                066C77310AB603FD00238CC4 /* RenderFileUploadControl.h in Headers */,
                                53C8298E13D8D92700DE2DEB /* RenderFlexibleBox.h in Headers */,
+                               D70AD65813E1342B005B50B4 /* RenderFragmentContainer.h in Headers */,
+                               BCE93F451517C567008CCF74 /* RenderFragmentContainerSet.h in Headers */,
                                508CCA4F13CF106B003151F3 /* RenderFragmentedFlow.h in Headers */,
                                A871DED30A1530C700B12A68 /* RenderFrame.h in Headers */,
                                0FD3080F117CF7E700A791F7 /* RenderFrameBase.h in Headers */,
                                A43BF59D1149292800C643CA /* RenderProgress.h in Headers */,
                                B5B65874186FDE4C009C26E8 /* RenderPtr.h in Headers */,
                                5A574F25131DB93900471B88 /* RenderQuote.h in Headers */,
-                               D70AD65813E1342B005B50B4 /* RenderFragmentContainer.h in Headers */,
-                               BCE93F451517C567008CCF74 /* RenderFragmentContainerSet.h in Headers */,
                                A871DFE30A15376B00B12A68 /* RenderReplaced.h in Headers */,
                                BCA846D70DC67A350026C309 /* RenderReplica.h in Headers */,
                                1479FAEE109AE37500DED655 /* RenderRuby.h in Headers */,
                                447958051643B4B2001E0A7F /* ParsedContentType.cpp in Sources */,
                                57B5F7EC1E57F1E300F34F90 /* PasswordCredential.cpp in Sources */,
                                F55B3DC91251F12D003EF269 /* PasswordInputType.cpp in Sources */,
+                               2EE02A1F1F7324280006AF72 /* Pasteboard.cpp in Sources */,
                                E453901E0EAFCACA003695C8 /* PasteboardIOS.mm in Sources */,
                                4B2709830AF2E5E00065127F /* PasteboardMac.mm in Sources */,
                                1AF5E4E21E5779B1004A1F01 /* PasteboardWriter.mm in Sources */,
                                0F5B7A5410F65D7A00376302 /* RenderEmbeddedObject.cpp in Sources */,
                                066C77300AB603FD00238CC4 /* RenderFileUploadControl.cpp in Sources */,
                                53C8298D13D8D92700DE2DEB /* RenderFlexibleBox.cpp in Sources */,
+                               D70AD65713E1342B005B50B4 /* RenderFragmentContainer.cpp in Sources */,
+                               BCE93F471517C6D5008CCF74 /* RenderFragmentContainerSet.cpp in Sources */,
                                508CCA5013CF106B003151F3 /* RenderFragmentedFlow.cpp in Sources */,
                                A871DED40A1530C700B12A68 /* RenderFrame.cpp in Sources */,
                                0FD3080E117CF7E700A791F7 /* RenderFrameBase.cpp in Sources */,
                                BCEA487F097D93020094C9E4 /* RenderObject.cpp in Sources */,
                                A43BF59C1149292800C643CA /* RenderProgress.cpp in Sources */,
                                5A574F24131DB93900471B88 /* RenderQuote.cpp in Sources */,
-                               D70AD65713E1342B005B50B4 /* RenderFragmentContainer.cpp in Sources */,
-                               BCE93F471517C6D5008CCF74 /* RenderFragmentContainerSet.cpp in Sources */,
                                A871DFE20A15376B00B12A68 /* RenderReplaced.cpp in Sources */,
                                BCA846D60DC67A350026C309 /* RenderReplica.cpp in Sources */,
                                1479FAED109AE37500DED655 /* RenderRuby.cpp in Sources */,
index 57be7c8..30fa6a2 100644 (file)
@@ -140,7 +140,7 @@ String DataTransfer::getData(const String& type) const
         return { };
 #endif
 
-    return m_pasteboard->readString(normalizeType(type));
+    return m_pasteboard->readStringForBindings(normalizeType(type));
 }
 
 void DataTransfer::setData(const String& type, const String& data)
@@ -257,6 +257,11 @@ Ref<DataTransfer> DataTransfer::createForDrag()
     return adoptRef(*new DataTransfer(StoreMode::ReadWrite, Pasteboard::createForDragAndDrop(), Type::DragAndDropData));
 }
 
+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)
 {
     auto type = dragData.containsFiles() ? Type::DragAndDropFiles : Type::DragAndDropData;
@@ -460,6 +465,31 @@ void DataTransfer::setEffectAllowed(const String& effect)
     m_effectAllowed = effect;
 }
 
+void DataTransfer::moveDragState(Ref<DataTransfer>&& other)
+{
+    RELEASE_ASSERT(is<StaticPasteboard>(other->pasteboard()));
+    // 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, and after ending the last drag session.
+    // After pushing the static pasteboard's contents to the platform, the pasteboard should only
+    // contain data that was in the static pasteboard.
+    m_pasteboard->clear();
+    downcast<StaticPasteboard>(other->pasteboard()).commitToPasteboard(*m_pasteboard);
+
+    m_dropEffect = other->m_dropEffect;
+    m_effectAllowed = other->m_effectAllowed;
+    m_dragLocation = other->m_dragLocation;
+    m_dragImage = other->m_dragImage;
+    m_dragImageElement = WTFMove(other->m_dragImageElement);
+    m_dragImageLoader = WTFMove(other->m_dragImageLoader);
+    m_itemList = WTFMove(other->m_itemList);
+    m_fileList = WTFMove(other->m_fileList);
+}
+
+bool DataTransfer::hasDragImage() const
+{
+    return m_dragImage || m_dragImageElement;
+}
+
 #endif // ENABLE(DRAG_SUPPORT)
 
 } // namespace WebCore
index 675c6e2..a6185d5 100644 (file)
@@ -80,6 +80,7 @@ public:
 
 #if ENABLE(DRAG_SUPPORT)
     static Ref<DataTransfer> createForDrag();
+    static Ref<DataTransfer> createForDragStartEvent();
     static Ref<DataTransfer> createForDrop(StoreMode, const DragData&);
 
     bool dropEffectIsUninitialized() const { return m_dropEffect == "uninitialized"; }
@@ -93,6 +94,9 @@ public:
     DragImageRef createDragImage(IntPoint& dragLocation) const;
     void updateDragImage();
     RefPtr<Element> dragImageElement() const;
+
+    void moveDragState(Ref<DataTransfer>&&);
+    bool hasDragImage() const;
 #endif
 
 private:
index 1ea4911..537336c 100644 (file)
@@ -50,6 +50,7 @@
 #import "Text.h"
 #import "WebContentReader.h"
 #import "WebCoreNSURLExtras.h"
+#import "markup.h"
 #import <pal/spi/cocoa/NSAttributedStringSPI.h>
 #import <wtf/BlockObjCExceptions.h>
 
@@ -143,6 +144,13 @@ static RefPtr<SharedBuffer> archivedDataForAttributedString(NSAttributedString *
     return SharedBuffer::create([NSKeyedArchiver archivedDataWithRootObject:attributedString]);
 }
 
+String Editor::selectionInHTMLFormat()
+{
+    if (auto range = selectedRange())
+        return createMarkup(*range, nullptr, AnnotateForInterchange, false, ResolveNonLocalURLs);
+    return { };
+}
+
 void Editor::writeSelectionToPasteboard(Pasteboard& pasteboard)
 {
     NSAttributedString *attributedString = attributedStringFromRange(*selectedRange());
@@ -153,10 +161,7 @@ void Editor::writeSelectionToPasteboard(Pasteboard& pasteboard)
     content.dataInRTFDFormat = attributedString.containsAttachments ? dataInRTFDFormat(attributedString) : nullptr;
     content.dataInRTFFormat = dataInRTFFormat(attributedString);
     content.dataInAttributedStringFormat = archivedDataForAttributedString(attributedString);
-    // FIXME: Why don't we want this on iOS?
-#if PLATFORM(MAC)
     content.dataInHTMLFormat = selectionInHTMLFormat();
-#endif
     content.dataInStringFormat = stringSelectionForPasteboardWithImageAltText();
     client()->getClientPasteboardDataForRange(selectedRange().get(), content.clientTypes, content.clientData);
 
@@ -173,10 +178,7 @@ void Editor::writeSelection(PasteboardWriterData& pasteboardWriterData)
     webContent.dataInRTFDFormat = attributedString.containsAttachments ? dataInRTFDFormat(attributedString) : nullptr;
     webContent.dataInRTFFormat = dataInRTFFormat(attributedString);
     webContent.dataInAttributedStringFormat = archivedDataForAttributedString(attributedString);
-    // FIXME: Why don't we want this on iOS?
-#if PLATFORM(MAC)
     webContent.dataInHTMLFormat = selectionInHTMLFormat();
-#endif
     webContent.dataInStringFormat = stringSelectionForPasteboardWithImageAltText();
     client()->getClientPasteboardDataForRange(selectedRange().get(), webContent.clientTypes, webContent.clientData);
 
index 27c846e..5800970 100644 (file)
@@ -182,11 +182,6 @@ void Editor::replaceNodeFromPasteboard(Node* node, const String& pasteboardName)
     client()->setInsertionPasteboard(String());
 }
 
-String Editor::selectionInHTMLFormat()
-{
-    return createMarkup(*selectedRange(), nullptr, AnnotateForInterchange, false, ResolveNonLocalURLs);
-}
-
 RefPtr<SharedBuffer> Editor::imageInWebArchiveFormat(Element& imageElement)
 {
     RefPtr<LegacyWebArchive> archive = LegacyWebArchive::create(imageElement);
index 1ca9af1..e9ae5d1 100644 (file)
@@ -43,7 +43,7 @@ static RefPtr<DocumentFragment> createFragmentFromPasteboardData(Pasteboard& pas
         return nullptr;
 
     if (types.contains("text/html;charset=utf-8") && frame.document()) {
-        String markup = pasteboard.readString("text/html;charset=utf-8");
+        String markup = pasteboard.readStringForBindings("text/html;charset=utf-8");
         if (RefPtr<DocumentFragment> fragment = createFragmentFromMarkup(*frame.document(), markup, emptyString(), DisallowScriptingAndPluginContent))
             return fragment;
     }
@@ -53,7 +53,7 @@ static RefPtr<DocumentFragment> createFragmentFromPasteboardData(Pasteboard& pas
 
     if (types.contains("text/plain;charset=utf-8")) {
         chosePlainText = true;
-        if (RefPtr<DocumentFragment> fragment = createFragmentFromText(range, pasteboard.readString("text/plain;charset=utf-8")))
+        if (RefPtr<DocumentFragment> fragment = createFragmentFromText(range, pasteboard.readStringForBindings("text/plain;charset=utf-8")))
             return fragment;
     }
 
index ef069c4..23d47c5 100644 (file)
@@ -86,6 +86,7 @@
 #include "Settings.h"
 #include "ShadowRoot.h"
 #include "SpatialNavigation.h"
+#include "StaticPasteboard.h"
 #include "StyleCachedImage.h"
 #include "TextEvent.h"
 #include "TextIterator.h"
@@ -3547,21 +3548,9 @@ bool EventHandler::dispatchDragSrcEvent(const AtomicString& eventType, const Pla
     return !dispatchDragEvent(eventType, *dragState().source, event, dragState().dataTransfer.get());
 }
 
-bool EventHandler::dispatchDragStartEvent(HasNonDefaultPasteboardData& hasNonDefaultPasteboardData)
+bool EventHandler::dispatchDragStartEventOnSourceElement(DataTransfer& dataTransfer)
 {
-#if PLATFORM(COCOA)
-    auto changeCountBeforeDragStart = dragState().dataTransfer->pasteboard().changeCount();
-#endif
-
-    bool mayStartDrag = dispatchDragSrcEvent(eventNames().dragstartEvent, m_mouseDown);
-
-#if PLATFORM(COCOA)
-    hasNonDefaultPasteboardData = changeCountBeforeDragStart != dragState().dataTransfer->pasteboard().changeCount() ? HasNonDefaultPasteboardData::Yes : HasNonDefaultPasteboardData::No;
-#else
-    hasNonDefaultPasteboardData = dragState().dataTransfer->pasteboard().hasData() ? HasNonDefaultPasteboardData::Yes : HasNonDefaultPasteboardData::No;
-#endif
-
-    return mayStartDrag && !m_frame.selection().selection().isInPasswordField();
+    return !dispatchDragEvent(eventNames().dragstartEvent, *dragState().source, m_mouseDown, &dataTransfer) && !m_frame.selection().selection().isInPasswordField();
 }
     
 static bool ExactlyOneBitSet(DragSourceAction n)
@@ -3667,29 +3656,29 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event, CheckDr
     // This does work only if we missed a dragEnd. Do it anyway, just to make sure the old dataTransfer gets numbed.
     invalidateDataTransfer();
 
-    dragState().dataTransfer = createDraggingDataTransfer();
+    dragState().dataTransfer = DataTransfer::createForDrag();
     HasNonDefaultPasteboardData hasNonDefaultPasteboardData = HasNonDefaultPasteboardData::No;
     
     if (dragState().shouldDispatchEvents) {
-        // Check to see if the is a DOM based drag. If it is, get the DOM specified drag image and offset.
-        if (dragState().type == DragSourceActionDHTML) {
-            if (RenderObject* renderer = dragState().source->renderer()) {
-                // FIXME: This doesn't work correctly with transforms.
-                FloatPoint absPos = renderer->localToAbsolute();
-                IntSize delta = m_mouseDownPos - roundedIntPoint(absPos);
+        auto dragStartDataTransfer = DataTransfer::createForDragStartEvent();
+        m_mouseDownMayStartDrag = dispatchDragStartEventOnSourceElement(dragStartDataTransfer);
+        hasNonDefaultPasteboardData = dragStartDataTransfer->pasteboard().hasData() ? HasNonDefaultPasteboardData::Yes : HasNonDefaultPasteboardData::No;
+        dragState().dataTransfer->moveDragState(WTFMove(dragStartDataTransfer));
+
+        if (dragState().source && dragState().type == DragSourceActionDHTML && !dragState().dataTransfer->hasDragImage()) {
+            dragState().source->document().updateStyleIfNeeded();
+            if (auto* renderer = dragState().source->renderer()) {
+                auto absolutePosition = renderer->localToAbsolute();
+                auto delta = m_mouseDownPos - roundedIntPoint(absolutePosition);
                 dragState().dataTransfer->setDragImage(dragState().source.get(), delta.width(), delta.height());
             } else {
-                // The renderer has disappeared, this can happen if the onStartDrag handler has hidden
-                // the element in some way.  In this case we just kill the drag.
+                dispatchDragSrcEvent(eventNames().dragendEvent, event.event());
                 m_mouseDownMayStartDrag = false;
                 invalidateDataTransfer();
                 dragState().source = nullptr;
-
                 return true;
             }
-        } 
-
-        m_mouseDownMayStartDrag = dispatchDragStartEvent(hasNonDefaultPasteboardData);
+        }
 
         dragState().dataTransfer->makeInvalidForSecurity();
 
index a0aa535..2de5acf 100644 (file)
@@ -324,7 +324,6 @@ private:
 #if ENABLE(DRAG_SUPPORT)
     static DragState& dragState();
     static const Seconds TextDragDelay;
-    Ref<DataTransfer> createDraggingDataTransfer() const;
 #endif
 
     bool eventActivatedView(const PlatformMouseEvent&) const;
@@ -405,7 +404,7 @@ private:
     void clearDragState();
 
     bool dispatchDragSrcEvent(const AtomicString& eventType, const PlatformMouseEvent&);
-    bool dispatchDragStartEvent(HasNonDefaultPasteboardData&);
+    bool dispatchDragStartEventOnSourceElement(DataTransfer&);
 
     bool dragHysteresisExceeded(const FloatPoint&) const;
     bool dragHysteresisExceeded(const IntPoint&) const;
index 138c51c..691c1d9 100644 (file)
 #include <wtf/NeverDestroyed.h>
 #include <wtf/StdLibExtras.h>
 
+#if PLATFORM(COCOA)
+#include <wtf/spi/darwin/dyldSPI.h>
+#endif
+
 #if ENABLE(MEDIA_STREAM) && USE(AVFOUNDATION)
 #include "RealtimeMediaSourceCenterMac.h"
 #endif
@@ -115,6 +119,7 @@ bool Settings::gAVKitEnabled = false;
 bool Settings::gShouldOptOutOfNetworkStateObservation = false;
 #endif
 bool Settings::gManageAudioSession = false;
+bool Settings::gCustomPasteboardDataEnabled = false;
 
 // NOTEs
 //  1) EditingMacBehavior comprises Tiger, Leopard, SnowLeopard and iOS builds, as well as QtWebKit when built on Mac;
@@ -139,6 +144,22 @@ static EditingBehaviorType editingBehaviorTypeForPlatform()
     ;
 }
 
+bool Settings::customPasteboardDataEnabled()
+{
+    static std::once_flag initializeCustomPasteboardDataToDefaultValue;
+    std::call_once(initializeCustomPasteboardDataToDefaultValue, [] {
+#if PLATFORM(IOS) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110300
+        gCustomPasteboardDataEnabled = IOSApplication::isMobileSafari() || dyld_get_program_sdk_version() >= DYLD_IOS_VERSION_11_3;
+#elif PLATFORM(MAC) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 101304
+        // FIXME: Update this linked-on check once the correct macro is in the SDK.
+        gCustomPasteboardDataEnabled = MacApplication::isSafari() || dyld_get_program_sdk_version() > DYLD_MACOSX_VERSION_10_13;
+#else
+        gCustomPasteboardDataEnabled = false;
+#endif
+    });
+    return gCustomPasteboardDataEnabled;
+}
+
 #if PLATFORM(COCOA)
 static const bool defaultYouTubeFlashPluginReplacementEnabled = true;
 #else
index 83b1988..f60096d 100644 (file)
@@ -304,6 +304,9 @@ public:
     static bool shouldManageAudioSessionCategory() { return gManageAudioSession; }
 #endif
 
+    static void setCustomPasteboardDataEnabled(bool enabled) { gCustomPasteboardDataEnabled = enabled; }
+    WEBCORE_EXPORT static bool customPasteboardDataEnabled();
+
 #if ENABLE(LEGACY_ENCRYPTED_MEDIA)
     void setMediaKeysStorageDirectory(const String& directory) { m_mediaKeysStorageDirectory = directory; }
     const String& mediaKeysStorageDirectory() const { return m_mediaKeysStorageDirectory; }
@@ -419,6 +422,7 @@ private:
     WEBCORE_EXPORT static bool gShouldOptOutOfNetworkStateObservation;
 #endif
     WEBCORE_EXPORT static bool gManageAudioSession;
+    WEBCORE_EXPORT static bool gCustomPasteboardDataEnabled;
 
 #if ENABLE(LEGACY_ENCRYPTED_MEDIA)
     String m_mediaKeysStorageDirectory;
index 9f9758b..806d92b 100644 (file)
@@ -561,12 +561,6 @@ PlatformMouseEvent EventHandler::currentPlatformMouseEvent() const
 
 #if ENABLE(DRAG_SUPPORT)
 
-Ref<DataTransfer> EventHandler::createDraggingDataTransfer() const
-{
-    Pasteboard("data interaction pasteboard").clear();
-    return DataTransfer::createForDrag();
-}
-
 bool EventHandler::eventLoopHandleMouseDragged(const MouseEventWithHitTestResults&)
 {
     return false;
index f71e4fd..4cb70e9 100644 (file)
@@ -718,22 +718,6 @@ bool EventHandler::eventActivatedView(const PlatformMouseEvent& event) const
     return m_activationEventNumber == event.eventNumber();
 }
 
-#if ENABLE(DRAG_SUPPORT)
-
-Ref<DataTransfer> EventHandler::createDraggingDataTransfer() const
-{
-    // Must be done before ondragstart adds types and data to the pboard,
-    // also done for security, as it erases data from the last drag.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
-    auto pasteboard = std::make_unique<Pasteboard>(NSDragPboard);
-#pragma clang diagnostic pop
-    pasteboard->clear();
-    return DataTransfer::createForDrag();
-}
-
-#endif
-
 bool EventHandler::tabsToAllFormControls(KeyboardEvent& event) const
 {
     Page* page = m_frame.page();
index 42106a6..f74d381 100644 (file)
@@ -91,15 +91,6 @@ bool EventHandler::eventActivatedView(const PlatformMouseEvent& event) const
     return event.didActivateWebView();
 }
 
-#if ENABLE(DRAG_SUPPORT)
-
-Ref<DataTransfer> EventHandler::createDraggingDataTransfer() const
-{
-    return DataTransfer::createForDrag();
-}
-
-#endif
-
 void EventHandler::focusDocumentView()
 {
     Page* page = m_frame.page();
diff --git a/Source/WebCore/platform/Pasteboard.cpp b/Source/WebCore/platform/Pasteboard.cpp
new file mode 100644 (file)
index 0000000..9bf74eb
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "Pasteboard.h"
+
+#include "PasteboardStrategy.h"
+#include "PlatformStrategies.h"
+#include "Settings.h"
+#include "SharedBuffer.h"
+#include <wtf/persistence/PersistentCoders.h>
+#include <wtf/text/StringHash.h>
+
+namespace WebCore {
+
+bool isSafeTypeForDOMToReadAndWrite(const String& type)
+{
+    return type == "text/plain" || type == "text/html" || type == "text/uri-list";
+}
+
+Ref<SharedBuffer> sharedBufferFromCustomData(const PasteboardCustomData& data)
+{
+    const static unsigned currentCustomDataSerializationVersion = 1;
+
+    WTF::Persistence::Encoder encoder;
+    encoder << currentCustomDataSerializationVersion;
+    // FIXME: Replace with origin information from PasteboardCustomData once same origin restrictions are implemented.
+    encoder << emptyString();
+    encoder << data.sameOriginCustomData;
+    encoder << data.orderedTypes;
+    return SharedBuffer::create(encoder.buffer(), encoder.bufferSize());
+}
+
+PasteboardCustomData customDataFromSharedBuffer(const SharedBuffer& buffer)
+{
+    const static unsigned maxSupportedDataSerializationVersionNumber = 1;
+
+    PasteboardCustomData result;
+    WTF::Persistence::Decoder decoder { reinterpret_cast<const uint8_t*>(buffer.data()), buffer.size() };
+    unsigned version;
+    if (!decoder.decode(version) || version > maxSupportedDataSerializationVersionNumber)
+        return { };
+
+    String origin;
+    if (!decoder.decode(origin))
+        return { };
+
+    if (!decoder.decode(result.sameOriginCustomData))
+        return { };
+
+    if (!decoder.decode(result.orderedTypes))
+        return { };
+
+    return result;
+}
+
+};
index f4dad2b..33bbd04 100644 (file)
@@ -27,6 +27,7 @@
 
 #include "DragImage.h"
 #include "URL.h"
+#include <wtf/HashMap.h>
 #include <wtf/Noncopyable.h>
 #include <wtf/Vector.h>
 #include <wtf/text/WTFString.h>
@@ -145,6 +146,22 @@ struct PasteboardPlainText {
 #endif
 };
 
+// FIXME: We need to ensure that the contents of sameOriginCustomData are not accessible across different origins.
+struct PasteboardCustomData {
+    Vector<String> orderedTypes;
+    HashMap<String, String> platformData;
+    HashMap<String, String> sameOriginCustomData;
+};
+
+WEBCORE_EXPORT Ref<SharedBuffer> sharedBufferFromCustomData(const PasteboardCustomData&);
+WEBCORE_EXPORT PasteboardCustomData customDataFromSharedBuffer(const SharedBuffer&);
+
+#if PLATFORM(COCOA)
+const char customWebKitPasteboardDataType[] = "com.apple.WebKit.custom-pasteboard-data";
+#endif
+
+bool isSafeTypeForDOMToReadAndWrite(const String& type);
+
 class Pasteboard {
     WTF_MAKE_NONCOPYABLE(Pasteboard); WTF_MAKE_FAST_ALLOCATED;
 public:
@@ -168,7 +185,7 @@ public:
 
     virtual bool hasData();
     virtual Vector<String> types();
-    virtual String readString(const String& type);
+    virtual String readStringForBindings(const String& type);
 
     virtual void writeString(const String& type, const String& data);
     virtual void clear();
@@ -231,6 +248,8 @@ public:
     void writeImageToDataObject(Element&, const URL&); // FIXME: Layering violation.
 #endif
 
+    void writeCustomData(const PasteboardCustomData&);
+
 private:
 #if PLATFORM(IOS)
     bool respectsUTIFidelities() const;
@@ -244,6 +263,10 @@ private:
     void writePlainTextToDataObject(const String&, SmartReplaceOption);
 #endif
 
+#if PLATFORM(COCOA)
+    String readStringForPlatformType(const String&);
+#endif
+
 #if PLATFORM(GTK)
     void writeToClipboard();
     void readFromClipboard();
index d0a35cd..b2684c9 100644 (file)
@@ -37,6 +37,7 @@ class URL;
 struct PasteboardImage;
 struct PasteboardURL;
 struct PasteboardWebContent;
+struct PasteboardCustomData;
 
 class PasteboardStrategy {
 public:
@@ -71,6 +72,9 @@ public:
     virtual long setStringForType(const String&, const String& pasteboardType, const String& pasteboardName) = 0;
 #endif
 
+    virtual Vector<String> typesSafeForDOMToReadAndWrite(const String& pasteboardName) = 0;
+    virtual long writeCustomData(const PasteboardCustomData&, const String& pasteboardName) = 0;
+
 #if PLATFORM(GTK)
     virtual void writeToClipboard(const String& pasteboardName, const SelectionData&) = 0;
     virtual Ref<SelectionData> readFromClipboard(const String& pasteboardName) = 0;
index 6feee93..b86bc21 100644 (file)
@@ -55,10 +55,7 @@ public:
         RefPtr<SharedBuffer> dataInRTFDFormat;
         RefPtr<SharedBuffer> dataInRTFFormat;
         RefPtr<SharedBuffer> dataInAttributedStringFormat;
-        // FIXME: Why don't we want this on iOS?
-#if PLATFORM(MAC)
         String dataInHTMLFormat;
-#endif
         String dataInStringFormat;
         Vector<String> clientTypes;
         Vector<RefPtr<SharedBuffer>> clientData;
index b6e4fd5..3954c21 100644 (file)
@@ -49,6 +49,7 @@ class Color;
 class SelectionData;
 class SharedBuffer;
 class URL;
+struct PasteboardCustomData;
 struct PasteboardImage;
 struct PasteboardURL;
 struct PasteboardWebContent;
@@ -64,9 +65,11 @@ public:
 #endif
     WEBCORE_EXPORT static String uniqueName();
 
+    WEBCORE_EXPORT static String platformPasteboardTypeForSafeTypeForDOMToReadAndWrite(const String& domType);
+
     WEBCORE_EXPORT void getTypes(Vector<String>& types);
     WEBCORE_EXPORT RefPtr<SharedBuffer> bufferForType(const String& pasteboardType);
-    WEBCORE_EXPORT void getPathnamesForType(Vector<String>& pathnames, const String& pasteboardType);
+    WEBCORE_EXPORT void getPathnamesForType(Vector<String>& pathnames, const String& pasteboardType) const;
     WEBCORE_EXPORT String stringForType(const String& pasteboardType);
     WEBCORE_EXPORT long changeCount() const;
     WEBCORE_EXPORT Color color();
@@ -89,7 +92,10 @@ public:
     WEBCORE_EXPORT String readString(int index, const String& pasteboardType);
     WEBCORE_EXPORT URL readURL(int index, const String& pasteboardType, String& title);
     WEBCORE_EXPORT int count();
-    WEBCORE_EXPORT int numberOfFiles();
+    WEBCORE_EXPORT int numberOfFiles() const;
+
+    WEBCORE_EXPORT long write(const PasteboardCustomData&);
+    WEBCORE_EXPORT Vector<String> typesSafeForDOMToReadAndWrite() const;
 
 #if PLATFORM(GTK)
     WEBCORE_EXPORT void writeToClipboard(const SelectionData&, WTF::Function<void()>&& primarySelectionCleared);
index e0a2353..711fa63 100644 (file)
@@ -26,6 +26,9 @@
 #include "config.h"
 #include "StaticPasteboard.h"
 
+#include "Settings.h"
+#include "SharedBuffer.h"
+
 namespace WebCore {
 
 StaticPasteboard::StaticPasteboard()
@@ -34,20 +37,24 @@ StaticPasteboard::StaticPasteboard()
 
 bool StaticPasteboard::hasData()
 {
-    return !m_stringContents.isEmpty();
+    return !m_platformData.isEmpty() || !m_customData.isEmpty();
 }
 
-String StaticPasteboard::readString(const String& type)
+String StaticPasteboard::readStringForBindings(const String& type)
 {
-    if (!m_stringContents.contains(type))
-        return { };
-    return m_stringContents.get(type);
+    if (m_platformData.contains(type))
+        return m_platformData.get(type);
+
+    if (m_customData.contains(type))
+        return m_customData.get(type);
+
+    return { };
 }
 
 void StaticPasteboard::writeString(const String& type, const String& value)
 {
-    auto result = m_stringContents.set(type, value);
-    if (result.isNewEntry)
+    auto& pasteboardData = isSafeTypeForDOMToReadAndWrite(type) ? m_platformData : m_customData;
+    if (pasteboardData.set(type, value).isNewEntry)
         m_types.append(type);
     else {
         m_types.removeFirst(type);
@@ -58,23 +65,33 @@ void StaticPasteboard::writeString(const String& type, const String& value)
 
 void StaticPasteboard::clear()
 {
-    m_stringContents.clear();
+    m_customData.clear();
+    m_platformData.clear();
     m_types.clear();
 }
 
 void StaticPasteboard::clear(const String& type)
 {
-    if (!m_stringContents.remove(type))
+    if (!m_platformData.remove(type) && !m_customData.remove(type))
         return;
     m_types.removeFirst(type);
     ASSERT(!m_types.contains(type));
 }
 
-// FIXME: Copy the entire StaticPasteboard to UIProcess instead of writing each string.
 void StaticPasteboard::commitToPasteboard(Pasteboard& pasteboard)
 {
-    for (auto& type : m_types)
-        pasteboard.writeString(type, m_stringContents.get(type));
+    if (m_platformData.isEmpty() && m_customData.isEmpty())
+        return;
+
+    if (Settings::customPasteboardDataEnabled()) {
+        pasteboard.writeCustomData({ WTFMove(m_types), WTFMove(m_platformData), WTFMove(m_customData) });
+        return;
+    }
+
+    for (auto& entry : m_platformData)
+        pasteboard.writeString(entry.key, entry.value);
+    for (auto& entry : m_customData)
+        pasteboard.writeString(entry.key, entry.value);
 }
 
 }
index 3a1c039..461d3a4 100644 (file)
@@ -42,7 +42,7 @@ public:
 
     bool hasData() final;
     Vector<String> types() final { return m_types; }
-    String readString(const String& type) final;
+    String readStringForBindings(const String& type) final;
 
     void writeString(const String& type, const String& data) final;
     void clear() final;
@@ -67,7 +67,8 @@ public:
 
 private:
     Vector<String> m_types;
-    HashMap<String, String> m_stringContents;
+    HashMap<String, String> m_platformData;
+    HashMap<String, String> m_customData;
 };
 
 }
index a5e6853..763658e 100644 (file)
@@ -101,15 +101,6 @@ bool EventHandler::widgetDidHandleWheelEvent(const PlatformWheelEvent& event, Wi
     return downcast<FrameView>(widget).frame().eventHandler().handleWheelEvent(event);
 }
 
-#if ENABLE(DRAG_SUPPORT)
-
-Ref<DataTransfer> EventHandler::createDraggingDataTransfer() const
-{
-    return DataTransfer::createForDrag();
-}
-
-#endif
-
 bool EventHandler::passMousePressEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe)
 {
     subframe->eventHandler().handleMousePressEvent(mev.event());
index 974d3ea..bde242c 100644 (file)
@@ -285,7 +285,7 @@ Vector<String> Pasteboard::types()
     return types;
 }
 
-String Pasteboard::readString(const String& type)
+String Pasteboard::readStringForBindings(const String& type)
 {
     readFromClipboard();
 
@@ -317,4 +317,8 @@ void Pasteboard::writeMarkup(const String&)
 {
 }
 
+void Pasteboard::writeCustomData(const PasteboardCustomData&)
+{
+}
+
 }
index eba3e03..b9334da 100644 (file)
@@ -46,4 +46,14 @@ Ref<SelectionData> PlatformPasteboard::readFromClipboard()
     return selection;
 }
 
+Vector<String> PlatformPasteboard::typesSafeForDOMToReadAndWrite() const
+{
+    return { };
+}
+
+long PlatformPasteboard::write(const PasteboardCustomData&)
+{
+    return 0;
+}
+
 }
index acccfb0..e8e5bf3 100644 (file)
@@ -43,6 +43,7 @@ NS_ASSUME_NONNULL_BEGIN
 #endif
 
 - (NSArray<NSString *> *)pasteboardTypes;
+- (NSData *)dataForPasteboardType:(NSString *)pasteboardType;
 - (NSArray *)dataForPasteboardType:(NSString *)pasteboardType inItemSet:(NSIndexSet *)itemSet;
 - (NSArray *)valuesForPasteboardType:(NSString *)pasteboardType inItemSet:(NSIndexSet *)itemSet;
 - (NSInteger)changeCount;
index 76f4397..2306cca 100644 (file)
 #import "LegacyWebArchive.h"
 #import "NotImplemented.h"
 #import "PasteboardStrategy.h"
+#import "PlatformPasteboard.h"
 #import "PlatformStrategies.h"
 #import "RenderImage.h"
 #import "RuntimeApplicationChecks.h"
+#import "Settings.h"
 #import "SharedBuffer.h"
 #import "Text.h"
 #import "URL.h"
@@ -133,6 +135,11 @@ std::unique_ptr<Pasteboard> Pasteboard::createForCopyAndPaste()
     return std::make_unique<Pasteboard>(changeCountForPasteboard());
 }
 
+void Pasteboard::writeCustomData(const PasteboardCustomData& data)
+{
+    m_changeCount = platformStrategies()->pasteboardStrategy()->writeCustomData(data, m_pasteboardName);
+}
+
 void Pasteboard::write(const PasteboardWebContent& content)
 {
     platformStrategies()->pasteboardStrategy()->writeToPasteboard(content, m_pasteboardName);
@@ -320,17 +327,10 @@ static String utiTypeFromCocoaType(NSString *type)
 
 static RetainPtr<NSString> cocoaTypeFromHTMLClipboardType(const String& type)
 {
-    // Ignore any trailing charset - JS strings are Unicode, which encapsulates the charset issue.
-    if (type == "text/plain")
-#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
-        return (NSString *)kUTTypePlainText;
-#else
-        return (NSString *)kUTTypeText;
-#endif
-
-    // Special case because UTI doesn't work with Cocoa's URL type.
-    if (type == "text/uri-list")
-        return (NSString *)kUTTypeURL;
+    if (NSString *platformType = PlatformPasteboard::platformPasteboardTypeForSafeTypeForDOMToReadAndWrite(type)) {
+        if (platformType.length)
+            return platformType;
+    }
 
     // Try UTI now.
     if (NSString *utiType = utiTypeFromCocoaType(type))
@@ -356,42 +356,54 @@ void Pasteboard::clear()
     platformStrategies()->pasteboardStrategy()->writeToPasteboard(String(), String(), m_pasteboardName);
 }
 
-String Pasteboard::readString(const String& type)
+static String readPlatformValueAsString(const String& domType, long changeCount, const String& pasteboardName)
 {
     PasteboardStrategy& strategy = *platformStrategies()->pasteboardStrategy();
 
-    int numberOfItems = strategy.getPasteboardItemsCount(m_pasteboardName);
+    int numberOfItems = strategy.getPasteboardItemsCount(pasteboardName);
 
     if (!numberOfItems)
         return String();
 
     // Grab the value off the pasteboard corresponding to the cocoaType.
-    RetainPtr<NSString> cocoaType = cocoaTypeFromHTMLClipboardType(type);
+    RetainPtr<NSString> cocoaType = cocoaTypeFromHTMLClipboardType(domType);
 
     NSString *cocoaValue = nil;
 
     if ([cocoaType isEqualToString:(NSString *)kUTTypeURL]) {
         String title;
-        URL url = strategy.readURLFromPasteboard(0, kUTTypeURL, m_pasteboardName, title);
+        URL url = strategy.readURLFromPasteboard(0, kUTTypeURL, pasteboardName, title);
         if (!url.isNull())
             cocoaValue = [(NSURL *)url absoluteString];
-    } else if ([cocoaType isEqualToString:(NSString *)kUTTypeText]) {
-        String value = strategy.readStringFromPasteboard(0, kUTTypeText, m_pasteboardName);
+    } else if ([cocoaType isEqualToString:(NSString *)kUTTypePlainText]) {
+        String value = strategy.readStringFromPasteboard(0, kUTTypePlainText, pasteboardName);
         if (!value.isNull())
             cocoaValue = [(NSString *)value precomposedStringWithCanonicalMapping];
-    } else if (cocoaType) {
-        if (RefPtr<SharedBuffer> buffer = strategy.readBufferFromPasteboard(0, cocoaType.get(), m_pasteboardName))
-            cocoaValue = [[[NSString alloc] initWithData:buffer->createNSData().get() encoding:NSUTF8StringEncoding] autorelease];
-    }
+    } else if (cocoaType)
+        cocoaValue = (NSString *)strategy.readStringFromPasteboard(0, cocoaType.get(), pasteboardName);
 
     // Enforce changeCount ourselves for security. We check after reading instead of before to be
     // sure it doesn't change between our testing the change count and accessing the data.
-    if (cocoaValue && m_changeCount == changeCountForPasteboard(m_pasteboardName))
+    if (cocoaValue && changeCount == changeCountForPasteboard(pasteboardName))
         return cocoaValue;
 
     return String();
 }
 
+String Pasteboard::readStringForBindings(const String& type)
+{
+    if (!Settings::customPasteboardDataEnabled() || isSafeTypeForDOMToReadAndWrite(type))
+        return readPlatformValueAsString(type, m_changeCount, m_pasteboardName);
+
+    if (auto buffer = platformStrategies()->pasteboardStrategy()->bufferForType(customWebKitPasteboardDataType, m_pasteboardName)) {
+        NSString *customDataValue = customDataFromSharedBuffer(*buffer).sameOriginCustomData.get(type);
+        if (customDataValue.length)
+            return customDataValue;
+    }
+
+    return { };
+}
+
 static void addHTMLClipboardTypesForCocoaType(ListHashSet<String>& resultTypes, NSString *cocoaType)
 {
     // UTI may not do these right, so make sure we get the right, predictable result.
@@ -425,16 +437,23 @@ void Pasteboard::writeString(const String& type, const String& data)
 
 Vector<String> Pasteboard::types()
 {
-    Vector<String> cocoaTypes;
-    platformStrategies()->pasteboardStrategy()->getTypes(cocoaTypes, m_pasteboardName);
+    Vector<String> types;
+    if (Settings::customPasteboardDataEnabled())
+        types = platformStrategies()->pasteboardStrategy()->typesSafeForDOMToReadAndWrite(m_pasteboardName);
+    else
+        platformStrategies()->pasteboardStrategy()->getTypes(types, m_pasteboardName);
 
     // Enforce changeCount ourselves for security. We check after reading instead of before to be
     // sure it doesn't change between our testing the change count and accessing the data.
-    if (m_changeCount != changeCountForPasteboard(m_pasteboardName))
-        return Vector<String>();
+    auto changeCount = changeCountForPasteboard(m_pasteboardName);
+    if (m_changeCount != changeCount)
+        return { };
+
+    if (Settings::customPasteboardDataEnabled())
+        return types;
 
     ListHashSet<String> result;
-    for (auto cocoaType : cocoaTypes)
+    for (auto cocoaType : types)
         addHTMLClipboardTypesForCocoaType(result, cocoaType);
 
     Vector<String> vector;
index ab9f1a0..77bb7f2 100644 (file)
@@ -37,7 +37,9 @@
 #import <UIKit/UIImage.h>
 #import <UIKit/UIPasteboard.h>
 #import <pal/spi/ios/UIKitSPI.h>
+#import <wtf/ListHashSet.h>
 #import <wtf/SoftLinking.h>
+#import <wtf/text/StringHash.h>
 
 SOFT_LINK_FRAMEWORK(UIKit)
 SOFT_LINK_CLASS(UIKit, UIImage)
@@ -81,16 +83,18 @@ void PlatformPasteboard::getTypesByFidelityForItemAtIndex(Vector<String>& types,
         types.append(typeIdentifier);
 }
 
-RefPtr<SharedBuffer> PlatformPasteboard::bufferForType(const String&)
+RefPtr<SharedBuffer> PlatformPasteboard::bufferForType(const String& type)
 {
+    if (NSData *data = [m_pasteboard dataForPasteboardType:type])
+        return SharedBuffer::create(data);
     return nullptr;
 }
 
-void PlatformPasteboard::getPathnamesForType(Vector<String>&, const String&)
+void PlatformPasteboard::getPathnamesForType(Vector<String>&, const String&) const
 {
 }
 
-int PlatformPasteboard::numberOfFiles()
+int PlatformPasteboard::numberOfFiles() const
 {
     return [m_pasteboard respondsToSelector:@selector(numberOfFiles)] ? [m_pasteboard numberOfFiles] : 0;
 }
@@ -173,6 +177,20 @@ String PlatformPasteboard::uniqueName()
     return String();
 }
 
+String PlatformPasteboard::platformPasteboardTypeForSafeTypeForDOMToReadAndWrite(const String& domType)
+{
+    if (domType == "text/plain")
+        return kUTTypePlainText;
+
+    if (domType == "text/html")
+        return kUTTypeHTML;
+
+    if (domType == "text/uri-list")
+        return kUTTypeURL;
+
+    return { };
+}
+
 static NSString *webIOSPastePboardType = @"iOS rich content paste pasteboard type";
 
 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
@@ -274,6 +292,11 @@ void PlatformPasteboard::write(const PasteboardWebContent& content)
     if (content.dataInRTFFormat)
         [representationsToRegister addData:content.dataInRTFFormat->createNSData().get() forType:(NSString *)kUTTypeRTF];
 
+    if (!content.dataInHTMLFormat.isEmpty()) {
+        NSData *htmlAsData = [(NSString *)content.dataInHTMLFormat dataUsingEncoding:NSUTF8StringEncoding];
+        [representationsToRegister addData:htmlAsData forType:(NSString *)kUTTypeHTML];
+    }
+
     if (!content.dataInStringFormat.isEmpty())
         addRepresentationsForPlainText(representationsToRegister.get(), content.dataInStringFormat);
 
@@ -396,6 +419,103 @@ void PlatformPasteboard::write(const PasteboardURL& url)
 #endif
 }
 
+static const char *safeTypeForDOMToReadAndWriteForPlatformType(const String& platformType)
+{
+    auto cfType = platformType.createCFString();
+    if (UTTypeConformsTo(cfType.get(), kUTTypePlainText))
+        return ASCIILiteral("text/plain");
+
+    if (UTTypeConformsTo(cfType.get(), kUTTypeHTML))
+        return ASCIILiteral("text/html");
+
+    if (UTTypeConformsTo(cfType.get(), kUTTypeURL))
+        return ASCIILiteral("text/uri-list");
+
+    return nullptr;
+}
+
+Vector<String> PlatformPasteboard::typesSafeForDOMToReadAndWrite() const
+{
+    ListHashSet<String> domPasteboardTypes;
+    for (NSItemProvider *provider in [m_pasteboard itemProviders]) {
+        if (!provider.teamData.length)
+            continue;
+
+        id teamDataObject = [NSKeyedUnarchiver unarchiveObjectWithData:provider.teamData];
+        if (!teamDataObject || ![teamDataObject isKindOfClass:[NSDictionary class]])
+            continue;
+
+        id customTypes = [(NSDictionary *)teamDataObject objectForKey:@(customWebKitPasteboardDataType)];
+        if (![customTypes isKindOfClass:[NSArray class]])
+            continue;
+
+        for (NSString *type in customTypes)
+            domPasteboardTypes.add(type);
+    }
+
+    if (NSData *serializedCustomData = [m_pasteboard dataForPasteboardType:@(customWebKitPasteboardDataType)]) {
+        auto buffer = SharedBuffer::create(serializedCustomData);
+        for (auto& type : customDataFromSharedBuffer(buffer.get()).orderedTypes)
+            domPasteboardTypes.add(type);
+    }
+
+    for (NSString *type in [m_pasteboard pasteboardTypes]) {
+        if ([type isEqualToString:@(customWebKitPasteboardDataType)])
+            continue;
+
+        if (isSafeTypeForDOMToReadAndWrite(type)) {
+            domPasteboardTypes.add(type);
+            continue;
+        }
+
+        if (auto* coercedType = safeTypeForDOMToReadAndWriteForPlatformType(type))
+            domPasteboardTypes.add(String::fromUTF8(coercedType));
+    }
+
+    Vector<String> result;
+    copyToVector(domPasteboardTypes, result);
+    return result;
+}
+
+long PlatformPasteboard::write(const PasteboardCustomData& data)
+{
+    auto representationsToRegister = adoptNS([[WebItemProviderRegistrationInfoList alloc] init]);
+    [representationsToRegister setPreferredPresentationStyle:WebPreferredPresentationStyleInline];
+
+    if (data.sameOriginCustomData.size()) {
+        if (auto serializedSharedBuffer = sharedBufferFromCustomData(data)->createNSData()) {
+            // We stash the list of supplied pasteboard types in teamData here for compatibility with drag and drop.
+            // Since the contents of item providers cannot be loaded prior to drop, but the pasteboard types are
+            // contained within the custom data blob and we need to vend them to the page when firing `dragover`
+            // events, we need an additional in-memory representation of the pasteboard types array that contains
+            // all of the custom types. We use the teamData property, available on NSItemProvider on iOS, to store
+            // this information, since the contents of teamData are immediately available prior to the drop.
+            NSMutableArray<NSString *> *typesAsNSArray = [NSMutableArray array];
+            for (auto& type : data.orderedTypes)
+                [typesAsNSArray addObject:type];
+            [representationsToRegister setTeamData:[NSKeyedArchiver archivedDataWithRootObject:@{ @(customWebKitPasteboardDataType) : typesAsNSArray }]];
+            [representationsToRegister addData:serializedSharedBuffer.get() forType:@(customWebKitPasteboardDataType)];
+        }
+    }
+
+    for (auto& type : data.orderedTypes) {
+        NSString *stringValue = data.platformData.get(type);
+        if (!stringValue.length)
+            continue;
+
+        auto cocoaType = platformPasteboardTypeForSafeTypeForDOMToReadAndWrite(type).createCFString();
+        if (UTTypeConformsTo(cocoaType.get(), kUTTypeURL))
+            [representationsToRegister addRepresentingObject:[NSURL URLWithString:stringValue]];
+        else if (UTTypeConformsTo(cocoaType.get(), kUTTypePlainText))
+            [representationsToRegister addRepresentingObject:stringValue];
+        else
+            [representationsToRegister addData:[stringValue dataUsingEncoding:NSUTF8StringEncoding] forType:(NSString *)cocoaType.get()];
+    }
+
+    registerItemToPasteboard(representationsToRegister.get(), m_pasteboard.get());
+    return [m_pasteboard changeCount];
+}
+
 int PlatformPasteboard::count()
 {
     return [m_pasteboard numberOfItems];
@@ -416,25 +536,32 @@ String PlatformPasteboard::readString(int index, const String& type)
 {
     NSIndexSet *indexSet = [NSIndexSet indexSetWithIndex:index];
 
-    RetainPtr<NSArray> pasteboardItem = [m_pasteboard valuesForPasteboardType:type inItemSet:indexSet];
-
-    if (![pasteboardItem count])
-        return String();
+    NSArray *pasteboardValues = [m_pasteboard valuesForPasteboardType:type inItemSet:indexSet];
+    if (!pasteboardValues.count) {
+        NSArray<NSData *> *pasteboardData = [m_pasteboard dataForPasteboardType:type inItemSet:indexSet];
+        if (!pasteboardData.count)
+            return { };
+        pasteboardValues = pasteboardData;
+    }
 
-    id value = [pasteboardItem objectAtIndex:0];
+    RetainPtr<id> value = [pasteboardValues objectAtIndex:0];
+    if ([value isKindOfClass:[NSData class]])
+        value = adoptNS([[NSString alloc] initWithData:(NSData *)value.get() encoding:NSUTF8StringEncoding]);
     
     if (type == String(kUTTypePlainText) || type == String(kUTTypeHTML)) {
         ASSERT([value isKindOfClass:[NSString class]]);
-        return [value isKindOfClass:[NSString class]] ? value : nil;
+        return [value isKindOfClass:[NSString class]] ? value.get() : nil;
     }
     if (type == String(kUTTypeText)) {
         ASSERT([value isKindOfClass:[NSString class]] || [value isKindOfClass:[NSAttributedString class]]);
         if ([value isKindOfClass:[NSString class]])
-            return value;
+            return value.get();
         if ([value isKindOfClass:[NSAttributedString class]])
             return [(NSAttributedString *)value string];
     } else if (type == String(kUTTypeURL)) {
-        ASSERT([value isKindOfClass:[NSURL class]]);
+        ASSERT([value isKindOfClass:[NSURL class]] || [value isKindOfClass:[NSString class]]);
+        if ([value isKindOfClass:[NSString class]])
+            value = [NSURL URLWithString:value.get()];
         if ([value isKindOfClass:[NSURL class]] && allowReadingURLAtIndex((NSURL *)value, index))
             return [(NSURL *)value absoluteString];
     }
index 275ea45..abff268 100644 (file)
@@ -71,6 +71,7 @@ WEBCORE_EXPORT @interface WebItemProviderRegistrationInfoList : NSObject
 @property (nonatomic, readonly, nullable) UIItemProvider *itemProvider;
 
 @property (nonatomic) WebPreferredPresentationStyle preferredPresentationStyle;
+@property (nonatomic, copy) NSData *teamData;
 
 - (NSUInteger)numberOfItems;
 - (nullable WebItemProviderRegistrationInfo *)itemAtIndex:(NSUInteger)index;
index 4b9c3a5..d977195 100644 (file)
@@ -50,7 +50,7 @@ SOFT_LINK_CLASS(UIKit, UIItemProvider)
 using namespace WebCore;
 
 typedef void(^ItemProviderDataLoadCompletionHandler)(NSData *, NSError *);
-typedef NSDictionary<NSString *, NSURL *> TypeToFileURLMap;
+typedef NSMutableDictionary<NSString *, NSURL *> TypeToFileURLMap;
 
 @interface WebItemProviderRegistrationInfo ()
 {
@@ -188,6 +188,7 @@ static UIPreferredPresentationStyle uiPreferredPresentationStyle(WebPreferredPre
     [itemProvider setPreferredPresentationSize:self.preferredPresentationSize];
     [itemProvider setSuggestedName:self.suggestedName];
     [itemProvider setPreferredPresentationStyle:uiPreferredPresentationStyle(self.preferredPresentationStyle)];
+    [itemProvider setTeamData:self.teamData];
     return itemProvider.autorelease();
 }
 
@@ -212,46 +213,56 @@ static UIPreferredPresentationStyle uiPreferredPresentationStyle(WebPreferredPre
 
 @interface WebItemProviderLoadResult : NSObject
 
-+ (instancetype)loadResultWithFileURLMap:(TypeToFileURLMap *)fileURLs presentationStyle:(UIPreferredPresentationStyle)presentationStyle;
-+ (instancetype)emptyLoadResult;
-
+- (instancetype)initWithItemProvider:(NSItemProvider *)itemProvider typesToLoad:(NSArray<NSString *> *)typesToLoad;
 - (NSURL *)fileURLForType:(NSString *)type;
+- (void)setFileURL:(NSURL *)url forType:(NSString *)type;
 @property (nonatomic, readonly) NSArray<NSURL *> *loadedFileURLs;
 @property (nonatomic, readonly) NSArray<NSString *> *loadedTypeIdentifiers;
 @property (nonatomic, readonly) BOOL canBeRepresentedAsFileUpload;
+@property (nonatomic, readonly) NSItemProvider *itemProvider;
+@property (nonatomic, readonly) NSArray<NSString *> *typesToLoad;
 
 @end
 
 @implementation WebItemProviderLoadResult {
     RetainPtr<TypeToFileURLMap> _fileURLs;
+    RetainPtr<NSItemProvider> _itemProvider;
+    RetainPtr<NSArray<NSString *>> _typesToLoad;
 }
 
-+ (instancetype)emptyLoadResult
-{
-    return [[[self alloc] initWithFileURLMap:@{ } presentationStyle:UIPreferredPresentationStyleUnspecified] autorelease];
-}
-
-+ (instancetype)loadResultWithFileURLMap:(TypeToFileURLMap *)fileURLs presentationStyle:(UIPreferredPresentationStyle)presentationStyle
++ (instancetype)loadResultWithItemProvider:(NSItemProvider *)itemProvider typesToLoad:(NSArray<NSString *> *)typesToLoad
 {
-    return [[[self alloc] initWithFileURLMap:fileURLs presentationStyle:presentationStyle] autorelease];
+    return [[[self alloc] initWithItemProvider:itemProvider typesToLoad:typesToLoad] autorelease];
 }
 
-- (instancetype)initWithFileURLMap:(TypeToFileURLMap *)fileURLs presentationStyle:(UIPreferredPresentationStyle)presentationStyle
+- (instancetype)initWithItemProvider:(NSItemProvider *)itemProvider typesToLoad:(NSArray<NSString *> *)typesToLoad
 {
     if (!(self = [super init]))
         return nil;
 
-    _fileURLs = fileURLs;
-    _canBeRepresentedAsFileUpload = presentationStyle != UIPreferredPresentationStyleInline;
+    _fileURLs = adoptNS([[NSMutableDictionary alloc] init]);
+    _itemProvider = itemProvider;
+    _typesToLoad = typesToLoad;
+    _canBeRepresentedAsFileUpload = itemProvider.preferredPresentationStyle != UIPreferredPresentationStyleInline;
 
     return self;
 }
 
+- (NSArray<NSString *> *)typesToLoad
+{
+    return _typesToLoad.get();
+}
+
 - (NSURL *)fileURLForType:(NSString *)type
 {
     return [_fileURLs objectForKey:type];
 }
 
+- (void)setFileURL:(NSURL *)url forType:(NSString *)type
+{
+    [_fileURLs setObject:url forKey:type];
+}
+
 - (NSArray<NSURL *> *)loadedFileURLs
 {
     return [_fileURLs allValues];
@@ -262,6 +273,11 @@ static UIPreferredPresentationStyle uiPreferredPresentationStyle(WebPreferredPre
     return [_fileURLs allKeys];
 }
 
+- (NSItemProvider *)itemProvider
+{
+    return _itemProvider.get();
+}
+
 @end
 
 @interface WebItemProviderPasteboard ()
@@ -343,6 +359,9 @@ static UIPreferredPresentationStyle uiPreferredPresentationStyle(WebPreferredPre
 
     _itemProviders = itemProviders;
     _changeCount++;
+
+    if (!itemProviders.count)
+        _loadResults = { };
 }
 
 - (NSInteger)numberOfItems
@@ -369,6 +388,11 @@ static UIPreferredPresentationStyle uiPreferredPresentationStyle(WebPreferredPre
     return nil;
 }
 
+- (NSData *)dataForPasteboardType:(NSString *)pasteboardType
+{
+    return [self dataForPasteboardType:pasteboardType inItemSet:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, self.numberOfItems)]].firstObject;
+}
+
 - (NSArray *)dataForPasteboardType:(NSString *)pasteboardType inItemSet:(NSIndexSet *)itemSet
 {
     auto values = adoptNS([[NSMutableArray alloc] init]);
@@ -504,17 +528,41 @@ static NSURL *linkTemporaryItemProviderFilesToDropStagingDirectory(NSURL *url, N
     return [fileManager linkItemAtURL:url toURL:destination error:nil] ? destination : nil;
 }
 
-- (NSString *)typeIdentifierToLoadForRegisteredTypeIdentfiers:(NSArray<NSString *> *)registeredTypeIdentifiers
+- (NSArray<NSString *> *)typeIdentifiersToLoadForRegisteredTypeIdentfiers:(NSArray<NSString *> *)registeredTypeIdentifiers
 {
+    NSMutableSet *typesToLoad = [NSMutableSet set];
     NSString *highestFidelityContentType = nil;
+
+    // First, we want to either load the highest fidelity supported type or the highest fidelity generic content type.
     for (NSString *registeredTypeIdentifier in registeredTypeIdentifiers) {
-        if (typeConformsToTypes(registeredTypeIdentifier, _supportedTypeIdentifiers.get()))
-            return registeredTypeIdentifier;
+        if (typeConformsToTypes(registeredTypeIdentifier, _supportedTypeIdentifiers.get())) {
+            [typesToLoad addObject:registeredTypeIdentifier];
+            break;
+        }
 
         if (!highestFidelityContentType && UTTypeConformsTo((CFStringRef)registeredTypeIdentifier, kUTTypeContent))
             highestFidelityContentType = registeredTypeIdentifier;
     }
-    return highestFidelityContentType;
+    if (!typesToLoad.count && highestFidelityContentType)
+        [typesToLoad addObject:highestFidelityContentType];
+
+    // For compatibility with DataTransfer APIs, additionally load web-exposed types. Since this is the only chance to
+    // fault in any data at all, we need to load up front any information that the page may ask for later down the line.
+    NSString *customPasteboardDataUTI = @(customWebKitPasteboardDataType);
+    for (NSString *registeredTypeIdentifier in registeredTypeIdentifiers) {
+        if ([typesToLoad containsObject:registeredTypeIdentifier])
+            continue;
+        if ([registeredTypeIdentifier isEqualToString:customPasteboardDataUTI])
+            [typesToLoad addObject:customPasteboardDataUTI];
+        if (UTTypeConformsTo((CFStringRef)registeredTypeIdentifier, kUTTypeURL))
+            [typesToLoad addObject:(NSString *)kUTTypeURL];
+        if (UTTypeConformsTo((CFStringRef)registeredTypeIdentifier, kUTTypeHTML))
+            [typesToLoad addObject:(NSString *)kUTTypeHTML];
+        if (UTTypeConformsTo((CFStringRef)registeredTypeIdentifier, kUTTypePlainText))
+            [typesToLoad addObject:(NSString *)kUTTypePlainText];
+    }
+
+    return typesToLoad.allObjects;
 }
 
 - (void)doAfterLoadingProvidedContentIntoFileURLs:(WebItemProviderFileLoadBlock)action
@@ -530,21 +578,15 @@ static NSURL *linkTemporaryItemProviderFilesToDropStagingDirectory(NSURL *url, N
     auto loadResults = adoptNS([[NSMutableArray alloc] initWithCapacity:[_itemProviders count]]);
 
     // First, figure out which item providers we want to try and load files from.
-    auto itemProvidersToLoad = adoptNS([[NSMutableArray alloc] init]);
-    auto typeIdentifiersToLoad = adoptNS([[NSMutableArray alloc] init]);
-    auto indicesOfitemProvidersToLoad = adoptNS([[NSMutableArray alloc] init]);
+    BOOL foundAnyDataToLoad = NO;
     RetainPtr<WebItemProviderPasteboard> protectedSelf = self;
-    [_itemProviders enumerateObjectsUsingBlock:[protectedSelf, itemProvidersToLoad, typeIdentifiersToLoad, indicesOfitemProvidersToLoad, loadResults] (UIItemProvider *itemProvider, NSUInteger index, BOOL *) {
-        NSString *typeIdentifierToLoad = [protectedSelf typeIdentifierToLoadForRegisteredTypeIdentfiers:itemProvider.registeredTypeIdentifiers];
-        if (typeIdentifierToLoad) {
-            [itemProvidersToLoad addObject:itemProvider];
-            [typeIdentifiersToLoad addObject:typeIdentifierToLoad];
-            [indicesOfitemProvidersToLoad addObject:@(index)];
-        }
-        [loadResults addObject:[WebItemProviderLoadResult emptyLoadResult]];
-    }];
+    for (NSItemProvider *itemProvider in _itemProviders.get()) {
+        NSArray<NSString *> *typeIdentifiersToLoad = [protectedSelf typeIdentifiersToLoadForRegisteredTypeIdentfiers:itemProvider.registeredTypeIdentifiers];
+        foundAnyDataToLoad |= typeIdentifiersToLoad.count;
+        [loadResults addObject:[WebItemProviderLoadResult loadResultWithItemProvider:itemProvider typesToLoad:typeIdentifiersToLoad]];
+    }
 
-    if (![itemProvidersToLoad count]) {
+    if (!foundAnyDataToLoad) {
         action(@[ ]);
         return;
     }
@@ -552,27 +594,23 @@ static NSURL *linkTemporaryItemProviderFilesToDropStagingDirectory(NSURL *url, N
     auto setFileURLsLock = adoptNS([[NSLock alloc] init]);
     auto synchronousFileLoadingGroup = adoptOSObject(dispatch_group_create());
     auto fileLoadingGroup = adoptOSObject(dispatch_group_create());
-    for (NSUInteger index = 0; index < [itemProvidersToLoad count]; ++index) {
-        RetainPtr<UIItemProvider> itemProvider = [itemProvidersToLoad objectAtIndex:index];
-        RetainPtr<NSString> typeIdentifier = [typeIdentifiersToLoad objectAtIndex:index];
-        NSUInteger indexInItemProviderArray = [[indicesOfitemProvidersToLoad objectAtIndex:index] unsignedIntegerValue];
-        RetainPtr<NSString> suggestedName = [itemProvider suggestedName];
-        auto presentationStyle = [itemProvider preferredPresentationStyle];
-        dispatch_group_enter(fileLoadingGroup.get());
-        dispatch_group_enter(synchronousFileLoadingGroup.get());
-        [itemProvider loadFileRepresentationForTypeIdentifier:typeIdentifier.get() completionHandler:[synchronousFileLoadingGroup, setFileURLsLock, indexInItemProviderArray, suggestedName, typeIdentifier, loadResults, fileLoadingGroup, presentationStyle] (NSURL *url, NSError *) {
-            // After executing this completion block, UIKit removes the file at the given URL. However, we need this data to persist longer for the web content process.
-            // To address this, we hard link the given URL to a new temporary file in the temporary directory. This follows the same flow as regular file upload, in
-            // WKFileUploadPanel.mm. The temporary files are cleaned up by the system at a later time.
-            if (NSURL *destination = linkTemporaryItemProviderFilesToDropStagingDirectory(url, suggestedName.get(), typeIdentifier.get())) {
-                WebItemProviderLoadResult *loadResult = [WebItemProviderLoadResult loadResultWithFileURLMap:@{ typeIdentifier.get() : destination } presentationStyle:presentationStyle];
-                [setFileURLsLock lock];
-                [loadResults setObject:loadResult atIndexedSubscript:indexInItemProviderArray];
-                [setFileURLsLock unlock];
-            }
-            dispatch_group_leave(fileLoadingGroup.get());
-            dispatch_group_leave(synchronousFileLoadingGroup.get());
-        }];
+    for (WebItemProviderLoadResult *loadResult in loadResults.get()) {
+        for (NSString *typeToLoad in loadResult.typesToLoad) {
+            dispatch_group_enter(fileLoadingGroup.get());
+            dispatch_group_enter(synchronousFileLoadingGroup.get());
+            [loadResult.itemProvider loadFileRepresentationForTypeIdentifier:typeToLoad completionHandler:[protectedLoadResult = retainPtr(loadResult), synchronousFileLoadingGroup, setFileURLsLock, protectedTypeToLoad = retainPtr(typeToLoad), fileLoadingGroup] (NSURL *url, NSError *) {
+                // After executing this completion block, UIKit removes the file at the given URL. However, we need this data to persist longer for the web content process.
+                // To address this, we hard link the given URL to a new temporary file in the temporary directory. This follows the same flow as regular file upload, in
+                // WKFileUploadPanel.mm. The temporary files are cleaned up by the system at a later time.
+                if (NSURL *destination = linkTemporaryItemProviderFilesToDropStagingDirectory(url, [protectedLoadResult itemProvider].suggestedName, protectedTypeToLoad.get())) {
+                    [setFileURLsLock lock];
+                    [protectedLoadResult setFileURL:destination forType:protectedTypeToLoad.get()];
+                    [setFileURLsLock unlock];
+                }
+                dispatch_group_leave(fileLoadingGroup.get());
+                dispatch_group_leave(synchronousFileLoadingGroup.get());
+            }];
+        }
     }
 
     RetainPtr<WebItemProviderPasteboard> retainedSelf = self;
index df332b6..926394d 100644 (file)
 #import "LoaderNSURLExtras.h"
 #import "MIMETypeRegistry.h"
 #import "PasteboardStrategy.h"
+#import "PlatformPasteboard.h"
 #import "PlatformStrategies.h"
 #import "RenderImage.h"
+#import "Settings.h"
 #import "Text.h"
 #import "URL.h"
 #import "UTIUtilities.h"
@@ -195,6 +197,11 @@ void Pasteboard::write(const PasteboardWebContent& content)
         m_changeCount = platformStrategies()->pasteboardStrategy()->setStringForType(content.dataInStringFormat, NSStringPboardType, m_pasteboardName);
 }
 
+void Pasteboard::writeCustomData(const PasteboardCustomData& data)
+{
+    m_changeCount = platformStrategies()->pasteboardStrategy()->writeCustomData(data, m_pasteboardName);
+}
+
 void Pasteboard::writePlainText(const String& text, SmartReplaceOption smartReplaceOption)
 {
     Vector<String> types;
@@ -460,12 +467,9 @@ bool Pasteboard::hasData()
 
 static String cocoaTypeFromHTMLClipboardType(const String& type)
 {
-    // Ignore any trailing charset - strings are already UTF-16, and the charset issue has already been dealt with.
-    if (type == "text/plain")
-        return NSStringPboardType;
-    if (type == "text/uri-list") {
-        // Special case because UTI doesn't work with Cocoa's URL type.
-        return NSURLPboardType;
+    if (NSString *platformType = PlatformPasteboard::platformPasteboardTypeForSafeTypeForDOMToReadAndWrite(type)) {
+        if (platformType.length)
+            return platformType;
     }
 
     // Blacklist types that might contain subframe information.
@@ -507,24 +511,38 @@ static Vector<String> absoluteURLsFromPasteboardFilenames(const String& pasteboa
     return urls;
 }
 
-String Pasteboard::readString(const String& type)
+static String readPlatformValueAsString(const String& domType, long changeCount, const String& pasteboardName)
 {
-    const String& cocoaType = cocoaTypeFromHTMLClipboardType(type);
+    const String& cocoaType = cocoaTypeFromHTMLClipboardType(domType);
     String cocoaValue;
 
     if (cocoaType == String(NSStringPboardType))
-        cocoaValue = [platformStrategies()->pasteboardStrategy()->stringForType(cocoaType, m_pasteboardName) precomposedStringWithCanonicalMapping];
+        cocoaValue = [platformStrategies()->pasteboardStrategy()->stringForType(cocoaType, pasteboardName) precomposedStringWithCanonicalMapping];
     else if (!cocoaType.isEmpty())
-        cocoaValue = platformStrategies()->pasteboardStrategy()->stringForType(cocoaType, m_pasteboardName);
+        cocoaValue = platformStrategies()->pasteboardStrategy()->stringForType(cocoaType, pasteboardName);
 
     // Enforce changeCount ourselves for security.  We check after reading instead of before to be
     // sure it doesn't change between our testing the change count and accessing the data.
-    if (!cocoaValue.isEmpty() && m_changeCount == platformStrategies()->pasteboardStrategy()->changeCount(m_pasteboardName))
+    if (!cocoaValue.isEmpty() && changeCount == platformStrategies()->pasteboardStrategy()->changeCount(pasteboardName))
         return cocoaValue;
 
     return String();
 }
 
+String Pasteboard::readStringForBindings(const String& type)
+{
+    if (!Settings::customPasteboardDataEnabled() || isSafeTypeForDOMToReadAndWrite(type))
+        return readPlatformValueAsString(type, m_changeCount, m_pasteboardName);
+
+    if (auto buffer = platformStrategies()->pasteboardStrategy()->bufferForType(customWebKitPasteboardDataType, m_pasteboardName)) {
+        NSString *customDataValue = customDataFromSharedBuffer(*buffer).sameOriginCustomData.get(type);
+        if (customDataValue.length)
+            return customDataValue;
+    }
+
+    return { };
+}
+
 static String utiTypeFromCocoaType(const String& type)
 {
     if (RetainPtr<CFStringRef> utiType = adoptCF(UTTypeCreatePreferredIdentifierForTag(kUTTagClassNSPboardType, type.createCFString().get(), 0))) {
@@ -594,12 +612,18 @@ void Pasteboard::writeString(const String& type, const String& data)
 Vector<String> Pasteboard::types()
 {
     Vector<String> types;
-    platformStrategies()->pasteboardStrategy()->getTypes(types, m_pasteboardName);
+    if (Settings::customPasteboardDataEnabled())
+        types = platformStrategies()->pasteboardStrategy()->typesSafeForDOMToReadAndWrite(m_pasteboardName);
+    else
+        platformStrategies()->pasteboardStrategy()->getTypes(types, m_pasteboardName);
 
     // Enforce changeCount ourselves for security. We check after reading instead of before to be
     // sure it doesn't change between our testing the change count and accessing the data.
     if (m_changeCount != platformStrategies()->pasteboardStrategy()->changeCount(m_pasteboardName))
-        return Vector<String>();
+        return { };
+
+    if (Settings::customPasteboardDataEnabled())
+        return types;
 
     ListHashSet<String> result;
     // FIXME: This loop could be split into two stages. One which adds all the HTML5 specified types
index 16a248c..82b7094 100644 (file)
  */
 
 #import "config.h"
+#import "PlatformPasteboard.h"
+
 #import "Color.h"
+#import "Pasteboard.h"
+#import "Settings.h"
 #import "URL.h"
-#import "PlatformPasteboard.h"
 #import "SharedBuffer.h"
+#import <wtf/HashCountedSet.h>
+#import <wtf/ListHashSet.h>
+#import <wtf/text/StringHash.h>
 
 namespace WebCore {
 
@@ -53,7 +59,7 @@ RefPtr<SharedBuffer> PlatformPasteboard::bufferForType(const String& pasteboardT
     return SharedBuffer::create([[data copy] autorelease]);
 }
 
-int PlatformPasteboard::numberOfFiles()
+int PlatformPasteboard::numberOfFiles() const
 {
     Vector<String> files;
     getPathnamesForType(files, String(NSFilenamesPboardType));
@@ -62,7 +68,7 @@ int PlatformPasteboard::numberOfFiles()
     return files.size();
 }
 
-void PlatformPasteboard::getPathnamesForType(Vector<String>& pathnames, const String& pasteboardType)
+void PlatformPasteboard::getPathnamesForType(Vector<String>& pathnames, const String& pasteboardType) const
 {
     NSArray* paths = [m_pasteboard.get() propertyListForType:pasteboardType];
     if ([paths isKindOfClass:[NSString class]]) {
@@ -75,10 +81,88 @@ void PlatformPasteboard::getPathnamesForType(Vector<String>& pathnames, const St
 
 String PlatformPasteboard::stringForType(const String& pasteboardType)
 {
-    if (pasteboardType == String(NSURLPboardType))
-        return [[NSURL URLFromPasteboard:m_pasteboard.get()] absoluteString];
+    if (pasteboardType == String(NSURLPboardType)) {
+        if (NSURL *urlFromPasteboard = [NSURL URLFromPasteboard:m_pasteboard.get()])
+            return urlFromPasteboard.absoluteString;
+
+        URL url([NSURL URLWithString:[m_pasteboard stringForType:NSURLPboardType]]);
+        if (!url.isValid())
+            return { };
+        return url.string();
+    }
+
+    return [m_pasteboard stringForType:pasteboardType];
+}
+
+static const char* safeTypeForDOMToReadAndWriteForPlatformType(const String& platformType)
+{
+    if (platformType == String(NSStringPboardType) || platformType == String(NSPasteboardTypeString))
+        return ASCIILiteral("text/plain");
+
+    if (platformType == String(NSURLPboardType))
+        return ASCIILiteral("text/uri-list");
+
+    if (platformType == String(NSHTMLPboardType))
+        return ASCIILiteral("text/html");
 
-    return [m_pasteboard.get() stringForType:pasteboardType];
+    if (platformType == String(NSFilenamesPboardType) || platformType == String(NSFilesPromisePboardType))
+        return ASCIILiteral("Files");
+
+    return nullptr;
+}
+
+Vector<String> PlatformPasteboard::typesSafeForDOMToReadAndWrite() const
+{
+    ListHashSet<String> domPasteboardTypes;
+    if (NSData *serializedCustomData = [m_pasteboard dataForType:@(customWebKitPasteboardDataType)]) {
+        auto buffer = SharedBuffer::create(serializedCustomData);
+        for (auto& type : customDataFromSharedBuffer(buffer.get()).orderedTypes)
+            domPasteboardTypes.add(type);
+    }
+
+    NSArray<NSString *> *allTypes = [m_pasteboard types];
+    for (NSString *type in allTypes) {
+        if ([type isEqualToString:@(customWebKitPasteboardDataType)])
+            continue;
+
+        if (isSafeTypeForDOMToReadAndWrite(type))
+            domPasteboardTypes.add(type);
+        else if (auto* domType = safeTypeForDOMToReadAndWriteForPlatformType(type)) {
+            auto coercedType = String::fromUTF8(domType);
+            if (coercedType == "Files" && !numberOfFiles())
+                continue;
+            domPasteboardTypes.add(WTFMove(coercedType));
+        }
+    }
+
+    Vector<String> result;
+    copyToVector(domPasteboardTypes, result);
+    return result;
+}
+
+long PlatformPasteboard::write(const PasteboardCustomData& data)
+{
+    NSMutableArray *types = [NSMutableArray array];
+    for (auto& entry : data.platformData)
+        [types addObject:platformPasteboardTypeForSafeTypeForDOMToReadAndWrite(entry.key)];
+    if (data.sameOriginCustomData.size())
+        [types addObject:@(customWebKitPasteboardDataType)];
+
+    [m_pasteboard declareTypes:types owner:nil];
+
+    for (auto& entry : data.platformData) {
+        auto platformType = platformPasteboardTypeForSafeTypeForDOMToReadAndWrite(entry.key);
+        ASSERT(!platformType.isEmpty());
+        if (!platformType.isEmpty())
+            [m_pasteboard setString:entry.value forType:platformType];
+    }
+
+    if (data.sameOriginCustomData.size()) {
+        if (auto serializedCustomData = sharedBufferFromCustomData(data)->createNSData())
+            [m_pasteboard setData:serializedCustomData.get() forType:@(customWebKitPasteboardDataType)];
+    }
+
+    return changeCount();
 }
 
 long PlatformPasteboard::changeCount() const
@@ -86,6 +170,20 @@ long PlatformPasteboard::changeCount() const
     return [m_pasteboard.get() changeCount];
 }
 
+String PlatformPasteboard::platformPasteboardTypeForSafeTypeForDOMToReadAndWrite(const String& domType)
+{
+    if (domType == "text/plain")
+        return NSStringPboardType;
+
+    if (domType == "text/html")
+        return NSHTMLPboardType;
+
+    if (domType == "text/uri-list")
+        return NSURLPboardType;
+
+    return { };
+}
+
 String PlatformPasteboard::uniqueName()
 {
     return [[NSPasteboard pasteboardWithUniqueName] name];
index 72aecf6..b34945c 100644 (file)
@@ -277,7 +277,7 @@ Vector<String> Pasteboard::types()
     return vector;
 }
 
-String Pasteboard::readString(const String& type)
+String Pasteboard::readStringForBindings(const String& type)
 {
     if (!m_dataObject && m_dragDataMap.isEmpty())
         return "";
@@ -1054,4 +1054,8 @@ void Pasteboard::write(const PasteboardImage&)
 {
 }
 
+void Pasteboard::writeCustomData(const PasteboardCustomData&)
+{
+}
+
 } // namespace WebCore
index da476a6..d3254b9 100644 (file)
@@ -56,7 +56,7 @@ Vector<String> Pasteboard::types()
     return types;
 }
 
-String Pasteboard::readString(const String& type)
+String Pasteboard::readStringForBindings(const String& type)
 {
     return platformStrategies()->pasteboardStrategy()->readStringFromPasteboard(0, type);
 }
@@ -122,4 +122,8 @@ void Pasteboard::writePlainText(const String& text, SmartReplaceOption)
     writeString("text/plain;charset=utf-8", text);
 }
 
+void Pasteboard::writeCustomData(const PasteboardCustomData&)
+{
+}
+
 } // namespace WebCore
index 449c367..3bded13 100644 (file)
@@ -116,4 +116,14 @@ void PlatformPasteboard::write(const String& type, const String& string)
     wpe_pasteboard_string_free(&pairs[0].string);
 }
 
+Vector<String> PlatformPasteboard::typesSafeForDOMToReadAndWrite() const
+{
+    return { };
+}
+
+long PlatformPasteboard::write(const PasteboardCustomData&)
+{
+    return 0;
+}
+
 } // namespace WebCore
index 9a8ce77..5321fe7 100644 (file)
@@ -114,6 +114,7 @@ InternalSettings::Backup::Backup(Settings& settings)
 #if USE(AUDIO_SESSION)
     , m_shouldManageAudioSessionCategory(Settings::shouldManageAudioSessionCategory())
 #endif
+    , m_customPasteboardDataEnabled(Settings::customPasteboardDataEnabled())
 {
 }
 
@@ -210,6 +211,7 @@ void InternalSettings::Backup::restoreTo(Settings& settings)
 #if USE(AUDIO_SESSION)
     Settings::setShouldManageAudioSessionCategory(m_shouldManageAudioSessionCategory);
 #endif
+    Settings::setCustomPasteboardDataEnabled(m_customPasteboardDataEnabled);
 }
 
 class InternalSettingsWrapper : public Supplement<Page> {
@@ -852,6 +854,12 @@ ExceptionOr<void> InternalSettings::setShouldManageAudioSessionCategory(bool sho
 #endif
 }
 
+ExceptionOr<void> InternalSettings::setCustomPasteboardDataEnabled(bool enabled)
+{
+    Settings::setCustomPasteboardDataEnabled(enabled);
+    return { };
+}
+
 static InternalSettings::ForcedAccessibilityValue settingsToInternalSettingsValue(Settings::ForcedAccessibilityValue value)
 {
     switch (value) {
index 01aa688..201023d 100644 (file)
@@ -97,6 +97,7 @@ public:
     ExceptionOr<void> setSystemLayoutDirection(const String&);
     ExceptionOr<void> setShouldMockBoldSystemFontForAccessibility(bool);
     ExceptionOr<void> setShouldManageAudioSessionCategory(bool);
+    ExceptionOr<void> setCustomPasteboardDataEnabled(bool);
 
     enum class FrameFlatteningValue { Disabled, EnabledForNonFullScreenIFrames, FullyEnabled };
     ExceptionOr<void> setFrameFlattening(const FrameFlatteningValue&);
@@ -204,6 +205,7 @@ private:
 #if USE(AUDIO_SESSION)
         bool m_shouldManageAudioSessionCategory;
 #endif
+        bool m_customPasteboardDataEnabled;
     };
 
     Page* m_page;
index 07cab3e..057d464 100644 (file)
@@ -106,5 +106,6 @@ enum FrameFlatteningValue { "Disabled", "EnabledForNonFullScreenIFrames", "Fully
     attribute ForcedAccessibilityValue forcedPrefersReducedMotionAccessibilityValue;
 
     [MayThrowException] void setShouldManageAudioSessionCategory(boolean should);
+    [MayThrowException] void setCustomPasteboardDataEnabled(boolean enabled);
 };
 
index d38fec5..616e535 100644 (file)
@@ -1,3 +1,41 @@
+2017-09-27  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        Drag event DataTransfer has unexpected types "dyn.ah62d4..."
+        https://bugs.webkit.org/show_bug.cgi?id=172526
+        <rdar://problem/32396081>
+
+        Reviewed by Ryosuke Niwa.
+
+        Add boilerplate plumbing and encoder/decoder support for new pasteboard codepaths. See WebCore ChangeLog for
+        more details.
+
+        * Scripts/webkit/messages.py:
+        (headers_for_type):
+        * Shared/WebCoreArgumentCoders.cpp:
+        (IPC::ArgumentCoder<PasteboardCustomData>::encode):
+        (IPC::ArgumentCoder<PasteboardCustomData>::decode):
+
+        Add encoder/decoder support for PasteboardCustomData.
+
+        (IPC::ArgumentCoder<PasteboardWebContent>::encode):
+        (IPC::ArgumentCoder<PasteboardWebContent>::decode):
+
+        Encode and decode dataInHTMLFormat.
+
+        * Shared/WebCoreArgumentCoders.h:
+        * UIProcess/Cocoa/WebPasteboardProxyCocoa.mm:
+        (WebKit::WebPasteboardProxy::typesSafeForDOMToReadAndWrite):
+        (WebKit::WebPasteboardProxy::writeCustomData):
+        * UIProcess/WebPasteboardProxy.cpp:
+        (WebKit::WebPasteboardProxy::typesSafeForDOMToReadAndWrite):
+        (WebKit::WebPasteboardProxy::writeCustomData):
+        * UIProcess/WebPasteboardProxy.h:
+        * UIProcess/WebPasteboardProxy.messages.in:
+        * WebProcess/WebCoreSupport/WebPlatformStrategies.cpp:
+        (WebKit::WebPlatformStrategies::typesSafeForDOMToReadAndWrite):
+        (WebKit::WebPlatformStrategies::writeCustomData):
+        * WebProcess/WebCoreSupport/WebPlatformStrategies.h:
+
 2017-09-27  Alex Christensen  <achristensen@webkit.org>
 
         Allow modern decoding of std::optional<T>
index 7b121c4..ce64d6e 100644 (file)
@@ -363,6 +363,7 @@ def headers_for_type(type):
         'WebCore::KeyframeValueList': ['<WebCore/GraphicsLayer.h>'],
         'WebCore::KeypressCommand': ['<WebCore/KeyboardEvent.h>'],
         'WebCore::MediaConstraints': ['<WebCore/MediaConstraints.h>'],
+        'WebCore::PasteboardCustomData': ['<WebCore/Pasteboard.h>'],
         'WebCore::PasteboardImage': ['<WebCore/Pasteboard.h>'],
         'WebCore::PasteboardURL': ['<WebCore/Pasteboard.h>'],
         'WebCore::PasteboardWebContent': ['<WebCore/Pasteboard.h>'],
index fd26b88..205637b 100644 (file)
@@ -91,7 +91,7 @@
 #include <WebCore/SharedBuffer.h>
 #endif // PLATFORM(IOS)
 
-#if PLATFORM(IOS) || PLATFORM(WPE)
+#if PLATFORM(COCOA) || PLATFORM(WPE) || PLATFORM(GTK)
 #include <WebCore/Pasteboard.h>
 #endif
 
@@ -1578,6 +1578,27 @@ bool ArgumentCoder<DatabaseDetails>::decode(Decoder& decoder, DatabaseDetails& d
     return true;
 }
 
+void ArgumentCoder<PasteboardCustomData>::encode(Encoder& encoder, const PasteboardCustomData& data)
+{
+    encoder << data.orderedTypes;
+    encoder << data.platformData;
+    encoder << data.sameOriginCustomData;
+}
+
+bool ArgumentCoder<PasteboardCustomData>::decode(Decoder& decoder, PasteboardCustomData& data)
+{
+    if (!decoder.decode(data.orderedTypes))
+        return false;
+
+    if (!decoder.decode(data.platformData))
+        return false;
+
+    if (!decoder.decode(data.sameOriginCustomData))
+        return false;
+
+    return true;
+}
+
 #if PLATFORM(IOS)
 
 void ArgumentCoder<Highlight>::encode(Encoder& encoder, const Highlight& highlight)
@@ -1664,6 +1685,7 @@ void ArgumentCoder<PasteboardWebContent>::encode(Encoder& encoder, const Pastebo
 {
     encoder << content.canSmartCopyOrDelete;
     encoder << content.dataInStringFormat;
+    encoder << content.dataInHTMLFormat;
 
     encodeSharedBuffer(encoder, content.dataInWebArchiveFormat.get());
     encodeSharedBuffer(encoder, content.dataInRTFDFormat.get());
@@ -1679,6 +1701,8 @@ bool ArgumentCoder<PasteboardWebContent>::decode(Decoder& decoder, PasteboardWeb
         return false;
     if (!decoder.decode(content.dataInStringFormat))
         return false;
+    if (!decoder.decode(content.dataInHTMLFormat))
+        return false;
     if (!decodeSharedBuffer(decoder, content.dataInWebArchiveFormat))
         return false;
     if (!decodeSharedBuffer(decoder, content.dataInRTFDFormat))
index dd9d84e..c9a820a 100644 (file)
@@ -99,6 +99,7 @@ struct Length;
 struct GrammarDetail;
 struct MimeClassInfo;
 struct PasteboardImage;
+struct PasteboardCustomData;
 struct PasteboardURL;
 struct PluginInfo;
 struct RecentSearch;
@@ -425,6 +426,11 @@ template<> struct ArgumentCoder<WebCore::PasteboardImage> {
 };
 #endif
 
+template<> struct ArgumentCoder<WebCore::PasteboardCustomData> {
+    static void encode(Encoder&, const WebCore::PasteboardCustomData&);
+    static bool decode(Decoder&, WebCore::PasteboardCustomData&);
+};
+
 #if USE(SOUP)
 template<> struct ArgumentCoder<WebCore::SoupNetworkProxySettings> {
     static void encode(Encoder&, const WebCore::SoupNetworkProxySettings&);
index 411e5bc..48242ab 100644 (file)
@@ -139,6 +139,16 @@ void WebPasteboardProxy::getNumberOfFiles(const String& pasteboardName, uint64_t
     numberOfFiles = PlatformPasteboard(pasteboardName).numberOfFiles();
 }
 
+void WebPasteboardProxy::typesSafeForDOMToReadAndWrite(const String& pasteboardName, Vector<String>& types)
+{
+    types = PlatformPasteboard(pasteboardName).typesSafeForDOMToReadAndWrite();
+}
+
+void WebPasteboardProxy::writeCustomData(const WebCore::PasteboardCustomData& data, const String& pasteboardName, uint64_t& newChangeCount)
+{
+    newChangeCount = PlatformPasteboard(pasteboardName).write(data);
+}
+
 #if PLATFORM(IOS)
 void WebPasteboardProxy::getPasteboardTypesByFidelityForItemAtIndex(uint64_t index, const String& pasteboardName, Vector<String>& types)
 {
index 4be0ae6..108d3dd 100644 (file)
@@ -61,4 +61,18 @@ void WebPasteboardProxy::removeWebProcessProxy(WebProcessProxy& webProcessProxy)
     m_webProcessProxyList.remove(&webProcessProxy);
 }
 
+#if !PLATFORM(COCOA)
+
+void WebPasteboardProxy::typesSafeForDOMToReadAndWrite(const String&, Vector<String>& types)
+{
+    types = { };
+}
+
+void WebPasteboardProxy::writeCustomData(const WebCore::PasteboardCustomData&, const String&, uint64_t& newChangeCount)
+{
+    newChangeCount = 0;
+}
+
+#endif
+
 } // namespace WebKit
index 0955727..d017c48 100644 (file)
@@ -32,6 +32,7 @@
 
 namespace WebCore {
 class Color;
+struct PasteboardCustomData;
 struct PasteboardImage;
 struct PasteboardURL;
 struct PasteboardWebContent;
@@ -96,6 +97,9 @@ private:
     void setPasteboardBufferForType(const String& pasteboardName, const String& pasteboardType, const SharedMemory::Handle&, uint64_t size, uint64_t& newChangeCount);
 #endif
 
+    void writeCustomData(const WebCore::PasteboardCustomData&, const String& pasteboardName, uint64_t& newChangeCount);
+    void typesSafeForDOMToReadAndWrite(const String& pasteboardName, Vector<String>& types);
+
 #if PLATFORM(GTK)
     void writeToClipboard(const String& pasteboardName, const WebSelectionData&);
     void readFromClipboard(const String& pasteboardName, WebSelectionData&);
index c388293..3ba0c2c 100644 (file)
@@ -35,6 +35,9 @@ messages -> WebPasteboardProxy {
     GetPasteboardTypesByFidelityForItemAtIndex(uint64_t index, String pasteboardName) -> (Vector<String> types)
 #endif
 
+    WriteCustomData(struct WebCore::PasteboardCustomData data, String pasteboardName) -> (uint64_t changeCount)
+    TypesSafeForDOMToReadAndWrite(String pasteboardName) -> (Vector<String> types)
+
 #if PLATFORM(COCOA)
     # Pasteboard messages.
     GetNumberOfFiles(String pasteboardName) -> (uint64_t numberOfFiles)
index def355d..884e4ed 100644 (file)
@@ -398,4 +398,18 @@ void WebPlatformStrategies::writeToPasteboard(const String& pasteboardType, cons
 
 #endif // PLATFORM(WPE)
 
+Vector<String> WebPlatformStrategies::typesSafeForDOMToReadAndWrite(const String& pasteboardName)
+{
+    Vector<String> types;
+    WebProcess::singleton().parentProcessConnection()->sendSync(Messages::WebPasteboardProxy::TypesSafeForDOMToReadAndWrite(pasteboardName), Messages::WebPasteboardProxy::TypesSafeForDOMToReadAndWrite::Reply(types), 0);
+    return types;
+}
+
+long WebPlatformStrategies::writeCustomData(const WebCore::PasteboardCustomData& data, const String& pasteboardName)
+{
+    uint64_t newChangeCount;
+    WebProcess::singleton().parentProcessConnection()->sendSync(Messages::WebPasteboardProxy::WriteCustomData(data, pasteboardName), Messages::WebPasteboardProxy::WriteCustomData::Reply(newChangeCount), 0);
+    return newChangeCount;
+}
+
 } // namespace WebKit
index 8bc9381..11db2c9 100644 (file)
@@ -96,6 +96,9 @@ private:
     void writeToPasteboard(const WebCore::PasteboardWebContent&) override;
     void writeToPasteboard(const String& pasteboardType, const String&) override;
 #endif
+
+    Vector<String> typesSafeForDOMToReadAndWrite(const String& pasteboardName) override;
+    long writeCustomData(const WebCore::PasteboardCustomData&, const String&) override;
 };
 
 } // namespace WebKit
index f647b95..c3a092d 100644 (file)
@@ -1,3 +1,18 @@
+2017-09-27  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        Drag event DataTransfer has unexpected types "dyn.ah62d4..."
+        https://bugs.webkit.org/show_bug.cgi?id=172526
+        <rdar://problem/32396081>
+
+        Reviewed by Ryosuke Niwa.
+
+        Adjust for changes in WebCore. See WebCore ChangeLog for more details.
+
+        * WebCoreSupport/WebPlatformStrategies.h:
+        * WebCoreSupport/WebPlatformStrategies.mm:
+        (WebPlatformStrategies::webExposedTypes):
+        (WebPlatformStrategies::writeCustomData):
+
 2017-09-27  Matt Lewis  <jlewis3@apple.com>
 
         Unreviewed, rolling out r222567.
index b5b522b..cefc4fd 100644 (file)
@@ -32,6 +32,7 @@
 
 struct PasteboardImage;
 struct PasteboardWebContent;
+struct PasteboardCustomData;
 
 class WebPlatformStrategies : public WebCore::PlatformStrategies, private WebCore::CookiesStrategy, private WebCore::PasteboardStrategy {
 public:
@@ -79,6 +80,9 @@ private:
     WebCore::Color color(const String& pasteboardName) override;
     WebCore::URL url(const String& pasteboardName) override;
 
+    long writeCustomData(const WebCore::PasteboardCustomData&, const String& pasteboardName) override;
+    Vector<String> typesSafeForDOMToReadAndWrite(const String& pasteboardName) override;
+
     long addTypes(const Vector<String>& pasteboardTypes, const String& pasteboardName) override;
     long setTypes(const Vector<String>& pasteboardTypes, const String& pasteboardName) override;
     long setBufferForType(WebCore::SharedBuffer*, const String& pasteboardType, const String& pasteboardName) override;
index a665518..3e93dc2 100644 (file)
@@ -179,6 +179,16 @@ int WebPlatformStrategies::getNumberOfFiles(const String& pasteboardName)
     return PlatformPasteboard(pasteboardName).numberOfFiles();
 }
 
+Vector<String> WebPlatformStrategies::typesSafeForDOMToReadAndWrite(const String& pasteboardName)
+{
+    return PlatformPasteboard(pasteboardName).typesSafeForDOMToReadAndWrite();
+}
+
+long WebPlatformStrategies::writeCustomData(const WebCore::PasteboardCustomData& data, const String& pasteboardName)
+{
+    return PlatformPasteboard(pasteboardName).write(data);
+}
+
 #if PLATFORM(IOS)
 void WebPlatformStrategies::getTypesByFidelityForItemAtIndex(Vector<String>& types, uint64_t index, const String& pasteboardName)
 {
index 5f7492d..05fd83d 100644 (file)
@@ -1,3 +1,34 @@
+2017-09-27  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        Drag event DataTransfer has unexpected types "dyn.ah62d4..."
+        https://bugs.webkit.org/show_bug.cgi?id=172526
+        <rdar://problem/32396081>
+
+        Reviewed by Ryosuke Niwa.
+
+        Adds new API tests on iOS to cover various cases of using DataTransfer.setData, DataTransfer.getData, and
+        DataTransfer.types, as well as their interaction with platform objects (source NSItemProviders in the case of
+        drag and drop, and the general UIPasteboard for copy and paste).
+
+        * TestWebKitAPI/PlatformUtilities.h:
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WebKitCocoa/dump-datatransfer-types.html: Added.
+
+        Introduce a new API test harness that both drag-and-drop and copy-and-paste tests use to dump DataTransfer's
+        web-exposed types and values.
+
+        * TestWebKitAPI/Tests/ios/DataInteractionTests.mm:
+        (checkFirstTypeIsPresentAndSecondTypeIsMissing):
+        (checkJSONWithLogging):
+        (TestWebKitAPI::TEST):
+        (checkTypeIdentifierAndIsNotOtherTypeIdentifier): Deleted.
+        * TestWebKitAPI/Tests/ios/UIPasteboardTests.mm:
+        (TestWebKitAPI::checkJSONWithLogging):
+        (TestWebKitAPI::setUpWebViewForPasteboardTests):
+        (TestWebKitAPI::TEST):
+        * TestWebKitAPI/cocoa/PlatformUtilitiesCocoa.mm:
+        (TestWebKitAPI::Util::jsonMatchesExpectedValues):
+
 2017-09-27  Lucas Forschler  <lforschler@apple.com>
 
         Add macOS High Sierra and iOS Simulator 11 support to bisect-builds.
index 86ddcfa..e1e309e 100644 (file)
@@ -36,6 +36,7 @@
 
 #if USE(FOUNDATION)
 OBJC_CLASS NSString;
+OBJC_CLASS NSDictionary;
 #endif
 
 namespace TestWebKitAPI {
@@ -48,6 +49,7 @@ void sleep(double seconds);
 std::string toSTD(const char*);
 #if USE(FOUNDATION)
 std::string toSTD(NSString *);
+bool jsonMatchesExpectedValues(NSString *jsonString, NSDictionary *expected);
 #endif
 
 #if WK_HAVE_C_SPI
index c6fa2cc..a1f890d 100644 (file)
@@ -97,6 +97,7 @@
                2E7765CD16C4D80A00BA2BB1 /* mainIOS.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2E7765CC16C4D80A00BA2BB1 /* mainIOS.mm */; };
                2E7765CF16C4D81100BA2BB1 /* mainMac.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2E7765CE16C4D81100BA2BB1 /* mainMac.mm */; };
                2E9896151D8F093800739892 /* text-and-password-inputs.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 2E9896141D8F092B00739892 /* text-and-password-inputs.html */; };
+               2EB29D5E1F762DB90023A5F1 /* dump-datatransfer-types.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 2EB29D5D1F762DA50023A5F1 /* dump-datatransfer-types.html */; };
                2ECFF5551D9B12F800B55394 /* NowPlayingControlsTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2ECFF5541D9B12F800B55394 /* NowPlayingControlsTests.mm */; };
                2EFF06C31D88621E0004BB30 /* large-video-offscreen.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 2EFF06C21D8862120004BB30 /* large-video-offscreen.html */; };
                2EFF06C51D8867760004BB30 /* change-video-source-on-click.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 2EFF06C41D8867700004BB30 /* change-video-source-on-click.html */; };
                                F4A32EC41F05F3850047C544 /* dragstart-change-selection-offscreen.html in Copy Resources */,
                                F4D5E4E81F0C5D38008C1A49 /* dragstart-clear-selection.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 */,
                                9984FACE1CFFB090008D198C /* editable-body.html in Copy Resources */,
                                F44D06451F395C26001A0E29 /* editor-state-test-harness.html in Copy Resources */,
                2E7765CE16C4D81100BA2BB1 /* mainMac.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = mainMac.mm; sourceTree = "<group>"; };
                2E7EF7AC1F266A8100DFB67C /* AppKitSPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppKitSPI.h; sourceTree = "<group>"; };
                2E9896141D8F092B00739892 /* text-and-password-inputs.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "text-and-password-inputs.html"; sourceTree = "<group>"; };
+               2EB29D5D1F762DA50023A5F1 /* dump-datatransfer-types.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "dump-datatransfer-types.html"; sourceTree = "<group>"; };
                2ECFF5541D9B12F800B55394 /* NowPlayingControlsTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = NowPlayingControlsTests.mm; sourceTree = "<group>"; };
                2EFF06C21D8862120004BB30 /* large-video-offscreen.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "large-video-offscreen.html"; sourceTree = "<group>"; };
                2EFF06C41D8867700004BB30 /* change-video-source-on-click.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "change-video-source-on-click.html"; sourceTree = "<group>"; };
                                F4A32EC31F05F3780047C544 /* dragstart-change-selection-offscreen.html */,
                                F4D5E4E71F0C5D27008C1A49 /* dragstart-clear-selection.html */,
                                F4194AD01F5A2EA500ADD83F /* drop-targets.html */,
+                               2EB29D5D1F762DA50023A5F1 /* dump-datatransfer-types.html */,
                                A155022B1E050BC500A24C57 /* duplicate-completion-handler-calls.html */,
                                9984FACD1CFFB038008D198C /* editable-body.html */,
                                F44D06441F395C0D001A0E29 /* editor-state-test-harness.html */,
diff --git a/Tools/TestWebKitAPI/Tests/WebKitCocoa/dump-datatransfer-types.html b/Tools/TestWebKitAPI/Tests/WebKitCocoa/dump-datatransfer-types.html
new file mode 100644 (file)
index 0000000..b730a1d
--- /dev/null
@@ -0,0 +1,112 @@
+<!DOCTYPE html>
+<html>
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<meta charset="utf-8">
+<style>
+html, body {
+    margin: 0;
+    font-family: -apple-system;
+}
+
+#plain, #rich, #destination {
+    width: 100%;
+    height: 150px;
+    margin: 0;
+}
+
+img {
+    width: 150px;
+    height: 150px;
+}
+
+#destination {
+    border: 4px dashed lightgray;
+    caret-color: transparent;
+}
+
+#destination:focus {
+    outline: none;
+    border: 4px dashed lightgreen;
+}
+
+#plain, #rich {
+    font-size: 125px;
+    white-space: nowrap;
+    border: 1px solid black;
+}
+
+#output {
+    width: 100%;
+    height: 150px;
+}
+</style>
+<body>
+    <textarea id="plain">Plain text</textarea>
+    <div id="rich"><strong style="color: purple">Rich text</strong></div>
+    <div id="destination" contenteditable></div>
+    <textarea id="output"></textarea>
+    <label>Write custom data? </label><input id="checkbox" type="checkbox"></input>
+</body>
+<script>
+
+// The contents of this `result` dictionary will contain a map of {event type => {MIME type => data}}.
+result = {};
+
+// Setting this flag to `true` allows the page to write custom MIME types via the DataTransfer when dragging or copying.
+writeCustomData = false;
+
+// Entries in this dictionary will be supplied as custom data using DataTransfer.setData when dragging or copying.
+// This is prepopulated with sample data by default, but tests may override this as needed.
+customData = {
+    "text/plain" : "ben bitdiddle",
+    "foo/plain" : "eva lu ator",
+    "text/html" : "<b>bold text</b>",
+    "bar/html" : `<i>italic text</i>`,
+    "text/uri-list" : "https://www.apple.com",
+    "baz/uri-list" : "https://www.webkit.org"
+};
+
+function updateResultWithEvent(event) {
+    let pasteboard = event.dataTransfer || event.clipboardData;
+    const eventData = {};
+    for (const type of pasteboard.types)
+        eventData[type] = pasteboard.getData(type);
+    result[event.type] = eventData;
+    if (event.type === "paste" || event.type === "drop") {
+        output.value = JSON.stringify(result, null, "    ");
+        result = {};
+    }
+    event.preventDefault();
+}
+
+function setCustomData(event) {
+    if (!writeCustomData)
+        return true;
+
+    const pasteboard = event.dataTransfer || event.clipboardData;
+    for (const type in customData)
+        pasteboard.setData(type, customData[type]);
+
+    if (event.type === "copy")
+        event.preventDefault();
+}
+
+destination.addEventListener("dragover", updateResultWithEvent);
+destination.addEventListener("paste", updateResultWithEvent);
+destination.addEventListener("drop", updateResultWithEvent);
+[plain, rich].forEach(source => {
+    source.addEventListener("dragstart", setCustomData);
+    source.addEventListener("copy", setCustomData);
+});
+
+function select(element) {
+    if (element.setSelectionRange && element.value)
+        element.setSelectionRange(0, element.value.length);
+    else
+        getSelection().setBaseAndExtent(element, 0, element, 1);
+}
+
+checkbox.addEventListener("change", () => window.writeCustomData = checkbox.checked);
+
+</script>
+</html>
index 4a2c1fe..6e4b782 100644 (file)
@@ -125,11 +125,11 @@ static void checkTypeIdentifierPrecedesOtherTypeIdentifier(DataInteractionSimula
     EXPECT_TRUE([registeredTypes indexOfObject:firstType] < [registeredTypes indexOfObject:secondType]);
 }
 
-static void checkTypeIdentifierAndIsNotOtherTypeIdentifier(DataInteractionSimulator *simulator, NSString *firstType, NSString *secondType)
+static void checkFirstTypeIsPresentAndSecondTypeIsMissing(DataInteractionSimulator *simulator, CFStringRef firstType, CFStringRef secondType)
 {
     NSArray *registeredTypes = [simulator.sourceItemProviders.firstObject registeredTypeIdentifiers];
-    EXPECT_TRUE([registeredTypes containsObject:firstType]);
-    EXPECT_FALSE([registeredTypes containsObject:secondType]);
+    EXPECT_TRUE([registeredTypes containsObject:(NSString *)firstType]);
+    EXPECT_FALSE([registeredTypes containsObject:(NSString *)secondType]);
 }
 
 static void checkTypeIdentifierIsRegisteredAtIndex(DataInteractionSimulator *simulator, NSString *type, NSUInteger index)
@@ -174,6 +174,18 @@ static void checkDragCaretRectIsContainedInRect(CGRect caretRect, CGRect contain
         NSLog(@"Expected caret rect: %@ to fit within container rect: %@", NSStringFromCGRect(caretRect), NSStringFromCGRect(containerRect));
 }
 
+#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110300
+
+static void checkJSONWithLogging(NSString *jsonString, NSDictionary *expected)
+{
+    BOOL success = TestWebKitAPI::Util::jsonMatchesExpectedValues(jsonString, expected);
+    EXPECT_TRUE(success);
+    if (!success)
+        NSLog(@"Expected JSON: %@ to match values: %@", jsonString, expected);
+}
+
+#endif // __IPHONE_OS_VERSION_MIN_REQUIRED >= 110300
+
 static void runTestWithTemporaryTextFile(void(^runTest)(NSURL *fileURL))
 {
     NSString *fileName = [NSString stringWithFormat:@"drag-drop-text-file-%@.txt", [NSUUID UUID].UUIDString];
@@ -240,7 +252,7 @@ TEST(DataInteractionTests, ImageToContentEditable)
     EXPECT_TRUE([observedEventNames containsObject:DataInteractionOverEventName]);
     EXPECT_TRUE([observedEventNames containsObject:DataInteractionPerformOperationEventName]);
     checkSelectionRectsWithLogging(@[ makeCGRectValue(1, 201, 215, 174) ], [dataInteractionSimulator finalSelectionRects]);
-    checkTypeIdentifierAndIsNotOtherTypeIdentifier(dataInteractionSimulator.get(), (NSString *)kUTTypePNG, (NSString *)kUTTypeFileURL);
+    checkFirstTypeIsPresentAndSecondTypeIsMissing(dataInteractionSimulator.get(), kUTTypePNG, kUTTypeFileURL);
     checkEstimatedSize(dataInteractionSimulator.get(), { 215, 174 });
 }
 
@@ -270,7 +282,7 @@ TEST(DataInteractionTests, ImageToTextarea)
     EXPECT_TRUE([observedEventNames containsObject:DataInteractionEnterEventName]);
     EXPECT_TRUE([observedEventNames containsObject:DataInteractionOverEventName]);
     EXPECT_TRUE([observedEventNames containsObject:DataInteractionPerformOperationEventName]);
-    checkTypeIdentifierAndIsNotOtherTypeIdentifier(dataInteractionSimulator.get(), (NSString *)kUTTypePNG, (NSString *)kUTTypeFileURL);
+    checkFirstTypeIsPresentAndSecondTypeIsMissing(dataInteractionSimulator.get(), kUTTypePNG, kUTTypeFileURL);
     checkEstimatedSize(dataInteractionSimulator.get(), { 215, 174 });
 }
 
@@ -1207,7 +1219,7 @@ TEST(DataInteractionTests, LargeImageToTargetDiv)
     auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
     [dataInteractionSimulator runFrom:CGPointMake(200, 400) to:CGPointMake(200, 150)];
     EXPECT_WK_STREQ("PASS", [webView stringByEvaluatingJavaScript:@"target.textContent"].UTF8String);
-    checkTypeIdentifierAndIsNotOtherTypeIdentifier(dataInteractionSimulator.get(), (NSString *)kUTTypePNG, (NSString *)kUTTypeFileURL);
+    checkFirstTypeIsPresentAndSecondTypeIsMissing(dataInteractionSimulator.get(), kUTTypePNG, kUTTypeFileURL);
     checkEstimatedSize(dataInteractionSimulator.get(), { 2000, 2000 });
 }
 
@@ -1467,6 +1479,171 @@ TEST(DataInteractionTests, DragLiftPreviewDataTransferSetDragImage)
     checkCGRectIsEqualToCGRectWithLogging({{0, 400}, {215, 174}}, [simulator liftPreviews][0].view.frame);
 }
 
+#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110300
+
+TEST(DataInteractionTests, DataTransferGetDataWhenDroppingPlainText)
+{
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    [webView synchronouslyLoadTestPageNamed:@"dump-datatransfer-types"];
+    auto simulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
+
+    [webView stringByEvaluatingJavaScript:@"select(plain)"];
+    [simulator runFrom:CGPointMake(50, 75) to:CGPointMake(50, 375)];
+    checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{
+        @"dragover": @{ @"text/plain" : @"" },
+        @"drop": @{ @"text/plain" : @"Plain text" }
+    });
+}
+
+TEST(DataInteractionTests, DataTransferGetDataWhenDroppingCustomData)
+{
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    [webView synchronouslyLoadTestPageNamed:@"dump-datatransfer-types"];
+    auto simulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
+
+    [webView stringByEvaluatingJavaScript:@"select(rich)"];
+    [webView stringByEvaluatingJavaScript:@"writeCustomData = true"];
+    [simulator runFrom:CGPointMake(50, 225) to:CGPointMake(50, 375)];
+    checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{
+        @"dragover" : @{
+            @"text/plain" : @"",
+            @"foo/plain" : @"",
+            @"text/html" : @"",
+            @"bar/html" : @"",
+            @"text/uri-list" : @"",
+            @"baz/uri-list" : @""
+        },
+        @"drop" : @{
+            @"text/plain" : @"ben bitdiddle",
+            @"foo/plain" : @"eva lu ator",
+            @"text/html" : @"<b>bold text</b>",
+            @"bar/html" : @"<i>italic text</i>",
+            @"text/uri-list" : @"https://www.apple.com/",
+            @"baz/uri-list" : @"https://www.webkit.org"
+        }
+    });
+}
+
+TEST(DataInteractionTests, DataTransferGetDataWhenDroppingURL)
+{
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    [webView synchronouslyLoadTestPageNamed:@"dump-datatransfer-types"];
+    auto simulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
+
+    [webView stringByEvaluatingJavaScript:@"rich.innerHTML = '<a href=\"https://www.apple.com/\">This is a link.</a>'"];
+    [simulator runFrom:CGPointMake(50, 225) to:CGPointMake(50, 375)];
+    checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{
+        @"dragover": @{ @"text/uri-list" : @"" },
+        @"drop": @{ @"text/uri-list" : @"https://www.apple.com/" }
+    });
+}
+
+TEST(DataInteractionTests, DataTransferGetDataWhenDroppingImageWithFileURL)
+{
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    [webView synchronouslyLoadTestPageNamed:@"dump-datatransfer-types"];
+    auto simulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
+
+    auto itemProvider = adoptNS([[UIItemProvider alloc] init]);
+    NSURL *iconURL = [[NSBundle mainBundle] URLForResource:@"icon" withExtension:@"png" subdirectory:@"TestWebKitAPI.resources"];
+    [itemProvider registerFileRepresentationForTypeIdentifier:(NSString *)kUTTypePNG fileOptions:0 visibility:NSItemProviderRepresentationVisibilityAll loadHandler:[protectedIconURL = retainPtr(iconURL)] (FileLoadCompletionBlock completionHandler) -> NSProgress * {
+        completionHandler(protectedIconURL.get(), NO, nil);
+        return nil;
+    }];
+    [itemProvider registerObject:iconURL visibility:UIItemProviderRepresentationOptionsVisibilityAll];
+    [simulator setExternalItemProviders:@[ itemProvider.get() ]];
+
+    [simulator runFrom:CGPointMake(300, 375) to:CGPointMake(50, 375)];
+
+    // File URLs should never be exposed directly to web content, so DataTransfer.getData should return an empty string here.
+    checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{
+        @"dragover": @{ @"text/uri-list" : @"" },
+        @"drop": @{ @"text/uri-list" : @"" }
+    });
+}
+
+TEST(DataInteractionTests, DataTransferGetDataWhenDroppingRespectsPresentationStyle)
+{
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    [webView synchronouslyLoadTestPageNamed:@"dump-datatransfer-types"];
+    auto simulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
+
+    runTestWithTemporaryTextFile(^(NSURL *fileURL) {
+        auto itemProvider = adoptNS([[UIItemProvider alloc] init]);
+        [itemProvider registerFileRepresentationForTypeIdentifier:(NSString *)kUTTypeUTF8PlainText fileOptions:0 visibility:NSItemProviderRepresentationVisibilityAll loadHandler:[protectedFileURL = retainPtr(fileURL)] (FileLoadCompletionBlock completionHandler) -> NSProgress * {
+            completionHandler(protectedFileURL.get(), NO, nil);
+            return nil;
+        }];
+        [simulator setExternalItemProviders:@[ itemProvider.get() ]];
+
+        [itemProvider setPreferredPresentationStyle:UIPreferredPresentationStyleAttachment];
+        [simulator runFrom:CGPointMake(300, 375) to:CGPointMake(50, 375)];
+        checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{
+            @"dragover": @{ @"text/plain" : @"" },
+            @"drop": @{ @"text/plain" : @"" }
+        });
+
+        [itemProvider setPreferredPresentationStyle:UIPreferredPresentationStyleInline];
+        [simulator runFrom:CGPointMake(300, 375) to:CGPointMake(50, 375)];
+        checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{
+            @"dragover": @{ @"text/plain" : @"" },
+            @"drop": @{ @"text/plain" : @"This is a tiny blob of text." }
+        });
+    });
+}
+
+TEST(DataInteractionTests, DataTransferSetDataCannotWritePlatformTypes)
+{
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    [webView synchronouslyLoadTestPageNamed:@"dump-datatransfer-types"];
+    auto simulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
+
+    [webView stringByEvaluatingJavaScript:@"select(rich)"];
+    [webView stringByEvaluatingJavaScript:@"customData = { 'text/plain' : 'foo', 'com.adobe.pdf' : 'try and decode me!' }"];
+    [webView stringByEvaluatingJavaScript:@"writeCustomData = true"];
+
+    [simulator runFrom:CGPointMake(50, 225) to:CGPointMake(50, 375)];
+    checkFirstTypeIsPresentAndSecondTypeIsMissing(simulator.get(), kUTTypeUTF8PlainText, kUTTypePDF);
+    checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{
+        @"dragover": @{
+            @"text/plain": @"",
+            @"com.adobe.pdf": @""
+        },
+        @"drop": @{
+            @"text/plain": @"foo",
+            @"com.adobe.pdf": @"try and decode me!"
+        }
+    });
+}
+
+TEST(DataInteractionTests, DataTransferGetDataCannotReadPrivateArbitraryTypes)
+{
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    [webView synchronouslyLoadTestPageNamed:@"dump-datatransfer-types"];
+    auto simulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
+
+    auto itemProvider = adoptNS([[UIItemProvider alloc] init]);
+    [itemProvider registerDataRepresentationForTypeIdentifier:(NSString *)kUTTypeMP3 visibility:NSItemProviderRepresentationVisibilityAll loadHandler:^NSProgress *(DataLoadCompletionBlock completionHandler)
+    {
+        completionHandler([@"this is a test" dataUsingEncoding:NSUTF8StringEncoding], nil);
+        return nil;
+    }];
+    [itemProvider registerDataRepresentationForTypeIdentifier:@"org.WebKit.TestWebKitAPI.custom-pasteboard-type" visibility:NSItemProviderRepresentationVisibilityAll loadHandler:^NSProgress *(DataLoadCompletionBlock completionHandler)
+    {
+        completionHandler([@"this is another test" dataUsingEncoding:NSUTF8StringEncoding], nil);
+        return nil;
+    }];
+    [simulator setExternalItemProviders:@[ itemProvider.get() ]];
+    [itemProvider setPreferredPresentationStyle:UIPreferredPresentationStyleInline];
+    [simulator runFrom:CGPointMake(300, 375) to:CGPointMake(50, 375)];
+    checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{
+        @"dragover": @{ },
+        @"drop": @{ }
+    });
+}
+
+#endif // __IPHONE_OS_VERSION_MIN_REQUIRED >= 110300
+
 } // namespace TestWebKitAPI
 
 #endif // ENABLE(DATA_INTERACTION)
index 496f83f..0b0560c 100644 (file)
 #import <WebKit/WKWebViewPrivate.h>
 #import <wtf/SoftLinking.h>
 
+typedef void (^DataLoadCompletionBlock)(NSData *, NSError *);
+
+#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110300
+
+static void checkJSONWithLogging(NSString *jsonString, NSDictionary *expected)
+{
+    BOOL success = TestWebKitAPI::Util::jsonMatchesExpectedValues(jsonString, expected);
+    EXPECT_TRUE(success);
+    if (!success)
+        NSLog(@"Expected JSON: %@ to match values: %@", jsonString, expected);
+}
+
+#endif // __IPHONE_OS_VERSION_MIN_REQUIRED >= 110300
+
 namespace TestWebKitAPI {
 
 NSData *dataForPasteboardType(CFStringRef type)
@@ -43,7 +57,7 @@ NSData *dataForPasteboardType(CFStringRef type)
     return [[UIPasteboard generalPasteboard] dataForPasteboardType:(NSString *)type inItemSet:[NSIndexSet indexSetWithIndex:0]].firstObject;
 }
 
-RetainPtr<TestWKWebView> setUpWebViewForPasteboardTests()
+RetainPtr<TestWKWebView> setUpWebViewForPasteboardTests(NSString *pageName)
 {
     // UIPasteboard's type coercion codepaths only take effect when the UIApplication has been initialized.
     UIApplicationInitialize();
@@ -56,13 +70,13 @@ RetainPtr<TestWKWebView> setUpWebViewForPasteboardTests()
     WKPreferences *preferences = [webView configuration].preferences;
     preferences._javaScriptCanAccessClipboard = YES;
     preferences._domPasteAllowed = YES;
-    [webView synchronouslyLoadTestPageNamed:@"rich-and-plain-text"];
+    [webView synchronouslyLoadTestPageNamed:pageName];
     return webView;
 }
 
 TEST(UIPasteboardTests, CopyPlainTextWritesConcreteTypes)
 {
-    auto webView = setUpWebViewForPasteboardTests();
+    auto webView = setUpWebViewForPasteboardTests(@"rich-and-plain-text");
     [webView stringByEvaluatingJavaScript:@"selectPlainText()"];
     [webView stringByEvaluatingJavaScript:@"document.execCommand('copy')"];
 
@@ -72,7 +86,7 @@ TEST(UIPasteboardTests, CopyPlainTextWritesConcreteTypes)
 
 TEST(UIPasteboardTests, CopyRichTextWritesConcreteTypes)
 {
-    auto webView = setUpWebViewForPasteboardTests();
+    auto webView = setUpWebViewForPasteboardTests(@"rich-and-plain-text");
     [webView stringByEvaluatingJavaScript:@"selectRichText()"];
     [webView stringByEvaluatingJavaScript:@"document.execCommand('copy')"];
 
@@ -80,11 +94,9 @@ TEST(UIPasteboardTests, CopyRichTextWritesConcreteTypes)
     EXPECT_WK_STREQ("Hello world", [utf8Result UTF8String]);
 }
 
-#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
-
 TEST(UIPasteboardTests, DoNotPastePlainTextAsURL)
 {
-    auto webView = setUpWebViewForPasteboardTests();
+    auto webView = setUpWebViewForPasteboardTests(@"rich-and-plain-text");
 
     NSString *testString = @"[helloworld]";
     [UIPasteboard generalPasteboard].string = testString;
@@ -101,7 +113,7 @@ TEST(UIPasteboardTests, DoNotPastePlainTextAsURL)
 
 TEST(UIPasteboardTests, PastePlainTextAsURL)
 {
-    auto webView = setUpWebViewForPasteboardTests();
+    auto webView = setUpWebViewForPasteboardTests(@"rich-and-plain-text");
 
     NSString *testString = @"https://www.apple.com/iphone";
     [UIPasteboard generalPasteboard].string = testString;
@@ -118,7 +130,7 @@ TEST(UIPasteboardTests, PastePlainTextAsURL)
 
 TEST(UIPasteboardTests, PasteURLWithPlainTextAsURL)
 {
-    auto webView = setUpWebViewForPasteboardTests();
+    auto webView = setUpWebViewForPasteboardTests(@"rich-and-plain-text");
 
     NSString *testString = @"thisisdefinitelyaurl";
     [UIPasteboard generalPasteboard].URL = [NSURL URLWithString:testString];
@@ -133,7 +145,104 @@ TEST(UIPasteboardTests, PasteURLWithPlainTextAsURL)
     EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"!!rich.querySelector('a')"].boolValue);
 }
 
-#endif // __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
+#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110300
+
+TEST(UIPasteboardTests, DataTransferGetDataWhenPastingURL)
+{
+    auto webView = setUpWebViewForPasteboardTests(@"dump-datatransfer-types");
+
+    NSString *testURLString = @"https://www.apple.com/";
+    [UIPasteboard generalPasteboard].URL = [NSURL URLWithString:testURLString];
+
+    [webView stringByEvaluatingJavaScript:@"destination.focus()"];
+    [webView stringByEvaluatingJavaScript:@"document.execCommand('paste')"];
+    checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{
+        @"paste" : @{
+            @"text/plain" : testURLString,
+            @"text/uri-list" : testURLString
+        }
+    });
+}
+
+TEST(UIPasteboardTests, DataTransferGetDataWhenPastingPlatformRepresentations)
+{
+    auto webView = setUpWebViewForPasteboardTests(@"dump-datatransfer-types");
+
+    // This simulates how a native app on iOS might write to the pasteboard when copying.
+    RetainPtr<NSURL> testURL = [NSURL URLWithString:@"https://www.apple.com/"];
+    RetainPtr<NSString> testPlainTextString = @"WebKit";
+    RetainPtr<NSString> testMarkupString = @"<a href='https://www.webkit.org/'>The WebKit Project</a>";
+    auto itemProvider = adoptNS([[NSItemProvider alloc] init]);
+    [itemProvider registerDataRepresentationForTypeIdentifier:(NSString *)kUTTypeHTML visibility:NSItemProviderRepresentationVisibilityAll loadHandler:^NSProgress *(DataLoadCompletionBlock completionHandler)
+    {
+        completionHandler([testMarkupString dataUsingEncoding:NSUTF8StringEncoding], nil);
+        return nil;
+    }];
+    [itemProvider registerObject:testURL.get() visibility:NSItemProviderRepresentationVisibilityAll];
+    [itemProvider registerObject:testPlainTextString.get() visibility:NSItemProviderRepresentationVisibilityAll];
+
+    [UIPasteboard generalPasteboard].itemProviders = @[ itemProvider.get() ];
+    [webView stringByEvaluatingJavaScript:@"destination.focus()"];
+    [webView stringByEvaluatingJavaScript:@"document.execCommand('paste')"];
+    checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{
+        @"paste" : @{
+            @"text/plain" : testPlainTextString.get(),
+            @"text/uri-list" : [testURL absoluteString],
+            @"text/html" : testMarkupString.get()
+        }
+    });
+}
+
+TEST(UIPasteboardTests, DataTransferSetDataCannotWritePlatformTypes)
+{
+    auto webView = setUpWebViewForPasteboardTests(@"dump-datatransfer-types");
+
+    [webView stringByEvaluatingJavaScript:@"customData = { 'text/plain' : '年年年', '年年年年年年' : 'test', 'com.adobe.pdf' : '🔥🔥🔥', 'text/rtf' : 'not actually rich text!' }"];
+    [webView stringByEvaluatingJavaScript:@"writeCustomData = true"];
+    [webView stringByEvaluatingJavaScript:@"select(rich)"];
+    [webView stringByEvaluatingJavaScript:@"document.execCommand('copy')"];
+    [webView stringByEvaluatingJavaScript:@"destination.focus()"];
+    [webView stringByEvaluatingJavaScript:@"document.execCommand('paste')"];
+    checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{
+        @"paste": @{
+            @"text/plain": @"年年年",
+            @"年年年年年年": @"test",
+            @"com.adobe.pdf": @"🔥🔥🔥",
+            @"text/rtf": @"not actually rich text!"
+        }
+    });
+
+    // Most importantly, the system pasteboard should not contain the PDF UTI.
+    NSData *pdfData = [[UIPasteboard generalPasteboard] dataForPasteboardType:(NSString *)kUTTypePDF];
+    EXPECT_EQ(0UL, pdfData.length);
+
+    // However, the system pasteboard should contain a plain text string.
+    EXPECT_WK_STREQ("年年年", [UIPasteboard generalPasteboard].string);
+}
+
+TEST(UIPasteboardTests, DataTransferGetDataCannotReadArbitraryPlatformTypes)
+{
+    auto webView = setUpWebViewForPasteboardTests(@"dump-datatransfer-types");
+    auto itemProvider = adoptNS([[NSItemProvider alloc] init]);
+    [itemProvider registerDataRepresentationForTypeIdentifier:(NSString *)kUTTypeMP3 visibility:NSItemProviderRepresentationVisibilityAll loadHandler:^NSProgress *(DataLoadCompletionBlock completionHandler)
+    {
+        completionHandler([@"this is a test" dataUsingEncoding:NSUTF8StringEncoding], nil);
+        return nil;
+    }];
+    [itemProvider registerDataRepresentationForTypeIdentifier:@"org.WebKit.TestWebKitAPI.custom-pasteboard-type" visibility:NSItemProviderRepresentationVisibilityAll loadHandler:^NSProgress *(DataLoadCompletionBlock completionHandler)
+    {
+        completionHandler([@"this is another test" dataUsingEncoding:NSUTF8StringEncoding], nil);
+        return nil;
+    }];
+
+    [webView stringByEvaluatingJavaScript:@"destination.focus()"];
+    [webView stringByEvaluatingJavaScript:@"document.execCommand('paste')"];
+    checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{
+        @"paste": @{ }
+    });
+}
+
+#endif // __IPHONE_OS_VERSION_MIN_REQUIRED >= 110300
 
 } // namespace TestWebKitAPI
 
index 1e9baf9..67e7530 100644 (file)
@@ -44,6 +44,15 @@ std::string toSTD(NSString *string)
     return std::string(buffer.get(), stringLength);
 }
 
+bool jsonMatchesExpectedValues(NSString *jsonString, NSDictionary *expected)
+{
+    NSError *error = nil;
+    id result = [NSJSONSerialization JSONObjectWithData:[jsonString dataUsingEncoding:NSUTF8StringEncoding] options:0 error:&error];
+    if (error)
+        NSLog(@"Encountered error: %@ while serializing JSON string.", error);
+    return [expected isEqualToDictionary:result];
+}
+
 #if WK_API_ENABLED
 NSString * const TestPlugInClassNameParameter = @"TestPlugInPrincipalClassName";
 #endif