Bug 357601 - Drag and drop from an external program to Firefox does not use the proper drop effect. r=neil
authorBrian R. Bondy <netzen@gmail.com>
Thu, 01 Sep 2011 09:48:48 -0400
changeset 76379 2c6b5f33faa63f98dc8a88b4a0ab0ce271315ed2
parent 76378 89c47cdfad0a20e111ccea68d1c492df4d2fc410
child 76380 191f7bab5f5a01be721e48771a9e62ada0285d41
push id3
push userfelipc@gmail.com
push dateFri, 30 Sep 2011 20:09:13 +0000
reviewersneil
bugs357601
milestone9.0a1
Bug 357601 - Drag and drop from an external program to Firefox does not use the proper drop effect. r=neil
widget/src/windows/nsNativeDragTarget.cpp
widget/src/windows/nsNativeDragTarget.h
--- a/widget/src/windows/nsNativeDragTarget.cpp
+++ b/widget/src/windows/nsNativeDragTarget.cpp
@@ -56,17 +56,18 @@ static NS_DEFINE_IID(kIDragServiceIID, N
 
 // This is cached for Leave notification
 static POINTL gDragLastPoint;
 
 /*
  * class nsNativeDragTarget
  */
 nsNativeDragTarget::nsNativeDragTarget(nsIWidget * aWnd)
-  : m_cRef(0), mEffect(DROPEFFECT_MOVE | DROPEFFECT_COPY | DROPEFFECT_LINK), 
+  : m_cRef(0), 
+    mEffectsAllowed(DROPEFFECT_MOVE | DROPEFFECT_COPY | DROPEFFECT_LINK), 
     mTookOwnRef(PR_FALSE), mWindow(aWnd), mDropTargetHelper(nsnull)
 {
   mHWnd = (HWND)mWindow->GetNativeData(NS_NATIVE_WINDOW);
 
   /*
    * Create/Get the DragService that we have implemented
    */
   CallGetService(kCDragServiceCID, &mDragService);
@@ -120,47 +121,47 @@ STDMETHODIMP_(ULONG) nsNativeDragTarget:
 
   delete this;
   return 0;
 }
 
 void
 nsNativeDragTarget::GetGeckoDragAction(DWORD grfKeyState, LPDWORD pdwEffect,
                                        PRUint32 * aGeckoAction)
-{  
-  // Default is move if we can, in fact drop here,
-  // and if the drop source supports a move operation.
-  // If move is not preferred (mMovePreferred is false)
-  // move only when the shift key is down.
-  if ((mEffect & DROPEFFECT_MOVE) && 
-      (mMovePreferred || (grfKeyState & MK_SHIFT))) {
+{
+  // Only a single effect should be specified outgoing for the parameter pdwEffect.
+  // When Shift and Control are pressed, we should specify a LINK effect.
+  if (!mMovePreferred && (grfKeyState & MK_CONTROL) && 
+      (grfKeyState & MK_SHIFT) && (mEffectsAllowed & DROPEFFECT_LINK)) {
+    *pdwEffect = DROPEFFECT_LINK;
+    *aGeckoAction = nsIDragService::DRAGDROP_ACTION_LINK;
+  }
+  // When Shift is pressed we should specify a MOVE effect.
+  else if ((mEffectsAllowed & DROPEFFECT_MOVE) && 
+           (mMovePreferred || (grfKeyState & MK_SHIFT))) {
+    *pdwEffect = DROPEFFECT_MOVE;
     *aGeckoAction = nsIDragService::DRAGDROP_ACTION_MOVE;
-    *pdwEffect    = DROPEFFECT_MOVE;
-  } 
-  else if (!(mEffect & DROPEFFECT_MOVE) && 
-           !(mEffect & DROPEFFECT_COPY) && 
-           (mEffect & DROPEFFECT_LINK)) {
-    *aGeckoAction = nsIDragService::DRAGDROP_ACTION_LINK;
-    *pdwEffect    = DROPEFFECT_LINK;
   }
-  else {
+  // When Control is pressed we should specify a COPY effect.
+  else if ((mEffectsAllowed & DROPEFFECT_COPY) && (grfKeyState & MK_CONTROL)) {
+    *pdwEffect = DROPEFFECT_COPY;
     *aGeckoAction = nsIDragService::DRAGDROP_ACTION_COPY;
-    *pdwEffect    = DROPEFFECT_COPY;
   }
-
-  // Given the key modifiers figure out what state we are in for both
-  // the native system and Gecko
-  if (grfKeyState & MK_CONTROL) {
-    if ((mEffect & DROPEFFECT_LINK) && (grfKeyState & MK_SHIFT)) {
-      *aGeckoAction = nsIDragService::DRAGDROP_ACTION_LINK;
-      *pdwEffect    = DROPEFFECT_LINK;
-    } else {
-      *aGeckoAction = nsIDragService::DRAGDROP_ACTION_COPY;
-      *pdwEffect    = DROPEFFECT_COPY;
-    }
+  // Otherwise we should specify the first available effect from MOVE, COPY, or LINK.
+  else if (mEffectsAllowed & DROPEFFECT_MOVE) {
+    *pdwEffect = DROPEFFECT_MOVE;
+    *aGeckoAction = nsIDragService::DRAGDROP_ACTION_MOVE;
+  }
+  else if (mEffectsAllowed & DROPEFFECT_COPY) {
+    *pdwEffect = DROPEFFECT_COPY;
+    *aGeckoAction = nsIDragService::DRAGDROP_ACTION_COPY;
+  }
+  else if (mEffectsAllowed & DROPEFFECT_LINK) {
+    *pdwEffect = DROPEFFECT_LINK;
+    *aGeckoAction = nsIDragService::DRAGDROP_ACTION_LINK;
   }
 }
 
 inline
 PRBool
 IsKeyDown(char key)
 {
   return GetKeyState(key) < 0;
@@ -214,101 +215,113 @@ nsNativeDragTarget::ProcessDrag(PRUint32
     return;
   }
 
   currSession->SetDragAction(geckoAction);
 
   // Dispatch the event into Gecko
   DispatchDragDropEvent(aEventType, ptl);
 
-  // Now get the cached Drag effect from the drag service
-  // the data memeber should have been set by who ever handled the
-  // nsGUIEvent or nsIDOMEvent
-  PRBool canDrop;
-  currSession->GetCanDrop(&canDrop);
-  if (!canDrop)
-    *pdwEffect = DROPEFFECT_NONE;
+  if (aEventType != NS_DRAGDROP_DROP) {
+    // Get the cached drag effect from the drag service, the data member should
+    // have been set by whoever handled the nsGUIEvent or nsIDOMEvent on drags.
+    PRBool canDrop;
+    currSession->GetCanDrop(&canDrop);
+    if (!canDrop) {
+      *pdwEffect = DROPEFFECT_NONE;
+    }
+  }
 
   // Clear the cached value
   currSession->SetCanDrop(PR_FALSE);
 }
 
 // IDropTarget methods
 STDMETHODIMP
 nsNativeDragTarget::DragEnter(LPDATAOBJECT pIDataSource,
                               DWORD        grfKeyState,
                               POINTL       ptl,
                               DWORD*       pdwEffect)
 {
   if (!mDragService) {
     return E_FAIL;
   }
 
+  mEffectsAllowed = *pdwEffect;
+  AddLinkSupportIfCanBeGenerated(pIDataSource);
+
   // Drag and drop image helper
   if (mDropTargetHelper) {
     POINT pt = { ptl.x, ptl.y };
     mDropTargetHelper->DragEnter(mHWnd, pIDataSource, &pt, *pdwEffect);
   }
 
   // save a ref to this, in case the window is destroyed underneath us
   NS_ASSERTION(!mTookOwnRef, "own ref already taken!");
   this->AddRef();
   mTookOwnRef = PR_TRUE;
 
   // tell the drag service about this drag (it may have come from an
   // outside app).
   mDragService->StartDragSession();
 
-  mEffect = *pdwEffect;
-  // If we don't have a link effect, but we can generate one, fix the 
-  // drop effect to include it
-  if (!(mEffect & DROPEFFECT_LINK) && pIDataSource) {
-    if (S_OK == ::OleQueryLinkFromData(pIDataSource)) {
-      mEffect |= DROPEFFECT_LINK;
-    }
-  }
-
   void* tempOutData = nsnull;
   PRUint32 tempDataLen = 0;
   nsresult loadResult = nsClipboard::GetNativeDataOffClipboard(
       pIDataSource, 0, ::RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT), nsnull, &tempOutData, &tempDataLen);
   if (NS_SUCCEEDED(loadResult) && tempOutData) {
     NS_ASSERTION(tempDataLen == 2, "Expected word size");
     WORD preferredEffect = *((WORD*)tempOutData);
 
     // Mask effect coming from function call with effect preferred by the source.
     mMovePreferred = (preferredEffect & DROPEFFECT_MOVE) != 0;
+  } else {
+    mMovePreferred = PR_FALSE;
   }
-  else
-    mMovePreferred = (mEffect & DROPEFFECT_MOVE) != 0;
 
   // Set the native data object into drag service
   //
   // This cast is ok because in the constructor we created a
   // the actual implementation we wanted, so we know this is
   // a nsDragService. It should be a private interface, though.
   nsDragService * winDragService =
     static_cast<nsDragService *>(mDragService);
   winDragService->SetIDataObject(pIDataSource);
 
   // Now process the native drag state and then dispatch the event
   ProcessDrag(NS_DRAGDROP_ENTER, grfKeyState, ptl, pdwEffect);
 
   return S_OK;
 }
 
+void 
+nsNativeDragTarget::AddLinkSupportIfCanBeGenerated(LPDATAOBJECT aIDataSource) 
+{
+  // If we don't have a link effect, but we can generate one, fix the 
+  // drop effect to include it.
+  if (!(mEffectsAllowed & DROPEFFECT_LINK) && aIDataSource) {
+    if (S_OK == ::OleQueryLinkFromData(aIDataSource)) {
+      mEffectsAllowed |= DROPEFFECT_LINK;
+    }
+  }
+}
+
 STDMETHODIMP
 nsNativeDragTarget::DragOver(DWORD   grfKeyState,
                              POINTL  ptl,
                              LPDWORD pdwEffect)
 {
   if (!mDragService) {
     return E_FAIL;
   }
 
+  // If a LINK effect could be generated previously from a DragEnter(),
+  // then we should include it as an allowed effect.
+  mEffectsAllowed = (*pdwEffect) | (mEffectsAllowed & DROPEFFECT_LINK);
+
   nsCOMPtr<nsIDragSession> currentDragSession;
   mDragService->GetCurrentSession(getter_AddRefs(currentDragSession));
   if (!currentDragSession) {
     return S_OK;  // Drag was canceled.
   }
 
   // without the AddRef() |this| can get destroyed in an event handler
   this->AddRef();
@@ -390,16 +403,19 @@ nsNativeDragTarget::Drop(LPDATAOBJECT pD
                          DWORD        grfKeyState,
                          POINTL       aPT,
                          LPDWORD      pdwEffect)
 {
   if (!mDragService) {
     return E_FAIL;
   }
 
+  mEffectsAllowed = *pdwEffect;
+  AddLinkSupportIfCanBeGenerated(pData);
+
   // Drag and drop image helper
   if (mDropTargetHelper) {
     POINT pt = { aPT.x, aPT.y };
     mDropTargetHelper->Drop(pData, &pt, *pdwEffect);
   }
 
   // Set the native data object into the drag service
   //
--- a/widget/src/windows/nsNativeDragTarget.h
+++ b/widget/src/windows/nsNativeDragTarget.h
@@ -102,21 +102,22 @@ public:
 
 protected:
 
   void GetGeckoDragAction(DWORD grfKeyState, LPDWORD pdwEffect, 
                           PRUint32 * aGeckoAction);
   void ProcessDrag(PRUint32 aEventType, DWORD grfKeyState,
                    POINTL pt, DWORD* pdwEffect);
   void DispatchDragDropEvent(PRUint32 aType, POINTL pt);
+  void AddLinkSupportIfCanBeGenerated(LPDATAOBJECT aIDataSource);
 
   // Native Stuff
   ULONG            m_cRef;      // reference count
   HWND             mHWnd;
-  DWORD            mEffect;
+  DWORD            mEffectsAllowed;
   PRBool           mMovePreferred;
   PRBool           mTookOwnRef;
 
   // Gecko Stuff
   nsIWidget      * mWindow;
   nsIDragService * mDragService;
 
   // Drag target helper