[Attachment Support] Support dragging attachment elements out as files on macOS
[WebKit-https.git] / Tools / TestWebKitAPI / mac / DragAndDropSimulatorMac.mm
index 8c1199d..efd70bd 100644 (file)
@@ -37,7 +37,9 @@
 @class DragAndDropTestWKWebView;
 
 @interface DragAndDropSimulator ()
+- (void)beginDraggingSessionInWebView:(DragAndDropTestWKWebView *)webView withItems:(NSArray<NSDraggingItem *> *)items source:(id<NSDraggingSource>)source;
 - (void)performDragInWebView:(DragAndDropTestWKWebView *)webView atLocation:(NSPoint)viewLocation withImage:(NSImage *)image pasteboard:(NSPasteboard *)pasteboard source:(id)source;
+@property (nonatomic, readonly) NSDraggingSession *draggingSession;
 @end
 
 @interface DragAndDropTestWKWebView : TestWKWebView
     [_dragAndDropSimulator performDragInWebView:self atLocation:viewLocation withImage:image pasteboard:pboard source:sourceObj];
 }
 
+- (NSDraggingSession *)beginDraggingSessionWithItems:(NSArray<NSDraggingItem *> *)items event:(NSEvent *)event source:(id<NSDraggingSource>)source
+{
+    [_dragAndDropSimulator beginDraggingSessionInWebView:self withItems:items source:source];
+    return [_dragAndDropSimulator draggingSession];
+}
+
 - (void)waitForPendingMouseEvents
 {
     __block bool doneProcessMouseEvents = false;
@@ -85,10 +93,16 @@ static NSImage *defaultExternalDragImage()
     RetainPtr<NSPasteboard> _externalDragPasteboard;
     RetainPtr<NSImage> _externalDragImage;
     RetainPtr<NSArray<NSURL *>> _externalPromisedFiles;
+    RetainPtr<NSMutableArray<_WKAttachment *>> _insertedAttachments;
+    RetainPtr<NSMutableArray<_WKAttachment *>> _removedAttachments;
+    RetainPtr<NSMutableArray<NSURL *>> _filePromiseDestinationURLs;
+    RetainPtr<NSDraggingSession> _draggingSession;
+    RetainPtr<NSMutableArray<NSFilePromiseProvider *>> _filePromiseProviders;
     BlockPtr<void()> _willEndDraggingHandler;
     NSPoint _startLocationInWindow;
     NSPoint _endLocationInWindow;
     double _progress;
+    bool _doneWaitingForDraggingSession;
 }
 
 @synthesize currentDragOperation=_currentDragOperation;
@@ -103,11 +117,20 @@ static NSImage *defaultExternalDragImage()
 {
     if (self = [super init]) {
         _webView = adoptNS([[DragAndDropTestWKWebView alloc] initWithFrame:frame configuration:configuration ?: [[[WKWebViewConfiguration alloc] init] autorelease] simulator:self]);
+        _filePromiseDestinationURLs = adoptNS([NSMutableArray new]);
         [_webView setUIDelegate:self];
     }
     return self;
 }
 
+- (void)dealloc
+{
+    for (NSURL *url in _filePromiseDestinationURLs.get())
+        [[NSFileManager defaultManager] removeItemAtURL:url error:nil];
+
+    [super dealloc];
+}
+
 - (NSPoint)flipAboutXAxisInHostWindow:(NSPoint)point
 {
     return { point.x, NSHeight([[_webView hostWindow] frame]) - point.y };
@@ -129,11 +152,16 @@ static NSImage *defaultExternalDragImage()
 
 - (void)runFrom:(CGPoint)flippedStartLocation to:(CGPoint)flippedEndLocation
 {
+    _insertedAttachments = adoptNS([NSMutableArray new]);
+    _removedAttachments = adoptNS([NSMutableArray new]);
+    _doneWaitingForDraggingSession = true;
     _startLocationInWindow = [self flipAboutXAxisInHostWindow:flippedStartLocation];
     _endLocationInWindow = [self flipAboutXAxisInHostWindow:flippedEndLocation];
     _currentDragOperation = NSDragOperationNone;
     _draggingInfo = nil;
+    _draggingSession = nil;
     _progress = 0;
+    _filePromiseProviders = adoptNS([NSMutableArray new]);
 
     if (NSPasteboard *pasteboard = self.externalDragPasteboard) {
         NSPoint startLocationInView = [_webView convertPoint:_startLocationInWindow fromView:nil];
@@ -154,20 +182,76 @@ static NSImage *defaultExternalDragImage()
     [_webView mouseDragToPoint:[self locationInViewForCurrentProgress]];
     [_webView waitForPendingMouseEvents];
 
+    TestWebKitAPI::Util::run(&_doneWaitingForDraggingSession);
+
     [_webView mouseUpAtPoint:_endLocationInWindow];
     [_webView waitForPendingMouseEvents];
 }
 
+- (void)beginDraggingSessionInWebView:(DragAndDropTestWKWebView *)webView withItems:(NSArray<NSDraggingItem *> *)items source:(id<NSDraggingSource>)source
+{
+    NSMutableArray *pasteboardObjects = [NSMutableArray arrayWithCapacity:items.count];
+    NSMutableArray<NSString *> *promisedFileTypes = [NSMutableArray array];
+    for (NSDraggingItem *item in items) {
+        id pasteboardObject = item.item;
+        [pasteboardObjects addObject:pasteboardObject];
+        if ([pasteboardObject isKindOfClass:[NSFilePromiseProvider class]]) {
+            [_filePromiseProviders addObject:pasteboardObject];
+            [promisedFileTypes addObject:[(NSFilePromiseProvider *)pasteboardObject fileType]];
+        }
+    }
+
+    NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
+    [pasteboard clearContents];
+    [pasteboard writeObjects:pasteboardObjects];
+    if (promisedFileTypes.count) {
+        // Match AppKit behavior by writing legacy file promise types to the pasteboard as well.
+        [pasteboard setPropertyList:promisedFileTypes forType:NSFilesPromisePboardType];
+        [pasteboard addTypes:@[@"NSPromiseContentsPboardType", (NSString *)kPasteboardTypeFileURLPromise] owner:nil];
+    }
+
+    _draggingSession = adoptNS([[NSDraggingSession alloc] init]);
+    _doneWaitingForDraggingSession = false;
+    _initialDragImageLocationInView = items[0].draggingFrame.origin;
+    id dragImageContents = items[0].imageComponents.firstObject.contents;
+    [self initializeDraggingInfo:pasteboard dragImage:[dragImageContents isKindOfClass:[NSImage class]] ? dragImageContents : nil source:source];
+
+    _currentDragOperation = [_webView draggingEntered:_draggingInfo.get()];
+    [_webView waitForNextPresentationUpdate];
+    [self performSelector:@selector(continueDragSession) withObject:nil afterDelay:0];
+}
+
+- (void)continueDragSession
+{
+    _progress = std::min<double>(1, _progress + dragUpdateProgressIncrement);
+
+    if (_progress < 1) {
+        [_draggingInfo setDraggingLocation:[self locationInViewForCurrentProgress]];
+        _currentDragOperation = [_webView draggingUpdated:_draggingInfo.get()];
+        [_webView waitForNextPresentationUpdate];
+        [self performSelector:@selector(continueDragSession) withObject:nil afterDelay:0];
+        return;
+    }
+
+    [_draggingInfo setDraggingLocation:_endLocationInWindow];
+
+    if (_willEndDraggingHandler)
+        _willEndDraggingHandler();
+
+    if (_currentDragOperation != NSDragOperationNone && [_webView prepareForDragOperation:_draggingInfo.get()])
+        [_webView performDragOperation:_draggingInfo.get()];
+    else if (_currentDragOperation == NSDragOperationNone)
+        [_webView draggingExited:_draggingInfo.get()];
+    [_webView waitForNextPresentationUpdate];
+    [(id <NSDraggingSource>)_webView.get() draggingSession:_draggingSession.get() endedAtPoint:_endLocationInWindow operation:_currentDragOperation];
+
+    _doneWaitingForDraggingSession = true;
+}
+
 - (void)performDragInWebView:(DragAndDropTestWKWebView *)webView atLocation:(NSPoint)viewLocation withImage:(NSImage *)image pasteboard:(NSPasteboard *)pasteboard source:(id)source
 {
     _initialDragImageLocationInView = viewLocation;
-    _draggingInfo = adoptNS([[TestDraggingInfo alloc] initWithDragAndDropSimulator:self]);
-    [_draggingInfo setDraggedImage:image];
-    [_draggingInfo setDraggingPasteboard:pasteboard];
-    [_draggingInfo setDraggingSource:source];
-    [_draggingInfo setDraggingLocation:[self locationInViewForCurrentProgress]];
-    [_draggingInfo setDraggingSourceOperationMask:NSDragOperationEvery];
-    [_draggingInfo setNumberOfValidItemsForDrop:pasteboard.pasteboardItems.count];
+    [self initializeDraggingInfo:pasteboard dragImage:image source:source];
 
     _currentDragOperation = [_webView draggingEntered:_draggingInfo.get()];
     [_webView waitForNextPresentationUpdate];
@@ -196,14 +280,25 @@ static NSImage *defaultExternalDragImage()
     }
 }
 
+- (void)initializeDraggingInfo:(NSPasteboard *)pasteboard dragImage:(NSImage *)image source:(id)source
+{
+    _draggingInfo = adoptNS([[TestDraggingInfo alloc] initWithDragAndDropSimulator:self]);
+    [_draggingInfo setDraggedImage:image];
+    [_draggingInfo setDraggingPasteboard:pasteboard];
+    [_draggingInfo setDraggingSource:source];
+    [_draggingInfo setDraggingLocation:[self locationInViewForCurrentProgress]];
+    [_draggingInfo setDraggingSourceOperationMask:NSDragOperationEvery];
+    [_draggingInfo setNumberOfValidItemsForDrop:pasteboard.pasteboardItems.count];
+}
+
 - (NSArray<_WKAttachment *> *)insertedAttachments
 {
-    return @[ ];
+    return _insertedAttachments.get();
 }
 
 - (NSArray<_WKAttachment *> *)removedAttachments
 {
-    return @[ ];
+    return _removedAttachments.get();
 }
 
 - (TestWKWebView *)webView
@@ -231,6 +326,11 @@ static NSImage *defaultExternalDragImage()
     return _externalDragImage.get();
 }
 
+- (NSDraggingSession *)draggingSession
+{
+    return _draggingSession.get();
+}
+
 - (id <NSDraggingInfo>)draggingInfo
 {
     return _draggingInfo.get();
@@ -308,6 +408,45 @@ static BOOL getFilePathsAndTypeIdentifiers(NSArray<NSURL *> *fileURLs, NSArray<N
     [_externalDragPasteboard setPropertyList:paths forType:NSFilenamesPboardType];
 }
 
+- (NSArray<NSURL *> *)receivePromisedFiles
+{
+    auto destinationURLs = adoptNS([NSMutableArray new]);
+    for (NSFilePromiseProvider *provider in _filePromiseProviders.get()) {
+        if (!provider.delegate)
+            continue;
+
+        int suffix = 1;
+        NSString *baseFileName = [provider.delegate filePromiseProvider:provider fileNameForType:provider.fileType];
+        NSString *uniqueFileName = baseFileName;
+        while ([[NSFileManager defaultManager] fileExistsAtPath:[NSTemporaryDirectory() stringByAppendingPathComponent:uniqueFileName]])
+            uniqueFileName = [NSString stringWithFormat:@"%@ %d", baseFileName, ++suffix];
+
+        NSURL *destinationURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:uniqueFileName]];
+        __block bool done = false;
+        [provider.delegate filePromiseProvider:provider writePromiseToURL:destinationURL completionHandler:^(NSError *) {
+            done = true;
+        }];
+        TestWebKitAPI::Util::run(&done);
+        [destinationURLs addObject:destinationURL];
+        [_filePromiseDestinationURLs addObject:destinationURL];
+    }
+    return destinationURLs.autorelease();
+}
+
+- (void)endDataTransfer
+{
+}
+
+- (void)_webView:(WKWebView *)webView didInsertAttachment:(_WKAttachment *)attachment withSource:(NSString *)source
+{
+    [_insertedAttachments addObject:attachment];
+}
+
+- (void)_webView:(WKWebView *)webView didRemoveAttachment:(_WKAttachment *)attachment
+{
+    [_removedAttachments addObject:attachment];
+}
+
 @end
 
 #endif // ENABLE(DRAG_SUPPORT) && PLATFORM(MAC) && WK_API_ENABLED