Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / widget / windows / nsDragService.cpp
blob80f545d9a78c2b6c7091b52ebec7ddbb9ee51e28
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 <ole2.h>
7 #include <oleidl.h>
8 #include <shlobj.h>
9 #include <shlwapi.h>
11 // shellapi.h is needed to build with WIN32_LEAN_AND_MEAN
12 #include <shellapi.h>
14 #include "mozilla/RefPtr.h"
15 #include "nsDragService.h"
16 #include "nsITransferable.h"
17 #include "nsDataObj.h"
19 #include "nsWidgetsCID.h"
20 #include "nsNativeDragTarget.h"
21 #include "nsNativeDragSource.h"
22 #include "nsClipboard.h"
23 #include "mozilla/dom/Document.h"
24 #include "mozilla/dom/DocumentInlines.h"
25 #include "nsDataObjCollection.h"
27 #include "nsArrayUtils.h"
28 #include "nsString.h"
29 #include "nsEscape.h"
30 #include "nsIScreenManager.h"
31 #include "nsToolkit.h"
32 #include "nsCRT.h"
33 #include "nsDirectoryServiceDefs.h"
34 #include "nsUnicharUtils.h"
35 #include "nsRect.h"
36 #include "nsMathUtils.h"
37 #include "WinUtils.h"
38 #include "KeyboardLayout.h"
39 #include "gfxContext.h"
40 #include "mozilla/gfx/2D.h"
41 #include "mozilla/gfx/DataSurfaceHelpers.h"
42 #include "mozilla/gfx/Tools.h"
43 #include "mozilla/ScopeExit.h"
45 using namespace mozilla;
46 using namespace mozilla::gfx;
47 using namespace mozilla::widget;
49 //-------------------------------------------------------------------------
51 // DragService constructor
53 //-------------------------------------------------------------------------
54 nsDragService::nsDragService()
55 : mDataObject(nullptr), mSentLocalDropEvent(false) {}
57 //-------------------------------------------------------------------------
59 // DragService destructor
61 //-------------------------------------------------------------------------
62 nsDragService::~nsDragService() { NS_IF_RELEASE(mDataObject); }
64 bool nsDragService::CreateDragImage(nsINode* aDOMNode,
65 const Maybe<CSSIntRegion>& aRegion,
66 SHDRAGIMAGE* psdi) {
67 if (!psdi) return false;
69 memset(psdi, 0, sizeof(SHDRAGIMAGE));
70 if (!aDOMNode) return false;
72 // Prepare the drag image
73 LayoutDeviceIntRect dragRect;
74 RefPtr<SourceSurface> surface;
75 nsPresContext* pc;
76 DrawDrag(aDOMNode, aRegion, mScreenPosition, &dragRect, &surface, &pc);
77 if (!surface) return false;
79 uint32_t bmWidth = dragRect.Width(), bmHeight = dragRect.Height();
81 if (bmWidth == 0 || bmHeight == 0) return false;
83 psdi->crColorKey = CLR_NONE;
85 RefPtr<DataSourceSurface> dataSurface = Factory::CreateDataSourceSurface(
86 IntSize(bmWidth, bmHeight), SurfaceFormat::B8G8R8A8);
87 NS_ENSURE_TRUE(dataSurface, false);
89 DataSourceSurface::MappedSurface map;
90 if (!dataSurface->Map(DataSourceSurface::MapType::READ_WRITE, &map)) {
91 return false;
94 RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForData(
95 BackendType::CAIRO, map.mData, dataSurface->GetSize(), map.mStride,
96 dataSurface->GetFormat());
97 if (!dt) {
98 dataSurface->Unmap();
99 return false;
102 dt->DrawSurface(
103 surface,
104 Rect(0, 0, dataSurface->GetSize().width, dataSurface->GetSize().height),
105 Rect(0, 0, surface->GetSize().width, surface->GetSize().height),
106 DrawSurfaceOptions(), DrawOptions(1.0f, CompositionOp::OP_SOURCE));
107 dt->Flush();
109 BITMAPV5HEADER bmih;
110 memset((void*)&bmih, 0, sizeof(BITMAPV5HEADER));
111 bmih.bV5Size = sizeof(BITMAPV5HEADER);
112 bmih.bV5Width = bmWidth;
113 bmih.bV5Height = -(int32_t)bmHeight; // flip vertical
114 bmih.bV5Planes = 1;
115 bmih.bV5BitCount = 32;
116 bmih.bV5Compression = BI_BITFIELDS;
117 bmih.bV5RedMask = 0x00FF0000;
118 bmih.bV5GreenMask = 0x0000FF00;
119 bmih.bV5BlueMask = 0x000000FF;
120 bmih.bV5AlphaMask = 0xFF000000;
122 HDC hdcSrc = CreateCompatibleDC(nullptr);
123 void* lpBits = nullptr;
124 if (hdcSrc) {
125 psdi->hbmpDragImage =
126 ::CreateDIBSection(hdcSrc, (BITMAPINFO*)&bmih, DIB_RGB_COLORS,
127 (void**)&lpBits, nullptr, 0);
128 if (psdi->hbmpDragImage && lpBits) {
129 CopySurfaceDataToPackedArray(map.mData, static_cast<uint8_t*>(lpBits),
130 dataSurface->GetSize(), map.mStride,
131 BytesPerPixel(dataSurface->GetFormat()));
134 psdi->sizeDragImage.cx = bmWidth;
135 psdi->sizeDragImage.cy = bmHeight;
137 const auto screenPoint =
138 LayoutDeviceIntPoint::Round(mScreenPosition * pc->CSSToDevPixelScale());
139 psdi->ptOffset.x = screenPoint.x - dragRect.X();
140 psdi->ptOffset.y = screenPoint.y - dragRect.Y();
142 DeleteDC(hdcSrc);
145 dataSurface->Unmap();
147 return psdi->hbmpDragImage != nullptr;
150 //-------------------------------------------------------------------------
151 nsresult nsDragService::InvokeDragSessionImpl(
152 nsIArray* anArrayTransferables, const Maybe<CSSIntRegion>& aRegion,
153 uint32_t aActionType) {
154 // Try and get source URI of the items that are being dragged
155 nsIURI* uri = nullptr;
157 RefPtr<dom::Document> doc(mSourceDocument);
158 if (doc) {
159 uri = doc->GetDocumentURI();
162 uint32_t numItemsToDrag = 0;
163 nsresult rv = anArrayTransferables->GetLength(&numItemsToDrag);
164 if (!numItemsToDrag) return NS_ERROR_FAILURE;
166 // The clipboard class contains some static utility methods that we
167 // can use to create an IDataObject from the transferable
169 // if we're dragging more than one item, we need to create a
170 // "collection" object to fake out the OS. This collection contains
171 // one |IDataObject| for each transferable. If there is just the one
172 // (most cases), only pass around the native |IDataObject|.
173 RefPtr<IDataObject> itemToDrag;
174 if (numItemsToDrag > 1) {
175 nsDataObjCollection* dataObjCollection = new nsDataObjCollection();
176 if (!dataObjCollection) return NS_ERROR_OUT_OF_MEMORY;
177 itemToDrag = dataObjCollection;
178 for (uint32_t i = 0; i < numItemsToDrag; ++i) {
179 nsCOMPtr<nsITransferable> trans =
180 do_QueryElementAt(anArrayTransferables, i);
181 if (trans) {
182 RefPtr<IDataObject> dataObj;
183 rv = nsClipboard::CreateNativeDataObject(trans, getter_AddRefs(dataObj),
184 uri);
185 NS_ENSURE_SUCCESS(rv, rv);
186 // Add the flavors to the collection object too
187 rv = nsClipboard::SetupNativeDataObject(trans, dataObjCollection);
188 NS_ENSURE_SUCCESS(rv, rv);
190 dataObjCollection->AddDataObject(dataObj);
193 } // if dragging multiple items
194 else {
195 nsCOMPtr<nsITransferable> trans =
196 do_QueryElementAt(anArrayTransferables, 0);
197 if (trans) {
198 rv = nsClipboard::CreateNativeDataObject(trans,
199 getter_AddRefs(itemToDrag), uri);
200 NS_ENSURE_SUCCESS(rv, rv);
202 } // else dragging a single object
204 // Create a drag image if support is available
205 IDragSourceHelper* pdsh;
206 if (SUCCEEDED(CoCreateInstance(CLSID_DragDropHelper, nullptr,
207 CLSCTX_INPROC_SERVER, IID_IDragSourceHelper,
208 (void**)&pdsh))) {
209 SHDRAGIMAGE sdi;
210 if (CreateDragImage(mSourceNode, aRegion, &sdi)) {
211 if (FAILED(pdsh->InitializeFromBitmap(&sdi, itemToDrag)))
212 DeleteObject(sdi.hbmpDragImage);
214 pdsh->Release();
217 // Kick off the native drag session
218 return StartInvokingDragSession(itemToDrag, aActionType);
221 static HWND GetSourceWindow(dom::Document* aSourceDocument) {
222 if (!aSourceDocument) {
223 return nullptr;
226 auto* pc = aSourceDocument->GetPresContext();
227 if (!pc) {
228 return nullptr;
231 nsCOMPtr<nsIWidget> widget = pc->GetRootWidget();
232 if (!widget) {
233 return nullptr;
236 return (HWND)widget->GetNativeData(NS_NATIVE_WINDOW);
239 //-------------------------------------------------------------------------
240 nsresult nsDragService::StartInvokingDragSession(IDataObject* aDataObj,
241 uint32_t aActionType) {
242 // To do the drag we need to create an object that
243 // implements the IDataObject interface (for OLE)
244 RefPtr<nsNativeDragSource> nativeDragSrc =
245 new nsNativeDragSource(mDataTransfer);
247 // Now figure out what the native drag effect should be
248 DWORD winDropRes;
249 DWORD effects = DROPEFFECT_SCROLL;
250 if (aActionType & DRAGDROP_ACTION_COPY) {
251 effects |= DROPEFFECT_COPY;
253 if (aActionType & DRAGDROP_ACTION_MOVE) {
254 effects |= DROPEFFECT_MOVE;
256 if (aActionType & DRAGDROP_ACTION_LINK) {
257 effects |= DROPEFFECT_LINK;
260 // XXX not sure why we bother to cache this, it can change during
261 // the drag
262 mDragAction = aActionType;
263 mSentLocalDropEvent = false;
265 // Start dragging
266 StartDragSession();
267 OpenDragPopup();
269 RefPtr<IDataObjectAsyncCapability> pAsyncOp;
270 // Offer to do an async drag
271 if (SUCCEEDED(aDataObj->QueryInterface(IID_IDataObjectAsyncCapability,
272 getter_AddRefs(pAsyncOp)))) {
273 pAsyncOp->SetAsyncMode(VARIANT_TRUE);
274 } else {
275 MOZ_ASSERT_UNREACHABLE("When did our data object stop being async");
278 // Call the native D&D method
279 HRESULT res = ::DoDragDrop(aDataObj, nativeDragSrc, effects, &winDropRes);
281 // In cases where the drop operation completed outside the application,
282 // update the source node's DataTransfer dropEffect value so it is up to date.
283 if (!mSentLocalDropEvent) {
284 uint32_t dropResult;
285 // Order is important, since multiple flags can be returned.
286 if (winDropRes & DROPEFFECT_COPY)
287 dropResult = DRAGDROP_ACTION_COPY;
288 else if (winDropRes & DROPEFFECT_LINK)
289 dropResult = DRAGDROP_ACTION_LINK;
290 else if (winDropRes & DROPEFFECT_MOVE)
291 dropResult = DRAGDROP_ACTION_MOVE;
292 else
293 dropResult = DRAGDROP_ACTION_NONE;
295 if (mDataTransfer) {
296 if (res == DRAGDROP_S_DROP) // Success
297 mDataTransfer->SetDropEffectInt(dropResult);
298 else
299 mDataTransfer->SetDropEffectInt(DRAGDROP_ACTION_NONE);
303 mUserCancelled = nativeDragSrc->UserCancelled();
305 // We're done dragging, get the cursor position and end the drag
306 // Use GetMessagePos to get the position of the mouse at the last message
307 // seen by the event loop. (Bug 489729)
308 // Note that we must convert this from device pixels back to Windows logical
309 // pixels (bug 818927).
310 DWORD pos = ::GetMessagePos();
311 POINT cpos;
312 cpos.x = GET_X_LPARAM(pos);
313 cpos.y = GET_Y_LPARAM(pos);
314 if (auto wnd = GetSourceWindow(mSourceDocument)) {
315 // Convert from screen to client coordinates like nsWindow::InitEvent does.
316 ::ScreenToClient(wnd, &cpos);
318 SetDragEndPoint(LayoutDeviceIntPoint(cpos.x, cpos.y));
320 ModifierKeyState modifierKeyState;
321 EndDragSession(true, modifierKeyState.GetModifiers());
323 mDoingDrag = false;
325 return DRAGDROP_S_DROP == res ? NS_OK : NS_ERROR_FAILURE;
328 //-------------------------------------------------------------------------
329 // Make Sure we have the right kind of object
330 nsDataObjCollection* nsDragService::GetDataObjCollection(
331 IDataObject* aDataObj) {
332 nsDataObjCollection* dataObjCol = nullptr;
333 if (aDataObj) {
334 nsIDataObjCollection* dataObj;
335 if (aDataObj->QueryInterface(IID_IDataObjCollection, (void**)&dataObj) ==
336 S_OK) {
337 dataObjCol = static_cast<nsDataObjCollection*>(aDataObj);
338 dataObj->Release();
342 return dataObjCol;
345 //-------------------------------------------------------------------------
346 NS_IMETHODIMP
347 nsDragService::GetNumDropItems(uint32_t* aNumItems) {
348 if (!mDataObject) {
349 *aNumItems = 0;
350 return NS_OK;
353 if (IsCollectionObject(mDataObject)) {
354 nsDataObjCollection* dataObjCol = GetDataObjCollection(mDataObject);
355 // If the count cannot be determined just return 0.
356 // This can happen if we have collection data of type
357 // MULTI_MIME ("Mozilla/IDataObjectCollectionFormat") on the clipboard
358 // from another process but we can't obtain an IID_IDataObjCollection
359 // from this process.
360 *aNumItems = dataObjCol ? dataObjCol->GetNumDataObjects() : 0;
361 return NS_OK;
363 // Next check if we have a file drop. Return the number of files in
364 // the file drop as the number of items we have, pretending like we
365 // actually have > 1 drag item.
366 FORMATETC fe2;
367 SET_FORMATETC(fe2, CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL);
368 if (SUCCEEDED(mDataObject->QueryGetData(&fe2))) {
369 STGMEDIUM stm;
370 if (FAILED(mDataObject->GetData(&fe2, &stm))) {
371 *aNumItems = 1;
372 return NS_OK;
374 HDROP hdrop = static_cast<HDROP>(GlobalLock(stm.hGlobal));
375 MOZ_ASSERT(hdrop != NULL);
376 *aNumItems = ::DragQueryFileW(hdrop, 0xFFFFFFFF, nullptr, 0);
377 ::GlobalUnlock(stm.hGlobal);
378 ::ReleaseStgMedium(&stm);
379 // Data may be provided later, so assume we have 1 item
380 if (*aNumItems == 0) {
381 *aNumItems = 1;
383 return NS_OK;
385 // Next check if we have a virtual file drop.
386 SET_FORMATETC(fe2, nsClipboard::GetClipboardFileDescriptorFormatW(), 0,
387 DVASPECT_CONTENT, -1, TYMED_HGLOBAL);
388 STGMEDIUM stm;
390 if (SUCCEEDED(mDataObject->GetData(&fe2, &stm))) {
391 LPFILEGROUPDESCRIPTOR pDesc =
392 static_cast<LPFILEGROUPDESCRIPTOR>(GlobalLock(stm.hGlobal));
393 if (pDesc) {
394 *aNumItems = pDesc->cItems;
396 GlobalUnlock(stm.hGlobal);
397 ReleaseStgMedium(&stm);
398 return NS_OK;
400 *aNumItems = 1;
401 return NS_OK;
404 //-------------------------------------------------------------------------
405 NS_IMETHODIMP
406 nsDragService::GetData(nsITransferable* aTransferable, uint32_t anItem) {
407 // This typcially happens on a drop, the target would be asking
408 // for it's transferable to be filled in
409 // Use a static clipboard utility method for this
410 if (!mDataObject) return NS_ERROR_FAILURE;
412 nsresult dataFound = NS_ERROR_FAILURE;
414 if (IsCollectionObject(mDataObject)) {
415 // multiple items, use |anItem| as an index into our collection
416 nsDataObjCollection* dataObjCol = GetDataObjCollection(mDataObject);
417 uint32_t cnt = dataObjCol->GetNumDataObjects();
418 if (anItem < cnt) {
419 IDataObject* dataObj = dataObjCol->GetDataObjectAt(anItem);
420 dataFound = nsClipboard::GetDataFromDataObject(dataObj, 0, nullptr,
421 aTransferable);
422 } else
423 NS_WARNING("Index out of range!");
424 } else {
425 // If they are asking for item "0", we can just get it...
426 if (anItem == 0) {
427 dataFound = nsClipboard::GetDataFromDataObject(mDataObject, anItem,
428 nullptr, aTransferable);
429 } else {
430 // It better be a file drop, or else non-zero indexes are invalid!
431 FORMATETC fe2;
432 FORMATETC fe3;
433 SET_FORMATETC(fe2, CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL);
434 SET_FORMATETC(fe3, nsClipboard::GetClipboardFileDescriptorFormatW(), 0,
435 DVASPECT_CONTENT, -1, TYMED_HGLOBAL);
436 if (SUCCEEDED(mDataObject->QueryGetData(&fe2)) ||
437 SUCCEEDED(mDataObject->QueryGetData(&fe3)))
438 dataFound = nsClipboard::GetDataFromDataObject(mDataObject, anItem,
439 nullptr, aTransferable);
440 else
441 NS_WARNING(
442 "Reqesting non-zero index, but clipboard data is not a "
443 "collection!");
446 return dataFound;
449 //---------------------------------------------------------
450 NS_IMETHODIMP
451 nsDragService::SetIDataObject(IDataObject* aDataObj) {
452 // When the native drag starts the DragService gets
453 // the IDataObject that is being dragged
454 NS_IF_RELEASE(mDataObject);
455 mDataObject = aDataObj;
456 NS_IF_ADDREF(mDataObject);
458 if (MOZ_DRAGSERVICE_LOG_ENABLED()) {
459 MOZ_DRAGSERVICE_LOG("nsDragService::SetIDataObject (%p)", mDataObject);
460 IEnumFORMATETC* pEnum = nullptr;
461 if (mDataObject &&
462 S_OK == mDataObject->EnumFormatEtc(DATADIR_GET, &pEnum)) {
463 MOZ_DRAGSERVICE_LOG(" formats in DataObject:");
465 FORMATETC fEtc;
466 while (S_OK == pEnum->Next(1, &fEtc, nullptr)) {
467 nsAutoString format;
468 WinUtils::GetClipboardFormatAsString(fEtc.cfFormat, format);
469 MOZ_DRAGSERVICE_LOG(" FORMAT %s",
470 NS_ConvertUTF16toUTF8(format).get());
472 pEnum->Release();
476 return NS_OK;
479 //---------------------------------------------------------
480 void nsDragService::SetDroppedLocal() {
481 // Sent from the native drag handler, letting us know
482 // a drop occurred within the application vs. outside of it.
483 mSentLocalDropEvent = true;
484 return;
487 //-------------------------------------------------------------------------
488 NS_IMETHODIMP
489 nsDragService::IsDataFlavorSupported(const char* aDataFlavor, bool* _retval) {
490 if (!aDataFlavor || !mDataObject || !_retval) {
491 MOZ_DRAGSERVICE_LOG("%s: error", __PRETTY_FUNCTION__);
492 return NS_ERROR_FAILURE;
495 *_retval = false;
496 auto logging = MakeScopeExit([&] {
497 MOZ_DRAGSERVICE_LOG("IsDataFlavorSupported: %s is%s found", aDataFlavor,
498 *_retval ? "" : " not");
501 FORMATETC fe;
502 UINT format = 0;
504 if (IsCollectionObject(mDataObject)) {
505 // We know we have one of our special collection objects.
506 format = nsClipboard::GetFormat(aDataFlavor);
507 SET_FORMATETC(fe, format, 0, DVASPECT_CONTENT, -1,
508 TYMED_HGLOBAL | TYMED_FILE | TYMED_GDI);
510 // See if any one of the IDataObjects in the collection supports
511 // this data type
512 nsDataObjCollection* dataObjCol = GetDataObjCollection(mDataObject);
513 if (dataObjCol) {
514 uint32_t cnt = dataObjCol->GetNumDataObjects();
515 for (uint32_t i = 0; i < cnt; ++i) {
516 IDataObject* dataObj = dataObjCol->GetDataObjectAt(i);
517 if (S_OK == dataObj->QueryGetData(&fe)) {
518 *_retval = true; // found it!
522 return NS_OK;
525 // Ok, so we have a single object. Check to see if has the correct
526 // data type. Since this can come from an outside app, we also
527 // need to see if we need to perform text->unicode conversion if
528 // the client asked for unicode and it wasn't available.
529 format = nsClipboard::GetFormat(aDataFlavor);
530 SET_FORMATETC(fe, format, 0, DVASPECT_CONTENT, -1,
531 TYMED_HGLOBAL | TYMED_FILE | TYMED_GDI);
532 if (mDataObject->QueryGetData(&fe) == S_OK) {
533 *_retval = true; // found it!
534 return NS_OK;
537 // We haven't found the exact flavor the client asked for, but
538 // maybe we can still find it from something else that's in the
539 // data object.
540 if (strcmp(aDataFlavor, kTextMime) == 0) {
541 // If unicode wasn't there, it might exist as CF_TEXT, client asked
542 // for unicode and it wasn't present, check if we
543 // have CF_TEXT. We'll handle the actual data substitution in
544 // the data object.
545 SET_FORMATETC(fe, CF_TEXT, 0, DVASPECT_CONTENT, -1,
546 TYMED_HGLOBAL | TYMED_FILE | TYMED_GDI);
547 if (mDataObject->QueryGetData(&fe) == S_OK) {
548 *_retval = true; // found it!
550 return NS_OK;
553 if (strcmp(aDataFlavor, kURLMime) == 0) {
554 // client asked for a url and it wasn't present, but if we
555 // have a file, then we have a URL to give them (the path, or
556 // the internal URL if an InternetShortcut).
557 format = nsClipboard::GetFormat(kFileMime);
558 SET_FORMATETC(fe, format, 0, DVASPECT_CONTENT, -1,
559 TYMED_HGLOBAL | TYMED_FILE | TYMED_GDI);
560 if (mDataObject->QueryGetData(&fe) == S_OK) {
561 *_retval = true; // found it!
563 return NS_OK;
566 if (format == CF_HDROP) {
567 // Dragging a link from browsers creates both a URL and a FILE which is a
568 // *.url shortcut in the data object. The file is useful when dropping in
569 // Windows Explorer to create a internet shortcut. But when dropping in the
570 // browser, users do not expect to have this file. So do not try to look up
571 // virtal file if there is a URL in the data object.
572 format = nsClipboard::GetFormat(kURLMime);
573 SET_FORMATETC(fe, format, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL);
574 if (mDataObject->QueryGetData(&fe) == S_OK) {
575 return NS_OK;
578 // If the client wants a file, maybe we find a virtual file.
579 format = nsClipboard::GetClipboardFileDescriptorFormatW();
580 SET_FORMATETC(fe, format, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL);
581 if (mDataObject->QueryGetData(&fe) == S_OK) {
582 *_retval = true; // found it!
585 // XXX should we fall back to CFSTR_FILEDESCRIPTORA?
586 return NS_OK;
589 return NS_OK;
593 // IsCollectionObject
595 // Determine if this is a single |IDataObject| or one of our private
596 // collection objects. We know the difference because our collection
597 // object will respond to supporting the private |MULTI_MIME| format.
599 bool nsDragService::IsCollectionObject(IDataObject* inDataObj) {
600 bool isCollection = false;
602 // setup the format object to ask for the MULTI_MIME format. We only
603 // need to do this once
604 static UINT sFormat = 0;
605 static FORMATETC sFE;
606 if (!sFormat) {
607 sFormat = nsClipboard::GetFormat(MULTI_MIME);
608 SET_FORMATETC(sFE, sFormat, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL);
611 // ask the object if it supports it. If yes, we have a collection
612 // object
613 if (inDataObj->QueryGetData(&sFE) == S_OK) isCollection = true;
615 return isCollection;
617 } // IsCollectionObject
620 // EndDragSession
622 // Override the default to make sure that we release the data object
623 // when the drag ends. It seems that OLE doesn't like to let apps quit
624 // w/out crashing when we're still holding onto their data
626 NS_IMETHODIMP
627 nsDragService::EndDragSession(bool aDoneDrag, uint32_t aKeyModifiers) {
628 // Bug 100180: If we've got mouse events captured, make sure we release it -
629 // that way, if we happen to call EndDragSession before diving into a nested
630 // event loop, we can still respond to mouse events.
631 if (::GetCapture()) {
632 ::ReleaseCapture();
635 nsBaseDragService::EndDragSession(aDoneDrag, aKeyModifiers);
636 NS_IF_RELEASE(mDataObject);
638 return NS_OK;
641 NS_IMETHODIMP
642 nsDragService::UpdateDragImage(nsINode* aImage, int32_t aImageX,
643 int32_t aImageY) {
644 if (!mDataObject) {
645 return NS_OK;
648 nsBaseDragService::UpdateDragImage(aImage, aImageX, aImageY);
650 IDragSourceHelper* pdsh;
651 if (SUCCEEDED(CoCreateInstance(CLSID_DragDropHelper, nullptr,
652 CLSCTX_INPROC_SERVER, IID_IDragSourceHelper,
653 (void**)&pdsh))) {
654 SHDRAGIMAGE sdi;
655 if (CreateDragImage(mSourceNode, Nothing(), &sdi)) {
656 nsNativeDragTarget::DragImageChanged();
657 if (FAILED(pdsh->InitializeFromBitmap(&sdi, mDataObject)))
658 DeleteObject(sdi.hbmpDragImage);
660 pdsh->Release();
663 return NS_OK;