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 db22ade18e8d2c70dda429bdee4fe1249d64adfe..1d1d20e8e45d5df926ba21dd2e545e05cf5fff88 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 ce62316bdb77a3fd49843e6cb8e5654cfb9adf1f..dc922652cf6f5fce06b6505d78dd3c35fcbde7b6 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 51c63a63040d7982062c7ca5a8a685873b1ae1f1..a7d77c60141f412fdc7b2d2e1e85dde034789501 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 80373334fef0f6de5207559cd7c2d9ec526fb043..974a806a5cf53be8302dac70e3614fd1ae860fae 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 206328ffdec8abe3de66f2fe65ad605b9265ec83..1e39d29a788aa92afed20ba5305b26966446d486 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 acdc19780d8e895472b8ff1e00f14af07a0174c6..80396b90cfac28036eddb1f18dbe2efcb19fa9bd 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 7998dc0ba0d517651da5598848c6c48c2561d316..6905c23936c5f233317dd359adf71612db86b1e9 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 57be7c84313831274124351c97bd606f1bb2a52c..30fa6a22b6d4c397678b7fbf26d3e0b213c66611 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 675c6e2731690c5a43d042d82e483b4ed9a61852..a6185d52d89649d12dc111445c2da75660ca5ad4 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 1ea4911782e728cdc9251835e0c28e7b4250c7d8..537336cf6e381075a728d8ccf49c560917948fc2 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 27c846ea4cbb4d16eb0d07e1103390d7f492a3be..58009706b0a1ec3e40a610b78d7f21639a932a80 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 1ca9af17212362c1e9a20b684e2aaab4a069ee66..e9ae5d111618bde885a62c151910d2d56d873491 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 ef069c47e746b81344e929cf774e2934d4a44796..23d47c5a1950c1acfc4fe7009494e5863ff2f31e 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 a0aa535f5a5d8ca81175a891015b6e9bbf487984..2de5acf5ab2bba7c8a2336e7c38eb454e8327054 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 138c51c5677f816605305871918f3dca61c99162..691c1d91a9bdd76f2133791de22e088739c50556 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 83b198893c83e2ee65a50644049641b93b34aaae..f60096d3c27a12ae63131e905481b7b902aa20d3 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 9f9758bf35017ebb4b5d2d71b20d7e8d99caac9b..806d92b6d5242f0be19160f3274865f79de17c7e 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 f71e4fdc159c35a610f45fb615b0f4e148dba869..4cb70e9af60faef07318de2736a00bfacae98685 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 42106a68ffbd81ac107f50b90786b7e09e08327c..f74d38180ad4484319b2b77351e1610f1963bff6 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 f4dad2bf1986c93ea0fad41839ef7c49a93d12f2..33bbd046c9d15384d81984ef9d49eb93e2a241e2 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 d0a35cdbccdd7ca7a3b6b7ec5b638eadfdd702d3..b2684c968112352f3afcc0f7d59172ea66034997 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 6feee93252fc795e2656abad94ec82e656329238..b86bc21fb4bd94637038e56cc63aefb3dbbdd5e6 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 b6e4fd5d9f8af0e34bf794e39186c58be7056e2e..3954c210cf974765b9587ecaf4b91e3af1931881 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 e0a2353f830582d747cf6976560a377b4b9010a4..711fa63227becd73ceba400fb43143145355bb82 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 3a1c039646223257d008592131b9b70672442933..461d3a4669b6535443e00cf14c2a31743cef3693 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 a5e6853ae2dc6101f1d6c7a2bceada1b30690d31..763658e4c1977272bd32c6bd51751d11a4e457e2 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 974d3ea2b0262c19ab5ca513088ef6847607b70f..bde242c36d643917f04e219ed2b0e583457c86ff 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 eba3e037621898f8753560fffcfdddc6bde6233a..b9334da925e7c24d627d5fd357118f0f27fcf8ff 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 acccfb0393fd846c65a277fd80a7a50a41899fb4..e8e5bf36bcb267391edd3c61729523e0430cfbca 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 76f4397e3837e074bb1a832f000e3a5ed90566fc..2306ccaa8d2472a364d8a54fca391db84809e84f 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 ab9f1a00606f7505e09f32b85ce03225d433b61e..77bb7f26393dbceb301748a2ed923166d8abc828 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 275ea45fb86e0ac48eb8c989d2407245e73c40e8..abff268c8a2081ca4090d5def74978817a4adb52 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 4b9c3a57ea0aac28369b19cdee0d7e9f338a93e5..d977195bfc58d46c449ff8b3b1445d45908ea4e9 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 df332b6d8038e701a9f72ce1563f36aa9fc16c71..926394d78bcdc9cf99bb1b6dcd839aaa1ead6e04 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 16a248c7bb7c5233f826451dee06a2af843b2897..82b7094f38e7c3978fe33ca49861fd21d07b3e2c 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 72aecf67b19d5592388c0874d86e36eb96c62b77..b34945cfbc3a264550b43a39a02048c80c567697 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 da476a69416575f48f3068a530887ab63b55e7d1..d3254b906aff116430c3f98f75ebb6f57121c622 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 449c36777c4daddcaeeedd8b5de2356b139f7e3e..3bded132f40e535ae03a511834598688cca64438 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 9a8ce77e9ba115a55974a930463f2836dcd16885..5321fe72b8a485ffff2a09fdb45c397d19318b43 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 01aa688ec38e0ad614fd5d7748ca199472d5e6f8..201023db92bdc9e96564908f4e06598a9361535f 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 07cab3e87230c709a47f5acf42071a814d504044..057d46471efc73343feaf7358514041b080a976e 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 d38fec5fca7933006619f9a77d5362e3d03d46b3..616e535ad6e3b1c1bffd127afc9fcc4868be6eac 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 7b121c4656936aed0c56111b885b8c344ad56064..ce64d6ea54bcb92eba207e870a89ca7922a13548 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 fd26b883dd2c7f054e07ec436f554d9561d84ff6..205637b397197ecad99003bcc0f41baf339003a8 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 dd9d84ef53e2494a71b047647c1b01803ed068ce..c9a820a673470d268bbb6bbe299f7d3a6ceaae8f 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 411e5bc36fec7487b47f59203331750dadf86699..48242abda562aa8429a1ab8895f4620bb9af37a4 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 4be0ae6e5deb95fb591a4e95e4459f2e8da4329a..108d3dd9344ea7571ee2d84aab5b5d0c4246a387 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 0955727dd8eace769c1c2291cb386e8dc5aae1f5..d017c489ab2b069a5b2f61a37937bb7b2b551e14 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 c388293dd087d8e989db7e859b33f0c7b78d423b..3ba0c2ce48c259bf1328aff99f01ae9c2f9773e4 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 def355d4f2e88ba324599f1b8621816e0f57b0d9..884e4ed9a1dc07abc995f4e6d1e6648ef177ef9f 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 8bc9381bd6118c11e1b07c333cbc1ebd1e31a074..11db2c95196a45e391ae05964feecba6f0c96fba 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 f647b95d58fb340b27fb41f5709bd48f9adfb60e..c3a092d7bb23949da6ed9d10353870c9b1e3f6c1 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 b5b522b1e168c401313cdbfabbe71fde1ca71cbf..cefc4fda0a4ad4710a81222574e075e7316ce535 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 a665518111d5b50857d815be2b578a4c7c9de8c8..3e93dc212d6687dec71081f7bc466981f4e49d0d 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 5f7492d2d37e59f121bf2c69bef8822d9637918e..05fd83d1d6151d1277ee352227340d672886cc30 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 86ddcfa57f59cdbc7d8cb4e64022d123e81c3fd4..e1e309e6931f71ade97d6208d9b8c59587ec59bc 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 c6fa2cc6f82a556a89e8765ab99f1f1d1b313a02..a1f890dd1c2b6d0617094a7345ccf2b758d50f69 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 4a2c1fe16ba987c3e9c05c26b243ed2c35b9f664..6e4b782a04ce7c31ef80d9edda2c894106fc9314 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 496f83fddb89904a282f0101773ecc970ddcb0e6..0b0560ca91c4b256603188f6547782707321676a 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 1e9baf990c8be4ec30eb9f54e690e7b6110efb6e..67e7530acd5a0fd383200e24fce4e90f9bc4034b 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