Title: [221595] trunk
Revision
221595
Author
[email protected]
Date
2017-09-04 16:36:20 -0700 (Mon, 04 Sep 2017)

Log Message

[iOS DnD] Refactor drag and drop logic in WKContentView in preparation for supporting multiple drag items in a drag session
https://bugs.webkit.org/show_bug.cgi?id=176264
<rdar://problem/31144674>

Reviewed by Darin Adler.

Source/WebCore:

Makes some small adjustments to WebItemProviderPasteboard. Rather than just -setItemProviders: on the
WebItemProviderPasteboard when initiating a drag, also stage the WebItemProviderRegistrationList that will be
used to generate an item provider. While it would be cleaner to avoid directly setting item providers so we
don't overwrite item providers on the WebItemProviderPasteboard when adding items to an existing drag session,
this isn't possible without breaking binary compability with older UIKit versions.

Importantly, WKContentView will now ignore any item providers that have been set on the
WebItemProviderPasteboard when initiating a drag or adding items to an existing drag session, and instead only
consider the staged registration list when generating item providers for dragging. This only has the drawback of
generating an unnecessary item provider, but otherwise maintains backwards compatibility while allowing us to
provide WebKit2 support for multiple items per drag session.

Tests: DataInteractionTests.DragEventClientCoordinatesBasic
       DataInteractionTests.DragEventClientCoordinatesWithScrollOffset
       DataInteractionTests.DragEventPageCoordinatesBasic
       DataInteractionTests.DragEventPageCoordinatesWithScrollOffset

* platform/ios/AbstractPasteboard.h:
* platform/ios/PlatformPasteboardIOS.mm:
(WebCore::registerItemToPasteboard):

Changed to only stage registration info on the item provider pasteboard, if possible. This has no effect on the
copy/paste codepath, since it uses a UIPasteboard.

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

Rename _registrationInfoLists to _stagedRegistrationInfoList, and change it from an array of registration info
lists to a single registration info list. This could be updated in the future to be an array of registration
lists, but currently, it serves no purpose and makes coordination with DragItem info more difficult. This would
need to support multiple registration lists if we are to add a way to begin a drag containing multiple items in
vanilla web content, such as dragging multiple selections.

(-[WebItemProviderPasteboard init]):
(-[WebItemProviderPasteboard setItemProviders:]):
(-[WebItemProviderPasteboard stageRegistrationList:]):

Sets the staged item provider registration list.

(-[WebItemProviderPasteboard takeRegistrationList]):

Removes the staged item provider registration list from the WebItemProviderPasteboard and also returns it.

(-[WebItemProviderPasteboard registrationInfoAtIndex:]): Deleted.
(-[WebItemProviderPasteboard setRegistrationInfoLists:]): Deleted.

Source/WebKit:

Move DataInteractionState from WKContentViewInteraction.h to DragDropInteractionState.h, and also rename it to
DragDropInteractionState. Additionally, refactor drag and drop state in the UI process to capture metadata about
the dragged element in a separate DragSourceState struct. This patch also moves drag and drop state transition
logic that doesn't involve WKContentView internals out of WKContentView, and into the implementation of
DragDropInteractionState.

To support multiple drag items per session, we also introduce a simple mechanism to trace a UIDragItem back to
the DragSourceState used to generate it. When generating a DragSourceState, we assign it a unique identifier,
which we also set as the privateLocalContext of all UIDragItems generated when beginning the drag (this
includes drag items returned by an internal client that uses one of the SPI hooks to augment drag items when
starting a drag). This is subsequently used in the implementation of lift and cancellation preview delegate
methods to supply the appropriate drag preview for each UIDragItem.

Lastly, fix a bug wherein the pageX and pageY of mouse drag events are inconsistent with other synthetic mouse
events, such as synthetic clicks. For synthetic clicks, the PlatformMouseEvent is initialized with the same
position and globalPosition. Whether this is really intended is unclear (see http://webkit.org/b/173855), but
it's a trivial change for now to keep mouse events on iOS consistent by tweaking the behavior during drag and
drop. See Tools/ChangeLog for some more information.

* Platform/spi/ios/UIKitSPI.h:

Add -[UIDragItem privateLocalContext].

* UIProcess/ios/DragDropInteractionState.h: Added.
(WebKit::DragDropInteractionState::stagedDragSource const):
(WebKit::DragDropInteractionState::dropSessionDidExit):
(WebKit::DragDropInteractionState::dropSessionWillPerformDrop):
(WebKit::DragDropInteractionState::adjustedPositionForDragEnd const):
(WebKit::DragDropInteractionState::didBeginDragging const):
(WebKit::DragDropInteractionState::isPerformingDrop const):
(WebKit::DragDropInteractionState::dragSession const):
(WebKit::DragDropInteractionState::dropSession const):

Wrap private drag/drop state member variables behind const getters, and move drag and drop logic that involves
only the DragDropInteractionState into helper methods on DragDropInteractionState.

(WebKit::DragDropInteractionState::BlockPtr<void):
* UIProcess/ios/DragDropInteractionState.mm: Added.
(WebKit::dragItemMatchingIdentifier):
(WebKit::createTargetedDragPreview):
(WebKit::uiImageForImage):

Move drag preview creation logic here, from WKContentViewInteraction.mm.

(WebKit::shouldUseTextIndicatorToCreatePreviewForDragAction):
(WebKit::DragDropInteractionState::activeDragSourceForItem const):
(WebKit::DragDropInteractionState::anyActiveDragSourceIs const):
(WebKit::DragDropInteractionState::prepareForDragSession):
(WebKit::DragDropInteractionState::dragSessionWillBegin):
(WebKit::DragDropInteractionState::previewForDragItem const):
(WebKit::DragDropInteractionState::dragSessionWillDelaySetDownAnimation):
(WebKit::DragDropInteractionState::dropSessionDidEnterOrUpdate):
(WebKit::DragDropInteractionState::stageDragItem):
(WebKit::DragDropInteractionState::hasStagedDragSource const):
(WebKit::DragDropInteractionState::clearStagedDragSource):
(WebKit::DragDropInteractionState::dragAndDropSessionsDidEnd):
(WebKit::DragDropInteractionState::updatePreviewsForActiveDragSources):
* UIProcess/ios/WKContentViewInteraction.h:

Move drag-and-drop-related state tied to the WKContentView here, from DataInteractionState (for instance, the
UIView for the drop caret, the WKContentView snapshot when dropping, and a flag use to keep track of hiding the
callout bar when dragging a text selection).

(): Deleted.
* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView _startAssistingNode:userIsInteracting:blurPreviousNode:userObject:]):
(-[WKContentView actionSheetAssistant:showCustomSheetForElement:]):
(-[WKContentView _didChangeDragInteractionPolicy]):
(-[WKContentView setupDataInteractionDelegates]):
(-[WKContentView teardownDataInteractionDelegates]):
(-[WKContentView _startDrag:item:]):
(-[WKContentView _didHandleStartDataInteractionRequest:]):
(-[WKContentView computeClientAndGlobalPointsForDropSession:outClientPoint:outGlobalPoint:]):
(-[WKContentView cleanUpDragSourceSessionState]):
(-[WKContentView _didConcludeEditDataInteraction:]):
(-[WKContentView _didPerformDataInteractionControllerOperation:]):
(-[WKContentView _didChangeDataInteractionCaretRect:currentRect:]):
(-[WKContentView currentDragOrDropSession]):
(-[WKContentView _restoreCalloutBarIfNeeded]):
(-[WKContentView _dragInteraction:prepareForSession:completion:]):
(-[WKContentView dragInteraction:itemsForBeginningSession:]):
(-[WKContentView dragInteraction:previewForLiftingItem:session:]):
(-[WKContentView dragInteraction:willAnimateLiftWithAnimator:session:]):
(-[WKContentView dragInteraction:sessionWillBegin:]):
(-[WKContentView dragInteraction:session:didEndWithOperation:]):
(-[WKContentView dragInteraction:previewForCancellingItem:withDefault:]):
(-[WKContentView _dragInteraction:item:shouldDelaySetDownAnimationWithCompletion:]):
(-[WKContentView dragInteraction:item:willAnimateCancelWithAnimator:]):
(-[WKContentView dropInteraction:sessionDidEnter:]):
(-[WKContentView dropInteraction:sessionDidUpdate:]):
(-[WKContentView dropInteraction:sessionDidExit:]):
(-[WKContentView dropInteraction:performDrop:]):
(-[WKContentView dropInteraction:sessionDidEnd:]):

Pull out logic that mutates drag and drop state into DragDropInteractionState. Adjust places that previously
accessed DataInteractionState's members directly with corresponding getters in DragDropInteractionState.

(-[WKContentView _simulateDataInteractionEntered:]):
(-[WKContentView _simulateDataInteractionUpdated:]):
(-[WKContentView _simulateDataInteractionEnded:]):
(-[WKContentView _simulateDataInteractionPerformOperation:]):
(-[WKContentView _simulateDataInteractionSessionDidEnd:]):
(-[WKContentView _simulateWillBeginDataInteractionWithSession:]):
(-[WKContentView _simulatedItemsForSession:]):
(-[WKContentView _simulatePrepareForDataInteractionSession:completion:]):

Rename _dataInteraction => _dragInteraction and _dataOperation => _dropInteraction.

(uiImageForImage): Deleted.
(shouldUseTextIndicatorToCreatePreviewForDragAction): Deleted.
(-[WKContentView dragPreviewForImage:frameInRootViewCoordinates:clippingRectsInFrameCoordinates:backgroundColor:]): Deleted.
(-[WKContentView dragPreviewForCurrentDataInteractionState]): Deleted.
(-[WKContentView _transitionDragPreviewToImageIfNecessary:]): Deleted.
* WebKit.xcodeproj/project.pbxproj:

Tools:

Adds two new iOS drag and drop tests to check that the clientX and clientY attributes of mouse events propagated
to the page during drop are correct. Each test drags from an image element and drops into three custom-drop-
handling elements; `dragenter`, `dragover`, and `drop` event listeners on the body then use the clientX and
clientY event attributes to hit-test for drop target elements. The first test is suffixed with "-Basic"; the
second test, suffixed with "-WithScrollOffset", makes the document scrollable to check that clientY is correct
when scrolled.

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WebKitCocoa/drop-targets.html: Added.
* TestWebKitAPI/Tests/ios/DataInteractionTests.mm:
(TestWebKitAPI::testDragAndDropOntoTargetElements):
(TestWebKitAPI::TEST):
* TestWebKitAPI/cocoa/TestWKWebView.mm:
(-[TestWKWebView stringByEvaluatingJavaScript:]):

Log a warning message when an API test fails due to _javascript_ evaluation in TestWKWebView.

Modified Paths

Added Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (221594 => 221595)


--- trunk/Source/WebCore/ChangeLog	2017-09-04 20:24:10 UTC (rev 221594)
+++ trunk/Source/WebCore/ChangeLog	2017-09-04 23:36:20 UTC (rev 221595)
@@ -1,3 +1,57 @@
+2017-09-04  Wenson Hsieh  <[email protected]>
+
+        [iOS DnD] Refactor drag and drop logic in WKContentView in preparation for supporting multiple drag items in a drag session
+        https://bugs.webkit.org/show_bug.cgi?id=176264
+        <rdar://problem/31144674>
+
+        Reviewed by Darin Adler.
+
+        Makes some small adjustments to WebItemProviderPasteboard. Rather than just -setItemProviders: on the
+        WebItemProviderPasteboard when initiating a drag, also stage the WebItemProviderRegistrationList that will be
+        used to generate an item provider. While it would be cleaner to avoid directly setting item providers so we
+        don't overwrite item providers on the WebItemProviderPasteboard when adding items to an existing drag session,
+        this isn't possible without breaking binary compability with older UIKit versions.
+
+        Importantly, WKContentView will now ignore any item providers that have been set on the
+        WebItemProviderPasteboard when initiating a drag or adding items to an existing drag session, and instead only
+        consider the staged registration list when generating item providers for dragging. This only has the drawback of
+        generating an unnecessary item provider, but otherwise maintains backwards compatibility while allowing us to
+        provide WebKit2 support for multiple items per drag session.
+
+        Tests: DataInteractionTests.DragEventClientCoordinatesBasic
+               DataInteractionTests.DragEventClientCoordinatesWithScrollOffset
+               DataInteractionTests.DragEventPageCoordinatesBasic
+               DataInteractionTests.DragEventPageCoordinatesWithScrollOffset
+
+        * platform/ios/AbstractPasteboard.h:
+        * platform/ios/PlatformPasteboardIOS.mm:
+        (WebCore::registerItemToPasteboard):
+
+        Changed to only stage registration info on the item provider pasteboard, if possible. This has no effect on the
+        copy/paste codepath, since it uses a UIPasteboard.
+
+        * platform/ios/WebItemProviderPasteboard.h:
+        * platform/ios/WebItemProviderPasteboard.mm:
+
+        Rename _registrationInfoLists to _stagedRegistrationInfoList, and change it from an array of registration info
+        lists to a single registration info list. This could be updated in the future to be an array of registration
+        lists, but currently, it serves no purpose and makes coordination with DragItem info more difficult. This would
+        need to support multiple registration lists if we are to add a way to begin a drag containing multiple items in
+        vanilla web content, such as dragging multiple selections.
+
+        (-[WebItemProviderPasteboard init]):
+        (-[WebItemProviderPasteboard setItemProviders:]):
+        (-[WebItemProviderPasteboard stageRegistrationList:]):
+
+        Sets the staged item provider registration list.
+
+        (-[WebItemProviderPasteboard takeRegistrationList]):
+
+        Removes the staged item provider registration list from the WebItemProviderPasteboard and also returns it.
+
+        (-[WebItemProviderPasteboard registrationInfoAtIndex:]): Deleted.
+        (-[WebItemProviderPasteboard setRegistrationInfoLists:]): Deleted.
+
 2017-09-04  Commit Queue  <[email protected]>
 
         Unreviewed, rolling out r221494 and r221500.

Modified: trunk/Source/WebCore/platform/ios/AbstractPasteboard.h (221594 => 221595)


--- trunk/Source/WebCore/platform/ios/AbstractPasteboard.h	2017-09-04 20:24:10 UTC (rev 221594)
+++ trunk/Source/WebCore/platform/ios/AbstractPasteboard.h	2017-09-04 23:36:20 UTC (rev 221595)
@@ -49,7 +49,8 @@
 
 @optional
 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
-- (void)setRegistrationInfoLists:(NSArray <WebItemProviderRegistrationInfoList *> *)info;
+- (void)stageRegistrationList:(nullable WebItemProviderRegistrationInfoList *)info;
+- (nullable WebItemProviderRegistrationInfoList *)takeRegistrationList;
 #endif
 - (void)setItems:(NSArray<NSDictionary *> *)items;
 - (NSArray<NSString *> *)pasteboardTypesByFidelityForItemAtIndex:(NSUInteger)index;

Modified: trunk/Source/WebCore/platform/ios/PlatformPasteboardIOS.mm (221594 => 221595)


--- trunk/Source/WebCore/platform/ios/PlatformPasteboardIOS.mm	2017-09-04 20:24:10 UTC (rev 221594)
+++ trunk/Source/WebCore/platform/ios/PlatformPasteboardIOS.mm	2017-09-04 23:36:20 UTC (rev 221595)
@@ -179,15 +179,13 @@
 
 static void registerItemToPasteboard(WebItemProviderRegistrationInfoList *representationsToRegister, id <AbstractPasteboard> pasteboard)
 {
-    UIItemProvider *itemProvider = [representationsToRegister itemProvider];
-    if (!itemProvider) {
+    if (UIItemProvider *itemProvider = representationsToRegister.itemProvider)
+        [pasteboard setItemProviders:@[ itemProvider ]];
+    else
         [pasteboard setItemProviders:@[ ]];
-        return;
-    }
 
-    [pasteboard setItemProviders:@[ itemProvider ]];
-    if ([pasteboard respondsToSelector:@selector(setRegistrationInfoLists:)])
-        [pasteboard setRegistrationInfoLists:@[ representationsToRegister ]];
+    if ([pasteboard respondsToSelector:@selector(stageRegistrationList:)])
+        [pasteboard stageRegistrationList:representationsToRegister];
 }
 
 #else

Modified: trunk/Source/WebCore/platform/ios/WebItemProviderPasteboard.h (221594 => 221595)


--- trunk/Source/WebCore/platform/ios/WebItemProviderPasteboard.h	2017-09-04 20:24:10 UTC (rev 221594)
+++ trunk/Source/WebCore/platform/ios/WebItemProviderPasteboard.h	2017-09-04 23:36:20 UTC (rev 221595)
@@ -76,10 +76,6 @@
 
 + (instancetype)sharedInstance;
 
-// Registration info lists are only available upon starting data interaction.
-- (WebItemProviderRegistrationInfoList *)registrationInfoAtIndex:(NSUInteger)index;
-- (UIItemProvider *)itemProviderAtIndex:(NSUInteger)index;
-
 @property (copy, nonatomic, nullable) NSArray<__kindof NSItemProvider *> *itemProviders;
 @property (readonly, nonatomic) NSInteger numberOfItems;
 @property (readonly, nonatomic) NSInteger changeCount;

Modified: trunk/Source/WebCore/platform/ios/WebItemProviderPasteboard.mm (221594 => 221595)


--- trunk/Source/WebCore/platform/ios/WebItemProviderPasteboard.mm	2017-09-04 20:24:10 UTC (rev 221594)
+++ trunk/Source/WebCore/platform/ios/WebItemProviderPasteboard.mm	2017-09-04 23:36:20 UTC (rev 221595)
@@ -207,7 +207,7 @@
     RetainPtr<NSArray> _cachedTypeIdentifiers;
     RetainPtr<NSArray> _typeToFileURLMaps;
     RetainPtr<NSArray> _supportedTypeIdentifiers;
-    RetainPtr<NSArray> _registrationInfoLists;
+    RetainPtr<WebItemProviderRegistrationInfoList> _stagedRegistrationInfoList;
 }
 
 + (instancetype)sharedInstance
@@ -228,7 +228,7 @@
         _pendingOperationCount = 0;
         _typeToFileURLMaps = adoptNS([[NSArray alloc] init]);
         _supportedTypeIdentifiers = nil;
-        _registrationInfoLists = nil;
+        _stagedRegistrationInfoList = nil;
     }
     return self;
 }
@@ -277,7 +277,7 @@
     _itemProviders = itemProviders;
     _changeCount++;
     _cachedTypeIdentifiers = nil;
-    _registrationInfoLists = nil;
+    _stagedRegistrationInfoList = nil;
 
     NSMutableArray *typeToFileURLMaps = [NSMutableArray arrayWithCapacity:itemProviders.count];
     [itemProviders enumerateObjectsUsingBlock:[typeToFileURLMaps] (UIItemProvider *, NSUInteger, BOOL *) {
@@ -515,11 +515,6 @@
     dispatch_group_notify(fileLoadingGroup.get(), dispatch_get_main_queue(), itemLoadCompletion);
 }
 
-- (WebItemProviderRegistrationInfoList *)registrationInfoAtIndex:(NSUInteger)index
-{
-    return index < [_registrationInfoLists count] ? [_registrationInfoLists objectAtIndex:index] : nil;
-}
-
 - (UIItemProvider *)itemProviderAtIndex:(NSUInteger)index
 {
     return index < [_itemProviders count] ? [_itemProviders objectAtIndex:index] : nil;
@@ -545,11 +540,18 @@
     [_itemProviders enumerateObjectsUsingBlock:block];
 }
 
-- (void)setRegistrationInfoLists:(NSArray <WebItemProviderRegistrationInfoList *> *)infoLists
+- (void)stageRegistrationList:(nullable WebItemProviderRegistrationInfoList *)info
 {
-    _registrationInfoLists = infoLists;
+    _stagedRegistrationInfoList = info.numberOfItems ? info : nil;
 }
 
+- (WebItemProviderRegistrationInfoList *)takeRegistrationList
+{
+    auto stagedRegistrationInfoList = _stagedRegistrationInfoList;
+    _stagedRegistrationInfoList = nil;
+    return stagedRegistrationInfoList.autorelease();
+}
+
 @end
 
 #endif // ENABLE(DATA_INTERACTION)

Modified: trunk/Source/WebKit/ChangeLog (221594 => 221595)


--- trunk/Source/WebKit/ChangeLog	2017-09-04 20:24:10 UTC (rev 221594)
+++ trunk/Source/WebKit/ChangeLog	2017-09-04 23:36:20 UTC (rev 221595)
@@ -1,3 +1,126 @@
+2017-09-04  Wenson Hsieh  <[email protected]>
+
+        [iOS DnD] Refactor drag and drop logic in WKContentView in preparation for supporting multiple drag items in a drag session
+        https://bugs.webkit.org/show_bug.cgi?id=176264
+        <rdar://problem/31144674>
+
+        Reviewed by Darin Adler.
+
+        Move DataInteractionState from WKContentViewInteraction.h to DragDropInteractionState.h, and also rename it to
+        DragDropInteractionState. Additionally, refactor drag and drop state in the UI process to capture metadata about
+        the dragged element in a separate DragSourceState struct. This patch also moves drag and drop state transition
+        logic that doesn't involve WKContentView internals out of WKContentView, and into the implementation of
+        DragDropInteractionState.
+
+        To support multiple drag items per session, we also introduce a simple mechanism to trace a UIDragItem back to
+        the DragSourceState used to generate it. When generating a DragSourceState, we assign it a unique identifier,
+        which we also set as the privateLocalContext of all UIDragItems generated when beginning the drag (this
+        includes drag items returned by an internal client that uses one of the SPI hooks to augment drag items when
+        starting a drag). This is subsequently used in the implementation of lift and cancellation preview delegate
+        methods to supply the appropriate drag preview for each UIDragItem.
+
+        Lastly, fix a bug wherein the pageX and pageY of mouse drag events are inconsistent with other synthetic mouse
+        events, such as synthetic clicks. For synthetic clicks, the PlatformMouseEvent is initialized with the same
+        position and globalPosition. Whether this is really intended is unclear (see http://webkit.org/b/173855), but
+        it's a trivial change for now to keep mouse events on iOS consistent by tweaking the behavior during drag and
+        drop. See Tools/ChangeLog for some more information.
+
+        * Platform/spi/ios/UIKitSPI.h:
+
+        Add -[UIDragItem privateLocalContext].
+
+        * UIProcess/ios/DragDropInteractionState.h: Added.
+        (WebKit::DragDropInteractionState::stagedDragSource const):
+        (WebKit::DragDropInteractionState::dropSessionDidExit):
+        (WebKit::DragDropInteractionState::dropSessionWillPerformDrop):
+        (WebKit::DragDropInteractionState::adjustedPositionForDragEnd const):
+        (WebKit::DragDropInteractionState::didBeginDragging const):
+        (WebKit::DragDropInteractionState::isPerformingDrop const):
+        (WebKit::DragDropInteractionState::dragSession const):
+        (WebKit::DragDropInteractionState::dropSession const):
+
+        Wrap private drag/drop state member variables behind const getters, and move drag and drop logic that involves
+        only the DragDropInteractionState into helper methods on DragDropInteractionState.
+
+        (WebKit::DragDropInteractionState::BlockPtr<void):
+        * UIProcess/ios/DragDropInteractionState.mm: Added.
+        (WebKit::dragItemMatchingIdentifier):
+        (WebKit::createTargetedDragPreview):
+        (WebKit::uiImageForImage):
+
+        Move drag preview creation logic here, from WKContentViewInteraction.mm.
+
+        (WebKit::shouldUseTextIndicatorToCreatePreviewForDragAction):
+        (WebKit::DragDropInteractionState::activeDragSourceForItem const):
+        (WebKit::DragDropInteractionState::anyActiveDragSourceIs const):
+        (WebKit::DragDropInteractionState::prepareForDragSession):
+        (WebKit::DragDropInteractionState::dragSessionWillBegin):
+        (WebKit::DragDropInteractionState::previewForDragItem const):
+        (WebKit::DragDropInteractionState::dragSessionWillDelaySetDownAnimation):
+        (WebKit::DragDropInteractionState::dropSessionDidEnterOrUpdate):
+        (WebKit::DragDropInteractionState::stageDragItem):
+        (WebKit::DragDropInteractionState::hasStagedDragSource const):
+        (WebKit::DragDropInteractionState::clearStagedDragSource):
+        (WebKit::DragDropInteractionState::dragAndDropSessionsDidEnd):
+        (WebKit::DragDropInteractionState::updatePreviewsForActiveDragSources):
+        * UIProcess/ios/WKContentViewInteraction.h:
+
+        Move drag-and-drop-related state tied to the WKContentView here, from DataInteractionState (for instance, the
+        UIView for the drop caret, the WKContentView snapshot when dropping, and a flag use to keep track of hiding the
+        callout bar when dragging a text selection).
+
+        (): Deleted.
+        * UIProcess/ios/WKContentViewInteraction.mm:
+        (-[WKContentView _startAssistingNode:userIsInteracting:blurPreviousNode:userObject:]):
+        (-[WKContentView actionSheetAssistant:showCustomSheetForElement:]):
+        (-[WKContentView _didChangeDragInteractionPolicy]):
+        (-[WKContentView setupDataInteractionDelegates]):
+        (-[WKContentView teardownDataInteractionDelegates]):
+        (-[WKContentView _startDrag:item:]):
+        (-[WKContentView _didHandleStartDataInteractionRequest:]):
+        (-[WKContentView computeClientAndGlobalPointsForDropSession:outClientPoint:outGlobalPoint:]):
+        (-[WKContentView cleanUpDragSourceSessionState]):
+        (-[WKContentView _didConcludeEditDataInteraction:]):
+        (-[WKContentView _didPerformDataInteractionControllerOperation:]):
+        (-[WKContentView _didChangeDataInteractionCaretRect:currentRect:]):
+        (-[WKContentView currentDragOrDropSession]):
+        (-[WKContentView _restoreCalloutBarIfNeeded]):
+        (-[WKContentView _dragInteraction:prepareForSession:completion:]):
+        (-[WKContentView dragInteraction:itemsForBeginningSession:]):
+        (-[WKContentView dragInteraction:previewForLiftingItem:session:]):
+        (-[WKContentView dragInteraction:willAnimateLiftWithAnimator:session:]):
+        (-[WKContentView dragInteraction:sessionWillBegin:]):
+        (-[WKContentView dragInteraction:session:didEndWithOperation:]):
+        (-[WKContentView dragInteraction:previewForCancellingItem:withDefault:]):
+        (-[WKContentView _dragInteraction:item:shouldDelaySetDownAnimationWithCompletion:]):
+        (-[WKContentView dragInteraction:item:willAnimateCancelWithAnimator:]):
+        (-[WKContentView dropInteraction:sessionDidEnter:]):
+        (-[WKContentView dropInteraction:sessionDidUpdate:]):
+        (-[WKContentView dropInteraction:sessionDidExit:]):
+        (-[WKContentView dropInteraction:performDrop:]):
+        (-[WKContentView dropInteraction:sessionDidEnd:]):
+
+        Pull out logic that mutates drag and drop state into DragDropInteractionState. Adjust places that previously
+        accessed DataInteractionState's members directly with corresponding getters in DragDropInteractionState.
+
+        (-[WKContentView _simulateDataInteractionEntered:]):
+        (-[WKContentView _simulateDataInteractionUpdated:]):
+        (-[WKContentView _simulateDataInteractionEnded:]):
+        (-[WKContentView _simulateDataInteractionPerformOperation:]):
+        (-[WKContentView _simulateDataInteractionSessionDidEnd:]):
+        (-[WKContentView _simulateWillBeginDataInteractionWithSession:]):
+        (-[WKContentView _simulatedItemsForSession:]):
+        (-[WKContentView _simulatePrepareForDataInteractionSession:completion:]):
+
+        Rename _dataInteraction => _dragInteraction and _dataOperation => _dropInteraction.
+
+        (uiImageForImage): Deleted.
+        (shouldUseTextIndicatorToCreatePreviewForDragAction): Deleted.
+        (-[WKContentView dragPreviewForImage:frameInRootViewCoordinates:clippingRectsInFrameCoordinates:backgroundColor:]): Deleted.
+        (-[WKContentView dragPreviewForCurrentDataInteractionState]): Deleted.
+        (-[WKContentView _transitionDragPreviewToImageIfNecessary:]): Deleted.
+        * WebKit.xcodeproj/project.pbxproj:
+
 2017-09-03  Carlos Garcia Campos  <[email protected]>
 
         [Threaded Compositor] Deadlock in ThreadedDisplayRefreshMonitor

Modified: trunk/Source/WebKit/Platform/spi/ios/UIKitSPI.h (221594 => 221595)


--- trunk/Source/WebKit/Platform/spi/ios/UIKitSPI.h	2017-09-04 20:24:10 UTC (rev 221594)
+++ trunk/Source/WebKit/Platform/spi/ios/UIKitSPI.h	2017-09-04 23:36:20 UTC (rev 221595)
@@ -90,6 +90,7 @@
 #if ENABLE(DRAG_SUPPORT)
 #import <UIKit/UIDragInteraction.h>
 #import <UIKit/UIDragInteraction_Private.h>
+#import <UIKit/UIDragItem_Private.h>
 #import <UIKit/UIDragPreviewParameters.h>
 #import <UIKit/UIDragPreview_Private.h>
 #import <UIKit/UIDragSession.h>
@@ -899,6 +900,10 @@
 @property (nonatomic, assign, getter=_liftDelay, setter=_setLiftDelay:) NSTimeInterval liftDelay;
 @end
 
+@interface UIDragItem ()
+@property (nonatomic, strong, nullable, setter=_setPrivateLocalContext:, getter=_privateLocalContext) id privateLocalContext;
+@end
+
 @protocol UITextInput;
 @interface _UITextDragCaretView : UIView
 - (instancetype)initWithTextInputView:(UIView<UITextInput> *)textInputView;

Added: trunk/Source/WebKit/UIProcess/ios/DragDropInteractionState.h (0 => 221595)


--- trunk/Source/WebKit/UIProcess/ios/DragDropInteractionState.h	                        (rev 0)
+++ trunk/Source/WebKit/UIProcess/ios/DragDropInteractionState.h	2017-09-04 23:36:20 UTC (rev 221595)
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(DRAG_SUPPORT) && PLATFORM(IOS)
+
+#import "UIKitSPI.h"
+#import <WebCore/DragActions.h>
+#import <WebCore/DragData.h>
+#import <WebCore/TextIndicator.h>
+#import <WebCore/URL.h>
+#import <WebCore/WebItemProviderPasteboard.h>
+#import <wtf/BlockPtr.h>
+#import <wtf/RetainPtr.h>
+#import <wtf/Vector.h>
+
+namespace WebCore {
+struct DragItem;
+}
+
+namespace WebKit {
+
+struct DragSourceState {
+    WebCore::DragSourceAction action { WebCore::DragSourceActionNone };
+    CGPoint adjustedOrigin { CGPointZero };
+    CGRect elementBounds { CGRectZero };
+    RetainPtr<UIImage> image;
+    std::optional<WebCore::TextIndicatorData> indicatorData;
+    String linkTitle;
+    WebCore::URL linkURL;
+    bool possiblyNeedsDragPreviewUpdate { true };
+
+    NSInteger itemIdentifier { 0 };
+};
+
+class DragDropInteractionState {
+public:
+    bool anyActiveDragSourceIs(WebCore::DragSourceAction) const;
+
+    // These helper methods are unique to UIDragInteraction.
+    void prepareForDragSession(id <UIDragSession>, dispatch_block_t completionHandler);
+    void dragSessionWillBegin();
+    void stageDragItem(const WebCore::DragItem&, UIImage *);
+    bool hasStagedDragSource() const;
+    const DragSourceState& stagedDragSource() const { return m_stagedDragSource.value(); }
+    enum class DidBecomeActive { No, Yes };
+    void clearStagedDragSource(DidBecomeActive = DidBecomeActive::No);
+    UITargetedDragPreview *previewForDragItem(UIDragItem *, UIView *contentView, UIView *previewContainer) const;
+    void dragSessionWillDelaySetDownAnimation(dispatch_block_t completion);
+
+    // These helper methods are unique to UIDropInteraction.
+    void dropSessionDidEnterOrUpdate(id <UIDropSession>, const WebCore::DragData&);
+    void dropSessionDidExit() { m_dropSession = nil; }
+    void dropSessionWillPerformDrop() { m_isPerformingDrop = true; }
+
+    // This is invoked when both drag and drop interactions are no longer active.
+    void dragAndDropSessionsDidEnd();
+
+    CGPoint adjustedPositionForDragEnd() const { return m_adjustedPositionForDragEnd; }
+    bool didBeginDragging() const { return m_didBeginDragging; }
+    bool isPerformingDrop() const { return m_isPerformingDrop; }
+    id<UIDragSession> dragSession() const { return m_dragSession.get(); }
+    id<UIDropSession> dropSession() const { return m_dropSession.get(); }
+    BlockPtr<void()> takeDragStartCompletionBlock() { return WTFMove(m_dragStartCompletionBlock); }
+    BlockPtr<void()> takeDragCancelSetDownBlock() { return WTFMove(m_dragCancelSetDownBlock); }
+
+private:
+    void updatePreviewsForActiveDragSources();
+    std::optional<DragSourceState> activeDragSourceForItem(UIDragItem *) const;
+
+    CGPoint m_lastGlobalPosition { CGPointZero };
+    CGPoint m_adjustedPositionForDragEnd { CGPointZero };
+    bool m_didBeginDragging { false };
+    bool m_isPerformingDrop { false };
+    RetainPtr<id <UIDragSession>> m_dragSession;
+    RetainPtr<id <UIDropSession>> m_dropSession;
+    BlockPtr<void()> m_dragStartCompletionBlock;
+    BlockPtr<void()> m_dragCancelSetDownBlock;
+
+    std::optional<DragSourceState> m_stagedDragSource;
+    Vector<DragSourceState> m_activeDragSources;
+};
+
+} // namespace WebKit
+
+#endif // ENABLE(DRAG_SUPPORT) && PLATFORM(IOS)

Added: trunk/Source/WebKit/UIProcess/ios/DragDropInteractionState.mm (0 => 221595)


--- trunk/Source/WebKit/UIProcess/ios/DragDropInteractionState.mm	                        (rev 0)
+++ trunk/Source/WebKit/UIProcess/ios/DragDropInteractionState.mm	2017-09-04 23:36:20 UTC (rev 221595)
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "DragDropInteractionState.h"
+
+#if ENABLE(DRAG_SUPPORT) && PLATFORM(IOS)
+
+#import <WebCore/DragItem.h>
+#import <WebCore/Image.h>
+
+using namespace WebCore;
+using namespace WebKit;
+
+namespace WebKit {
+
+static UIDragItem *dragItemMatchingIdentifier(id <UIDragSession> session, NSInteger identifier)
+{
+    for (UIDragItem *item in session.items) {
+        id context = item.privateLocalContext;
+        if ([context isKindOfClass:[NSNumber class]] && [context integerValue] == identifier)
+            return item;
+    }
+    return nil;
+}
+
+static UITargetedDragPreview *createTargetedDragPreview(UIImage *image, UIView *rootView, UIView *previewContainer, const FloatRect& frameInRootViewCoordinates, const Vector<FloatRect>& clippingRectsInFrameCoordinates, UIColor *backgroundColor)
+{
+    if (frameInRootViewCoordinates.isEmpty() || !image)
+        return nullptr;
+
+    NSMutableArray *clippingRectValuesInFrameCoordinates = [NSMutableArray arrayWithCapacity:clippingRectsInFrameCoordinates.size()];
+
+    FloatRect frameInContainerCoordinates = [rootView convertRect:frameInRootViewCoordinates toView:previewContainer];
+    if (frameInContainerCoordinates.isEmpty())
+        return nullptr;
+
+    float widthScalingRatio = frameInContainerCoordinates.width() / frameInRootViewCoordinates.width();
+    float heightScalingRatio = frameInContainerCoordinates.height() / frameInRootViewCoordinates.height();
+    for (auto rect : clippingRectsInFrameCoordinates) {
+        rect.scale(widthScalingRatio, heightScalingRatio);
+        [clippingRectValuesInFrameCoordinates addObject:[NSValue valueWithCGRect:rect]];
+    }
+
+    auto imageView = adoptNS([[UIImageView alloc] initWithImage:image]);
+    [imageView setFrame:frameInContainerCoordinates];
+
+    RetainPtr<UIDragPreviewParameters> parameters;
+    if (clippingRectValuesInFrameCoordinates.count)
+        parameters = adoptNS([[UIDragPreviewParameters alloc] initWithTextLineRects:clippingRectValuesInFrameCoordinates]);
+    else
+        parameters = adoptNS([[UIDragPreviewParameters alloc] init]);
+
+    if (backgroundColor)
+        [parameters setBackgroundColor:backgroundColor];
+
+    CGPoint centerInContainerCoordinates = { CGRectGetMidX(frameInContainerCoordinates), CGRectGetMidY(frameInContainerCoordinates) };
+    auto target = adoptNS([[UIDragPreviewTarget alloc] initWithContainer:previewContainer center:centerInContainerCoordinates]);
+    auto dragPreview = adoptNS([[UITargetedDragPreview alloc] initWithView:imageView.get() parameters:parameters.get() target:target.get()]);
+    return dragPreview.autorelease();
+}
+
+static RetainPtr<UIImage> uiImageForImage(Image* image)
+{
+    if (!image)
+        return nullptr;
+
+    auto cgImage = image->nativeImage();
+    if (!cgImage)
+        return nullptr;
+
+    return adoptNS([[UIImage alloc] initWithCGImage:cgImage.get()]);
+}
+
+static bool shouldUseTextIndicatorToCreatePreviewForDragAction(DragSourceAction action)
+{
+    if (action & (DragSourceActionLink | DragSourceActionSelection))
+        return true;
+
+#if ENABLE(ATTACHMENT_ELEMENT)
+    if (action & DragSourceActionAttachment)
+        return true;
+#endif
+
+    return false;
+}
+
+std::optional<DragSourceState> DragDropInteractionState::activeDragSourceForItem(UIDragItem *item) const
+{
+    if (![item.privateLocalContext isKindOfClass:[NSNumber class]])
+        return std::nullopt;
+
+    auto identifier = [(NSNumber *)item.privateLocalContext integerValue];
+    for (auto& source : m_activeDragSources) {
+        if (source.itemIdentifier == identifier)
+            return source;
+    }
+    return std::nullopt;
+}
+
+bool DragDropInteractionState::anyActiveDragSourceIs(WebCore::DragSourceAction action) const
+{
+    for (auto& source : m_activeDragSources) {
+        if (source.action & action)
+            return true;
+    }
+    return false;
+}
+
+void DragDropInteractionState::prepareForDragSession(id <UIDragSession> session, dispatch_block_t completionHandler)
+{
+    m_dragSession = session;
+    m_dragStartCompletionBlock = completionHandler;
+}
+
+void DragDropInteractionState::dragSessionWillBegin()
+{
+    m_didBeginDragging = true;
+    updatePreviewsForActiveDragSources();
+}
+
+UITargetedDragPreview *DragDropInteractionState::previewForDragItem(UIDragItem *item, UIView *contentView, UIView *previewContainer) const
+{
+    auto foundSource = activeDragSourceForItem(item);
+    if (!foundSource)
+        return nil;
+
+    auto& source = foundSource.value();
+    if ((source.action & DragSourceActionImage) && source.image) {
+        Vector<FloatRect> emptyClippingRects;
+        return createTargetedDragPreview(source.image.get(), contentView, previewContainer, source.elementBounds, emptyClippingRects, nil);
+    }
+
+    if (shouldUseTextIndicatorToCreatePreviewForDragAction(source.action) && source.indicatorData) {
+        auto indicator = source.indicatorData.value();
+        auto textIndicatorImage = uiImageForImage(indicator.contentImage.get());
+        return createTargetedDragPreview(textIndicatorImage.get(), contentView, previewContainer, indicator.textBoundingRectInRootViewCoordinates, indicator.textRectsInBoundingRectCoordinates, [UIColor colorWithCGColor:cachedCGColor(indicator.estimatedBackgroundColor)]);
+    }
+
+    return nil;
+}
+
+void DragDropInteractionState::dragSessionWillDelaySetDownAnimation(dispatch_block_t completion)
+{
+    m_dragCancelSetDownBlock = completion;
+}
+
+void DragDropInteractionState::dropSessionDidEnterOrUpdate(id <UIDropSession> session, const DragData& dragData)
+{
+    m_dropSession = session;
+    m_lastGlobalPosition = dragData.globalPosition();
+}
+
+void DragDropInteractionState::stageDragItem(const DragItem& item, UIImage *dragImage)
+{
+    static NSInteger currentDragSourceItemIdentifier = 0;
+
+    m_adjustedPositionForDragEnd = item.eventPositionInContentCoordinates;
+    m_stagedDragSource = {{
+        static_cast<DragSourceAction>(item.sourceAction),
+        item.eventPositionInContentCoordinates,
+        item.elementBounds,
+        dragImage,
+        item.image.indicatorData(),
+        item.title.isEmpty() ? nil : (NSString *)item.title,
+        item.url.isEmpty() ? nil : (NSURL *)item.url,
+        true, // We assume here that drag previews need to be updated until proven otherwise in updatePreviewsForActiveDragSources().
+        ++currentDragSourceItemIdentifier
+    }};
+}
+
+bool DragDropInteractionState::hasStagedDragSource() const
+{
+    return m_stagedDragSource && stagedDragSource().action != WebCore::DragSourceActionNone;
+}
+
+void DragDropInteractionState::clearStagedDragSource(DidBecomeActive didBecomeActive)
+{
+    if (didBecomeActive == DidBecomeActive::Yes)
+        m_activeDragSources.append(stagedDragSource());
+    m_stagedDragSource = std::nullopt;
+}
+
+void DragDropInteractionState::dragAndDropSessionsDidEnd()
+{
+    // If any of UIKit's completion blocks are still in-flight when the drag interaction ends, we need to ensure that they are still invoked
+    // to prevent UIKit from getting into an inconsistent state.
+    if (auto completionBlock = takeDragCancelSetDownBlock())
+        completionBlock();
+
+    if (auto completionBlock = takeDragStartCompletionBlock())
+        completionBlock();
+}
+
+void DragDropInteractionState::updatePreviewsForActiveDragSources()
+{
+    for (auto& source : m_activeDragSources) {
+        if (!source.possiblyNeedsDragPreviewUpdate)
+            continue;
+
+        if (source.action & DragSourceActionImage || !(source.action & DragSourceActionLink)) {
+            // Currently, non-image links are the only type of source for which we need to update
+            // drag preview providers after the initial lift. All other dragged content should maintain
+            // the same targeted drag preview used during the lift animation.
+            continue;
+        }
+
+        UIDragItem *dragItem = dragItemMatchingIdentifier(m_dragSession.get(), source.itemIdentifier);
+        if (!dragItem)
+            continue;
+
+        auto linkDraggingCenter = source.adjustedOrigin;
+        RetainPtr<NSString> title = (NSString *)source.linkTitle;
+        RetainPtr<NSURL> url = "" *)source.linkURL;
+        dragItem.previewProvider = [title, url, linkDraggingCenter] () -> UIDragPreview * {
+            UIURLDragPreviewView *previewView = [UIURLDragPreviewView viewWithTitle:title.get() URL:url.get()];
+            previewView.center = linkDraggingCenter;
+            UIDragPreviewParameters *parameters = [[[UIDragPreviewParameters alloc] initWithTextLineRects:@[ [NSValue valueWithCGRect:previewView.bounds] ]] autorelease];
+            return [[[UIDragPreview alloc] initWithView:previewView parameters:parameters] autorelease];
+        };
+
+        source.possiblyNeedsDragPreviewUpdate = false;
+    }
+}
+
+} // namespace WebKit
+
+#endif // ENABLE(DRAG_SUPPORT) && PLATFORM(IOS)

Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h (221594 => 221595)


--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h	2017-09-04 20:24:10 UTC (rev 221594)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h	2017-09-04 23:36:20 UTC (rev 221595)
@@ -28,6 +28,7 @@
 #import "WKContentView.h"
 
 #import "AssistedNodeInformation.h"
+#import "DragDropInteractionState.h"
 #import "EditorState.h"
 #import "GestureTypes.h"
 #import "InteractionInformationAtPosition.h"
@@ -39,7 +40,6 @@
 #import "WKSyntheticClickTapGestureRecognizer.h"
 #import <UIKit/UIView.h>
 #import <WebCore/Color.h>
-#import <WebCore/DragActions.h>
 #import <WebCore/FloatQuad.h>
 #import <wtf/BlockPtr.h>
 #import <wtf/Forward.h>
@@ -118,33 +118,6 @@
 
 namespace WebKit {
 
-#if ENABLE(DRAG_SUPPORT)
-
-struct WKDataInteractionState {
-    RetainPtr<UIImage> image;
-    std::optional<WebCore::TextIndicatorData> indicatorData;
-    CGPoint adjustedOrigin { CGPointZero };
-    CGPoint lastGlobalPosition { CGPointZero };
-    CGRect elementBounds { CGRectZero };
-    BOOL didBeginDragging { NO };
-    BOOL isPerformingOperation { NO };
-    BOOL isAnimatingConcludeEditDrag { NO };
-    BOOL shouldRestoreCalloutBar { NO };
-    RetainPtr<id <UIDragSession>> dragSession;
-    RetainPtr<id <UIDropSession>> dropSession;
-    BlockPtr<void()> dragStartCompletionBlock;
-    BlockPtr<void()> dragCancelSetDownBlock;
-    WebCore::DragSourceAction sourceAction { WebCore::DragSourceActionNone };
-
-    String linkTitle;
-    WebCore::URL linkURL;
-
-    RetainPtr<UIView> visibleContentViewSnapshot;
-    RetainPtr<_UITextDragCaretView> caretView;
-};
-
-#endif // ENABLE(DRAG_SUPPORT)
-
 struct WKSelectionDrawingInfo {
     enum class SelectionType { None, Plugin, Range };
     WKSelectionDrawingInfo();
@@ -256,9 +229,13 @@
     BOOL _needsDeferredEndScrollingSelectionUpdate;
 
 #if ENABLE(DATA_INTERACTION)
-    WebKit::WKDataInteractionState _dataInteractionState;
-    RetainPtr<UIDragInteraction> _dataInteraction;
-    RetainPtr<UIDropInteraction> _dataOperation;
+    WebKit::DragDropInteractionState _dragDropInteractionState;
+    RetainPtr<UIDragInteraction> _dragInteraction;
+    RetainPtr<UIDropInteraction> _dropInteraction;
+    BOOL _shouldRestoreCalloutBarAfterDrop;
+    BOOL _isAnimatingConcludeEditDrag;
+    RetainPtr<UIView> _visibleContentViewSnapshot;
+    RetainPtr<_UITextDragCaretView> _editDropCaretView;
 #endif
 }
 

Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm (221594 => 221595)


--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2017-09-04 20:24:10 UTC (rev 221594)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2017-09-04 23:36:20 UTC (rev 221595)
@@ -3861,7 +3861,7 @@
         // The default behavior is to allow node assistance if the user is interacting or the keyboard is already active.
         shouldShowKeyboard = userIsInteracting || _textSelectionAssistant;
 #if ENABLE(DATA_INTERACTION)
-        shouldShowKeyboard |= _dataInteractionState.isPerformingOperation;
+        shouldShowKeyboard |= _dragDropInteractionState.isPerformingDrop();
 #endif
     }
     if (!shouldShowKeyboard)
@@ -4135,7 +4135,7 @@
     if ([uiDelegate respondsToSelector:@selector(_webView:showCustomSheetForElement:)]) {
         if ([uiDelegate _webView:_webView showCustomSheetForElement:element]) {
 #if ENABLE(DATA_INTERACTION)
-            BOOL shouldCancelAllTouches = !_dataInteractionState.sourceAction;
+            BOOL shouldCancelAllTouches = !_dragDropInteractionState.dragSession();
 #else
             BOOL shouldCancelAllTouches = YES;
 #endif
@@ -4206,7 +4206,7 @@
 
 - (void)_didChangeDragInteractionPolicy
 {
-    [_dataInteraction setEnabled:shouldEnableDragInteractionForPolicy(_webView._dragInteractionPolicy)];
+    [_dragInteraction setEnabled:shouldEnableDragInteractionForPolicy(_webView._dragInteractionPolicy)];
 }
 
 - (NSTimeInterval)dragLiftDelay
@@ -4228,25 +4228,25 @@
 
 - (void)setupDataInteractionDelegates
 {
-    _dataInteraction = adoptNS([[UIDragInteraction alloc] initWithDelegate:self]);
-    _dataOperation = adoptNS([[UIDropInteraction alloc] initWithDelegate:self]);
-    [_dataInteraction _setLiftDelay:self.dragLiftDelay];
-    [_dataInteraction setEnabled:shouldEnableDragInteractionForPolicy(_webView._dragInteractionPolicy)];
+    _dragInteraction = adoptNS([[UIDragInteraction alloc] initWithDelegate:self]);
+    _dropInteraction = adoptNS([[UIDropInteraction alloc] initWithDelegate:self]);
+    [_dragInteraction _setLiftDelay:self.dragLiftDelay];
+    [_dragInteraction setEnabled:shouldEnableDragInteractionForPolicy(_webView._dragInteractionPolicy)];
 
-    [self addInteraction:_dataInteraction.get()];
-    [self addInteraction:_dataOperation.get()];
+    [self addInteraction:_dragInteraction.get()];
+    [self addInteraction:_dropInteraction.get()];
 }
 
 - (void)teardownDataInteractionDelegates
 {
-    if (_dataInteraction)
-        [self removeInteraction:_dataInteraction.get()];
+    if (_dragInteraction)
+        [self removeInteraction:_dragInteraction.get()];
 
-    if (_dataOperation)
-        [self removeInteraction:_dataOperation.get()];
+    if (_dropInteraction)
+        [self removeInteraction:_dropInteraction.get()];
 
-    _dataInteraction = nil;
-    _dataOperation = nil;
+    _dragInteraction = nil;
+    _dropInteraction = nil;
 
     [self cleanUpDragSourceSessionState];
 }
@@ -4255,19 +4255,13 @@
 {
     ASSERT(item.sourceAction != DragSourceActionNone);
 
-    _dataInteractionState.image = adoptNS([[UIImage alloc] initWithCGImage:image.get() scale:_page->deviceScaleFactor() orientation:UIImageOrientationUp]);
-    _dataInteractionState.indicatorData = item.image.indicatorData();
-    _dataInteractionState.sourceAction = static_cast<DragSourceAction>(item.sourceAction);
-    _dataInteractionState.adjustedOrigin = item.eventPositionInContentCoordinates;
-    _dataInteractionState.elementBounds = item.elementBounds;
-    _dataInteractionState.linkTitle = item.title.isEmpty() ? nil : (NSString *)item.title;
-    _dataInteractionState.linkURL = item.url.isEmpty() ? nil : (NSURL *)item.url;
+    auto dragImage = adoptNS([[UIImage alloc] initWithCGImage:image.get() scale:_page->deviceScaleFactor() orientation:UIImageOrientationUp]);
+    _dragDropInteractionState.stageDragItem(item, dragImage.get());
 }
 
 - (void)_didHandleStartDataInteractionRequest:(BOOL)started
 {
-    BlockPtr<void()> savedCompletionBlock = _dataInteractionState.dragStartCompletionBlock;
-    _dataInteractionState.dragStartCompletionBlock = nil;
+    BlockPtr<void()> savedCompletionBlock = _dragDropInteractionState.takeDragStartCompletionBlock();
     ASSERT(savedCompletionBlock);
 
     RELEASE_LOG(DragAndDrop, "Handling drag start request (started: %d, completion block: %p)", started, savedCompletionBlock.get());
@@ -4274,105 +4268,28 @@
     if (savedCompletionBlock)
         savedCompletionBlock();
 
-    if (![_dataInteractionState.dragSession items].count) {
-        CGPoint adjustedOrigin = _dataInteractionState.adjustedOrigin;
+    if (!_dragDropInteractionState.dragSession().items.count) {
+        auto positionForDragEnd = roundedIntPoint(_dragDropInteractionState.adjustedPositionForDragEnd());
         [self cleanUpDragSourceSessionState];
         if (started) {
             // A client of the Objective C SPI or UIKit might have prevented the drag from beginning entirely in the UI process, in which case
             // we need to balance the `dragstart` event with a `dragend`.
-            _page->dragEnded(roundedIntPoint(adjustedOrigin), roundedIntPoint([self convertPoint:adjustedOrigin toView:self.window]), DragOperationNone);
+            _page->dragEnded(positionForDragEnd, positionForDragEnd, DragOperationNone);
         }
     }
 }
 
-static RetainPtr<UIImage> uiImageForImage(RefPtr<Image> image)
-{
-    if (!image)
-        return nullptr;
-
-    auto cgImage = image->nativeImage();
-    if (!cgImage)
-        return nullptr;
-
-    return adoptNS([[UIImage alloc] initWithCGImage:cgImage.get()]);
-}
-
-static BOOL shouldUseTextIndicatorToCreatePreviewForDragAction(DragSourceAction action)
-{
-    if (action & (DragSourceActionLink | DragSourceActionSelection))
-        return YES;
-
-#if ENABLE(ATTACHMENT_ELEMENT)
-    if (action & DragSourceActionAttachment)
-        return YES;
-#endif
-
-    return NO;
-}
-
-- (RetainPtr<UITargetedDragPreview>)dragPreviewForImage:(UIImage *)image frameInRootViewCoordinates:(const FloatRect&)frame clippingRectsInFrameCoordinates:(const Vector<FloatRect>&)clippingRects backgroundColor:(UIColor *)backgroundColor
-{
-    if (frame.isEmpty() || !image)
-        return nullptr;
-
-    UIView *container = [self unscaledView];
-    FloatRect frameInContainerCoordinates;
-    NSMutableArray *clippingRectValuesInFrameCoordinates = [NSMutableArray arrayWithCapacity:clippingRects.size()];
-
-    frameInContainerCoordinates = [self convertRect:frame toView:container];
-    if (frameInContainerCoordinates.isEmpty())
-        return nullptr;
-
-    float widthScalingRatio = frameInContainerCoordinates.width() / frame.width();
-    float heightScalingRatio = frameInContainerCoordinates.height() / frame.height();
-    for (auto rect : clippingRects) {
-        rect.scale(widthScalingRatio, heightScalingRatio);
-        [clippingRectValuesInFrameCoordinates addObject:[NSValue valueWithCGRect:rect]];
-    }
-
-    auto imageView = adoptNS([[UIImageView alloc] initWithImage:image]);
-    [imageView setFrame:frameInContainerCoordinates];
-
-    RetainPtr<UIDragPreviewParameters> parameters;
-    if (clippingRectValuesInFrameCoordinates.count)
-        parameters = adoptNS([[UIDragPreviewParameters alloc] initWithTextLineRects:clippingRectValuesInFrameCoordinates]);
-    else
-        parameters = adoptNS([[UIDragPreviewParameters alloc] init]);
-
-    if (backgroundColor)
-        [parameters setBackgroundColor:backgroundColor];
-
-    CGPoint centerInContainerCoordinates = { CGRectGetMidX(frameInContainerCoordinates), CGRectGetMidY(frameInContainerCoordinates) };
-    auto target = adoptNS([[UIDragPreviewTarget alloc] initWithContainer:container center:centerInContainerCoordinates]);
-    auto dragPreview = adoptNS([[UITargetedDragPreview alloc] initWithView:imageView.get() parameters:parameters.get() target:target.get()]);
-    return dragPreview;
-}
-
-- (RetainPtr<UITargetedDragPreview>)dragPreviewForCurrentDataInteractionState
-{
-    auto action = ""
-    if (action & DragSourceActionImage && _dataInteractionState.image) {
-        Vector<FloatRect> emptyClippingRects;
-        return [self dragPreviewForImage:_dataInteractionState.image.get() frameInRootViewCoordinates:_dataInteractionState.elementBounds clippingRectsInFrameCoordinates:emptyClippingRects backgroundColor:nil];
-    }
-
-    if (shouldUseTextIndicatorToCreatePreviewForDragAction(action) && _dataInteractionState.indicatorData) {
-        auto indicator = _dataInteractionState.indicatorData.value();
-        return [self dragPreviewForImage:uiImageForImage(indicator.contentImage).get() frameInRootViewCoordinates:indicator.textBoundingRectInRootViewCoordinates clippingRectsInFrameCoordinates:indicator.textRectsInBoundingRectCoordinates backgroundColor:[UIColor colorWithCGColor:cachedCGColor(indicator.estimatedBackgroundColor)]];
-    }
-
-    return nil;
-}
-
 - (void)computeClientAndGlobalPointsForDropSession:(id <UIDropSession>)session outClientPoint:(CGPoint *)outClientPoint outGlobalPoint:(CGPoint *)outGlobalPoint
 {
+    // FIXME: This makes the behavior of drag events on iOS consistent with other synthetic mouse events on iOS (see WebPage::completeSyntheticClick).
+    // However, we should experiment with making the client position relative to the window and the global position in document coordinates. See
+    // https://bugs.webkit.org/show_bug.cgi?id=173855 for more details.
+    auto locationInContentView = [session locationInView:self];
     if (outClientPoint)
-        *outClientPoint = [session locationInView:self];
+        *outClientPoint = locationInContentView;
 
-    if (outGlobalPoint) {
-        UIWindow *window = self.window;
-        *outGlobalPoint = window ? [session locationInView:window] : _dataInteractionState.lastGlobalPosition;
-    }
+    if (outGlobalPoint)
+        *outGlobalPoint = locationInContentView;
 }
 
 static UIDropOperation dropOperationForWebCoreDragOperation(DragOperation operation)
@@ -4405,27 +4322,23 @@
         [[WebItemProviderPasteboard sharedInstance] setItemProviders:nil];
     }
 
-    if (auto completionBlock = _dataInteractionState.dragCancelSetDownBlock) {
-        _dataInteractionState.dragCancelSetDownBlock = nil;
-        completionBlock();
-    }
+    [[WebItemProviderPasteboard sharedInstance] stageRegistrationList:nil];
+    [self _restoreCalloutBarIfNeeded];
 
-    if (auto completionBlock = _dataInteractionState.dragStartCompletionBlock) {
-        // If the previous drag session is still initializing, we need to ensure that its completion block is called to prevent UIKit from getting out of state.
-        _dataInteractionState.dragStartCompletionBlock = nil;
-        completionBlock();
-    }
+    [_visibleContentViewSnapshot removeFromSuperview];
+    _visibleContentViewSnapshot = nil;
+    [_editDropCaretView remove];
+    _editDropCaretView = nil;
+    _isAnimatingConcludeEditDrag = NO;
+    _shouldRestoreCalloutBarAfterDrop = NO;
 
-    [self _restoreCalloutBarIfNeeded];
-    [_dataInteractionState.caretView remove];
-    [_dataInteractionState.visibleContentViewSnapshot removeFromSuperview];
-
-    _dataInteractionState = { };
+    _dragDropInteractionState.dragAndDropSessionsDidEnd();
+    _dragDropInteractionState = { };
 }
 
 static NSArray<UIItemProvider *> *extractItemProvidersFromDragItems(NSArray<UIDragItem *> *dragItems)
 {
-    __block NSMutableArray<UIItemProvider *> *providers = [NSMutableArray array];
+    NSMutableArray<UIItemProvider *> *providers = [NSMutableArray array];
     for (UIDragItem *item in dragItems) {
         RetainPtr<UIItemProvider> provider = item.itemProvider;
         if (provider)
@@ -4457,9 +4370,9 @@
     [unselectedContentSnapshot setFrame:data->contentImageWithoutSelectionRectInRootViewCoordinates];
 
     RetainPtr<WKContentView> protectedSelf = self;
-    RetainPtr<UIView> visibleContentViewSnapshot = adoptNS(_dataInteractionState.visibleContentViewSnapshot.leakRef());
+    RetainPtr<UIView> visibleContentViewSnapshot = adoptNS(_visibleContentViewSnapshot.leakRef());
 
-    _dataInteractionState.isAnimatingConcludeEditDrag = YES;
+    _isAnimatingConcludeEditDrag = YES;
     [self insertSubview:unselectedContentSnapshot.get() belowSubview:visibleContentViewSnapshot.get()];
     [UIView animateWithDuration:0.25 animations:^() {
         [visibleContentViewSnapshot setAlpha:0];
@@ -4478,37 +4391,20 @@
 {
     RELEASE_LOG(DragAndDrop, "Finished performing drag controller operation (handled: %d)", handled);
     [[WebItemProviderPasteboard sharedInstance] decrementPendingOperationCount];
-    RetainPtr<id <UIDropSession>> dropSession = _dataInteractionState.dropSession;
+    id <UIDropSession> dropSession = _dragDropInteractionState.dropSession();
     if ([self.webViewUIDelegate respondsToSelector:@selector(_webView:dataInteractionOperationWasHandled:forSession:itemProviders:)])
-        [self.webViewUIDelegate _webView:_webView dataInteractionOperationWasHandled:handled forSession:dropSession.get() itemProviders:[WebItemProviderPasteboard sharedInstance].itemProviders];
+        [self.webViewUIDelegate _webView:_webView dataInteractionOperationWasHandled:handled forSession:dropSession itemProviders:[WebItemProviderPasteboard sharedInstance].itemProviders];
 
-    if (!_dataInteractionState.isAnimatingConcludeEditDrag)
+    if (!_isAnimatingConcludeEditDrag)
         self.suppressAssistantSelectionView = NO;
 
     CGPoint global;
     CGPoint client;
-    [self computeClientAndGlobalPointsForDropSession:dropSession.get() outClientPoint:&client outGlobalPoint:&global];
+    [self computeClientAndGlobalPointsForDropSession:dropSession outClientPoint:&client outGlobalPoint:&global];
     [self cleanUpDragSourceSessionState];
     _page->dragEnded(roundedIntPoint(client), roundedIntPoint(global), _page->currentDragOperation());
 }
 
-- (void)_transitionDragPreviewToImageIfNecessary:(id <UIDragSession>)session
-{
-    if (_dataInteractionState.sourceAction & DragSourceActionImage || !(_dataInteractionState.sourceAction & DragSourceActionLink))
-        return;
-
-    auto linkDraggingCenter = _dataInteractionState.adjustedOrigin;
-    RetainPtr<NSString> title = (NSString *)_dataInteractionState.linkTitle;
-    RetainPtr<NSURL> url = "" *)_dataInteractionState.linkURL;
-    session.items.firstObject.previewProvider = [title, url, linkDraggingCenter] () -> UIDragPreview * {
-        UIURLDragPreviewView *previewView = [UIURLDragPreviewView viewWithTitle:title.get() URL:url.get()];
-        previewView.center = linkDraggingCenter;
-
-        UIDragPreviewParameters *parameters = [[[UIDragPreviewParameters alloc] initWithTextLineRects:@[ [NSValue valueWithCGRect:previewView.bounds] ]] autorelease];
-        return [[[UIDragPreview alloc] initWithView:previewView parameters:parameters] autorelease];
-    };
-}
-
 - (void)_didChangeDataInteractionCaretRect:(CGRect)previousRect currentRect:(CGRect)rect
 {
     BOOL previousRectIsEmpty = CGRectIsEmpty(previousRect);
@@ -4517,18 +4413,18 @@
         return;
 
     if (previousRectIsEmpty) {
-        _dataInteractionState.caretView = adoptNS([[_UITextDragCaretView alloc] initWithTextInputView:self]);
-        [_dataInteractionState.caretView insertAtPosition:[WKTextPosition textPositionWithRect:rect]];
+        _editDropCaretView = adoptNS([[_UITextDragCaretView alloc] initWithTextInputView:self]);
+        [_editDropCaretView insertAtPosition:[WKTextPosition textPositionWithRect:rect]];
         return;
     }
 
     if (currentRectIsEmpty) {
-        [_dataInteractionState.caretView remove];
-        _dataInteractionState.caretView = nil;
+        [_editDropCaretView remove];
+        _editDropCaretView = nil;
         return;
     }
 
-    [_dataInteractionState.caretView updateToPosition:[WKTextPosition textPositionWithRect:rect]];
+    [_editDropCaretView updateToPosition:[WKTextPosition textPositionWithRect:rect]];
 }
 
 - (WKDragDestinationAction)_dragDestinationActionForDropSession:(id <UIDropSession>)session
@@ -4542,20 +4438,20 @@
 
 - (id <UIDragDropSession>)currentDragOrDropSession
 {
-    if (_dataInteractionState.dropSession)
-        return _dataInteractionState.dropSession.get();
-    return _dataInteractionState.dragSession.get();
+    if (_dragDropInteractionState.dropSession())
+        return _dragDropInteractionState.dropSession();
+    return _dragDropInteractionState.dragSession();
 }
 
 - (void)_restoreCalloutBarIfNeeded
 {
-    if (!_dataInteractionState.shouldRestoreCalloutBar)
+    if (!_shouldRestoreCalloutBarAfterDrop)
         return;
 
     // FIXME: This SPI should be renamed in UIKit to reflect a more general purpose of revealing hidden interaction assistant controls.
     [_webSelectionAssistant didEndScrollingOverflow];
     [_textSelectionAssistant didEndScrollingOverflow];
-    _dataInteractionState.shouldRestoreCalloutBar = NO;
+    _shouldRestoreCalloutBarAfterDrop = NO;
 }
 
 #pragma mark - UIDragInteractionDelegate
@@ -4595,9 +4491,9 @@
 
     [self cleanUpDragSourceSessionState];
 
+    _dragDropInteractionState.prepareForDragSession(session, completion);
+
     auto dragOrigin = roundedIntPoint([session locationInView:self]);
-    _dataInteractionState.dragStartCompletionBlock = completion;
-    _dataInteractionState.dragSession = session;
     _page->requestStartDataInteraction(dragOrigin, roundedIntPoint([self convertPoint:dragOrigin toView:self.window]));
 
     RELEASE_LOG(DragAndDrop, "Drag session requested: %p at origin: {%d, %d}", session, dragOrigin.x(), dragOrigin.y());
@@ -4605,57 +4501,58 @@
 
 - (NSArray<UIDragItem *> *)dragInteraction:(UIDragInteraction *)interaction itemsForBeginningSession:(id <UIDragSession>)session
 {
+    ASSERT(interaction == _dragInteraction);
     RELEASE_LOG(DragAndDrop, "Drag items requested for session: %p", session);
-    if (_dataInteractionState.dragSession != session) {
-        RELEASE_LOG(DragAndDrop, "Drag session failed: %p (delegate session does not match %p)", session, _dataInteractionState.dragSession.get());
+    if (_dragDropInteractionState.dragSession() != session) {
+        RELEASE_LOG(DragAndDrop, "Drag session failed: %p (delegate session does not match %p)", session, _dragDropInteractionState.dragSession());
         return @[ ];
     }
 
-    if (_dataInteractionState.sourceAction == DragSourceActionNone) {
-        RELEASE_LOG(DragAndDrop, "Drag session failed: %p (no drag source action)", session);
+    if (!_dragDropInteractionState.hasStagedDragSource()) {
+        RELEASE_LOG(DragAndDrop, "Drag session failed: %p (missing staged drag source)", session);
         return @[ ];
     }
 
-    WebItemProviderPasteboard *draggingPasteboard = [WebItemProviderPasteboard sharedInstance];
-    ASSERT(interaction == _dataInteraction);
-    NSUInteger numberOfItems = draggingPasteboard.numberOfItems;
-    if (!numberOfItems) {
+    auto stagedDragSource = _dragDropInteractionState.stagedDragSource();
+    WebItemProviderRegistrationInfoList *registrationList = [[WebItemProviderPasteboard sharedInstance] takeRegistrationList];
+    UIItemProvider *defaultItemProvider = registrationList.itemProvider;
+    if (!defaultItemProvider) {
         RELEASE_LOG(DragAndDrop, "Drag session failed: %p (no item providers generated before adjustment)", session);
         _page->dragCancelled();
+        _dragDropInteractionState.clearStagedDragSource();
         return @[ ];
     }
 
     // Give internal clients such as Mail one final chance to augment the contents of each UIItemProvider before sending the drag items off to UIKit.
+    NSArray *adjustedItemProviders;
     id <WKUIDelegatePrivate> uiDelegate = self.webViewUIDelegate;
     if ([uiDelegate respondsToSelector:@selector(_webView:adjustedDataInteractionItemProvidersForItemProvider:representingObjects:additionalData:)]) {
-        NSMutableArray *adjustedItemProviders = [NSMutableArray array];
-        for (NSUInteger itemIndex = 0; itemIndex < numberOfItems; ++itemIndex) {
-            WebItemProviderRegistrationInfoList *infoList = [draggingPasteboard registrationInfoAtIndex:itemIndex];
-            auto representingObjects = adoptNS([[NSMutableArray alloc] init]);
-            auto additionalData = adoptNS([[NSMutableDictionary alloc] init]);
-            [infoList 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];
-            }];
-            if (NSArray *replacementItemProviders = [uiDelegate _webView:_webView adjustedDataInteractionItemProvidersForItemProvider:[draggingPasteboard itemProviderAtIndex:itemIndex] representingObjects:representingObjects.get() additionalData:additionalData.get()])
-                [adjustedItemProviders addObjectsFromArray:replacementItemProviders];
-        }
-        draggingPasteboard.itemProviders = adjustedItemProviders;
-    } else if ([uiDelegate respondsToSelector:@selector(_webView:adjustedDataInteractionItemProviders:)])
-        draggingPasteboard.itemProviders = [uiDelegate _webView:_webView adjustedDataInteractionItemProviders:draggingPasteboard.itemProviders];
+        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];
+        }];
+        adjustedItemProviders = [uiDelegate _webView:_webView adjustedDataInteractionItemProvidersForItemProvider:defaultItemProvider representingObjects:representingObjects.get() additionalData:additionalData.get()];
+    } else
+        adjustedItemProviders = @[ defaultItemProvider ];
 
-    __block RetainPtr<NSMutableArray> itemsForDragInteraction = [NSMutableArray array];
-    [draggingPasteboard enumerateItemProvidersWithBlock:^(UIItemProvider *itemProvider, NSUInteger index, BOOL *stop) {
-        [itemsForDragInteraction addObject:[[[UIDragItem alloc] initWithItemProvider:itemProvider] autorelease]];
-    }];
+    NSMutableArray *dragItems = [NSMutableArray arrayWithCapacity:adjustedItemProviders.count];
+    for (UIItemProvider *itemProvider in adjustedItemProviders) {
+        auto item = adoptNS([[UIDragItem alloc] initWithItemProvider:itemProvider]);
+        [item _setPrivateLocalContext:@(stagedDragSource.itemIdentifier)];
+        [dragItems addObject:item.autorelease()];
+    }
 
-    if (![itemsForDragInteraction count])
+    if (![dragItems count])
         _page->dragCancelled();
 
-    RELEASE_LOG(DragAndDrop, "Drag session: %p starting with %tu items", session, [itemsForDragInteraction count]);
-    return itemsForDragInteraction.get();
+    RELEASE_LOG(DragAndDrop, "Drag session: %p starting with %tu items", session, [dragItems count]);
+    _dragDropInteractionState.clearStagedDragSource([dragItems count] ? DragDropInteractionState::DidBecomeActive::Yes : DragDropInteractionState::DidBecomeActive::No);
+
+    return dragItems;
 }
 
 - (UITargetedDragPreview *)dragInteraction:(UIDragInteraction *)interaction previewForLiftingItem:(UIDragItem *)item session:(id <UIDragSession>)session
@@ -4666,27 +4563,26 @@
         if (overridenPreview)
             return overridenPreview;
     }
-    return self.dragPreviewForCurrentDataInteractionState.autorelease();
+    return _dragDropInteractionState.previewForDragItem(item, self, self.unscaledView);
 }
 
 - (void)dragInteraction:(UIDragInteraction *)interaction willAnimateLiftWithAnimator:(id <UIDragAnimating>)animator session:(id <UIDragSession>)session
 {
-    if (!_dataInteractionState.shouldRestoreCalloutBar && (_dataInteractionState.sourceAction & DragSourceActionSelection)) {
+    if (!_shouldRestoreCalloutBarAfterDrop && _dragDropInteractionState.anyActiveDragSourceIs(DragSourceActionSelection)) {
         // FIXME: This SPI should be renamed in UIKit to reflect a more general purpose of hiding interaction assistant controls.
         [_webSelectionAssistant willStartScrollingOverflow];
         [_textSelectionAssistant willStartScrollingOverflow];
-        _dataInteractionState.shouldRestoreCalloutBar = YES;
+        _shouldRestoreCalloutBarAfterDrop = YES;
     }
 
-    auto adjustedOrigin = _dataInteractionState.adjustedOrigin;
+    auto positionForDragEnd = roundedIntPoint(_dragDropInteractionState.adjustedPositionForDragEnd());
     RetainPtr<WKContentView> protectedSelf(self);
-    [animator addCompletion:[session, adjustedOrigin, protectedSelf, page = _page] (UIViewAnimatingPosition finalPosition) {
+    [animator addCompletion:[session, positionForDragEnd, protectedSelf, page = _page] (UIViewAnimatingPosition finalPosition) {
         if (finalPosition == UIViewAnimatingPositionStart) {
             RELEASE_LOG(DragAndDrop, "Drag session ended at start: %p", session);
             // The lift was canceled, so -dropInteraction:sessionDidEnd: will never be invoked. This is the last chance to clean up.
             [protectedSelf cleanUpDragSourceSessionState];
-            auto originInWindowCoordinates = [protectedSelf convertPoint:adjustedOrigin toView:[protectedSelf window]];
-            page->dragEnded(roundedIntPoint(adjustedOrigin), roundedIntPoint(originInWindowCoordinates), DragOperationNone);
+            page->dragEnded(positionForDragEnd, positionForDragEnd, DragOperationNone);
         }
     }];
 }
@@ -4699,16 +4595,13 @@
         [uiDelegate _webView:_webView dataInteraction:interaction sessionWillBegin:session];
 
     [_actionSheetAssistant cleanupSheet];
-
-    _dataInteractionState.didBeginDragging = YES;
-    [self _transitionDragPreviewToImageIfNecessary:session];
-
+    _dragDropInteractionState.dragSessionWillBegin();
     _page->didStartDrag();
 }
 
 - (void)dragInteraction:(UIDragInteraction *)interaction session:(id <UIDragSession>)session didEndWithOperation:(UIDropOperation)operation
 {
-    RELEASE_LOG(DragAndDrop, "Drag session ended: %p (with operation: %tu, performing operation: %d, began dragging: %d)", session, operation, _dataInteractionState.isPerformingOperation, _dataInteractionState.didBeginDragging);
+    RELEASE_LOG(DragAndDrop, "Drag session ended: %p (with operation: %tu, performing operation: %d, began dragging: %d)", session, operation, _dragDropInteractionState.isPerformingDrop(), _dragDropInteractionState.didBeginDragging());
 
     [self _restoreCalloutBarIfNeeded];
 
@@ -4716,11 +4609,11 @@
     if ([uiDelegate respondsToSelector:@selector(_webView:dataInteraction:session:didEndWithOperation:)])
         [uiDelegate _webView:_webView dataInteraction:interaction session:session didEndWithOperation:operation];
 
-    if (_dataInteractionState.isPerformingOperation)
+    if (_dragDropInteractionState.isPerformingDrop())
         return;
 
     [self cleanUpDragSourceSessionState];
-    _page->dragEnded(roundedIntPoint(_dataInteractionState.adjustedOrigin), roundedIntPoint([self convertPoint:_dataInteractionState.adjustedOrigin toView:self.window]), operation);
+    _page->dragEnded(roundedIntPoint(_dragDropInteractionState.adjustedPositionForDragEnd()), roundedIntPoint(_dragDropInteractionState.adjustedPositionForDragEnd()), operation);
 }
 
 - (UITargetedDragPreview *)dragInteraction:(UIDragInteraction *)interaction previewForCancellingItem:(UIDragItem *)item withDefault:(UITargetedDragPreview *)defaultPreview
@@ -4731,12 +4624,12 @@
         if (overridenPreview)
             return overridenPreview;
     }
-    return self.dragPreviewForCurrentDataInteractionState.autorelease();
+    return _dragDropInteractionState.previewForDragItem(item, self, self.unscaledView);
 }
 
 - (BOOL)_dragInteraction:(UIDragInteraction *)interaction item:(UIDragItem *)item shouldDelaySetDownAnimationWithCompletion:(void(^)(void))completion
 {
-    _dataInteractionState.dragCancelSetDownBlock = completion;
+    _dragDropInteractionState.dragSessionWillDelaySetDownAnimation(completion);
     return YES;
 }
 
@@ -4744,8 +4637,7 @@
 {
     [animator addCompletion:[protectedSelf = retainPtr(self), page = _page] (UIViewAnimatingPosition finalPosition) {
         page->dragCancelled();
-        if (auto completion = protectedSelf->_dataInteractionState.dragCancelSetDownBlock) {
-            protectedSelf->_dataInteractionState.dragCancelSetDownBlock = nil;
+        if (auto completion = protectedSelf->_dragDropInteractionState.takeDragCancelSetDownBlock()) {
             page->callAfterNextPresentationUpdate([completion] (CallbackBase::Error) {
                 completion();
             });
@@ -4776,14 +4668,12 @@
 - (void)dropInteraction:(UIDropInteraction *)interaction sessionDidEnter:(id <UIDropSession>)session
 {
     RELEASE_LOG(DragAndDrop, "Drop session entered: %p with %tu items", session, session.items.count);
-    _dataInteractionState.dropSession = session;
+    auto dragData = [self dragDataForDropSession:session dragDestinationAction:[self _dragDestinationActionForDropSession:session]];
 
+    _dragDropInteractionState.dropSessionDidEnterOrUpdate(session, dragData);
+
     [[WebItemProviderPasteboard sharedInstance] setItemProviders:extractItemProvidersFromDropSession(session)];
-
-    auto dragData = [self dragDataForDropSession:session dragDestinationAction:[self _dragDestinationActionForDropSession:session]];
-
     _page->dragEntered(dragData, "data interaction pasteboard");
-    _dataInteractionState.lastGlobalPosition = dragData.globalPosition();
 }
 
 - (UIDropProposal *)dropInteraction:(UIDropInteraction *)interaction sessionDidUpdate:(id <UIDropSession>)session
@@ -4792,7 +4682,7 @@
 
     auto dragData = [self dragDataForDropSession:session dragDestinationAction:[self _dragDestinationActionForDropSession:session]];
     _page->dragUpdated(dragData, "data interaction pasteboard");
-    _dataInteractionState.lastGlobalPosition = dragData.globalPosition();
+    _dragDropInteractionState.dropSessionDidEnterOrUpdate(session, dragData);
 
     NSUInteger operation = dropOperationForWebCoreDragOperation(_page->currentDragOperation());
     if ([self.webViewUIDelegate respondsToSelector:@selector(_webView:willUpdateDataInteractionOperationToOperation:forSession:)])
@@ -4810,7 +4700,7 @@
     _page->dragExited(dragData, "data interaction pasteboard");
     _page->resetCurrentDragInformation();
 
-    _dataInteractionState.dropSession = nil;
+    _dragDropInteractionState.dropSessionDidExit();
 }
 
 - (void)dropInteraction:(UIDropInteraction *)interaction performDrop:(id <UIDropSession>)session
@@ -4828,9 +4718,10 @@
             return;
     }
 
+    _dragDropInteractionState.dropSessionWillPerformDrop();
+
     [[WebItemProviderPasteboard sharedInstance] setItemProviders:itemProviders];
     [[WebItemProviderPasteboard sharedInstance] incrementPendingOperationCount];
-    _dataInteractionState.isPerformingOperation = YES;
     auto dragData = [self dragDataForDropSession:session dragDestinationAction:WKDragDestinationActionAny];
 
     RELEASE_LOG(DragAndDrop, "Loading data from %tu item providers for session: %p", itemProviders.count, session);
@@ -4851,11 +4742,11 @@
         retainedSelf->_page->createSandboxExtensionsIfNeeded(filenames, sandboxExtensionHandle, sandboxExtensionForUpload);
         retainedSelf->_page->performDragOperation(capturedDragData, "data interaction pasteboard", sandboxExtensionHandle, sandboxExtensionForUpload);
 
-        retainedSelf->_dataInteractionState.visibleContentViewSnapshot = [retainedSelf snapshotViewAfterScreenUpdates:NO];
+        retainedSelf->_visibleContentViewSnapshot = [retainedSelf snapshotViewAfterScreenUpdates:NO];
         [retainedSelf setSuppressAssistantSelectionView:YES];
         [UIView performWithoutAnimation:[retainedSelf] {
-            [retainedSelf->_dataInteractionState.visibleContentViewSnapshot setFrame:[retainedSelf bounds]];
-            [retainedSelf addSubview:retainedSelf->_dataInteractionState.visibleContentViewSnapshot.get()];
+            [retainedSelf->_visibleContentViewSnapshot setFrame:[retainedSelf bounds]];
+            [retainedSelf addSubview:retainedSelf->_visibleContentViewSnapshot.get()];
         }];
     }];
 }
@@ -4877,10 +4768,17 @@
 
 - (void)dropInteraction:(UIDropInteraction *)interaction sessionDidEnd:(id <UIDropSession>)session
 {
-    RELEASE_LOG(DragAndDrop, "Drop session ended: %p (performing operation: %d, began dragging: %d)", session, _dataInteractionState.isPerformingOperation, _dataInteractionState.didBeginDragging);
-    if (_dataInteractionState.isPerformingOperation || _dataInteractionState.didBeginDragging)
+    RELEASE_LOG(DragAndDrop, "Drop session ended: %p (performing operation: %d, began dragging: %d)", session, _dragDropInteractionState.isPerformingDrop(), _dragDropInteractionState.didBeginDragging());
+    if (_dragDropInteractionState.isPerformingDrop()) {
+        // In the case where we are performing a drop, wait until after the drop is handled in the web process to reset drag and drop interaction state.
         return;
+    }
 
+    if (_dragDropInteractionState.didBeginDragging()) {
+        // In the case where the content view is a source of drag items, wait until -dragInteraction:session:didEndWithOperation: to reset drag and drop interaction state.
+        return;
+    }
+
     CGPoint global;
     CGPoint client;
     [self computeClientAndGlobalPointsForDropSession:session outClientPoint:&client outGlobalPoint:&global];
@@ -4892,42 +4790,42 @@
 
 - (void)_simulateDataInteractionEntered:(id)session
 {
-    [self dropInteraction:_dataOperation.get() sessionDidEnter:session];
+    [self dropInteraction:_dropInteraction.get() sessionDidEnter:session];
 }
 
 - (NSUInteger)_simulateDataInteractionUpdated:(id)session
 {
-    return [self dropInteraction:_dataOperation.get() sessionDidUpdate:session].operation;
+    return [self dropInteraction:_dropInteraction.get() sessionDidUpdate:session].operation;
 }
 
 - (void)_simulateDataInteractionEnded:(id)session
 {
-    [self dropInteraction:_dataOperation.get() sessionDidEnd:session];
+    [self dropInteraction:_dropInteraction.get() sessionDidEnd:session];
 }
 
 - (void)_simulateDataInteractionPerformOperation:(id)session
 {
-    [self dropInteraction:_dataOperation.get() performDrop:session];
+    [self dropInteraction:_dropInteraction.get() performDrop:session];
 }
 
 - (void)_simulateDataInteractionSessionDidEnd:(id)session
 {
-    [self dragInteraction:_dataInteraction.get() session:session didEndWithOperation:UIDropOperationCopy];
+    [self dragInteraction:_dragInteraction.get() session:session didEndWithOperation:UIDropOperationCopy];
 }
 
 - (void)_simulateWillBeginDataInteractionWithSession:(id)session
 {
-    [self dragInteraction:_dataInteraction.get() sessionWillBegin:session];
+    [self dragInteraction:_dragInteraction.get() sessionWillBegin:session];
 }
 
 - (NSArray *)_simulatedItemsForSession:(id)session
 {
-    return [self dragInteraction:_dataInteraction.get() itemsForBeginningSession:session];
+    return [self dragInteraction:_dragInteraction.get() itemsForBeginningSession:session];
 }
 
 - (void)_simulatePrepareForDataInteractionSession:(id)session completion:(dispatch_block_t)completion
 {
-    [self _dragInteraction:_dataInteraction.get() prepareForSession:session completion:completion];
+    [self _dragInteraction:_dragInteraction.get() prepareForSession:session completion:completion];
 }
 
 #endif

Modified: trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj (221594 => 221595)


--- trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj	2017-09-04 20:24:10 UTC (rev 221594)
+++ trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj	2017-09-04 23:36:20 UTC (rev 221595)
@@ -2057,6 +2057,8 @@
 		F409BA181E6E64BC009DA28E /* WKDragDestinationAction.h in Headers */ = {isa = PBXBuildFile; fileRef = F409BA171E6E64B3009DA28E /* WKDragDestinationAction.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		F44DFEB21E9E752F0038D196 /* WebIconUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = F44DFEB01E9E752F0038D196 /* WebIconUtilities.h */; };
 		F44DFEB31E9E752F0038D196 /* WebIconUtilities.mm in Sources */ = {isa = PBXBuildFile; fileRef = F44DFEB11E9E752F0038D196 /* WebIconUtilities.mm */; };
+		F496A4311F58A272004C1757 /* DragDropInteractionState.h in Headers */ = {isa = PBXBuildFile; fileRef = F496A42F1F58A272004C1757 /* DragDropInteractionState.h */; };
+		F496A4321F58A272004C1757 /* DragDropInteractionState.mm in Sources */ = {isa = PBXBuildFile; fileRef = F496A4301F58A272004C1757 /* DragDropInteractionState.mm */; };
 		F6113E25126CE1820057D0A7 /* APIUserContentURLPattern.h in Headers */ = {isa = PBXBuildFile; fileRef = F6113E24126CE1820057D0A7 /* APIUserContentURLPattern.h */; };
 		F6113E28126CE19B0057D0A7 /* WKUserContentURLPattern.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F6113E26126CE19B0057D0A7 /* WKUserContentURLPattern.cpp */; };
 		F6113E29126CE19B0057D0A7 /* WKUserContentURLPattern.h in Headers */ = {isa = PBXBuildFile; fileRef = F6113E27126CE19B0057D0A7 /* WKUserContentURLPattern.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -4459,6 +4461,8 @@
 		F409BA171E6E64B3009DA28E /* WKDragDestinationAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKDragDestinationAction.h; sourceTree = "<group>"; };
 		F44DFEB01E9E752F0038D196 /* WebIconUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WebIconUtilities.h; path = ios/WebIconUtilities.h; sourceTree = "<group>"; };
 		F44DFEB11E9E752F0038D196 /* WebIconUtilities.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = WebIconUtilities.mm; path = ios/WebIconUtilities.mm; sourceTree = "<group>"; };
+		F496A42F1F58A272004C1757 /* DragDropInteractionState.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = DragDropInteractionState.h; path = ios/DragDropInteractionState.h; sourceTree = "<group>"; };
+		F496A4301F58A272004C1757 /* DragDropInteractionState.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = DragDropInteractionState.mm; path = ios/DragDropInteractionState.mm; sourceTree = "<group>"; };
 		F6113E24126CE1820057D0A7 /* APIUserContentURLPattern.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = APIUserContentURLPattern.h; sourceTree = "<group>"; };
 		F6113E26126CE19B0057D0A7 /* WKUserContentURLPattern.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WKUserContentURLPattern.cpp; sourceTree = "<group>"; };
 		F6113E27126CE19B0057D0A7 /* WKUserContentURLPattern.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKUserContentURLPattern.h; sourceTree = "<group>"; };
@@ -5516,6 +5520,8 @@
 				A19DD3BF1D07D16800AC823B /* _WKWebViewPrintFormatterInternal.h */,
 				1AD4C1911B39F33200ABC28E /* ApplicationStateTracker.h */,
 				1AD4C1901B39F33200ABC28E /* ApplicationStateTracker.mm */,
+				F496A42F1F58A272004C1757 /* DragDropInteractionState.h */,
+				F496A4301F58A272004C1757 /* DragDropInteractionState.mm */,
 				2DD45ADC1E5F8972006C355F /* InputViewUpdateDeferrer.h */,
 				2DD45ADD1E5F8972006C355F /* InputViewUpdateDeferrer.mm */,
 				0F0C365D18C110A500F607D7 /* LayerRepresentation.mm */,
@@ -8476,6 +8482,7 @@
 				1AD25E96167AB08100EA9BCD /* DownloadProxyMap.h in Headers */,
 				1AB7D61A1288B9D900CFD08C /* DownloadProxyMessages.h in Headers */,
 				C517388112DF8F4F00EE3F47 /* DragControllerAction.h in Headers */,
+				F496A4311F58A272004C1757 /* DragDropInteractionState.h in Headers */,
 				BC8452A81162C80900CAB9B5 /* DrawingArea.h in Headers */,
 				0FB659231208B4DB0044816C /* DrawingAreaInfo.h in Headers */,
 				1A64229A12DD029200CAAE2C /* DrawingAreaMessages.h in Headers */,
@@ -10037,6 +10044,7 @@
 				1AB7D4CB1288AAA700CFD08C /* DownloadProxy.cpp in Sources */,
 				1AD25E95167AB08100EA9BCD /* DownloadProxyMap.cpp in Sources */,
 				1AB7D6191288B9D900CFD08C /* DownloadProxyMessageReceiver.cpp in Sources */,
+				F496A4321F58A272004C1757 /* DragDropInteractionState.mm in Sources */,
 				BC8452A71162C80900CAB9B5 /* DrawingArea.cpp in Sources */,
 				1A64229912DD029200CAAE2C /* DrawingAreaMessageReceiver.cpp in Sources */,
 				BC2652161182608100243E12 /* DrawingAreaProxy.cpp in Sources */,

Modified: trunk/Tools/ChangeLog (221594 => 221595)


--- trunk/Tools/ChangeLog	2017-09-04 20:24:10 UTC (rev 221594)
+++ trunk/Tools/ChangeLog	2017-09-04 23:36:20 UTC (rev 221595)
@@ -1,3 +1,28 @@
+2017-09-04  Wenson Hsieh  <[email protected]>
+
+        [iOS DnD] Refactor drag and drop logic in WKContentView in preparation for supporting multiple drag items in a drag session
+        https://bugs.webkit.org/show_bug.cgi?id=176264
+        <rdar://problem/31144674>
+
+        Reviewed by Darin Adler.
+
+        Adds two new iOS drag and drop tests to check that the clientX and clientY attributes of mouse events propagated
+        to the page during drop are correct. Each test drags from an image element and drops into three custom-drop-
+        handling elements; `dragenter`, `dragover`, and `drop` event listeners on the body then use the clientX and
+        clientY event attributes to hit-test for drop target elements. The first test is suffixed with "-Basic"; the
+        second test, suffixed with "-WithScrollOffset", makes the document scrollable to check that clientY is correct
+        when scrolled.
+
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WebKitCocoa/drop-targets.html: Added.
+        * TestWebKitAPI/Tests/ios/DataInteractionTests.mm:
+        (TestWebKitAPI::testDragAndDropOntoTargetElements):
+        (TestWebKitAPI::TEST):
+        * TestWebKitAPI/cocoa/TestWKWebView.mm:
+        (-[TestWKWebView stringByEvaluatingJavaScript:]):
+
+        Log a warning message when an API test fails due to _javascript_ evaluation in TestWKWebView.
+
 2017-09-03  Filip Pizlo  <[email protected]>
 
         WSL IntLiteral should have variable type so that it can unify with things like uint

Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (221594 => 221595)


--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj	2017-09-04 20:24:10 UTC (rev 221594)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj	2017-09-04 23:36:20 UTC (rev 221595)
@@ -658,6 +658,7 @@
 		ECA680CE1E68CC0900731D20 /* StringUtilities.mm in Sources */ = {isa = PBXBuildFile; fileRef = ECA680CD1E68CC0900731D20 /* StringUtilities.mm */; };
 		F407FE391F1D0DFC0017CF25 /* enormous.svg in Copy Resources */ = {isa = PBXBuildFile; fileRef = F407FE381F1D0DE60017CF25 /* enormous.svg */; };
 		F415086D1DA040C50044BE9B /* play-audio-on-click.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F415086C1DA040C10044BE9B /* play-audio-on-click.html */; };
+		F4194AD11F5A320100ADD83F /* drop-targets.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F4194AD01F5A2EA500ADD83F /* drop-targets.html */; };
 		F41AB99F1EF4696B0083FA08 /* autofocus-contenteditable.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F41AB9981EF4692C0083FA08 /* autofocus-contenteditable.html */; };
 		F41AB9A01EF4696B0083FA08 /* background-image-link-and-input.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F41AB9971EF4692C0083FA08 /* background-image-link-and-input.html */; };
 		F41AB9A11EF4696B0083FA08 /* contenteditable-and-textarea.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F41AB99C1EF4692C0083FA08 /* contenteditable-and-textarea.html */; };
@@ -825,6 +826,7 @@
 				5714ECBB1CA8BFE400051AC8 /* DownloadRequestOriginalURLFrame.html in Copy Resources */,
 				F4A32EC41F05F3850047C544 /* dragstart-change-selection-offscreen.html in Copy Resources */,
 				F4D5E4E81F0C5D38008C1A49 /* dragstart-clear-selection.html in Copy Resources */,
+				F4194AD11F5A320100ADD83F /* drop-targets.html in Copy Resources */,
 				A155022C1E050D0300A24C57 /* duplicate-completion-handler-calls.html in Copy Resources */,
 				9984FACE1CFFB090008D198C /* editable-body.html in Copy Resources */,
 				F44D06451F395C26001A0E29 /* editor-state-test-harness.html in Copy Resources */,
@@ -1683,6 +1685,7 @@
 		F3FC3EE213678B7300126A65 /* libgtest.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgtest.a; sourceTree = BUILT_PRODUCTS_DIR; };
 		F407FE381F1D0DE60017CF25 /* enormous.svg */ = {isa = PBXFileReference; lastKnownFileType = text; path = enormous.svg; sourceTree = "<group>"; };
 		F415086C1DA040C10044BE9B /* play-audio-on-click.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "play-audio-on-click.html"; sourceTree = "<group>"; };
+		F4194AD01F5A2EA500ADD83F /* drop-targets.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "drop-targets.html"; sourceTree = "<group>"; };
 		F41AB9931EF4692C0083FA08 /* image-and-textarea.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "image-and-textarea.html"; sourceTree = "<group>"; };
 		F41AB9941EF4692C0083FA08 /* prevent-operation.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "prevent-operation.html"; sourceTree = "<group>"; };
 		F41AB9951EF4692C0083FA08 /* textarea-to-input.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "textarea-to-input.html"; sourceTree = "<group>"; };
@@ -2154,6 +2157,7 @@
 				5714ECBA1CA8BFD100051AC8 /* DownloadRequestOriginalURLFrame.html */,
 				F4A32EC31F05F3780047C544 /* dragstart-change-selection-offscreen.html */,
 				F4D5E4E71F0C5D27008C1A49 /* dragstart-clear-selection.html */,
+				F4194AD01F5A2EA500ADD83F /* drop-targets.html */,
 				A155022B1E050BC500A24C57 /* duplicate-completion-handler-calls.html */,
 				9984FACD1CFFB038008D198C /* editable-body.html */,
 				F44D06441F395C0D001A0E29 /* editor-state-test-harness.html */,

Added: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/drop-targets.html (0 => 221595)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/drop-targets.html	                        (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/drop-targets.html	2017-09-04 23:36:20 UTC (rev 221595)
@@ -0,0 +1,111 @@
+<!DOCTYPE html>
+<html>
+<meta name="viewport" content="width=device-width">
+<head>
+<style>
+    body {
+        width: 100%;
+        height: 100%;
+        margin: 0;
+    }
+
+    img {
+        width: 100px;
+        height: 100px;
+    }
+
+    #target1 {
+        left: 0;
+        top: 200px;
+    }
+
+    #target2 {
+        left: 200px;
+        top: 0;
+    }
+
+    #target3 {
+        left: 200px;
+        top: 200px;
+    }
+
+    .target {
+        position: absolute;
+        background-color: red;
+        border-radius: 50%;
+        width: 100px;
+        height: 100px;
+        color: white;
+        text-align: center;
+        line-height: 90px;
+    }
+
+    #description {
+        padding-top: 250px;
+        display: block;
+    }
+
+    #container {
+        position: relative;
+    }
+</style>
+</head>
+
+<body>
+    <div id="container">
+    <img src=""
+    <div id="target1" class="target"></div>
+    <div id="target2" class="target"></div>
+    <div id="target3" class="target"></div>
+    <code id="description">
+        <div>To test this manually, drag the image onto each of the three red circles.</div>
+        <div>Each red circle should turn green upon dragging over it.</div>
+        <div>Upon drop, the text "PASS" should appear in each target.</div>
+    </code>
+    </div>
+</body>
+
+<script>
+    function elementAtMouseEvent(e) {
+        if (window.usePageCoordinates)
+            return document.elementFromPoint(e.pageX - pageXOffset, e.pageY - pageYOffset);
+        return document.elementFromPoint(e.clientX, e.clientY);
+    }
+
+    function targetAtEvent(e) {
+        let target = elementAtMouseEvent(e);
+        return target && target.classList.contains("target") ? target : null;
+    }
+
+    function resetTestState() {
+        target1.style.backgroundColor = "";
+        target2.style.backgroundColor = "";
+        target3.style.backgroundColor = "";
+        target1.innerHTML = "";
+        target2.innerHTML = "";
+        target3.innerHTML = "";
+    }
+
+    document.body.addEventListener("dragstart", resetTestState);
+    document.body.addEventListener("dragenter", event => {
+        let target = targetAtEvent(event);
+        if (target)
+            target.style.backgroundColor = "green";
+        event.preventDefault();
+    });
+
+    document.body.addEventListener("dragover", event => {
+        let target = targetAtEvent(event);
+        if (target)
+            target.style.backgroundColor = "green";
+        event.preventDefault();
+    });
+
+    document.body.addEventListener("drop", event => {
+        let target = targetAtEvent(event);
+        if (target)
+            target.innerHTML = `<code>PASS</code>`;
+        event.preventDefault();
+    });
+</script>
+</html>

Modified: trunk/Tools/TestWebKitAPI/Tests/ios/DataInteractionTests.mm (221594 => 221595)


--- trunk/Tools/TestWebKitAPI/Tests/ios/DataInteractionTests.mm	2017-09-04 20:24:10 UTC (rev 221594)
+++ trunk/Tools/TestWebKitAPI/Tests/ios/DataInteractionTests.mm	2017-09-04 23:36:20 UTC (rev 221595)
@@ -1076,6 +1076,62 @@
     checkStringArraysAreEqual(@[@"dragstart", @"dragend"], [outputText componentsSeparatedByString:@" "]);
 }
 
+static void testDragAndDropOntoTargetElements(TestWKWebView *webView)
+{
+    auto simulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView]);
+    [simulator runFrom:CGPointMake(50, 50) to:CGPointMake(50, 250)];
+    EXPECT_WK_STREQ("rgb(0, 128, 0)", [webView stringByEvaluatingJavaScript:@"getComputedStyle(target1).backgroundColor"]);
+    EXPECT_WK_STREQ("PASS", [webView stringByEvaluatingJavaScript:@"target1.textContent"]);
+
+    [simulator runFrom:CGPointMake(50, 50) to:CGPointMake(250, 50)];
+    EXPECT_WK_STREQ("rgb(0, 128, 0)", [webView stringByEvaluatingJavaScript:@"getComputedStyle(target2).backgroundColor"]);
+    EXPECT_WK_STREQ("PASS", [webView stringByEvaluatingJavaScript:@"target2.textContent"]);
+
+    [simulator runFrom:CGPointMake(50, 50) to:CGPointMake(250, 250)];
+    EXPECT_WK_STREQ("rgb(0, 128, 0)", [webView stringByEvaluatingJavaScript:@"getComputedStyle(target3).backgroundColor"]);
+    EXPECT_WK_STREQ("PASS", [webView stringByEvaluatingJavaScript:@"target3.textContent"]);
+}
+
+TEST(DataInteractionTests, DragEventClientCoordinatesBasic)
+{
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    [webView synchronouslyLoadTestPageNamed:@"drop-targets"];
+
+    testDragAndDropOntoTargetElements(webView.get());
+}
+
+TEST(DataInteractionTests, DragEventClientCoordinatesWithScrollOffset)
+{
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    [webView synchronouslyLoadTestPageNamed:@"drop-targets"];
+    [webView stringByEvaluatingJavaScript:@"document.body.style.margin = '1000px 0'"];
+    [webView stringByEvaluatingJavaScript:@"document.scrollingElement.scrollTop = 1000"];
+    [webView waitForNextPresentationUpdate];
+
+    testDragAndDropOntoTargetElements(webView.get());
+}
+
+TEST(DataInteractionTests, DragEventPageCoordinatesBasic)
+{
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    [webView synchronouslyLoadTestPageNamed:@"drop-targets"];
+    [webView stringByEvaluatingJavaScript:@"window.usePageCoordinates = true"];
+
+    testDragAndDropOntoTargetElements(webView.get());
+}
+
+TEST(DataInteractionTests, DragEventPageCoordinatesWithScrollOffset)
+{
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    [webView synchronouslyLoadTestPageNamed:@"drop-targets"];
+    [webView stringByEvaluatingJavaScript:@"document.body.style.margin = '1000px 0'"];
+    [webView stringByEvaluatingJavaScript:@"document.scrollingElement.scrollTop = 1000"];
+    [webView stringByEvaluatingJavaScript:@"window.usePageCoordinates = true"];
+    [webView waitForNextPresentationUpdate];
+
+    testDragAndDropOntoTargetElements(webView.get());
+}
+
 TEST(DataInteractionTests, DoNotCrashWhenSelectionIsClearedInDragStart)
 {
     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);

Modified: trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.mm (221594 => 221595)


--- trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.mm	2017-09-04 20:24:10 UTC (rev 221594)
+++ trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.mm	2017-09-04 23:36:20 UTC (rev 221595)
@@ -246,6 +246,8 @@
         evalResult = [[NSString alloc] initWithFormat:@"%@", result];
         isWaitingForJavaScript = true;
         EXPECT_TRUE(!error);
+        if (error)
+            NSLog(@"Encountered error: %@ while evaluating script: %@", error, script);
     }];
 
     TestWebKitAPI::Util::run(&isWaitingForJavaScript);
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to