[iOS DnD] Refactor drag and drop logic in WKContentView in preparation for supporting...
authorwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 4 Sep 2017 23:36:20 +0000 (23:36 +0000)
committerwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 4 Sep 2017 23:36:20 +0000 (23:36 +0000)
commit178f68841287086a0725c682239c07b509971399
tree46ec7e7c89af7d8529bf8cada16a7dbfb153ca2f
parent5d6defcbb9cd5fab2fb17c7da02872e4fb3fc72c
[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.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@221595 268f45cc-cd09-0410-ab3c-d52691b4dbfc
17 files changed:
Source/WebCore/ChangeLog
Source/WebCore/platform/ios/AbstractPasteboard.h
Source/WebCore/platform/ios/PlatformPasteboardIOS.mm
Source/WebCore/platform/ios/WebItemProviderPasteboard.h
Source/WebCore/platform/ios/WebItemProviderPasteboard.mm
Source/WebKit/ChangeLog
Source/WebKit/Platform/spi/ios/UIKitSPI.h
Source/WebKit/UIProcess/ios/DragDropInteractionState.h [new file with mode: 0644]
Source/WebKit/UIProcess/ios/DragDropInteractionState.mm [new file with mode: 0644]
Source/WebKit/UIProcess/ios/WKContentViewInteraction.h
Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm
Source/WebKit/WebKit.xcodeproj/project.pbxproj
Tools/ChangeLog
Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
Tools/TestWebKitAPI/Tests/WebKitCocoa/drop-targets.html [new file with mode: 0644]
Tools/TestWebKitAPI/Tests/ios/DataInteractionTests.mm
Tools/TestWebKitAPI/cocoa/TestWKWebView.mm