Skip to content

Commit 71bc35a

Browse files
sapohlspohlMozilla
authored andcommitted
Bug 1802163: Improve dragging of file URLs on macOS. r=mac-reviewers,mstange
This patch fixes dragging of images to Photos.app and Pages.app (possibly others, more testing necessary). It is also the first time that we support dragging of images into Apple's drag & drop test app at https://developer.apple.com/documentation/appkit/supporting-drag-and-drop-through-file-promises?language=objc, which resolves bug 1450419 without actually implementing NSFilePromiseProvider. NSFilePromiseProvider is not easily implemented since it expects to be the only data provider for a drag session, which means that we wouldn't be able to write out other pasteboard types to the pasteboard like we have traditionally done and that third-party apps are relying on. If the set of Gecko dragging transferables contains a transferable with flavor kFilePromiseMime, we will now create a file even if the pasteboard that's passed to -[NSPasteboardItemDataProvider pasteboard:item:provideDataForType:] does not come with a "paste location". Additionally, we will now write the path of the paste location into the pasteboard for type kUTTypeFileURL. In the past, we would only create a file for such transferables if the pasteboard provided the location to store the file, and we would not write a kUTTypeFileURL value - instead, we would write two empty strings for the types kPasteboardTypeFileURLPromise and kPasteboardTypeFilePromiseContent. We no longer write those empty strings. Differential Revision: https://phabricator.services.mozilla.com/D260021
1 parent 156001c commit 71bc35a

File tree

3 files changed

+75
-54
lines changed

3 files changed

+75
-54
lines changed

widget/cocoa/nsClipboard.mm

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,17 @@
748748
forKey:urlPromise];
749749
[pasteboardOutputDict setObject:[NSArray arrayWithObject:@""]
750750
forKey:urlPromiseContent];
751+
NSString* fileUTType =
752+
[UTIHelper stringFromPboardType:(NSString*)kUTTypeFileURL];
753+
// Ideally, we would make use of `NSFilePromiseProvider` for file
754+
// promises. However, we cannot easily support this since
755+
// `NSFilePromiseProvider` expects to be the only data provider for the
756+
// drag session. This means that no other data types could be written out
757+
// to the drag pasteboard simultaneously without major rework. We work
758+
// around this by setting an empty string here for `kUTTypeFileURL` as our
759+
// signal to create the file once the drag completes, replicating the file
760+
// promise behavior.
761+
[pasteboardOutputDict setObject:@"" forKey:fileUTType];
751762
} else if (flavorStr.EqualsLiteral(kURLMime)) {
752763
nsCOMPtr<nsISupports> genericURL;
753764
rv = aTransferable->GetTransferData(flavorStr.get(),

widget/cocoa/nsCocoaWindow.mm

Lines changed: 60 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ static void RollUpPopups(nsIRollupListener::AllowAnimations aAllowAnimations =
162162
}
163163

164164
extern nsIArray* gDraggedTransferables;
165+
extern bool gCreatedPromisedFile;
165166
ChildView* ChildViewMouseTracker::sLastMouseEventView = nil;
166167
NSEvent* ChildViewMouseTracker::sLastMouseMoveEvent = nil;
167168
NSWindow* ChildViewMouseTracker::sWindowUnderMouse = nil;
@@ -3706,8 +3707,6 @@ - (void)draggingSession:(NSDraggingSession*)aSession
37063707
MOZ_RELEASE_ASSERT(NS_IsMainThread());
37073708
#endif
37083709

3709-
gDraggedTransferables = nullptr;
3710-
37113710
NSEvent* currentEvent = [NSApp currentEvent];
37123711
gUserCancelledDrag = ([currentEvent type] == NSEventTypeKeyDown &&
37133712
[currentEvent keyCode] == kVK_Escape);
@@ -3801,20 +3800,20 @@ - (void)draggingSession:(NSDraggingSession*)aSession
38013800
NS_OBJC_END_TRY_IGNORE_BLOCK;
38023801
}
38033802

3804-
// Get the paste location from the low level pasteboard.
3805-
static CFTypeRefPtr<CFURLRef> GetPasteLocation(NSPasteboard* aPasteboard) {
3806-
PasteboardRef pboardRef = nullptr;
3807-
PasteboardCreate((CFStringRef)[aPasteboard name], &pboardRef);
3808-
if (!pboardRef) {
3809-
return nullptr;
3803+
static NSURL* GetPasteLocation() {
3804+
// First, attempt to get the paste location from the pasteboard.
3805+
NSPasteboard* pasteboard =
3806+
[NSPasteboard pasteboardWithName:NSPasteboardNameDrag];
3807+
NSString* pasteLocation =
3808+
[pasteboard stringForType:@"com.apple.pastelocation"];
3809+
if (pasteLocation) {
3810+
return [NSURL fileURLWithPath:pasteLocation];
38103811
}
3811-
3812-
auto pasteBoard = CFTypeRefPtr<PasteboardRef>::WrapUnderCreateRule(pboardRef);
3813-
PasteboardSynchronize(pasteBoard.get());
3814-
3815-
CFURLRef urlRef = nullptr;
3816-
PasteboardCopyPasteLocation(pasteBoard.get(), &urlRef);
3817-
return CFTypeRefPtr<CFURLRef>::WrapUnderCreateRule(urlRef);
3812+
// If no paste location was present on the pasteboard, fall back to a temp
3813+
// directory instead.
3814+
return [NSURL
3815+
fileURLWithPath:[NSTemporaryDirectory()
3816+
stringByAppendingPathComponent:@"mozDraggedFiles"]];
38183817
}
38193818

38203819
// NSPasteboardItemDataProvider
@@ -3862,47 +3861,20 @@ - (void)pasteboard:(NSPasteboard*)aPasteboard
38623861
stringFromPboardType:kPublicUrlPboardType]] ||
38633862
[curType isEqualToString:[UTIHelper stringFromPboardType:
38643863
kPublicUrlNamePboardType]] ||
3865-
[curType
3866-
isEqualToString:[UTIHelper
3867-
stringFromPboardType:(NSString*)
3868-
kUTTypeFileURL]]) {
3864+
([curType
3865+
isEqualToString:[UTIHelper
3866+
stringFromPboardType:(NSString*)
3867+
kUTTypeFileURL]] &&
3868+
![[pasteboardOutputDict valueForKey:curType] isEqualToString:@""])) {
38693869
[aPasteboard setString:[pasteboardOutputDict valueForKey:curType]
38703870
forType:curType];
38713871
} else if ([curType isEqualToString:[UTIHelper
38723872
stringFromPboardType:
3873-
kUrlsWithTitlesPboardType]]) {
3874-
[aPasteboard setPropertyList:[pasteboardOutputDict valueForKey:curType]
3875-
forType:curType];
3876-
} else if ([curType
3877-
isEqualToString:[UTIHelper stringFromPboardType:
3878-
NSPasteboardTypeHTML]]) {
3879-
[aPasteboard setString:(nsClipboard::WrapHtmlForSystemPasteboard(
3880-
[pasteboardOutputDict valueForKey:curType]))
3881-
forType:curType];
3882-
} else if ([curType
3883-
isEqualToString:[UTIHelper stringFromPboardType:
3884-
NSPasteboardTypeTIFF]] ||
3885-
[curType isEqualToString:[UTIHelper
3886-
stringFromPboardType:
3887-
kMozCustomTypesPboardType]]) {
3888-
[aPasteboard setData:[pasteboardOutputDict valueForKey:curType]
3889-
forType:curType];
3890-
} else if ([curType
3891-
isEqualToString:[UTIHelper stringFromPboardType:
3892-
kMozFileUrlsPboardType]]) {
3893-
[aPasteboard writeObjects:[pasteboardOutputDict valueForKey:curType]];
3894-
} else if ([curType
3895-
isEqualToString:
3896-
[UTIHelper
3897-
stringFromPboardType:
3898-
(NSString*)kPasteboardTypeFileURLPromise]]) {
3899-
CFTypeRefPtr<CFURLRef> url = GetPasteLocation(aPasteboard);
3900-
if (!url) {
3901-
continue;
3902-
}
3903-
3873+
(NSString*)kUTTypeFileURL]] &&
3874+
!gCreatedPromisedFile) {
3875+
NSURL* url = GetPasteLocation();
39043876
nsCOMPtr<nsILocalFileMac> macLocalFile;
3905-
if (NS_FAILED(NS_NewLocalFileWithCFURL(url.get(),
3877+
if (NS_FAILED(NS_NewLocalFileWithCFURL((__bridge CFURLRef)url,
39063878
getter_AddRefs(macLocalFile)))) {
39073879
NS_ERROR("failed NS_NewLocalFileWithCFURL");
39083880
continue;
@@ -3931,12 +3903,46 @@ - (void)pasteboard:(NSPasteboard*)aPasteboard
39313903
// Now request the kFilePromiseMime data, which will invoke the data
39323904
// provider. If successful, the file will have been created.
39333905
nsCOMPtr<nsISupports> fileDataPrimitive;
3934-
Unused << item->GetTransferData(kFilePromiseMime,
3935-
getter_AddRefs(fileDataPrimitive));
3906+
if (NS_FAILED(item->GetTransferData(
3907+
kFilePromiseMime, getter_AddRefs(fileDataPrimitive)))) {
3908+
continue;
3909+
}
3910+
nsCOMPtr<nsIFile> file = do_QueryInterface(fileDataPrimitive);
3911+
if (!file) {
3912+
continue;
3913+
}
3914+
nsAutoCString finalPath;
3915+
file->GetNativePath(finalPath);
3916+
NSString* filePath =
3917+
[NSString stringWithUTF8String:(const char*)finalPath.get()];
3918+
[aPasteboard
3919+
setString:[[NSURL fileURLWithPath:filePath] absoluteString]
3920+
forType:curType];
3921+
gCreatedPromisedFile = true;
39363922
}
3937-
3923+
} else if ([curType isEqualToString:[UTIHelper
3924+
stringFromPboardType:
3925+
kUrlsWithTitlesPboardType]]) {
39383926
[aPasteboard setPropertyList:[pasteboardOutputDict valueForKey:curType]
39393927
forType:curType];
3928+
} else if ([curType
3929+
isEqualToString:[UTIHelper stringFromPboardType:
3930+
NSPasteboardTypeHTML]]) {
3931+
[aPasteboard setString:(nsClipboard::WrapHtmlForSystemPasteboard(
3932+
[pasteboardOutputDict valueForKey:curType]))
3933+
forType:curType];
3934+
} else if ([curType
3935+
isEqualToString:[UTIHelper stringFromPboardType:
3936+
NSPasteboardTypeTIFF]] ||
3937+
[curType isEqualToString:[UTIHelper
3938+
stringFromPboardType:
3939+
kMozCustomTypesPboardType]]) {
3940+
[aPasteboard setData:[pasteboardOutputDict valueForKey:curType]
3941+
forType:curType];
3942+
} else if ([curType
3943+
isEqualToString:[UTIHelper stringFromPboardType:
3944+
kMozFileUrlsPboardType]]) {
3945+
[aPasteboard writeObjects:[pasteboardOutputDict valueForKey:curType]];
39403946
}
39413947
}
39423948
}

widget/cocoa/nsDragService.mm

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@
4343
// This global makes the transferable array available to Cocoa's promised
4444
// file destination callback.
4545
mozilla::StaticRefPtr<nsIArray> gDraggedTransferables;
46+
// This global ensures that file promises only resolve once and that the
47+
// associated file is only created once on disk per drag session.
48+
bool gCreatedPromisedFile;
4649

4750
already_AddRefed<nsIDragSession> nsDragService::CreateDragSession() {
4851
RefPtr<nsIDragSession> sess = new nsDragSession();
@@ -200,6 +203,7 @@
200203

201204
// Save the transferables away in case a promised file callback is invoked.
202205
gDraggedTransferables = aTransferableArray;
206+
gCreatedPromisedFile = false;
203207

204208
// We need to retain the view and the event during the drag in case either
205209
// gets destroyed.

0 commit comments

Comments
 (0)