Title: [261638] trunk
Revision
261638
Author
wenson_hs...@apple.com
Date
2020-05-13 12:43:10 -0700 (Wed, 13 May 2020)

Log Message

[iOS] "Copy" context menu action for attachment element does not work in Mail
https://bugs.webkit.org/show_bug.cgi?id=211817
<rdar://problem/58043110>

Reviewed by Tim Horton.

Source/WebCore:

Minor refactoring to help support writing attachment data to the pasteboard when using context menu actions to
copy an attachment element on iOS. See below for more details, as well as the WebKit ChangeLog entry.

Test: WKAttachmentTestsIOS.CopyAttachmentUsingElementAction

* editing/Editor.cpp:
(WebCore::Editor::platformContentTypeForBlobType const):
(WebCore::Editor::promisedAttachmentInfo):

Move promisedAttachmentInfo out of DragController and into Editor, so that it is accessible outside of drag
and drop code.

* editing/Editor.h:
* editing/cocoa/EditorCocoa.mm:
(WebCore::Editor::platformContentTypeForBlobType const):

Move this private helper function out of DragController as well, and into Editor.

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

Refactor this to use Editor::promisedAttachmentInfo().

(WebCore::DragController::platformContentTypeForBlobType const): Deleted.
(WebCore::DragController::promisedAttachmentInfo): Deleted.
* page/DragController.h:
* page/mac/DragControllerMac.mm:
(WebCore::DragController::platformContentTypeForBlobType const): Deleted.

Source/WebKit:

Implements support for copying an attachment element, via context menu actions. To achieve this, we first
refactor code in WebCore that is currently responsible for converting an attachment element into a
PromisedAttachmentInfo, which provides a handle to an UI-process-side API attachment object. We then use this
helper in WebKit to send PromisedAttachmentInfo back to the UI process when handling the copy action from the
context menu on iOS.

On iOS, we then take this promised attachment info in the UI process, map it to an API::Attachment object, and
use the file handle to create and write an NSItemProvider to the general pasteboard. (In the future, similar
logic could be implemented on macOS as well to handle copying attachment elements by implementing a version of
writePromisedAttachmentToPasteboard in PageClientImplMac. Currently, we fall back to treating this case as if
we're copying a text selection containing a single attachment element).

* UIProcess/API/ios/WKWebViewPrivateForTestingIOS.h:

Add a new helper function to simulate an element action (_WKElementActionType) for an element at the given
location. This is used by the new API test.

* UIProcess/API/ios/WKWebViewTestingIOS.mm:
(-[WKWebView _simulateElementAction:atLocation:]):
* UIProcess/PageClient.h:
(WebKit::PageClient::writePromisedAttachmentToPasteboard):
* UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::writePromisedAttachmentToPasteboard):
* UIProcess/WebPageProxy.h:
* UIProcess/WebPageProxy.messages.in:

Add an IPC message for the UI process to receive a PromisedAttachmentInfo, and copy it to the system pasteboard
if it maps to an actual API::Attachment. For now, we only support copying data if the attachment is backed by
a file wrapper in the UI process (as opposed to blob data written by the web process).

* UIProcess/ios/PageClientImplIOS.h:
* UIProcess/ios/PageClientImplIOS.mm:
(WebKit::PageClientImpl::writePromisedAttachmentToPasteboard):
* UIProcess/ios/WKContentViewInteraction.h:
* UIProcess/ios/WKContentViewInteraction.mm:
(createItemProvider):

Create a helper function to convert PromisedAttachmentInfo into an NSItemProvider. This is similar to
`-_prepareToDragPromisedAttachment:`, but with a couple differences: (1) we only create an NSItemProvider
instead of going through `WebItemProviderRegistrationInfoList`; (2) since there's no opportunity to clean up
temporary after pasting, serialize the attachment data using `-serializedRepresentation`, and provide the
attachment data instead of a file URL.

Ideally, we would offer the data directly to the pasteboard so that even if Mail terminates, the contents of the
pasteboard can still be provided when pasting; however, it doesn't seem like we can do this while being able to
offer a suggested name for the item we're writing. Some other apps work around this limitation by writing the
file name as plain text on the pasteboard, but this doesn't work then attempting to copy a plain text file that
has a file name.

(-[WKContentView _writePromisedAttachmentToPasteboard:]):
(-[WKContentView _simulateElementAction:atLocation:]):
* WebProcess/WebPage/ios/WebPageIOS.mm:
(WebKit::WebPage::performActionOnElement):

Tools:

Add a new iOS API test that simulates the context menu action to "copy" on an attachment element.

* TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm:

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (261637 => 261638)


--- trunk/Source/WebCore/ChangeLog	2020-05-13 19:26:58 UTC (rev 261637)
+++ trunk/Source/WebCore/ChangeLog	2020-05-13 19:43:10 UTC (rev 261638)
@@ -1,3 +1,41 @@
+2020-05-13  Wenson Hsieh  <wenson_hs...@apple.com>
+
+        [iOS] "Copy" context menu action for attachment element does not work in Mail
+        https://bugs.webkit.org/show_bug.cgi?id=211817
+        <rdar://problem/58043110>
+
+        Reviewed by Tim Horton.
+
+        Minor refactoring to help support writing attachment data to the pasteboard when using context menu actions to
+        copy an attachment element on iOS. See below for more details, as well as the WebKit ChangeLog entry.
+
+        Test: WKAttachmentTestsIOS.CopyAttachmentUsingElementAction
+
+        * editing/Editor.cpp:
+        (WebCore::Editor::platformContentTypeForBlobType const):
+        (WebCore::Editor::promisedAttachmentInfo):
+
+        Move promisedAttachmentInfo out of DragController and into Editor, so that it is accessible outside of drag
+        and drop code.
+
+        * editing/Editor.h:
+        * editing/cocoa/EditorCocoa.mm:
+        (WebCore::Editor::platformContentTypeForBlobType const):
+
+        Move this private helper function out of DragController as well, and into Editor.
+
+        * page/DragClient.h:
+        * page/DragController.cpp:
+        (WebCore::DragController::startDrag):
+
+        Refactor this to use Editor::promisedAttachmentInfo().
+
+        (WebCore::DragController::platformContentTypeForBlobType const): Deleted.
+        (WebCore::DragController::promisedAttachmentInfo): Deleted.
+        * page/DragController.h:
+        * page/mac/DragControllerMac.mm:
+        (WebCore::DragController::platformContentTypeForBlobType const): Deleted.
+
 2020-05-13  Antoine Quint  <grao...@apple.com>
 
         [Web Animations] Calling reverse() on an accelerated animation has no effect

Modified: trunk/Source/WebCore/editing/Editor.cpp (261637 => 261638)


--- trunk/Source/WebCore/editing/Editor.cpp	2020-05-13 19:26:58 UTC (rev 261637)
+++ trunk/Source/WebCore/editing/Editor.cpp	2020-05-13 19:43:10 UTC (rev 261638)
@@ -124,6 +124,10 @@
 #include "ServicesOverlayController.h"
 #endif
 
+#if ENABLE(ATTACHMENT_ELEMENT)
+#include "PromisedAttachmentInfo.h"
+#endif
+
 namespace WebCore {
 
 static bool dispatchBeforeInputEvent(Element& element, const AtomString& inputType, const String& data = { }, RefPtr<DataTransfer>&& dataTransfer = nullptr, const Vector<RefPtr<StaticRange>>& targetRanges = { }, Event::IsCancelable cancelable = Event::IsCancelable::Yes)
@@ -3950,6 +3954,11 @@
 {
 }
 
+String Editor::platformContentTypeForBlobType(const String& type) const
+{
+    return type;
+}
+
 #endif
 
 static Vector<TextList> editableTextListsAtPositionInDescendingOrder(const Position& position)
@@ -4083,6 +4092,33 @@
 
 #if ENABLE(ATTACHMENT_ELEMENT)
 
+PromisedAttachmentInfo Editor::promisedAttachmentInfo(Element& element)
+{
+    auto* client = this->client();
+    if (!client || !client->supportsClientSideAttachmentData())
+        return { };
+
+    RefPtr<HTMLAttachmentElement> attachment;
+    if (is<HTMLAttachmentElement>(element))
+        attachment = &downcast<HTMLAttachmentElement>(element);
+    else if (is<HTMLImageElement>(element))
+        attachment = downcast<HTMLImageElement>(element).attachmentElement();
+
+    if (!attachment)
+        return { };
+
+    Vector<String> additionalTypes;
+    Vector<RefPtr<SharedBuffer>> additionalData;
+#if PLATFORM(COCOA)
+    getPasteboardTypesAndDataForAttachment(element, additionalTypes, additionalData);
+#endif
+
+    if (auto* file = attachment->file())
+        return { file->url(), platformContentTypeForBlobType(file->type()), file->name(), { }, WTFMove(additionalTypes), WTFMove(additionalData) };
+
+    return { { }, { }, { }, attachment->uniqueIdentifier(), WTFMove(additionalTypes), WTFMove(additionalData) };
+}
+
 void Editor::registerAttachmentIdentifier(const String& identifier, const String& contentType, const String& preferredFileName, Ref<SharedBuffer>&& data)
 {
     if (auto* client = this->client())

Modified: trunk/Source/WebCore/editing/Editor.h (261637 => 261638)


--- trunk/Source/WebCore/editing/Editor.h	2020-05-13 19:26:58 UTC (rev 261637)
+++ trunk/Source/WebCore/editing/Editor.h	2020-05-13 19:43:10 UTC (rev 261638)
@@ -92,6 +92,7 @@
 struct TextCheckingResult;
 
 #if ENABLE(ATTACHMENT_ELEMENT)
+struct PromisedAttachmentInfo;
 struct SerializedAttachmentData;
 #endif
 
@@ -559,6 +560,7 @@
     void didInsertAttachmentElement(HTMLAttachmentElement&);
     void didRemoveAttachmentElement(HTMLAttachmentElement&);
 
+    WEBCORE_EXPORT PromisedAttachmentInfo promisedAttachmentInfo(Element&);
 #if PLATFORM(COCOA)
     void getPasteboardTypesAndDataForAttachment(Element&, Vector<String>& outTypes, Vector<RefPtr<SharedBuffer>>& outData);
 #endif
@@ -619,6 +621,8 @@
     void notifyClientOfAttachmentUpdates();
 #endif
 
+    String platformContentTypeForBlobType(const String& type) const;
+
     void postTextStateChangeNotificationForCut(const String&, const VisibleSelection&);
 
     Document& m_document;

Modified: trunk/Source/WebCore/editing/cocoa/EditorCocoa.mm (261637 => 261638)


--- trunk/Source/WebCore/editing/cocoa/EditorCocoa.mm	2020-05-13 19:26:58 UTC (rev 261637)
+++ trunk/Source/WebCore/editing/cocoa/EditorCocoa.mm	2020-05-13 19:43:10 UTC (rev 261638)
@@ -55,6 +55,7 @@
 #import "RenderStyle.h"
 #import "Settings.h"
 #import "Text.h"
+#import "UTIUtilities.h"
 #import "WebContentReader.h"
 #import "markup.h"
 #import <pal/spi/cocoa/NSAttributedStringSPI.h>
@@ -257,4 +258,12 @@
 #endif
 }
 
+String Editor::platformContentTypeForBlobType(const String& type) const
+{
+    auto utiType = UTIFromMIMEType(type);
+    if (!utiType.isEmpty())
+        return utiType;
+    return type;
 }
+
+}

Modified: trunk/Source/WebCore/page/DragClient.h (261637 => 261638)


--- trunk/Source/WebCore/page/DragClient.h	2020-05-13 19:26:58 UTC (rev 261637)
+++ trunk/Source/WebCore/page/DragClient.h	2020-05-13 19:43:10 UTC (rev 261638)
@@ -37,7 +37,10 @@
 class Element;
 class Frame;
 class Image;
+
+#if ENABLE(ATTACHMENT_ELEMENT)
 struct PromisedAttachmentInfo;
+#endif
 
 class DragClient {
     WTF_MAKE_FAST_ALLOCATED;

Modified: trunk/Source/WebCore/page/DragController.cpp (261637 => 261638)


--- trunk/Source/WebCore/page/DragController.cpp	2020-05-13 19:26:58 UTC (rev 261637)
+++ trunk/Source/WebCore/page/DragController.cpp	2020-05-13 19:43:10 UTC (rev 261638)
@@ -1036,7 +1036,7 @@
         ASSERT(!image->filenameExtension().isEmpty());
 
 #if ENABLE(ATTACHMENT_ELEMENT)
-        auto attachmentInfo = promisedAttachmentInfo(src, element);
+        auto attachmentInfo = src.editor().promisedAttachmentInfo(element);
 #else
         PromisedAttachmentInfo attachmentInfo;
 #endif
@@ -1132,9 +1132,9 @@
 
         PromisedAttachmentInfo promisedAttachment;
         if (hasData == HasNonDefaultPasteboardData::No) {
-            promisedAttachment = promisedAttachmentInfo(src, attachment);
+            auto& editor = src.editor();
+            promisedAttachment = editor.promisedAttachmentInfo(attachment);
 #if PLATFORM(COCOA)
-            auto& editor = src.editor();
             if (!promisedAttachment && editor.client()) {
                 // Otherwise, if no file URL is specified, call out to the injected bundle to populate the pasteboard with data.
                 editor.willWriteSelectionToPasteboard(createLiveRange(src.selection().selection().toNormalizedRange()).get());
@@ -1474,46 +1474,6 @@
 #endif
 }
 
-#if !PLATFORM(COCOA)
-
-String DragController::platformContentTypeForBlobType(const String& type) const
-{
-    return type;
-}
-
-#endif
-
-#if ENABLE(ATTACHMENT_ELEMENT)
-
-PromisedAttachmentInfo DragController::promisedAttachmentInfo(Frame& frame, Element& element)
-{
-    auto* client = frame.editor().client();
-    if (!client || !client->supportsClientSideAttachmentData())
-        return { };
-
-    RefPtr<HTMLAttachmentElement> attachment;
-    if (is<HTMLAttachmentElement>(element))
-        attachment = &downcast<HTMLAttachmentElement>(element);
-    else if (is<HTMLImageElement>(element))
-        attachment = downcast<HTMLImageElement>(element).attachmentElement();
-
-    if (!attachment)
-        return { };
-
-    Vector<String> additionalTypes;
-    Vector<RefPtr<SharedBuffer>> additionalData;
-#if PLATFORM(COCOA)
-    frame.editor().getPasteboardTypesAndDataForAttachment(element, additionalTypes, additionalData);
-#endif
-
-    if (auto* file = attachment->file())
-        return { file->url(), platformContentTypeForBlobType(file->type()), file->name(), { }, WTFMove(additionalTypes), WTFMove(additionalData) };
-
-    return { { }, { }, { }, attachment->uniqueIdentifier(), WTFMove(additionalTypes), WTFMove(additionalData) };
-}
-
-#endif // ENABLE(ATTACHMENT_ELEMENT)
-
 #endif // ENABLE(DRAG_SUPPORT)
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/page/DragController.h (261637 => 261638)


--- trunk/Source/WebCore/page/DragController.h	2020-05-13 19:26:58 UTC (rev 261637)
+++ trunk/Source/WebCore/page/DragController.h	2020-05-13 19:43:10 UTC (rev 261638)
@@ -137,14 +137,9 @@
         bool tryToUpdateDroppedImagePlaceholders(const DragData&);
         void removeAllDroppedImagePlaceholders();
 
-        String platformContentTypeForBlobType(const String& type) const;
-
         void cleanupAfterSystemDrag();
         void declareAndWriteDragImage(DataTransfer&, Element&, const URL&, const String& label);
 
-#if ENABLE(ATTACHMENT_ELEMENT)
-        PromisedAttachmentInfo promisedAttachmentInfo(Frame&, Element&);
-#endif
         Page& m_page;
         std::unique_ptr<DragClient> m_client;
 

Modified: trunk/Source/WebCore/page/mac/DragControllerMac.mm (261637 => 261638)


--- trunk/Source/WebCore/page/mac/DragControllerMac.mm	2020-05-13 19:26:58 UTC (rev 261637)
+++ trunk/Source/WebCore/page/mac/DragControllerMac.mm	2020-05-13 19:43:10 UTC (rev 261638)
@@ -46,7 +46,6 @@
 #import "PlatformStrategies.h"
 #import "Range.h"
 #import "RuntimeEnabledFeatures.h"
-#import "UTIUtilities.h"
 
 #if ENABLE(DATA_INTERACTION)
 #import <MobileCoreServices/MobileCoreServices.h>
@@ -92,14 +91,6 @@
     return maxDragImageSize;
 }
 
-String DragController::platformContentTypeForBlobType(const String& type) const
-{
-    auto utiType = UTIFromMIMEType(type);
-    if (!utiType.isEmpty())
-        return utiType;
-    return type;
-}
-
 void DragController::cleanupAfterSystemDrag()
 {
 #if PLATFORM(MAC)

Modified: trunk/Source/WebKit/ChangeLog (261637 => 261638)


--- trunk/Source/WebKit/ChangeLog	2020-05-13 19:26:58 UTC (rev 261637)
+++ trunk/Source/WebKit/ChangeLog	2020-05-13 19:43:10 UTC (rev 261638)
@@ -1,3 +1,65 @@
+2020-05-13  Wenson Hsieh  <wenson_hs...@apple.com>
+
+        [iOS] "Copy" context menu action for attachment element does not work in Mail
+        https://bugs.webkit.org/show_bug.cgi?id=211817
+        <rdar://problem/58043110>
+
+        Reviewed by Tim Horton.
+
+        Implements support for copying an attachment element, via context menu actions. To achieve this, we first
+        refactor code in WebCore that is currently responsible for converting an attachment element into a
+        PromisedAttachmentInfo, which provides a handle to an UI-process-side API attachment object. We then use this
+        helper in WebKit to send PromisedAttachmentInfo back to the UI process when handling the copy action from the
+        context menu on iOS.
+
+        On iOS, we then take this promised attachment info in the UI process, map it to an API::Attachment object, and
+        use the file handle to create and write an NSItemProvider to the general pasteboard. (In the future, similar
+        logic could be implemented on macOS as well to handle copying attachment elements by implementing a version of
+        writePromisedAttachmentToPasteboard in PageClientImplMac. Currently, we fall back to treating this case as if
+        we're copying a text selection containing a single attachment element).
+
+        * UIProcess/API/ios/WKWebViewPrivateForTestingIOS.h:
+
+        Add a new helper function to simulate an element action (_WKElementActionType) for an element at the given
+        location. This is used by the new API test.
+
+        * UIProcess/API/ios/WKWebViewTestingIOS.mm:
+        (-[WKWebView _simulateElementAction:atLocation:]):
+        * UIProcess/PageClient.h:
+        (WebKit::PageClient::writePromisedAttachmentToPasteboard):
+        * UIProcess/WebPageProxy.cpp:
+        (WebKit::WebPageProxy::writePromisedAttachmentToPasteboard):
+        * UIProcess/WebPageProxy.h:
+        * UIProcess/WebPageProxy.messages.in:
+
+        Add an IPC message for the UI process to receive a PromisedAttachmentInfo, and copy it to the system pasteboard
+        if it maps to an actual API::Attachment. For now, we only support copying data if the attachment is backed by
+        a file wrapper in the UI process (as opposed to blob data written by the web process).
+
+        * UIProcess/ios/PageClientImplIOS.h:
+        * UIProcess/ios/PageClientImplIOS.mm:
+        (WebKit::PageClientImpl::writePromisedAttachmentToPasteboard):
+        * UIProcess/ios/WKContentViewInteraction.h:
+        * UIProcess/ios/WKContentViewInteraction.mm:
+        (createItemProvider):
+
+        Create a helper function to convert PromisedAttachmentInfo into an NSItemProvider. This is similar to
+        `-_prepareToDragPromisedAttachment:`, but with a couple differences: (1) we only create an NSItemProvider
+        instead of going through `WebItemProviderRegistrationInfoList`; (2) since there's no opportunity to clean up
+        temporary after pasting, serialize the attachment data using `-serializedRepresentation`, and provide the
+        attachment data instead of a file URL.
+
+        Ideally, we would offer the data directly to the pasteboard so that even if Mail terminates, the contents of the
+        pasteboard can still be provided when pasting; however, it doesn't seem like we can do this while being able to
+        offer a suggested name for the item we're writing. Some other apps work around this limitation by writing the
+        file name as plain text on the pasteboard, but this doesn't work then attempting to copy a plain text file that
+        has a file name.
+
+        (-[WKContentView _writePromisedAttachmentToPasteboard:]):
+        (-[WKContentView _simulateElementAction:atLocation:]):
+        * WebProcess/WebPage/ios/WebPageIOS.mm:
+        (WebKit::WebPage::performActionOnElement):
+
 2020-05-13  Per Arne Vollan  <pvol...@apple.com>
 
         [iOS] Update message filtering rules in the WebContent process' sandbox

Modified: trunk/Source/WebKit/UIProcess/API/ios/WKWebViewPrivateForTestingIOS.h (261637 => 261638)


--- trunk/Source/WebKit/UIProcess/API/ios/WKWebViewPrivateForTestingIOS.h	2020-05-13 19:26:58 UTC (rev 261637)
+++ trunk/Source/WebKit/UIProcess/API/ios/WKWebViewPrivateForTestingIOS.h	2020-05-13 19:43:10 UTC (rev 261638)
@@ -24,6 +24,7 @@
  */
 
 #import <WebKit/WKWebView.h>
+#import <WebKit/_WKElementAction.h>
 
 #if TARGET_OS_IPHONE
 
@@ -67,6 +68,7 @@
 - (void)_doAfterResettingSingleTapGesture:(dispatch_block_t)action;
 
 - (NSDictionary *)_propertiesOfLayerWithID:(unsigned long long)layerID;
+- (void)_simulateElementAction:(_WKElementActionType)actionType atLocation:(CGPoint)location;
 - (void)_simulateLongPressActionAtLocation:(CGPoint)location;
 - (void)_simulateTextEntered:(NSString *)text;
 

Modified: trunk/Source/WebKit/UIProcess/API/ios/WKWebViewTestingIOS.mm (261637 => 261638)


--- trunk/Source/WebKit/UIProcess/API/ios/WKWebViewTestingIOS.mm	2020-05-13 19:26:58 UTC (rev 261637)
+++ trunk/Source/WebKit/UIProcess/API/ios/WKWebViewTestingIOS.mm	2020-05-13 19:43:10 UTC (rev 261638)
@@ -293,6 +293,11 @@
 #endif
 }
 
+- (void)_simulateElementAction:(_WKElementActionType)actionType atLocation:(CGPoint)location
+{
+    [_contentView _simulateElementAction:actionType atLocation:location];
+}
+
 - (void)_simulateLongPressActionAtLocation:(CGPoint)location
 {
     [_contentView _simulateLongPressActionAtLocation:location];

Modified: trunk/Source/WebKit/UIProcess/PageClient.h (261637 => 261638)


--- trunk/Source/WebKit/UIProcess/PageClient.h	2020-05-13 19:26:58 UTC (rev 261637)
+++ trunk/Source/WebKit/UIProcess/PageClient.h	2020-05-13 19:43:10 UTC (rev 261638)
@@ -106,6 +106,10 @@
 #if ENABLE(DRAG_SUPPORT)
 struct DragItem;
 #endif
+
+#if ENABLE(ATTACHMENT_ELEMENT)
+struct PromisedAttachmentInfo;
+#endif
 }
 
 namespace WebKit {
@@ -509,6 +513,7 @@
     virtual void didInsertAttachment(API::Attachment&, const String& source) { }
     virtual void didRemoveAttachment(API::Attachment&) { }
     virtual void didInvalidateDataForAttachment(API::Attachment&) { }
+    virtual void writePromisedAttachmentToPasteboard(WebCore::PromisedAttachmentInfo&&) { }
 #if PLATFORM(COCOA)
     virtual NSFileWrapper *allocFileWrapperInstance() const { return nullptr; }
     virtual NSSet *serializableFileWrapperClasses() const { return nullptr; }

Modified: trunk/Source/WebKit/UIProcess/WebPageProxy.cpp (261637 => 261638)


--- trunk/Source/WebKit/UIProcess/WebPageProxy.cpp	2020-05-13 19:26:58 UTC (rev 261637)
+++ trunk/Source/WebKit/UIProcess/WebPageProxy.cpp	2020-05-13 19:43:10 UTC (rev 261638)
@@ -9456,6 +9456,11 @@
 
 #if ENABLE(ATTACHMENT_ELEMENT)
 
+void WebPageProxy::writePromisedAttachmentToPasteboard(WebCore::PromisedAttachmentInfo&& info)
+{
+    pageClient().writePromisedAttachmentToPasteboard(WTFMove(info));
+}
+
 RefPtr<API::Attachment> WebPageProxy::attachmentForIdentifier(const String& identifier) const
 {
     if (identifier.isEmpty())

Modified: trunk/Source/WebKit/UIProcess/WebPageProxy.h (261637 => 261638)


--- trunk/Source/WebKit/UIProcess/WebPageProxy.h	2020-05-13 19:26:58 UTC (rev 261637)
+++ trunk/Source/WebKit/UIProcess/WebPageProxy.h	2020-05-13 19:43:10 UTC (rev 261638)
@@ -157,6 +157,10 @@
 #include "SOAuthorizationLoadPolicy.h"
 #endif
 
+#if ENABLE(ATTACHMENT_ELEMENT)
+#include <WebCore/PromisedAttachmentInfo.h>
+#endif
+
 #if ENABLE(MEDIA_SESSION)
 namespace WebCore {
 class MediaSessionMetadata;
@@ -2262,6 +2266,8 @@
     void didRemoveAttachment(API::Attachment&);
     Ref<API::Attachment> ensureAttachment(const String& identifier);
     void invalidateAllAttachments();
+
+    void writePromisedAttachmentToPasteboard(WebCore::PromisedAttachmentInfo&&);
 #endif
 
     void reportPageLoadResult(const WebCore::ResourceError& = { });

Modified: trunk/Source/WebKit/UIProcess/WebPageProxy.messages.in (261637 => 261638)


--- trunk/Source/WebKit/UIProcess/WebPageProxy.messages.in	2020-05-13 19:26:58 UTC (rev 261637)
+++ trunk/Source/WebKit/UIProcess/WebPageProxy.messages.in	2020-05-13 19:43:10 UTC (rev 261638)
@@ -556,6 +556,7 @@
     DidInsertAttachmentWithIdentifier(String identifier, String source, bool hasEnclosingImage)
     DidRemoveAttachmentWithIdentifier(String identifier)
     SerializedAttachmentDataForIdentifiers(Vector<String> identifiers) -> (Vector<WebCore::SerializedAttachmentData> seralizedData) Synchronous
+    WritePromisedAttachmentToPasteboard(struct WebCore::PromisedAttachmentInfo info)
 #endif
 
     SignedPublicKeyAndChallengeString(unsigned keySizeIndex, String challengeString, URL url) -> (String result) Synchronous

Modified: trunk/Source/WebKit/UIProcess/ios/PageClientImplIOS.h (261637 => 261638)


--- trunk/Source/WebKit/UIProcess/ios/PageClientImplIOS.h	2020-05-13 19:26:58 UTC (rev 261637)
+++ trunk/Source/WebKit/UIProcess/ios/PageClientImplIOS.h	2020-05-13 19:43:10 UTC (rev 261638)
@@ -258,6 +258,10 @@
     RetainPtr<WKDrawingView> createDrawingView(WebCore::GraphicsLayer::EmbeddedViewID) override;
 #endif
 
+#if ENABLE(ATTACHMENT_ELEMENT)
+    void writePromisedAttachmentToPasteboard(WebCore::PromisedAttachmentInfo&&) final;
+#endif
+
     void cancelPointersForGestureRecognizer(UIGestureRecognizer*) override;
     WTF::Optional<unsigned> activeTouchIdentifierForGestureRecognizer(UIGestureRecognizer*) override;
 

Modified: trunk/Source/WebKit/UIProcess/ios/PageClientImplIOS.mm (261637 => 261638)


--- trunk/Source/WebKit/UIProcess/ios/PageClientImplIOS.mm	2020-05-13 19:26:58 UTC (rev 261637)
+++ trunk/Source/WebKit/UIProcess/ios/PageClientImplIOS.mm	2020-05-13 19:43:10 UTC (rev 261638)
@@ -952,6 +952,15 @@
     [m_contentView _showDataDetectorsUIForPositionInformation:positionInformation];
 }
 
+#if ENABLE(ATTACHMENT_ELEMENT)
+
+void PageClientImpl::writePromisedAttachmentToPasteboard(WebCore::PromisedAttachmentInfo&& info)
+{
+    [m_contentView _writePromisedAttachmentToPasteboard:WTFMove(info)];
+}
+
+#endif // ENABLE(ATTACHMENT_ELEMENT)
+
 } // namespace WebKit
 
 #endif // PLATFORM(IOS_FAMILY)

Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h (261637 => 261638)


--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h	2020-05-13 19:26:58 UTC (rev 261637)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h	2020-05-13 19:43:10 UTC (rev 261638)
@@ -50,6 +50,7 @@
 #import "WKShareSheet.h"
 #import "WKSyntheticTapGestureRecognizer.h"
 #import "WKTouchActionGestureRecognizer.h"
+#import "_WKElementAction.h"
 #import "_WKFormInputSession.h"
 #import <UIKit/UIView.h>
 #import <WebCore/ActivityState.h>
@@ -598,10 +599,15 @@
 - (void)_removeContextMenuViewIfPossible;
 #endif
 
+#if ENABLE(ATTACHMENT_ELEMENT)
+- (void)_writePromisedAttachmentToPasteboard:(WebCore::PromisedAttachmentInfo&&)info;
+#endif
+
 @end
 
 @interface WKContentView (WKTesting)
 
+- (void)_simulateElementAction:(_WKElementActionType)actionType atLocation:(CGPoint)location;
 - (void)_simulateLongPressActionAtLocation:(CGPoint)location;
 - (void)_simulateTextEntered:(NSString *)text;
 - (void)selectFormAccessoryPickerRow:(NSInteger)rowIndex;

Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm (261637 => 261638)


--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2020-05-13 19:26:58 UTC (rev 261637)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2020-05-13 19:43:10 UTC (rev 261638)
@@ -149,6 +149,10 @@
 #import <UIKit/_UILookupGestureRecognizer.h>
 #endif
 
+#if ENABLE(ATTACHMENT_ELEMENT)
+#import "APIAttachment.h"
+#endif
+
 #if ENABLE(INPUT_TYPE_COLOR)
 #import "WKFormColorControl.h"
 #endif
@@ -8587,6 +8591,61 @@
 
 #endif // HAVE(UI_CURSOR_INTERACTION)
 
+#if ENABLE(ATTACHMENT_ELEMENT)
+
+static RetainPtr<NSItemProvider> createItemProvider(const WebKit::WebPageProxy& page, const WebCore::PromisedAttachmentInfo& info)
+{
+    auto numberOfAdditionalTypes = info.additionalTypes.size();
+    ASSERT(numberOfAdditionalTypes == info.additionalData.size());
+
+    auto attachment = page.attachmentForIdentifier(info.attachmentIdentifier);
+    if (!attachment)
+        return { };
+
+    NSString *utiType = attachment->utiType();
+    if (![utiType length])
+        return { };
+
+    auto fileWrapper = retainPtr(attachment->fileWrapper());
+    if (!fileWrapper)
+        return { };
+
+    auto item = adoptNS([[NSItemProvider alloc] init]);
+    [item setPreferredPresentationStyle:UIPreferredPresentationStyleAttachment];
+
+    NSString *fileName = attachment->fileName();
+    if ([fileName length])
+        [item setSuggestedName:fileName];
+
+    if (numberOfAdditionalTypes == info.additionalData.size() && numberOfAdditionalTypes) {
+        for (size_t index = 0; index < numberOfAdditionalTypes; ++index) {
+            auto nsData = info.additionalData[index]->createNSData();
+            [item registerDataRepresentationForTypeIdentifier:info.additionalTypes[index] visibility:NSItemProviderRepresentationVisibilityAll loadHandler:[nsData](void (^completionHandler)(NSData *, NSError *)) -> NSProgress * {
+                completionHandler(nsData.get(), nil);
+                return nil;
+            }];
+        }
+    }
+
+    [item registerDataRepresentationForTypeIdentifier:utiType visibility:NSItemProviderRepresentationVisibilityAll loadHandler:[fileWrapper](void (^completionHandler)(NSData *, NSError *)) -> NSProgress * {
+        if (auto nsData = retainPtr([fileWrapper serializedRepresentation]))
+            completionHandler(nsData.get(), nil);
+        else
+            completionHandler(nil, [NSError errorWithDomain:WKErrorDomain code:WKErrorUnknown userInfo:nil]);
+        return nil;
+    }];
+
+    return item;
+}
+
+- (void)_writePromisedAttachmentToPasteboard:(WebCore::PromisedAttachmentInfo&&)info
+{
+    if (auto item = createItemProvider(*_page, WTFMove(info)))
+        UIPasteboard.generalPasteboard.itemProviders = @[ item.get() ];
+}
+
+#endif // ENABLE(ATTACHMENT_ELEMENT)
+
 @end
 
 @implementation WKContentView (WKTesting)
@@ -8629,6 +8688,16 @@
 #endif
 }
 
+- (void)_simulateElementAction:(_WKElementActionType)actionType atLocation:(CGPoint)location
+{
+    RetainPtr<WKContentView> protectedSelf = self;
+    [self doAfterPositionInformationUpdate:[actionType, protectedSelf] (WebKit::InteractionInformationAtPosition info) {
+        _WKElementAction *action = "" _elementActionWithType:actionType assistant:protectedSelf->_actionSheetAssistant.get()];
+        _WKActivatedElementInfo *elementInfo = [_WKActivatedElementInfo activatedElementInfoWithInteractionInformationAtPosition:info userInfo:nil];
+        [action runActionWithElementInfo:elementInfo];
+    } forRequest:WebKit::InteractionInformationRequest(WebCore::roundedIntPoint(location))];
+}
+
 - (void)_simulateLongPressActionAtLocation:(CGPoint)location
 {
     RetainPtr<WKContentView> protectedSelf = self;

Modified: trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm (261637 => 261638)


--- trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm	2020-05-13 19:26:58 UTC (rev 261637)
+++ trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm	2020-05-13 19:43:10 UTC (rev 261638)
@@ -138,6 +138,10 @@
 #import <wtf/cocoa/Entitlements.h>
 #import <wtf/text/TextStream.h>
 
+#if ENABLE(ATTACHMENT_ELEMENT)
+#import <WebCore/PromisedAttachmentInfo.h>
+#endif
+
 #define RELEASE_LOG_IF_ALLOWED(channel, fmt, ...) RELEASE_LOG_IF(isAlwaysOnLoggingAllowed(), channel, "%p - WebPage::" fmt, this, ##__VA_ARGS__)
 #define RELEASE_LOG_ERROR_IF_ALLOWED(channel, fmt, ...) RELEASE_LOG_ERROR_IF(isAlwaysOnLoggingAllowed(), channel, "%p - WebPage::" fmt, this, ##__VA_ARGS__)
 
@@ -2976,9 +2980,12 @@
                 title = stripLeadingAndTrailingHTMLSpaces(title);
             }
             m_interactionNode->document().editor().writeImageToPasteboard(*Pasteboard::createForCopyAndPaste(), element, url, title);
-        } else if (element.isLink()) {
+        } else if (element.isLink())
             m_interactionNode->document().editor().copyURL(element.document().completeURL(stripLeadingAndTrailingHTMLSpaces(element.attributeWithoutSynchronization(HTMLNames::hrefAttr))), element.textContent());
-        }
+#if ENABLE(ATTACHMENT_ELEMENT)
+        else if (auto attachmentInfo = element.document().editor().promisedAttachmentInfo(element))
+            send(Messages::WebPageProxy::WritePromisedAttachmentToPasteboard(WTFMove(attachmentInfo)));
+#endif
     } else if (static_cast<SheetAction>(action) == SheetAction::SaveImage) {
         if (!is<RenderImage>(*element.renderer()))
             return;

Modified: trunk/Tools/ChangeLog (261637 => 261638)


--- trunk/Tools/ChangeLog	2020-05-13 19:26:58 UTC (rev 261637)
+++ trunk/Tools/ChangeLog	2020-05-13 19:43:10 UTC (rev 261638)
@@ -1,3 +1,15 @@
+2020-05-13  Wenson Hsieh  <wenson_hs...@apple.com>
+
+        [iOS] "Copy" context menu action for attachment element does not work in Mail
+        https://bugs.webkit.org/show_bug.cgi?id=211817
+        <rdar://problem/58043110>
+
+        Reviewed by Tim Horton.
+
+        Add a new iOS API test that simulates the context menu action to "copy" on an attachment element.
+
+        * TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm:
+
 2020-05-12  Matt Lewis  <jlew...@apple.com>
 
         Run-webkit-tests should not fail if all tests found to run are skipped.

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm (261637 => 261638)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm	2020-05-13 19:26:58 UTC (rev 261637)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm	2020-05-13 19:43:10 UTC (rev 261638)
@@ -2153,6 +2153,48 @@
     });
 }
 
+TEST(WKAttachmentTestsIOS, CopyAttachmentUsingElementAction)
+{
+    UIPasteboard.generalPasteboard.items = @[ ];
+
+    auto webView = webViewForTestingAttachments();
+    [webView _setEditable:YES];
+    [webView synchronouslyLoadHTMLString:@"<body></body><script>document.body.focus();</script>"];
+
+    auto document = adoptNS([[NSFileWrapper alloc] initRegularFileWithContents:testPDFData()]);
+    [document setPreferredFilename:@"hello.pdf"];
+
+    auto attachment = retainPtr([webView synchronouslyInsertAttachmentWithFileWrapper:document.get() contentType:(__bridge NSString *)kUTTypePDF]);
+    NSString *identifier = [webView stringByEvaluatingJavaScript:@"document.querySelector('attachment').uniqueIdentifier"];
+    EXPECT_WK_STREQ(identifier, [attachment uniqueIdentifier]);
+
+    // Ensure that we can hit-test to the attachment element when simulating a context menu element by adding a click event handler.
+    [webView objectByEvaluatingJavaScript:@"document.querySelector('attachment').addEventListener('click', () => { })"];
+    [webView _setEditable:NO];
+
+    [webView _simulateElementAction:_WKElementActionTypeCopy atLocation:CGPointMake(20, 20)];
+
+    // It takes two IPC round trips between the UI process and web process until the pasteboard data is written,
+    // since we first need to hit-test to discover the activated element, and then use the activated element to
+    // simulate the "copy" action.
+    [webView waitForNextPresentationUpdate];
+    [webView waitForNextPresentationUpdate];
+
+    NSArray<NSItemProvider *> *itemProviders = UIPasteboard.generalPasteboard.itemProviders;
+    EXPECT_EQ(1U, itemProviders.count);
+
+    NSItemProvider *itemProvider = itemProviders.firstObject;
+    EXPECT_EQ(UIPreferredPresentationStyleAttachment, itemProvider.preferredPresentationStyle);
+    EXPECT_WK_STREQ("hello.pdf", itemProvider.suggestedName);
+
+    __block bool done = false;
+    [itemProvider loadDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypePDF completionHandler:^(NSData *data, NSError *) {
+        EXPECT_TRUE([[document serializedRepresentation] isEqualToData:data]);
+        done = true;
+    }];
+    TestWebKitAPI::Util::run(&done);
+}
+
 #if HAVE(PENCILKIT)
 static BOOL forEachViewInHierarchy(UIView *view, void(^mapFunction)(UIView *subview, BOOL *stop))
 {
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to