Title: [226779] trunk
Revision
226779
Author
wenson_hs...@apple.com
Date
2018-01-11 07:41:39 -0800 (Thu, 11 Jan 2018)

Log Message

[Attachment Support] Support dragging attachment elements out as files on iOS
https://bugs.webkit.org/show_bug.cgi?id=181199
<rdar://problem/36299316>

Reviewed by Tim Horton, Andy Estes and Joseph Pecoraro.

Source/WebCore:

Adds support for dragging "files" (i.e. creating item providers with preferred attachment presentation styles)
from attachment elements on iOS for Mail. See below for more detail.

Tests:  WKAttachmentTestsIOS.DragAttachmentInsertedAsData
        WKAttachmentTestsIOS.DragAttachmentInsertedAsFile

* page/DragController.cpp:
(WebCore::DragController::platformContentTypeForBlobType const):
(WebCore::DragController::dragAttachmentElement):
* page/DragController.h:
* page/mac/DragControllerMac.mm:
(WebCore::DragController::platformContentTypeForBlobType const):

Add a private method to convert the type of a promised blob to a platform type. For Cocoa platforms, this
converts the blob type (either a UTI or a MIME type) to a UTI for the platform to consume.

* platform/ios/WebItemProviderPasteboard.h:
* platform/ios/WebItemProviderPasteboard.mm:

Refactor WebItemProviderRegistrationInfo. WebItemProviderRegistrationInfo currently encapsulates a single item
provider registration call, and contains either a type identifier and data buffer, or an NSItemProviderWriting-
conformant object. To register an item provider using a WebItemProviderRegistrationInfo, the item provider
pasteboard currently checks to see whether the info contains an object or a type and data.

This patch removes WebItemProviderRegistrationInfo and replaces it with WebItemProviderDataRegistrar. Objects
that implement this protocol know how to take an NSItemProvider and register data to it. So far, there are
three implementations below.

(-[WebItemProviderDataRegistrar initWithData:type:]):
(-[WebItemProviderDataRegistrar typeIdentifier]):
(-[WebItemProviderDataRegistrar data]):
(-[WebItemProviderDataRegistrar typeIdentifierForClient]):
(-[WebItemProviderDataRegistrar dataForClient]):
(-[WebItemProviderDataRegistrar registerItemProvider:]):
(-[WebItemProviderDataRegistrar description]):

A data registrar takes a UTI and data buffer, and registers the UTI to the data. This replaces a
WebItemProviderRegistrationInfo with both a type and data, but no representing object.

(-[WebItemProviderWritableObjectRegistrar initWithObject:]):
(-[WebItemProviderWritableObjectRegistrar representingObjectForClient]):
(-[WebItemProviderWritableObjectRegistrar registerItemProvider:]):
(-[WebItemProviderWritableObjectRegistrar description]):

The writable object registrar writes an NSItemProviderWriting-conformant object to an item provider. This
replaces a WebItemProviderRegistrationInfo with only a representing object.

(-[WebItemProviderPromisedFileRegistrar initWithType:callback:]):
(-[WebItemProviderPromisedFileRegistrar registerItemProvider:]):
(-[WebItemProviderPromisedFileRegistrar description]):
(-[WebItemProviderRegistrationInfoList addData:forType:]):
(-[WebItemProviderRegistrationInfoList addRepresentingObject:]):
(-[WebItemProviderRegistrationInfoList addPromisedType:fileCallback:]):

Helper methods to add new registrars to a registration info list.

(-[WebItemProviderRegistrationInfoList itemAtIndex:]):
(-[WebItemProviderRegistrationInfoList enumerateItems:]):
(-[WebItemProviderRegistrationInfoList itemProvider]):
(-[WebItemProviderRegistrationInfoList description]):
(-[WebItemProviderRegistrationInfo initWithRepresentingObject:typeIdentifier:data:]): Deleted.
(-[WebItemProviderRegistrationInfo representingObject]): Deleted.
(-[WebItemProviderRegistrationInfo typeIdentifier]): Deleted.

Source/WebKit:

Implement support for registering and beginning a drag with promised blob info. See below for more detail.

* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKDragSessionContext addTemporaryDirectory:]):
(-[WKDragSessionContext cleanUpTemporaryDirectories]):

Introduce WKDragSessionContext, which represents the localContext of a UIDragSession initiated in WebKit. The
blob promise dragging codepath uses this to register temporary directories when saving blob data to a location
on disk; when all data transfers are finished, or if the drag interaction is being reset, we then use
-cleanUpTemporaryDirectories to remove each temporary directory.

(existingLocalDragSessionContext):
(ensureLocalDragSessionContext):

Helper methods to set the UIDragSession's localContext to a WKDragSessionContext and query for any existing
context.

(-[WKContentView cleanupInteraction]):

Before the content view's UIDragInteraction goes away, clean up any temporary directories added to the
UIDragSession.

(-[WKContentView _prepareToDragPromisedBlob:]):

When dragging with a promised blob, register a new item provider on the pasteboard representing the blob data,
along with any additional metadata associated with the blob. For the promise callback, call out to the network
process to write the blob data to a temporary path; when done, call the NSItemProvider's completion handler with
the temporary blob data location.

(-[WKContentView _itemsForBeginningOrAddingToSessionWithRegistrationList:stagedDragSource:]):
(-[WKContentView dragInteraction:sessionDidTransferItems:]):

Use this delegate hook as an opportunity to remove any temporary directories created when promised blob data is
requested upon drop. Since we know the drag session that has finished transferring data, we simply ask its local
context (a WKDragSessionContext) to remove any temporary filepaths it has created.

Tools:

Add support in the drag and drop simulator for testing blob-backed attachment element dragging, and also add new
attachment API tests.

* TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm:
(-[NSItemProvider expectType:withData:]):
(TestWebKitAPI::TEST):

Add two new WKAttachmentTests to exercise dragging data- and file-backed blobs via attachment elements. These
tests first insert attachments via drop or WKWebView SPI, and then drag these attachments out and use the
-expectType:withData: helper to inspect the item providers created from the drag source.

* TestWebKitAPI/Tests/ios/DataInteractionTests.mm:
(TestWebKitAPI::TEST):
* TestWebKitAPI/ios/DataInteractionSimulator.h:
* TestWebKitAPI/ios/DataInteractionSimulator.mm:
(-[MockDragSession localContext]):
(-[MockDragSession setLocalContext:]):
(-[DataInteractionSimulator _resetSimulatedState]):
(-[DataInteractionSimulator simulateAllTouchesCanceled:]):
(-[DataInteractionSimulator _concludeDataInteractionAndPerformOperationIfNecessary]):
(-[DataInteractionSimulator _advanceProgress]):
(-[DataInteractionSimulator endDataTransfer]):

Make some tweaks to the iOS drag and drop simulator. In particular, this patch (1) adds a new hook to tell
WebKit that data transfers have been completed, (2) fixes incorrect drop proposal handling when returning
UIDropOperationForbidden by replacing _shouldPerformOperation with a UIDropProposal, and (3) teach the
MockDragSession to hold on to a localContext.

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (226778 => 226779)


--- trunk/Source/WebCore/ChangeLog	2018-01-11 15:28:38 UTC (rev 226778)
+++ trunk/Source/WebCore/ChangeLog	2018-01-11 15:41:39 UTC (rev 226779)
@@ -1,3 +1,75 @@
+2018-01-11  Wenson Hsieh  <wenson_hs...@apple.com>
+
+        [Attachment Support] Support dragging attachment elements out as files on iOS
+        https://bugs.webkit.org/show_bug.cgi?id=181199
+        <rdar://problem/36299316>
+
+        Reviewed by Tim Horton, Andy Estes and Joseph Pecoraro.
+
+        Adds support for dragging "files" (i.e. creating item providers with preferred attachment presentation styles)
+        from attachment elements on iOS for Mail. See below for more detail.
+
+        Tests:  WKAttachmentTestsIOS.DragAttachmentInsertedAsData
+                WKAttachmentTestsIOS.DragAttachmentInsertedAsFile
+
+        * page/DragController.cpp:
+        (WebCore::DragController::platformContentTypeForBlobType const):
+        (WebCore::DragController::dragAttachmentElement):
+        * page/DragController.h:
+        * page/mac/DragControllerMac.mm:
+        (WebCore::DragController::platformContentTypeForBlobType const):
+
+        Add a private method to convert the type of a promised blob to a platform type. For Cocoa platforms, this
+        converts the blob type (either a UTI or a MIME type) to a UTI for the platform to consume.
+
+        * platform/ios/WebItemProviderPasteboard.h:
+        * platform/ios/WebItemProviderPasteboard.mm:
+
+        Refactor WebItemProviderRegistrationInfo. WebItemProviderRegistrationInfo currently encapsulates a single item
+        provider registration call, and contains either a type identifier and data buffer, or an NSItemProviderWriting-
+        conformant object. To register an item provider using a WebItemProviderRegistrationInfo, the item provider
+        pasteboard currently checks to see whether the info contains an object or a type and data.
+
+        This patch removes WebItemProviderRegistrationInfo and replaces it with WebItemProviderDataRegistrar. Objects
+        that implement this protocol know how to take an NSItemProvider and register data to it. So far, there are
+        three implementations below.
+
+        (-[WebItemProviderDataRegistrar initWithData:type:]):
+        (-[WebItemProviderDataRegistrar typeIdentifier]):
+        (-[WebItemProviderDataRegistrar data]):
+        (-[WebItemProviderDataRegistrar typeIdentifierForClient]):
+        (-[WebItemProviderDataRegistrar dataForClient]):
+        (-[WebItemProviderDataRegistrar registerItemProvider:]):
+        (-[WebItemProviderDataRegistrar description]):
+
+        A data registrar takes a UTI and data buffer, and registers the UTI to the data. This replaces a
+        WebItemProviderRegistrationInfo with both a type and data, but no representing object.
+
+        (-[WebItemProviderWritableObjectRegistrar initWithObject:]):
+        (-[WebItemProviderWritableObjectRegistrar representingObjectForClient]):
+        (-[WebItemProviderWritableObjectRegistrar registerItemProvider:]):
+        (-[WebItemProviderWritableObjectRegistrar description]):
+
+        The writable object registrar writes an NSItemProviderWriting-conformant object to an item provider. This
+        replaces a WebItemProviderRegistrationInfo with only a representing object.
+
+        (-[WebItemProviderPromisedFileRegistrar initWithType:callback:]):
+        (-[WebItemProviderPromisedFileRegistrar registerItemProvider:]):
+        (-[WebItemProviderPromisedFileRegistrar description]):
+        (-[WebItemProviderRegistrationInfoList addData:forType:]):
+        (-[WebItemProviderRegistrationInfoList addRepresentingObject:]):
+        (-[WebItemProviderRegistrationInfoList addPromisedType:fileCallback:]):
+
+        Helper methods to add new registrars to a registration info list.
+
+        (-[WebItemProviderRegistrationInfoList itemAtIndex:]):
+        (-[WebItemProviderRegistrationInfoList enumerateItems:]):
+        (-[WebItemProviderRegistrationInfoList itemProvider]):
+        (-[WebItemProviderRegistrationInfoList description]):
+        (-[WebItemProviderRegistrationInfo initWithRepresentingObject:typeIdentifier:data:]): Deleted.
+        (-[WebItemProviderRegistrationInfo representingObject]): Deleted.
+        (-[WebItemProviderRegistrationInfo typeIdentifier]): Deleted.
+
 2018-01-11  Michael Saboff  <msab...@apple.com>
 
         Add a DOM gadget for Spectre testing

Modified: trunk/Source/WebCore/page/DragController.cpp (226778 => 226779)


--- trunk/Source/WebCore/page/DragController.cpp	2018-01-11 15:28:38 UTC (rev 226778)
+++ trunk/Source/WebCore/page/DragController.cpp	2018-01-11 15:41:39 UTC (rev 226779)
@@ -1274,6 +1274,15 @@
 #endif
 }
 
+#if !PLATFORM(COCOA)
+
+String DragController::platformContentTypeForBlobType(const String& type) const
+{
+    return type;
+}
+
+#endif
+
 #if ENABLE(ATTACHMENT_ELEMENT)
 
 bool DragController::dragAttachmentElement(Frame& frame, HTMLAttachmentElement& attachment)
@@ -1289,7 +1298,7 @@
 #endif
 
     auto& file = *attachment.file();
-    m_client.prepareToDragPromisedBlob({ file.url(), file.type(), file.name(), additionalTypes, additionalData });
+    m_client.prepareToDragPromisedBlob({ file.url(), platformContentTypeForBlobType(file.type()), file.name(), WTFMove(additionalTypes), WTFMove(additionalData) });
 
     return true;
 }

Modified: trunk/Source/WebCore/page/DragController.h (226778 => 226779)


--- trunk/Source/WebCore/page/DragController.h	2018-01-11 15:28:38 UTC (rev 226778)
+++ trunk/Source/WebCore/page/DragController.h	2018-01-11 15:41:39 UTC (rev 226779)
@@ -128,6 +128,8 @@
 #endif
         }
 
+        String platformContentTypeForBlobType(const String& type) const;
+
         void cleanupAfterSystemDrag();
         void declareAndWriteDragImage(DataTransfer&, Element&, const URL&, const String& label);
 

Modified: trunk/Source/WebCore/page/mac/DragControllerMac.mm (226778 => 226779)


--- trunk/Source/WebCore/page/mac/DragControllerMac.mm	2018-01-11 15:28:38 UTC (rev 226778)
+++ trunk/Source/WebCore/page/mac/DragControllerMac.mm	2018-01-11 15:41:39 UTC (rev 226779)
@@ -46,6 +46,7 @@
 #import "PlatformStrategies.h"
 #import "Range.h"
 #import "RuntimeEnabledFeatures.h"
+#import "UTIUtilities.h"
 
 #if ENABLE(DATA_INTERACTION)
 #import <MobileCoreServices/MobileCoreServices.h>
@@ -91,6 +92,14 @@
     return maxDragImageSize;
 }
 
+String DragController::platformContentTypeForBlobType(const String& type) const
+{
+    auto utiType = UTIFromMIMEType(type);
+    if (!utiType.isEmpty())
+        return utiType;
+    return type;
+}
+
 void DragController::cleanupAfterSystemDrag()
 {
 #if PLATFORM(MAC)

Modified: trunk/Source/WebCore/platform/ios/WebItemProviderPasteboard.h (226778 => 226779)


--- trunk/Source/WebCore/platform/ios/WebItemProviderPasteboard.h	2018-01-11 15:28:38 UTC (rev 226778)
+++ trunk/Source/WebCore/platform/ios/WebItemProviderPasteboard.h	2018-01-11 15:41:39 UTC (rev 226779)
@@ -40,20 +40,23 @@
 
 NS_ASSUME_NONNULL_BEGIN
 
-/*! A WebItemProviderRegistrationInfo represents a single call to register something to an item provider.
- @discussion Either the representing object exists and the type identifier and data are nil, or the
- representing object is nil and the type identifier and data exist. The former represents a call to
- register an entire UIItemProviderWriting-conformant object to the item provider, while the latter
- represents a call to register only a data representation for the given type identifier.
+/*! A WebItemProviderRegistrar encapsulates a single call to register something to an item provider.
+ @discussion Classes that implement this protocol each represent a different way of writing data to
+ an item provider. Some examples include setting a chunk of data corresponding to a type identifier,
+ or registering a NSItemProviderWriting-conformant object, or registering a type to a promised file
+ where the data has been written.
  */
-WEBCORE_EXPORT @interface WebItemProviderRegistrationInfo : NSObject
+@protocol WebItemProviderRegistrar <NSObject>
+- (void)registerItemProvider:(NSItemProvider *)itemProvider;
 
-@property (nonatomic, readonly, nullable, strong) id <UIItemProviderWriting> representingObject;
-@property (nonatomic, readonly, nullable, strong) NSString *typeIdentifier;
-@property (nonatomic, readonly, nullable, strong) NSData *data;
-
+@optional
+@property (nonatomic, readonly) id <NSItemProviderWriting> representingObjectForClient;
+@property (nonatomic, readonly) NSString *typeIdentifierForClient;
+@property (nonatomic, readonly) NSData *dataForClient;
 @end
 
+typedef void(^WebItemProviderFileCallback)(NSURL * _Nullable, NSError * _Nullable);
+
 /*! A WebItemProviderRegistrationInfoList represents a series of registration calls used to set up a
  single item provider.
  @discussion The order of items specified in the list (lowest indices first) is the order in which
@@ -65,6 +68,7 @@
 
 - (void)addRepresentingObject:(id <UIItemProviderWriting>)object;
 - (void)addData:(NSData *)data forType:(NSString *)typeIdentifier;
+- (void)addPromisedType:(NSString *)typeIdentifier fileCallback:(void(^)(WebItemProviderFileCallback))callback;
 
 @property (nonatomic) CGSize preferredPresentationSize;
 @property (nonatomic, copy) NSString *suggestedName;
@@ -74,8 +78,8 @@
 @property (nonatomic, copy) NSData *teamData;
 
 - (NSUInteger)numberOfItems;
-- (nullable WebItemProviderRegistrationInfo *)itemAtIndex:(NSUInteger)index;
-- (void)enumerateItems:(void(^)(WebItemProviderRegistrationInfo *item, NSUInteger index))block;
+- (nullable id <WebItemProviderRegistrar>)itemAtIndex:(NSUInteger)index;
+- (void)enumerateItems:(void(^)(id <WebItemProviderRegistrar> item, NSUInteger index))block;
 
 @end
 

Modified: trunk/Source/WebCore/platform/ios/WebItemProviderPasteboard.mm (226778 => 226779)


--- trunk/Source/WebCore/platform/ios/WebItemProviderPasteboard.mm	2018-01-11 15:28:38 UTC (rev 226778)
+++ trunk/Source/WebCore/platform/ios/WebItemProviderPasteboard.mm	2018-01-11 15:41:39 UTC (rev 226779)
@@ -48,32 +48,83 @@
 SOFT_LINK_CLASS(UIKit, UIItemProvider)
 
 typedef void(^ItemProviderDataLoadCompletionHandler)(NSData *, NSError *);
+typedef void(^ItemProviderFileLoadCompletionHandler)(NSURL *, BOOL, NSError *);
 typedef NSMutableDictionary<NSString *, NSURL *> TypeToFileURLMap;
 
 using WebCore::Pasteboard;
 using WebCore::PasteboardCustomData;
-@interface WebItemProviderRegistrationInfo ()
-{
-    RetainPtr<id <UIItemProviderWriting>> _representingObject;
+
+@interface WebItemProviderDataRegistrar : NSObject <WebItemProviderRegistrar>
+- (instancetype)initWithData:(NSData *)data type:(NSString *)utiType;
+@property (nonatomic, readonly) NSString *typeIdentifier;
+@property (nonatomic, readonly) NSData *data;
+@end
+
+@implementation WebItemProviderDataRegistrar {
     RetainPtr<NSString> _typeIdentifier;
     RetainPtr<NSData> _data;
 }
+
+- (instancetype)initWithData:(NSData *)data type:(NSString *)utiType
+{
+    if (!(self = [super init]))
+        return nil;
+
+    _data = data;
+    _typeIdentifier = utiType;
+    return self;
+}
+
+- (NSString *)typeIdentifier
+{
+    return _typeIdentifier.get();
+}
+
+- (NSData *)data
+{
+    return _data.get();
+}
+
+- (NSString *)typeIdentifierForClient
+{
+    return self.typeIdentifier;
+}
+
+- (NSData *)dataForClient
+{
+    return self.data;
+}
+
+- (void)registerItemProvider:(NSItemProvider *)itemProvider
+{
+    [itemProvider registerDataRepresentationForTypeIdentifier:self.typeIdentifier visibility:UIItemProviderRepresentationOptionsVisibilityAll loadHandler:[itemData = _data] (ItemProviderDataLoadCompletionHandler completionHandler) -> NSProgress * {
+        completionHandler(itemData.get(), nil);
+        return nil;
+    }];
+}
+
+- (NSString *)description
+{
+    return [NSString stringWithFormat:@"(%@ => %tu bytes)", _typeIdentifier.get(), [_data length]];
+}
+
 @end
 
-@implementation WebItemProviderRegistrationInfo
+@interface WebItemProviderWritableObjectRegistrar : NSObject <WebItemProviderRegistrar>
+- (instancetype)initWithObject:(id <UIItemProviderWriting>)representingObject;
+@property (nonatomic, readonly) id <UIItemProviderWriting> representingObject;
+@end
 
-- (instancetype)initWithRepresentingObject:(id <UIItemProviderWriting>)representingObject typeIdentifier:(NSString *)typeIdentifier data:(NSData *)data
+@implementation WebItemProviderWritableObjectRegistrar {
+    RetainPtr<id <UIItemProviderWriting>> _representingObject;
+}
+
+- (instancetype)initWithObject:(id <UIItemProviderWriting>)representingObject
 {
-    if (representingObject)
-        ASSERT(!typeIdentifier && !data);
-    else
-        ASSERT(typeIdentifier && data);
+    if (!(self = [super init]))
+        return nil;
 
-    if (self = [super init]) {
-        _representingObject = representingObject;
-        _typeIdentifier = typeIdentifier;
-        _data = data;
-    }
+    _representingObject = representingObject;
     return self;
 }
 
@@ -82,14 +133,61 @@
     return _representingObject.get();
 }
 
+- (id <NSItemProviderWriting>)representingObjectForClient
+{
+    return self.representingObject;
+}
+
+- (void)registerItemProvider:(NSItemProvider *)itemProvider
+{
+    [itemProvider registerObject:self.representingObject visibility:UIItemProviderRepresentationOptionsVisibilityAll];
+}
+
+- (NSString *)description
+{
+    return [NSString stringWithFormat:@"(%@)", [_representingObject class]];
+}
+
+@end
+
+@interface WebItemProviderPromisedFileRegistrar : NSObject <WebItemProviderRegistrar>
+- (instancetype)initWithType:(NSString *)utiType callback:(void(^)(WebItemProviderFileCallback))callback;
+@property (nonatomic, readonly) NSString *typeIdentifier;
+@end
+
+@implementation WebItemProviderPromisedFileRegistrar {
+    RetainPtr<NSString> _typeIdentifier;
+    BlockPtr<void(WebItemProviderFileCallback)> _callback;
+}
+
+- (instancetype)initWithType:(NSString *)utiType callback:(void(^)(WebItemProviderFileCallback))callback
+{
+    if (!(self = [super init]))
+        return nil;
+
+    _typeIdentifier = utiType;
+    _callback = callback;
+    return self;
+}
+
+- (void)registerItemProvider:(NSItemProvider *)itemProvider
+{
+    [itemProvider registerFileRepresentationForTypeIdentifier:_typeIdentifier.get() fileOptions:0 visibility:NSItemProviderRepresentationVisibilityAll loadHandler:[callback = _callback] (ItemProviderFileLoadCompletionHandler completionHandler) -> NSProgress * {
+        callback([protectedCompletionHandler = makeBlockPtr(completionHandler)] (NSURL *fileURL, NSError *error) {
+            protectedCompletionHandler(fileURL, NO, error);
+        });
+        return nil;
+    }];
+}
+
 - (NSString *)typeIdentifier
 {
     return _typeIdentifier.get();
 }
 
-- (NSData *)data
+- (NSString *)description
 {
-    return _data.get();
+    return [NSString stringWithFormat:@"(file promise: %@)", _typeIdentifier.get()];
 }
 
 @end
@@ -121,21 +219,29 @@
 
 - (void)addData:(NSData *)data forType:(NSString *)typeIdentifier
 {
-    [_representations addObject:[[[WebItemProviderRegistrationInfo alloc] initWithRepresentingObject:nil typeIdentifier:typeIdentifier data:data] autorelease]];
+    auto representation = adoptNS([[WebItemProviderDataRegistrar alloc] initWithData:data type:typeIdentifier]);
+    [_representations addObject:representation.get()];
 }
 
 - (void)addRepresentingObject:(id <UIItemProviderWriting>)object
 {
     ASSERT([object conformsToProtocol:@protocol(UIItemProviderWriting)]);
-    [_representations addObject:[[[WebItemProviderRegistrationInfo alloc] initWithRepresentingObject:object typeIdentifier:nil data:nil] autorelease]];
+    auto representation = adoptNS([[WebItemProviderWritableObjectRegistrar alloc] initWithObject:object]);
+    [_representations addObject:representation.get()];
 }
 
+- (void)addPromisedType:(NSString *)typeIdentifier fileCallback:(void(^)(WebItemProviderFileCallback))callback
+{
+    auto representation = adoptNS([[WebItemProviderPromisedFileRegistrar alloc] initWithType:typeIdentifier callback:callback]);
+    [_representations addObject:representation.get()];
+}
+
 - (NSUInteger)numberOfItems
 {
     return [_representations count];
 }
 
-- (WebItemProviderRegistrationInfo *)itemAtIndex:(NSUInteger)index
+- (id <WebItemProviderRegistrar>)itemAtIndex:(NSUInteger)index
 {
     if (index >= self.numberOfItems)
         return nil;
@@ -143,7 +249,7 @@
     return [_representations objectAtIndex:index];
 }
 
-- (void)enumerateItems:(void (^)(WebItemProviderRegistrationInfo *, NSUInteger))block
+- (void)enumerateItems:(void (^)(id <WebItemProviderRegistrar>, NSUInteger))block
 {
     for (NSUInteger index = 0; index < self.numberOfItems; ++index)
         block([self itemAtIndex:index], index);
@@ -170,21 +276,8 @@
         return nil;
 
     auto itemProvider = adoptNS([allocUIItemProviderInstance() init]);
-    for (WebItemProviderRegistrationInfo *representation in _representations.get()) {
-        if (representation.representingObject) {
-            [itemProvider registerObject:representation.representingObject visibility:UIItemProviderRepresentationOptionsVisibilityAll];
-            continue;
-        }
-
-        if (!representation.typeIdentifier.length || !representation.data.length)
-            continue;
-
-        RetainPtr<NSData> itemData = representation.data;
-        [itemProvider registerDataRepresentationForTypeIdentifier:representation.typeIdentifier visibility:UIItemProviderRepresentationOptionsVisibilityAll loadHandler:[itemData] (ItemProviderDataLoadCompletionHandler completionHandler) -> NSProgress * {
-            completionHandler(itemData.get(), nil);
-            return nil;
-        }];
-    }
+    for (id <WebItemProviderRegistrar> representation in _representations.get())
+        [representation registerItemProvider:itemProvider.get()];
     [itemProvider setPreferredPresentationSize:self.preferredPresentationSize];
     [itemProvider setSuggestedName:self.suggestedName];
     [itemProvider setPreferredPresentationStyle:uiPreferredPresentationStyle(self.preferredPresentationStyle)];
@@ -196,14 +289,10 @@
 {
     __block NSMutableString *description = [NSMutableString string];
     [description appendFormat:@"<%@: %p", [self class], self];
-    [self enumerateItems:^(WebItemProviderRegistrationInfo *item, NSUInteger index) {
+    [self enumerateItems:^(id <WebItemProviderRegistrar> item, NSUInteger index) {
         if (index)
-            [description appendString:@","];
-
-        if (item.representingObject)
-            [description appendFormat:@" (%@: %p)", [item.representingObject class], item.representingObject];
-        else
-            [description appendFormat:@" ('%@' => %tu bytes)", item.typeIdentifier, item.data.length];
+            [description appendString:@", "];
+        [description appendString:[item description]];
     }];
     [description appendString:@">"];
     return description;

Modified: trunk/Source/WebKit/ChangeLog (226778 => 226779)


--- trunk/Source/WebKit/ChangeLog	2018-01-11 15:28:38 UTC (rev 226778)
+++ trunk/Source/WebKit/ChangeLog	2018-01-11 15:41:39 UTC (rev 226779)
@@ -1,3 +1,47 @@
+2018-01-11  Wenson Hsieh  <wenson_hs...@apple.com>
+
+        [Attachment Support] Support dragging attachment elements out as files on iOS
+        https://bugs.webkit.org/show_bug.cgi?id=181199
+        <rdar://problem/36299316>
+
+        Reviewed by Tim Horton, Andy Estes and Joseph Pecoraro.
+
+        Implement support for registering and beginning a drag with promised blob info. See below for more detail.
+
+        * UIProcess/ios/WKContentViewInteraction.mm:
+        (-[WKDragSessionContext addTemporaryDirectory:]):
+        (-[WKDragSessionContext cleanUpTemporaryDirectories]):
+
+        Introduce WKDragSessionContext, which represents the localContext of a UIDragSession initiated in WebKit. The
+        blob promise dragging codepath uses this to register temporary directories when saving blob data to a location
+        on disk; when all data transfers are finished, or if the drag interaction is being reset, we then use
+        -cleanUpTemporaryDirectories to remove each temporary directory.
+
+        (existingLocalDragSessionContext):
+        (ensureLocalDragSessionContext):
+
+        Helper methods to set the UIDragSession's localContext to a WKDragSessionContext and query for any existing
+        context.
+
+        (-[WKContentView cleanupInteraction]):
+
+        Before the content view's UIDragInteraction goes away, clean up any temporary directories added to the
+        UIDragSession.
+
+        (-[WKContentView _prepareToDragPromisedBlob:]):
+
+        When dragging with a promised blob, register a new item provider on the pasteboard representing the blob data,
+        along with any additional metadata associated with the blob. For the promise callback, call out to the network
+        process to write the blob data to a temporary path; when done, call the NSItemProvider's completion handler with
+        the temporary blob data location.
+
+        (-[WKContentView _itemsForBeginningOrAddingToSessionWithRegistrationList:stagedDragSource:]):
+        (-[WKContentView dragInteraction:sessionDidTransferItems:]):
+
+        Use this delegate hook as an opportunity to remove any temporary directories created when promised blob data is
+        requested upon drop. Since we know the drag session that has finished transferring data, we simply ask its local
+        context (a WKDragSessionContext) to remove any temporary filepaths it has created.
+
 2018-01-10  Jeff Miller  <je...@apple.com>
 
         -[WKWebView _web_gestureEventWasNotHandledByWebCore:] should call -_gestureEventWasNotHandledByWebCore:

Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm (226778 => 226779)


--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2018-01-11 15:28:38 UTC (rev 226778)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2018-01-11 15:41:39 UTC (rev 226779)
@@ -40,6 +40,7 @@
 #import "TextInputSPI.h"
 #import "UIKitSPI.h"
 #import "WKActionSheetAssistant.h"
+#import "WKError.h"
 #import "WKFormInputControl.h"
 #import "WKFormSelectControl.h"
 #import "WKImagePreviewViewController.h"
@@ -53,6 +54,7 @@
 #import "WKWebViewConfigurationPrivate.h"
 #import "WKWebViewInternal.h"
 #import "WKWebViewPrivate.h"
+#import "WeakObjCPtr.h"
 #import "WebEvent.h"
 #import "WebIOSEventFactory.h"
 #import "WebPageMessages.h"
@@ -473,6 +475,57 @@
 }
 @end
 
+#if ENABLE(DRAG_SUPPORT)
+
+@interface WKDragSessionContext : NSObject
+- (void)addTemporaryDirectory:(NSString *)temporaryDirectory;
+- (void)cleanUpTemporaryDirectories;
+@end
+
+@implementation WKDragSessionContext {
+    RetainPtr<NSMutableArray> _temporaryDirectories;
+}
+
+- (void)addTemporaryDirectory:(NSString *)temporaryDirectory
+{
+    if (!_temporaryDirectories)
+        _temporaryDirectories = adoptNS([NSMutableArray new]);
+    [_temporaryDirectories addObject:temporaryDirectory];
+}
+
+- (void)cleanUpTemporaryDirectories
+{
+    for (NSString *directory in _temporaryDirectories.get()) {
+        NSError *error = nil;
+        [[NSFileManager defaultManager] removeItemAtPath:directory error:&error];
+        RELEASE_LOG(DragAndDrop, "Removed temporary download directory: %@ with error: %@", directory, error);
+    }
+    _temporaryDirectories = nil;
+}
+
+@end
+
+static WKDragSessionContext *existingLocalDragSessionContext(id <UIDragSession> session)
+{
+    return [session.localContext isKindOfClass:[WKDragSessionContext class]] ? (WKDragSessionContext *)session.localContext : nil;
+}
+
+static WKDragSessionContext *ensureLocalDragSessionContext(id <UIDragSession> session)
+{
+    if (WKDragSessionContext *existingContext = existingLocalDragSessionContext(session))
+        return existingContext;
+
+    if (session.localContext) {
+        RELEASE_LOG(DragAndDrop, "Overriding existing local context: %@ on session: %@", session.localContext, session);
+        ASSERT_NOT_REACHED();
+    }
+
+    session.localContext = [[[WKDragSessionContext alloc] init] autorelease];
+    return (WKDragSessionContext *)session.localContext;
+}
+
+#endif // ENABLE(DRAG_SUPPORT)
+
 @interface WKContentView (WKInteractionPrivate)
 - (void)accessibilitySpeakSelectionSetContent:(NSString *)string;
 - (NSArray *)webSelectionRectsForSelectionRects:(const Vector<WebCore::SelectionRect>&)selectionRects;
@@ -627,6 +680,7 @@
     _layerTreeTransactionIdAtLastTouchStart = 0;
 
 #if ENABLE(DATA_INTERACTION)
+    [existingLocalDragSessionContext(_dragDropInteractionState.dragSession()) cleanUpTemporaryDirectories];
     [self teardownDataInteractionDelegates];
 #endif
 
@@ -4472,8 +4526,52 @@
 
 - (void)_prepareToDragPromisedBlob:(const PromisedBlobInfo&)info
 {
-    // FIXME: Add iOS support for dragging promised blob data as file promises.
-    UNUSED_PARAM(info);
+    auto session = retainPtr(_dragDropInteractionState.dragSession());
+    if (!session) {
+        ASSERT_NOT_REACHED();
+        return;
+    }
+
+    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());
+
+    auto registrationList = adoptNS([[WebItemProviderRegistrationInfoList alloc] init]);
+    [registrationList setPreferredPresentationStyle:WebPreferredPresentationStyleAttachment];
+    if (!info.filename.isEmpty())
+        [registrationList setSuggestedName:info.filename];
+    if (numberOfAdditionalTypes == info.additionalData.size() && numberOfAdditionalTypes) {
+        for (size_t index = 0; index < numberOfAdditionalTypes; ++index) {
+            auto nsData = info.additionalData[index]->createNSData();
+            [registrationList addData:nsData.get() forType:info.additionalTypes[index]];
+        }
+    }
+
+    [registrationList addPromisedType:info.contentType fileCallback:[session = WTFMove(session), weakSelf = WeakObjCPtr<WKContentView>(self), url = "" (WebItemProviderFileCallback callback) {
+        auto strongSelf = weakSelf.get();
+        if (!strongSelf) {
+            callback(nil, [NSError errorWithDomain:WKErrorDomain code:WKErrorWebViewInvalidated userInfo:nil]);
+            return;
+        }
+
+        NSString *temporaryBlobDirectory = FileSystem::createTemporaryDirectory(@"blobs");
+        NSURL *destinationURL = [NSURL fileURLWithPath:[temporaryBlobDirectory stringByAppendingPathComponent:[NSUUID UUID].UUIDString]];
+
+        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);
+            else
+                protectedCallback(nil, [NSError errorWithDomain:WKErrorDomain code:WKErrorUnknown userInfo:nil]);
+        });
+
+        [ensureLocalDragSessionContext(session.get()) addTemporaryDirectory:temporaryBlobDirectory];
+    }];
+
+    WebItemProviderPasteboard *pasteboard = [WebItemProviderPasteboard sharedInstance];
+    pasteboard.itemProviders = @[ [registrationList itemProvider] ];
+    [pasteboard stageRegistrationList:registrationList.get()];
 }
 
 - (WKDragDestinationAction)_dragDestinationActionForDropSession:(id <UIDropSession>)session
@@ -4514,11 +4612,11 @@
     if ([uiDelegate respondsToSelector:@selector(_webView:adjustedDataInteractionItemProvidersForItemProvider:representingObjects:additionalData:)]) {
         auto representingObjects = adoptNS([[NSMutableArray alloc] init]);
         auto additionalData = adoptNS([[NSMutableDictionary alloc] init]);
-        [registrationList enumerateItems:[representingObjects, additionalData] (WebItemProviderRegistrationInfo *item, NSUInteger) {
-            if (item.representingObject)
-                [representingObjects addObject:item.representingObject];
-            if (item.typeIdentifier && item.data)
-                [additionalData setObject:item.data forKey:item.typeIdentifier];
+        [registrationList enumerateItems:[representingObjects, additionalData] (id <WebItemProviderRegistrar> item, NSUInteger) {
+            if ([item respondsToSelector:@selector(representingObjectForClient)])
+                [representingObjects addObject:item.representingObjectForClient];
+            if ([item respondsToSelector:@selector(typeIdentifierForClient)] && [item respondsToSelector:@selector(dataForClient)])
+                [additionalData setObject:item.dataForClient forKey:item.typeIdentifierForClient];
         }];
         adjustedItemProviders = [uiDelegate _webView:_webView adjustedDataInteractionItemProvidersForItemProvider:defaultItemProvider representingObjects:representingObjects.get() additionalData:additionalData.get()];
     } else
@@ -4718,6 +4816,11 @@
     }];
 }
 
+- (void)dragInteraction:(UIDragInteraction *)interaction sessionDidTransferItems:(id <UIDragSession>)session
+{
+    [existingLocalDragSessionContext(session) cleanUpTemporaryDirectories];
+}
+
 #pragma mark - UIDropInteractionDelegate
 
 - (NSInteger)_dropInteraction:(UIDropInteraction *)interaction dataOwnerForSession:(id <UIDropSession>)session

Modified: trunk/Tools/ChangeLog (226778 => 226779)


--- trunk/Tools/ChangeLog	2018-01-11 15:28:38 UTC (rev 226778)
+++ trunk/Tools/ChangeLog	2018-01-11 15:41:39 UTC (rev 226779)
@@ -1,3 +1,39 @@
+2018-01-11  Wenson Hsieh  <wenson_hs...@apple.com>
+
+        [Attachment Support] Support dragging attachment elements out as files on iOS
+        https://bugs.webkit.org/show_bug.cgi?id=181199
+        <rdar://problem/36299316>
+
+        Reviewed by Tim Horton, Andy Estes and Joseph Pecoraro.
+
+        Add support in the drag and drop simulator for testing blob-backed attachment element dragging, and also add new
+        attachment API tests.
+
+        * TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm:
+        (-[NSItemProvider expectType:withData:]):
+        (TestWebKitAPI::TEST):
+
+        Add two new WKAttachmentTests to exercise dragging data- and file-backed blobs via attachment elements. These
+        tests first insert attachments via drop or WKWebView SPI, and then drag these attachments out and use the
+        -expectType:withData: helper to inspect the item providers created from the drag source.
+
+        * TestWebKitAPI/Tests/ios/DataInteractionTests.mm:
+        (TestWebKitAPI::TEST):
+        * TestWebKitAPI/ios/DataInteractionSimulator.h:
+        * TestWebKitAPI/ios/DataInteractionSimulator.mm:
+        (-[MockDragSession localContext]):
+        (-[MockDragSession setLocalContext:]):
+        (-[DataInteractionSimulator _resetSimulatedState]):
+        (-[DataInteractionSimulator simulateAllTouchesCanceled:]):
+        (-[DataInteractionSimulator _concludeDataInteractionAndPerformOperationIfNecessary]):
+        (-[DataInteractionSimulator _advanceProgress]):
+        (-[DataInteractionSimulator endDataTransfer]):
+
+        Make some tweaks to the iOS drag and drop simulator. In particular, this patch (1) adds a new hook to tell
+        WebKit that data transfers have been completed, (2) fixes incorrect drop proposal handling when returning
+        UIDropOperationForbidden by replacing _shouldPerformOperation with a UIDropProposal, and (3) teach the
+        MockDragSession to hold on to a localContext.
+
 2018-01-11  Ali Juma  <aj...@chromium.org>
 
         Unreviewed. Add Ali Juma as contributor

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm (226778 => 226779)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm	2018-01-11 15:28:38 UTC (rev 226778)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm	2018-01-11 15:41:39 UTC (rev 226779)
@@ -341,6 +341,28 @@
     }];
 }
 
+- (void)expectType:(NSString *)type withData:(NSData *)expectedData
+{
+    BOOL containsType = [self.registeredTypeIdentifiers containsObject:type];
+    EXPECT_TRUE(containsType);
+    if (!containsType) {
+        NSLog(@"Expected: %@ to contain %@", self, type);
+        return;
+    }
+
+    __block bool done = false;
+    [self loadDataRepresentationForTypeIdentifier:type completionHandler:^(NSData *observedData, NSError *error) {
+        EXPECT_TRUE([observedData isEqualToData:expectedData]);
+        if (![observedData isEqualToData:expectedData])
+            NSLog(@"Expected data: <%tu bytes> to be equal to data: <%tu bytes>", observedData.length, expectedData.length);
+        EXPECT_TRUE(!error);
+        if (error)
+            NSLog(@"Encountered error when loading data: %@", error);
+        done = true;
+    }];
+    TestWebKitAPI::Util::run(&done);
+}
+
 @end
 
 #endif // PLATFORM(IOS)
@@ -1228,6 +1250,67 @@
     EXPECT_WK_STREQ("application/pdf", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[1].getAttribute('type')"]);
 }
 
+TEST(WKAttachmentTestsIOS, DragAttachmentInsertedAsFile)
+{
+    auto item = adoptNS([[NSItemProvider alloc] init]);
+    auto data = ""
+    [item registerData:data.get() type:(NSString *)kUTTypePDF];
+    [item setSuggestedName:@"document.pdf"];
+
+    auto webView = webViewForTestingAttachments();
+    auto draggingSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
+    [draggingSimulator setExternalItemProviders:@[ item.get() ]];
+    [draggingSimulator runFrom:CGPointZero to:CGPointMake(50, 50)];
+
+    // First, verify that the attachment was successfully dropped.
+    EXPECT_EQ(1U, [draggingSimulator insertedAttachments].count);
+    _WKAttachment *attachment = [draggingSimulator insertedAttachments].firstObject;
+    [attachment expectRequestedDataToBe:data.get()];
+    EXPECT_WK_STREQ("document.pdf", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
+    EXPECT_WK_STREQ("application/pdf", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
+
+    [webView evaluateJavaScript:@"getSelection().removeAllRanges()" completionHandler:nil];
+    [draggingSimulator setExternalItemProviders:@[ ]];
+    [draggingSimulator runFrom:CGPointMake(25, 25) to:CGPointMake(-100, -100)];
+
+    // Next, verify that dragging the attachment produces an item provider with a PDF attachment.
+    EXPECT_EQ(1U, [draggingSimulator sourceItemProviders].count);
+    NSItemProvider *itemProvider = [draggingSimulator sourceItemProviders].firstObject;
+    EXPECT_EQ(UIPreferredPresentationStyleAttachment, itemProvider.preferredPresentationStyle);
+    [itemProvider expectType:(NSString *)kUTTypePDF withData:data.get()];
+    EXPECT_WK_STREQ("document.pdf", [itemProvider suggestedName]);
+    [draggingSimulator endDataTransfer];
+}
+
+TEST(WKAttachmentTestsIOS, DragAttachmentInsertedAsData)
+{
+    auto webView = webViewForTestingAttachments();
+    auto data = ""
+    RetainPtr<_WKAttachment> attachment;
+    {
+        ObserveAttachmentUpdatesForScope observer(webView.get());
+        attachment = [webView synchronouslyInsertAttachmentWithFilename:@"document.pdf" contentType:@"application/pdf" data:data.get() options:displayOptionsWithMode(_WKAttachmentDisplayModeAsIcon)];
+        observer.expectAttachmentUpdates(@[], @[attachment.get()]);
+    }
+
+    // First, verify that the attachment was successfully inserted from raw data.
+    [attachment expectRequestedDataToBe:data.get()];
+    EXPECT_WK_STREQ("document.pdf", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
+    EXPECT_WK_STREQ("application/pdf", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
+
+    [webView evaluateJavaScript:@"getSelection().removeAllRanges()" completionHandler:nil];
+    auto draggingSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
+    [draggingSimulator runFrom:CGPointMake(25, 25) to:CGPointMake(-100, -100)];
+
+    // Next, verify that dragging the attachment produces an item provider with a PDF attachment.
+    EXPECT_EQ(1U, [draggingSimulator sourceItemProviders].count);
+    NSItemProvider *itemProvider = [draggingSimulator sourceItemProviders].firstObject;
+    EXPECT_EQ(UIPreferredPresentationStyleAttachment, itemProvider.preferredPresentationStyle);
+    [itemProvider expectType:(NSString *)kUTTypePDF withData:data.get()];
+    EXPECT_WK_STREQ("document.pdf", [itemProvider suggestedName]);
+    [draggingSimulator endDataTransfer];
+}
+
 #endif // PLATFORM(IOS)
 
 } // namespace TestWebKitAPI

Modified: trunk/Tools/TestWebKitAPI/Tests/ios/DataInteractionTests.mm (226778 => 226779)


--- trunk/Tools/TestWebKitAPI/Tests/ios/DataInteractionTests.mm	2018-01-11 15:28:38 UTC (rev 226778)
+++ trunk/Tools/TestWebKitAPI/Tests/ios/DataInteractionTests.mm	2018-01-11 15:41:39 UTC (rev 226779)
@@ -1119,7 +1119,7 @@
     [dataInteractionSimulator setOverrideDataInteractionOperationBlock:^NSUInteger(NSUInteger operation, id session)
     {
         EXPECT_EQ(0U, operation);
-        return 1;
+        return UIDropOperationCopy;
     }];
     [dataInteractionSimulator setDataInteractionOperationCompletionBlock:^(BOOL handled, NSArray *itemProviders) {
         EXPECT_FALSE(handled);

Modified: trunk/Tools/TestWebKitAPI/ios/DataInteractionSimulator.h (226778 => 226779)


--- trunk/Tools/TestWebKitAPI/ios/DataInteractionSimulator.h	2018-01-11 15:28:38 UTC (rev 226778)
+++ trunk/Tools/TestWebKitAPI/ios/DataInteractionSimulator.h	2018-01-11 15:41:39 UTC (rev 226779)
@@ -90,12 +90,9 @@
 @end
 
 @interface MockDropSession : MockDragDropSession <UIDropSession>
-@property (nonatomic, strong) id localContext;
 @end
 
 @interface MockDragSession : MockDragDropSession <UIDragSession>
-@property (nonatomic, strong) id localContext;
-@property (nonatomic, strong) id context;
 @end
 
 extern NSString * const DataInteractionEnterEventName;
@@ -142,10 +139,11 @@
     RetainPtr<NSMutableArray<_WKAttachment *>> _removedAttachments;
 
     bool _isDoneWaitingForInputSession;
-    BOOL _shouldPerformOperation;
     double _currentProgress;
     bool _isDoneWithCurrentRun;
     DataInteractionPhase _phase;
+
+    RetainPtr<UIDropProposal> _currentDropProposal;
 }
 
 - (instancetype)initWithWebView:(TestWKWebView *)webView;
@@ -153,6 +151,7 @@
 - (void)runFrom:(CGPoint)startLocation to:(CGPoint)endLocation;
 - (void)runFrom:(CGPoint)startLocation to:(CGPoint)endLocation additionalItemRequestLocations:(ProgressToCGPointValueMap)additionalItemRequestLocations;
 - (void)waitForInputSession;
+- (void)endDataTransfer;
 
 @property (nonatomic) BOOL allowsFocusToStartInputSession;
 @property (nonatomic) BOOL shouldEnsureUIApplication;

Modified: trunk/Tools/TestWebKitAPI/ios/DataInteractionSimulator.mm (226778 => 226779)


--- trunk/Tools/TestWebKitAPI/ios/DataInteractionSimulator.mm	2018-01-11 15:28:38 UTC (rev 226778)
+++ trunk/Tools/TestWebKitAPI/ios/DataInteractionSimulator.mm	2018-01-11 15:41:39 UTC (rev 226779)
@@ -235,7 +235,9 @@
 
 @end
 
-@implementation MockDragSession
+@implementation MockDragSession {
+    RetainPtr<id> _localContext;
+}
 
 - (instancetype)initWithWindow:(UIWindow *)window allowMove:(BOOL)allowMove
 {
@@ -259,6 +261,16 @@
     return nil;
 }
 
+- (id)localContext
+{
+    return _localContext.get();
+}
+
+- (void)setLocalContext:(id)localContext
+{
+    _localContext = localContext;
+}
+
 @end
 
 static double progressIncrementStep = 0.033;
@@ -322,7 +334,7 @@
     _finalSelectionRects = @[ ];
     _dragSession = nil;
     _dropSession = nil;
-    _shouldPerformOperation = NO;
+    _currentDropProposal = nil;
     _lastKnownDragCaretRect = CGRectZero;
     _remainingAdditionalItemRequestLocationsByProgress = nil;
     _queuedAdditionalItemRequestLocations = adoptNS([[NSMutableArray alloc] init]);
@@ -341,7 +353,7 @@
     _currentProgress = 1;
     _isDoneWithCurrentRun = true;
     if (_dragSession)
-        [[_webView dragInteractionDelegate] dragInteraction:[_webView dragInteraction] session:_dragSession.get() didEndWithOperation:UIDropOperationCopy];
+        [[_webView dragInteractionDelegate] dragInteraction:[_webView dragInteraction] session:_dragSession.get() didEndWithOperation:UIDropOperationCancel];
 }
 
 - (void)runFrom:(CGPoint)startLocation to:(CGPoint)endLocation
@@ -405,7 +417,8 @@
 - (void)_concludeDataInteractionAndPerformOperationIfNecessary
 {
     _lastKnownDragCaretRect = [_webView _dragCaretRect];
-    if (_shouldPerformOperation) {
+    auto operation = [_currentDropProposal operation];
+    if (operation != UIDropOperationCancel && operation != UIDropOperationForbidden) {
         [[_webView dropInteractionDelegate] dropInteraction:[_webView dropInteraction] performDrop:_dropSession.get()];
         _phase = DataInteractionPerforming;
     } else {
@@ -416,7 +429,7 @@
     [[_webView dropInteractionDelegate] dropInteraction:[_webView dropInteraction] sessionDidEnd:_dropSession.get()];
 
     if (_dragSession)
-        [[_webView dragInteractionDelegate] dragInteraction:[_webView dragInteraction] session:_dragSession.get() didEndWithOperation:UIDropOperationCopy];
+        [[_webView dragInteractionDelegate] dragInteraction:[_webView dragInteraction] session:_dragSession.get() didEndWithOperation:operation];
 }
 
 - (void)_enqueuePendingAdditionalItemRequestLocations
@@ -514,8 +527,9 @@
         _phase = DataInteractionEntered;
         break;
     case DataInteractionEntered: {
-        auto operation = static_cast<UIDropOperation>([[_webView dropInteractionDelegate] dropInteraction:[_webView dropInteraction] sessionDidUpdate:_dropSession.get()].operation);
-        _shouldPerformOperation = operation == UIDropOperationCopy || ([_dropSession allowsMoveOperation] && operation != UIDropOperationCancel);
+        _currentDropProposal = [[_webView dropInteractionDelegate] dropInteraction:[_webView dropInteraction] sessionDidUpdate:_dropSession.get()];
+        if (![self shouldAllowMoveOperation] && [_currentDropProposal operation] == UIDropOperationMove)
+            _currentDropProposal = adoptNS([[UIDropProposal alloc] initWithDropOperation:UIDropOperationCancel]);
         break;
     }
     default:
@@ -588,6 +602,11 @@
     return _removedAttachments.get();
 }
 
+- (void)endDataTransfer
+{
+    [[_webView dragInteractionDelegate] dragInteraction:[_webView dragInteraction] sessionDidTransferItems:_dragSession.get()];
+}
+
 #pragma mark - WKUIDelegatePrivate
 
 - (void)_webView:(WKWebView *)webView dataInteractionOperationWasHandled:(BOOL)handled forSession:(id)session itemProviders:(NSArray<UIItemProvider *> *)itemProviders
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to