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);