1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/widget/windows/nsNativeDragTarget.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,462 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include <stdio.h> 1.10 +#include "nsIDragService.h" 1.11 +#include "nsWidgetsCID.h" 1.12 +#include "nsNativeDragTarget.h" 1.13 +#include "nsDragService.h" 1.14 +#include "nsIServiceManager.h" 1.15 +#include "nsIDOMNode.h" 1.16 +#include "nsCOMPtr.h" 1.17 + 1.18 +#include "nsIWidget.h" 1.19 +#include "nsWindow.h" 1.20 +#include "nsClipboard.h" 1.21 +#include "KeyboardLayout.h" 1.22 + 1.23 +#include "mozilla/MouseEvents.h" 1.24 + 1.25 +using namespace mozilla; 1.26 +using namespace mozilla::widget; 1.27 + 1.28 +/* Define Interface IDs */ 1.29 +static NS_DEFINE_IID(kIDragServiceIID, NS_IDRAGSERVICE_IID); 1.30 + 1.31 +// This is cached for Leave notification 1.32 +static POINTL gDragLastPoint; 1.33 + 1.34 +/* 1.35 + * class nsNativeDragTarget 1.36 + */ 1.37 +nsNativeDragTarget::nsNativeDragTarget(nsIWidget * aWidget) 1.38 + : m_cRef(0), 1.39 + mEffectsAllowed(DROPEFFECT_MOVE | DROPEFFECT_COPY | DROPEFFECT_LINK), 1.40 + mEffectsPreferred(DROPEFFECT_NONE), 1.41 + mTookOwnRef(false), mWidget(aWidget), mDropTargetHelper(nullptr) 1.42 +{ 1.43 + static NS_DEFINE_IID(kCDragServiceCID, NS_DRAGSERVICE_CID); 1.44 + 1.45 + mHWnd = (HWND)mWidget->GetNativeData(NS_NATIVE_WINDOW); 1.46 + 1.47 + /* 1.48 + * Create/Get the DragService that we have implemented 1.49 + */ 1.50 + CallGetService(kCDragServiceCID, &mDragService); 1.51 +} 1.52 + 1.53 +nsNativeDragTarget::~nsNativeDragTarget() 1.54 +{ 1.55 + NS_RELEASE(mDragService); 1.56 + 1.57 + if (mDropTargetHelper) { 1.58 + mDropTargetHelper->Release(); 1.59 + mDropTargetHelper = nullptr; 1.60 + } 1.61 +} 1.62 + 1.63 +// IUnknown methods - see iunknown.h for documentation 1.64 +STDMETHODIMP 1.65 +nsNativeDragTarget::QueryInterface(REFIID riid, void** ppv) 1.66 +{ 1.67 + *ppv=nullptr; 1.68 + 1.69 + if (IID_IUnknown == riid || IID_IDropTarget == riid) 1.70 + *ppv=this; 1.71 + 1.72 + if (nullptr!=*ppv) { 1.73 + ((LPUNKNOWN)*ppv)->AddRef(); 1.74 + return S_OK; 1.75 + } 1.76 + 1.77 + return E_NOINTERFACE; 1.78 +} 1.79 + 1.80 +STDMETHODIMP_(ULONG) 1.81 +nsNativeDragTarget::AddRef(void) 1.82 +{ 1.83 + ++m_cRef; 1.84 + NS_LOG_ADDREF(this, m_cRef, "nsNativeDragTarget", sizeof(*this)); 1.85 + return m_cRef; 1.86 +} 1.87 + 1.88 +STDMETHODIMP_(ULONG) nsNativeDragTarget::Release(void) 1.89 +{ 1.90 + --m_cRef; 1.91 + NS_LOG_RELEASE(this, m_cRef, "nsNativeDragTarget"); 1.92 + if (0 != m_cRef) 1.93 + return m_cRef; 1.94 + 1.95 + delete this; 1.96 + return 0; 1.97 +} 1.98 + 1.99 +void 1.100 +nsNativeDragTarget::GetGeckoDragAction(DWORD grfKeyState, LPDWORD pdwEffect, 1.101 + uint32_t * aGeckoAction) 1.102 +{ 1.103 + // If a window is disabled or a modal window is on top of it 1.104 + // (which implies it is disabled), then we should not allow dropping. 1.105 + if (!mWidget->IsEnabled()) { 1.106 + *pdwEffect = DROPEFFECT_NONE; 1.107 + *aGeckoAction = nsIDragService::DRAGDROP_ACTION_NONE; 1.108 + return; 1.109 + } 1.110 + 1.111 + // If the user explicitly uses a modifier key, they want the associated action 1.112 + // Shift + Control -> LINK, Shift -> MOVE, Ctrl -> COPY 1.113 + DWORD desiredEffect = DROPEFFECT_NONE; 1.114 + if ((grfKeyState & MK_CONTROL) && (grfKeyState & MK_SHIFT)) { 1.115 + desiredEffect = DROPEFFECT_LINK; 1.116 + } else if (grfKeyState & MK_SHIFT) { 1.117 + desiredEffect = DROPEFFECT_MOVE; 1.118 + } else if (grfKeyState & MK_CONTROL) { 1.119 + desiredEffect = DROPEFFECT_COPY; 1.120 + } 1.121 + 1.122 + // Determine the desired effect from what is allowed and preferred. 1.123 + if (!(desiredEffect &= mEffectsAllowed)) { 1.124 + // No modifier key effect is set which is also allowed, check 1.125 + // the preference of the data. 1.126 + desiredEffect = mEffectsPreferred & mEffectsAllowed; 1.127 + if (!desiredEffect) { 1.128 + // No preference is set, so just fall back to the allowed effect itself 1.129 + desiredEffect = mEffectsAllowed; 1.130 + } 1.131 + } 1.132 + 1.133 + // Otherwise we should specify the first available effect 1.134 + // from MOVE, COPY, or LINK. 1.135 + if (desiredEffect & DROPEFFECT_MOVE) { 1.136 + *pdwEffect = DROPEFFECT_MOVE; 1.137 + *aGeckoAction = nsIDragService::DRAGDROP_ACTION_MOVE; 1.138 + } else if (desiredEffect & DROPEFFECT_COPY) { 1.139 + *pdwEffect = DROPEFFECT_COPY; 1.140 + *aGeckoAction = nsIDragService::DRAGDROP_ACTION_COPY; 1.141 + } else if (desiredEffect & DROPEFFECT_LINK) { 1.142 + *pdwEffect = DROPEFFECT_LINK; 1.143 + *aGeckoAction = nsIDragService::DRAGDROP_ACTION_LINK; 1.144 + } else { 1.145 + *pdwEffect = DROPEFFECT_NONE; 1.146 + *aGeckoAction = nsIDragService::DRAGDROP_ACTION_NONE; 1.147 + } 1.148 +} 1.149 + 1.150 +inline 1.151 +bool 1.152 +IsKeyDown(char key) 1.153 +{ 1.154 + return GetKeyState(key) < 0; 1.155 +} 1.156 + 1.157 +void 1.158 +nsNativeDragTarget::DispatchDragDropEvent(uint32_t aEventType, POINTL aPT) 1.159 +{ 1.160 + nsEventStatus status; 1.161 + WidgetDragEvent event(true, aEventType, mWidget); 1.162 + 1.163 + nsWindow * win = static_cast<nsWindow *>(mWidget); 1.164 + win->InitEvent(event); 1.165 + POINT cpos; 1.166 + 1.167 + cpos.x = aPT.x; 1.168 + cpos.y = aPT.y; 1.169 + 1.170 + if (mHWnd != nullptr) { 1.171 + ::ScreenToClient(mHWnd, &cpos); 1.172 + event.refPoint.x = cpos.x; 1.173 + event.refPoint.y = cpos.y; 1.174 + } else { 1.175 + event.refPoint.x = 0; 1.176 + event.refPoint.y = 0; 1.177 + } 1.178 + 1.179 + ModifierKeyState modifierKeyState; 1.180 + modifierKeyState.InitInputEvent(event); 1.181 + 1.182 + event.inputSource = static_cast<nsBaseDragService*>(mDragService)->GetInputSource(); 1.183 + 1.184 + mWidget->DispatchEvent(&event, status); 1.185 +} 1.186 + 1.187 +void 1.188 +nsNativeDragTarget::ProcessDrag(uint32_t aEventType, 1.189 + DWORD grfKeyState, 1.190 + POINTL ptl, 1.191 + DWORD* pdwEffect) 1.192 +{ 1.193 + // Before dispatching the event make sure we have the correct drop action set 1.194 + uint32_t geckoAction; 1.195 + GetGeckoDragAction(grfKeyState, pdwEffect, &geckoAction); 1.196 + 1.197 + // Set the current action into the Gecko specific type 1.198 + nsCOMPtr<nsIDragSession> currSession; 1.199 + mDragService->GetCurrentSession(getter_AddRefs(currSession)); 1.200 + if (!currSession) { 1.201 + return; 1.202 + } 1.203 + 1.204 + currSession->SetDragAction(geckoAction); 1.205 + 1.206 + // Dispatch the event into Gecko 1.207 + DispatchDragDropEvent(aEventType, ptl); 1.208 + 1.209 + if (aEventType != NS_DRAGDROP_DROP) { 1.210 + // Get the cached drag effect from the drag service, the data member should 1.211 + // have been set by whoever handled the WidgetGUIEvent or nsIDOMEvent on 1.212 + // drags. 1.213 + bool canDrop; 1.214 + currSession->GetCanDrop(&canDrop); 1.215 + if (!canDrop) { 1.216 + *pdwEffect = DROPEFFECT_NONE; 1.217 + } 1.218 + } 1.219 + 1.220 + // Clear the cached value 1.221 + currSession->SetCanDrop(false); 1.222 +} 1.223 + 1.224 +// IDropTarget methods 1.225 +STDMETHODIMP 1.226 +nsNativeDragTarget::DragEnter(LPDATAOBJECT pIDataSource, 1.227 + DWORD grfKeyState, 1.228 + POINTL ptl, 1.229 + DWORD* pdwEffect) 1.230 +{ 1.231 + if (!mDragService) { 1.232 + return E_FAIL; 1.233 + } 1.234 + 1.235 + mEffectsAllowed = *pdwEffect; 1.236 + AddLinkSupportIfCanBeGenerated(pIDataSource); 1.237 + 1.238 + // Drag and drop image helper 1.239 + if (GetDropTargetHelper()) { 1.240 + POINT pt = { ptl.x, ptl.y }; 1.241 + GetDropTargetHelper()->DragEnter(mHWnd, pIDataSource, &pt, *pdwEffect); 1.242 + } 1.243 + 1.244 + // save a ref to this, in case the window is destroyed underneath us 1.245 + NS_ASSERTION(!mTookOwnRef, "own ref already taken!"); 1.246 + this->AddRef(); 1.247 + mTookOwnRef = true; 1.248 + 1.249 + // tell the drag service about this drag (it may have come from an 1.250 + // outside app). 1.251 + mDragService->StartDragSession(); 1.252 + 1.253 + void* tempOutData = nullptr; 1.254 + uint32_t tempDataLen = 0; 1.255 + nsresult loadResult = nsClipboard::GetNativeDataOffClipboard( 1.256 + pIDataSource, 0, ::RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT), nullptr, &tempOutData, &tempDataLen); 1.257 + if (NS_SUCCEEDED(loadResult) && tempOutData) { 1.258 + mEffectsPreferred = *((DWORD*)tempOutData); 1.259 + nsMemory::Free(tempOutData); 1.260 + } else { 1.261 + // We have no preference if we can't obtain it 1.262 + mEffectsPreferred = DROPEFFECT_NONE; 1.263 + } 1.264 + 1.265 + // Set the native data object into drag service 1.266 + // 1.267 + // This cast is ok because in the constructor we created a 1.268 + // the actual implementation we wanted, so we know this is 1.269 + // a nsDragService. It should be a private interface, though. 1.270 + nsDragService * winDragService = 1.271 + static_cast<nsDragService *>(mDragService); 1.272 + winDragService->SetIDataObject(pIDataSource); 1.273 + 1.274 + // Now process the native drag state and then dispatch the event 1.275 + ProcessDrag(NS_DRAGDROP_ENTER, grfKeyState, ptl, pdwEffect); 1.276 + 1.277 + return S_OK; 1.278 +} 1.279 + 1.280 +void 1.281 +nsNativeDragTarget::AddLinkSupportIfCanBeGenerated(LPDATAOBJECT aIDataSource) 1.282 +{ 1.283 + // If we don't have a link effect, but we can generate one, fix the 1.284 + // drop effect to include it. 1.285 + if (!(mEffectsAllowed & DROPEFFECT_LINK) && aIDataSource) { 1.286 + if (S_OK == ::OleQueryLinkFromData(aIDataSource)) { 1.287 + mEffectsAllowed |= DROPEFFECT_LINK; 1.288 + } 1.289 + } 1.290 +} 1.291 + 1.292 +STDMETHODIMP 1.293 +nsNativeDragTarget::DragOver(DWORD grfKeyState, 1.294 + POINTL ptl, 1.295 + LPDWORD pdwEffect) 1.296 +{ 1.297 + if (!mDragService) { 1.298 + return E_FAIL; 1.299 + } 1.300 + 1.301 + // If a LINK effect could be generated previously from a DragEnter(), 1.302 + // then we should include it as an allowed effect. 1.303 + mEffectsAllowed = (*pdwEffect) | (mEffectsAllowed & DROPEFFECT_LINK); 1.304 + 1.305 + nsCOMPtr<nsIDragSession> currentDragSession; 1.306 + mDragService->GetCurrentSession(getter_AddRefs(currentDragSession)); 1.307 + if (!currentDragSession) { 1.308 + return S_OK; // Drag was canceled. 1.309 + } 1.310 + 1.311 + // without the AddRef() |this| can get destroyed in an event handler 1.312 + this->AddRef(); 1.313 + 1.314 + // Drag and drop image helper 1.315 + if (GetDropTargetHelper()) { 1.316 + POINT pt = { ptl.x, ptl.y }; 1.317 + GetDropTargetHelper()->DragOver(&pt, *pdwEffect); 1.318 + } 1.319 + 1.320 + mDragService->FireDragEventAtSource(NS_DRAGDROP_DRAG); 1.321 + // Now process the native drag state and then dispatch the event 1.322 + ProcessDrag(NS_DRAGDROP_OVER, grfKeyState, ptl, pdwEffect); 1.323 + 1.324 + this->Release(); 1.325 + 1.326 + return S_OK; 1.327 +} 1.328 + 1.329 +STDMETHODIMP 1.330 +nsNativeDragTarget::DragLeave() 1.331 +{ 1.332 + if (!mDragService) { 1.333 + return E_FAIL; 1.334 + } 1.335 + 1.336 + // Drag and drop image helper 1.337 + if (GetDropTargetHelper()) { 1.338 + GetDropTargetHelper()->DragLeave(); 1.339 + } 1.340 + 1.341 + // dispatch the event into Gecko 1.342 + DispatchDragDropEvent(NS_DRAGDROP_EXIT, gDragLastPoint); 1.343 + 1.344 + nsCOMPtr<nsIDragSession> currentDragSession; 1.345 + mDragService->GetCurrentSession(getter_AddRefs(currentDragSession)); 1.346 + 1.347 + if (currentDragSession) { 1.348 + nsCOMPtr<nsIDOMNode> sourceNode; 1.349 + currentDragSession->GetSourceNode(getter_AddRefs(sourceNode)); 1.350 + 1.351 + if (!sourceNode) { 1.352 + // We're leaving a window while doing a drag that was 1.353 + // initiated in a different app. End the drag session, since 1.354 + // we're done with it for now (until the user drags back into 1.355 + // mozilla). 1.356 + mDragService->EndDragSession(false); 1.357 + } 1.358 + } 1.359 + 1.360 + // release the ref that was taken in DragEnter 1.361 + NS_ASSERTION(mTookOwnRef, "want to release own ref, but not taken!"); 1.362 + if (mTookOwnRef) { 1.363 + this->Release(); 1.364 + mTookOwnRef = false; 1.365 + } 1.366 + 1.367 + return S_OK; 1.368 +} 1.369 + 1.370 +void 1.371 +nsNativeDragTarget::DragCancel() 1.372 +{ 1.373 + // Cancel the drag session if we did DragEnter. 1.374 + if (mTookOwnRef) { 1.375 + if (GetDropTargetHelper()) { 1.376 + GetDropTargetHelper()->DragLeave(); 1.377 + } 1.378 + if (mDragService) { 1.379 + mDragService->EndDragSession(false); 1.380 + } 1.381 + this->Release(); // matching the AddRef in DragEnter 1.382 + mTookOwnRef = false; 1.383 + } 1.384 +} 1.385 + 1.386 +STDMETHODIMP 1.387 +nsNativeDragTarget::Drop(LPDATAOBJECT pData, 1.388 + DWORD grfKeyState, 1.389 + POINTL aPT, 1.390 + LPDWORD pdwEffect) 1.391 +{ 1.392 + if (!mDragService) { 1.393 + return E_FAIL; 1.394 + } 1.395 + 1.396 + mEffectsAllowed = *pdwEffect; 1.397 + AddLinkSupportIfCanBeGenerated(pData); 1.398 + 1.399 + // Drag and drop image helper 1.400 + if (GetDropTargetHelper()) { 1.401 + POINT pt = { aPT.x, aPT.y }; 1.402 + GetDropTargetHelper()->Drop(pData, &pt, *pdwEffect); 1.403 + } 1.404 + 1.405 + // Set the native data object into the drag service 1.406 + // 1.407 + // This cast is ok because in the constructor we created a 1.408 + // the actual implementation we wanted, so we know this is 1.409 + // a nsDragService (but it should still be a private interface) 1.410 + nsDragService* winDragService = static_cast<nsDragService*>(mDragService); 1.411 + winDragService->SetIDataObject(pData); 1.412 + 1.413 + // NOTE: ProcessDrag spins the event loop which may destroy arbitrary objects. 1.414 + // We use strong refs to prevent it from destroying these: 1.415 + nsRefPtr<nsNativeDragTarget> kungFuDeathGrip = this; 1.416 + nsCOMPtr<nsIDragService> serv = mDragService; 1.417 + 1.418 + // Now process the native drag state and then dispatch the event 1.419 + ProcessDrag(NS_DRAGDROP_DROP, grfKeyState, aPT, pdwEffect); 1.420 + 1.421 + nsCOMPtr<nsIDragSession> currentDragSession; 1.422 + serv->GetCurrentSession(getter_AddRefs(currentDragSession)); 1.423 + if (!currentDragSession) { 1.424 + return S_OK; // DragCancel() was called. 1.425 + } 1.426 + 1.427 + // Let the win drag service know whether this session experienced 1.428 + // a drop event within the application. Drop will not oocur if the 1.429 + // drop landed outside the app. (used in tab tear off, bug 455884) 1.430 + winDragService->SetDroppedLocal(); 1.431 + 1.432 + // tell the drag service we're done with the session 1.433 + // Use GetMessagePos to get the position of the mouse at the last message 1.434 + // seen by the event loop. (Bug 489729) 1.435 + DWORD pos = ::GetMessagePos(); 1.436 + POINT cpos; 1.437 + cpos.x = GET_X_LPARAM(pos); 1.438 + cpos.y = GET_Y_LPARAM(pos); 1.439 + winDragService->SetDragEndPoint(nsIntPoint(cpos.x, cpos.y)); 1.440 + serv->EndDragSession(true); 1.441 + 1.442 + // release the ref that was taken in DragEnter 1.443 + NS_ASSERTION(mTookOwnRef, "want to release own ref, but not taken!"); 1.444 + if (mTookOwnRef) { 1.445 + this->Release(); 1.446 + mTookOwnRef = false; 1.447 + } 1.448 + 1.449 + return S_OK; 1.450 +} 1.451 + 1.452 +/** 1.453 + * By lazy loading mDropTargetHelper we save 50-70ms of startup time 1.454 + * which is ~5% of startup time. 1.455 +*/ 1.456 +IDropTargetHelper* 1.457 +nsNativeDragTarget::GetDropTargetHelper() 1.458 +{ 1.459 + if (!mDropTargetHelper) { 1.460 + CoCreateInstance(CLSID_DragDropHelper, nullptr, CLSCTX_INPROC_SERVER, 1.461 + IID_IDropTargetHelper, (LPVOID*)&mDropTargetHelper); 1.462 + } 1.463 + 1.464 + return mDropTargetHelper; 1.465 +}