[Attachment Support] Create attachment elements when dropping files on iOS
authorwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 4 Jan 2018 07:44:55 +0000 (07:44 +0000)
committerwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 4 Jan 2018 07:44:55 +0000 (07:44 +0000)
https://bugs.webkit.org/show_bug.cgi?id=181192
<rdar://problem/36280945>

Reviewed by Tim Horton.

Source/WebCore:

Implements support for dropping data as attachment elements on iOS. See comments below for more detail.

Tests:  WKAttachmentTests.InsertDroppedRichAndPlainTextFilesAsAttachments
        WKAttachmentTests.InsertDroppedZipArchiveAsAttachment
        WKAttachmentTests.InsertDroppedItemProvidersInOrder

* WebCore.xcodeproj/project.pbxproj:
* editing/WebContentReader.cpp:
(WebCore::WebContentReader::ensureFragment):

Add a new helper to create the WebContentReader's fragment, if it hasn't already been created.

* editing/WebContentReader.h:
* editing/cocoa/WebContentReaderCocoa.mm:
(WebCore::WebContentReader::readFilePaths):

Rename readFilenames to readFilePaths (which better reflects its parameters, which are file paths). Also, move
the implementation of readFilePaths to shared iOS/macOS code in WebContentReaderCocoa, and remove the stub
implementation on iOS.

There's a bit of code here that I kept macOS-only which deals with inserting file paths as plain text in
editable areas, but it's unclear to me why and if WebKit clients currently find this useful, so I left a FIXME
to investigate removing this altogether. Code for handling this plain text insertion of file paths on Mac was
introduced in r67403.

* editing/ios/WebContentReaderIOS.mm:
(WebCore::WebContentReader::readFilenames): Deleted.
* editing/mac/WebContentReaderMac.mm:
(WebCore::WebContentReader::readFilenames): Deleted.
* page/mac/DragControllerMac.mm:
(WebCore::DragController::updateSupportedTypeIdentifiersForDragHandlingMethod const):

Teach DragController to accept all types conforming to "public.item" and "public.content" on iOS, only when
attachment elements are enabled. This allows us to load content from item providers that we otherwise would not
have loaded, since we now have the ability to fall back to attachment element insertion if the type is not have
a default representation using standard web content.

* platform/Pasteboard.h:
* platform/PasteboardItemInfo.h: Added.
(WebCore::PasteboardItemInfo::encode const):
(WebCore::PasteboardItemInfo::decode):

Add PasteboardItemInfo, a struct that describes an item on the pasteboard. Also, implement encoding and decoding
support for PasteboardItemInfo. So far, the item info only describes file information about the pasteboard item,
and flags indicating whether the item prefers attachment or inline presentation.

* platform/PasteboardStrategy.h:

Replace getFilenamesForDataInteraction with informationForItemAtIndex. Instead of returning all of the file
paths associated with any item on the pasteboard, fetch a PasteboardItemInfo at a given item index, which
includes information about the file path as well as some other metadata we'll need when deciding how to read
pasteboard contents as a document fragment.

* platform/PlatformPasteboard.h:
* platform/cocoa/PasteboardCocoa.mm:
(WebCore::Pasteboard::read):
* platform/ios/AbstractPasteboard.h:
* platform/ios/PasteboardIOS.mm:
(WebCore::Pasteboard::read):
(WebCore::Pasteboard::readRespectingUTIFidelities):

Teach the iOS Pasteboard to read web content using attachment elements, if enabled. There are two scenarios in
which we would want to insert an attachment element:
(1) The item provider uses a preferred presentation style of attachment, in which case we bail out of trying to
    handle the drop using the default mechanisms, and simply insert it as an attachment. We need this to deal
    with the case where we drop text or HTML files from the Files app, so that we don't try and insert the
    contents of the text or HTML as inline web content.
(2) The item provider doesn't have a preferred attachment presentation style, but there's nothing WebKit would
    otherwise do with the dropped content, so insert an attachment element as a fallback. Examples where this is
    relevant are dropping a PDF or ZIP archive without attachment presentation style explicitly set.
We first check if we fall into case (1). If so, we can bail early by inserting an attachment; otherwise, we
proceed normally and see if we can read the contents of the drop as web content. If, at the end of default drop
handling, we don't still have a way to represent the dropped content, enter case (2).

(WebCore::Pasteboard::readFilePaths):
(WebCore::Pasteboard::readFilenames): Deleted.

Rename readFilenames to readFilePaths, and reimplement it using informationForItemAtIndex.

* platform/ios/PlatformPasteboardIOS.mm:
(WebCore::pasteboardItemPresentationStyle):
(WebCore::PlatformPasteboard::informationForItemAtIndex):
(WebCore::PlatformPasteboard::filenamesForDataInteraction): Deleted.

Implement informationForItemAtIndex and remove filenamesForDataInteraction. As before, we ask the pasteboard
(i.e. WebItemProviderPasteboard) for information about dropped file URLs. This time, we limit this to a single
file, so we don't end up creating multiple attachment elements for each representation of a single item
provider. See below for -preferredFileUploadURLAtIndex:fileType: for more detail.

* platform/ios/WebItemProviderPasteboard.h:
* platform/ios/WebItemProviderPasteboard.mm:
(-[WebItemProviderLoadResult initWithItemProvider:typesToLoad:]):
(-[WebItemProviderLoadResult canBeRepresentedAsFileUpload]):

Remove this synthesized instance variable and instead just check the item provider's preferredPresentationStyle.

(-[WebItemProviderLoadResult description]):

Add a verbose -description to the load result object. Useful for debugging what was content was loaded from an
item provider on drop.

(-[WebItemProviderPasteboard preferredFileUploadURLAtIndex:fileType:]):

Return the highest fidelity loaded type identifier for a given item.

(-[WebItemProviderPasteboard allDroppedFileURLs]):
(-[WebItemProviderPasteboard typeIdentifiersToLoadForRegisteredTypeIdentfiers:]):

Prefer flat RTFD to RTFD. In the case where attachments are enabled and we're accepting all types of content
using attachment elements as a fallback representation, if the source writes attributed strings to the
pasteboard with com.apple.rtfd at a higher fidelity than com.apple.flat-rtfd, we'll end up loading only
com.apple.rtfd and dropping the text as an attachment element because we cannot convert the dropped content to
markup. Instead, if flat RTFD is present in the item provider, always prefer that over RTFD so that dropping as
regular web content isn't overridden when attachment elements are enabled.

(-[WebItemProviderPasteboard doAfterLoadingProvidedContentIntoFileURLs:synchronousTimeout:]):
(-[WebItemProviderPasteboard droppedFileURLs]): Deleted.
* platform/mac/DragDataMac.mm:
(WebCore::DragData::containsCompatibleContent const):

DragData::containsCompatibleContent should be true when attachment elements are enabled, and there are files we
can drop as attachment elements.

* platform/mac/PasteboardMac.mm:
(WebCore::Pasteboard::read):
(WebCore::Pasteboard::readFilePaths):
(WebCore::Pasteboard::readFilenames): Deleted.

Source/WebKit:

Make some minor adjustments for changes to the pasteboard in WebCore. See WebCore/ChangeLog for more detail.
Teaches WebPasteboardProxy et. al. to plumb PasteboardItemInfo from the UI process to the web process via the
new `InformationForItemAtIndex` codepath.

* UIProcess/Cocoa/WebPasteboardProxyCocoa.mm:
(WebKit::WebPasteboardProxy::informationForItemAtIndex):
(WebKit::WebPasteboardProxy::getFilenamesForDataInteraction): Deleted.
* UIProcess/WebPasteboardProxy.h:
* UIProcess/WebPasteboardProxy.messages.in:
* WebProcess/WebCoreSupport/WebPlatformStrategies.cpp:
(WebKit::WebPlatformStrategies::informationForItemAtIndex):
(WebKit::WebPlatformStrategies::getFilenamesForDataInteraction): Deleted.
* WebProcess/WebCoreSupport/WebPlatformStrategies.h:

Source/WebKitLegacy/mac:

Make some minor adjustments for changes to the pasteboard in WebCore. See WebCore/ChangeLog for more detail.

* WebCoreSupport/WebPlatformStrategies.h:
* WebCoreSupport/WebPlatformStrategies.mm:
(WebPlatformStrategies::informationForItemAtIndex):
(WebPlatformStrategies::getFilenamesForDataInteraction): Deleted.

Tools:

Adds 3 new API tests to exercise different use cases of dropping content as attachment elements when the runtime
switch is enabled. See below for more details.

* TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm:
(-[NSItemProvider registerData:type:]):
(platformCopyPNG):
(TestWebKitAPI::TEST):
* TestWebKitAPI/Tests/ios/DataInteractionTests.mm:

Fix some currently failing iOS drag and drop tests. In this case, there's no reason RTFD should appear in the
source item provider when dragging rich text *without* attachments, so this should have been a check for just
kUTTypeRTF instead.

(TestWebKitAPI::TEST):

Tests a few cases of inserting attachment elements via drop:
1.  We should distinguish between drops containing rich/plain text files from just dropping rich/plain text.
    Instead of inserting the contents as inline web content, this should generate attachment elements.
2.  Test the fallback mechanism for inserting attachment elements. If the preferred presentation style is not
    explicitly set, but there's nothing WebKit would otherwise do with the dropped content, then we should fall
    back to inserting the content as an attachment.
3.  Test that if multiple attachments and inline item providers are present, WebKit will respect the order in
    which they were inserted by the source (as opposed to, for instance, putting all of the attachments in front
    or at the end).

* TestWebKitAPI/cocoa/TestWKWebView.h:
* TestWebKitAPI/cocoa/TestWKWebView.mm:
(-[TestWKWebView objectByEvaluatingJavaScript:]):

Add a helper method to return an object that represents the result of evaluating some given script, and rewrite
-stringByEvaluatingJavaScript to just turn around and call this.

(-[TestWKWebView stringByEvaluatingJavaScript:]):

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

34 files changed:
Source/WebCore/ChangeLog
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/editing/WebContentReader.cpp
Source/WebCore/editing/WebContentReader.h
Source/WebCore/editing/cocoa/WebContentReaderCocoa.mm
Source/WebCore/editing/ios/WebContentReaderIOS.mm
Source/WebCore/editing/mac/WebContentReaderMac.mm
Source/WebCore/page/mac/DragControllerMac.mm
Source/WebCore/platform/Pasteboard.h
Source/WebCore/platform/PasteboardItemInfo.h [new file with mode: 0644]
Source/WebCore/platform/PasteboardStrategy.h
Source/WebCore/platform/PlatformPasteboard.h
Source/WebCore/platform/cocoa/PasteboardCocoa.mm
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/DragDataMac.mm
Source/WebCore/platform/mac/PasteboardMac.mm
Source/WebKit/ChangeLog
Source/WebKit/UIProcess/Cocoa/WebPasteboardProxyCocoa.mm
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/Tests/WebKitCocoa/WKAttachmentTests.mm
Tools/TestWebKitAPI/Tests/ios/DataInteractionTests.mm
Tools/TestWebKitAPI/cocoa/TestWKWebView.h
Tools/TestWebKitAPI/cocoa/TestWKWebView.mm

index b2ce02f..d34b9bd 100644 (file)
@@ -1,3 +1,139 @@
+2018-01-03  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [Attachment Support] Create attachment elements when dropping files on iOS
+        https://bugs.webkit.org/show_bug.cgi?id=181192
+        <rdar://problem/36280945>
+
+        Reviewed by Tim Horton.
+
+        Implements support for dropping data as attachment elements on iOS. See comments below for more detail.
+
+        Tests:  WKAttachmentTests.InsertDroppedRichAndPlainTextFilesAsAttachments
+                WKAttachmentTests.InsertDroppedZipArchiveAsAttachment
+                WKAttachmentTests.InsertDroppedItemProvidersInOrder
+
+        * WebCore.xcodeproj/project.pbxproj:
+        * editing/WebContentReader.cpp:
+        (WebCore::WebContentReader::ensureFragment):
+
+        Add a new helper to create the WebContentReader's fragment, if it hasn't already been created.
+
+        * editing/WebContentReader.h:
+        * editing/cocoa/WebContentReaderCocoa.mm:
+        (WebCore::WebContentReader::readFilePaths):
+
+        Rename readFilenames to readFilePaths (which better reflects its parameters, which are file paths). Also, move
+        the implementation of readFilePaths to shared iOS/macOS code in WebContentReaderCocoa, and remove the stub
+        implementation on iOS.
+
+        There's a bit of code here that I kept macOS-only which deals with inserting file paths as plain text in
+        editable areas, but it's unclear to me why and if WebKit clients currently find this useful, so I left a FIXME
+        to investigate removing this altogether. Code for handling this plain text insertion of file paths on Mac was
+        introduced in r67403.
+
+        * editing/ios/WebContentReaderIOS.mm:
+        (WebCore::WebContentReader::readFilenames): Deleted.
+        * editing/mac/WebContentReaderMac.mm:
+        (WebCore::WebContentReader::readFilenames): Deleted.
+        * page/mac/DragControllerMac.mm:
+        (WebCore::DragController::updateSupportedTypeIdentifiersForDragHandlingMethod const):
+
+        Teach DragController to accept all types conforming to "public.item" and "public.content" on iOS, only when
+        attachment elements are enabled. This allows us to load content from item providers that we otherwise would not
+        have loaded, since we now have the ability to fall back to attachment element insertion if the type is not have
+        a default representation using standard web content.
+
+        * platform/Pasteboard.h:
+        * platform/PasteboardItemInfo.h: Added.
+        (WebCore::PasteboardItemInfo::encode const):
+        (WebCore::PasteboardItemInfo::decode):
+
+        Add PasteboardItemInfo, a struct that describes an item on the pasteboard. Also, implement encoding and decoding
+        support for PasteboardItemInfo. So far, the item info only describes file information about the pasteboard item,
+        and flags indicating whether the item prefers attachment or inline presentation.
+
+        * platform/PasteboardStrategy.h:
+
+        Replace getFilenamesForDataInteraction with informationForItemAtIndex. Instead of returning all of the file
+        paths associated with any item on the pasteboard, fetch a PasteboardItemInfo at a given item index, which
+        includes information about the file path as well as some other metadata we'll need when deciding how to read
+        pasteboard contents as a document fragment.
+
+        * platform/PlatformPasteboard.h:
+        * platform/cocoa/PasteboardCocoa.mm:
+        (WebCore::Pasteboard::read):
+        * platform/ios/AbstractPasteboard.h:
+        * platform/ios/PasteboardIOS.mm:
+        (WebCore::Pasteboard::read):
+        (WebCore::Pasteboard::readRespectingUTIFidelities):
+
+        Teach the iOS Pasteboard to read web content using attachment elements, if enabled. There are two scenarios in
+        which we would want to insert an attachment element:
+        (1) The item provider uses a preferred presentation style of attachment, in which case we bail out of trying to
+            handle the drop using the default mechanisms, and simply insert it as an attachment. We need this to deal
+            with the case where we drop text or HTML files from the Files app, so that we don't try and insert the
+            contents of the text or HTML as inline web content.
+        (2) The item provider doesn't have a preferred attachment presentation style, but there's nothing WebKit would
+            otherwise do with the dropped content, so insert an attachment element as a fallback. Examples where this is
+            relevant are dropping a PDF or ZIP archive without attachment presentation style explicitly set.
+        We first check if we fall into case (1). If so, we can bail early by inserting an attachment; otherwise, we
+        proceed normally and see if we can read the contents of the drop as web content. If, at the end of default drop
+        handling, we don't still have a way to represent the dropped content, enter case (2).
+
+        (WebCore::Pasteboard::readFilePaths):
+        (WebCore::Pasteboard::readFilenames): Deleted.
+
+        Rename readFilenames to readFilePaths, and reimplement it using informationForItemAtIndex.
+
+        * platform/ios/PlatformPasteboardIOS.mm:
+        (WebCore::pasteboardItemPresentationStyle):
+        (WebCore::PlatformPasteboard::informationForItemAtIndex):
+        (WebCore::PlatformPasteboard::filenamesForDataInteraction): Deleted.
+
+        Implement informationForItemAtIndex and remove filenamesForDataInteraction. As before, we ask the pasteboard
+        (i.e. WebItemProviderPasteboard) for information about dropped file URLs. This time, we limit this to a single
+        file, so we don't end up creating multiple attachment elements for each representation of a single item
+        provider. See below for -preferredFileUploadURLAtIndex:fileType: for more detail.
+
+        * platform/ios/WebItemProviderPasteboard.h:
+        * platform/ios/WebItemProviderPasteboard.mm:
+        (-[WebItemProviderLoadResult initWithItemProvider:typesToLoad:]):
+        (-[WebItemProviderLoadResult canBeRepresentedAsFileUpload]):
+
+        Remove this synthesized instance variable and instead just check the item provider's preferredPresentationStyle.
+
+        (-[WebItemProviderLoadResult description]):
+
+        Add a verbose -description to the load result object. Useful for debugging what was content was loaded from an
+        item provider on drop.
+
+        (-[WebItemProviderPasteboard preferredFileUploadURLAtIndex:fileType:]):
+
+        Return the highest fidelity loaded type identifier for a given item.
+
+        (-[WebItemProviderPasteboard allDroppedFileURLs]):
+        (-[WebItemProviderPasteboard typeIdentifiersToLoadForRegisteredTypeIdentfiers:]):
+
+        Prefer flat RTFD to RTFD. In the case where attachments are enabled and we're accepting all types of content
+        using attachment elements as a fallback representation, if the source writes attributed strings to the
+        pasteboard with com.apple.rtfd at a higher fidelity than com.apple.flat-rtfd, we'll end up loading only
+        com.apple.rtfd and dropping the text as an attachment element because we cannot convert the dropped content to
+        markup. Instead, if flat RTFD is present in the item provider, always prefer that over RTFD so that dropping as
+        regular web content isn't overridden when attachment elements are enabled.
+
+        (-[WebItemProviderPasteboard doAfterLoadingProvidedContentIntoFileURLs:synchronousTimeout:]):
+        (-[WebItemProviderPasteboard droppedFileURLs]): Deleted.
+        * platform/mac/DragDataMac.mm:
+        (WebCore::DragData::containsCompatibleContent const):
+
+        DragData::containsCompatibleContent should be true when attachment elements are enabled, and there are files we
+        can drop as attachment elements.
+
+        * platform/mac/PasteboardMac.mm:
+        (WebCore::Pasteboard::read):
+        (WebCore::Pasteboard::readFilePaths):
+        (WebCore::Pasteboard::readFilenames): Deleted.
+
 2018-01-03  Ting-Wei Lan  <lantw44@gmail.com>
 
         Replace hard-coded paths in shebangs with #!/usr/bin/env
index 45bbb0c..40984af 100644 (file)
                F48223101E3869B80066FC79 /* WebItemProviderPasteboard.mm in Sources */ = {isa = PBXBuildFile; fileRef = F482230E1E3869B80066FC79 /* WebItemProviderPasteboard.mm */; };
                F48223111E3869B80066FC79 /* WebItemProviderPasteboard.h in Headers */ = {isa = PBXBuildFile; fileRef = F482230F1E3869B80066FC79 /* WebItemProviderPasteboard.h */; settings = {ATTRIBUTES = (Private, ); }; };
                F48223131E386E240066FC79 /* AbstractPasteboard.h in Headers */ = {isa = PBXBuildFile; fileRef = F48223121E386E240066FC79 /* AbstractPasteboard.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               F49786881FF45FA500E060AB /* PasteboardItemInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = F49786871FF45FA500E060AB /* PasteboardItemInfo.h */; settings = {ATTRIBUTES = (Private, ); }; };
                F4BFB9851E1DDF9B00862C24 /* DumpEditingHistory.js in Copy Scripts */ = {isa = PBXBuildFile; fileRef = F48389831E1DDF2B0076B7EA /* DumpEditingHistory.js */; };
                F4BFB9861E1DDF9B00862C24 /* EditingHistoryUtil.js in Copy Scripts */ = {isa = PBXBuildFile; fileRef = F48389841E1DDF2B0076B7EA /* EditingHistoryUtil.js */; };
                F50664F8157F52DC00AC226F /* FormController.h in Headers */ = {isa = PBXBuildFile; fileRef = F50664F6157F52DC00AC226F /* FormController.h */; };
                F48223121E386E240066FC79 /* AbstractPasteboard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AbstractPasteboard.h; sourceTree = "<group>"; };
                F48389831E1DDF2B0076B7EA /* DumpEditingHistory.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = DumpEditingHistory.js; path = Scripts/DumpEditingHistory.js; sourceTree = "<group>"; };
                F48389841E1DDF2B0076B7EA /* EditingHistoryUtil.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = EditingHistoryUtil.js; path = Scripts/EditingHistoryUtil.js; sourceTree = "<group>"; };
+               F49786871FF45FA500E060AB /* PasteboardItemInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PasteboardItemInfo.h; sourceTree = "<group>"; };
                F50664F5157F52DC00AC226F /* FormController.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FormController.cpp; sourceTree = "<group>"; };
                F50664F6157F52DC00AC226F /* FormController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FormController.h; sourceTree = "<group>"; };
                F513A3E915FF4841001526DB /* ValidationMessageClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ValidationMessageClient.h; sourceTree = "<group>"; };
                                4184F5151EAF059800F18BF0 /* OrientationNotifier.h */,
                                2EE02A1E1F7324280006AF72 /* Pasteboard.cpp */,
                                4B2708C50AF19EE40065127F /* Pasteboard.h */,
+                               F49786871FF45FA500E060AB /* PasteboardItemInfo.h */,
                                C5F765B414E1D414006C899B /* PasteboardStrategy.h */,
                                1AF5E4D21E56735A004A1F01 /* PasteboardWriterData.cpp */,
                                1AF5E4D31E56735A004A1F01 /* PasteboardWriterData.h */,
                                536D5A23193E8E0C00CE4CAB /* ParsingUtilities.h in Headers */,
                                F55B3DCA1251F12D003EF269 /* PasswordInputType.h in Headers */,
                                4B2708C70AF19EE40065127F /* Pasteboard.h in Headers */,
+                               F49786881FF45FA500E060AB /* PasteboardItemInfo.h in Headers */,
                                C598905714E9C28000E8D18B /* PasteboardStrategy.h in Headers */,
                                1AF5E4E31E5779B1004A1F01 /* PasteboardWriter.h in Headers */,
                                1AF5E4D51E56735B004A1F01 /* PasteboardWriterData.h in Headers */,
index cf41936..544912a 100644 (file)
 
 namespace WebCore {
 
+DocumentFragment& WebContentReader::ensureFragment()
+{
+    ASSERT(frame.document());
+    if (!fragment)
+        fragment = frame.document()->createDocumentFragment();
+    return *fragment;
+}
+
 void WebContentReader::addFragment(Ref<DocumentFragment>&& newFragment)
 {
     if (!fragment)
index cc17035..df984ba 100644 (file)
@@ -63,12 +63,13 @@ public:
     {
     }
 
+    DocumentFragment& ensureFragment();
     void addFragment(Ref<DocumentFragment>&&);
 
 private:
 #if PLATFORM(COCOA)
     bool readWebArchive(SharedBuffer&) override;
-    bool readFilenames(const Vector<String>&) override;
+    bool readFilePaths(const Vector<String>&) override;
     bool readHTML(const String&) override;
     bool readRTFD(SharedBuffer&) override;
     bool readRTF(SharedBuffer&) override;
@@ -90,7 +91,7 @@ public:
 private:
 #if PLATFORM(COCOA)
     bool readWebArchive(SharedBuffer&) override;
-    bool readFilenames(const Vector<String>&) override { return false; }
+    bool readFilePaths(const Vector<String>&) override { return false; }
     bool readHTML(const String&) override;
     bool readRTFD(SharedBuffer&) override;
     bool readRTF(SharedBuffer&) override;
index 1e3bbfd..204903d 100644 (file)
@@ -596,4 +596,34 @@ bool WebContentReader::readImage(Ref<SharedBuffer>&& buffer, const String& type)
     return fragment;
 }
 
+bool WebContentReader::readFilePaths(const Vector<String>& paths)
+{
+    if (paths.isEmpty() || !frame.document())
+        return false;
+
+    auto& document = *frame.document();
+    bool readAnyFilePath = false;
+    for (auto& path : paths) {
+#if ENABLE(ATTACHMENT_ELEMENT)
+        if (RuntimeEnabledFeatures::sharedFeatures().attachmentElementEnabled()) {
+            auto attachment = HTMLAttachmentElement::create(HTMLNames::attachmentTag, document);
+            attachment->setUniqueIdentifier(createCanonicalUUIDString());
+            attachment->setFile(File::create(path), HTMLAttachmentElement::UpdateDisplayAttributes::Yes);
+            ensureFragment().appendChild(attachment);
+            readAnyFilePath = true;
+            continue;
+        }
+#endif
+#if PLATFORM(MAC)
+        // FIXME: Does (and should) any macOS client depend on inserting file paths as plain text in web content?
+        // If not, we should just remove this.
+        auto paragraph = createDefaultParagraphElement(document);
+        paragraph->appendChild(document.createTextNode(userVisibleString([NSURL fileURLWithPath:path])));
+        ensureFragment().appendChild(paragraph);
+        readAnyFilePath = true;
+#endif
+    }
+    return readAnyFilePath;
+}
+
 }
index 034ba9a..203c847 100644 (file)
 
 namespace WebCore {
 
-bool WebContentReader::readFilenames(const Vector<String>&)
-{
-    return false;
-}
-
 bool WebContentReader::readURL(const URL& url, const String& title)
 {
     if (url.isEmpty())
index 9c5d254..4283a4f 100644 (file)
 #import "FrameLoader.h"
 #import "FrameLoaderClient.h"
 #import "HTMLAnchorElement.h"
-#import "HTMLAttachmentElement.h"
 #import "HTMLNames.h"
 #import "LegacyWebArchive.h"
-#import "MIMETypeRegistry.h"
-#import "RuntimeEnabledFeatures.h"
 #import "Settings.h"
 #import "Text.h"
 #import "WebCoreNSURLExtras.h"
 #import "markup.h"
-#import <wtf/UUID.h>
 
 namespace WebCore {
 
-bool WebContentReader::readFilenames(const Vector<String>& paths)
-{
-    if (paths.isEmpty())
-        return false;
-
-    if (!frame.document())
-        return false;
-    Document& document = *frame.document();
-
-    fragment = document.createDocumentFragment();
-
-    for (auto& text : paths) {
-#if ENABLE(ATTACHMENT_ELEMENT)
-        if (RuntimeEnabledFeatures::sharedFeatures().attachmentElementEnabled()) {
-            auto attachment = HTMLAttachmentElement::create(HTMLNames::attachmentTag, document);
-            attachment->setUniqueIdentifier(createCanonicalUUIDString());
-            attachment->setFile(File::create([NSURL fileURLWithPath:text].path), HTMLAttachmentElement::UpdateDisplayAttributes::Yes);
-            fragment->appendChild(attachment);
-            continue;
-        }
-#else
-        auto paragraph = createDefaultParagraphElement(document);
-        paragraph->appendChild(document.createTextNode(userVisibleString([NSURL fileURLWithPath:text])));
-        fragment->appendChild(paragraph);
-#endif
-    }
-
-    return true;
-}
-
 bool WebContentReader::readURL(const URL& url, const String& title)
 {
     if (url.string().isEmpty())
index 5e872c4..f75d1e4 100644 (file)
@@ -45,6 +45,7 @@
 #import "PasteboardStrategy.h"
 #import "PlatformStrategies.h"
 #import "Range.h"
+#import "RuntimeEnabledFeatures.h"
 
 #if ENABLE(DATA_INTERACTION)
 #import <MobileCoreServices/MobileCoreServices.h>
@@ -125,8 +126,14 @@ void DragController::updateSupportedTypeIdentifiersForDragHandlingMethod(DragHan
         supportedTypes.append(kUTTypePlainText);
         break;
     case DragHandlingMethod::EditRichText:
-        for (NSString *type in Pasteboard::supportedWebContentPasteboardTypes())
-            supportedTypes.append(type);
+        if (RuntimeEnabledFeatures::sharedFeatures().attachmentElementEnabled()) {
+            supportedTypes.append(WebArchivePboardType);
+            supportedTypes.append(kUTTypeContent);
+            supportedTypes.append(kUTTypeItem);
+        } else {
+            for (NSString *type in Pasteboard::supportedWebContentPasteboardTypes())
+                supportedTypes.append(type);
+        }
         break;
     default:
         for (NSString *type in Pasteboard::supportedFileUploadPasteboardTypes())
index 21bf903..a546f59 100644 (file)
@@ -26,6 +26,7 @@
 #pragma once
 
 #include "DragImage.h"
+#include "PasteboardItemInfo.h"
 #include "URL.h"
 #include <wtf/HashMap.h>
 #include <wtf/ListHashSet.h>
@@ -134,7 +135,7 @@ public:
 
 #if PLATFORM(COCOA)
     virtual bool readWebArchive(SharedBuffer&) = 0;
-    virtual bool readFilenames(const Vector<String>&) = 0;
+    virtual bool readFilePaths(const Vector<String>&) = 0;
     virtual bool readHTML(const String&) = 0;
     virtual bool readRTFD(SharedBuffer&) = 0;
     virtual bool readRTF(SharedBuffer&) = 0;
@@ -290,7 +291,7 @@ private:
 #endif
 
 #if PLATFORM(COCOA)
-    Vector<String> readFilenames();
+    Vector<String> readFilePaths();
     String readPlatformValueAsString(const String& domType, long changeCount, const String& pasteboardName);
     static void addHTMLClipboardTypesForCocoaType(ListHashSet<String>& resultTypes, const String& cocoaType);
     String readStringForPlatformType(const String&);
diff --git a/Source/WebCore/platform/PasteboardItemInfo.h b/Source/WebCore/platform/PasteboardItemInfo.h
new file mode 100644 (file)
index 0000000..f7c35d2
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <wtf/Optional.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+enum class PasteboardItemPresentationStyle {
+    Unspecified,
+    Inline,
+    Attachment
+};
+
+struct PasteboardItemInfo {
+    String pathForFileUpload;
+    String contentTypeForFileUpload;
+    PasteboardItemPresentationStyle preferredPresentationStyle { PasteboardItemPresentationStyle::Unspecified };
+
+    template<class Encoder> void encode(Encoder&) const;
+    template<class Decoder> static std::optional<PasteboardItemInfo> decode(Decoder&);
+};
+
+template<class Encoder>
+void PasteboardItemInfo::encode(Encoder& encoder) const
+{
+    encoder << pathForFileUpload << contentTypeForFileUpload;
+    encoder.encodeEnum(preferredPresentationStyle);
+}
+
+template<class Decoder>
+std::optional<PasteboardItemInfo> PasteboardItemInfo::decode(Decoder& decoder)
+{
+    PasteboardItemInfo result;
+    if (!decoder.decode(result.pathForFileUpload))
+        return std::nullopt;
+
+    if (!decoder.decode(result.contentTypeForFileUpload))
+        return std::nullopt;
+
+    if (!decoder.decodeEnum(result.preferredPresentationStyle))
+        return std::nullopt;
+
+    return WTFMove(result);
+}
+
+}
+
+namespace WTF {
+
+template<typename> struct EnumTraits;
+template<typename E, E...> struct EnumValues;
+
+template<> struct EnumTraits<WebCore::PasteboardItemPresentationStyle> {
+    using values = EnumValues<
+        WebCore::PasteboardItemPresentationStyle,
+        WebCore::PasteboardItemPresentationStyle::Unspecified,
+        WebCore::PasteboardItemPresentationStyle::Inline,
+        WebCore::PasteboardItemPresentationStyle::Attachment
+    >;
+};
+
+} // namespace WTF
index c064ad0..5837d91 100644 (file)
@@ -35,6 +35,7 @@ class SelectionData;
 class SharedBuffer;
 class URL;
 struct PasteboardImage;
+struct PasteboardItemInfo;
 struct PasteboardURL;
 struct PasteboardWebContent;
 struct PasteboardCustomData;
@@ -50,7 +51,7 @@ public:
     virtual String readStringFromPasteboard(int index, const String& pasteboardType, const String& pasteboardName) = 0;
     virtual RefPtr<SharedBuffer> readBufferFromPasteboard(int index, const String& pasteboardType, const String& pasteboardName) = 0;
     virtual URL readURLFromPasteboard(int index, const String& pasteboardType, const String& pasteboardName, String& title) = 0;
-    virtual void getFilenamesForDataInteraction(Vector<String>& filenames, const String& pasteboardName) = 0;
+    virtual PasteboardItemInfo informationForItemAtIndex(int index, const String& pasteboardName) = 0;
     virtual void updateSupportedTypeIdentifiers(const Vector<String>& identifiers, const String& pasteboardName) = 0;
     virtual void getTypesByFidelityForItemAtIndex(Vector<String>& types, uint64_t index, const String& pasteboardName) = 0;
 #endif // PLATFORM(IOS)
index d8d7c94..2488053 100644 (file)
@@ -51,6 +51,7 @@ class SharedBuffer;
 class URL;
 struct PasteboardCustomData;
 struct PasteboardImage;
+struct PasteboardItemInfo;
 struct PasteboardURL;
 struct PasteboardWebContent;
 
@@ -59,7 +60,7 @@ public:
     WEBCORE_EXPORT explicit PlatformPasteboard(const String& pasteboardName);
 #if PLATFORM(IOS) || PLATFORM(WPE)
     WEBCORE_EXPORT PlatformPasteboard();
-    WEBCORE_EXPORT Vector<String> filenamesForDataInteraction();
+    WEBCORE_EXPORT PasteboardItemInfo informationForItemAtIndex(int index);
     WEBCORE_EXPORT void getTypesByFidelityForItemAtIndex(Vector<String>& types, int index);
     WEBCORE_EXPORT void updateSupportedTypeIdentifiers(const Vector<String>& types);
 #endif
index 5085e20..a62e744 100644 (file)
@@ -191,7 +191,7 @@ static Ref<SharedBuffer> convertTIFFToPNG(SharedBuffer& tiffBuffer)
 
 void Pasteboard::read(PasteboardFileReader& reader)
 {
-    auto filenames = readFilenames();
+    auto filenames = readFilePaths();
     if (!filenames.isEmpty()) {
         for (auto& filename : filenames)
             reader.readFilename(filename);
index e8e5bf3..4210186 100644 (file)
@@ -56,7 +56,8 @@ NS_ASSUME_NONNULL_BEGIN
 - (void)setItems:(NSArray<NSDictionary *> *)items;
 - (NSArray<NSString *> *)pasteboardTypesByFidelityForItemAtIndex:(NSUInteger)index;
 @property (readonly, nonatomic) NSInteger numberOfFiles;
-@property (readonly, nonatomic) NSArray<NSURL *> *droppedFileURLs;
+@property (readonly, nonatomic) NSArray<NSURL *> *allDroppedFileURLs;
+- (nullable NSURL *)preferredFileUploadURLAtIndex:(NSUInteger)index fileType:(NSString *_Nullable *_Nullable)outFileType;
 - (void)updateSupportedTypeIdentifiers:(NSArray<NSString *> *)types;
 
 @end
index cd1b746..b887984 100644 (file)
@@ -31,6 +31,7 @@
 #import "PasteboardStrategy.h"
 #import "PlatformPasteboard.h"
 #import "PlatformStrategies.h"
+#import "RuntimeEnabledFeatures.h"
 #import "SharedBuffer.h"
 #import "URL.h"
 #import "UTIUtilities.h"
@@ -244,10 +245,10 @@ void Pasteboard::read(PasteboardWebContentReader& reader)
 
     for (int i = 0; i < numberOfItems; i++) {
         for (int typeIndex = 0; typeIndex < numberOfTypes; typeIndex++) {
-            auto result = readPasteboardWebContentDataForType(reader, strategy, [types objectAtIndex:typeIndex], i);
-            if (result == ReaderResult::PasteboardWasChangedExternally)
+            auto itemResult = readPasteboardWebContentDataForType(reader, strategy, [types objectAtIndex:typeIndex], i);
+            if (itemResult == ReaderResult::PasteboardWasChangedExternally)
                 return;
-            if (result == ReaderResult::ReadType)
+            if (itemResult == ReaderResult::ReadType)
                 break;
         }
     }
@@ -266,17 +267,30 @@ void Pasteboard::readRespectingUTIFidelities(PasteboardWebContentReader& reader)
     ASSERT(respectsUTIFidelities());
     auto& strategy = *platformStrategies()->pasteboardStrategy();
     for (NSUInteger index = 0, numberOfItems = strategy.getPasteboardItemsCount(m_pasteboardName); index < numberOfItems; ++index) {
+#if ENABLE(ATTACHMENT_ELEMENT)
+        auto info = strategy.informationForItemAtIndex(index, m_pasteboardName);
+        bool canReadAttachment = RuntimeEnabledFeatures::sharedFeatures().attachmentElementEnabled() && !info.pathForFileUpload.isEmpty();
+        if (canReadAttachment && info.preferredPresentationStyle == PasteboardItemPresentationStyle::Attachment) {
+            reader.readFilePaths({ info.pathForFileUpload });
+            continue;
+        }
+#endif
         // Try to read data from each type identifier that this pasteboard item supports, and WebKit also recognizes. Type identifiers are
         // read in order of fidelity, as specified by each pasteboard item.
         Vector<String> typesForItemInOrderOfFidelity;
         strategy.getTypesByFidelityForItemAtIndex(typesForItemInOrderOfFidelity, index, m_pasteboardName);
+        ReaderResult result = ReaderResult::DidNotReadType;
         for (auto& type : typesForItemInOrderOfFidelity) {
-            auto result = readPasteboardWebContentDataForType(reader, strategy, type, index);
+            result = readPasteboardWebContentDataForType(reader, strategy, type, index);
             if (result == ReaderResult::PasteboardWasChangedExternally)
                 return;
             if (result == ReaderResult::ReadType)
                 break;
         }
+#if ENABLE(ATTACHMENT_ELEMENT)
+        if (canReadAttachment && result == ReaderResult::DidNotReadType)
+            reader.readFilePaths({ info.pathForFileUpload });
+#endif
     }
 }
 
@@ -405,12 +419,17 @@ void Pasteboard::writeString(const String& type, const String& data)
     platformStrategies()->pasteboardStrategy()->writeToPasteboard(cocoaType.get(), data, m_pasteboardName);
 }
 
-Vector<String> Pasteboard::readFilenames()
+Vector<String> Pasteboard::readFilePaths()
 {
-    Vector<String> filenames;
-    // Currently, data interaction is the only case on iOS where the pasteboard may contain relevant filenames.
-    platformStrategies()->pasteboardStrategy()->getFilenamesForDataInteraction(filenames, m_pasteboardName);
-    return filenames;
+    Vector<String> filePaths;
+    auto& strategy = *platformStrategies()->pasteboardStrategy();
+    for (NSUInteger index = 0, numberOfItems = strategy.getPasteboardItemsCount(m_pasteboardName); index < numberOfItems; ++index) {
+        // Currently, drag and drop is the only case on iOS where the "pasteboard" may contain file paths.
+        auto filePath = strategy.informationForItemAtIndex(index, m_pasteboardName).pathForFileUpload;
+        if (!filePath.isEmpty())
+            filePaths.append(WTFMove(filePath));
+    }
+    return filePaths;
 }
 
 }
index 16b71f8..91c9011 100644 (file)
@@ -102,18 +102,49 @@ int PlatformPasteboard::numberOfFiles() const
     return [m_pasteboard respondsToSelector:@selector(numberOfFiles)] ? [m_pasteboard numberOfFiles] : 0;
 }
 
-Vector<String> PlatformPasteboard::filenamesForDataInteraction()
+#if PASTEBOARD_SUPPORTS_ITEM_PROVIDERS
+
+static PasteboardItemPresentationStyle pasteboardItemPresentationStyle(UIPreferredPresentationStyle style)
+{
+    switch (style) {
+    case UIPreferredPresentationStyleUnspecified:
+        return PasteboardItemPresentationStyle::Unspecified;
+    case UIPreferredPresentationStyleInline:
+        return PasteboardItemPresentationStyle::Inline;
+    case UIPreferredPresentationStyleAttachment:
+        return PasteboardItemPresentationStyle::Attachment;
+    default:
+        ASSERT_NOT_REACHED();
+        return PasteboardItemPresentationStyle::Unspecified;
+    }
+}
+
+PasteboardItemInfo PlatformPasteboard::informationForItemAtIndex(int index)
 {
-    if (![m_pasteboard respondsToSelector:@selector(droppedFileURLs)])
+    if (index >= [m_pasteboard numberOfItems])
         return { };
 
-    Vector<String> filenames;
-    for (NSURL *fileURL in [m_pasteboard droppedFileURLs])
-        filenames.append(fileURL.path);
+    PasteboardItemInfo info;
+    if ([m_pasteboard respondsToSelector:@selector(preferredFileUploadURLAtIndex:fileType:)]) {
+        NSString *fileType = nil;
+        info.pathForFileUpload = [m_pasteboard preferredFileUploadURLAtIndex:index fileType:&fileType].path;
+        info.contentTypeForFileUpload = fileType;
+    }
+
+    NSItemProvider *itemProvider = [[m_pasteboard itemProviders] objectAtIndex:index];
+    info.preferredPresentationStyle = pasteboardItemPresentationStyle(itemProvider.preferredPresentationStyle);
+    return info;
+}
+
+#else
 
-    return filenames;
+PasteboardItemInfo PlatformPasteboard::informationForItemAtIndex(int)
+{
+    return { };
 }
 
+#endif
+
 static bool pasteboardMayContainFilePaths(id<AbstractPasteboard> pasteboard)
 {
 #if PASTEBOARD_SUPPORTS_ITEM_PROVIDERS
index abff268..cc88ff6 100644 (file)
@@ -90,7 +90,10 @@ WEBCORE_EXPORT @interface WebItemProviderPasteboard : NSObject<AbstractPasteboar
 @property (readonly, nonatomic) NSInteger changeCount;
 
 // This will only be non-empty when an operation is being performed.
-@property (readonly, nonatomic) NSArray<NSURL *> *droppedFileURLs;
+@property (readonly, nonatomic) NSArray<NSURL *> *allDroppedFileURLs;
+
+// The preferred file URL corresponds to the highest fidelity non-private UTI that was loaded.
+- (nullable NSURL *)preferredFileUploadURLAtIndex:(NSUInteger)index fileType:(NSString *_Nullable *_Nullable)outFileType;
 
 @property (readonly, nonatomic) BOOL hasPendingOperation;
 - (void)incrementPendingOperationCount;
index b343168..f8ede15 100644 (file)
@@ -243,11 +243,15 @@ static UIPreferredPresentationStyle uiPreferredPresentationStyle(WebPreferredPre
     _fileURLs = adoptNS([[NSMutableDictionary alloc] init]);
     _itemProvider = itemProvider;
     _typesToLoad = typesToLoad;
-    _canBeRepresentedAsFileUpload = itemProvider.preferredPresentationStyle != UIPreferredPresentationStyleInline;
 
     return self;
 }
 
+- (BOOL)canBeRepresentedAsFileUpload
+{
+    return [_itemProvider preferredPresentationStyle] != UIPreferredPresentationStyleInline;
+}
+
 - (NSArray<NSString *> *)typesToLoad
 {
     return _typesToLoad.get();
@@ -278,6 +282,26 @@ static UIPreferredPresentationStyle uiPreferredPresentationStyle(WebPreferredPre
     return _itemProvider.get();
 }
 
+- (NSString *)description
+{
+    __block NSMutableString *description = [NSMutableString string];
+    [description appendFormat:@"<%@: %p typesToLoad: [ ", [self class], self];
+    [_typesToLoad enumerateObjectsUsingBlock:^(NSString *type, NSUInteger index, BOOL *) {
+        [description appendString:type];
+        if (index + 1 < [_typesToLoad count])
+            [description appendString:@", "];
+    }];
+    [description appendFormat:@" ] fileURLs: { "];
+    __block NSUInteger index = 0;
+    [_fileURLs enumerateKeysAndObjectsUsingBlock:^(NSString *type, NSURL *url, BOOL *) {
+        [description appendFormat:@"%@ => \"%@\"", type, url.path];
+        if (++index < [_fileURLs count])
+            [description appendString:@", "];
+    }];
+    [description appendFormat:@" }>"];
+    return description;
+}
+
 @end
 
 @interface WebItemProviderPasteboard ()
@@ -470,7 +494,38 @@ static Class classForTypeIdentifier(NSString *typeIdentifier, NSString *&outType
     return _changeCount;
 }
 
-- (NSArray<NSURL *> *)droppedFileURLs
+- (NSURL *)preferredFileUploadURLAtIndex:(NSUInteger)index fileType:(NSString **)outFileType
+{
+    if (outFileType)
+        *outFileType = nil;
+
+    if (index >= _loadResults.size())
+        return nil;
+
+    auto result = _loadResults[index];
+    if (![result canBeRepresentedAsFileUpload])
+        return nil;
+
+    NSItemProvider *itemProvider = [result itemProvider];
+    for (NSString *registeredTypeIdentifier in itemProvider.registeredTypeIdentifiers) {
+        // Search for the highest fidelity non-private type identifier we loaded from the item provider.
+        if (!UTTypeIsDeclared((CFStringRef)registeredTypeIdentifier) && !UTTypeIsDynamic((CFStringRef)registeredTypeIdentifier))
+            continue;
+
+        for (NSString *loadedTypeIdentifier in [result loadedTypeIdentifiers]) {
+            if (!UTTypeConformsTo((CFStringRef)registeredTypeIdentifier, (CFStringRef)loadedTypeIdentifier))
+                continue;
+
+            if (outFileType)
+                *outFileType = loadedTypeIdentifier;
+            return [result fileURLForType:loadedTypeIdentifier];
+        }
+    }
+
+    return nil;
+}
+
+- (NSArray<NSURL *> *)allDroppedFileURLs
 {
     NSMutableArray<NSURL *> *fileURLs = [NSMutableArray array];
     for (auto loadResult : _loadResults) {
@@ -539,8 +594,19 @@ static NSURL *linkTemporaryItemProviderFilesToDropStagingDirectory(NSURL *url, N
     NSMutableSet *typesToLoad = [NSMutableSet set];
     NSString *highestFidelityContentType = nil;
 
+    BOOL containsFlatRTFD = [registeredTypeIdentifiers containsObject:(NSString *)kUTTypeFlatRTFD];
     // First, we want to either load the highest fidelity supported type or the highest fidelity generic content type.
     for (NSString *registeredTypeIdentifier in registeredTypeIdentifiers) {
+        if (containsFlatRTFD && [registeredTypeIdentifier isEqualToString:(NSString *)kUTTypeRTFD]) {
+            // In the case where attachments are enabled and we're accepting all types of content using attachment
+            // elements as a fallback representation, if the source writes attributed strings to the pasteboard with
+            // com.apple.rtfd at a higher fidelity than com.apple.flat-rtfd, we'll end up loading only com.apple.rtfd
+            // and dropping the text as an attachment element because we cannot convert the dropped content to markup.
+            // Instead, if flat RTFD is present in the item provider, always prefer that over RTFD so that dropping as
+            // regular web content isn't overridden by enabling attachment elements.
+            continue;
+        }
+
         if (typeConformsToTypes(registeredTypeIdentifier, _supportedTypeIdentifiers.get())) {
             [typesToLoad addObject:registeredTypeIdentifier];
             break;
@@ -626,7 +692,7 @@ static NSURL *linkTemporaryItemProviderFilesToDropStagingDirectory(NSURL *url, N
                 retainedSelf->_loadResults.append(loadResult);
         }
 
-        completionBlock([retainedSelf droppedFileURLs]);
+        completionBlock([retainedSelf allDroppedFileURLs]);
     };
 
     if (synchronousTimeout > 0 && !dispatch_group_wait(synchronousFileLoadingGroup.get(), dispatch_time(DISPATCH_TIME_NOW, synchronousTimeout * NSEC_PER_SEC))) {
index 406d45a..60b7c3a 100644 (file)
@@ -34,6 +34,7 @@
 #import "PasteboardStrategy.h"
 #import "PlatformPasteboard.h"
 #import "PlatformStrategies.h"
+#import "RuntimeEnabledFeatures.h"
 #import "WebCoreNSURLExtras.h"
 
 #if PLATFORM(IOS)
@@ -220,6 +221,9 @@ bool DragData::containsCompatibleContent(DraggingPurpose purpose) const
     if (purpose == DraggingPurpose::ForFileUpload)
         return containsFiles();
 
+    if (purpose == DraggingPurpose::ForEditing && RuntimeEnabledFeatures::sharedFeatures().attachmentElementEnabled() && containsFiles())
+        return true;
+
     Vector<String> types;
     platformStrategies()->pasteboardStrategy()->getTypes(types, m_pasteboardName);
     return types.contains(String(WebArchivePboardType))
index b674c79..2b63b2f 100644 (file)
@@ -350,7 +350,7 @@ void Pasteboard::read(PasteboardWebContentReader& reader)
     if (types.contains(String(legacyFilenamesPasteboardType()))) {
         Vector<String> paths;
         strategy.getPathnamesForType(paths, legacyFilenamesPasteboardType(), m_pasteboardName);
-        if (m_changeCount != changeCount() || reader.readFilenames(paths))
+        if (m_changeCount != changeCount() || reader.readFilePaths(paths))
             return;
     }
 
@@ -554,7 +554,7 @@ void Pasteboard::writeString(const String& type, const String& data)
     }
 }
 
-Vector<String> Pasteboard::readFilenames()
+Vector<String> Pasteboard::readFilePaths()
 {
     // FIXME: Seems silly to convert paths to URLs and then back to paths. Does that do anything helpful?
     Vector<String> absoluteURLs = absoluteURLsFromPasteboardFilenames(m_pasteboardName);
index 54d6ac1..8b5bc47 100644 (file)
@@ -1,3 +1,25 @@
+2018-01-03  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [Attachment Support] Create attachment elements when dropping files on iOS
+        https://bugs.webkit.org/show_bug.cgi?id=181192
+        <rdar://problem/36280945>
+
+        Reviewed by Tim Horton.
+
+        Make some minor adjustments for changes to the pasteboard in WebCore. See WebCore/ChangeLog for more detail.
+        Teaches WebPasteboardProxy et. al. to plumb PasteboardItemInfo from the UI process to the web process via the
+        new `InformationForItemAtIndex` codepath.
+
+        * UIProcess/Cocoa/WebPasteboardProxyCocoa.mm:
+        (WebKit::WebPasteboardProxy::informationForItemAtIndex):
+        (WebKit::WebPasteboardProxy::getFilenamesForDataInteraction): Deleted.
+        * UIProcess/WebPasteboardProxy.h:
+        * UIProcess/WebPasteboardProxy.messages.in:
+        * WebProcess/WebCoreSupport/WebPlatformStrategies.cpp:
+        (WebKit::WebPlatformStrategies::informationForItemAtIndex):
+        (WebKit::WebPlatformStrategies::getFilenamesForDataInteraction): Deleted.
+        * WebProcess/WebCoreSupport/WebPlatformStrategies.h:
+
 2018-01-03  Ting-Wei Lan  <lantw44@gmail.com>
 
         Replace hard-coded paths in shebangs with #!/usr/bin/env
index 63254d9..b20122b 100644 (file)
@@ -29,6 +29,7 @@
 #import "SandboxExtension.h"
 #import "WebProcessProxy.h"
 #import <WebCore/Color.h>
+#import <WebCore/PasteboardItemInfo.h>
 #import <WebCore/PlatformPasteboard.h>
 #import <WebCore/SharedBuffer.h>
 #import <WebCore/URL.h>
@@ -225,9 +226,9 @@ void WebPasteboardProxy::getPasteboardItemsCount(const String& pasteboardName, u
     itemsCount = PlatformPasteboard(pasteboardName).count();
 }
 
-void WebPasteboardProxy::getFilenamesForDataInteraction(const String& pasteboardName, Vector<String>& filenames)
+void WebPasteboardProxy::informationForItemAtIndex(int index, const String& pasteboardName, PasteboardItemInfo& info)
 {
-    filenames = PlatformPasteboard(pasteboardName).filenamesForDataInteraction();
+    info = PlatformPasteboard(pasteboardName).informationForItemAtIndex(index);
 }
 
 void WebPasteboardProxy::updateSupportedTypeIdentifiers(const Vector<String>& identifiers, const String& pasteboardName)
index 6cede05..af2705d 100644 (file)
@@ -35,6 +35,7 @@ namespace WebCore {
 class Color;
 struct PasteboardCustomData;
 struct PasteboardImage;
+struct PasteboardItemInfo;
 struct PasteboardURL;
 struct PasteboardWebContent;
 }
@@ -77,7 +78,7 @@ private:
     void readURLFromPasteboard(uint64_t index, const String& pasteboardType, const String& pasteboardName, String& url, String& title);
     void readBufferFromPasteboard(uint64_t index, const String& pasteboardType, const String& pasteboardName, SharedMemory::Handle&, uint64_t& size);
     void getPasteboardItemsCount(const String& pasteboardName, uint64_t& itemsCount);
-    void getFilenamesForDataInteraction(const String& pasteboardName, Vector<String>& filenames);
+    void informationForItemAtIndex(int index, const String& pasteboardName, WebCore::PasteboardItemInfo& filename);
     void updateSupportedTypeIdentifiers(const Vector<String>& identifiers, const String& pasteboardName);
 #endif
 #if PLATFORM(COCOA)
index be16184..c7b3b26 100644 (file)
@@ -30,7 +30,7 @@ messages -> WebPasteboardProxy {
     ReadURLFromPasteboard(uint64_t index, String pasteboardType, String pasteboardName) -> (String url, String title)
     ReadBufferFromPasteboard(uint64_t index, String pasteboardType, String pasteboardName) -> (WebKit::SharedMemory::Handle handle, uint64_t size)
     GetPasteboardItemsCount(String pasteboardName) -> (uint64_t itemsCount)
-    GetFilenamesForDataInteraction(String pasteboardName) -> (Vector<String> filenames)
+    InformationForItemAtIndex(uint64_t index, String pasteboardName) -> (struct WebCore::PasteboardItemInfo info)
     UpdateSupportedTypeIdentifiers(Vector<String> identifiers, String pasteboardName)
     GetPasteboardTypesByFidelityForItemAtIndex(uint64_t index, String pasteboardName) -> (Vector<String> types)
 #endif
index db31788..854487c 100644 (file)
@@ -55,6 +55,7 @@
 #include <WebCore/NetworkingContext.h>
 #include <WebCore/Page.h>
 #include <WebCore/PageGroup.h>
+#include <WebCore/PasteboardItemInfo.h>
 #include <WebCore/PlatformCookieJar.h>
 #include <WebCore/PlatformPasteboard.h>
 #include <WebCore/ProgressTracker.h>
@@ -322,9 +323,11 @@ int WebPlatformStrategies::getPasteboardItemsCount(const String& pasteboardName)
     return itemsCount;
 }
 
-void WebPlatformStrategies::getFilenamesForDataInteraction(Vector<String>& filenames, const String& pasteboardName)
+PasteboardItemInfo WebPlatformStrategies::informationForItemAtIndex(int index, const String& pasteboardName)
 {
-    WebProcess::singleton().parentProcessConnection()->sendSync(Messages::WebPasteboardProxy::GetFilenamesForDataInteraction(pasteboardName), Messages::WebPasteboardProxy::GetFilenamesForDataInteraction::Reply(filenames), 0);
+    PasteboardItemInfo info;
+    WebProcess::singleton().parentProcessConnection()->sendSync(Messages::WebPasteboardProxy::InformationForItemAtIndex(index, pasteboardName), Messages::WebPasteboardProxy::InformationForItemAtIndex::Reply(info), 0);
+    return info;
 }
 
 void WebPlatformStrategies::updateSupportedTypeIdentifiers(const Vector<String>& identifiers, const String& pasteboardName)
index ad2e8f1..cd30fc1 100644 (file)
@@ -65,7 +65,7 @@ private:
     String readStringFromPasteboard(int index, const String& pasteboardType, const String& pasteboardName) override;
     RefPtr<WebCore::SharedBuffer> readBufferFromPasteboard(int index, const String& pasteboardType, const String& pasteboardName) override;
     WebCore::URL readURLFromPasteboard(int index, const String& pasteboardType, const String& pasteboardName, String& title) override;
-    void getFilenamesForDataInteraction(Vector<String>& filenames, const String& pasteboardName) override;
+    WebCore::PasteboardItemInfo informationForItemAtIndex(int index, const String& pasteboardName) override;
     void updateSupportedTypeIdentifiers(const Vector<String>& identifiers, const String& pasteboardName) override;
     void getTypesByFidelityForItemAtIndex(Vector<String>& types, uint64_t index, const String& pasteboardName) override;
 #endif
index 33341b8..3c650ff 100644 (file)
@@ -1,5 +1,20 @@
 2018-01-03  Wenson Hsieh  <wenson_hsieh@apple.com>
 
+        [Attachment Support] Create attachment elements when dropping files on iOS
+        https://bugs.webkit.org/show_bug.cgi?id=181192
+        <rdar://problem/36280945>
+
+        Reviewed by Tim Horton.
+
+        Make some minor adjustments for changes to the pasteboard in WebCore. See WebCore/ChangeLog for more detail.
+
+        * WebCoreSupport/WebPlatformStrategies.h:
+        * WebCoreSupport/WebPlatformStrategies.mm:
+        (WebPlatformStrategies::informationForItemAtIndex):
+        (WebPlatformStrategies::getFilenamesForDataInteraction): Deleted.
+
+2018-01-03  Wenson Hsieh  <wenson_hsieh@apple.com>
+
         [Attachment Support] Add plumbing for starting a drag with promised blob data
         https://bugs.webkit.org/show_bug.cgi?id=181201
 
index e759256..842ccdc 100644 (file)
@@ -66,7 +66,7 @@ private:
     String readStringFromPasteboard(int index, const String& pasteboardType, const String& pasteboardName) override;
     RefPtr<WebCore::SharedBuffer> readBufferFromPasteboard(int index, const String& pasteboardType, const String& pasteboardName) override;
     WebCore::URL readURLFromPasteboard(int index, const String& pasteboardType, const String& pasteboardName, String& title) override;
-    void getFilenamesForDataInteraction(Vector<String>& filenames, const String& pasteboardName) override;
+    WebCore::PasteboardItemInfo informationForItemAtIndex(int index, const String& pasteboardName) override;
     void updateSupportedTypeIdentifiers(const Vector<String>& identifiers, const String& pasteboardName) override;
     void getTypesByFidelityForItemAtIndex(Vector<String>& types, uint64_t index, const String& pasteboardName) override;
 #endif
index 2340967..ed7f7f5 100644 (file)
@@ -32,6 +32,7 @@
 #import <WebCore/Color.h>
 #import <WebCore/MainFrame.h>
 #import <WebCore/NetworkStorageSession.h>
+#import <WebCore/PasteboardItemInfo.h>
 #import <WebCore/PlatformCookieJar.h>
 #import <WebCore/PlatformPasteboard.h>
 #import <WebCore/SharedBuffer.h>
@@ -239,8 +240,8 @@ String WebPlatformStrategies::readStringFromPasteboard(int index, const String&
     return PlatformPasteboard(pasteboardName).readString(index, type);
 }
 
-void WebPlatformStrategies::getFilenamesForDataInteraction(Vector<String>& filenames, const String& pasteboardName)
+WebCore::PasteboardItemInfo WebPlatformStrategies::informationForItemAtIndex(int index, const String& pasteboardName)
 {
-    filenames = PlatformPasteboard(pasteboardName).filenamesForDataInteraction();
+    return PlatformPasteboard(pasteboardName).informationForItemAtIndex(index);
 }
 #endif // PLATFORM(IOS)
index 19c9a18..6d4ad21 100644 (file)
@@ -1,3 +1,45 @@
+2018-01-03  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [Attachment Support] Create attachment elements when dropping files on iOS
+        https://bugs.webkit.org/show_bug.cgi?id=181192
+        <rdar://problem/36280945>
+
+        Reviewed by Tim Horton.
+
+        Adds 3 new API tests to exercise different use cases of dropping content as attachment elements when the runtime
+        switch is enabled. See below for more details.
+
+        * TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm:
+        (-[NSItemProvider registerData:type:]):
+        (platformCopyPNG):
+        (TestWebKitAPI::TEST):
+        * TestWebKitAPI/Tests/ios/DataInteractionTests.mm:
+
+        Fix some currently failing iOS drag and drop tests. In this case, there's no reason RTFD should appear in the
+        source item provider when dragging rich text *without* attachments, so this should have been a check for just
+        kUTTypeRTF instead.
+
+        (TestWebKitAPI::TEST):
+
+        Tests a few cases of inserting attachment elements via drop:
+        1.  We should distinguish between drops containing rich/plain text files from just dropping rich/plain text.
+            Instead of inserting the contents as inline web content, this should generate attachment elements.
+        2.  Test the fallback mechanism for inserting attachment elements. If the preferred presentation style is not
+            explicitly set, but there's nothing WebKit would otherwise do with the dropped content, then we should fall
+            back to inserting the content as an attachment.
+        3.  Test that if multiple attachments and inline item providers are present, WebKit will respect the order in
+            which they were inserted by the source (as opposed to, for instance, putting all of the attachments in front
+            or at the end).
+
+        * TestWebKitAPI/cocoa/TestWKWebView.h:
+        * TestWebKitAPI/cocoa/TestWKWebView.mm:
+        (-[TestWKWebView objectByEvaluatingJavaScript:]):
+
+        Add a helper method to return an object that represents the result of evaluating some given script, and rewrite
+        -stringByEvaluatingJavaScript to just turn around and call this.
+
+        (-[TestWKWebView stringByEvaluatingJavaScript:]):
+
 2018-01-03  Ting-Wei Lan  <lantw44@gmail.com>
 
         Replace hard-coded paths in shebangs with #!/usr/bin/env
index 04ea16c..743547d 100644 (file)
@@ -321,6 +321,24 @@ static _WKAttachmentDisplayOptions *displayOptionsWithMode(_WKAttachmentDisplayM
 
 #pragma mark - Platform testing helper functions
 
+#if PLATFORM(IOS)
+
+typedef void(^ItemProviderDataLoadHandler)(NSData *, NSError *);
+
+@implementation NSItemProvider (AttachmentTesting)
+
+- (void)registerData:(NSData *)data type:(NSString *)type
+{
+    [self registerDataRepresentationForTypeIdentifier:type visibility:NSItemProviderRepresentationVisibilityAll loadHandler:[protectedData = retainPtr(data)] (ItemProviderDataLoadHandler completionHandler) -> NSProgress * {
+        completionHandler(protectedData.get(), nil);
+        return nil;
+    }];
+}
+
+@end
+
+#endif // PLATFORM(IOS)
+
 void platformCopyRichTextWithMultipleAttachments()
 {
     auto image = adoptNS([[NSTextAttachment alloc] initWithData:testImageData() ofType:(NSString *)kUTTypePNG]);
@@ -363,8 +381,6 @@ void platformCopyRichTextWithImage()
 #endif
 }
 
-typedef void(^ItemProviderDataLoadHandler)(NSData *, NSError *);
-
 void platformCopyPNG()
 {
 #if PLATFORM(MAC)
@@ -375,10 +391,7 @@ void platformCopyPNG()
     UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
     auto item = adoptNS([[UIItemProvider alloc] init]);
     [item setPreferredPresentationStyle:UIPreferredPresentationStyleAttachment];
-    [item registerDataRepresentationForTypeIdentifier:(NSString *)kUTTypePNG visibility:NSItemProviderRepresentationVisibilityAll loadHandler:[] (ItemProviderDataLoadHandler completionHandler) -> NSProgress * {
-        completionHandler(testImageData(), nil);
-        return nil;
-    }];
+    [item registerData:testImageData() type:(NSString *)kUTTypePNG];
     pasteboard.itemProviders = @[ item.get() ];
 #endif
 }
@@ -960,11 +973,7 @@ TEST(WKAttachmentTestsIOS, InsertDroppedImageAsAttachment)
     auto webView = webViewForTestingAttachments();
     auto draggingSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
     auto item = adoptNS([[NSItemProvider alloc] init]);
-    [item setPreferredPresentationStyle:UIPreferredPresentationStyleAttachment];
-    [item registerDataRepresentationForTypeIdentifier:(NSString *)kUTTypePNG visibility:NSItemProviderRepresentationVisibilityAll loadHandler:[] (ItemProviderDataLoadHandler completionHandler) -> NSProgress * {
-        completionHandler(testImageData(), nil);
-        return nil;
-    }];
+    [item registerData:testImageData() type:(NSString *)kUTTypePNG];
     [draggingSimulator setExternalItemProviders:@[ item.get() ]];
     [draggingSimulator runFrom:CGPointZero to:CGPointMake(50, 50)];
 
@@ -1010,6 +1019,115 @@ TEST(WKAttachmentTestsIOS, InsertDroppedAttributedStringContainingAttachment)
     }
 }
 
+TEST(WKAttachmentTestsIOS, InsertDroppedRichAndPlainTextFilesAsAttachments)
+{
+    // Here, both rich text and plain text are content types that WebKit already understands how to insert in editable
+    // areas in the absence of attachment elements. However, due to the explicitly set attachment presentation style
+    // on the item providers, we should instead treat them as dropped files and insert attachment elements.
+    // This exercises the scenario of dragging rich and plain text files from Files to Mail.
+    auto richTextItem = adoptNS([[NSItemProvider alloc] init]);
+    auto richText = adoptNS([[NSAttributedString alloc] initWithString:@"Hello world" attributes:@{ NSFontAttributeName: [UIFont boldSystemFontOfSize:12] }]);
+    [richTextItem setPreferredPresentationStyle:UIPreferredPresentationStyleAttachment];
+    [richTextItem registerObject:richText.get() visibility:NSItemProviderRepresentationVisibilityAll];
+    [richTextItem setSuggestedName:@"hello.rtf"];
+
+    auto plainTextItem = adoptNS([[NSItemProvider alloc] init]);
+    [plainTextItem setPreferredPresentationStyle:UIPreferredPresentationStyleAttachment];
+    [plainTextItem registerObject:@"Hello world" visibility:NSItemProviderRepresentationVisibilityAll];
+    [plainTextItem setSuggestedName:@"world.txt"];
+
+    auto webView = webViewForTestingAttachments();
+    auto draggingSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
+    [draggingSimulator setExternalItemProviders:@[ richTextItem.get(), plainTextItem.get() ]];
+    [draggingSimulator runFrom:CGPointZero to:CGPointMake(50, 50)];
+
+    EXPECT_EQ(2U, [draggingSimulator insertedAttachments].count);
+    EXPECT_EQ(0U, [draggingSimulator removedAttachments].count);
+
+    for (_WKAttachment *attachment in [draggingSimulator insertedAttachments]) {
+        NSError *error = nil;
+        EXPECT_GT([attachment synchronouslyRequestData:&error].length, 0U);
+        EXPECT_TRUE(!error);
+        if (error)
+            NSLog(@"Error: %@", error);
+    }
+
+    EXPECT_EQ(2, [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment').length"].intValue);
+    EXPECT_WK_STREQ("hello.rtf", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[0].getAttribute('title')"]);
+    EXPECT_WK_STREQ("text/rtf", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[0].getAttribute('type')"]);
+    EXPECT_WK_STREQ("world.txt", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[1].getAttribute('title')"]);
+    EXPECT_WK_STREQ("text/plain", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[1].getAttribute('type')"]);
+}
+
+TEST(WKAttachmentTestsIOS, InsertDroppedZipArchiveAsAttachment)
+{
+    // Since WebKit doesn't have any default DOM representation for ZIP archives, we should fall back to inserting
+    // attachment elements. This exercises the flow of dragging a ZIP file from an app that doesn't specify a preferred
+    // presentation style (e.g. Notes) into Mail.
+    auto item = adoptNS([[NSItemProvider alloc] init]);
+    NSData *data = testZIPData();
+    [item registerData:data type:(NSString *)kUTTypeZipArchive];
+    [item setSuggestedName:@"archive.zip"];
+
+    auto webView = webViewForTestingAttachments();
+    auto draggingSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
+    [draggingSimulator setExternalItemProviders:@[ item.get() ]];
+    [draggingSimulator runFrom:CGPointZero to:CGPointMake(50, 50)];
+
+    EXPECT_EQ(1U, [draggingSimulator insertedAttachments].count);
+    EXPECT_EQ(0U, [draggingSimulator removedAttachments].count);
+    [[draggingSimulator insertedAttachments].firstObject expectRequestedDataToBe:data];
+    EXPECT_EQ(1, [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment').length"].intValue);
+    EXPECT_WK_STREQ("archive.zip", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
+    EXPECT_WK_STREQ("application/zip", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
+}
+
+TEST(WKAttachmentTestsIOS, InsertDroppedItemProvidersInOrder)
+{
+    // Tests that item providers are inserted in the order they are specified. In this case, the two inserted attachments
+    // should be separated by a link.
+    auto firstAttachmentItem = adoptNS([[NSItemProvider alloc] init]);
+    [firstAttachmentItem setPreferredPresentationStyle:UIPreferredPresentationStyleAttachment];
+    [firstAttachmentItem registerObject:@"FIRST" visibility:NSItemProviderRepresentationVisibilityAll];
+    [firstAttachmentItem setSuggestedName:@"first.txt"];
+
+    auto inlineTextItem = adoptNS([[NSItemProvider alloc] init]);
+    auto appleURL = retainPtr([NSURL URLWithString:@"https://www.apple.com/"]);
+    [inlineTextItem registerObject:appleURL.get() visibility:NSItemProviderRepresentationVisibilityAll];
+
+    auto secondAttachmentItem = adoptNS([[NSItemProvider alloc] init]);
+    [secondAttachmentItem registerData:testPDFData() type:(NSString *)kUTTypePDF];
+    [secondAttachmentItem setSuggestedName:@"second.pdf"];
+
+    auto webView = webViewForTestingAttachments();
+    auto draggingSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
+    [draggingSimulator setExternalItemProviders:@[ firstAttachmentItem.get(), inlineTextItem.get(), secondAttachmentItem.get() ]];
+    [draggingSimulator runFrom:CGPointZero to:CGPointMake(50, 50)];
+
+    EXPECT_EQ(2U, [draggingSimulator insertedAttachments].count);
+    EXPECT_EQ(0U, [draggingSimulator removedAttachments].count);
+
+    for (_WKAttachment *attachment in [draggingSimulator insertedAttachments]) {
+        NSError *error = nil;
+        EXPECT_GT([attachment synchronouslyRequestData:&error].length, 0U);
+        EXPECT_TRUE(!error);
+        if (error)
+            NSLog(@"Error: %@", error);
+    }
+
+    NSArray *observedElementTags = (NSArray *)[webView objectByEvaluatingJavaScript:@"Array.from(document.body.children).map(e => e.tagName)"];
+    NSArray *expectedElementTags = @[ @"ATTACHMENT", @"A", @"ATTACHMENT" ];
+    EXPECT_TRUE([observedElementTags isEqualToArray:expectedElementTags]);
+    if (![observedElementTags isEqualToArray:expectedElementTags])
+        NSLog(@"Observed elements: %@ did not match expectations: %@", observedElementTags, expectedElementTags);
+
+    EXPECT_WK_STREQ("first.txt", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[0].getAttribute('title')"]);
+    EXPECT_WK_STREQ("text/plain", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[0].getAttribute('type')"]);
+    EXPECT_WK_STREQ([appleURL absoluteString], [webView valueOfAttribute:@"href" forQuerySelector:@"a"]);
+    EXPECT_WK_STREQ("second.pdf", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[1].getAttribute('title')"]);
+    EXPECT_WK_STREQ("application/pdf", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[1].getAttribute('type')"]);
+}
+
 #endif // PLATFORM(IOS)
 
 } // namespace TestWebKitAPI
index 85a11a4..a5908d6 100644 (file)
@@ -344,7 +344,7 @@ TEST(DataInteractionTests, ContentEditableToContentEditable)
     EXPECT_TRUE([observedEventNames containsObject:DataInteractionOverEventName]);
     EXPECT_TRUE([observedEventNames containsObject:DataInteractionPerformOperationEventName]);
     checkSelectionRectsWithLogging(@[ makeCGRectValue(1, 201, 961, 227) ], [dataInteractionSimulator finalSelectionRects]);
-    checkTypeIdentifierPrecedesOtherTypeIdentifier(dataInteractionSimulator.get(), (NSString *)kUTTypeRTFD, (NSString *)kUTTypeUTF8PlainText);
+    checkTypeIdentifierPrecedesOtherTypeIdentifier(dataInteractionSimulator.get(), (NSString *)kUTTypeRTF, (NSString *)kUTTypeUTF8PlainText);
 }
 
 TEST(DataInteractionTests, ContentEditableToTextarea)
@@ -364,7 +364,7 @@ TEST(DataInteractionTests, ContentEditableToTextarea)
     EXPECT_TRUE([observedEventNames containsObject:DataInteractionOverEventName]);
     EXPECT_TRUE([observedEventNames containsObject:DataInteractionPerformOperationEventName]);
     checkSelectionRectsWithLogging(@[ makeCGRectValue(6, 203, 990, 232) ], [dataInteractionSimulator finalSelectionRects]);
-    checkTypeIdentifierPrecedesOtherTypeIdentifier(dataInteractionSimulator.get(), (NSString *)kUTTypeRTFD, (NSString *)kUTTypeUTF8PlainText);
+    checkTypeIdentifierPrecedesOtherTypeIdentifier(dataInteractionSimulator.get(), (NSString *)kUTTypeRTF, (NSString *)kUTTypeUTF8PlainText);
 }
 
 TEST(DataInteractionTests, ContentEditableMoveParagraphs)
index e552d96..daa3b26 100644 (file)
@@ -45,6 +45,7 @@
 - (void)loadTestPageNamed:(NSString *)pageName;
 - (void)synchronouslyLoadHTMLString:(NSString *)html;
 - (void)synchronouslyLoadTestPageNamed:(NSString *)pageName;
+- (id)objectByEvaluatingJavaScript:(NSString *)script;
 - (NSString *)stringByEvaluatingJavaScript:(NSString *)script;
 - (void)waitForMessage:(NSString *)message;
 - (void)performAfterLoading:(dispatch_block_t)actions;
index ef417fb..25897d6 100644 (file)
@@ -238,21 +238,24 @@ NSEventMask __simulated_forceClickAssociatedEventsMask(id self, SEL _cmd)
     [self _test_waitForDidFinishNavigation];
 }
 
-- (NSString *)stringByEvaluatingJavaScript:(NSString *)script
+- (id)objectByEvaluatingJavaScript:(NSString *)script
 {
-    __block bool isWaitingForJavaScript = false;
-    __block NSString *evalResult = nil;
-    [self _evaluateJavaScriptWithoutUserGesture:script completionHandler:^(id result, NSError *error)
-    {
-        evalResult = [[NSString alloc] initWithFormat:@"%@", result];
+    bool isWaitingForJavaScript = false;
+    RetainPtr<id> evalResult;
+    [self _evaluateJavaScriptWithoutUserGesture:script completionHandler:[&] (id result, NSError *error) {
+        evalResult = result;
         isWaitingForJavaScript = true;
         EXPECT_TRUE(!error);
         if (error)
             NSLog(@"Encountered error: %@ while evaluating script: %@", error, script);
     }];
-
     TestWebKitAPI::Util::run(&isWaitingForJavaScript);
-    return [evalResult autorelease];
+    return evalResult.autorelease();
+}
+
+- (NSString *)stringByEvaluatingJavaScript:(NSString *)script
+{
+    return [NSString stringWithFormat:@"%@", [self objectByEvaluatingJavaScript:script]];
 }
 
 - (void)waitForMessage:(NSString *)message