https://bugs.webkit.org/show_bug.cgi?id=174219
<rdar://problem/
32083177>
Reviewed by Ryosuke Niwa.
Source/WebCore:
Currently, in DragController.cpp, defaultOperationForDrag maps a drag source operation mask of
DragOperationGeneric to DragOperationMove across all platforms. However, on iOS, where cross-app drag moves do
not trigger a drop, this means drop handlers won't fire unless the dropEffect is explicitly set to copy.
To fix this, we introduce DragController::platformGenericDragOperation(), which returns DragOperationCopy on iOS
and DragOperationMove (the existing behavior) elsewhere. defaultOperationForDrag then maps a drag source
operation mask of DragOperationGeneric to platformGenericDragOperation().
Tests: DataInteractionTests.ExternalSourceHTMLToUploadArea
DataInteractionTests.ExternalSourceImageAndHTMLToUploadArea
DataInteractionTests.ExternalSourceMoveOperationNotAllowed
* page/DragController.cpp:
(WebCore::DragController::platformGenericDragOperation):
(WebCore::defaultOperationForDrag):
* page/DragController.h:
* page/mac/DragControllerMac.mm:
(WebCore::DragController::platformGenericDragOperation):
Source/WebKit2:
Tweak some testing SPI to return a drop operation flag instead of whether or not the drop operation was not
UIDropOperationCancel.
* UIProcess/API/Cocoa/WKWebView.mm:
(-[WKWebView _simulateDataInteractionUpdated:]):
* UIProcess/API/Cocoa/WKWebViewPrivate.h:
* UIProcess/ios/WKContentViewInteraction.h:
* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView _simulateDataInteractionUpdated:]):
Tools:
Add plumbing and support to mock the value of -allowsMoveOperation on the simulated UIDragDropSession objects.
Setting the DataInteractionSimulator's shouldAllowMoveOperation property to NO simulates a drag operation coming
in from another app out-of-process, for which move operations won't cause a drop to be performed in the first
place.
Also tweaks 2 existing unit tests regarding file uploads via JavaScript to simulate items coming in from a
different application, and adds a new test to check that if a drop area specifically requests a MOVE operation,
no action is taken when dropping.
* TestWebKitAPI/Tests/WebKit2Cocoa/file-uploading.html:
* TestWebKitAPI/Tests/ios/DataInteractionTests.mm:
(TestWebKitAPI::TEST):
* TestWebKitAPI/ios/DataInteractionSimulator.h:
* TestWebKitAPI/ios/DataInteractionSimulator.mm:
(-[MockDragDropSession initWithItems:location:window:allowMove:]):
(-[MockDragDropSession allowsMoveOperation]):
(-[MockDataOperationSession initWithProviders:location:window:allowMove:]):
(-[MockDataInteractionSession initWithWindow:allowMove:]):
(-[DataInteractionSimulator initWithWebView:]):
(-[DataInteractionSimulator runFrom:to:]):
(-[DataInteractionSimulator _advanceProgress]):
(-[MockDragDropSession initWithItems:location:window:]): Deleted.
(-[MockDataOperationSession initWithProviders:location:window:]): Deleted.
(-[MockDataInteractionSession initWithWindow:]): Deleted.
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@219271
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2017-07-07 Wenson Hsieh <wenson_hsieh@apple.com>
+
+ [iOS DnD] For cross-app drags, 'drop' event handlers are never invoked if dataTransfer.dropEffect is not set while dragging
+ https://bugs.webkit.org/show_bug.cgi?id=174219
+ <rdar://problem/32083177>
+
+ Reviewed by Ryosuke Niwa.
+
+ Currently, in DragController.cpp, defaultOperationForDrag maps a drag source operation mask of
+ DragOperationGeneric to DragOperationMove across all platforms. However, on iOS, where cross-app drag moves do
+ not trigger a drop, this means drop handlers won't fire unless the dropEffect is explicitly set to copy.
+
+ To fix this, we introduce DragController::platformGenericDragOperation(), which returns DragOperationCopy on iOS
+ and DragOperationMove (the existing behavior) elsewhere. defaultOperationForDrag then maps a drag source
+ operation mask of DragOperationGeneric to platformGenericDragOperation().
+
+ Tests: DataInteractionTests.ExternalSourceHTMLToUploadArea
+ DataInteractionTests.ExternalSourceImageAndHTMLToUploadArea
+ DataInteractionTests.ExternalSourceMoveOperationNotAllowed
+
+ * page/DragController.cpp:
+ (WebCore::DragController::platformGenericDragOperation):
+ (WebCore::defaultOperationForDrag):
+ * page/DragController.h:
+ * page/mac/DragControllerMac.mm:
+ (WebCore::DragController::platformGenericDragOperation):
+
2017-07-07 Devin Rousso <drousso@apple.com>
Web Inspector: Show all elements currently using a given CSS Canvas
return nullptr;
}
+#if !PLATFORM(IOS)
+
+DragOperation DragController::platformGenericDragOperation()
+{
+ return DragOperationMove;
+}
+
+#endif
+
bool DragController::dragIsMove(FrameSelection& selection, const DragData& dragData)
{
const VisibleSelection& visibleSelection = selection.selection();
return DragOperationCopy;
if (srcOpMask == DragOperationNone)
return DragOperationNone;
- if (srcOpMask & DragOperationMove || srcOpMask & DragOperationGeneric)
+ if (srcOpMask & DragOperationMove)
return DragOperationMove;
+ if (srcOpMask & DragOperationGeneric)
+ return DragController::platformGenericDragOperation();
if (srcOpMask & DragOperationCopy)
return DragOperationCopy;
if (srcOpMask & DragOperationLink)
~DragController();
static std::unique_ptr<DragController> create(Page&, DragClient&);
+ static DragOperation platformGenericDragOperation();
DragClient& client() const { return m_client; }
#if ENABLE(DATA_INTERACTION)
+DragOperation DragController::platformGenericDragOperation()
+{
+ // On iOS, UIKit skips the -performDrop invocation altogether if MOVE is forbidden.
+ // Thus, if MOVE is not allowed in the drag source operation mask, fall back to only other allowable action, COPY.
+ return DragOperationCopy;
+}
+
void DragController::updateSupportedTypeIdentifiersForDragHandlingMethod(DragHandlingMethod dragHandlingMethod, const DragData& dragData) const
{
Vector<String> supportedTypes;
+2017-07-07 Wenson Hsieh <wenson_hsieh@apple.com>
+
+ [iOS DnD] For cross-app drags, 'drop' event handlers are never invoked if dataTransfer.dropEffect is not set while dragging
+ https://bugs.webkit.org/show_bug.cgi?id=174219
+ <rdar://problem/32083177>
+
+ Reviewed by Ryosuke Niwa.
+
+ Tweak some testing SPI to return a drop operation flag instead of whether or not the drop operation was not
+ UIDropOperationCancel.
+
+ * UIProcess/API/Cocoa/WKWebView.mm:
+ (-[WKWebView _simulateDataInteractionUpdated:]):
+ * UIProcess/API/Cocoa/WKWebViewPrivate.h:
+ * UIProcess/ios/WKContentViewInteraction.h:
+ * UIProcess/ios/WKContentViewInteraction.mm:
+ (-[WKContentView _simulateDataInteractionUpdated:]):
+
2017-07-07 Commit Queue <commit-queue@webkit.org>
Unreviewed, rolling out r219238, r219239, and r219241.
#endif
}
-- (BOOL)_simulateDataInteractionUpdated:(id)info
+- (NSUInteger)_simulateDataInteractionUpdated:(id)info
{
#if ENABLE(DATA_INTERACTION)
return [_contentView _simulateDataInteractionUpdated:info];
#else
- return NO;
+ return 0;
#endif
}
- (NSDictionary *)_propertiesOfLayerWithID:(unsigned long long)layerID WK_API_AVAILABLE(ios(WK_IOS_TBA));
- (void)_simulateDataInteractionEntered:(id)info WK_API_AVAILABLE(ios(WK_IOS_TBA));
-- (BOOL)_simulateDataInteractionUpdated:(id)info WK_API_AVAILABLE(ios(WK_IOS_TBA));
+- (NSUInteger)_simulateDataInteractionUpdated:(id)info WK_API_AVAILABLE(ios(WK_IOS_TBA));
- (void)_simulateDataInteractionPerformOperation:(id)info WK_API_AVAILABLE(ios(WK_IOS_TBA));
- (void)_simulateDataInteractionEnded:(id)info WK_API_AVAILABLE(ios(WK_IOS_TBA));
- (void)_simulateDataInteractionSessionDidEnd:(id)session WK_API_AVAILABLE(ios(WK_IOS_TBA));
- (void)_didChangeDataInteractionCaretRect:(CGRect)previousRect currentRect:(CGRect)rect;
- (void)_simulateDataInteractionEntered:(id)info;
-- (BOOL)_simulateDataInteractionUpdated:(id)info;
+- (NSUInteger)_simulateDataInteractionUpdated:(id)info;
- (void)_simulateDataInteractionPerformOperation:(id)info;
- (void)_simulateDataInteractionEnded:(id)info;
- (void)_simulateDataInteractionSessionDidEnd:(id)session;
[self dropInteraction:_dataOperation.get() sessionDidEnter:session];
}
-- (BOOL)_simulateDataInteractionUpdated:(id)session
+- (NSUInteger)_simulateDataInteractionUpdated:(id)session
{
- return [self dropInteraction:_dataOperation.get() sessionDidUpdate:session].operation != UIDropOperationCancel;
+ return [self dropInteraction:_dataOperation.get() sessionDidUpdate:session].operation;
}
- (void)_simulateDataInteractionEnded:(id)session
+2017-07-07 Wenson Hsieh <wenson_hsieh@apple.com>
+
+ [iOS DnD] For cross-app drags, 'drop' event handlers are never invoked if dataTransfer.dropEffect is not set while dragging
+ https://bugs.webkit.org/show_bug.cgi?id=174219
+ <rdar://problem/32083177>
+
+ Reviewed by Ryosuke Niwa.
+
+ Add plumbing and support to mock the value of -allowsMoveOperation on the simulated UIDragDropSession objects.
+ Setting the DataInteractionSimulator's shouldAllowMoveOperation property to NO simulates a drag operation coming
+ in from another app out-of-process, for which move operations won't cause a drop to be performed in the first
+ place.
+
+ Also tweaks 2 existing unit tests regarding file uploads via JavaScript to simulate items coming in from a
+ different application, and adds a new test to check that if a drop area specifically requests a MOVE operation,
+ no action is taken when dropping.
+
+ * TestWebKitAPI/Tests/WebKit2Cocoa/file-uploading.html:
+ * TestWebKitAPI/Tests/ios/DataInteractionTests.mm:
+ (TestWebKitAPI::TEST):
+ * TestWebKitAPI/ios/DataInteractionSimulator.h:
+ * TestWebKitAPI/ios/DataInteractionSimulator.mm:
+ (-[MockDragDropSession initWithItems:location:window:allowMove:]):
+ (-[MockDragDropSession allowsMoveOperation]):
+ (-[MockDataOperationSession initWithProviders:location:window:allowMove:]):
+ (-[MockDataInteractionSession initWithWindow:allowMove:]):
+ (-[DataInteractionSimulator initWithWebView:]):
+ (-[DataInteractionSimulator runFrom:to:]):
+ (-[DataInteractionSimulator _advanceProgress]):
+ (-[MockDragDropSession initWithItems:location:window:]): Deleted.
+ (-[MockDataOperationSession initWithProviders:location:window:]): Deleted.
+ (-[MockDataInteractionSession initWithWindow:]): Deleted.
+
2017-07-07 Commit Queue <commit-queue@webkit.org>
Unreviewed, rolling out r219238, r219239, and r219241.
sendUploadedFileTypesToApp(input.files);
});
- upload.addEventListener("dragenter", event => event.preventDefault());
- upload.addEventListener("dragover", event => event.preventDefault());
+ upload.addEventListener("dragenter", event => {
+ event.preventDefault();
+ if (upload.dropEffect)
+ event.dataTransfer.dropEffect = upload.dropEffect;
+ });
+
+ upload.addEventListener("dragover", event => {
+ event.preventDefault();
+ if (upload.dropEffect)
+ event.dataTransfer.dropEffect = upload.dropEffect;
+ });
+
upload.addEventListener("drop", event => {
sendUploadedFileTypesToApp(event.dataTransfer.files);
event.preventDefault();
TEST(DataInteractionTests, ExternalSourceHTMLToUploadArea)
{
- RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+ auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
[webView synchronouslyLoadTestPageNamed:@"file-uploading"];
- RetainPtr<UIItemProvider> simulatedHTMLItemProvider = adoptNS([[UIItemProvider alloc] init]);
+ auto simulatedHTMLItemProvider = adoptNS([[UIItemProvider alloc] init]);
NSData *htmlData = [@"<body contenteditable></body>" dataUsingEncoding:NSUTF8StringEncoding];
[simulatedHTMLItemProvider registerDataRepresentationForTypeIdentifier:(NSString *)kUTTypeHTML withData:htmlData];
- RetainPtr<DataInteractionSimulator> dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
+ auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
+ [dataInteractionSimulator setShouldAllowMoveOperation:NO];
[dataInteractionSimulator setExternalItemProviders:@[ simulatedHTMLItemProvider.get() ]];
[dataInteractionSimulator runFrom:CGPointMake(200, 300) to:CGPointMake(100, 300)];
EXPECT_WK_STREQ("text/html", outputValue.UTF8String);
}
+TEST(DataInteractionTests, ExternalSourceMoveOperationNotAllowed)
+{
+ auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+ [webView synchronouslyLoadTestPageNamed:@"file-uploading"];
+ [webView stringByEvaluatingJavaScript:@"upload.dropEffect = 'move'"];
+
+ auto simulatedHTMLItemProvider = adoptNS([[UIItemProvider alloc] init]);
+ NSData *htmlData = [@"<body contenteditable></body>" dataUsingEncoding:NSUTF8StringEncoding];
+ [simulatedHTMLItemProvider registerDataRepresentationForTypeIdentifier:(NSString *)kUTTypeHTML withData:htmlData];
+
+ auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
+ [dataInteractionSimulator setShouldAllowMoveOperation:NO];
+ [dataInteractionSimulator setExternalItemProviders:@[ simulatedHTMLItemProvider.get() ]];
+ [dataInteractionSimulator runFrom:CGPointMake(200, 300) to:CGPointMake(100, 300)];
+
+ EXPECT_WK_STREQ("", [webView stringByEvaluatingJavaScript:@"output.value"]);
+}
+
TEST(DataInteractionTests, ExternalSourceZIPArchiveAndURLToSingleFileInput)
{
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
TEST(DataInteractionTests, ExternalSourceImageAndHTMLToUploadArea)
{
- RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+ auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
[webView synchronouslyLoadTestPageNamed:@"file-uploading"];
- RetainPtr<UIItemProvider> simulatedImageItemProvider = adoptNS([[UIItemProvider alloc] init]);
+ auto simulatedImageItemProvider = adoptNS([[UIItemProvider alloc] init]);
NSData *imageData = UIImageJPEGRepresentation(testIconImage(), 0.5);
[simulatedImageItemProvider registerDataRepresentationForTypeIdentifier:(NSString *)kUTTypeJPEG withData:imageData];
- RetainPtr<UIItemProvider> firstSimulatedHTMLItemProvider = adoptNS([[UIItemProvider alloc] init]);
+ auto firstSimulatedHTMLItemProvider = adoptNS([[UIItemProvider alloc] init]);
NSData *firstHTMLData = [@"<body contenteditable></body>" dataUsingEncoding:NSUTF8StringEncoding];
[firstSimulatedHTMLItemProvider registerDataRepresentationForTypeIdentifier:(NSString *)kUTTypeHTML withData:firstHTMLData];
- RetainPtr<UIItemProvider> secondSimulatedHTMLItemProvider = adoptNS([[UIItemProvider alloc] init]);
+ auto secondSimulatedHTMLItemProvider = adoptNS([[UIItemProvider alloc] init]);
NSData *secondHTMLData = [@"<html><body>hello world</body></html>" dataUsingEncoding:NSUTF8StringEncoding];
[secondSimulatedHTMLItemProvider registerDataRepresentationForTypeIdentifier:(NSString *)kUTTypeHTML withData:secondHTMLData];
- RetainPtr<DataInteractionSimulator> dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
+ auto dataInteractionSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
+ [dataInteractionSimulator setShouldAllowMoveOperation:NO];
[dataInteractionSimulator setExternalItemProviders:@[ simulatedImageItemProvider.get(), firstSimulatedHTMLItemProvider.get(), secondSimulatedHTMLItemProvider.get() ]];
[dataInteractionSimulator runFrom:CGPointMake(200, 300) to:CGPointMake(100, 300)];
@property (nonatomic) BOOL allowsFocusToStartInputSession;
@property (nonatomic) BOOL shouldEnsureUIApplication;
+@property (nonatomic) BOOL shouldAllowMoveOperation;
@property (nonatomic) BlockPtr<BOOL(_WKActivatedElementInfo *)> showCustomActionSheetBlock;
@property (nonatomic) BlockPtr<NSArray *(UIItemProvider *, NSArray *, NSDictionary *)> convertItemProvidersBlock;
@property (nonatomic) BlockPtr<NSArray *(id <UIDropSession>)> overridePerformDropBlock;
RetainPtr<UIWindow> _window;
}
@property (nonatomic) CGPoint mockLocationInWindow;
+@property (nonatomic) BOOL allowMove;
@end
@implementation MockDragDropSession
-- (instancetype)initWithItems:(NSArray <UIDragItem *>*)items location:(CGPoint)locationInWindow window:(UIWindow *)window
+- (instancetype)initWithItems:(NSArray <UIDragItem *>*)items location:(CGPoint)locationInWindow window:(UIWindow *)window allowMove:(BOOL)allowMove
{
if (self = [super init]) {
_mockItems = items;
_mockLocationInWindow = locationInWindow;
_window = window;
+ _allowMove = allowMove;
}
return self;
}
- (BOOL)allowsMoveOperation
{
- return YES;
+ return _allowMove;
}
- (BOOL)isRestrictedToDraggingApplication
@implementation MockDataOperationSession
-- (instancetype)initWithProviders:(NSArray<UIItemProvider *> *)providers location:(CGPoint)locationInWindow window:(UIWindow *)window
+- (instancetype)initWithProviders:(NSArray<UIItemProvider *> *)providers location:(CGPoint)locationInWindow window:(UIWindow *)window allowMove:(BOOL)allowMove
{
auto items = adoptNS([[NSMutableArray alloc] init]);
for (UIItemProvider *itemProvider in providers)
[items addObject:[[[UIDragItem alloc] initWithItemProvider:itemProvider] autorelease]];
- return [super initWithItems:items.get() location:locationInWindow window:window];
+ return [super initWithItems:items.get() location:locationInWindow window:window allowMove:allowMove];
}
- (UIDraggingSession *)session
@implementation MockDataInteractionSession
-- (instancetype)initWithWindow:(UIWindow *)window
+- (instancetype)initWithWindow:(UIWindow *)window allowMove:(BOOL)allowMove
{
- return [super initWithItems:@[ ] location:CGPointZero window:window];
+ return [super initWithItems:@[ ] location:CGPointZero window:window allowMove:allowMove];
}
- (NSUInteger)localOperationMask
if (self = [super init]) {
_webView = webView;
_shouldEnsureUIApplication = NO;
+ _shouldAllowMoveOperation = YES;
_isDoneWaitingForInputSession = true;
[_webView setUIDelegate:self];
[_webView _setInputDelegate:self];
_endLocation = endLocation;
if (self.externalItemProviders.count) {
- _dataOperationSession = adoptNS([[MockDataOperationSession alloc] initWithProviders:self.externalItemProviders location:_startLocation window:[_webView window]]);
+ _dataOperationSession = adoptNS([[MockDataOperationSession alloc] initWithProviders:self.externalItemProviders location:_startLocation window:[_webView window] allowMove:self.shouldAllowMoveOperation]);
_phase = DataInteractionBegan;
[self _advanceProgress];
} else {
- _dataInteractionSession = adoptNS([[MockDataInteractionSession alloc] initWithWindow:[_webView window]]);
+ _dataInteractionSession = adoptNS([[MockDataInteractionSession alloc] initWithWindow:[_webView window] allowMove:self.shouldAllowMoveOperation]);
[_dataInteractionSession setMockLocationInWindow:_startLocation];
[_webView _simulatePrepareForDataInteractionSession:_dataInteractionSession.get() completion:^() {
DataInteractionSimulator *weakSelf = strongSelf.get();
for (UIDragItem *item in items)
[itemProviders addObject:item.itemProvider];
- _dataOperationSession = adoptNS([[MockDataOperationSession alloc] initWithProviders:itemProviders location:self._currentLocation window:[_webView window]]);
+ _dataOperationSession = adoptNS([[MockDataOperationSession alloc] initWithProviders:itemProviders location:self._currentLocation window:[_webView window] allowMove:self.shouldAllowMoveOperation]);
[_dataInteractionSession setItems:items];
_sourceItemProviders = itemProviders;
if (self.showCustomActionSheetBlock) {
[_webView _simulateDataInteractionEntered:_dataOperationSession.get()];
_phase = DataInteractionEntered;
break;
- case DataInteractionEntered:
- _shouldPerformOperation = [_webView _simulateDataInteractionUpdated:_dataOperationSession.get()];
+ case DataInteractionEntered: {
+ auto operation = static_cast<UIDropOperation>([_webView _simulateDataInteractionUpdated:_dataOperationSession.get()]);
+ _shouldPerformOperation = operation == UIDropOperationCopy || ([_dataOperationSession allowsMoveOperation] && operation != UIDropOperationCancel);
break;
+ }
default:
break;
}