widget/windows/nsNativeDragTarget.cpp

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include <stdio.h>
     7 #include "nsIDragService.h"
     8 #include "nsWidgetsCID.h"
     9 #include "nsNativeDragTarget.h"
    10 #include "nsDragService.h"
    11 #include "nsIServiceManager.h"
    12 #include "nsIDOMNode.h"
    13 #include "nsCOMPtr.h"
    15 #include "nsIWidget.h"
    16 #include "nsWindow.h"
    17 #include "nsClipboard.h"
    18 #include "KeyboardLayout.h"
    20 #include "mozilla/MouseEvents.h"
    22 using namespace mozilla;
    23 using namespace mozilla::widget;
    25 /* Define Interface IDs */
    26 static NS_DEFINE_IID(kIDragServiceIID, NS_IDRAGSERVICE_IID);
    28 // This is cached for Leave notification
    29 static POINTL gDragLastPoint;
    31 /*
    32  * class nsNativeDragTarget
    33  */
    34 nsNativeDragTarget::nsNativeDragTarget(nsIWidget * aWidget)
    35   : m_cRef(0), 
    36     mEffectsAllowed(DROPEFFECT_MOVE | DROPEFFECT_COPY | DROPEFFECT_LINK),
    37     mEffectsPreferred(DROPEFFECT_NONE),
    38     mTookOwnRef(false), mWidget(aWidget), mDropTargetHelper(nullptr)
    39 {
    40   static NS_DEFINE_IID(kCDragServiceCID,  NS_DRAGSERVICE_CID);
    42   mHWnd = (HWND)mWidget->GetNativeData(NS_NATIVE_WINDOW);
    44   /*
    45    * Create/Get the DragService that we have implemented
    46    */
    47   CallGetService(kCDragServiceCID, &mDragService);
    48 }
    50 nsNativeDragTarget::~nsNativeDragTarget()
    51 {
    52   NS_RELEASE(mDragService);
    54   if (mDropTargetHelper) {
    55     mDropTargetHelper->Release();
    56     mDropTargetHelper = nullptr;
    57   }
    58 }
    60 // IUnknown methods - see iunknown.h for documentation
    61 STDMETHODIMP
    62 nsNativeDragTarget::QueryInterface(REFIID riid, void** ppv)
    63 {
    64   *ppv=nullptr;
    66   if (IID_IUnknown == riid || IID_IDropTarget == riid)
    67     *ppv=this;
    69   if (nullptr!=*ppv) {
    70     ((LPUNKNOWN)*ppv)->AddRef();
    71     return S_OK;
    72   }
    74   return E_NOINTERFACE;
    75 }
    77 STDMETHODIMP_(ULONG)
    78 nsNativeDragTarget::AddRef(void)
    79 {
    80   ++m_cRef;
    81   NS_LOG_ADDREF(this, m_cRef, "nsNativeDragTarget", sizeof(*this));
    82   return m_cRef;
    83 }
    85 STDMETHODIMP_(ULONG) nsNativeDragTarget::Release(void)
    86 {
    87   --m_cRef;
    88   NS_LOG_RELEASE(this, m_cRef, "nsNativeDragTarget");
    89   if (0 != m_cRef)
    90     return m_cRef;
    92   delete this;
    93   return 0;
    94 }
    96 void
    97 nsNativeDragTarget::GetGeckoDragAction(DWORD grfKeyState, LPDWORD pdwEffect,
    98                                        uint32_t * aGeckoAction)
    99 {
   100   // If a window is disabled or a modal window is on top of it
   101   // (which implies it is disabled), then we should not allow dropping.
   102   if (!mWidget->IsEnabled()) {
   103     *pdwEffect = DROPEFFECT_NONE;
   104     *aGeckoAction = nsIDragService::DRAGDROP_ACTION_NONE;
   105     return;
   106   }
   108   // If the user explicitly uses a modifier key, they want the associated action
   109   // Shift + Control -> LINK, Shift -> MOVE, Ctrl -> COPY
   110   DWORD desiredEffect = DROPEFFECT_NONE;
   111   if ((grfKeyState & MK_CONTROL) && (grfKeyState & MK_SHIFT)) {
   112     desiredEffect = DROPEFFECT_LINK;
   113   } else if (grfKeyState & MK_SHIFT) {
   114     desiredEffect = DROPEFFECT_MOVE;
   115   } else if (grfKeyState & MK_CONTROL) {
   116     desiredEffect = DROPEFFECT_COPY;
   117   }
   119   // Determine the desired effect from what is allowed and preferred.
   120   if (!(desiredEffect &= mEffectsAllowed)) {
   121     // No modifier key effect is set which is also allowed, check
   122     // the preference of the data.
   123     desiredEffect = mEffectsPreferred & mEffectsAllowed;
   124     if (!desiredEffect) {
   125       // No preference is set, so just fall back to the allowed effect itself
   126       desiredEffect = mEffectsAllowed;
   127     }
   128   }
   130   // Otherwise we should specify the first available effect 
   131   // from MOVE, COPY, or LINK.
   132   if (desiredEffect & DROPEFFECT_MOVE) {
   133     *pdwEffect = DROPEFFECT_MOVE;
   134     *aGeckoAction = nsIDragService::DRAGDROP_ACTION_MOVE;
   135   } else if (desiredEffect & DROPEFFECT_COPY) {
   136     *pdwEffect = DROPEFFECT_COPY;
   137     *aGeckoAction = nsIDragService::DRAGDROP_ACTION_COPY;
   138   } else if (desiredEffect & DROPEFFECT_LINK) {
   139     *pdwEffect = DROPEFFECT_LINK;
   140     *aGeckoAction = nsIDragService::DRAGDROP_ACTION_LINK;
   141   } else {
   142     *pdwEffect = DROPEFFECT_NONE;
   143     *aGeckoAction = nsIDragService::DRAGDROP_ACTION_NONE;
   144   } 
   145 }
   147 inline
   148 bool
   149 IsKeyDown(char key)
   150 {
   151   return GetKeyState(key) < 0;
   152 }
   154 void
   155 nsNativeDragTarget::DispatchDragDropEvent(uint32_t aEventType, POINTL aPT)
   156 {
   157   nsEventStatus status;
   158   WidgetDragEvent event(true, aEventType, mWidget);
   160   nsWindow * win = static_cast<nsWindow *>(mWidget);
   161   win->InitEvent(event);
   162   POINT cpos;
   164   cpos.x = aPT.x;
   165   cpos.y = aPT.y;
   167   if (mHWnd != nullptr) {
   168     ::ScreenToClient(mHWnd, &cpos);
   169     event.refPoint.x = cpos.x;
   170     event.refPoint.y = cpos.y;
   171   } else {
   172     event.refPoint.x = 0;
   173     event.refPoint.y = 0;
   174   }
   176   ModifierKeyState modifierKeyState;
   177   modifierKeyState.InitInputEvent(event);
   179   event.inputSource = static_cast<nsBaseDragService*>(mDragService)->GetInputSource();
   181   mWidget->DispatchEvent(&event, status);
   182 }
   184 void
   185 nsNativeDragTarget::ProcessDrag(uint32_t     aEventType,
   186                                 DWORD        grfKeyState,
   187                                 POINTL       ptl,
   188                                 DWORD*       pdwEffect)
   189 {
   190   // Before dispatching the event make sure we have the correct drop action set
   191   uint32_t geckoAction;
   192   GetGeckoDragAction(grfKeyState, pdwEffect, &geckoAction);
   194   // Set the current action into the Gecko specific type
   195   nsCOMPtr<nsIDragSession> currSession;
   196   mDragService->GetCurrentSession(getter_AddRefs(currSession));
   197   if (!currSession) {
   198     return;
   199   }
   201   currSession->SetDragAction(geckoAction);
   203   // Dispatch the event into Gecko
   204   DispatchDragDropEvent(aEventType, ptl);
   206   if (aEventType != NS_DRAGDROP_DROP) {
   207     // Get the cached drag effect from the drag service, the data member should
   208     // have been set by whoever handled the WidgetGUIEvent or nsIDOMEvent on
   209     // drags.
   210     bool canDrop;
   211     currSession->GetCanDrop(&canDrop);
   212     if (!canDrop) {
   213       *pdwEffect = DROPEFFECT_NONE;
   214     }
   215   }
   217   // Clear the cached value
   218   currSession->SetCanDrop(false);
   219 }
   221 // IDropTarget methods
   222 STDMETHODIMP
   223 nsNativeDragTarget::DragEnter(LPDATAOBJECT pIDataSource,
   224                               DWORD        grfKeyState,
   225                               POINTL       ptl,
   226                               DWORD*       pdwEffect)
   227 {
   228   if (!mDragService) {
   229     return E_FAIL;
   230   }
   232   mEffectsAllowed = *pdwEffect;
   233   AddLinkSupportIfCanBeGenerated(pIDataSource);
   235   // Drag and drop image helper
   236   if (GetDropTargetHelper()) {
   237     POINT pt = { ptl.x, ptl.y };
   238     GetDropTargetHelper()->DragEnter(mHWnd, pIDataSource, &pt, *pdwEffect);
   239   }
   241   // save a ref to this, in case the window is destroyed underneath us
   242   NS_ASSERTION(!mTookOwnRef, "own ref already taken!");
   243   this->AddRef();
   244   mTookOwnRef = true;
   246   // tell the drag service about this drag (it may have come from an
   247   // outside app).
   248   mDragService->StartDragSession();
   250   void* tempOutData = nullptr;
   251   uint32_t tempDataLen = 0;
   252   nsresult loadResult = nsClipboard::GetNativeDataOffClipboard(
   253       pIDataSource, 0, ::RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT), nullptr, &tempOutData, &tempDataLen);
   254   if (NS_SUCCEEDED(loadResult) && tempOutData) {
   255     mEffectsPreferred = *((DWORD*)tempOutData);
   256     nsMemory::Free(tempOutData);
   257   } else {
   258     // We have no preference if we can't obtain it
   259     mEffectsPreferred = DROPEFFECT_NONE;
   260   }
   262   // Set the native data object into drag service
   263   //
   264   // This cast is ok because in the constructor we created a
   265   // the actual implementation we wanted, so we know this is
   266   // a nsDragService. It should be a private interface, though.
   267   nsDragService * winDragService =
   268     static_cast<nsDragService *>(mDragService);
   269   winDragService->SetIDataObject(pIDataSource);
   271   // Now process the native drag state and then dispatch the event
   272   ProcessDrag(NS_DRAGDROP_ENTER, grfKeyState, ptl, pdwEffect);
   274   return S_OK;
   275 }
   277 void 
   278 nsNativeDragTarget::AddLinkSupportIfCanBeGenerated(LPDATAOBJECT aIDataSource) 
   279 {
   280   // If we don't have a link effect, but we can generate one, fix the 
   281   // drop effect to include it.
   282   if (!(mEffectsAllowed & DROPEFFECT_LINK) && aIDataSource) {
   283     if (S_OK == ::OleQueryLinkFromData(aIDataSource)) {
   284       mEffectsAllowed |= DROPEFFECT_LINK;
   285     }
   286   }
   287 }
   289 STDMETHODIMP
   290 nsNativeDragTarget::DragOver(DWORD   grfKeyState,
   291                              POINTL  ptl,
   292                              LPDWORD pdwEffect)
   293 {
   294   if (!mDragService) {
   295     return E_FAIL;
   296   }
   298   // If a LINK effect could be generated previously from a DragEnter(),
   299   // then we should include it as an allowed effect.
   300   mEffectsAllowed = (*pdwEffect) | (mEffectsAllowed & DROPEFFECT_LINK);
   302   nsCOMPtr<nsIDragSession> currentDragSession;
   303   mDragService->GetCurrentSession(getter_AddRefs(currentDragSession));
   304   if (!currentDragSession) {
   305     return S_OK;  // Drag was canceled.
   306   }
   308   // without the AddRef() |this| can get destroyed in an event handler
   309   this->AddRef();
   311   // Drag and drop image helper
   312   if (GetDropTargetHelper()) {
   313     POINT pt = { ptl.x, ptl.y };
   314     GetDropTargetHelper()->DragOver(&pt, *pdwEffect);
   315   }
   317   mDragService->FireDragEventAtSource(NS_DRAGDROP_DRAG);
   318   // Now process the native drag state and then dispatch the event
   319   ProcessDrag(NS_DRAGDROP_OVER, grfKeyState, ptl, pdwEffect);
   321   this->Release();
   323   return S_OK;
   324 }
   326 STDMETHODIMP
   327 nsNativeDragTarget::DragLeave()
   328 {
   329   if (!mDragService) {
   330     return E_FAIL;
   331   }
   333   // Drag and drop image helper
   334   if (GetDropTargetHelper()) {
   335     GetDropTargetHelper()->DragLeave();
   336   }
   338   // dispatch the event into Gecko
   339   DispatchDragDropEvent(NS_DRAGDROP_EXIT, gDragLastPoint);
   341   nsCOMPtr<nsIDragSession> currentDragSession;
   342   mDragService->GetCurrentSession(getter_AddRefs(currentDragSession));
   344   if (currentDragSession) {
   345     nsCOMPtr<nsIDOMNode> sourceNode;
   346     currentDragSession->GetSourceNode(getter_AddRefs(sourceNode));
   348     if (!sourceNode) {
   349       // We're leaving a window while doing a drag that was
   350       // initiated in a different app. End the drag session, since
   351       // we're done with it for now (until the user drags back into
   352       // mozilla).
   353       mDragService->EndDragSession(false);
   354     }
   355   }
   357   // release the ref that was taken in DragEnter
   358   NS_ASSERTION(mTookOwnRef, "want to release own ref, but not taken!");
   359   if (mTookOwnRef) {
   360     this->Release();
   361     mTookOwnRef = false;
   362   }
   364   return S_OK;
   365 }
   367 void
   368 nsNativeDragTarget::DragCancel()
   369 {
   370   // Cancel the drag session if we did DragEnter.
   371   if (mTookOwnRef) {
   372     if (GetDropTargetHelper()) {
   373       GetDropTargetHelper()->DragLeave();
   374     }
   375     if (mDragService) {
   376       mDragService->EndDragSession(false);
   377     }
   378     this->Release(); // matching the AddRef in DragEnter
   379     mTookOwnRef = false;
   380   }
   381 }
   383 STDMETHODIMP
   384 nsNativeDragTarget::Drop(LPDATAOBJECT pData,
   385                          DWORD        grfKeyState,
   386                          POINTL       aPT,
   387                          LPDWORD      pdwEffect)
   388 {
   389   if (!mDragService) {
   390     return E_FAIL;
   391   }
   393   mEffectsAllowed = *pdwEffect;
   394   AddLinkSupportIfCanBeGenerated(pData);
   396   // Drag and drop image helper
   397   if (GetDropTargetHelper()) {
   398     POINT pt = { aPT.x, aPT.y };
   399     GetDropTargetHelper()->Drop(pData, &pt, *pdwEffect);
   400   }
   402   // Set the native data object into the drag service
   403   //
   404   // This cast is ok because in the constructor we created a
   405   // the actual implementation we wanted, so we know this is
   406   // a nsDragService (but it should still be a private interface)
   407   nsDragService* winDragService = static_cast<nsDragService*>(mDragService);
   408   winDragService->SetIDataObject(pData);
   410   // NOTE: ProcessDrag spins the event loop which may destroy arbitrary objects.
   411   // We use strong refs to prevent it from destroying these:
   412   nsRefPtr<nsNativeDragTarget> kungFuDeathGrip = this;
   413   nsCOMPtr<nsIDragService> serv = mDragService;
   415   // Now process the native drag state and then dispatch the event
   416   ProcessDrag(NS_DRAGDROP_DROP, grfKeyState, aPT, pdwEffect);
   418   nsCOMPtr<nsIDragSession> currentDragSession;
   419   serv->GetCurrentSession(getter_AddRefs(currentDragSession));
   420   if (!currentDragSession) {
   421     return S_OK;  // DragCancel() was called.
   422   }
   424   // Let the win drag service know whether this session experienced 
   425   // a drop event within the application. Drop will not oocur if the
   426   // drop landed outside the app. (used in tab tear off, bug 455884)
   427   winDragService->SetDroppedLocal();
   429   // tell the drag service we're done with the session
   430   // Use GetMessagePos to get the position of the mouse at the last message
   431   // seen by the event loop. (Bug 489729)
   432   DWORD pos = ::GetMessagePos();
   433   POINT cpos;
   434   cpos.x = GET_X_LPARAM(pos);
   435   cpos.y = GET_Y_LPARAM(pos);
   436   winDragService->SetDragEndPoint(nsIntPoint(cpos.x, cpos.y));
   437   serv->EndDragSession(true);
   439   // release the ref that was taken in DragEnter
   440   NS_ASSERTION(mTookOwnRef, "want to release own ref, but not taken!");
   441   if (mTookOwnRef) {
   442     this->Release();
   443     mTookOwnRef = false;
   444   }
   446   return S_OK;
   447 }
   449 /**
   450  * By lazy loading mDropTargetHelper we save 50-70ms of startup time
   451  * which is ~5% of startup time.
   452 */
   453 IDropTargetHelper*
   454 nsNativeDragTarget::GetDropTargetHelper()
   455 {
   456   if (!mDropTargetHelper) { 
   457     CoCreateInstance(CLSID_DragDropHelper, nullptr, CLSCTX_INPROC_SERVER,
   458                      IID_IDropTargetHelper, (LPVOID*)&mDropTargetHelper);
   459   }
   461   return mDropTargetHelper;
   462 }

mercurial