support promised file dragging in cocoa drag service. patch by Stan Shebs. b=358093 r=josh sr=pav
authorjoshmoz@gmail.com
Mon, 16 Jul 2007 19:24:05 -0700
changeset 3545 733e5403e264cf93354c7493e2e7baf0b5875b2a
parent 3544 e221879b157d31c3d92d99deeaef9f5ff1721bee
child 3546 e5f2fbcd206b0ef899bc9fdc4a78c71bcefa624f
push idunknown
push userunknown
push dateunknown
reviewersjosh, pav
bugs358093
milestone1.9a7pre
support promised file dragging in cocoa drag service. patch by Stan Shebs. b=358093 r=josh sr=pav
widget/src/cocoa/nsChildView.h
widget/src/cocoa/nsChildView.mm
widget/src/cocoa/nsClipboard.h
widget/src/cocoa/nsClipboard.mm
widget/src/cocoa/nsDragService.h
widget/src/cocoa/nsDragService.mm
--- a/widget/src/cocoa/nsChildView.h
+++ b/widget/src/cocoa/nsChildView.h
@@ -64,16 +64,22 @@
 #include "nsIMenuBar.h"
 
 #include "nsplugindefs.h"
 
 #undef DARWIN
 #import <Carbon/Carbon.h>
 #import <Cocoa/Cocoa.h>
 
+#ifdef MOZ_LOGGING
+// make sure that logging is enabled before including prlog.h
+#define FORCE_PR_LOG
+#include "prlog.h"
+#endif
+
 class gfxASurface;
 class nsChildView;
 union nsPluginPort;
 
 @interface ChildView : NSView<
 #ifdef ACCESSIBILITY
                               mozAccessible,
 #endif
--- a/widget/src/cocoa/nsChildView.mm
+++ b/widget/src/cocoa/nsChildView.mm
@@ -53,30 +53,36 @@
 #include "nsIDeviceContext.h"
 #include "nsIRegion.h"
 #include "nsIRollupListener.h"
 #include "nsIEventSink.h"
 #include "nsIScrollableView.h"
 #include "nsIViewManager.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIServiceManager.h"
+#include "nsILocalFile.h"
+#include "nsILocalFileMac.h"
 #include "nsGfxCIID.h"
 
 #include "nsDragService.h"
 #include "nsCursorManager.h"
 #include "nsWindowMap.h"
 #include "nsCocoaUtils.h"
 
 #include "gfxContext.h"
 #include "gfxQuartzSurface.h"
 
 #undef DEBUG_IME
 #undef DEBUG_UPDATE
 #undef INVALIDATE_DEBUGGING  // flash areas as they are invalidated
 
+#ifdef PR_LOGGING
+PRLogModuleInfo* sCocoaLog = nsnull;
+#endif
+
 extern "C" {
   CG_EXTERN void CGContextResetCTM(CGContextRef);
   CG_EXTERN void CGContextSetCTM(CGContextRef, CGAffineTransform);
   CG_EXTERN void CGContextResetClip(CGContextRef);
 }
 
 PRBool nsTSMManager::sIsIMEEnabled = PR_TRUE;
 PRBool nsTSMManager::sIsRomanKeyboardsOnly = PR_FALSE;
@@ -323,16 +329,22 @@ nsChildView::nsChildView() : nsBaseWidge
 , mLiveResizeInProgress(PR_FALSE)
 , mIsPluginView(PR_FALSE)
 , mPluginDrawing(PR_FALSE)
 , mPluginIsCG(PR_FALSE)
 , mVisRgn(nsnull)
 {
   SetBackgroundColor(NS_RGB(255, 255, 255));
   SetForegroundColor(NS_RGB(0, 0, 0));
+
+#ifdef PR_LOGGING
+  if (!sCocoaLog)
+    sCocoaLog = PR_NewLogModule("nsCocoaWidgets");
+#endif
+
 }
 
 
 nsChildView::~nsChildView()
 {
   // notify the children that we're gone
   for (nsIWidget* kid = mFirstChild; kid; kid = kid->GetNextSibling()) {
     nsChildView* childView = static_cast<nsChildView*>(kid);
@@ -1849,19 +1861,21 @@ NSEvent* globalDragEvent = nil;
     mMarkedRange.length = 0;
     mSelectedRange.location = NSNotFound;
     mSelectedRange.length = 0;
     mLastMenuForEventEvent = nil;
     mDragService = nsnull;
   }
   
   // register for things we'll take from other applications
+  PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView initWithFrame: registering drag types\n"));
   [self registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType,
                                                           NSStringPboardType,
                                                           NSURLPboardType,
+                                                          NSFilesPromisePboardType,
                                                           kWildcardPboardType,
                                                           nil]];
 
   return self;
 }
 
 
 - (void)dealloc
@@ -3874,16 +3888,18 @@ static PRBool IsSpecialGeckoKey(UInt32 m
 // to send events. It contains all of the logic needed for Gecko
 // dragging to work. Returns YES if the event was handled, NO
 // if it wasn't.
 - (BOOL)doDragAction:(PRUint32)aMessage sender:(id)aSender
 {
   if (!mGeckoChild)
     return NO;
 
+  PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView doDragAction: entered\n"));
+
   if (!mDragService) {
     CallGetService(kDragServiceContractID, &mDragService);
     NS_ASSERTION(mDragService, "Couldn't get a drag service - big problem!");
     if (!mDragService)
       return NO;
   }
 
   if (aMessage == NS_DRAGDROP_ENTER)
@@ -3940,16 +3956,18 @@ static PRBool IsSpecialGeckoKey(UInt32 m
   }
 
   return handled ? YES : NO;
 }
 
 
 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
 {
+  PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView draggingEntered: entered\n"));
+  
   // there should never be a globalDragPboard when "draggingEntered:" is
   // called, but just in case we'll take care of it here.
   [globalDragPboard release];
 
   // Set the global drag pasteboard that will be used for this drag session.
   // This will be set back to nil when the drag session ends (mouse exits
   // the view or a drop happens within the view).
   globalDragPboard = [[sender draggingPasteboard] retain];
@@ -3957,23 +3975,27 @@ static PRBool IsSpecialGeckoKey(UInt32 m
   BOOL handled = [self doDragAction:NS_DRAGDROP_ENTER sender:sender];
 
   return handled ? NSDragOperationGeneric : NSDragOperationNone;
 }
 
 
 - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
 {
+  PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView draggingUpdated: entered\n"));
+
   BOOL handled = [self doDragAction:NS_DRAGDROP_OVER sender:sender];
   return handled ? NSDragOperationGeneric : NSDragOperationNone;
 }
 
 
 - (void)draggingExited:(id <NSDraggingInfo>)sender
 {
+  PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView draggingExited: entered\n"));
+
   [self doDragAction:NS_DRAGDROP_EXIT sender:sender];
 }
 
 
 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
 {
   return [self doDragAction:NS_DRAGDROP_DROP sender:sender];
 }
@@ -3999,16 +4021,73 @@ static PRBool IsSpecialGeckoKey(UInt32 m
 
 // NSDraggingSource
 // this is just implemented so we comply with the NSDraggingSource informal protocol
 - (unsigned int)draggingSourceOperationMaskForLocal:(BOOL)isLocal
 {
   return UINT_MAX;
 }
 
+// This method is a callback typically invoked in response to a drag ending on the desktop
+// or a Findow folder window; the argument passed is a path to the drop location, to be used
+// in constructing a complete pathname for the file(s) we want to create as a result of
+// the drag.
+- (NSArray *)namesOfPromisedFilesDroppedAtDestination:(id <NSDraggingInfo>)dropDestination
+{
+  nsresult rv;
+
+  PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView namesOfPromisedFilesDroppedAtDestination: entering callback for promised files\n"));
+
+  nsCOMPtr<nsILocalFile> targFile;
+  NS_NewLocalFile(EmptyString(), PR_TRUE, getter_AddRefs(targFile));
+  nsCOMPtr<nsILocalFileMac> macLocalFile = do_QueryInterface(targFile);
+  if (!macLocalFile) {
+    NS_ERROR("No Mac local file");
+    return nil;
+  }
+
+  if (!NS_SUCCEEDED(macLocalFile->InitWithCFURL((CFURLRef)dropDestination))) {
+    NS_ERROR("failed InitWithCFURL");
+    return nil;
+  }
+
+  extern nsISupportsArray *draggedTransferables;
+
+  PRUint32 transferableCount;
+  rv = draggedTransferables->Count(&transferableCount);
+  if (NS_FAILED(rv))
+    return nil;
+
+  for (PRUint32 i = 0; i < transferableCount; i++) {
+    nsCOMPtr<nsISupports> genericItem;
+    draggedTransferables->GetElementAt(i, getter_AddRefs(genericItem));
+    nsCOMPtr<nsITransferable> item(do_QueryInterface(genericItem));
+    if (!item) {
+      NS_ERROR("no transferable");
+      return nil;
+    }
+
+    item->SetTransferData(kFilePromiseDirectoryMime, macLocalFile, sizeof(nsILocalFile*));
+    
+    // now request the kFilePromiseMime data, which will invoke the data provider
+    // If successful, the returned data is a reference to the resulting file.
+    nsCOMPtr<nsISupports> fileDataPrimitive;
+    PRUint32 dataSize = 0;
+    item->GetTransferData(kFilePromiseMime, getter_AddRefs(fileDataPrimitive), &dataSize);
+  }
+  
+  NSPasteboard* generalPboard = [NSPasteboard pasteboardWithName:NSDragPboard];
+  NSData* data = [generalPboard dataForType:@"application/x-moz-file-promise-dest-filename"];
+  NSString* name = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
+  NSArray* rslt = [NSArray arrayWithObject:name];
+
+  [name release];
+
+  return rslt;
+}
 
 #pragma mark -
 
 
 #ifdef ACCESSIBILITY
 
 /* Every ChildView has a corresponding mozDocAccessible object that is doing all
    the heavy lifting. The topmost ChildView corresponds to a mozRootAccessible
--- a/widget/src/cocoa/nsClipboard.h
+++ b/widget/src/cocoa/nsClipboard.h
@@ -38,16 +38,26 @@
 
 #ifndef nsClipboard_h_
 #define nsClipboard_h_
 
 #include "nsBaseClipboard.h"
 
 #import <Cocoa/Cocoa.h>
 
+#ifdef MOZ_LOGGING
+// make sure that logging is enabled before including prlog.h
+#define FORCE_PR_LOG
+#include "prlog.h"
+#endif
+
+#ifdef PR_LOGGING
+extern PRLogModuleInfo* sCocoaLog;
+#endif
+
 class nsITransferable;
 
 
 class nsClipboard : public nsBaseClipboard
 {
 
 public:
   nsClipboard();
--- a/widget/src/cocoa/nsClipboard.mm
+++ b/widget/src/cocoa/nsClipboard.mm
@@ -294,17 +294,17 @@ nsClipboard::PasteboardDictFromTransfera
     flavorList->GetElementAt(i, getter_AddRefs(genericFlavor));
     nsCOMPtr<nsISupportsCString> currentFlavor(do_QueryInterface(genericFlavor));
     if (!currentFlavor)
       continue;
 
     nsXPIDLCString flavorStr;
     currentFlavor->ToString(getter_Copies(flavorStr));
 
-    // printf("writing out clipboard data of type %s\n", flavorStr.get());
+    PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("writing out clipboard data of type %s (%d)\n", flavorStr.get(), i));
 
     if (flavorStr.EqualsLiteral(kUnicodeMime)) {
       void* data = nsnull;
       PRUint32 dataSize = 0;
       nsCOMPtr<nsISupports> genericDataWrapper;
       rv = aTransferable->GetTransferData(flavorStr, getter_AddRefs(genericDataWrapper), &dataSize);
       nsPrimitiveHelpers::CreateDataFromPrimitive(flavorStr, genericDataWrapper, &data, dataSize);
       
--- a/widget/src/cocoa/nsDragService.h
+++ b/widget/src/cocoa/nsDragService.h
@@ -38,16 +38,26 @@
 
 #ifndef nsDragService_h_
 #define nsDragService_h_
 
 #include "nsBaseDragService.h"
 
 #include <Cocoa/Cocoa.h>
 
+#ifdef MOZ_LOGGING
+// make sure that logging is enabled before including prlog.h
+#define FORCE_PR_LOG
+#include "prlog.h"
+#endif
+
+#ifdef PR_LOGGING
+extern PRLogModuleInfo* sCocoaLog;
+#endif
+
 extern NSString* const kWildcardPboardType;
 
 class nsDragService : public nsBaseDragService
 {
 public:
   nsDragService();
   virtual ~nsDragService();
 
--- a/widget/src/cocoa/nsDragService.mm
+++ b/widget/src/cocoa/nsDragService.mm
@@ -64,16 +64,20 @@
 #include "gfxContext.h"
 
 #import <Cocoa/Cocoa.h>
 
 extern NSPasteboard* globalDragPboard;
 extern NSView* globalDragView;
 extern NSEvent* globalDragEvent;
 
+// This global makes the transferable array available to Cocoa's promised
+// file destination callback.
+nsISupportsArray *draggedTransferables;
+
 NSString* const kWildcardPboardType = @"MozillaWildcard";
 
 NS_IMPL_ADDREF_INHERITED(nsDragService, nsBaseDragService)
 NS_IMPL_RELEASE_INHERITED(nsDragService, nsBaseDragService)
 NS_IMPL_QUERY_INTERFACE2(nsDragService, nsIDragService, nsIDragSession)
 
 
 nsDragService::nsDragService()
@@ -257,22 +261,33 @@ nsDragService::InvokeDragSession(nsIDOMN
   if ([[NSScreen screens] count] > 0)
     point.y = NSMaxY([[[NSScreen screens] objectAtIndex:0] frame]) - dragRect.YMost();
   else
     point.y = dragRect.y;
 
   point = [[globalDragView window] convertScreenToBase: point];
   NSPoint localPoint = [globalDragView convertPoint:point fromView:nil];
  
+  // Save the transferables away in case a promised file callback is invoked.
+  draggedTransferables = aTransferableArray;
+
   nsBaseDragService::StartDragSession();
+
+  // It is possible to specify what file types we will create, but the Finder doesn't
+  // care; it is happy to store any type of file it is handed. So use an empty string
+  // for type.
+  NSPasteboard* workingPBoard = [NSPasteboard pasteboardWithName:NSDragPboard];
+  NSArray* fileTypeList = [NSArray arrayWithObject:@""];
+  [workingPBoard setPropertyList:fileTypeList forType:NSFilesPromisePboardType];
+
   [globalDragView dragImage:image
                          at:localPoint
                      offset:NSMakeSize(0,0)
                       event:globalDragEvent
-                 pasteboard:[NSPasteboard pasteboardWithName:NSDragPboard]
+                 pasteboard:workingPBoard
                      source:globalDragView
                   slideBack:YES];
 
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
@@ -326,17 +341,17 @@ nsDragService::GetData(nsITransferable* 
     nsCOMPtr<nsISupportsCString> currentFlavor(do_QueryInterface(genericFlavor));
 
     if (!currentFlavor)
       continue;
 
     nsXPIDLCString flavorStr;
     currentFlavor->ToString(getter_Copies(flavorStr));
 
-    // printf("looking for clipboard data of type %s\n", flavorStr.get());
+    PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("nsDragService::GetData: looking for clipboard data of type %s\n", flavorStr.get()));
 
     if (flavorStr.EqualsLiteral(kFileMime)) {
       NSArray* pFiles = [globalDragPboard propertyListForType:NSFilenamesPboardType];
       if (!pFiles || [pFiles count] < (aItemIndex + 1))
         continue;
 
       NSString* filePath = [pFiles objectAtIndex:aItemIndex];
       if (!filePath)