[Attachment Support] Augment _WKAttachment SPI to handle NSFileWrappers in addition...
authorwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 21 Aug 2018 21:10:34 +0000 (21:10 +0000)
committerwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 21 Aug 2018 21:10:34 +0000 (21:10 +0000)
https://bugs.webkit.org/show_bug.cgi?id=188496
<rdar://problem/43216836>

Reviewed by Tim Horton.

Source/WebCore:

Refactors logic around HTMLAttachmentElement and pasteboard reading helpers, in support of moving the data
backing for attachment elements to the client layer, instead of keeping it in the attachment element's File.
Augmented existing API tests in WKAttachmentTests, and also added a new API test (see Tools for more detail).

* WebCore.xcodeproj/project.pbxproj:
* dom/Document.cpp:
(WebCore::Document::didInsertAttachmentElement):

Notify the client layer when a newly inserted attachment element's identifier has been updated to avoid
colliding with the identifier of an existing attachment element. This can happen if, for instance, one or more
attachments are copied and pasted within the same document.

* editing/Editor.cpp:
(WebCore::Editor::registerAttachmentIdentifier):
(WebCore::Editor::cloneAttachmentData):

Add new helper functions to notify the client when the attachment identifier to data mapping needs to be
updated. This can happen in three ways: (1) an attachment is created with raw data, or (2) an attachment is
created with a file path, or (3) the unique identifier for an attachment element has been reassigned. These
correspond to the two versions of `registerAttachmentIdentifier`, and `cloneAttachmentData`, respectively.

(WebCore::EditorClient::supportsClientSideAttachmentData const):

Add a new EditorClient hook to determine whether client-side attachment data management is supported. Currently,
this only returns true for WebKit2. If this flag is set to true, we register attachment identifiers and don't
create a new File object for the attachment element; otherwise, fall back to creating and setting a File for the
new attachment element.

(WebCore::Editor::insertAttachment):
(WebCore::Editor::insertAttachmentFromFile): Deleted.

Adjust logic when inserting an attachment; we now only need to update the attributes of the attachment element
with metadata from the client layer.

* editing/Editor.h:
* editing/cocoa/WebContentReaderCocoa.mm:
(WebCore::supportsClientSideAttachmentData):
(WebCore::createFragmentForImageAttachment):

Notify the client when creating an attachment element from image data on the pasteboard.

(WebCore::replaceRichContentWithAttachments):

Refactor this helper function to no longer connect attachment elements to File objects created from
subresources. Instead, just update each attachment element's attributes using information about the subresource,
and then notify the client about the new attachment data and attachment identifier.

(WebCore::createFragmentAndAddResources):
(WebCore::sanitizeMarkupWithArchive):

Plumb the current WebContentReader's Frame& to each of these helpers, so that it can call out to the client.

(WebCore::WebContentReader::readWebArchive):
(WebCore::WebContentMarkupReader::readWebArchive):
(WebCore::WebContentReader::readImage):
(WebCore::WebContentReader::readFilePaths):

Notify the client when creating an attachment from the path of a dropped file.

* html/AttachmentTypes.h:

Remove AttachmentInfo. This is no longer necessary because we don't need to request attachment data from the web
process anymore.

* html/HTMLAttachmentElement.cpp:
(WebCore::HTMLAttachmentElement::ensureUniqueIdentifier):

Add a helper function on the attachment element to create and return a unique identifier if needed.

(WebCore::HTMLAttachmentElement::updateAttributes):

Add a helper method to update the displayed element attributes (type, title and subtitle) or an attachment.

(WebCore::AttachmentDataReader::create): Deleted.
(WebCore::AttachmentDataReader::AttachmentDataReader): Deleted.

Remove AttachmentDataReader. This helper class was only used to load attachment data when requesting attachment
information in the client, but this is now obviated by moving attachment data to the client layer.

(WebCore::HTMLAttachmentElement::updateFileWithData): Deleted.
(WebCore::HTMLAttachmentElement::requestInfo): Deleted.
(WebCore::HTMLAttachmentElement::destroyReader): Deleted.
(WebCore::AttachmentDataReader::~AttachmentDataReader): Deleted.
(WebCore::AttachmentDataReader::didFinishLoading): Deleted.
(WebCore::AttachmentDataReader::didFail): Deleted.
(WebCore::AttachmentDataReader::invokeCallbackAndFinishReading): Deleted.
* html/HTMLAttachmentElement.h:
* page/DragClient.h:
* page/DragController.cpp:
(WebCore::DragController::startDrag):
(WebCore::DragController::doSystemDrag):
(WebCore::DragController::promisedAttachmentInfo):

Allow dragging an attachment (even if it does not have a file) as long as it has a unique identifier and a
content type.

(WebCore::DragController::promisedBlobInfo): Deleted.
* page/DragController.h:
* page/EditorClient.h:
(WebCore::EditorClient::registerAttachmentIdentifier):
(WebCore::EditorClient::cloneAttachmentData):
* platform/DragItem.h:
(WebCore::DragItem::encode const):
(WebCore::DragItem::decode):
* platform/PromisedAttachmentInfo.h: Renamed from Source/WebCore/platform/PromisedBlobInfo.h.

Add an attachment identifier to PromisedBlobInfo. Additionally, rename PromisedBlobInfo to
PromisedAttachmentInfo, since it is currently exclusively used to attachment element data to the pasteboard.
In the future, this could be renamed to something more general (e.g. PromisedPasteboardData), should we use this
mechanism to write data from other sources to the pasteboard.

(WebCore::PromisedAttachmentInfo::operator bool const):

Source/WebKit:

Adjusts WebKit's attachment editing SPI with the following modifications:
•   Deprecate `-_insertAttachmentWithFilename:contentType:data:options:completion:` in favor of
    `-_insertAttachmentWithFileWrapper:contentType:options:completion:`.
•   Deprecate `-requestInfo:` and in favor of just `-info`.
•   Add a `-fileWrapper` property to `_WKAttachmentInfo` that returns an `NSFileWrapper`.
•   Remove some SPI methods that would otherwise be deprecated, but are not even necessary, since they're no
    longer even used by Mail.

To make this possible, we refactor where and how attachment data is tracked. Currently, the data is stored in
the network process, and made accessible to the web process via blob URLs stored in the File object in the
attachment element. As such, requests from the UI process for attachment data would first be routed through the
web process to network process and back.

Instead, we now keep the relevant attachment data (in the form of NSFileWrapper on Cocoa platforms) in the UI
process, on API::Attachment. We additionally keep a map of attachment identifiers to API::Attachments, which
allows us to propagate the same _WKAttachment wrapper object to the SPI client for each uniquely identified
attachment element. This also has the benefit of allowing us to remove the asynchronous version of `-requestInfo:`
and replace it with just an `info` property.

Changes are covered by new and existing API tests.

* PlatformMac.cmake:

Remove APIAttachment.cpp, now that APIAttachment.cpp is listed in Sources.txt.

* Scripts/webkit/messages.py:
* Shared/Cocoa/APIObject.mm:
(API::Object::newObject):

Guard _WKAttachment creation with ENABLE_ATTACHMENT_ELEMENT.

* Shared/WebCoreArgumentCoders.cpp:
(IPC::ArgumentCoder<PromisedAttachmentInfo>::encode):
(IPC::ArgumentCoder<PromisedAttachmentInfo>::decode):
(IPC::ArgumentCoder<PromisedBlobInfo>::encode): Deleted.
(IPC::ArgumentCoder<PromisedBlobInfo>::decode): Deleted.
(IPC::ArgumentCoder<AttachmentInfo>::encode): Deleted.
(IPC::ArgumentCoder<AttachmentInfo>::decode): Deleted.
* Shared/WebCoreArgumentCoders.h:

Continue removing encoding support for WebCore::AttachmentInfo. Additionally, rename PromisedBlobInfo to
PromisedAttachmentInfo.

* Sources.txt:
* SourcesCocoa.txt:

Move APIAttachment.cpp from the SourcesCocoa.txt to the platform-agnostic Sources.txt.

* UIProcess/API/APIAttachment.cpp:
(API::Attachment::updateAttributes):

Rename setDataAndContentType to just updateAttributes; instead of sending data, only send the information needed
to update the presentational attributes of the attachment element.

(API::Attachment::requestInfo): Deleted.

Just call the completion handler with the result of `self.info`.

(API::Attachment::setDataAndContentType): Deleted.
* UIProcess/API/APIAttachment.h:

Add additional attributes: a content type, a file path, and (on Cocoa platforms) an NSFileWrapper.

* UIProcess/API/Cocoa/WKUIDelegatePrivate.h:

Remove -_webView:didInsertAttachment:, since this is unused by any client currently, and is superceded by
-_webView:didInsertAttachment:withSource:.

* UIProcess/API/Cocoa/WKWebView.mm:
(-[WKWebView _didInsertAttachment:withSource:]):
(-[WKWebView _didRemoveAttachment:]):

Look up the API::Attachment corresponding to the identifier, and send its wrapper object to the client.

(-[WKWebView _insertAttachmentWithFilename:contentType:data:options:completion:]):
(-[WKWebView _insertAttachmentWithFileWrapper:contentType:options:completion:]):

Add a way to insert an attachment using NSFileWrapper, and reimplement _insertAttachmentWithFilename: using it.

* UIProcess/API/Cocoa/WKWebViewPrivate.h:
* UIProcess/API/Cocoa/_WKAttachment.h:
* UIProcess/API/Cocoa/_WKAttachment.mm:
(-[_WKAttachmentInfo initWithFileWrapper:filePath:contentType:]):
(-[_WKAttachmentInfo data]):
(-[_WKAttachmentInfo name]):
(isDeclaredOrDynamicTypeIdentifier):
(-[_WKAttachmentInfo _typeIdentifierFromPathExtension]):
(-[_WKAttachmentInfo contentType]):
(-[_WKAttachmentInfo mimeType]):
(-[_WKAttachmentInfo utiType]):
(-[_WKAttachmentInfo fileWrapper]):
(-[_WKAttachment info]):
(-[_WKAttachment requestInfo:]):

Add a property on _WKAttachment to retrieve a _WKAttachmentInfo (a snapshot of the current state of the
attachment, along with the NSFileWrapper). Reimplement requestInfo using this property.

(-[_WKAttachment setFileWrapper:contentType:completion:]):
(-[_WKAttachment setData:newContentType:newFilename:completion:]):

Reimplemented by calling -setFileWrapper:contentType:completion: with an NSFileWrapper created using the given
data. Additionally, create and associate the unique identifier with an API::Attachment right away.

(-[_WKAttachment uniqueIdentifier]):
(-[_WKAttachment description]):
(-[_WKAttachmentInfo initWithInfo:]): Deleted.
(-[_WKAttachmentInfo fileLoadingError]): Deleted.
(-[_WKAttachment isEqual:]): Deleted.
(-[_WKAttachment hash]): Deleted.

There's no longer any point to implementing these methods, since the SPI client is now guaranteed a unique
mapping of _WKAttachments to attachment elements in the document.

* UIProcess/Cocoa/PageClientImplCocoa.mm:
(WebKit::PageClientImplCocoa::didInsertAttachment):
* UIProcess/Cocoa/WebPageProxyCocoa.mm:
(WebKit::WebPageProxy::platformRegisterAttachment):
(WebKit::WebPageProxy::platformCloneAttachment):

Extend the behavior of registering new attachment data on Cocoa platforms by additionally creating and setting
NSFileWrappers on the API::Attachment.

* UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::resetStateAfterProcessExited):

Clear out the map of attachment identifiers to API::Attachments when the web content process is terminated.

(WebKit::WebPageProxy::attachmentForIdentifier const):

Helper function to look up an API::Attachment for the given attachment identifier. Returns null if the
attachment is not found, or the attachment identifier is invalid.

(WebKit::WebPageProxy::insertAttachment):
(WebKit::WebPageProxy::updateAttachmentAttributes):
(WebKit::WebPageProxy::registerAttachmentIdentifierFromData):
(WebKit::WebPageProxy::registerAttachmentIdentifierFromFilePath):
(WebKit::WebPageProxy::cloneAttachmentData):
(WebKit::WebPageProxy::platformRegisterAttachment):
(WebKit::WebPageProxy::platformCloneAttachment):
(WebKit::WebPageProxy::didInsertAttachment):

Create an entry in the attachment identifier to API::Attachment map when an attachment is inserted, if one does
not already exist. An attachment mapping would not exist only in the case where an attachment element was
created via bindings; in this case, the client wouldn't have access to an NSFileWrapper containing the contents
of the file; in the future, this can be improved by adding a mechanism to register an attachment element with
this data, but for now, this is unnecessary for Mail's purposes.

(WebKit::WebPageProxy::didRemoveAttachment):
(WebKit::WebPageProxy::ensureAttachment):

Ensures an attachment identifier to API::Attachment mapping.

(WebKit::WebPageProxy::attachmentInfoCallback): Deleted.
(WebKit::WebPageProxy::requestAttachmentInfo): Deleted.
(WebKit::WebPageProxy::setAttachmentDataAndContentType): Deleted.
* UIProcess/WebPageProxy.h:
* UIProcess/WebPageProxy.messages.in:
* UIProcess/ios/PageClientImplIOS.h:
* UIProcess/ios/PageClientImplIOS.mm:
* UIProcess/ios/WKContentViewInteraction.h:
* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView _startDrag:item:]):
(-[WKContentView _prepareToDragPromisedAttachment:]):
(-[WKContentView _prepareToDragPromisedBlob:]): Deleted.

Reimplement the way promised attachment data is delivered to the destination when an attachment element is
dragged. Instead of relying on the blob URL of the File on the attachment element, first try to find an API
Attachment object corresponding to the unique identifier of the dragged attachment, and use its NSFileWrapper to
deliver promised data to the destination file URL. The blob URL codepath still exists in the case where script
specifies the dragged attachment's File.

* UIProcess/mac/PageClientImplMac.h:
* UIProcess/mac/PageClientImplMac.mm:
* WebProcess/WebCoreSupport/WebEditorClient.cpp:
(WebKit::WebEditorClient::registerAttachmentIdentifier):
(WebKit::WebEditorClient::cloneAttachmentData):
* WebProcess/WebCoreSupport/WebEditorClient.h:
* WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::insertAttachment):
(WebKit::WebPage::updateAttachmentAttributes):
(WebKit::WebPage::requestAttachmentInfo): Deleted.
(WebKit::WebPage::setAttachmentDataAndContentType): Deleted.
* WebProcess/WebPage/WebPage.h:
* WebProcess/WebPage/WebPage.messages.in:

More renaming and IPC message plumbing.

Source/WebKitLegacy/win:

Adjust for changing PromisedAttachmentInfo.h to forward declare WebCore::SharedBuffer rather than include the
header directly.

* WebCoreSupport/WebDragClient.cpp:

Tools:

Adjusts existing attachment API tests. See below for more detail.

* TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm:
(-[TestWKWebView synchronouslyInsertAttachmentWithFileWrapper:contentType:]):
(-[TestWKWebView synchronouslyInsertAttachmentWithFilename:contentType:data:]):
(-[_WKAttachment synchronouslySetData:newContentType:newFilename:error:]):
(-[_WKAttachment synchronouslySetFileWrapper:newContentType:error:]):

Move off of deprecated attachment SPI, and add new helper functions to synchronously insert a new attachment or
update an existing attachment with a file wrapper.

(-[_WKAttachment expectRequestedDataToBe:]):
(TestWebKitAPI::TEST):

Add a new test to verify that file-URL-backed NSFileWrappers can be used to insert and update attachment data.
Also augment an existing test to check that an attachment element which has been copied and pasted within the
same document has a different _WKAttachment wrapper object than its duplicate, but both _WKAttachments are
backed by the same NSFileWrapper that was originally used to insert the attachment.

Additionally, add another macOS test to verify that dropping promised files in an attachment-element-enabled
editable area inserts attachment elements into the document and notifies the UI client with the inserted
attachment data.

(-[_WKAttachment synchronouslyRequestInfo:]): Deleted.
(-[_WKAttachment synchronouslyRequestData:]): Deleted.
* TestWebKitAPI/ios/DragAndDropSimulatorIOS.mm:
(-[DragAndDropSimulator _webView:didInsertAttachment:withSource:]):

Move off of -_webView:didInsertAttachment:.

(-[DragAndDropSimulator _webView:didInsertAttachment:]): Deleted.

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

51 files changed:
Source/WebCore/ChangeLog
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/dom/Document.cpp
Source/WebCore/editing/Editor.cpp
Source/WebCore/editing/Editor.h
Source/WebCore/editing/cocoa/WebContentReaderCocoa.mm
Source/WebCore/html/AttachmentTypes.h
Source/WebCore/html/HTMLAttachmentElement.cpp
Source/WebCore/html/HTMLAttachmentElement.h
Source/WebCore/page/DragClient.h
Source/WebCore/page/DragController.cpp
Source/WebCore/page/DragController.h
Source/WebCore/page/EditorClient.h
Source/WebCore/platform/DragItem.h
Source/WebCore/platform/PromisedAttachmentInfo.h [moved from Source/WebCore/platform/PromisedBlobInfo.h with 79% similarity]
Source/WebKit/ChangeLog
Source/WebKit/PlatformMac.cmake
Source/WebKit/Scripts/webkit/messages.py
Source/WebKit/Shared/Cocoa/APIObject.mm
Source/WebKit/Shared/WebCoreArgumentCoders.cpp
Source/WebKit/Shared/WebCoreArgumentCoders.h
Source/WebKit/Sources.txt
Source/WebKit/SourcesCocoa.txt
Source/WebKit/UIProcess/API/APIAttachment.cpp
Source/WebKit/UIProcess/API/APIAttachment.h
Source/WebKit/UIProcess/API/Cocoa/WKUIDelegatePrivate.h
Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm
Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivate.h
Source/WebKit/UIProcess/API/Cocoa/_WKAttachment.h
Source/WebKit/UIProcess/API/Cocoa/_WKAttachment.mm
Source/WebKit/UIProcess/Cocoa/PageClientImplCocoa.mm
Source/WebKit/UIProcess/Cocoa/WebPageProxyCocoa.mm
Source/WebKit/UIProcess/WebPageProxy.cpp
Source/WebKit/UIProcess/WebPageProxy.h
Source/WebKit/UIProcess/WebPageProxy.messages.in
Source/WebKit/UIProcess/ios/PageClientImplIOS.h
Source/WebKit/UIProcess/ios/PageClientImplIOS.mm
Source/WebKit/UIProcess/ios/WKContentViewInteraction.h
Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm
Source/WebKit/UIProcess/mac/PageClientImplMac.h
Source/WebKit/UIProcess/mac/PageClientImplMac.mm
Source/WebKit/WebProcess/WebCoreSupport/WebEditorClient.cpp
Source/WebKit/WebProcess/WebCoreSupport/WebEditorClient.h
Source/WebKit/WebProcess/WebPage/WebPage.cpp
Source/WebKit/WebProcess/WebPage/WebPage.h
Source/WebKit/WebProcess/WebPage/WebPage.messages.in
Source/WebKitLegacy/win/ChangeLog
Source/WebKitLegacy/win/WebCoreSupport/WebDragClient.cpp
Tools/ChangeLog
Tools/TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm
Tools/TestWebKitAPI/ios/DragAndDropSimulatorIOS.mm

index 2ff9090..fa04528 100644 (file)
@@ -1,3 +1,124 @@
+2018-08-21  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [Attachment Support] Augment _WKAttachment SPI to handle NSFileWrappers in addition to NSData
+        https://bugs.webkit.org/show_bug.cgi?id=188496
+        <rdar://problem/43216836>
+
+        Reviewed by Tim Horton.
+
+        Refactors logic around HTMLAttachmentElement and pasteboard reading helpers, in support of moving the data
+        backing for attachment elements to the client layer, instead of keeping it in the attachment element's File.
+        Augmented existing API tests in WKAttachmentTests, and also added a new API test (see Tools for more detail).
+
+        * WebCore.xcodeproj/project.pbxproj:
+        * dom/Document.cpp:
+        (WebCore::Document::didInsertAttachmentElement):
+
+        Notify the client layer when a newly inserted attachment element's identifier has been updated to avoid
+        colliding with the identifier of an existing attachment element. This can happen if, for instance, one or more
+        attachments are copied and pasted within the same document.
+
+        * editing/Editor.cpp:
+        (WebCore::Editor::registerAttachmentIdentifier):
+        (WebCore::Editor::cloneAttachmentData):
+
+        Add new helper functions to notify the client when the attachment identifier to data mapping needs to be
+        updated. This can happen in three ways: (1) an attachment is created with raw data, or (2) an attachment is
+        created with a file path, or (3) the unique identifier for an attachment element has been reassigned. These
+        correspond to the two versions of `registerAttachmentIdentifier`, and `cloneAttachmentData`, respectively.
+
+        (WebCore::EditorClient::supportsClientSideAttachmentData const):
+
+        Add a new EditorClient hook to determine whether client-side attachment data management is supported. Currently,
+        this only returns true for WebKit2. If this flag is set to true, we register attachment identifiers and don't
+        create a new File object for the attachment element; otherwise, fall back to creating and setting a File for the
+        new attachment element.
+
+        (WebCore::Editor::insertAttachment):
+        (WebCore::Editor::insertAttachmentFromFile): Deleted.
+
+        Adjust logic when inserting an attachment; we now only need to update the attributes of the attachment element
+        with metadata from the client layer.
+
+        * editing/Editor.h:
+        * editing/cocoa/WebContentReaderCocoa.mm:
+        (WebCore::supportsClientSideAttachmentData):
+        (WebCore::createFragmentForImageAttachment):
+
+        Notify the client when creating an attachment element from image data on the pasteboard.
+
+        (WebCore::replaceRichContentWithAttachments):
+
+        Refactor this helper function to no longer connect attachment elements to File objects created from
+        subresources. Instead, just update each attachment element's attributes using information about the subresource,
+        and then notify the client about the new attachment data and attachment identifier.
+
+        (WebCore::createFragmentAndAddResources):
+        (WebCore::sanitizeMarkupWithArchive):
+
+        Plumb the current WebContentReader's Frame& to each of these helpers, so that it can call out to the client.
+
+        (WebCore::WebContentReader::readWebArchive):
+        (WebCore::WebContentMarkupReader::readWebArchive):
+        (WebCore::WebContentReader::readImage):
+        (WebCore::WebContentReader::readFilePaths):
+
+        Notify the client when creating an attachment from the path of a dropped file.
+
+        * html/AttachmentTypes.h:
+
+        Remove AttachmentInfo. This is no longer necessary because we don't need to request attachment data from the web
+        process anymore.
+
+        * html/HTMLAttachmentElement.cpp:
+        (WebCore::HTMLAttachmentElement::ensureUniqueIdentifier):
+
+        Add a helper function on the attachment element to create and return a unique identifier if needed.
+
+        (WebCore::HTMLAttachmentElement::updateAttributes):
+
+        Add a helper method to update the displayed element attributes (type, title and subtitle) or an attachment.
+
+        (WebCore::AttachmentDataReader::create): Deleted.
+        (WebCore::AttachmentDataReader::AttachmentDataReader): Deleted.
+
+        Remove AttachmentDataReader. This helper class was only used to load attachment data when requesting attachment
+        information in the client, but this is now obviated by moving attachment data to the client layer.
+
+        (WebCore::HTMLAttachmentElement::updateFileWithData): Deleted.
+        (WebCore::HTMLAttachmentElement::requestInfo): Deleted.
+        (WebCore::HTMLAttachmentElement::destroyReader): Deleted.
+        (WebCore::AttachmentDataReader::~AttachmentDataReader): Deleted.
+        (WebCore::AttachmentDataReader::didFinishLoading): Deleted.
+        (WebCore::AttachmentDataReader::didFail): Deleted.
+        (WebCore::AttachmentDataReader::invokeCallbackAndFinishReading): Deleted.
+        * html/HTMLAttachmentElement.h:
+        * page/DragClient.h:
+        * page/DragController.cpp:
+        (WebCore::DragController::startDrag):
+        (WebCore::DragController::doSystemDrag):
+        (WebCore::DragController::promisedAttachmentInfo):
+
+        Allow dragging an attachment (even if it does not have a file) as long as it has a unique identifier and a
+        content type.
+
+        (WebCore::DragController::promisedBlobInfo): Deleted.
+        * page/DragController.h:
+        * page/EditorClient.h:
+        (WebCore::EditorClient::registerAttachmentIdentifier):
+        (WebCore::EditorClient::cloneAttachmentData):
+        * platform/DragItem.h:
+        (WebCore::DragItem::encode const):
+        (WebCore::DragItem::decode):
+        * platform/PromisedAttachmentInfo.h: Renamed from Source/WebCore/platform/PromisedBlobInfo.h.
+
+        Add an attachment identifier to PromisedBlobInfo. Additionally, rename PromisedBlobInfo to
+        PromisedAttachmentInfo, since it is currently exclusively used to attachment element data to the pasteboard.
+        In the future, this could be renamed to something more general (e.g. PromisedPasteboardData), should we use this
+        mechanism to write data from other sources to the pasteboard.
+
+        (WebCore::PromisedAttachmentInfo::operator bool const):
+
 2018-08-21  Brent Fulgham  <bfulgham@apple.com>
 
         Remove experimental affiliated domain code now that StorageAccess API is available
index ee295f9..ca191d5 100644 (file)
                F478755419983AFF0024A287 /* ScrollSnapAnimatorState.h in Headers */ = {isa = PBXBuildFile; fileRef = F478755219983AFF0024A287 /* ScrollSnapAnimatorState.h */; settings = {ATTRIBUTES = (Private, ); }; };
                F47A09D120A93A9700240FAE /* DisabledAdaptations.h in Headers */ = {isa = PBXBuildFile; fileRef = F47A09CF20A939F600240FAE /* DisabledAdaptations.h */; settings = {ATTRIBUTES = (Private, ); }; };
                F47A5E3E195B8C8A00483100 /* StyleScrollSnapPoints.h in Headers */ = {isa = PBXBuildFile; fileRef = F47A5E3B195B8C8A00483100 /* StyleScrollSnapPoints.h */; settings = {ATTRIBUTES = (Private, ); }; };
-               F47A633D1FF6FD500081B3CC /* PromisedBlobInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = F47A633C1FF6FD500081B3CC /* PromisedBlobInfo.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               F47A633D1FF6FD500081B3CC /* PromisedAttachmentInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = F47A633C1FF6FD500081B3CC /* PromisedAttachmentInfo.h */; settings = {ATTRIBUTES = (Private, ); }; };
                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, ); }; };
                F47A09D420A9DD0400240FAE /* DisabledAdaptations.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DisabledAdaptations.cpp; sourceTree = "<group>"; };
                F47A5E3A195B8C8A00483100 /* StyleScrollSnapPoints.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StyleScrollSnapPoints.cpp; sourceTree = "<group>"; };
                F47A5E3B195B8C8A00483100 /* StyleScrollSnapPoints.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StyleScrollSnapPoints.h; sourceTree = "<group>"; };
-               F47A633C1FF6FD500081B3CC /* PromisedBlobInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PromisedBlobInfo.h; sourceTree = "<group>"; };
+               F47A633C1FF6FD500081B3CC /* PromisedAttachmentInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PromisedAttachmentInfo.h; sourceTree = "<group>"; };
                F482230E1E3869B80066FC79 /* WebItemProviderPasteboard.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WebItemProviderPasteboard.mm; sourceTree = "<group>"; };
                F482230F1E3869B80066FC79 /* WebItemProviderPasteboard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebItemProviderPasteboard.h; sourceTree = "<group>"; };
                F48223121E386E240066FC79 /* AbstractPasteboard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AbstractPasteboard.h; sourceTree = "<group>"; };
                                BC3BE12A0E98092F00835588 /* PopupMenuStyle.h */,
                                51F645D21FECDBC800B54DED /* Process.cpp */,
                                51F645D31FECDBC800B54DED /* Process.h */,
-                               F47A633C1FF6FD500081B3CC /* PromisedBlobInfo.h */,
+                               F47A633C1FF6FD500081B3CC /* PromisedAttachmentInfo.h */,
                                0081FEFD16B0A244008AAA7A /* PublicSuffix.h */,
                                5C97A3361F5F7A6500105207 /* RectEdges.h */,
                                CA1635DC2072E76900E7D2CE /* ReferrerPolicy.cpp */,
                                A715E653134BBBEC00D8E713 /* ProgressShadowElement.h in Headers */,
                                1A2A68240B5BEDE70002A480 /* ProgressTracker.h in Headers */,
                                1ACADD791880D91C00D8B71D /* ProgressTrackerClient.h in Headers */,
-                               F47A633D1FF6FD500081B3CC /* PromisedBlobInfo.h in Headers */,
+                               F47A633D1FF6FD500081B3CC /* PromisedAttachmentInfo.h in Headers */,
                                A578F4351DE00EEB003DFC6A /* PromiseRejectionEvent.h in Headers */,
                                E4BBED0F14F4025D003F0B98 /* PropertySetCSSStyleDeclaration.h in Headers */,
                                37BAAE581980D1DD005DFE71 /* ProtectionSpace.h in Headers */,
index 39b5342..da1acf5 100644 (file)
@@ -7944,15 +7944,21 @@ Vector<RefPtr<WebAnimation>> Document::getAnimations()
 void Document::didInsertAttachmentElement(HTMLAttachmentElement& attachment)
 {
     auto identifier = attachment.uniqueIdentifier();
-    if (identifier.isEmpty() || m_attachmentIdentifierToElementMap.contains(identifier)) {
+    auto previousIdentifier = identifier;
+    bool previousIdentifierIsNotUnique = !previousIdentifier.isEmpty() && m_attachmentIdentifierToElementMap.contains(previousIdentifier);
+    if (identifier.isEmpty() || previousIdentifierIsNotUnique) {
+        previousIdentifier = identifier;
         identifier = createCanonicalUUIDString();
         attachment.setUniqueIdentifier(identifier);
     }
 
     m_attachmentIdentifierToElementMap.set(identifier, attachment);
 
-    if (frame())
-        frame()->editor().didInsertAttachmentElement(attachment);
+    if (auto* frame = this->frame()) {
+        if (previousIdentifierIsNotUnique)
+            frame->editor().cloneAttachmentData(previousIdentifier, identifier);
+        frame->editor().didInsertAttachmentElement(attachment);
+    }
 }
 
 void Document::didRemoveAttachmentElement(HTMLAttachmentElement& attachment)
index bd57eca..514e25e 100644 (file)
@@ -3818,6 +3818,24 @@ void Editor::scheduleEditorUIUpdate()
 
 #if ENABLE(ATTACHMENT_ELEMENT)
 
+void Editor::registerAttachmentIdentifier(const String& identifier, const String& contentType, const String& preferredFileName, Ref<SharedBuffer>&& data)
+{
+    if (auto* client = this->client())
+        client->registerAttachmentIdentifier(identifier, contentType, preferredFileName, WTFMove(data));
+}
+
+void Editor::registerAttachmentIdentifier(const String& identifier, const String& contentType, const String& filePath)
+{
+    if (auto* client = this->client())
+        client->registerAttachmentIdentifier(identifier, contentType, filePath);
+}
+
+void Editor::cloneAttachmentData(const String& fromIdentifier, const String& toIdentifier)
+{
+    if (auto* client = this->client())
+        client->cloneAttachmentData(fromIdentifier, toIdentifier);
+}
+
 void Editor::didInsertAttachmentElement(HTMLAttachmentElement& attachment)
 {
     auto identifier = attachment.uniqueIdentifier();
@@ -3862,28 +3880,11 @@ void Editor::notifyClientOfAttachmentUpdates()
     }
 }
 
-void Editor::insertAttachment(const String& identifier, const AttachmentDisplayOptions& options, const String& filename, const String& filepath, std::optional<String> contentType)
-{
-    if (!contentType)
-        contentType = File::contentTypeForFile(filename);
-    insertAttachmentFromFile(identifier, options, filename, *contentType, File::create(filepath));
-}
-
-void Editor::insertAttachment(const String& identifier, const AttachmentDisplayOptions& options, const String& filename, Ref<SharedBuffer>&& data, std::optional<String> contentType)
-{
-    if (!contentType)
-        contentType = File::contentTypeForFile(filename);
-    insertAttachmentFromFile(identifier, options, filename, *contentType, File::create(Blob::create(WTFMove(data), *contentType), filename));
-}
-
-void Editor::insertAttachmentFromFile(const String& identifier, const AttachmentDisplayOptions&, const String& filename, const String& contentType, Ref<File>&& file)
+void Editor::insertAttachment(const String& identifier, const AttachmentDisplayOptions&, uint64_t fileSize, const String& fileName, std::optional<String>&& explicitContentType)
 {
     auto attachment = HTMLAttachmentElement::create(HTMLNames::attachmentTag, document());
-    attachment->setAttribute(HTMLNames::titleAttr, filename);
-    attachment->setAttribute(HTMLNames::subtitleAttr, fileSizeDescription(file->size()));
-    attachment->setAttribute(HTMLNames::typeAttr, contentType);
     attachment->setUniqueIdentifier(identifier);
-    attachment->setFile(WTFMove(file));
+    attachment->updateAttributes(fileSize, WTFMove(explicitContentType), fileName);
 
     auto fragmentToInsert = document().createDocumentFragment();
     fragmentToInsert->appendChild(attachment.get());
index b237795..deef53c 100644 (file)
@@ -503,8 +503,10 @@ public:
     bool isGettingDictionaryPopupInfo() const { return m_isGettingDictionaryPopupInfo; }
 
 #if ENABLE(ATTACHMENT_ELEMENT)
-    WEBCORE_EXPORT void insertAttachment(const String& identifier, const AttachmentDisplayOptions&, const String& filename, const String& filepath, std::optional<String> contentType = std::nullopt);
-    WEBCORE_EXPORT void insertAttachment(const String& identifier, const AttachmentDisplayOptions&, const String& filename, Ref<SharedBuffer>&& data, std::optional<String> contentType = std::nullopt);
+    WEBCORE_EXPORT void insertAttachment(const String& identifier, const AttachmentDisplayOptions&, uint64_t fileSize, const String& fileName, std::optional<String>&& explicitContentType = std::nullopt);
+    void registerAttachmentIdentifier(const String&, const String& /* contentType */, const String& /* preferredFileName */, Ref<SharedBuffer>&&);
+    void registerAttachmentIdentifier(const String&, const String& /* contentType */, const String& /* filePath */);
+    void cloneAttachmentData(const String& fromIdentifier, const String& toIdentifier);
     void didInsertAttachmentElement(HTMLAttachmentElement&);
     void didRemoveAttachmentElement(HTMLAttachmentElement&);
 
@@ -516,10 +518,6 @@ public:
 private:
     Document& document() const;
 
-#if ENABLE(ATTACHMENT_ELEMENT)
-    void insertAttachmentFromFile(const String& identifier, const AttachmentDisplayOptions&, const String& filename, const String& contentType, Ref<File>&&);
-#endif
-
     bool canDeleteRange(Range*) const;
     bool canSmartReplaceWithPasteboard(Pasteboard&);
     void pasteAsPlainTextWithPasteboard(Pasteboard&);
index 1cb2b1c..b0cb796 100644 (file)
@@ -35,6 +35,7 @@
 #import "DocumentFragment.h"
 #import "DocumentLoader.h"
 #import "File.h"
+#import "FileSystem.h"
 #import "Frame.h"
 #import "FrameLoader.h"
 #import "FrameLoaderClient.h"
@@ -204,11 +205,30 @@ static bool shouldReplaceRichContentWithAttachments()
 #endif
 }
 
-static Ref<DocumentFragment> createFragmentForImageAttachment(Document& document, Ref<Blob>&& blob)
+#if ENABLE(ATTACHMENT_ELEMENT)
+
+static bool supportsClientSideAttachmentData(const Frame& frame)
+{
+    if (auto* client = frame.editor().client())
+        return client->supportsClientSideAttachmentData();
+
+    return false;
+}
+
+#endif
+
+static Ref<DocumentFragment> createFragmentForImageAttachment(Frame& frame, Document& document, Ref<SharedBuffer>&& buffer, const String& contentType)
 {
 #if ENABLE(ATTACHMENT_ELEMENT)
     auto attachment = HTMLAttachmentElement::create(HTMLNames::attachmentTag, document);
-    attachment->setFile(File::create(blob, AtomicString("image")), HTMLAttachmentElement::UpdateDisplayAttributes::Yes);
+    // FIXME: This fallback image name needs to be a localized string.
+    String defaultImageAttachmentName { "image"_s };
+
+    if (supportsClientSideAttachmentData(frame)) {
+        attachment->updateAttributes(buffer->size(), contentType, defaultImageAttachmentName);
+        frame.editor().registerAttachmentIdentifier(attachment->ensureUniqueIdentifier(), contentType, defaultImageAttachmentName, WTFMove(buffer));
+    } else
+        attachment->setFile(File::create(Blob::create(buffer.get(), contentType), defaultImageAttachmentName), HTMLAttachmentElement::UpdateDisplayAttributes::Yes);
 
     auto fragment = document.createDocumentFragment();
     fragment->appendChild(attachment);
@@ -220,11 +240,13 @@ static Ref<DocumentFragment> createFragmentForImageAttachment(Document& document
 #endif
 }
 
-static void replaceRichContentWithAttachments(DocumentFragment& fragment, const Vector<Ref<ArchiveResource>>& subresources)
+static void replaceRichContentWithAttachments(Frame& frame, DocumentFragment& fragment, const Vector<Ref<ArchiveResource>>& subresources)
 {
 #if ENABLE(ATTACHMENT_ELEMENT)
     struct AttachmentReplacementInfo {
-        Ref<File> file;
+        String fileName;
+        String contentType;
+        Ref<SharedBuffer> data;
         Ref<Element> elementToReplace;
     };
 
@@ -233,11 +255,11 @@ static void replaceRichContentWithAttachments(DocumentFragment& fragment, const
         return;
 
     // FIXME: Handle resources in subframe archives.
-    HashMap<AtomicString, Ref<Blob>> urlToBlobMap;
-    for (const Ref<ArchiveResource>& subresource : subresources) {
+    HashMap<AtomicString, Ref<ArchiveResource>> urlToResourceMap;
+    for (auto& subresource : subresources) {
         auto& url = subresource->url();
         if (shouldReplaceSubresourceURL(url))
-            urlToBlobMap.set(url.string(), Blob::create(subresource->data(), subresource->mimeType()));
+            urlToResourceMap.set(url.string(), subresource.copyRef());
     }
 
     Vector<Ref<Element>> elementsToRemove;
@@ -247,15 +269,15 @@ static void replaceRichContentWithAttachments(DocumentFragment& fragment, const
         if (resourceURLString.isEmpty())
             continue;
 
-        auto blob = urlToBlobMap.get(resourceURLString);
-        if (!blob)
+        auto resource = urlToResourceMap.find(resourceURLString);
+        if (resource == urlToResourceMap.end())
             continue;
 
-        auto title = URLParser { resourceURLString }.result().lastPathComponent();
-        if (title.isEmpty())
-            title = AtomicString("media");
+        auto name = URLParser { resourceURLString }.result().lastPathComponent();
+        if (name.isEmpty())
+            name = AtomicString("media");
 
-        attachmentReplacementInfo.append({ File::create(*blob, title), image });
+        attachmentReplacementInfo.append({ name, resource->value->mimeType(), resource->value->data(), image });
     }
 
     for (auto& object : descendantsOfType<HTMLObjectElement>(fragment)) {
@@ -265,28 +287,30 @@ static void replaceRichContentWithAttachments(DocumentFragment& fragment, const
             continue;
         }
 
-        auto blob = urlToBlobMap.get(resourceURLString);
-        if (!blob) {
-            elementsToRemove.append(object);
+        auto resource = urlToResourceMap.find(resourceURLString);
+        if (resource == urlToResourceMap.end())
             continue;
-        }
 
-        auto title = URLParser { resourceURLString }.result().lastPathComponent();
-        if (title.isEmpty())
-            title = AtomicString("file");
+        auto name = URLParser { resourceURLString }.result().lastPathComponent();
+        if (name.isEmpty())
+            name = AtomicString("file");
 
-        attachmentReplacementInfo.append({ File::create(*blob, title), object });
+        attachmentReplacementInfo.append({ name, resource->value->mimeType(), resource->value->data(), object });
     }
 
     for (auto& info : attachmentReplacementInfo) {
-        auto file = WTFMove(info.file);
         auto elementToReplace = WTFMove(info.elementToReplace);
         auto parent = makeRefPtr(elementToReplace->parentNode());
         if (!parent)
             continue;
 
         auto attachment = HTMLAttachmentElement::create(HTMLNames::attachmentTag, fragment.document());
-        attachment->setFile(WTFMove(file), HTMLAttachmentElement::UpdateDisplayAttributes::Yes);
+        if (supportsClientSideAttachmentData(frame)) {
+            attachment->updateAttributes(info.data->size(), info.contentType, info.fileName);
+            frame.editor().registerAttachmentIdentifier(attachment->ensureUniqueIdentifier(), WTFMove(info.contentType), WTFMove(info.fileName), WTFMove(info.data));
+        } else
+            attachment->setFile(File::create(Blob::create(info.data, info.contentType), info.fileName), HTMLAttachmentElement::UpdateDisplayAttributes::Yes);
+
         parent->replaceChild(attachment, elementToReplace);
     }
 
@@ -349,7 +373,7 @@ RefPtr<DocumentFragment> createFragmentAndAddResources(Frame& frame, NSAttribute
     replaceSubresourceURLsWithURLsFromClient(*fragmentAndResources.fragment, fragmentAndResources.resources, unreplacedResources);
 
     if (shouldReplaceRichContentWithAttachments()) {
-        replaceRichContentWithAttachments(*fragmentAndResources.fragment, unreplacedResources);
+        replaceRichContentWithAttachments(frame, *fragmentAndResources.fragment, unreplacedResources);
         return WTFMove(fragmentAndResources.fragment);
     }
 
@@ -387,7 +411,7 @@ static std::optional<MarkupAndArchive> extractMarkupAndArchive(SharedBuffer& buf
     return MarkupAndArchive { String::fromUTF8(mainResource->data().data(), mainResource->data().size()), mainResource.releaseNonNull(), archive.releaseNonNull() };
 }
 
-static String sanitizeMarkupWithArchive(Document& destinationDocument, MarkupAndArchive& markupAndArchive, MSOListQuirks msoListQuirks, const std::function<bool(const String)>& canShowMIMETypeAsHTML)
+static String sanitizeMarkupWithArchive(Frame& frame, Document& destinationDocument, MarkupAndArchive& markupAndArchive, MSOListQuirks msoListQuirks, const std::function<bool(const String)>& canShowMIMETypeAsHTML)
 {
     auto page = createPageForSanitizingWebContent();
     Document* stagingDocument = page->mainFrame().document();
@@ -398,7 +422,7 @@ static String sanitizeMarkupWithArchive(Document& destinationDocument, MarkupAnd
     replaceSubresourceURLsWithURLsFromClient(fragment, markupAndArchive.archive->subresources(), unreplacedResources);
 
     if (shouldReplaceRichContentWithAttachments()) {
-        replaceRichContentWithAttachments(fragment, unreplacedResources);
+        replaceRichContentWithAttachments(frame, fragment, unreplacedResources);
         return sanitizedMarkupForFragmentInDocument(WTFMove(fragment), *stagingDocument, msoListQuirks, markupAndArchive.markup);
     }
 
@@ -428,7 +452,7 @@ static String sanitizeMarkupWithArchive(Document& destinationDocument, MarkupAnd
 
         MarkupAndArchive subframeContent = { String::fromUTF8(subframeMainResource->data().data(), subframeMainResource->data().size()),
             subframeMainResource.releaseNonNull(), subframeArchive.copyRef() };
-        auto subframeMarkup = sanitizeMarkupWithArchive(destinationDocument, subframeContent, MSOListQuirks::Disabled, canShowMIMETypeAsHTML);
+        auto subframeMarkup = sanitizeMarkupWithArchive(frame, destinationDocument, subframeContent, MSOListQuirks::Disabled, canShowMIMETypeAsHTML);
 
         CString utf8 = subframeMarkup.utf8();
         Vector<uint8_t> blobBuffer;
@@ -469,7 +493,7 @@ bool WebContentReader::readWebArchive(SharedBuffer& buffer)
         return true;
     }
 
-    String sanitizedMarkup = sanitizeMarkupWithArchive(*frame.document(), *result, msoListQuirksForMarkup(), [&] (const String& type) {
+    String sanitizedMarkup = sanitizeMarkupWithArchive(frame, *frame.document(), *result, msoListQuirksForMarkup(), [&] (const String& type) {
         return frame.loader().client().canShowMIMETypeAsHTML(type);
     });
     fragment = createFragmentFromMarkup(*frame.document(), sanitizedMarkup, blankURL(), DisallowScriptingAndPluginContent);
@@ -496,7 +520,7 @@ bool WebContentMarkupReader::readWebArchive(SharedBuffer& buffer)
         return true;
     }
 
-    markup = sanitizeMarkupWithArchive(*frame.document(), *result, msoListQuirksForMarkup(), [&] (const String& type) {
+    markup = sanitizeMarkupWithArchive(frame, *frame.document(), *result, msoListQuirksForMarkup(), [&] (const String& type) {
         return frame.loader().client().canShowMIMETypeAsHTML(type);
     });
 
@@ -635,11 +659,10 @@ bool WebContentReader::readImage(Ref<SharedBuffer>&& buffer, const String& type)
         return true;
     }
 
-    auto blob = Blob::create(buffer.get(), type);
     if (shouldReplaceRichContentWithAttachments())
-        addFragment(createFragmentForImageAttachment(document, WTFMove(blob)));
+        addFragment(createFragmentForImageAttachment(frame, document, WTFMove(buffer), type));
     else
-        addFragment(createFragmentForImageAndURL(document, DOMURL::createObjectURL(document, blob)));
+        addFragment(createFragmentForImageAndURL(document, DOMURL::createObjectURL(document, Blob::create(buffer.get(), type))));
 
     return fragment;
 }
@@ -657,7 +680,15 @@ bool WebContentReader::readFilePaths(const Vector<String>& paths)
     if (RuntimeEnabledFeatures::sharedFeatures().attachmentElementEnabled()) {
         for (auto& path : paths) {
             auto attachment = HTMLAttachmentElement::create(HTMLNames::attachmentTag, document);
-            attachment->setFile(File::create(path), HTMLAttachmentElement::UpdateDisplayAttributes::Yes);
+            if (supportsClientSideAttachmentData(frame)) {
+                long long fileSize { 0 };
+                FileSystem::getFileSize(path, fileSize);
+                auto contentType = File::contentTypeForFile(path);
+                attachment->updateAttributes(fileSize, contentType, FileSystem::pathGetFileName(path));
+                frame.editor().registerAttachmentIdentifier(attachment->ensureUniqueIdentifier(), contentType, path);
+            } else
+                attachment->setFile(File::create(path), HTMLAttachmentElement::UpdateDisplayAttributes::Yes);
+
             fragment->appendChild(attachment);
         }
     }
index b5077ca..89bbcab 100644 (file)
@@ -32,8 +32,6 @@
 
 namespace WebCore {
 
-class SharedBuffer;
-
 struct AttachmentDisplayOptions {
     template<class Encoder> void encode(Encoder&) const;
     template<class Decoder> static std::optional<AttachmentDisplayOptions> decode(Decoder&);
@@ -50,13 +48,6 @@ template<class Decoder> inline std::optional<AttachmentDisplayOptions> Attachmen
     return AttachmentDisplayOptions();
 }
 
-struct AttachmentInfo {
-    String contentType;
-    String name;
-    String filePath;
-    RefPtr<SharedBuffer> data;
-};
-
 } // namespace WebCore
 
 #endif // ENABLE(ATTACHMENT_ELEMENT)
index e1719cd..405cb3e 100644 (file)
 #include "Document.h"
 #include "Editor.h"
 #include "File.h"
-#include "FileReaderLoader.h"
-#include "FileReaderLoaderClient.h"
 #include "Frame.h"
 #include "HTMLNames.h"
 #include "RenderAttachment.h"
 #include "SharedBuffer.h"
 #include <pal/FileSizeFormatter.h>
 #include <wtf/IsoMallocInlines.h>
+#include <wtf/UUID.h>
 
 namespace WebCore {
 
@@ -47,36 +46,6 @@ WTF_MAKE_ISO_ALLOCATED_IMPL(HTMLAttachmentElement);
 
 using namespace HTMLNames;
 
-class AttachmentDataReader : public FileReaderLoaderClient {
-public:
-    static std::unique_ptr<AttachmentDataReader> create(HTMLAttachmentElement& attachment, Function<void(RefPtr<SharedBuffer>&&)>&& callback)
-    {
-        return std::make_unique<AttachmentDataReader>(attachment, WTFMove(callback));
-    }
-
-    AttachmentDataReader(HTMLAttachmentElement& attachment, Function<void(RefPtr<SharedBuffer>&&)>&& callback)
-        : m_attachment(attachment)
-        , m_callback(std::make_unique<Function<void(RefPtr<SharedBuffer>&&)>>(WTFMove(callback)))
-        , m_loader(std::make_unique<FileReaderLoader>(FileReaderLoader::ReadType::ReadAsArrayBuffer, this))
-    {
-        m_loader->start(&attachment.document(), *attachment.file());
-    }
-
-    ~AttachmentDataReader();
-
-private:
-    void didStartLoading() final { }
-    void didReceiveData() final { }
-    void didFinishLoading() final;
-    void didFail(int error) final;
-
-    void invokeCallbackAndFinishReading(RefPtr<SharedBuffer>&&);
-
-    HTMLAttachmentElement& m_attachment;
-    std::unique_ptr<Function<void(RefPtr<SharedBuffer>&&)>> m_callback;
-    std::unique_ptr<FileReaderLoader> m_loader;
-};
-
 HTMLAttachmentElement::HTMLAttachmentElement(const QualifiedName& tagName, Document& document)
     : HTMLElement(tagName, document)
 {
@@ -140,6 +109,13 @@ void HTMLAttachmentElement::removedFromAncestor(RemovalType type, ContainerNode&
         document().didRemoveAttachmentElement(*this);
 }
 
+String HTMLAttachmentElement::ensureUniqueIdentifier()
+{
+    if (m_uniqueIdentifier.isEmpty())
+        m_uniqueIdentifier = createCanonicalUUIDString();
+    return m_uniqueIdentifier;
+}
+
 void HTMLAttachmentElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
 {
     if (name == progressAttr || name == subtitleAttr || name == titleAttr || name == typeAttr) {
@@ -168,68 +144,17 @@ String HTMLAttachmentElement::attachmentPath() const
     return attributeWithoutSynchronization(webkitattachmentpathAttr);
 }
 
-void HTMLAttachmentElement::updateFileWithData(Ref<SharedBuffer>&& data, std::optional<String>&& newContentType, std::optional<String>&& newFilename)
-{
-    auto filename = newFilename ? *newFilename : attachmentTitle();
-    auto contentType = newContentType ? *newContentType : File::contentTypeForFile(filename);
-    auto file = File::create(Blob::create(WTFMove(data), contentType), filename);
-    setFile(WTFMove(file), UpdateDisplayAttributes::Yes);
-}
-
-void HTMLAttachmentElement::requestInfo(Function<void(const AttachmentInfo&)>&& callback)
-{
-    if (!m_file) {
-        callback({ });
-        return;
-    }
-
-    AttachmentInfo infoWithoutData { m_file->type(), m_file->name(), m_file->path(), nullptr };
-    if (!m_file->path().isEmpty()) {
-        callback(infoWithoutData);
-        return;
-    }
-
-    m_attachmentReaders.append(AttachmentDataReader::create(*this, [infoWithoutData = WTFMove(infoWithoutData), protectedFile = makeRef(*m_file), callback = WTFMove(callback)] (RefPtr<SharedBuffer>&& data) {
-        callback({ infoWithoutData.contentType, infoWithoutData.name, infoWithoutData.filePath, WTFMove(data) });
-    }));
-}
-
-void HTMLAttachmentElement::destroyReader(AttachmentDataReader& finishedReader)
+void HTMLAttachmentElement::updateAttributes(uint64_t fileSize, std::optional<String>&& newContentType, std::optional<String>&& newFilename)
 {
-    m_attachmentReaders.removeFirstMatching([&] (const std::unique_ptr<AttachmentDataReader>& reader) -> bool {
-        return reader.get() == &finishedReader;
-    });
-}
+    if (newFilename)
+        setAttributeWithoutSynchronization(HTMLNames::titleAttr, *newFilename);
 
-AttachmentDataReader::~AttachmentDataReader()
-{
-    invokeCallbackAndFinishReading(nullptr);
-}
+    if (newContentType)
+        setAttributeWithoutSynchronization(HTMLNames::typeAttr, *newContentType);
 
-void AttachmentDataReader::didFinishLoading()
-{
-    if (auto arrayBuffer = m_loader->arrayBufferResult())
-        invokeCallbackAndFinishReading(SharedBuffer::create(reinterpret_cast<uint8_t*>(arrayBuffer->data()), arrayBuffer->byteLength()));
-    else
-        invokeCallbackAndFinishReading(nullptr);
-    m_attachment.destroyReader(*this);
-}
-
-void AttachmentDataReader::didFail(int)
-{
-    invokeCallbackAndFinishReading(nullptr);
-    m_attachment.destroyReader(*this);
-}
-
-void AttachmentDataReader::invokeCallbackAndFinishReading(RefPtr<SharedBuffer>&& data)
-{
-    auto callback = WTFMove(m_callback);
-    if (!callback)
-        return;
-
-    m_loader->cancel();
-    m_loader = nullptr;
-    (*callback)(WTFMove(data));
+    setAttributeWithoutSynchronization(HTMLNames::subtitleAttr, fileSizeDescription(fileSize));
+    if (auto* renderer = this->renderer())
+        renderer->invalidate();
 }
 
 } // namespace WebCore
index 14fc84b..db806ac 100644 (file)
 
 namespace WebCore {
 
-class AttachmentDataReader;
 class File;
-class HTMLImageElement;
-class HTMLVideoElement;
 class RenderAttachment;
-class SharedBuffer;
 
 class HTMLAttachmentElement final : public HTMLElement {
     WTF_MAKE_ISO_ALLOCATED(HTMLAttachmentElement);
@@ -53,20 +49,19 @@ public:
     String uniqueIdentifier() const { return m_uniqueIdentifier; }
     void setUniqueIdentifier(const String& uniqueIdentifier) { m_uniqueIdentifier = uniqueIdentifier; }
 
-    WEBCORE_EXPORT void updateFileWithData(Ref<SharedBuffer>&& data, std::optional<String>&& newContentType = std::nullopt, std::optional<String>&& newFilename = std::nullopt);
+    WEBCORE_EXPORT void updateAttributes(uint64_t fileSize, std::optional<String>&& newContentType = std::nullopt, std::optional<String>&& newFilename = std::nullopt);
 
     InsertedIntoAncestorResult insertedIntoAncestor(InsertionType, ContainerNode&) final;
     void removedFromAncestor(RemovalType, ContainerNode&) final;
 
+    String ensureUniqueIdentifier();
+
     WEBCORE_EXPORT String attachmentTitle() const;
     String attachmentType() const;
     String attachmentPath() const;
 
     RenderAttachment* renderer() const;
 
-    WEBCORE_EXPORT void requestInfo(Function<void(const AttachmentInfo&)>&& callback);
-    void destroyReader(AttachmentDataReader&);
-
 private:
     HTMLAttachmentElement(const QualifiedName&, Document&);
     virtual ~HTMLAttachmentElement();
@@ -83,7 +78,6 @@ private:
     void parseAttribute(const QualifiedName&, const AtomicString&) final;
     
     RefPtr<File> m_file;
-    Vector<std::unique_ptr<AttachmentDataReader>> m_attachmentReaders;
     String m_uniqueIdentifier;
 };
 
index b74485d..3786068 100644 (file)
@@ -37,7 +37,7 @@ class DataTransfer;
 class Element;
 class Frame;
 class Image;
-struct PromisedBlobInfo;
+struct PromisedAttachmentInfo;
 
 class DragClient {
 public:
index 9f9bc32..d50e118 100644 (file)
@@ -69,7 +69,7 @@
 #include "PluginDocument.h"
 #include "PluginViewBase.h"
 #include "Position.h"
-#include "PromisedBlobInfo.h"
+#include "PromisedAttachmentInfo.h"
 #include "RenderAttachment.h"
 #include "RenderFileUploadControl.h"
 #include "RenderImage.h"
@@ -1136,11 +1136,11 @@ bool DragController::startDrag(Frame& src, const DragState& state, DragOperation
         auto previousSelection = src.selection().selection();
         selectElement(element);
 
-        PromisedBlobInfo promisedBlob;
+        PromisedAttachmentInfo promisedAttachment;
         if (hasData == HasNonDefaultPasteboardData::No) {
-            promisedBlob = promisedBlobInfo(src, attachment);
+            promisedAttachment = promisedAttachmentInfo(src, attachment);
             auto& editor = src.editor();
-            if (!promisedBlob && editor.client()) {
+            if (!promisedAttachment && editor.client()) {
 #if PLATFORM(COCOA)
                 // Otherwise, if no file URL is specified, call out to the injected bundle to populate the pasteboard with data.
                 editor.willWriteSelectionToPasteboard(src.selection().toNormalizedRange().get());
@@ -1164,7 +1164,7 @@ bool DragController::startDrag(Frame& src, const DragState& state, DragOperation
             dragLoc = dragLocForSelectionDrag(src);
             m_dragOffset = IntPoint(dragOrigin.x() - dragLoc.x(), dragOrigin.y() - dragLoc.y());
         }
-        doSystemDrag(WTFMove(dragImage), dragLoc, dragOrigin, src, state, WTFMove(promisedBlob));
+        doSystemDrag(WTFMove(dragImage), dragLoc, dragOrigin, src, state, WTFMove(promisedAttachment));
         if (!element.isContentRichlyEditable())
             src.selection().setSelection(previousSelection);
         src.editor().setIgnoreSelectionChanges(false);
@@ -1270,7 +1270,7 @@ void DragController::beginDrag(DragItem dragItem, Frame& frame, const IntPoint&
     cleanupAfterSystemDrag();
 }
 
-void DragController::doSystemDrag(DragImage image, const IntPoint& dragLoc, const IntPoint& eventPos, Frame& frame, const DragState& state, PromisedBlobInfo&& promisedBlob)
+void DragController::doSystemDrag(DragImage image, const IntPoint& dragLoc, const IntPoint& eventPos, Frame& frame, const DragState& state, PromisedAttachmentInfo&& promisedAttachmentInfo)
 {
     m_didInitiateDrag = true;
     m_dragInitiator = frame.document();
@@ -1281,7 +1281,7 @@ void DragController::doSystemDrag(DragImage image, const IntPoint& dragLoc, cons
     DragItem item;
     item.image = WTFMove(image);
     item.sourceAction = state.type;
-    item.promisedBlob = WTFMove(promisedBlob);
+    item.promisedAttachmentInfo = WTFMove(promisedAttachmentInfo);
 
     auto eventPositionInRootViewCoordinates = frame.view()->contentsToRootView(eventPos);
     auto dragLocationInRootViewCoordinates = frame.view()->contentsToRootView(dragLoc);
@@ -1367,11 +1367,8 @@ String DragController::platformContentTypeForBlobType(const String& type) const
 
 #if ENABLE(ATTACHMENT_ELEMENT)
 
-PromisedBlobInfo DragController::promisedBlobInfo(Frame& frame, HTMLAttachmentElement& attachment)
+PromisedAttachmentInfo DragController::promisedAttachmentInfo(Frame& frame, HTMLAttachmentElement& attachment)
 {
-    if (!attachment.file())
-        return { };
-
     Vector<String> additionalTypes;
     Vector<RefPtr<SharedBuffer>> additionalData;
 #if PLATFORM(COCOA)
@@ -1379,8 +1376,10 @@ PromisedBlobInfo DragController::promisedBlobInfo(Frame& frame, HTMLAttachmentEl
         frame.editor().getPasteboardTypesAndDataForAttachment(attachment, additionalTypes, additionalData);
 #endif
 
-    auto& file = *attachment.file();
-    return { file.url(), platformContentTypeForBlobType(file.type()), file.name(), WTFMove(additionalTypes), WTFMove(additionalData) };
+    if (auto* file = attachment.file())
+        return { file->url(), platformContentTypeForBlobType(file->type()), file->name(), attachment.uniqueIdentifier(), WTFMove(additionalTypes), WTFMove(additionalData) };
+
+    return { { }, platformContentTypeForBlobType(attachment.attachmentType()), attachment.attachmentTitle(), attachment.uniqueIdentifier(), WTFMove(additionalTypes), WTFMove(additionalData) };
 }
 
 #endif // ENABLE(ATTACHMENT_ELEMENT)
index 83e575d..22a6958 100644 (file)
@@ -48,7 +48,7 @@ class PlatformMouseEvent;
 
 struct DragItem;
 struct DragState;
-struct PromisedBlobInfo;
+struct PromisedAttachmentInfo;
 
     class DragController {
         WTF_MAKE_NONCOPYABLE(DragController); WTF_MAKE_FAST_ALLOCATED;
@@ -116,7 +116,7 @@ struct PromisedBlobInfo;
         bool shouldUseCachedImageForDragImage(const Image&) const;
 
         void doImageDrag(Element&, const IntPoint&, const IntRect&, Frame&, IntPoint&, const DragState&);
-        void doSystemDrag(DragImage, const IntPoint&, const IntPoint&, Frame&, const DragState&, PromisedBlobInfo&&);
+        void doSystemDrag(DragImage, const IntPoint&, const IntPoint&, Frame&, const DragState&, PromisedAttachmentInfo&&);
 
         void beginDrag(DragItem, Frame&, const IntPoint& mouseDownPoint, const IntPoint& mouseDraggedPoint, DataTransfer&, DragSourceAction);
 
@@ -135,7 +135,7 @@ struct PromisedBlobInfo;
         void declareAndWriteDragImage(DataTransfer&, Element&, const URL&, const String& label);
 
 #if ENABLE(ATTACHMENT_ELEMENT)
-        PromisedBlobInfo promisedBlobInfo(Frame&, HTMLAttachmentElement&);
+        PromisedAttachmentInfo promisedAttachmentInfo(Frame&, HTMLAttachmentElement&);
 #endif
         Page& m_page;
         DragClient& m_client;
index 36ad7be..8ffd736 100644 (file)
@@ -73,8 +73,12 @@ public:
     virtual bool shouldMoveRangeAfterDelete(Range*, Range*) = 0;
 
 #if ENABLE(ATTACHMENT_ELEMENT)
+    virtual void registerAttachmentIdentifier(const String& /* identifier */, const String& /* contentType */, const String& /* preferredFileName */, Ref<SharedBuffer>&&) { }
+    virtual void registerAttachmentIdentifier(const String& /* identifier */, const String& /* contentType */, const String& /* filePath */) { }
+    virtual void cloneAttachmentData(const String& /* fromIdentifier */, const String& /* toIdentifier */) { }
     virtual void didInsertAttachment(const String& /* identifier */, const String& /* source */) { }
     virtual void didRemoveAttachment(const String&) { }
+    virtual bool supportsClientSideAttachmentData() const { return false; }
 #endif
 
     virtual void didBeginEditing() = 0;
index 3eae743..5992bea 100644 (file)
@@ -31,7 +31,7 @@
 #include "IntPoint.h"
 #include "IntRect.h"
 #include "PasteboardWriterData.h"
-#include "PromisedBlobInfo.h"
+#include "PromisedAttachmentInfo.h"
 
 namespace WebCore {
 
@@ -50,7 +50,7 @@ struct DragItem final {
     IntRect dragPreviewFrameInRootViewCoordinates;
 
     PasteboardWriterData data;
-    PromisedBlobInfo promisedBlob;
+    PromisedAttachmentInfo promisedAttachmentInfo;
 
     template<class Encoder> void encode(Encoder&) const;
     template<class Decoder> static bool decode(Decoder&, DragItem&);
@@ -71,7 +71,7 @@ void DragItem::encode(Encoder& encoder) const
     encoder << hasVisiblePath;
     if (hasVisiblePath)
         encoder << image.visiblePath().value();
-    encoder << promisedBlob;
+    encoder << promisedAttachmentInfo;
 }
 
 template<class Decoder>
@@ -113,7 +113,7 @@ bool DragItem::decode(Decoder& decoder, DragItem& result)
             return false;
         result.image.setVisiblePath(*visiblePath);
     }
-    if (!decoder.decode(result.promisedBlob))
+    if (!decoder.decode(result.promisedAttachmentInfo))
         return false;
     return true;
 }
 
 #pragma once
 
-#include "SharedBuffer.h"
+#include <wtf/Forward.h>
+#include <wtf/RefPtr.h>
 
 namespace WebCore {
 
+class SharedBuffer;
 class URL;
 
-struct PromisedBlobInfo {
+struct PromisedAttachmentInfo {
     URL blobURL;
     String contentType;
     String filename;
 
+#if ENABLE(ATTACHMENT_ELEMENT)
+    String attachmentIdentifier;
+#endif
+
     Vector<String> additionalTypes;
     Vector<RefPtr<SharedBuffer>> additionalData;
 
-    operator bool() const { return !blobURL.isEmpty(); }
+    operator bool() const
+    {
+        if (contentType.isEmpty())
+            return false;
+
+#if ENABLE(ATTACHMENT_ELEMENT)
+        if (!attachmentIdentifier.isEmpty())
+            return true;
+#endif
+
+        return !blobURL.isEmpty();
+    }
 };
 
 } // namespace WebCore
index 1e9a303..157d886 100644 (file)
@@ -1,3 +1,198 @@
+2018-08-21  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [Attachment Support] Augment _WKAttachment SPI to handle NSFileWrappers in addition to NSData
+        https://bugs.webkit.org/show_bug.cgi?id=188496
+        <rdar://problem/43216836>
+
+        Reviewed by Tim Horton.
+
+        Adjusts WebKit's attachment editing SPI with the following modifications:
+        •   Deprecate `-_insertAttachmentWithFilename:contentType:data:options:completion:` in favor of
+            `-_insertAttachmentWithFileWrapper:contentType:options:completion:`.
+        •   Deprecate `-requestInfo:` and in favor of just `-info`.
+        •   Add a `-fileWrapper` property to `_WKAttachmentInfo` that returns an `NSFileWrapper`.
+        •   Remove some SPI methods that would otherwise be deprecated, but are not even necessary, since they're no
+            longer even used by Mail.
+
+        To make this possible, we refactor where and how attachment data is tracked. Currently, the data is stored in
+        the network process, and made accessible to the web process via blob URLs stored in the File object in the
+        attachment element. As such, requests from the UI process for attachment data would first be routed through the
+        web process to network process and back.
+
+        Instead, we now keep the relevant attachment data (in the form of NSFileWrapper on Cocoa platforms) in the UI
+        process, on API::Attachment. We additionally keep a map of attachment identifiers to API::Attachments, which
+        allows us to propagate the same _WKAttachment wrapper object to the SPI client for each uniquely identified
+        attachment element. This also has the benefit of allowing us to remove the asynchronous version of `-requestInfo:`
+        and replace it with just an `info` property.
+
+        Changes are covered by new and existing API tests.
+
+        * PlatformMac.cmake:
+
+        Remove APIAttachment.cpp, now that APIAttachment.cpp is listed in Sources.txt.
+
+        * Scripts/webkit/messages.py:
+        * Shared/Cocoa/APIObject.mm:
+        (API::Object::newObject):
+
+        Guard _WKAttachment creation with ENABLE_ATTACHMENT_ELEMENT.
+
+        * Shared/WebCoreArgumentCoders.cpp:
+        (IPC::ArgumentCoder<PromisedAttachmentInfo>::encode):
+        (IPC::ArgumentCoder<PromisedAttachmentInfo>::decode):
+        (IPC::ArgumentCoder<PromisedBlobInfo>::encode): Deleted.
+        (IPC::ArgumentCoder<PromisedBlobInfo>::decode): Deleted.
+        (IPC::ArgumentCoder<AttachmentInfo>::encode): Deleted.
+        (IPC::ArgumentCoder<AttachmentInfo>::decode): Deleted.
+        * Shared/WebCoreArgumentCoders.h:
+
+        Continue removing encoding support for WebCore::AttachmentInfo. Additionally, rename PromisedBlobInfo to
+        PromisedAttachmentInfo.
+
+        * Sources.txt:
+        * SourcesCocoa.txt:
+
+        Move APIAttachment.cpp from the SourcesCocoa.txt to the platform-agnostic Sources.txt.
+
+        * UIProcess/API/APIAttachment.cpp:
+        (API::Attachment::updateAttributes):
+
+        Rename setDataAndContentType to just updateAttributes; instead of sending data, only send the information needed
+        to update the presentational attributes of the attachment element.
+
+        (API::Attachment::requestInfo): Deleted.
+
+        Just call the completion handler with the result of `self.info`.
+
+        (API::Attachment::setDataAndContentType): Deleted.
+        * UIProcess/API/APIAttachment.h:
+
+        Add additional attributes: a content type, a file path, and (on Cocoa platforms) an NSFileWrapper.
+
+        * UIProcess/API/Cocoa/WKUIDelegatePrivate.h:
+
+        Remove -_webView:didInsertAttachment:, since this is unused by any client currently, and is superceded by
+        -_webView:didInsertAttachment:withSource:.
+
+        * UIProcess/API/Cocoa/WKWebView.mm:
+        (-[WKWebView _didInsertAttachment:withSource:]):
+        (-[WKWebView _didRemoveAttachment:]):
+
+        Look up the API::Attachment corresponding to the identifier, and send its wrapper object to the client.
+
+        (-[WKWebView _insertAttachmentWithFilename:contentType:data:options:completion:]):
+        (-[WKWebView _insertAttachmentWithFileWrapper:contentType:options:completion:]):
+
+        Add a way to insert an attachment using NSFileWrapper, and reimplement _insertAttachmentWithFilename: using it.
+
+        * UIProcess/API/Cocoa/WKWebViewPrivate.h:
+        * UIProcess/API/Cocoa/_WKAttachment.h:
+        * UIProcess/API/Cocoa/_WKAttachment.mm:
+        (-[_WKAttachmentInfo initWithFileWrapper:filePath:contentType:]):
+        (-[_WKAttachmentInfo data]):
+        (-[_WKAttachmentInfo name]):
+        (isDeclaredOrDynamicTypeIdentifier):
+        (-[_WKAttachmentInfo _typeIdentifierFromPathExtension]):
+        (-[_WKAttachmentInfo contentType]):
+        (-[_WKAttachmentInfo mimeType]):
+        (-[_WKAttachmentInfo utiType]):
+        (-[_WKAttachmentInfo fileWrapper]):
+        (-[_WKAttachment info]):
+        (-[_WKAttachment requestInfo:]):
+
+        Add a property on _WKAttachment to retrieve a _WKAttachmentInfo (a snapshot of the current state of the
+        attachment, along with the NSFileWrapper). Reimplement requestInfo using this property.
+
+        (-[_WKAttachment setFileWrapper:contentType:completion:]):
+        (-[_WKAttachment setData:newContentType:newFilename:completion:]):
+
+        Reimplemented by calling -setFileWrapper:contentType:completion: with an NSFileWrapper created using the given
+        data. Additionally, create and associate the unique identifier with an API::Attachment right away.
+
+        (-[_WKAttachment uniqueIdentifier]):
+        (-[_WKAttachment description]):
+        (-[_WKAttachmentInfo initWithInfo:]): Deleted.
+        (-[_WKAttachmentInfo fileLoadingError]): Deleted.
+        (-[_WKAttachment isEqual:]): Deleted.
+        (-[_WKAttachment hash]): Deleted.
+
+        There's no longer any point to implementing these methods, since the SPI client is now guaranteed a unique
+        mapping of _WKAttachments to attachment elements in the document.
+
+        * UIProcess/Cocoa/PageClientImplCocoa.mm:
+        (WebKit::PageClientImplCocoa::didInsertAttachment):
+        * UIProcess/Cocoa/WebPageProxyCocoa.mm:
+        (WebKit::WebPageProxy::platformRegisterAttachment):
+        (WebKit::WebPageProxy::platformCloneAttachment):
+
+        Extend the behavior of registering new attachment data on Cocoa platforms by additionally creating and setting
+        NSFileWrappers on the API::Attachment.
+
+        * UIProcess/WebPageProxy.cpp:
+        (WebKit::WebPageProxy::resetStateAfterProcessExited):
+
+        Clear out the map of attachment identifiers to API::Attachments when the web content process is terminated.
+
+        (WebKit::WebPageProxy::attachmentForIdentifier const):
+
+        Helper function to look up an API::Attachment for the given attachment identifier. Returns null if the
+        attachment is not found, or the attachment identifier is invalid.
+
+        (WebKit::WebPageProxy::insertAttachment):
+        (WebKit::WebPageProxy::updateAttachmentAttributes):
+        (WebKit::WebPageProxy::registerAttachmentIdentifierFromData):
+        (WebKit::WebPageProxy::registerAttachmentIdentifierFromFilePath):
+        (WebKit::WebPageProxy::cloneAttachmentData):
+        (WebKit::WebPageProxy::platformRegisterAttachment):
+        (WebKit::WebPageProxy::platformCloneAttachment):
+        (WebKit::WebPageProxy::didInsertAttachment):
+
+        Create an entry in the attachment identifier to API::Attachment map when an attachment is inserted, if one does
+        not already exist. An attachment mapping would not exist only in the case where an attachment element was
+        created via bindings; in this case, the client wouldn't have access to an NSFileWrapper containing the contents
+        of the file; in the future, this can be improved by adding a mechanism to register an attachment element with
+        this data, but for now, this is unnecessary for Mail's purposes.
+
+        (WebKit::WebPageProxy::didRemoveAttachment):
+        (WebKit::WebPageProxy::ensureAttachment):
+
+        Ensures an attachment identifier to API::Attachment mapping.
+
+        (WebKit::WebPageProxy::attachmentInfoCallback): Deleted.
+        (WebKit::WebPageProxy::requestAttachmentInfo): Deleted.
+        (WebKit::WebPageProxy::setAttachmentDataAndContentType): Deleted.
+        * UIProcess/WebPageProxy.h:
+        * UIProcess/WebPageProxy.messages.in:
+        * UIProcess/ios/PageClientImplIOS.h:
+        * UIProcess/ios/PageClientImplIOS.mm:
+        * UIProcess/ios/WKContentViewInteraction.h:
+        * UIProcess/ios/WKContentViewInteraction.mm:
+        (-[WKContentView _startDrag:item:]):
+        (-[WKContentView _prepareToDragPromisedAttachment:]):
+        (-[WKContentView _prepareToDragPromisedBlob:]): Deleted.
+
+        Reimplement the way promised attachment data is delivered to the destination when an attachment element is
+        dragged. Instead of relying on the blob URL of the File on the attachment element, first try to find an API
+        Attachment object corresponding to the unique identifier of the dragged attachment, and use its NSFileWrapper to
+        deliver promised data to the destination file URL. The blob URL codepath still exists in the case where script
+        specifies the dragged attachment's File.
+
+        * UIProcess/mac/PageClientImplMac.h:
+        * UIProcess/mac/PageClientImplMac.mm:
+        * WebProcess/WebCoreSupport/WebEditorClient.cpp:
+        (WebKit::WebEditorClient::registerAttachmentIdentifier):
+        (WebKit::WebEditorClient::cloneAttachmentData):
+        * WebProcess/WebCoreSupport/WebEditorClient.h:
+        * WebProcess/WebPage/WebPage.cpp:
+        (WebKit::WebPage::insertAttachment):
+        (WebKit::WebPage::updateAttachmentAttributes):
+        (WebKit::WebPage::requestAttachmentInfo): Deleted.
+        (WebKit::WebPage::setAttachmentDataAndContentType): Deleted.
+        * WebProcess/WebPage/WebPage.h:
+        * WebProcess/WebPage/WebPage.messages.in:
+
+        More renaming and IPC message plumbing.
+
 2018-08-21  Alex Christensen  <achristensen@webkit.org>
 
         Increment NetworkCache::Storage::lastStableVersion after r233742
index 3d63120..d11a901 100644 (file)
@@ -173,7 +173,6 @@ list(APPEND WebKit_SOURCES
 
     UIProcess/Automation/mac/WebAutomationSessionMac.mm
 
-    UIProcess/API/APIAttachment.cpp
     UIProcess/API/APIUserScript.cpp
     UIProcess/API/APIUserStyleSheet.cpp
     UIProcess/API/APIWebsiteDataRecord.cpp
index 95cdc0f..7af168c 100644 (file)
@@ -378,7 +378,6 @@ def headers_for_type(type):
         'String': ['<wtf/text/WTFString.h>'],
         'PAL::SessionID': ['<pal/SessionID.h>'],
         'WebCore::AttachmentDisplayOptions': ['<WebCore/AttachmentTypes.h>'],
-        'WebCore::AttachmentInfo': ['<WebCore/AttachmentTypes.h>'],
         'WebCore::AutoplayEventFlags': ['<WebCore/AutoplayEvent.h>'],
         'WebCore::ExceptionDetails': ['<WebCore/JSDOMExceptionHandling.h>'],
         'WebCore::FileChooserSettings': ['<WebCore/FileChooser.h>'],
index 6899ca5..17d7a85 100644 (file)
@@ -134,9 +134,11 @@ void* Object::newObject(size_t size, Type type)
         wrapper = [WKNSArray alloc];
         break;
 
+#if ENABLE(ATTACHMENT_ELEMENT)
     case Type::Attachment:
         wrapper = [_WKAttachment alloc];
         break;
+#endif
 
     case Type::AuthenticationChallenge:
         wrapper = allocateWKObject([WKNSURLAuthenticationChallenge self], size);
index 5ee415a..365f592 100644 (file)
@@ -57,7 +57,7 @@
 #include <WebCore/Pasteboard.h>
 #include <WebCore/Path.h>
 #include <WebCore/PluginData.h>
-#include <WebCore/PromisedBlobInfo.h>
+#include <WebCore/PromisedAttachmentInfo.h>
 #include <WebCore/ProtectionSpace.h>
 #include <WebCore/RectEdges.h>
 #include <WebCore/Region.h>
@@ -2863,15 +2863,18 @@ std::optional<MediaSelectionOption> ArgumentCoder<MediaSelectionOption>::decode(
     return {{ WTFMove(*displayName), WTFMove(*type) }};
 }
 
-void ArgumentCoder<PromisedBlobInfo>::encode(Encoder& encoder, const PromisedBlobInfo& info)
+void ArgumentCoder<PromisedAttachmentInfo>::encode(Encoder& encoder, const PromisedAttachmentInfo& info)
 {
     encoder << info.blobURL;
     encoder << info.contentType;
     encoder << info.filename;
+#if ENABLE(ATTACHMENT_ELEMENT)
+    encoder << info.attachmentIdentifier;
+#endif
     encodeTypesAndData(encoder, info.additionalTypes, info.additionalData);
 }
 
-bool ArgumentCoder<PromisedBlobInfo>::decode(Decoder& decoder, PromisedBlobInfo& info)
+bool ArgumentCoder<PromisedAttachmentInfo>::decode(Decoder& decoder, PromisedAttachmentInfo& info)
 {
     if (!decoder.decode(info.blobURL))
         return false;
@@ -2882,51 +2885,17 @@ bool ArgumentCoder<PromisedBlobInfo>::decode(Decoder& decoder, PromisedBlobInfo&
     if (!decoder.decode(info.filename))
         return false;
 
-    if (!decodeTypesAndData(decoder, info.additionalTypes, info.additionalData))
-        return false;
-
-    return true;
-}
-
 #if ENABLE(ATTACHMENT_ELEMENT)
-
-void ArgumentCoder<AttachmentInfo>::encode(Encoder& encoder, const AttachmentInfo& info)
-{
-    bool dataIsNull = !info.data;
-    encoder << info.contentType << info.name << info.filePath << dataIsNull;
-    if (!dataIsNull) {
-        SharedBufferDataReference dataReference { info.data.get() };
-        encoder << static_cast<DataReference&>(dataReference);
-    }
-}
-
-bool ArgumentCoder<AttachmentInfo>::decode(Decoder& decoder, AttachmentInfo& info)
-{
-    if (!decoder.decode(info.contentType))
-        return false;
-
-    if (!decoder.decode(info.name))
-        return false;
-
-    if (!decoder.decode(info.filePath))
+    if (!decoder.decode(info.attachmentIdentifier))
         return false;
+#endif
 
-    bool dataIsNull = true;
-    if (!decoder.decode(dataIsNull))
+    if (!decodeTypesAndData(decoder, info.additionalTypes, info.additionalData))
         return false;
 
-    if (!dataIsNull) {
-        DataReference dataReference;
-        if (!decoder.decode(dataReference))
-            return false;
-        info.data = SharedBuffer::create(dataReference.data(), dataReference.size());
-    }
-
     return true;
 }
 
-#endif // ENABLE(ATTACHMENT_ELEMENT)
-
 void ArgumentCoder<Vector<RefPtr<SecurityOrigin>>>::encode(Encoder& encoder, const Vector<RefPtr<SecurityOrigin>>& origins)
 {
     encoder << static_cast<uint64_t>(origins.size());
index f2afcad..8ca760f 100644 (file)
@@ -95,7 +95,6 @@ class TransformationMatrix;
 class UserStyleSheet;
 class URL;
 
-struct AttachmentInfo;
 struct CacheQueryOptions;
 struct CompositionUnderline;
 struct DictationAlternative;
@@ -110,7 +109,7 @@ struct PasteboardImage;
 struct PasteboardCustomData;
 struct PasteboardURL;
 struct PluginInfo;
-struct PromisedBlobInfo;
+struct PromisedAttachmentInfo;
 struct RecentSearch;
 struct ResourceLoadStatistics;
 struct ScrollableAreaParameters;
@@ -699,20 +698,11 @@ template<> struct ArgumentCoder<WebCore::MediaSelectionOption> {
     static std::optional<WebCore::MediaSelectionOption> decode(Decoder&);
 };
 
-template<> struct ArgumentCoder<WebCore::PromisedBlobInfo> {
-    static void encode(Encoder&, const WebCore::PromisedBlobInfo&);
-    static bool decode(Decoder&, WebCore::PromisedBlobInfo&);
+template<> struct ArgumentCoder<WebCore::PromisedAttachmentInfo> {
+    static void encode(Encoder&, const WebCore::PromisedAttachmentInfo&);
+    static bool decode(Decoder&, WebCore::PromisedAttachmentInfo&);
 };
 
-#if ENABLE(ATTACHMENT_ELEMENT)
-
-template<> struct ArgumentCoder<WebCore::AttachmentInfo> {
-    static void encode(Encoder&, const WebCore::AttachmentInfo&);
-    static bool decode(Decoder&, WebCore::AttachmentInfo&);
-};
-
-#endif
-
 template<> struct ArgumentCoder<Vector<RefPtr<WebCore::SecurityOrigin>>> {
     static void encode(Encoder&, const Vector<RefPtr<WebCore::SecurityOrigin>>&);
     static bool decode(Decoder&, Vector<RefPtr<WebCore::SecurityOrigin>>&);
index 1fe4d50..ff1fd0a 100644 (file)
@@ -285,6 +285,7 @@ UIProcess/WebProcessProxy.cpp @no-unify
 UIProcess/WebURLSchemeHandler.cpp @no-unify
 UIProcess/WebURLSchemeTask.cpp @no-unify
 
+UIProcess/API/APIAttachment.cpp @no-unify
 UIProcess/API/APIContentRuleList.cpp @no-unify
 UIProcess/API/APIContentRuleListStore.cpp @no-unify
 UIProcess/API/APIExperimentalFeature.cpp @no-unify
index 368e57d..d6b323a 100644 (file)
@@ -215,7 +215,6 @@ UIProcess/WKInspectorHighlightView.mm @no-unify
 
 UIProcess/ApplePay/WebPaymentCoordinatorProxy.cpp @no-unify
 
-UIProcess/API/APIAttachment.cpp @no-unify
 UIProcess/API/APIWebsiteDataRecord.cpp @no-unify
 
 UIProcess/API/C/WKContextMenuListener.cpp @no-unify
index 7e009d6..d15620f 100644 (file)
 #include "config.h"
 #include "APIAttachment.h"
 
+#if ENABLE(ATTACHMENT_ELEMENT)
+
 #include <WebCore/AttachmentTypes.h>
 #include <WebCore/SharedBuffer.h>
-#include <wtf/BlockPtr.h>
 #include <wtf/text/WTFString.h>
 
 namespace API {
@@ -48,14 +49,6 @@ Attachment::~Attachment()
 {
 }
 
-void Attachment::requestInfo(Function<void(const WebCore::AttachmentInfo&, WebKit::CallbackBase::Error)>&& callback)
-{
-    if (m_webPage)
-        m_webPage->requestAttachmentInfo(m_identifier, WTFMove(callback));
-    else
-        callback({ }, WebKit::CallbackBase::Error::OwnerWasInvalidated);
-}
-
 void Attachment::setDisplayOptions(WebCore::AttachmentDisplayOptions options, Function<void(WebKit::CallbackBase::Error)>&& callback)
 {
     if (m_webPage)
@@ -64,14 +57,19 @@ void Attachment::setDisplayOptions(WebCore::AttachmentDisplayOptions options, Fu
         callback(WebKit::CallbackBase::Error::OwnerWasInvalidated);
 }
 
-void Attachment::setDataAndContentType(WebCore::SharedBuffer& data, const WTF::String& newContentType, const WTF::String& newFilename, Function<void(WebKit::CallbackBase::Error)>&& callback)
+void Attachment::updateAttributes(uint64_t fileSize, const WTF::String& newContentType, const WTF::String& newFilename, Function<void(WebKit::CallbackBase::Error)>&& callback)
 {
+    setContentType(newContentType);
+    setFilePath({ });
+
     auto optionalNewContentType = newContentType.isNull() ? std::nullopt : std::optional<WTF::String> { newContentType };
     auto optionalNewFilename = newFilename.isNull() ? std::nullopt : std::optional<WTF::String> { newFilename };
     if (m_webPage)
-        m_webPage->setAttachmentDataAndContentType(m_identifier, data, WTFMove(optionalNewContentType), WTFMove(optionalNewFilename), WTFMove(callback));
+        m_webPage->updateAttachmentAttributes(m_identifier, fileSize, WTFMove(optionalNewContentType), WTFMove(optionalNewFilename), WTFMove(callback));
     else
         callback(WebKit::CallbackBase::Error::OwnerWasInvalidated);
 }
 
 }
+
+#endif // ENABLE(ATTACHMENT_ELEMENT)
index bf7a227..dec4823 100644 (file)
@@ -25,6 +25,8 @@
 
 #pragma once
 
+#if ENABLE(ATTACHMENT_ELEMENT)
+
 #include "APIObject.h"
 #include "WKBase.h"
 #include "WebPageProxy.h"
 #include <wtf/WeakPtr.h>
 #include <wtf/text/WTFString.h>
 
+OBJC_CLASS NSFileWrapper;
+
 namespace WebCore {
 class SharedBuffer;
 struct AttachmentDisplayOptions;
-struct AttachmentInfo;
 }
 
 namespace WebKit {
@@ -50,15 +53,32 @@ public:
     virtual ~Attachment();
 
     const WTF::String& identifier() const { return m_identifier; }
-    void requestInfo(Function<void(const WebCore::AttachmentInfo&, WebKit::CallbackBase::Error)>&&);
     void setDisplayOptions(WebCore::AttachmentDisplayOptions, Function<void(WebKit::CallbackBase::Error)>&&);
-    void setDataAndContentType(WebCore::SharedBuffer&, const WTF::String& newContentType, const WTF::String& newFilename, Function<void(WebKit::CallbackBase::Error)>&&);
+    void updateAttributes(uint64_t fileSize, const WTF::String& newContentType, const WTF::String& newFilename, Function<void(WebKit::CallbackBase::Error)>&&);
+
+#if PLATFORM(COCOA)
+    NSFileWrapper *fileWrapper() const { return m_fileWrapper.get(); }
+    void setFileWrapper(NSFileWrapper *fileWrapper) { m_fileWrapper = fileWrapper; }
+#endif
+
+    const WTF::String& filePath() const { return m_filePath; }
+    void setFilePath(const WTF::String& filePath) { m_filePath = filePath; }
+
+    const WTF::String& contentType() const { return m_contentType; }
+    void setContentType(const WTF::String& contentType) { m_contentType = contentType; }
 
 private:
     explicit Attachment(const WTF::String& identifier, WebKit::WebPageProxy&);
 
+#if PLATFORM(COCOA)
+    RetainPtr<NSFileWrapper> m_fileWrapper;
+#endif
     WTF::String m_identifier;
+    WTF::String m_filePath;
+    WTF::String m_contentType;
     WeakPtr<WebKit::WebPageProxy> m_webPage;
 };
 
 } // namespace API
+
+#endif // ENABLE(ATTACHMENT_ELEMENT)
index f68cad1..4c26439 100644 (file)
@@ -114,7 +114,6 @@ struct UIEdgeInsets;
 - (void)_webView:(WKWebView *)webView editorStateDidChange:(NSDictionary *)editorState WK_API_AVAILABLE(macosx(10.13.4), ios(11.3));
 
 - (void)_webView:(WKWebView *)webView didRemoveAttachment:(_WKAttachment *)attachment WK_API_AVAILABLE(macosx(10.13.4), ios(11.3));
-- (void)_webView:(WKWebView *)webView didInsertAttachment:(_WKAttachment *)attachment WK_API_AVAILABLE(macosx(10.13.4), ios(11.3));
 - (void)_webView:(WKWebView *)webView didInsertAttachment:(_WKAttachment *)attachment withSource:(NSString *)source WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
 
 - (void)_webView:(WKWebView *)webView didResignInputElementStrongPasswordAppearanceWithUserInfo:(id <NSSecureCoding>)userInfo WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
index f023018..f0b7a6e 100644 (file)
 #import "WKScrollView.h"
 #import "WKWebViewContentProviderRegistry.h"
 #import "_WKWebViewPrintFormatter.h"
+#import <MobileCoreServices/MobileCoreServices.h>
 #import <UIKit/UIApplication.h>
 #import <WebCore/FrameLoaderTypes.h>
 #import <WebCore/InspectorOverlay.h>
@@ -1212,17 +1213,25 @@ static NSDictionary *dictionaryRepresentationForEditorState(const WebKit::Editor
 - (void)_didInsertAttachment:(NSString *)identifier withSource:(NSString *)source
 {
     id <WKUIDelegatePrivate> uiDelegate = (id <WKUIDelegatePrivate>)self.UIDelegate;
-    if ([uiDelegate respondsToSelector:@selector(_webView:didInsertAttachment:withSource:)])
-        [uiDelegate _webView:self didInsertAttachment:wrapper(API::Attachment::create(identifier, *_page)) withSource:source];
-    else if ([uiDelegate respondsToSelector:@selector(_webView:didInsertAttachment:)])
-        [uiDelegate _webView:self didInsertAttachment:wrapper(API::Attachment::create(identifier, *_page))];
+    if (![uiDelegate respondsToSelector:@selector(_webView:didInsertAttachment:withSource:)])
+        return;
+
+    if (auto attachment = _page->attachmentForIdentifier(identifier))
+        [uiDelegate _webView:self didInsertAttachment:wrapper(*attachment) withSource:source];
+    else
+        ASSERT_NOT_REACHED();
 }
 
 - (void)_didRemoveAttachment:(NSString *)identifier
 {
     id <WKUIDelegatePrivate> uiDelegate = (id <WKUIDelegatePrivate>)self.UIDelegate;
-    if ([uiDelegate respondsToSelector:@selector(_webView:didRemoveAttachment:)])
-        [uiDelegate _webView:self didRemoveAttachment:wrapper(API::Attachment::create(identifier, *_page))];
+    if (![uiDelegate respondsToSelector:@selector(_webView:didRemoveAttachment:)])
+        return;
+
+    if (auto attachment = _page->attachmentForIdentifier(identifier))
+        [uiDelegate _webView:self didRemoveAttachment:wrapper(*attachment)];
+    else
+        ASSERT_NOT_REACHED();
 }
 
 #endif // ENABLE(ATTACHMENT_ELEMENT)
@@ -4399,17 +4408,35 @@ WEBCORE_COMMAND(yankAndSelect)
 
 - (_WKAttachment *)_insertAttachmentWithFilename:(NSString *)filename contentType:(NSString *)contentType data:(NSData *)data options:(_WKAttachmentDisplayOptions *)options completion:(void(^)(BOOL success))completionHandler
 {
+    auto fileWrapper = adoptNS([[NSFileWrapper alloc] initRegularFileWithContents:data]);
+    if (filename)
+        [fileWrapper setPreferredFilename:filename];
+    return [self _insertAttachmentWithFileWrapper:fileWrapper.get() contentType:contentType options:options completion:completionHandler];
+}
+
+- (_WKAttachment *)_insertAttachmentWithFileWrapper:(NSFileWrapper *)fileWrapper contentType:(NSString *)contentType options:(_WKAttachmentDisplayOptions *)options completion:(void(^)(BOOL success))completionHandler
+{
 #if ENABLE(ATTACHMENT_ELEMENT)
     auto identifier = createCanonicalUUIDString();
-
     auto coreOptions = options ? options.coreDisplayOptions : WebCore::AttachmentDisplayOptions { };
-    auto buffer = WebCore::SharedBuffer::create(data);
-    _page->insertAttachment(identifier, coreOptions, filename, contentType.length ? std::optional<String> { contentType } : std::nullopt, buffer.get(), [capturedHandler = makeBlockPtr(completionHandler), capturedBuffer = buffer.copyRef()] (WebKit::CallbackBase::Error error) {
+    auto attachment = API::Attachment::create(identifier, *_page);
+    attachment->setFileWrapper(fileWrapper);
+
+    if (!contentType.length) {
+        if (NSString *pathExtension = (fileWrapper.filename.length ? fileWrapper.filename : fileWrapper.preferredFilename).pathExtension)
+            contentType = WebCore::MIMETypeRegistry::getMIMETypeForExtension(pathExtension);
+    }
+
+    auto fileSize = [[[fileWrapper fileAttributes] objectForKey:NSFileSize] unsignedLongLongValue];
+    if (!fileSize && fileWrapper.regularFile)
+        fileSize = fileWrapper.regularFileContents.length;
+
+    _page->insertAttachment(attachment.copyRef(), coreOptions, fileSize, [fileWrapper preferredFilename], contentType.length ? std::optional<String> { contentType } : std::nullopt, [capturedHandler = makeBlockPtr(completionHandler)] (WebKit::CallbackBase::Error error) {
         if (capturedHandler)
             capturedHandler(error == WebKit::CallbackBase::Error::None);
     });
 
-    return wrapper(API::Attachment::create(identifier, *_page));
+    return wrapper(attachment);
 #else
     return nil;
 #endif
index 581a4a6..d0cf2b8 100644 (file)
@@ -180,7 +180,8 @@ typedef NS_OPTIONS(NSUInteger, _WKRectEdge) {
 
 @property (nonatomic, setter=_setBackgroundExtendsBeyondPage:) BOOL _backgroundExtendsBeyondPage WK_API_AVAILABLE(macosx(10.13.4), ios(8.0));
 
-- (_WKAttachment *)_insertAttachmentWithFilename:(NSString *)filename contentType:(NSString *)contentType data:(NSData *)data options:(_WKAttachmentDisplayOptions *)options completion:(void(^)(BOOL success))completionHandler WK_API_AVAILABLE(macosx(10.13.4), ios(11.3));
+- (_WKAttachment *)_insertAttachmentWithFilename:(NSString *)filename contentType:(NSString *)contentType data:(NSData *)data options:(_WKAttachmentDisplayOptions *)options completion:(void(^)(BOOL success))completionHandler WK_API_DEPRECATED_WITH_REPLACEMENT("-_insertAttachmentWithFileWrapper:contentType:options:completion:", macosx(10.13.4, WK_MAC_TBA), ios(11.3, WK_IOS_TBA));
+- (_WKAttachment *)_insertAttachmentWithFileWrapper:(NSFileWrapper *)fileWrapper contentType:(NSString *)contentType options:(_WKAttachmentDisplayOptions *)options completion:(void(^)(BOOL success))completionHandler WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
 
 #if TARGET_OS_IPHONE
 // DERECATED: The setters of the three following function are deprecated, please use overrideLayoutParameters.
index 0829939..f1441b4 100644 (file)
@@ -44,19 +44,26 @@ WK_CLASS_AVAILABLE(macosx(10.13.4), ios(11.3))
 
 WK_CLASS_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA))
 @interface _WKAttachmentInfo : NSObject
-@property (nonatomic, readonly) NSString *contentType;
-@property (nonatomic, readonly) NSString *name;
-@property (nonatomic, readonly) NSString *filePath;
+@property (nonatomic, readonly, nullable) NSString *contentType;
+@property (nonatomic, readonly, nullable) NSString *name;
+@property (nonatomic, readonly, nullable) NSString *filePath;
 @property (nonatomic, readonly, nullable) NSData *data;
+@property (nonatomic, readonly, nullable) NSFileWrapper *fileWrapper;
 @end
 
 WK_CLASS_AVAILABLE(macosx(10.13.4), ios(11.3))
 @interface _WKAttachment : NSObject
-- (void)requestInfo:(void(^)(_WKAttachmentInfo * _Nullable, NSError * _Nullable))completionHandler WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
-- (void)requestData:(void(^)(NSData * _Nullable, NSError * _Nullable))completionHandler;
+
 - (void)setDisplayOptions:(_WKAttachmentDisplayOptions *)options completion:(void(^ _Nullable)(NSError * _Nullable))completionHandler;
-- (void)setData:(NSData *)data newContentType:(nullable NSString *)newContentType newFilename:(nullable NSString *)newFilename completion:(void(^ _Nullable)(NSError * _Nullable))completionHandler;
+- (void)setFileWrapper:(NSFileWrapper *)fileWrapper contentType:(nullable NSString *)contentType completion:(void(^ _Nullable)(NSError * _Nullable))completionHandler WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
+
+@property (nonatomic, readonly) _WKAttachmentInfo *info WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
 @property (nonatomic, readonly) NSString *uniqueIdentifier;
+
+// Deprecated SPI.
+- (void)requestInfo:(void(^)(_WKAttachmentInfo * _Nullable, NSError * _Nullable))completionHandler WK_API_DEPRECATED_WITH_REPLACEMENT("-info", macosx(WK_MAC_TBA, WK_MAC_TBA), ios(WK_IOS_TBA, WK_IOS_TBA));
+- (void)setData:(NSData *)data newContentType:(nullable NSString *)newContentType newFilename:(nullable NSString *)newFilename completion:(void(^ _Nullable)(NSError * _Nullable))completionHandler WK_API_DEPRECATED_WITH_REPLACEMENT("Please use -setFileWrapper:contentType:completion: instead.", macosx(10.13.4, WK_MAC_TBA), ios(11.3, WK_IOS_TBA));
+
 @end
 
 NS_ASSUME_NONNULL_END
index 5f00c8c..696adbe 100644 (file)
 #import "WKErrorPrivate.h"
 #import "_WKAttachmentInternal.h"
 #import <WebCore/AttachmentTypes.h>
+#import <WebCore/MIMETypeRegistry.h>
 #import <WebCore/SharedBuffer.h>
 #import <wtf/BlockPtr.h>
 
+#if PLATFORM(IOS)
+#import <MobileCoreServices/MobileCoreServices.h>
+#endif
+
 using namespace WebKit;
 
 @implementation _WKAttachmentDisplayOptions : NSObject
@@ -47,41 +52,38 @@ using namespace WebKit;
 @end
 
 @implementation _WKAttachmentInfo {
-    RetainPtr<NSData> _data;
-    RetainPtr<NSString> _name;
-    RetainPtr<NSString> _filePath;
+    RetainPtr<NSFileWrapper> _fileWrapper;
     RetainPtr<NSString> _contentType;
-    RetainPtr<NSError> _fileLoadingError;
+    RetainPtr<NSString> _filePath;
 }
 
-- (instancetype)initWithInfo:(const WebCore::AttachmentInfo&)info
+- (instancetype)initWithFileWrapper:(NSFileWrapper *)fileWrapper filePath:(NSString *)filePath contentType:(NSString *)contentType
 {
     if (!(self = [super init]))
         return nil;
 
-    _data = info.data ? info.data->createNSData() : nil;
-    _contentType = adoptNS([(NSString *)info.contentType ?: @"" copy]);
-    _name = adoptNS([(NSString *)info.name ?: @"" copy]);
-    _filePath = adoptNS([(NSString *)info.filePath ?: @"" copy]);
-    _fileLoadingError = nil;
-
-    if (!_data && [_filePath length]) {
-        NSError *error = nil;
-        _data = [NSData dataWithContentsOfFile:_filePath.get() options:NSDataReadingMappedIfSafe error:&error];
-        _fileLoadingError = error;
-    }
-
+    _fileWrapper = fileWrapper;
+    _filePath = filePath;
+    _contentType = contentType;
     return self;
 }
 
 - (NSData *)data
 {
-    return _data.get();
+    if (![_fileWrapper isRegularFile]) {
+        // FIXME: Handle attachments backed by NSFileWrappers that represent directories.
+        return nil;
+    }
+
+    return [_fileWrapper regularFileContents];
 }
 
 - (NSString *)name
 {
-    return _name.get();
+    if ([_fileWrapper filename].length)
+        return [_fileWrapper filename];
+
+    return [_fileWrapper preferredFilename];
 }
 
 - (NSString *)filePath
@@ -89,14 +91,48 @@ using namespace WebKit;
     return _filePath.get();
 }
 
+static BOOL isDeclaredOrDynamicTypeIdentifier(NSString *type)
+{
+    return UTTypeIsDeclared((CFStringRef)type) || UTTypeIsDynamic((CFStringRef)type);
+}
+
+- (NSString *)_typeIdentifierFromPathExtension
+{
+    if (NSString *extension = self.name.pathExtension)
+        return WebCore::MIMETypeRegistry::getMIMETypeForExtension(extension);
+
+    return nil;
+}
+
 - (NSString *)contentType
 {
-    return _contentType.get();
+    // A "content type" can refer to either a UTI or a MIME type. We prefer MIME type here to preserve existing behavior.
+    return self.mimeType ?: self.utiType;
 }
 
-- (NSError *)fileLoadingError
+- (NSString *)mimeType
 {
-    return _fileLoadingError.get();
+    NSString *contentType = [_contentType length] ? _contentType.get() : [self _typeIdentifierFromPathExtension];
+    if (!isDeclaredOrDynamicTypeIdentifier(contentType))
+        return contentType;
+
+    auto mimeType = adoptCF(UTTypeCopyPreferredTagWithClass((CFStringRef)contentType, kUTTagClassMIMEType));
+    return (NSString *)mimeType.autorelease();
+}
+
+- (NSString *)utiType
+{
+    NSString *contentType = [_contentType length] ? _contentType.get() : [self _typeIdentifierFromPathExtension];
+    if (isDeclaredOrDynamicTypeIdentifier(contentType))
+        return contentType;
+
+    auto utiType = adoptCF(UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, (CFStringRef)contentType, nil));
+    return (NSString *)utiType.autorelease();
+}
+
+- (NSFileWrapper *)fileWrapper
+{
+    return _fileWrapper.get();
 }
 
 @end
@@ -108,37 +144,14 @@ using namespace WebKit;
     return *_attachment;
 }
 
-- (BOOL)isEqual:(id)object
+- (_WKAttachmentInfo *)info
 {
-    return [object isKindOfClass:[_WKAttachment class]] && [self.uniqueIdentifier isEqual:[(_WKAttachment *)object uniqueIdentifier]];
+    return [[[_WKAttachmentInfo alloc] initWithFileWrapper:_attachment->fileWrapper() filePath:_attachment->filePath() contentType:_attachment->contentType()] autorelease];
 }
 
 - (void)requestInfo:(void(^)(_WKAttachmentInfo *, NSError *))completionHandler
 {
-    _attachment->requestInfo([capturedBlock = makeBlockPtr(completionHandler)] (const WebCore::AttachmentInfo& info, CallbackBase::Error error) {
-        if (!capturedBlock)
-            return;
-
-        if (error != CallbackBase::Error::None) {
-            capturedBlock(nil, [NSError errorWithDomain:WKErrorDomain code:WKErrorWebViewInvalidated userInfo:nil]);
-            return;
-        }
-
-        auto attachmentInfo = adoptNS([[_WKAttachmentInfo alloc] initWithInfo:info]);
-        if (NSError *fileLoadingError = [attachmentInfo fileLoadingError]) {
-            capturedBlock(nil, [NSError errorWithDomain:WKErrorDomain code:WKErrorUnknown userInfo:@{ NSUnderlyingErrorKey : fileLoadingError }]);
-            return;
-        }
-
-        capturedBlock(attachmentInfo.get(), nil);
-    });
-}
-
-- (void)requestData:(void(^)(NSData *, NSError *))completionHandler
-{
-    [self requestInfo:[protectedBlock = makeBlockPtr(completionHandler)] (_WKAttachmentInfo *info, NSError *error) {
-        protectedBlock(info.data, error);
-    }];
+    completionHandler(self.info, nil);
 }
 
 - (void)setDisplayOptions:(_WKAttachmentDisplayOptions *)options completion:(void(^)(NSError *))completionHandler
@@ -155,10 +168,19 @@ using namespace WebKit;
     });
 }
 
-- (void)setData:(NSData *)data newContentType:(NSString *)newContentType newFilename:(NSString *)newFilename completion:(void(^)(NSError *))completionHandler
+- (void)setFileWrapper:(NSFileWrapper *)fileWrapper contentType:(NSString *)contentType completion:(void (^)(NSError *))completionHandler
 {
-    auto buffer = WebCore::SharedBuffer::create(data);
-    _attachment->setDataAndContentType(buffer.get(), newContentType, newFilename, [capturedBlock = makeBlockPtr(completionHandler), capturedBuffer = buffer.copyRef()] (CallbackBase::Error error) {
+    auto fileSize = [fileWrapper.fileAttributes[NSFileSize] unsignedLongLongValue];
+    if (!fileSize && fileWrapper.regularFile)
+        fileSize = fileWrapper.regularFileContents.length;
+
+    if (!contentType.length) {
+        if (NSString *pathExtension = (fileWrapper.filename.length ? fileWrapper.filename : fileWrapper.preferredFilename).pathExtension)
+            contentType = WebCore::MIMETypeRegistry::getMIMETypeForExtension(pathExtension);
+    }
+
+    _attachment->setFileWrapper(fileWrapper);
+    _attachment->updateAttributes(fileSize, contentType, fileWrapper.preferredFilename, [capturedBlock = makeBlockPtr(completionHandler)] (auto error) {
         if (!capturedBlock)
             return;
 
@@ -169,19 +191,22 @@ using namespace WebKit;
     });
 }
 
-- (NSString *)uniqueIdentifier
+- (void)setData:(NSData *)data newContentType:(NSString *)newContentType newFilename:(NSString *)newFilename completion:(void(^)(NSError *))completionHandler
 {
-    return _attachment->identifier();
+    auto fileWrapper = adoptNS([[NSFileWrapper alloc] initRegularFileWithContents:data]);
+    if (newFilename)
+        [fileWrapper setPreferredFilename:newFilename];
+    [self setFileWrapper:fileWrapper.get() contentType:newContentType completion:completionHandler];
 }
 
-- (NSUInteger)hash
+- (NSString *)uniqueIdentifier
 {
-    return [self.uniqueIdentifier hash];
+    return _attachment->identifier();
 }
 
 - (NSString *)description
 {
-    return [NSString stringWithFormat:@"<%@ id='%@'>", [self class], self.uniqueIdentifier];
+    return [NSString stringWithFormat:@"<%@ %p id='%@'>", [self class], self, self.uniqueIdentifier];
 }
 
 @end
index 837ec1a..d6e0e42 100644 (file)
@@ -52,6 +52,7 @@ void PageClientImplCocoa::didInsertAttachment(const String& identifier, const St
     [m_webView _didInsertAttachment:identifier withSource:source];
 #else
     UNUSED_PARAM(identifier);
+    UNUSED_PARAM(source);
 #endif
 }
 
index 5ba2824..ecdc688 100644 (file)
@@ -26,6 +26,7 @@
 #import "config.h"
 #import "WebPageProxy.h"
 
+#import "APIAttachment.h"
 #import "APIUIClient.h"
 #import "DataDetectionResult.h"
 #import "LoadParameters.h"
@@ -160,4 +161,27 @@ void WebPageProxy::setDragCaretRect(const IntRect& dragCaretRect)
 
 #endif // ENABLE(DRAG_SUPPORT)
 
+#if ENABLE(ATTACHMENT_ELEMENT)
+
+void WebPageProxy::platformRegisterAttachment(Ref<API::Attachment>&& attachment, const String& preferredFileName, const IPC::DataReference& dataReference)
+{
+    auto buffer = SharedBuffer::create(dataReference.data(), dataReference.size());
+    auto fileWrapper = adoptNS([[NSFileWrapper alloc] initRegularFileWithContents:buffer->createNSData().autorelease()]);
+    [fileWrapper setPreferredFilename:preferredFileName];
+    attachment->setFileWrapper(fileWrapper.get());
+}
+
+void WebPageProxy::platformRegisterAttachment(Ref<API::Attachment>&& attachment, const String& filePath)
+{
+    auto fileWrapper = adoptNS([[NSFileWrapper alloc] initWithURL:[NSURL fileURLWithPath:filePath] options:0 error:nil]);
+    attachment->setFileWrapper(fileWrapper.get());
+}
+
+void WebPageProxy::platformCloneAttachment(Ref<API::Attachment>&& fromAttachment, Ref<API::Attachment>&& toAttachment)
+{
+    toAttachment->setFileWrapper(fromAttachment->fileWrapper());
+}
+
+#endif // ENABLE(ATTACHMENT_ELEMENT)
+
 }
index 01c40be..fc42a15 100644 (file)
@@ -5601,22 +5601,6 @@ void WebPageProxy::voidCallback(CallbackID callbackID)
     callback->performCallback();
 }
 
-#if ENABLE(ATTACHMENT_ELEMENT)
-
-void WebPageProxy::attachmentInfoCallback(const WebCore::AttachmentInfo& info, CallbackID callbackID)
-{
-    auto callback = m_callbacks.take<AttachmentInfoCallback>(callbackID);
-    if (!callback) {
-        ASSERT_NOT_REACHED();
-        return;
-    }
-
-    MESSAGE_CHECK_URL(info.filePath);
-    callback->performCallbackWithReturnValue(info);
-}
-
-#endif
-
 void WebPageProxy::dataCallback(const IPC::DataReference& dataReference, CallbackID callbackID)
 {
     auto callback = m_callbacks.take<DataCallback>(callbackID);
@@ -6196,6 +6180,12 @@ void WebPageProxy::resetStateAfterProcessExited(ProcessTerminationReason termina
     m_touchEventQueue.clear();
 #endif
 
+#if ENABLE(ATTACHMENT_ELEMENT)
+    for (auto identifier : m_attachmentIdentifierToAttachmentMap.keys())
+        m_pageClient.didRemoveAttachment(identifier);
+    m_attachmentIdentifierToAttachmentMap.clear();
+#endif
+
     if (terminationReason != ProcessTerminationReason::NavigationSwap) {
         PageLoadState::Transaction transaction = m_pageLoadState.transaction();
         m_pageLoadState.reset(transaction);
@@ -7748,26 +7738,29 @@ void WebPageProxy::touchBarMenuItemDataRemoved(const TouchBarMenuItemData& touch
 
 #if ENABLE(ATTACHMENT_ELEMENT)
 
-void WebPageProxy::insertAttachment(const String& identifier, const AttachmentDisplayOptions& options, const String& filename, std::optional<String> contentType, SharedBuffer& data, Function<void(CallbackBase::Error)>&& callback)
+RefPtr<API::Attachment> WebPageProxy::attachmentForIdentifier(const String& identifier) const
 {
-    if (!isValid()) {
-        callback(CallbackBase::Error::OwnerWasInvalidated);
-        return;
-    }
+    if (identifier.isEmpty())
+        return nullptr;
 
-    auto callbackID = m_callbacks.put(WTFMove(callback), m_process->throttler().backgroundActivityToken());
-    m_process->send(Messages::WebPage::InsertAttachment(identifier, options, filename, contentType, IPC::SharedBufferDataReference { &data }, callbackID), m_pageID);
+    return m_attachmentIdentifierToAttachmentMap.get(identifier);
 }
 
-void WebPageProxy::requestAttachmentInfo(const String& identifier, Function<void(const AttachmentInfo&, CallbackBase::Error)>&& callback)
+void WebPageProxy::insertAttachment(Ref<API::Attachment>&& attachment, const AttachmentDisplayOptions& options, uint64_t fileSize, const String& filename, std::optional<String> contentType, Function<void(CallbackBase::Error)>&& callback)
 {
     if (!isValid()) {
-        callback({ }, CallbackBase::Error::OwnerWasInvalidated);
+        callback(CallbackBase::Error::OwnerWasInvalidated);
         return;
     }
 
+    if (contentType)
+        attachment->setContentType(*contentType);
+
+    auto attachmentIdentifier = attachment->identifier();
+    m_attachmentIdentifierToAttachmentMap.set(attachmentIdentifier, WTFMove(attachment));
+
     auto callbackID = m_callbacks.put(WTFMove(callback), m_process->throttler().backgroundActivityToken());
-    m_process->send(Messages::WebPage::RequestAttachmentInfo(identifier, callbackID), m_pageID);
+    m_process->send(Messages::WebPage::InsertAttachment(attachmentIdentifier, options, fileSize, filename, contentType, callbackID), m_pageID);
 }
 
 void WebPageProxy::setAttachmentDisplayOptions(const String& identifier, AttachmentDisplayOptions options, Function<void(CallbackBase::Error)>&& callback)
@@ -7781,7 +7774,7 @@ void WebPageProxy::setAttachmentDisplayOptions(const String& identifier, Attachm
     m_process->send(Messages::WebPage::SetAttachmentDisplayOptions(identifier, options, callbackID), m_pageID);
 }
 
-void WebPageProxy::setAttachmentDataAndContentType(const String& identifier, SharedBuffer& data, std::optional<String>&& newContentType, std::optional<String>&& newFilename, Function<void(CallbackBase::Error)>&& callback)
+void WebPageProxy::updateAttachmentAttributes(const String& identifier, uint64_t fileSize, std::optional<String>&& newContentType, std::optional<String>&& newFilename, Function<void(CallbackBase::Error)>&& callback)
 {
     if (!isValid()) {
         callback(CallbackBase::Error::OwnerWasInvalidated);
@@ -7789,19 +7782,87 @@ void WebPageProxy::setAttachmentDataAndContentType(const String& identifier, Sha
     }
 
     auto callbackID = m_callbacks.put(WTFMove(callback), m_process->throttler().backgroundActivityToken());
-    m_process->send(Messages::WebPage::SetAttachmentDataAndContentType(identifier, IPC::SharedBufferDataReference { &data }, WTFMove(newContentType), WTFMove(newFilename), callbackID), m_pageID);
+    m_process->send(Messages::WebPage::UpdateAttachmentAttributes(identifier, fileSize, WTFMove(newContentType), WTFMove(newFilename), callbackID), m_pageID);
 }
 
+void WebPageProxy::registerAttachmentIdentifierFromData(const String& identifier, const String& contentType, const String& preferredFileName, const IPC::DataReference& data)
+{
+    if (attachmentForIdentifier(identifier))
+        return;
+
+    auto attachment = ensureAttachment(identifier);
+    attachment->setContentType(contentType);
+    m_attachmentIdentifierToAttachmentMap.set(identifier, attachment.copyRef());
+
+    platformRegisterAttachment(WTFMove(attachment), preferredFileName, data);
+}
+
+void WebPageProxy::registerAttachmentIdentifierFromFilePath(const String& identifier, const String& contentType, const String& filePath)
+{
+    if (attachmentForIdentifier(identifier))
+        return;
+
+    auto attachment = ensureAttachment(identifier);
+    attachment->setContentType(contentType);
+    attachment->setFilePath(filePath);
+    m_attachmentIdentifierToAttachmentMap.set(identifier, attachment.copyRef());
+
+    platformRegisterAttachment(WTFMove(attachment), filePath);
+}
+
+void WebPageProxy::cloneAttachmentData(const String& fromIdentifier, const String& toIdentifier)
+{
+    auto newAttachment = ensureAttachment(toIdentifier);
+    auto existingAttachment = attachmentForIdentifier(fromIdentifier);
+    if (!existingAttachment) {
+        ASSERT_NOT_REACHED();
+        return;
+    }
+
+    newAttachment->setContentType(existingAttachment->contentType());
+    newAttachment->setFilePath(existingAttachment->filePath());
+
+    platformCloneAttachment(existingAttachment.releaseNonNull(), WTFMove(newAttachment));
+}
+
+#if !PLATFORM(COCOA)
+
+void WebPageProxy::platformRegisterAttachment(Ref<API::Attachment>&&, const String& preferredFileName, const IPC::DataReference&)
+{
+}
+
+void WebPageProxy::platformRegisterAttachment(Ref<API::Attachment>&&, const String&)
+{
+}
+
+void WebPageProxy::platformCloneAttachment(Ref<API::Attachment>&&, Ref<API::Attachment>&&)
+{
+}
+
+#endif
+
 void WebPageProxy::didInsertAttachment(const String& identifier, const String& source)
 {
+    ensureAttachment(identifier);
     m_pageClient.didInsertAttachment(identifier, source);
 }
 
 void WebPageProxy::didRemoveAttachment(const String& identifier)
 {
+    ASSERT(attachmentForIdentifier(identifier));
     m_pageClient.didRemoveAttachment(identifier);
 }
 
+Ref<API::Attachment> WebPageProxy::ensureAttachment(const String& identifier)
+{
+    if (auto existingAttachment = attachmentForIdentifier(identifier))
+        return *existingAttachment;
+
+    auto attachment = API::Attachment::create(identifier, *this);
+    m_attachmentIdentifierToAttachmentMap.set(identifier, attachment.copyRef());
+    return attachment;
+}
+
 #endif // ENABLE(ATTACHMENT_ELEMENT)
 
 void WebPageProxy::writeBlobToFilePath(const URL& url, const String& path, Function<void(bool success)>&& callback)
index c90019d..ecde6ce 100644 (file)
@@ -135,6 +135,7 @@ class MediaSessionMetadata;
 #endif
 
 namespace API {
+class Attachment;
 class ContextMenuClient;
 class FindClient;
 class FindMatchesClient;
@@ -197,7 +198,6 @@ using FloatBoxExtent = RectEdges<float>;
 #if ENABLE(ATTACHMENT_ELEMENT)
 namespace WebCore {
 struct AttachmentDisplayOptions;
-struct AttachmentInfo;
 }
 #endif
 
@@ -275,10 +275,6 @@ typedef GenericCallback<EditingRange> EditingRangeCallback;
 typedef GenericCallback<const String&> StringCallback;
 typedef GenericCallback<API::SerializedScriptValue*, bool, const WebCore::ExceptionDetails&> ScriptValueCallback;
 
-#if ENABLE(ATTACHMENT_ELEMENT)
-typedef GenericCallback<const WebCore::AttachmentInfo&> AttachmentInfoCallback;
-#endif
-
 #if PLATFORM(GTK)
 typedef GenericCallback<API::Error*> PrintFinishedCallback;
 #endif
@@ -1319,11 +1315,10 @@ public:
 #endif
 
 #if ENABLE(ATTACHMENT_ELEMENT)
-    void insertAttachment(const String& identifier, const WebCore::AttachmentDisplayOptions&, const String& filename, std::optional<String> contentType, WebCore::SharedBuffer& data, Function<void(CallbackBase::Error)>&&);
-    void requestAttachmentInfo(const String& identifier, Function<void(const WebCore::AttachmentInfo&, CallbackBase::Error)>&&);
+    RefPtr<API::Attachment> attachmentForIdentifier(const String& identifier) const;
+    void insertAttachment(Ref<API::Attachment>&&, const WebCore::AttachmentDisplayOptions&, uint64_t fileSize, const String& fileName, std::optional<String> contentType, Function<void(CallbackBase::Error)>&&);
     void setAttachmentDisplayOptions(const String& identifier, WebCore::AttachmentDisplayOptions, Function<void(CallbackBase::Error)>&&);
-    void setAttachmentDataAndContentType(const String& identifier, WebCore::SharedBuffer& data, std::optional<String>&& newContentType, std::optional<String>&& newFilename, Function<void(CallbackBase::Error)>&&);
-    void attachmentInfoCallback(const WebCore::AttachmentInfo&, CallbackID);
+    void updateAttachmentAttributes(const String& identifier, uint64_t fileSize, std::optional<String>&& newContentType, std::optional<String>&& newFilename, Function<void(CallbackBase::Error)>&&);
 #endif
 
 #if ENABLE(APPLICATION_MANIFEST)
@@ -1808,8 +1803,17 @@ private:
     void stopAllURLSchemeTasks();
 
 #if ENABLE(ATTACHMENT_ELEMENT)
+    void registerAttachmentIdentifierFromData(const String& identifier, const String& contentType, const String& preferredFileName, const IPC::DataReference&);
+    void registerAttachmentIdentifierFromFilePath(const String& identifier, const String& contentType, const String& filePath);
+    void cloneAttachmentData(const String& fromIdentifier, const String& toIdentifier);
+
+    void platformRegisterAttachment(Ref<API::Attachment>&&, const String& preferredFileName, const IPC::DataReference&);
+    void platformRegisterAttachment(Ref<API::Attachment>&&, const String& filePath);
+    void platformCloneAttachment(Ref<API::Attachment>&& fromAttachment, Ref<API::Attachment>&& toAttachment);
+
     void didInsertAttachment(const String& identifier, const String& source);
     void didRemoveAttachment(const String& identifier);
+    Ref<API::Attachment> ensureAttachment(const String& identifier);
 #endif
 
 #if PLATFORM(MAC) && ENABLE(WEBPROCESS_WINDOWSERVER_BLOCKING)
@@ -2209,6 +2213,10 @@ private:
 
     HashMap<String, Ref<WebURLSchemeHandler>> m_urlSchemeHandlersByScheme;
     HashMap<uint64_t, Ref<WebURLSchemeHandler>> m_urlSchemeHandlersByIdentifier;
+
+#if ENABLE(ATTACHMENT_ELEMENT)
+    HashMap<String, Ref<API::Attachment>> m_attachmentIdentifierToAttachmentMap;
+#endif
         
 #if PLATFORM(MAC) && ENABLE(WEBPROCESS_WINDOWSERVER_BLOCKING)
     std::unique_ptr<DisplayLink> m_displayLink;
index b3bd5ec..d2e3d1c 100644 (file)
@@ -176,9 +176,6 @@ messages -> WebPageProxy {
     EditingRangeCallback(struct WebKit::EditingRange range, WebKit::CallbackID callbackID)
     UnsignedCallback(uint64_t result, WebKit::CallbackID callbackID)
     RectForCharacterRangeCallback(WebCore::IntRect rect, struct WebKit::EditingRange actualRange, WebKit::CallbackID callbackID)
-#if ENABLE(ATTACHMENT_ELEMENT)
-    AttachmentInfoCallback(struct WebCore::AttachmentInfo info, WebKit::CallbackID callbackID);
-#endif
 #if ENABLE(APPLICATION_MANIFEST)
     ApplicationManifestCallback(std::optional<WebCore::ApplicationManifest> manifest, WebKit::CallbackID callbackID)
 #endif
@@ -529,6 +526,9 @@ messages -> WebPageProxy {
 #endif
 
 #if ENABLE(ATTACHMENT_ELEMENT)
+    RegisterAttachmentIdentifierFromData(String identifier, String contentType, String preferredFileName, IPC::DataReference data)
+    RegisterAttachmentIdentifierFromFilePath(String identifier, String contentType, String filePath)
+    CloneAttachmentData(String fromIdentifier, String toIdentifier)
     DidInsertAttachment(String identifier, String source)
     DidRemoveAttachment(String identifier)
 #endif
index 1c7abfc..415e669 100644 (file)
@@ -35,7 +35,7 @@ OBJC_CLASS WKContentView;
 OBJC_CLASS WKEditorUndoTargetObjC;
 
 namespace WebCore {
-struct PromisedBlobInfo;
+struct PromisedAttachmentInfo;
 }
 
 namespace WebKit {
index f90a2f8..017498e 100644 (file)
@@ -53,7 +53,7 @@
 #import "_WKDownloadInternal.h"
 #import <WebCore/NotImplemented.h>
 #import <WebCore/PlatformScreen.h>
-#import <WebCore/PromisedBlobInfo.h>
+#import <WebCore/PromisedAttachmentInfo.h>
 #import <WebCore/SharedBuffer.h>
 #import <WebCore/TextIndicator.h>
 #import <WebCore/ValidationBubble.h>
index cc45c5c..a22830a 100644 (file)
@@ -61,7 +61,7 @@ class Color;
 class FloatQuad;
 class IntSize;
 class SelectionRect;
-struct PromisedBlobInfo;
+struct PromisedAttachmentInfo;
 enum class RouteSharingPolicy;
 }
 
index e829d45..0096049 100644 (file)
@@ -80,7 +80,7 @@
 #import <WebCore/Pasteboard.h>
 #import <WebCore/Path.h>
 #import <WebCore/PathUtilities.h>
-#import <WebCore/PromisedBlobInfo.h>
+#import <WebCore/PromisedAttachmentInfo.h>
 #import <WebCore/RuntimeApplicationChecks.h>
 #import <WebCore/Scrollbar.h>
 #import <WebCore/TextIndicator.h>
@@ -4873,8 +4873,8 @@ static BOOL shouldEnableDragInteractionForPolicy(_WKDragInteractionPolicy policy
 {
     ASSERT(item.sourceAction != DragSourceActionNone);
 
-    if (item.promisedBlob)
-        [self _prepareToDragPromisedBlob:item.promisedBlob];
+    if (item.promisedAttachmentInfo)
+        [self _prepareToDragPromisedAttachment:item.promisedAttachmentInfo];
 
     auto dragImage = adoptNS([[UIImage alloc] initWithCGImage:image.get() scale:_page->deviceScaleFactor() orientation:UIImageOrientationUp]);
     _dragDropInteractionState.stageDragItem(item, dragImage.get());
@@ -5075,7 +5075,7 @@ static NSArray<UIItemProvider *> *extractItemProvidersFromDropSession(id <UIDrop
     [_editDropCaretView updateToPosition:[WKTextPosition textPositionWithRect:rect]];
 }
 
-- (void)_prepareToDragPromisedBlob:(const PromisedBlobInfo&)info
+- (void)_prepareToDragPromisedAttachment:(const PromisedAttachmentInfo&)info
 {
     auto session = retainPtr(_dragDropInteractionState.dragSession());
     if (!session) {
@@ -5086,7 +5086,7 @@ static NSArray<UIItemProvider *> *extractItemProvidersFromDropSession(id <UIDrop
     auto numberOfAdditionalTypes = info.additionalTypes.size();
     ASSERT(numberOfAdditionalTypes == info.additionalData.size());
 
-    RELEASE_LOG(DragAndDrop, "Drag session: %p preparing to drag blob: %s", session.get(), info.blobURL.string().utf8().data());
+    RELEASE_LOG(DragAndDrop, "Drag session: %p preparing to drag blob: %s with attachment identifier: %s", session.get(), info.blobURL.string().utf8().data(), info.attachmentIdentifier.utf8().data());
 
     auto registrationList = adoptNS([[WebItemProviderRegistrationInfoList alloc] init]);
     [registrationList setPreferredPresentationStyle:WebPreferredPresentationStyleAttachment];
@@ -5099,7 +5099,7 @@ static NSArray<UIItemProvider *> *extractItemProvidersFromDropSession(id <UIDrop
         }
     }
 
-    [registrationList addPromisedType:info.contentType fileCallback:[session = WTFMove(session), weakSelf = WeakObjCPtr<WKContentView>(self), url = info.blobURL] (WebItemProviderFileCallback callback) {
+    [registrationList addPromisedType:info.contentType fileCallback:[session = WTFMove(session), weakSelf = WeakObjCPtr<WKContentView>(self), info] (WebItemProviderFileCallback callback) {
         auto strongSelf = weakSelf.get();
         if (!strongSelf) {
             callback(nil, [NSError errorWithDomain:WKErrorDomain code:WKErrorWebViewInvalidated userInfo:nil]);
@@ -5109,13 +5109,24 @@ static NSArray<UIItemProvider *> *extractItemProvidersFromDropSession(id <UIDrop
         NSString *temporaryBlobDirectory = FileSystem::createTemporaryDirectory(@"blobs");
         NSURL *destinationURL = [NSURL fileURLWithPath:[temporaryBlobDirectory stringByAppendingPathComponent:[NSUUID UUID].UUIDString] isDirectory:NO];
 
-        RELEASE_LOG(DragAndDrop, "Drag session: %p delivering promised blob at path: %@", session.get(), destinationURL.path);
-        strongSelf->_page->writeBlobToFilePath(url, destinationURL.path, [protectedURL = retainPtr(destinationURL), protectedCallback = makeBlockPtr(callback)] (bool success) {
-            if (success)
-                protectedCallback(protectedURL.get(), nil);
+        auto attachment = strongSelf->_page->attachmentForIdentifier(info.attachmentIdentifier);
+        if (attachment && attachment->fileWrapper()) {
+            RELEASE_LOG(DragAndDrop, "Drag session: %p delivering promised attachment: %s at path: %@", session.get(), info.attachmentIdentifier.utf8().data(), destinationURL.path);
+            NSError *fileWrapperError = nil;
+            if ([attachment->fileWrapper() writeToURL:destinationURL options:0 originalContentsURL:nil error:&fileWrapperError])
+                callback(destinationURL, nil);
             else
-                protectedCallback(nil, [NSError errorWithDomain:WKErrorDomain code:WKErrorUnknown userInfo:nil]);
-        });
+                callback(nil, fileWrapperError);
+        } else if (!info.blobURL.isEmpty()) {
+            RELEASE_LOG(DragAndDrop, "Drag session: %p delivering promised blob at path: %@", session.get(), destinationURL.path);
+            strongSelf->_page->writeBlobToFilePath(info.blobURL, destinationURL.path, [protectedURL = retainPtr(destinationURL), protectedCallback = makeBlockPtr(callback)] (bool success) {
+                if (success)
+                    protectedCallback(protectedURL.get(), nil);
+                else
+                    protectedCallback(nil, [NSError errorWithDomain:WKErrorDomain code:WKErrorUnknown userInfo:nil]);
+            });
+        } else
+            callback(nil, [NSError errorWithDomain:WKErrorDomain code:WKErrorWebViewInvalidated userInfo:nil]);
 
         [ensureLocalDragSessionContext(session.get()) addTemporaryDirectory:temporaryBlobDirectory];
     }];
index 972ca64..c884fa2 100644 (file)
@@ -38,7 +38,7 @@
 namespace WebCore {
 class AlternativeTextUIController;
 struct DragItem;
-struct PromisedBlobInfo;
+struct PromisedAttachmentInfo;
 }
 
 namespace WebKit {
index 3a59379..0857f14 100644 (file)
@@ -70,7 +70,7 @@
 #import <WebCore/KeyboardEvent.h>
 #import <WebCore/NotImplemented.h>
 #import <WebCore/PlatformScreen.h>
-#import <WebCore/PromisedBlobInfo.h>
+#import <WebCore/PromisedAttachmentInfo.h>
 #import <WebCore/SharedBuffer.h>
 #import <WebCore/TextIndicator.h>
 #import <WebCore/TextIndicatorWindow.h>
index fe5b605..51d7789 100644 (file)
@@ -160,6 +160,21 @@ bool WebEditorClient::shouldApplyStyle(StyleProperties* style, Range* range)
 
 #if ENABLE(ATTACHMENT_ELEMENT)
 
+void WebEditorClient::registerAttachmentIdentifier(const String& identifier, const String& contentType, const String& preferredFileName, Ref<SharedBuffer>&& data)
+{
+    m_page->send(Messages::WebPageProxy::RegisterAttachmentIdentifierFromData(identifier, contentType, preferredFileName, IPC::SharedBufferDataReference { data.ptr() }));
+}
+
+void WebEditorClient::registerAttachmentIdentifier(const String& identifier, const String& contentType, const String& filePath)
+{
+    m_page->send(Messages::WebPageProxy::RegisterAttachmentIdentifierFromFilePath(identifier, contentType, filePath));
+}
+
+void WebEditorClient::cloneAttachmentData(const String& fromIdentifier, const String& toIdentifier)
+{
+    m_page->send(Messages::WebPageProxy::CloneAttachmentData(fromIdentifier, toIdentifier));
+}
+
 void WebEditorClient::didInsertAttachment(const String& identifier, const String& source)
 {
     m_page->send(Messages::WebPageProxy::DidInsertAttachment(identifier, source));
index 75c675a..6df441f 100644 (file)
@@ -60,8 +60,12 @@ private:
     bool shouldMoveRangeAfterDelete(WebCore::Range*, WebCore::Range*) final;
 
 #if ENABLE(ATTACHMENT_ELEMENT)
+    void registerAttachmentIdentifier(const String& identifier, const String& contentType, const String& preferredFileName, Ref<WebCore::SharedBuffer>&&) final;
+    void registerAttachmentIdentifier(const String& identifier, const String& contentType, const String& filePath) final;
+    void cloneAttachmentData(const String& fromIdentifier, const String& toIdentifier) final;
     void didInsertAttachment(const String& identifier, const String& source) final;
     void didRemoveAttachment(const String& identifier) final;
+    bool supportsClientSideAttachmentData() const final { return true; }
 #endif
 
     void didBeginEditing() final;
index 17a0064..c2bba02 100644 (file)
 #include <WebCore/PlatformKeyboardEvent.h>
 #include <WebCore/PluginDocument.h>
 #include <WebCore/PrintContext.h>
-#include <WebCore/PromisedBlobInfo.h>
+#include <WebCore/PromisedAttachmentInfo.h>
 #include <WebCore/Range.h>
 #include <WebCore/RemoteDOMWindow.h>
 #include <WebCore/RemoteFrame.h>
@@ -6073,36 +6073,23 @@ void WebPage::storageAccessResponse(bool wasGranted, uint64_t contextId)
 
 #if ENABLE(ATTACHMENT_ELEMENT)
 
-void WebPage::insertAttachment(const String& identifier, const AttachmentDisplayOptions& options, const String& filename, std::optional<String> contentType, const IPC::DataReference& data, CallbackID callbackID)
+void WebPage::insertAttachment(const String& identifier, const AttachmentDisplayOptions& options, uint64_t fileSize, const String& fileName, std::optional<String> contentType, CallbackID callbackID)
 {
     auto& frame = m_page->focusController().focusedOrMainFrame();
-    frame.editor().insertAttachment(identifier, options, filename, SharedBuffer::create(data.data(), data.size()), contentType);
+    frame.editor().insertAttachment(identifier, options, fileSize, fileName, WTFMove(contentType));
     send(Messages::WebPageProxy::VoidCallback(callbackID));
 }
 
-void WebPage::requestAttachmentInfo(const String& identifier, CallbackID callbackID)
-{
-    auto attachment = attachmentElementWithIdentifier(identifier);
-    if (!attachment) {
-        send(Messages::WebPageProxy::AttachmentInfoCallback({ }, callbackID));
-        return;
-    }
-
-    attachment->requestInfo([callbackID, protectedThis = makeRef(*this), protectedAttachment = WTFMove(attachment)] (const AttachmentInfo& info) {
-        protectedThis->send(Messages::WebPageProxy::AttachmentInfoCallback(info, callbackID));
-    });
-}
-
 void WebPage::setAttachmentDisplayOptions(const String&, const AttachmentDisplayOptions&, CallbackID callbackID)
 {
     send(Messages::WebPageProxy::VoidCallback(callbackID));
 }
 
-void WebPage::setAttachmentDataAndContentType(const String& identifier, const IPC::DataReference& data, std::optional<String> newContentType, std::optional<String> newFilename, CallbackID callbackID)
+void WebPage::updateAttachmentAttributes(const String& identifier, uint64_t fileSize, std::optional<String> newContentType, std::optional<String> newFilename, CallbackID callbackID)
 {
     if (auto attachment = attachmentElementWithIdentifier(identifier)) {
         attachment->document().updateLayout();
-        attachment->updateFileWithData(SharedBuffer::create(data.data(), data.size()), WTFMove(newContentType), WTFMove(newFilename));
+        attachment->updateAttributes(fileSize, WTFMove(newContentType), WTFMove(newFilename));
     }
     send(Messages::WebPageProxy::VoidCallback(callbackID));
 }
index e51a604..6be1cbf 100644 (file)
@@ -168,7 +168,7 @@ struct GlobalFrameIdentifier;
 struct GlobalWindowIdentifier;
 struct Highlight;
 struct KeypressCommand;
-struct PromisedBlobInfo;
+struct PromisedAttachmentInfo;
 struct TextCheckingResult;
 struct ViewportArguments;
 
@@ -1075,10 +1075,9 @@ public:
 #endif
 
 #if ENABLE(ATTACHMENT_ELEMENT)
-    void insertAttachment(const String& identifier, const WebCore::AttachmentDisplayOptions&, const String& filename, std::optional<String> contentType, const IPC::DataReference&, CallbackID);
-    void requestAttachmentInfo(const String& identifier, CallbackID);
+    void insertAttachment(const String& identifier, const WebCore::AttachmentDisplayOptions&, uint64_t fileSize, const String& fileName, std::optional<String> contentType, CallbackID);
     void setAttachmentDisplayOptions(const String& identifier, const WebCore::AttachmentDisplayOptions&, CallbackID);
-    void setAttachmentDataAndContentType(const String& identifier, const IPC::DataReference&, std::optional<String> newContentType, std::optional<String> newFilename, CallbackID);
+    void updateAttachmentAttributes(const String& identifier, uint64_t fileSize, std::optional<String> newContentType, std::optional<String> newFilename, CallbackID);
 #endif
 
 #if ENABLE(APPLICATION_MANIFEST)
index b71f28b..96e487e 100644 (file)
@@ -510,10 +510,9 @@ messages -> WebPage LegacyReceiver {
 #endif
 
 #if ENABLE(ATTACHMENT_ELEMENT)
-    InsertAttachment(String identifier, struct WebCore::AttachmentDisplayOptions options, String filename, std::optional<String> contentType, IPC::DataReference data, WebKit::CallbackID callbackID)
-    RequestAttachmentInfo(String identifier, WebKit::CallbackID callbackID)
+    InsertAttachment(String identifier, struct WebCore::AttachmentDisplayOptions options, uint64_t fileSize, String fileName, std::optional<String> contentType, WebKit::CallbackID callbackID)
     SetAttachmentDisplayOptions(String identifier, struct WebCore::AttachmentDisplayOptions options, WebKit::CallbackID callbackID)
-    SetAttachmentDataAndContentType(String identifier, IPC::DataReference data, std::optional<String> newContentType, std::optional<String> newFilename, WebKit::CallbackID callbackID)
+    UpdateAttachmentAttributes(String identifier, uint64_t fileSize, std::optional<String> newContentType, std::optional<String> newFilename, WebKit::CallbackID callbackID)
 #endif
 
 #if ENABLE(APPLICATION_MANIFEST)
index 2103f6e..283618c 100644 (file)
@@ -1,3 +1,16 @@
+2018-08-21  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [Attachment Support] Augment _WKAttachment SPI to handle NSFileWrappers in addition to NSData
+        https://bugs.webkit.org/show_bug.cgi?id=188496
+        <rdar://problem/43216836>
+
+        Reviewed by Tim Horton.
+
+        Adjust for changing PromisedAttachmentInfo.h to forward declare WebCore::SharedBuffer rather than include the
+        header directly.
+
+        * WebCoreSupport/WebDragClient.cpp:
+
 2018-08-17  Alex Christensen  <achristensen@webkit.org>
 
         Add some plumbing for safe browsing
index f60091f..1bc1352 100644 (file)
@@ -39,6 +39,7 @@
 #include <WebCore/Page.h>
 #include <WebCore/Pasteboard.h>
 #include <WebCore/PlatformMouseEvent.h>
+#include <WebCore/SharedBuffer.h>
 #include <shlobj.h>
 
 using namespace WebCore;
index 63d2a01..1926943 100644 (file)
@@ -1,3 +1,43 @@
+2018-08-21  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [Attachment Support] Augment _WKAttachment SPI to handle NSFileWrappers in addition to NSData
+        https://bugs.webkit.org/show_bug.cgi?id=188496
+        <rdar://problem/43216836>
+
+        Reviewed by Tim Horton.
+
+        Adjusts existing attachment API tests. See below for more detail.
+
+        * TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm:
+        (-[TestWKWebView synchronouslyInsertAttachmentWithFileWrapper:contentType:]):
+        (-[TestWKWebView synchronouslyInsertAttachmentWithFilename:contentType:data:]):
+        (-[_WKAttachment synchronouslySetData:newContentType:newFilename:error:]):
+        (-[_WKAttachment synchronouslySetFileWrapper:newContentType:error:]):
+
+        Move off of deprecated attachment SPI, and add new helper functions to synchronously insert a new attachment or
+        update an existing attachment with a file wrapper.
+
+        (-[_WKAttachment expectRequestedDataToBe:]):
+        (TestWebKitAPI::TEST):
+
+        Add a new test to verify that file-URL-backed NSFileWrappers can be used to insert and update attachment data.
+        Also augment an existing test to check that an attachment element which has been copied and pasted within the
+        same document has a different _WKAttachment wrapper object than its duplicate, but both _WKAttachments are
+        backed by the same NSFileWrapper that was originally used to insert the attachment.
+
+        Additionally, add another macOS test to verify that dropping promised files in an attachment-element-enabled
+        editable area inserts attachment elements into the document and notifies the UI client with the inserted
+        attachment data.
+
+        (-[_WKAttachment synchronouslyRequestInfo:]): Deleted.
+        (-[_WKAttachment synchronouslyRequestData:]): Deleted.
+        * TestWebKitAPI/ios/DragAndDropSimulatorIOS.mm:
+        (-[DragAndDropSimulator _webView:didInsertAttachment:withSource:]):
+
+        Move off of -_webView:didInsertAttachment:.
+
+        (-[DragAndDropSimulator _webView:didInsertAttachment:]): Deleted.
+
 2018-08-21  David Kilzer  <ddkilzer@apple.com>
 
         sort-Xcode-project-file: top-level files and folders are sorted unintentionally when `mainGroup` has no name
index 2f7c503..953f5a6 100644 (file)
@@ -145,13 +145,17 @@ private:
 @interface TestWKWebView (AttachmentTesting)
 @end
 
+static NSString *attachmentEditingTestMarkup = @"<meta name='viewport' content='width=device-width, initial-scale=1'>"
+    "<script>focus = () => document.body.focus()</script>"
+    "<body onload=focus() contenteditable></body>";
+
 static RetainPtr<TestWKWebView> webViewForTestingAttachments(CGSize webViewSize, WKWebViewConfiguration *configuration)
 {
     configuration._attachmentElementEnabled = YES;
     WKPreferencesSetCustomPasteboardDataEnabled((WKPreferencesRef)[configuration preferences], YES);
 
     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, webViewSize.width, webViewSize.height) configuration:configuration]);
-    [webView synchronouslyLoadHTMLString:@"<meta name='viewport' content='width=device-width, initial-scale=1'><script>focus = () => document.body.focus()</script><body onload=focus() contenteditable></body>"];
+    [webView synchronouslyLoadHTMLString:attachmentEditingTestMarkup];
 
     return webView;
 }
@@ -237,10 +241,23 @@ static NSData *testPDFData()
     return success;
 }
 
+- (_WKAttachment *)synchronouslyInsertAttachmentWithFileWrapper:(NSFileWrapper *)fileWrapper contentType:(NSString *)contentType
+{
+    __block bool done = false;
+    RetainPtr<_WKAttachment> attachment = [self _insertAttachmentWithFileWrapper:fileWrapper contentType:contentType options:nil completion:^(BOOL) {
+        done = true;
+    }];
+    TestWebKitAPI::Util::run(&done);
+    return attachment.autorelease();
+}
+
 - (_WKAttachment *)synchronouslyInsertAttachmentWithFilename:(NSString *)filename contentType:(NSString *)contentType data:(NSData *)data
 {
     __block bool done = false;
-    RetainPtr<_WKAttachment> attachment = [self _insertAttachmentWithFilename:filename contentType:contentType data:data options:nil completion:^(BOOL) {
+    auto fileWrapper = adoptNS([[NSFileWrapper alloc] initRegularFileWithContents:data]);
+    if (filename)
+        [fileWrapper setPreferredFilename:filename];
+    RetainPtr<_WKAttachment> attachment = [self _insertAttachmentWithFileWrapper:fileWrapper.get() contentType:contentType options:nil completion:^(BOOL) {
         done = true;
     }];
     TestWebKitAPI::Util::run(&done);
@@ -312,13 +329,12 @@ static NSData *testPDFData()
         *error = resultError.autorelease();
 }
 
-- (_WKAttachmentInfo *)synchronouslyRequestInfo:(NSError **)error
+- (void)synchronouslySetFileWrapper:(NSFileWrapper *)fileWrapper newContentType:(NSString *)newContentType error:(NSError **)error
 {
-    __block RetainPtr<_WKAttachmentInfo> result;
     __block RetainPtr<NSError> resultError;
     __block bool done = false;
-    [self requestInfo:^(_WKAttachmentInfo *info, NSError *error) {
-        result = info;
+
+    [self setFileWrapper:fileWrapper contentType:newContentType completion:^(NSError *error) {
         resultError = error;
         done = true;
     }];
@@ -327,20 +343,17 @@ static NSData *testPDFData()
 
     if (error)
         *error = resultError.autorelease();
-
-    return result.autorelease();
-}
-
-- (NSData *)synchronouslyRequestData:(NSError **)error
-{
-    return [self synchronouslyRequestInfo:error].data;
 }
 
 - (void)synchronouslySetData:(NSData *)data newContentType:(NSString *)newContentType newFilename:(NSString *)newFilename error:(NSError **)error
 {
     __block RetainPtr<NSError> resultError;
     __block bool done = false;
-    [self setData:data newContentType:newContentType newFilename:newFilename completion:^(NSError *error) {
+    auto fileWrapper = adoptNS([[NSFileWrapper alloc] initRegularFileWithContents:data]);
+    if (newFilename)
+        [fileWrapper setPreferredFilename:newFilename];
+
+    [self setFileWrapper:fileWrapper.get() contentType:newContentType completion:^(NSError *error) {
         resultError = error;
         done = true;
     }];
@@ -354,9 +367,9 @@ static NSData *testPDFData()
 - (void)expectRequestedDataToBe:(NSData *)expectedData
 {
     NSError *requestError = nil;
-    _WKAttachmentInfo *info = [self synchronouslyRequestInfo:&requestError];
+    _WKAttachmentInfo *info = self.info;
 
-    BOOL observedDataIsEqualToExpectedData = [info.data isEqualToData:expectedData] || info.data == expectedData;
+    BOOL observedDataIsEqualToExpectedData = info.data == expectedData || [info.data isEqualToData:expectedData];
     EXPECT_TRUE(observedDataIsEqualToExpectedData);
     if (!observedDataIsEqualToExpectedData) {
         NSLog(@"Expected data: %@ but observed: %@ for %@", [expectedData shortDescription], [info.data shortDescription], self);
@@ -499,7 +512,7 @@ TEST(WKAttachmentTests, AttachmentElementInsertion)
         observer.expectAttachmentUpdates(@[ ], @[ firstAttachment.get() ]);
     }
 
-    _WKAttachmentInfo *info = [firstAttachment synchronouslyRequestInfo:nil];
+    _WKAttachmentInfo *info = [firstAttachment info];
     EXPECT_TRUE([info.data isEqualToData:testHTMLData()]);
     EXPECT_TRUE([info.contentType isEqualToString:@"text/html"]);
     EXPECT_TRUE([info.name isEqualToString:@"foo"]);
@@ -516,7 +529,7 @@ TEST(WKAttachmentTests, AttachmentElementInsertion)
         scope.expectAttachmentUpdates(@[ firstAttachment.get() ], @[ secondAttachment.get() ]);
     }
 
-    [firstAttachment expectRequestedDataToBe:nil];
+    [firstAttachment expectRequestedDataToBe:testHTMLData()];
     [secondAttachment expectRequestedDataToBe:testImageData()];
     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentbloburl" forQuerySelector:@"attachment"]);
     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentpath" forQuerySelector:@"attachment"]);
@@ -539,14 +552,14 @@ TEST(WKAttachmentTests, AttachmentUpdatesWhenInsertingAndDeletingNewline)
 
     [webView expectUpdatesAfterCommand:@"DeleteBackward" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
 
-    _WKAttachmentInfo *info = [attachment synchronouslyRequestInfo:nil];
+    _WKAttachmentInfo *info = [attachment info];
     EXPECT_TRUE([info.data isEqualToData:testHTMLData()]);
     EXPECT_TRUE([info.contentType isEqualToString:@"text/plain"]);
     EXPECT_TRUE([info.name isEqualToString:@"foo.txt"]);
     EXPECT_EQ(info.filePath.length, 0U);
 
     [webView expectUpdatesAfterCommand:@"DeleteForward" withArgument:nil expectedRemovals:@[attachment.get()] expectedInsertions:@[]];
-    [attachment expectRequestedDataToBe:nil];
+    [attachment expectRequestedDataToBe:testHTMLData()];
 }
 
 TEST(WKAttachmentTests, AttachmentUpdatesWhenUndoingAndRedoing)
@@ -560,19 +573,19 @@ TEST(WKAttachmentTests, AttachmentUpdatesWhenUndoingAndRedoing)
         observer.expectAttachmentUpdates(@[ ], @[attachment.get()]);
     }
     [webView expectUpdatesAfterCommand:@"Undo" withArgument:nil expectedRemovals:@[attachment.get()] expectedInsertions:@[]];
-    [attachment expectRequestedDataToBe:nil];
+    [attachment expectRequestedDataToBe:htmlData.get()];
 
     [webView expectUpdatesAfterCommand:@"Redo" withArgument:nil expectedRemovals:@[] expectedInsertions:@[attachment.get()]];
     [attachment expectRequestedDataToBe:htmlData.get()];
 
     [webView expectUpdatesAfterCommand:@"DeleteBackward" withArgument:nil expectedRemovals:@[attachment.get()] expectedInsertions:@[]];
-    [attachment expectRequestedDataToBe:nil];
+    [attachment expectRequestedDataToBe:htmlData.get()];
 
     [webView expectUpdatesAfterCommand:@"Undo" withArgument:nil expectedRemovals:@[] expectedInsertions:@[attachment.get()]];
     [attachment expectRequestedDataToBe:htmlData.get()];
 
     [webView expectUpdatesAfterCommand:@"Redo" withArgument:nil expectedRemovals:@[attachment.get()] expectedInsertions:@[]];
-    [attachment expectRequestedDataToBe:nil];
+    [attachment expectRequestedDataToBe:htmlData.get()];
 }
 
 TEST(WKAttachmentTests, AttachmentUpdatesWhenChangingFontStyles)
@@ -597,7 +610,7 @@ TEST(WKAttachmentTests, AttachmentUpdatesWhenChangingFontStyles)
 
     // Inserting text should delete the current selection, removing the attachment in the process.
     [webView expectUpdatesAfterCommand:@"InsertText" withArgument:@"foo" expectedRemovals:@[attachment.get()] expectedInsertions:@[]];
-    [attachment expectRequestedDataToBe:nil];
+    [attachment expectRequestedDataToBe:testHTMLData()];
 }
 
 TEST(WKAttachmentTests, AttachmentUpdatesWhenInsertingLists)
@@ -661,7 +674,7 @@ TEST(WKAttachmentTests, AttachmentUpdatesWhenCuttingAndPasting)
         [webView _synchronouslyExecuteEditCommand:@"Cut" argument:nil];
         observer.expectAttachmentUpdates(@[attachment.get()], @[]);
     }
-    [attachment expectRequestedDataToBe:nil];
+    [attachment expectRequestedDataToBe:testHTMLData()];
     {
         ObserveAttachmentUpdatesForScope observer(webView.get());
         [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
@@ -689,36 +702,7 @@ TEST(WKAttachmentTests, AttachmentDataForEmptyFile)
         [webView _synchronouslyExecuteEditCommand:@"DeleteBackward" argument:nil];
         scope.expectAttachmentUpdates(@[attachment.get()], @[]);
     }
-    [attachment expectRequestedDataToBe:nil];
-}
-
-TEST(WKAttachmentTests, MultipleSimultaneousAttachmentDataRequests)
-{
-    auto webView = webViewForTestingAttachments();
-    RetainPtr<NSData> htmlData = testHTMLData();
-    RetainPtr<_WKAttachment> attachment;
-    {
-        ObserveAttachmentUpdatesForScope observer(webView.get());
-        attachment = [webView synchronouslyInsertAttachmentWithFilename:@"foo.txt" contentType:@"text/plain" data:htmlData.get()];
-        observer.expectAttachmentUpdates(@[], @[attachment.get()]);
-    }
-    __block RetainPtr<NSData> dataForFirstRequest;
-    __block RetainPtr<NSData> dataForSecondRequest;
-    __block bool done = false;
-    [attachment requestInfo:^(_WKAttachmentInfo *info, NSError *error) {
-        EXPECT_TRUE(!error);
-        dataForFirstRequest = info.data;
-    }];
-    [attachment requestInfo:^(_WKAttachmentInfo *info, NSError *error) {
-        EXPECT_TRUE(!error);
-        dataForSecondRequest = info.data;
-        done = true;
-    }];
-
-    Util::run(&done);
-
-    EXPECT_TRUE([dataForFirstRequest isEqualToData:htmlData.get()]);
-    EXPECT_TRUE([dataForSecondRequest isEqualToData:htmlData.get()]);
+    [attachment expectRequestedDataToBe:[NSData data]];
 }
 
 TEST(WKAttachmentTests, ChangeAttachmentDataAndFileInformation)
@@ -779,7 +763,7 @@ TEST(WKAttachmentTests, InsertPastedImageAsAttachment)
         attachment = observer.observer().inserted[0];
     }
 
-    auto size = platformImageWithData([attachment synchronouslyRequestData:nil]).size;
+    auto size = platformImageWithData([attachment info].data).size;
     EXPECT_EQ(215., size.width);
     EXPECT_EQ(174., size.height);
     EXPECT_WK_STREQ([attachment uniqueIdentifier], [webView stringByEvaluatingJavaScript:@"document.querySelector('attachment').uniqueIdentifier"]);
@@ -833,7 +817,7 @@ TEST(WKAttachmentTests, InsertPastedAttributedStringContainingMultipleAttachment
         EXPECT_EQ(0U, observer.observer().removed.count);
         EXPECT_EQ(3U, observer.observer().inserted.count);
         for (_WKAttachment *attachment in observer.observer().inserted) {
-            NSData *data = [attachment synchronouslyRequestData:nil];
+            NSData *data = attachment.info.data;
             if ([data isEqualToData:testZIPData()])
                 zipAttachment = attachment;
             else if ([data isEqualToData:testPDFData()])
@@ -894,11 +878,12 @@ TEST(WKAttachmentTests, InsertAndRemoveDuplicateAttachment)
 {
     auto webView = webViewForTestingAttachments();
     RetainPtr<NSData> data = testHTMLData();
+    auto fileWrapper = adoptNS([[NSFileWrapper alloc] initRegularFileWithContents:data.get()]);
     RetainPtr<_WKAttachment> originalAttachment;
     RetainPtr<_WKAttachment> pastedAttachment;
     {
         ObserveAttachmentUpdatesForScope observer(webView.get());
-        originalAttachment = [webView synchronouslyInsertAttachmentWithFilename:@"foo.txt" contentType:@"text/plain" data:data.get()];
+        originalAttachment = [webView synchronouslyInsertAttachmentWithFileWrapper:fileWrapper.get() contentType:@"text/plain"];
         EXPECT_EQ(0U, observer.observer().removed.count);
         observer.expectAttachmentUpdates(@[], @[originalAttachment.get()]);
     }
@@ -924,17 +909,22 @@ TEST(WKAttachmentTests, InsertAndRemoveDuplicateAttachment)
         [webView _synchronouslyExecuteEditCommand:@"DeleteBackward" argument:nil];
         observer.expectAttachmentUpdates(@[originalAttachment.get()], @[]);
     }
+
+    EXPECT_FALSE([originalAttachment isEqual:pastedAttachment.get()]);
+    EXPECT_TRUE([[originalAttachment info].fileWrapper isEqual:[pastedAttachment info].fileWrapper]);
+    EXPECT_TRUE([[originalAttachment info].fileWrapper isEqual:fileWrapper.get()]);
 }
 
 TEST(WKAttachmentTests, InsertDuplicateAttachmentAndUpdateData)
 {
     auto webView = webViewForTestingAttachments();
-    RetainPtr<NSData> originalData = testHTMLData();
+    auto originalData = retainPtr(testHTMLData());
+    auto fileWrapper = adoptNS([[NSFileWrapper alloc] initRegularFileWithContents:originalData.get()]);
     RetainPtr<_WKAttachment> originalAttachment;
     RetainPtr<_WKAttachment> pastedAttachment;
     {
         ObserveAttachmentUpdatesForScope observer(webView.get());
-        originalAttachment = [webView synchronouslyInsertAttachmentWithFilename:@"foo.txt" contentType:@"text/plain" data:originalData.get()];
+        originalAttachment = [webView synchronouslyInsertAttachmentWithFileWrapper:fileWrapper.get() contentType:@"text/plain"];
         EXPECT_EQ(0U, observer.observer().removed.count);
         observer.expectAttachmentUpdates(@[], @[originalAttachment.get()]);
     }
@@ -953,6 +943,10 @@ TEST(WKAttachmentTests, InsertDuplicateAttachmentAndUpdateData)
     [originalAttachment synchronouslySetData:updatedData.get() newContentType:nil newFilename:nil error:nil];
     [originalAttachment expectRequestedDataToBe:updatedData.get()];
     [pastedAttachment expectRequestedDataToBe:originalData.get()];
+
+    EXPECT_FALSE([originalAttachment isEqual:pastedAttachment.get()]);
+    EXPECT_FALSE([[originalAttachment info].fileWrapper isEqual:[pastedAttachment info].fileWrapper]);
+    EXPECT_FALSE([[originalAttachment info].fileWrapper isEqual:fileWrapper.get()]);
 }
 
 TEST(WKAttachmentTests, InjectedBundleReplaceURLsWhenPastingAttributedString)
@@ -987,6 +981,37 @@ TEST(WKAttachmentTests, InjectedBundleReplaceURLWhenPastingImage)
     EXPECT_WK_STREQ("cid:foo-bar", [webView valueOfAttribute:@"src" forQuerySelector:@"img"]);
 }
 
+TEST(WKAttachmentTests, InsertAttachmentUsingFileWrapperWithFilePath)
+{
+    auto webView = webViewForTestingAttachments();
+    auto originalFileWrapper = adoptNS([[NSFileWrapper alloc] initWithURL:testImageFileURL() options:0 error:nil]);
+    RetainPtr<_WKAttachment> attachment;
+    {
+        ObserveAttachmentUpdatesForScope observer(webView.get());
+        attachment = [webView synchronouslyInsertAttachmentWithFileWrapper:originalFileWrapper.get() contentType:nil];
+        observer.expectAttachmentUpdates(@[ ], @[ attachment.get() ]);
+    }
+
+    _WKAttachmentInfo *infoBeforeUpdate = [attachment info];
+    EXPECT_WK_STREQ("image/png", infoBeforeUpdate.contentType);
+    EXPECT_WK_STREQ("icon.png", infoBeforeUpdate.name);
+    EXPECT_TRUE([originalFileWrapper isEqual:infoBeforeUpdate.fileWrapper]);
+    [attachment expectRequestedDataToBe:testImageData()];
+
+    auto newFileWrapper = adoptNS([[NSFileWrapper alloc] initWithURL:testPDFFileURL() options:0 error:nil]);
+    {
+        ObserveAttachmentUpdatesForScope observer(webView.get());
+        [attachment synchronouslySetFileWrapper:newFileWrapper.get() newContentType:nil error:nil];
+        observer.expectAttachmentUpdates(@[ ], @[ ]);
+    }
+
+    _WKAttachmentInfo *infoAfterUpdate = [attachment info];
+    EXPECT_WK_STREQ("application/pdf", infoAfterUpdate.contentType);
+    EXPECT_WK_STREQ("test.pdf", infoAfterUpdate.name);
+    EXPECT_TRUE([newFileWrapper isEqual:infoAfterUpdate.fileWrapper]);
+    [attachment expectRequestedDataToBe:testPDFData()];
+}
+
 #pragma mark - Platform-specific tests
 
 #if PLATFORM(MAC)
@@ -1008,17 +1033,15 @@ TEST(WKAttachmentTestsMac, InsertPastedFileURLsAsAttachments)
     }
 
     NSArray<NSData *> *expectedAttachmentData = @[ testPDFData(), testImageData() ];
-    EXPECT_TRUE([expectedAttachmentData containsObject:[[insertedAttachments firstObject] synchronouslyRequestData:nil]]);
-    EXPECT_TRUE([expectedAttachmentData containsObject:[[insertedAttachments lastObject] synchronouslyRequestData:nil]]);
+    EXPECT_TRUE([expectedAttachmentData containsObject:[insertedAttachments firstObject].info.data]);
+    EXPECT_TRUE([expectedAttachmentData containsObject:[insertedAttachments lastObject].info.data]);
     EXPECT_WK_STREQ("application/pdf", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[0].getAttribute('type')"]);
     EXPECT_WK_STREQ("test.pdf", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[0].getAttribute('title')"]);
     EXPECT_WK_STREQ("image/png", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[1].getAttribute('type')"]);
     EXPECT_WK_STREQ("icon.png", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[1].getAttribute('title')"]);
 
-    for (_WKAttachment *attachment in insertedAttachments.get()) {
-        _WKAttachmentInfo *info = [attachment synchronouslyRequestInfo:nil];
-        EXPECT_GT(info.filePath.length, 0U);
-    }
+    for (_WKAttachment *attachment in insertedAttachments.get())
+        EXPECT_GT(attachment.info.filePath.length, 0U);
 
     {
         ObserveAttachmentUpdatesForScope observer(webView.get());
@@ -1027,10 +1050,47 @@ TEST(WKAttachmentTestsMac, InsertPastedFileURLsAsAttachments)
         NSArray<_WKAttachment *> *removedAttachments = [observer.observer() removed];
         EXPECT_EQ(2U, removedAttachments.count);
         EXPECT_TRUE([removedAttachments containsObject:[insertedAttachments firstObject]]);
-        EXPECT_TRUE([removedAttachments.lastObject isEqual:[insertedAttachments lastObject]]);
+        EXPECT_TRUE([removedAttachments containsObject:[insertedAttachments lastObject]]);
     }
 }
 
+TEST(WKAttachmentTestsMac, InsertDroppedFilePromisesAsAttachments)
+{
+    auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+    [configuration _setAttachmentElementEnabled:YES];
+    auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebViewFrame:NSMakeRect(0, 0, 400, 400) configuration:configuration.get()]);
+    TestWKWebView *webView = [simulator webView];
+    [webView synchronouslyLoadHTMLString:attachmentEditingTestMarkup];
+    [simulator writePromisedFiles:@[ testPDFFileURL(), testImageFileURL() ]];
+
+    ObserveAttachmentUpdatesForScope observer(webView);
+    [simulator runFrom:CGPointMake(0, 0) to:CGPointMake(50, 50)];
+    while ([[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantPast]]) {
+        if (observer.observer().inserted.count == 2)
+            break;
+    }
+    EXPECT_EQ(2, [[webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment').length"] intValue]);
+
+    auto insertedAttachments = retainPtr(observer.observer().inserted);
+    NSArray<NSData *> *expectedData = @[ testPDFData(), testImageData() ];
+    for (_WKAttachment *attachment in insertedAttachments.get()) {
+        EXPECT_GT(attachment.info.filePath.length, 0U);
+        EXPECT_TRUE([expectedData containsObject:attachment.info.data]);
+        if ([testPDFData() isEqualToData:attachment.info.data])
+            EXPECT_WK_STREQ("application/pdf", attachment.info.contentType);
+        else if ([testImageData() isEqualToData:attachment.info.data])
+            EXPECT_WK_STREQ("image/png", attachment.info.contentType);
+    }
+
+    [webView _synchronouslyExecuteEditCommand:@"SelectAll" argument:nil];
+    [webView _synchronouslyExecuteEditCommand:@"DeleteBackward" argument:nil];
+    NSArray<_WKAttachment *> *removedAttachments = [observer.observer() removed];
+    EXPECT_EQ(2U, removedAttachments.count);
+    EXPECT_EQ(0, [[webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment').length"] intValue]);
+    EXPECT_TRUE([removedAttachments containsObject:[insertedAttachments firstObject]]);
+    EXPECT_TRUE([removedAttachments containsObject:[insertedAttachments lastObject]]);
+}
+
 #endif // PLATFORM(MAC)
 
 #if PLATFORM(IOS)
@@ -1073,7 +1133,7 @@ TEST(WKAttachmentTestsIOS, InsertDroppedAttributedStringContainingAttachment)
     EXPECT_EQ(0U, [dragAndDropSimulator removedAttachments].count);
     auto attachment = retainPtr([dragAndDropSimulator insertedAttachments].firstObject);
 
-    auto size = platformImageWithData([attachment synchronouslyRequestData:nil]).size;
+    auto size = platformImageWithData([attachment info].data).size;
     EXPECT_EQ(215., size.width);
     EXPECT_EQ(174., size.height);
     EXPECT_WK_STREQ("image/png", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
@@ -1111,13 +1171,8 @@ TEST(WKAttachmentTestsIOS, InsertDroppedRichAndPlainTextFilesAsAttachments)
     EXPECT_EQ(2U, [dragAndDropSimulator insertedAttachments].count);
     EXPECT_EQ(0U, [dragAndDropSimulator removedAttachments].count);
 
-    for (_WKAttachment *attachment in [dragAndDropSimulator insertedAttachments]) {
-        NSError *error = nil;
-        EXPECT_GT([attachment synchronouslyRequestData:&error].length, 0U);
-        EXPECT_TRUE(!error);
-        if (error)
-            NSLog(@"Error: %@", error);
-    }
+    for (_WKAttachment *attachment in [dragAndDropSimulator insertedAttachments])
+        EXPECT_GT([attachment info].data.length, 0U);
 
     EXPECT_EQ(2, [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment').length"].intValue);
     EXPECT_WK_STREQ("hello.rtf", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[0].getAttribute('title')"]);
@@ -1174,13 +1229,8 @@ TEST(WKAttachmentTestsIOS, InsertDroppedItemProvidersInOrder)
     EXPECT_EQ(2U, [dragAndDropSimulator insertedAttachments].count);
     EXPECT_EQ(0U, [dragAndDropSimulator removedAttachments].count);
 
-    for (_WKAttachment *attachment in [dragAndDropSimulator insertedAttachments]) {
-        NSError *error = nil;
-        EXPECT_GT([attachment synchronouslyRequestData:&error].length, 0U);
-        EXPECT_TRUE(!error);
-        if (error)
-            NSLog(@"Error: %@", error);
-    }
+    for (_WKAttachment *attachment in [dragAndDropSimulator insertedAttachments])
+        EXPECT_GT([attachment info].data.length, 0U);
 
     [webView expectElementTagsInOrder:@[ @"ATTACHMENT", @"A", @"ATTACHMENT" ]];
 
index 9e1f21e..0f97a39 100644 (file)
@@ -680,7 +680,7 @@ static NSArray *dragAndDropEventNames()
     return self.overridePerformDropBlock ? self.overridePerformDropBlock(session) : session.items;
 }
 
-- (void)_webView:(WKWebView *)webView didInsertAttachment:(_WKAttachment *)attachment
+- (void)_webView:(WKWebView *)webView didInsertAttachment:(_WKAttachment *)attachment withSource:(NSString *)source
 {
     [_insertedAttachments addObject:attachment];
 }