michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: // shellapi.h is needed to build with WIN32_LEAN_AND_MEAN michael@0: #include michael@0: michael@0: #include "nsDragService.h" michael@0: #include "nsITransferable.h" michael@0: #include "nsDataObj.h" michael@0: michael@0: #include "nsWidgetsCID.h" michael@0: #include "nsNativeDragTarget.h" michael@0: #include "nsNativeDragSource.h" michael@0: #include "nsClipboard.h" michael@0: #include "nsISupportsArray.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsDataObjCollection.h" michael@0: michael@0: #include "nsAutoPtr.h" michael@0: michael@0: #include "nsString.h" michael@0: #include "nsEscape.h" michael@0: #include "nsISupportsPrimitives.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsIURL.h" michael@0: #include "nsCWebBrowserPersist.h" michael@0: #include "nsToolkit.h" michael@0: #include "nsCRT.h" michael@0: #include "nsDirectoryServiceDefs.h" michael@0: #include "nsUnicharUtils.h" michael@0: #include "gfxContext.h" michael@0: #include "nsRect.h" michael@0: #include "nsMathUtils.h" michael@0: #include "gfxWindowsPlatform.h" michael@0: #include "mozilla/gfx/2D.h" michael@0: #include "mozilla/gfx/DataSurfaceHelpers.h" michael@0: #include "mozilla/gfx/Tools.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::gfx; michael@0: michael@0: //------------------------------------------------------------------------- michael@0: // michael@0: // DragService constructor michael@0: // michael@0: //------------------------------------------------------------------------- michael@0: nsDragService::nsDragService() michael@0: : mDataObject(nullptr), mSentLocalDropEvent(false) michael@0: { michael@0: } michael@0: michael@0: //------------------------------------------------------------------------- michael@0: // michael@0: // DragService destructor michael@0: // michael@0: //------------------------------------------------------------------------- michael@0: nsDragService::~nsDragService() michael@0: { michael@0: NS_IF_RELEASE(mDataObject); michael@0: } michael@0: michael@0: bool michael@0: nsDragService::CreateDragImage(nsIDOMNode *aDOMNode, michael@0: nsIScriptableRegion *aRegion, michael@0: SHDRAGIMAGE *psdi) michael@0: { michael@0: if (!psdi) michael@0: return false; michael@0: michael@0: memset(psdi, 0, sizeof(SHDRAGIMAGE)); michael@0: if (!aDOMNode) michael@0: return false; michael@0: michael@0: // Prepare the drag image michael@0: nsIntRect dragRect; michael@0: RefPtr surface; michael@0: nsPresContext* pc; michael@0: DrawDrag(aDOMNode, aRegion, michael@0: mScreenX, mScreenY, michael@0: &dragRect, &surface, &pc); michael@0: if (!surface) michael@0: return false; michael@0: michael@0: uint32_t bmWidth = dragRect.width, bmHeight = dragRect.height; michael@0: michael@0: if (bmWidth == 0 || bmHeight == 0) michael@0: return false; michael@0: michael@0: psdi->crColorKey = CLR_NONE; michael@0: michael@0: RefPtr dataSurface = michael@0: Factory::CreateDataSourceSurface(IntSize(bmWidth, bmHeight), michael@0: SurfaceFormat::B8G8R8A8); michael@0: NS_ENSURE_TRUE(dataSurface, false); michael@0: michael@0: DataSourceSurface::MappedSurface map; michael@0: if (!dataSurface->Map(DataSourceSurface::MapType::READ_WRITE, &map)) { michael@0: return false; michael@0: } michael@0: michael@0: RefPtr dt = michael@0: Factory::CreateDrawTargetForData(BackendType::CAIRO, michael@0: map.mData, michael@0: dataSurface->GetSize(), michael@0: map.mStride, michael@0: dataSurface->GetFormat()); michael@0: if (!dt) { michael@0: dataSurface->Unmap(); michael@0: return false; michael@0: } michael@0: michael@0: dt->DrawSurface(surface, michael@0: Rect(0, 0, dataSurface->GetSize().width, dataSurface->GetSize().height), michael@0: Rect(0, 0, surface->GetSize().width, surface->GetSize().height), michael@0: DrawSurfaceOptions(), michael@0: DrawOptions(1.0f, CompositionOp::OP_SOURCE)); michael@0: dt->Flush(); michael@0: michael@0: BITMAPV5HEADER bmih; michael@0: memset((void*)&bmih, 0, sizeof(BITMAPV5HEADER)); michael@0: bmih.bV5Size = sizeof(BITMAPV5HEADER); michael@0: bmih.bV5Width = bmWidth; michael@0: bmih.bV5Height = -(int32_t)bmHeight; // flip vertical michael@0: bmih.bV5Planes = 1; michael@0: bmih.bV5BitCount = 32; michael@0: bmih.bV5Compression = BI_BITFIELDS; michael@0: bmih.bV5RedMask = 0x00FF0000; michael@0: bmih.bV5GreenMask = 0x0000FF00; michael@0: bmih.bV5BlueMask = 0x000000FF; michael@0: bmih.bV5AlphaMask = 0xFF000000; michael@0: michael@0: HDC hdcSrc = CreateCompatibleDC(nullptr); michael@0: void *lpBits = nullptr; michael@0: if (hdcSrc) { michael@0: psdi->hbmpDragImage = michael@0: ::CreateDIBSection(hdcSrc, (BITMAPINFO*)&bmih, DIB_RGB_COLORS, michael@0: (void**)&lpBits, nullptr, 0); michael@0: if (psdi->hbmpDragImage && lpBits) { michael@0: CopySurfaceDataToPackedArray(map.mData, static_cast(lpBits), michael@0: dataSurface->GetSize(), map.mStride, michael@0: BytesPerPixel(dataSurface->GetFormat())); michael@0: } michael@0: michael@0: psdi->sizeDragImage.cx = bmWidth; michael@0: psdi->sizeDragImage.cy = bmHeight; michael@0: michael@0: // Mouse position in center michael@0: if (mScreenX == -1 || mScreenY == -1) { michael@0: psdi->ptOffset.x = (uint32_t)((float)bmWidth/2.0f); michael@0: psdi->ptOffset.y = (uint32_t)((float)bmHeight/2.0f); michael@0: } else { michael@0: int32_t sx = mScreenX, sy = mScreenY; michael@0: ConvertToUnscaledDevPixels(pc, &sx, &sy); michael@0: psdi->ptOffset.x = sx - dragRect.x; michael@0: psdi->ptOffset.y = sy - dragRect.y; michael@0: } michael@0: michael@0: DeleteDC(hdcSrc); michael@0: } michael@0: michael@0: dataSurface->Unmap(); michael@0: michael@0: return psdi->hbmpDragImage != nullptr; michael@0: } michael@0: michael@0: //------------------------------------------------------------------------- michael@0: NS_IMETHODIMP michael@0: nsDragService::InvokeDragSession(nsIDOMNode *aDOMNode, michael@0: nsISupportsArray *anArrayTransferables, michael@0: nsIScriptableRegion *aRegion, michael@0: uint32_t aActionType) michael@0: { michael@0: nsresult rv = nsBaseDragService::InvokeDragSession(aDOMNode, michael@0: anArrayTransferables, michael@0: aRegion, michael@0: aActionType); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Try and get source URI of the items that are being dragged michael@0: nsIURI *uri = nullptr; michael@0: michael@0: nsCOMPtr doc(do_QueryInterface(mSourceDocument)); michael@0: if (doc) { michael@0: uri = doc->GetDocumentURI(); michael@0: } michael@0: michael@0: uint32_t numItemsToDrag = 0; michael@0: rv = anArrayTransferables->Count(&numItemsToDrag); michael@0: if (!numItemsToDrag) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: // The clipboard class contains some static utility methods that we michael@0: // can use to create an IDataObject from the transferable michael@0: michael@0: // if we're dragging more than one item, we need to create a michael@0: // "collection" object to fake out the OS. This collection contains michael@0: // one |IDataObject| for each transferable. If there is just the one michael@0: // (most cases), only pass around the native |IDataObject|. michael@0: nsRefPtr itemToDrag; michael@0: if (numItemsToDrag > 1) { michael@0: nsDataObjCollection * dataObjCollection = new nsDataObjCollection(); michael@0: if (!dataObjCollection) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: itemToDrag = dataObjCollection; michael@0: for (uint32_t i=0; i supports; michael@0: anArrayTransferables->GetElementAt(i, getter_AddRefs(supports)); michael@0: nsCOMPtr trans(do_QueryInterface(supports)); michael@0: if (trans) { michael@0: nsRefPtr dataObj; michael@0: rv = nsClipboard::CreateNativeDataObject(trans, michael@0: getter_AddRefs(dataObj), uri); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: // Add the flavors to the collection object too michael@0: rv = nsClipboard::SetupNativeDataObject(trans, dataObjCollection); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: dataObjCollection->AddDataObject(dataObj); michael@0: } michael@0: } michael@0: } // if dragging multiple items michael@0: else { michael@0: nsCOMPtr supports; michael@0: anArrayTransferables->GetElementAt(0, getter_AddRefs(supports)); michael@0: nsCOMPtr trans(do_QueryInterface(supports)); michael@0: if (trans) { michael@0: rv = nsClipboard::CreateNativeDataObject(trans, michael@0: getter_AddRefs(itemToDrag), michael@0: uri); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: } // else dragging a single object michael@0: michael@0: // Create a drag image if support is available michael@0: IDragSourceHelper *pdsh; michael@0: if (SUCCEEDED(CoCreateInstance(CLSID_DragDropHelper, nullptr, michael@0: CLSCTX_INPROC_SERVER, michael@0: IID_IDragSourceHelper, (void**)&pdsh))) { michael@0: SHDRAGIMAGE sdi; michael@0: if (CreateDragImage(aDOMNode, aRegion, &sdi)) { michael@0: if (FAILED(pdsh->InitializeFromBitmap(&sdi, itemToDrag))) michael@0: DeleteObject(sdi.hbmpDragImage); michael@0: } michael@0: pdsh->Release(); michael@0: } michael@0: michael@0: // Kick off the native drag session michael@0: return StartInvokingDragSession(itemToDrag, aActionType); michael@0: } michael@0: michael@0: //------------------------------------------------------------------------- michael@0: NS_IMETHODIMP michael@0: nsDragService::StartInvokingDragSession(IDataObject * aDataObj, michael@0: uint32_t aActionType) michael@0: { michael@0: // To do the drag we need to create an object that michael@0: // implements the IDataObject interface (for OLE) michael@0: nsRefPtr nativeDragSrc = michael@0: new nsNativeDragSource(mDataTransfer); michael@0: michael@0: // Now figure out what the native drag effect should be michael@0: DWORD winDropRes; michael@0: DWORD effects = DROPEFFECT_SCROLL; michael@0: if (aActionType & DRAGDROP_ACTION_COPY) { michael@0: effects |= DROPEFFECT_COPY; michael@0: } michael@0: if (aActionType & DRAGDROP_ACTION_MOVE) { michael@0: effects |= DROPEFFECT_MOVE; michael@0: } michael@0: if (aActionType & DRAGDROP_ACTION_LINK) { michael@0: effects |= DROPEFFECT_LINK; michael@0: } michael@0: michael@0: // XXX not sure why we bother to cache this, it can change during michael@0: // the drag michael@0: mDragAction = aActionType; michael@0: mSentLocalDropEvent = false; michael@0: michael@0: // Start dragging michael@0: StartDragSession(); michael@0: OpenDragPopup(); michael@0: michael@0: nsRefPtr pAsyncOp; michael@0: // Offer to do an async drag michael@0: if (SUCCEEDED(aDataObj->QueryInterface(IID_IAsyncOperation, michael@0: getter_AddRefs(pAsyncOp)))) { michael@0: pAsyncOp->SetAsyncMode(VARIANT_TRUE); michael@0: } else { michael@0: NS_NOTREACHED("When did our data object stop being async"); michael@0: } michael@0: michael@0: // Call the native D&D method michael@0: HRESULT res = ::DoDragDrop(aDataObj, nativeDragSrc, effects, &winDropRes); michael@0: michael@0: // In cases where the drop operation completed outside the application, update michael@0: // the source node's nsIDOMDataTransfer dropEffect value so it is up to date. michael@0: if (!mSentLocalDropEvent) { michael@0: uint32_t dropResult; michael@0: // Order is important, since multiple flags can be returned. michael@0: if (winDropRes & DROPEFFECT_COPY) michael@0: dropResult = DRAGDROP_ACTION_COPY; michael@0: else if (winDropRes & DROPEFFECT_LINK) michael@0: dropResult = DRAGDROP_ACTION_LINK; michael@0: else if (winDropRes & DROPEFFECT_MOVE) michael@0: dropResult = DRAGDROP_ACTION_MOVE; michael@0: else michael@0: dropResult = DRAGDROP_ACTION_NONE; michael@0: michael@0: if (mDataTransfer) { michael@0: if (res == DRAGDROP_S_DROP) // Success michael@0: mDataTransfer->SetDropEffectInt(dropResult); michael@0: else michael@0: mDataTransfer->SetDropEffectInt(DRAGDROP_ACTION_NONE); michael@0: } michael@0: } michael@0: michael@0: mUserCancelled = nativeDragSrc->UserCancelled(); michael@0: michael@0: // We're done dragging, get the cursor position and end the drag michael@0: // Use GetMessagePos to get the position of the mouse at the last message michael@0: // seen by the event loop. (Bug 489729) michael@0: // Note that we must convert this from device pixels back to Windows logical michael@0: // pixels (bug 818927). michael@0: DWORD pos = ::GetMessagePos(); michael@0: FLOAT dpiScale = gfxWindowsPlatform::GetPlatform()->GetDPIScale(); michael@0: nsIntPoint logPos(NSToIntRound(GET_X_LPARAM(pos) / dpiScale), michael@0: NSToIntRound(GET_Y_LPARAM(pos) / dpiScale)); michael@0: SetDragEndPoint(logPos); michael@0: EndDragSession(true); michael@0: michael@0: mDoingDrag = false; michael@0: michael@0: return DRAGDROP_S_DROP == res ? NS_OK : NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: //------------------------------------------------------------------------- michael@0: // Make Sure we have the right kind of object michael@0: nsDataObjCollection* michael@0: nsDragService::GetDataObjCollection(IDataObject* aDataObj) michael@0: { michael@0: nsDataObjCollection * dataObjCol = nullptr; michael@0: if (aDataObj) { michael@0: nsIDataObjCollection* dataObj; michael@0: if (aDataObj->QueryInterface(IID_IDataObjCollection, michael@0: (void**)&dataObj) == S_OK) { michael@0: dataObjCol = static_cast(aDataObj); michael@0: dataObj->Release(); michael@0: } michael@0: } michael@0: michael@0: return dataObjCol; michael@0: } michael@0: michael@0: //------------------------------------------------------------------------- michael@0: NS_IMETHODIMP michael@0: nsDragService::GetNumDropItems(uint32_t * aNumItems) michael@0: { michael@0: if (!mDataObject) { michael@0: *aNumItems = 0; michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (IsCollectionObject(mDataObject)) { michael@0: nsDataObjCollection * dataObjCol = GetDataObjCollection(mDataObject); michael@0: if (dataObjCol) { michael@0: *aNumItems = dataObjCol->GetNumDataObjects(); michael@0: } michael@0: else { michael@0: // If the count cannot be determined just return 0. michael@0: // This can happen if we have collection data of type michael@0: // MULTI_MIME ("Mozilla/IDataObjectCollectionFormat") on the clipboard michael@0: // from another process but we can't obtain an IID_IDataObjCollection michael@0: // from this process. michael@0: *aNumItems = 0; michael@0: } michael@0: } michael@0: else { michael@0: // Next check if we have a file drop. Return the number of files in michael@0: // the file drop as the number of items we have, pretending like we michael@0: // actually have > 1 drag item. michael@0: FORMATETC fe2; michael@0: SET_FORMATETC(fe2, CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL); michael@0: if (mDataObject->QueryGetData(&fe2) == S_OK) { michael@0: STGMEDIUM stm; michael@0: if (mDataObject->GetData(&fe2, &stm) == S_OK) { michael@0: HDROP hdrop = (HDROP)GlobalLock(stm.hGlobal); michael@0: *aNumItems = ::DragQueryFileW(hdrop, 0xFFFFFFFF, nullptr, 0); michael@0: ::GlobalUnlock(stm.hGlobal); michael@0: ::ReleaseStgMedium(&stm); michael@0: // Data may be provided later, so assume we have 1 item michael@0: if (*aNumItems == 0) michael@0: *aNumItems = 1; michael@0: } michael@0: else michael@0: *aNumItems = 1; michael@0: } michael@0: else michael@0: *aNumItems = 1; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //------------------------------------------------------------------------- michael@0: NS_IMETHODIMP michael@0: nsDragService::GetData(nsITransferable * aTransferable, uint32_t anItem) michael@0: { michael@0: // This typcially happens on a drop, the target would be asking michael@0: // for it's transferable to be filled in michael@0: // Use a static clipboard utility method for this michael@0: if (!mDataObject) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsresult dataFound = NS_ERROR_FAILURE; michael@0: michael@0: if (IsCollectionObject(mDataObject)) { michael@0: // multiple items, use |anItem| as an index into our collection michael@0: nsDataObjCollection * dataObjCol = GetDataObjCollection(mDataObject); michael@0: uint32_t cnt = dataObjCol->GetNumDataObjects(); michael@0: if (anItem >= 0 && anItem < cnt) { michael@0: IDataObject * dataObj = dataObjCol->GetDataObjectAt(anItem); michael@0: dataFound = nsClipboard::GetDataFromDataObject(dataObj, 0, nullptr, michael@0: aTransferable); michael@0: } michael@0: else michael@0: NS_WARNING("Index out of range!"); michael@0: } michael@0: else { michael@0: // If they are asking for item "0", we can just get it... michael@0: if (anItem == 0) { michael@0: dataFound = nsClipboard::GetDataFromDataObject(mDataObject, anItem, michael@0: nullptr, aTransferable); michael@0: } else { michael@0: // It better be a file drop, or else non-zero indexes are invalid! michael@0: FORMATETC fe2; michael@0: SET_FORMATETC(fe2, CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL); michael@0: if (mDataObject->QueryGetData(&fe2) == S_OK) michael@0: dataFound = nsClipboard::GetDataFromDataObject(mDataObject, anItem, michael@0: nullptr, aTransferable); michael@0: else michael@0: NS_WARNING("Reqesting non-zero index, but clipboard data is not a collection!"); michael@0: } michael@0: } michael@0: return dataFound; michael@0: } michael@0: michael@0: //--------------------------------------------------------- michael@0: NS_IMETHODIMP michael@0: nsDragService::SetIDataObject(IDataObject * aDataObj) michael@0: { michael@0: // When the native drag starts the DragService gets michael@0: // the IDataObject that is being dragged michael@0: NS_IF_RELEASE(mDataObject); michael@0: mDataObject = aDataObj; michael@0: NS_IF_ADDREF(mDataObject); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //--------------------------------------------------------- michael@0: void michael@0: nsDragService::SetDroppedLocal() michael@0: { michael@0: // Sent from the native drag handler, letting us know michael@0: // a drop occurred within the application vs. outside of it. michael@0: mSentLocalDropEvent = true; michael@0: return; michael@0: } michael@0: michael@0: //------------------------------------------------------------------------- michael@0: NS_IMETHODIMP michael@0: nsDragService::IsDataFlavorSupported(const char *aDataFlavor, bool *_retval) michael@0: { michael@0: if (!aDataFlavor || !mDataObject || !_retval) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: #ifdef DEBUG michael@0: if (strcmp(aDataFlavor, kTextMime) == 0) michael@0: NS_WARNING("DO NOT USE THE text/plain DATA FLAVOR ANY MORE. USE text/unicode INSTEAD"); michael@0: #endif michael@0: michael@0: *_retval = false; michael@0: michael@0: FORMATETC fe; michael@0: UINT format = 0; michael@0: michael@0: if (IsCollectionObject(mDataObject)) { michael@0: // We know we have one of our special collection objects. michael@0: format = nsClipboard::GetFormat(aDataFlavor); michael@0: SET_FORMATETC(fe, format, 0, DVASPECT_CONTENT, -1, michael@0: TYMED_HGLOBAL | TYMED_FILE | TYMED_GDI); michael@0: michael@0: // See if any one of the IDataObjects in the collection supports michael@0: // this data type michael@0: nsDataObjCollection* dataObjCol = GetDataObjCollection(mDataObject); michael@0: if (dataObjCol) { michael@0: uint32_t cnt = dataObjCol->GetNumDataObjects(); michael@0: for (uint32_t i=0;iGetDataObjectAt(i); michael@0: if (S_OK == dataObj->QueryGetData(&fe)) michael@0: *_retval = true; // found it! michael@0: } michael@0: } michael@0: } // if special collection object michael@0: else { michael@0: // Ok, so we have a single object. Check to see if has the correct michael@0: // data type. Since this can come from an outside app, we also michael@0: // need to see if we need to perform text->unicode conversion if michael@0: // the client asked for unicode and it wasn't available. michael@0: format = nsClipboard::GetFormat(aDataFlavor); michael@0: SET_FORMATETC(fe, format, 0, DVASPECT_CONTENT, -1, michael@0: TYMED_HGLOBAL | TYMED_FILE | TYMED_GDI); michael@0: if (mDataObject->QueryGetData(&fe) == S_OK) michael@0: *_retval = true; // found it! michael@0: else { michael@0: // We haven't found the exact flavor the client asked for, but michael@0: // maybe we can still find it from something else that's on the michael@0: // clipboard michael@0: if (strcmp(aDataFlavor, kUnicodeMime) == 0) { michael@0: // client asked for unicode and it wasn't present, check if we michael@0: // have CF_TEXT. We'll handle the actual data substitution in michael@0: // the data object. michael@0: format = nsClipboard::GetFormat(kTextMime); michael@0: SET_FORMATETC(fe, format, 0, DVASPECT_CONTENT, -1, michael@0: TYMED_HGLOBAL | TYMED_FILE | TYMED_GDI); michael@0: if (mDataObject->QueryGetData(&fe) == S_OK) michael@0: *_retval = true; // found it! michael@0: } michael@0: else if (strcmp(aDataFlavor, kURLMime) == 0) { michael@0: // client asked for a url and it wasn't present, but if we michael@0: // have a file, then we have a URL to give them (the path, or michael@0: // the internal URL if an InternetShortcut). michael@0: format = nsClipboard::GetFormat(kFileMime); michael@0: SET_FORMATETC(fe, format, 0, DVASPECT_CONTENT, -1, michael@0: TYMED_HGLOBAL | TYMED_FILE | TYMED_GDI); michael@0: if (mDataObject->QueryGetData(&fe) == S_OK) michael@0: *_retval = true; // found it! michael@0: } michael@0: } // else try again michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: // michael@0: // IsCollectionObject michael@0: // michael@0: // Determine if this is a single |IDataObject| or one of our private michael@0: // collection objects. We know the difference because our collection michael@0: // object will respond to supporting the private |MULTI_MIME| format. michael@0: // michael@0: bool michael@0: nsDragService::IsCollectionObject(IDataObject* inDataObj) michael@0: { michael@0: bool isCollection = false; michael@0: michael@0: // setup the format object to ask for the MULTI_MIME format. We only michael@0: // need to do this once michael@0: static UINT sFormat = 0; michael@0: static FORMATETC sFE; michael@0: if (!sFormat) { michael@0: sFormat = nsClipboard::GetFormat(MULTI_MIME); michael@0: SET_FORMATETC(sFE, sFormat, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL); michael@0: } michael@0: michael@0: // ask the object if it supports it. If yes, we have a collection michael@0: // object michael@0: if (inDataObj->QueryGetData(&sFE) == S_OK) michael@0: isCollection = true; michael@0: michael@0: return isCollection; michael@0: michael@0: } // IsCollectionObject michael@0: michael@0: michael@0: // michael@0: // EndDragSession michael@0: // michael@0: // Override the default to make sure that we release the data object michael@0: // when the drag ends. It seems that OLE doesn't like to let apps quit michael@0: // w/out crashing when we're still holding onto their data michael@0: // michael@0: NS_IMETHODIMP michael@0: nsDragService::EndDragSession(bool aDoneDrag) michael@0: { michael@0: // Bug 100180: If we've got mouse events captured, make sure we release it - michael@0: // that way, if we happen to call EndDragSession before diving into a nested michael@0: // event loop, we can still respond to mouse events. michael@0: if (::GetCapture()) { michael@0: ::ReleaseCapture(); michael@0: } michael@0: michael@0: nsBaseDragService::EndDragSession(aDoneDrag); michael@0: NS_IF_RELEASE(mDataObject); michael@0: michael@0: return NS_OK; michael@0: }