NS_IMETHODIMP nsDragServiceProxy::InvokeDragSession(nsIDOMNode* aDOMNode, nsISupportsArray* aArrayTransferables, nsIScriptableRegion* aRegion, uint32_t aActionType) { nsresult rv = nsBaseDragService::InvokeDragSession(aDOMNode, aArrayTransferables, aRegion, aActionType); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsIDOMDocument> sourceDocument; aDOMNode->GetOwnerDocument(getter_AddRefs(sourceDocument)); nsCOMPtr<nsIDocument> doc = do_QueryInterface(sourceDocument); NS_ENSURE_STATE(doc->GetDocShell()); mozilla::dom::TabChild* child = mozilla::dom::TabChild::GetFrom(doc->GetDocShell()); NS_ENSURE_STATE(child); nsTArray<mozilla::dom::IPCDataTransfer> dataTransfers; nsContentUtils::TransferablesToIPCTransferables(aArrayTransferables, dataTransfers, child->Manager(), nullptr); if (mHasImage || mSelection) { nsIntRect dragRect; nsPresContext* pc; mozilla::RefPtr<mozilla::gfx::SourceSurface> surface; DrawDrag(mSourceNode, aRegion, mScreenX, mScreenY, &dragRect, &surface, &pc); if (surface) { mozilla::RefPtr<mozilla::gfx::DataSourceSurface> dataSurface = surface->GetDataSurface(); mozilla::gfx::IntSize size = dataSurface->GetSize(); size_t length; int32_t stride; mozilla::UniquePtr<char[]> surfaceData = nsContentUtils::GetSurfaceData(dataSurface, &length, &stride); nsDependentCString dragImage(surfaceData.get(), length); mozilla::unused << child->SendInvokeDragSession(dataTransfers, aActionType, dragImage, size.width, size.height, stride, static_cast<uint8_t>(dataSurface->GetFormat()), dragRect.x, dragRect.y); StartDragSession(); return NS_OK; } } mozilla::unused << child->SendInvokeDragSession(dataTransfers, aActionType, nsCString(), 0, 0, 0, 0, 0, 0); StartDragSession(); return NS_OK; }
//------------------------------------------------------------------------- NS_IMETHODIMP nsDragService::StartInvokingDragSession(IDataObject * aDataObj, PRUint32 aActionType) { // To do the drag we need to create an object that // implements the IDataObject interface (for OLE) nsNativeDragSource* nativeDragSource = new nsNativeDragSource(mDataTransfer); if (!nativeDragSource) return NS_ERROR_OUT_OF_MEMORY; NS_IF_RELEASE(mNativeDragSrc); mNativeDragSrc = (IDropSource *)nativeDragSource; mNativeDragSrc->AddRef(); // Now figure out what the native drag effect should be DWORD winDropRes; DWORD effects = DROPEFFECT_SCROLL; if (aActionType & DRAGDROP_ACTION_COPY) { effects |= DROPEFFECT_COPY; } if (aActionType & DRAGDROP_ACTION_MOVE) { effects |= DROPEFFECT_MOVE; } if (aActionType & DRAGDROP_ACTION_LINK) { effects |= DROPEFFECT_LINK; } // XXX not sure why we bother to cache this, it can change during // the drag mDragAction = aActionType; mSentLocalDropEvent = PR_FALSE; // Start dragging StartDragSession(); OpenDragPopup(); nsRefPtr<IAsyncOperation> pAsyncOp; // Offer to do an async drag if (SUCCEEDED(aDataObj->QueryInterface(IID_IAsyncOperation, getter_AddRefs(pAsyncOp)))) { pAsyncOp->SetAsyncMode(VARIANT_TRUE); } else { NS_NOTREACHED("When did our data object stop being async"); } // Call the native D&D method HRESULT res = ::DoDragDrop(aDataObj, mNativeDragSrc, effects, &winDropRes); // In cases where the drop operation completed outside the application, update // the source node's nsIDOMNSDataTransfer dropEffect value so it is up to date. if (!mSentLocalDropEvent) { PRUint32 dropResult; // Order is important, since multiple flags can be returned. if (winDropRes & DROPEFFECT_COPY) dropResult = DRAGDROP_ACTION_COPY; else if (winDropRes & DROPEFFECT_LINK) dropResult = DRAGDROP_ACTION_LINK; else if (winDropRes & DROPEFFECT_MOVE) dropResult = DRAGDROP_ACTION_MOVE; else dropResult = DRAGDROP_ACTION_NONE; nsCOMPtr<nsIDOMNSDataTransfer> dataTransfer = do_QueryInterface(mDataTransfer); if (dataTransfer) { if (res == DRAGDROP_S_DROP) // Success dataTransfer->SetDropEffectInt(dropResult); else dataTransfer->SetDropEffectInt(DRAGDROP_ACTION_NONE); } } mUserCancelled = nativeDragSource->UserCancelled(); // We're done dragging, get the cursor position and end the drag // Use GetMessagePos to get the position of the mouse at the last message // seen by the event loop. (Bug 489729) DWORD pos = ::GetMessagePos(); POINT cpos; cpos.x = GET_X_LPARAM(pos); cpos.y = GET_Y_LPARAM(pos); SetDragEndPoint(nsIntPoint(cpos.x, cpos.y)); EndDragSession(PR_TRUE); mDoingDrag = PR_FALSE; return DRAGDROP_S_DROP == res ? NS_OK : NS_ERROR_FAILURE; }
// // InvokeDragSession // // Do all the work to kick it off. // NS_IMETHODIMP nsDragService::InvokeDragSession (nsIDOMNode *aDOMNode, nsISupportsArray * aTransferableArray, nsIScriptableRegion * aDragRgn, PRUint32 aActionType) { #ifdef MOZ_WIDGET_COCOA nsGraphicsUtils::SetPortToKnownGoodPort(); GrafPtr port; GDHandle handle; ::GetGWorld(&port, &handle); if (!IsValidPort(port)) return NS_ERROR_FAILURE; #endif ::InitCursor(); nsresult rv = nsBaseDragService::InvokeDragSession(aDOMNode, aTransferableArray, aDragRgn, aActionType); NS_ENSURE_SUCCESS(rv, rv); DragReference theDragRef; OSErr result = ::NewDrag(&theDragRef); if ( result != noErr ) return NS_ERROR_FAILURE; mDragRef = theDragRef; #if DEBUG_DD printf("**** created drag ref %ld\n", theDragRef); #endif Rect frameRect = { 0, 0, 0, 0 }; RgnHandle theDragRgn = ::NewRgn(); ::RectRgn(theDragRgn, &frameRect); if ( mImageDraggingSupported ) { Point imgOffsetPt; imgOffsetPt.v = imgOffsetPt.h = 0; PRBool canUseRect = BuildDragRegion ( aDragRgn, aDOMNode, theDragRgn ); if ( canUseRect ) { // passing in null for image's PixMapHandle to SetDragImage() means use bits on screen ::SetDragImage (theDragRef, nsnull, theDragRgn, imgOffsetPt, kDragDarkerTranslucency); } } else BuildDragRegion ( aDragRgn, aDOMNode, theDragRgn ); // add the flavors from the transferables. Cache this array for the send data proc mDataItems = aTransferableArray; RegisterDragItemsAndFlavors(aTransferableArray, theDragRgn); // we have to synthesize the native event because we may be called from JavaScript // through XPConnect. In that case, we only have a DOM event and no way to // get to the native event. As a consequence, we just always fake it. EventRecord theEvent; theEvent.what = mouseDown; theEvent.message = 0L; theEvent.when = TickCount(); theEvent.modifiers = 0L; // since we don't have the original mouseDown location, just assume the drag // started in the middle of the frame. This prevents us from having the mouse // and the region we're dragging separated by a big gap (which could happen if // we used the current mouse position). I know this isn't exactly right, and you can // see it if you're paying attention, but who pays such close attention? Rect dragRect; ::GetRegionBounds(theDragRgn, &dragRect); theEvent.where.v = dragRect.top + ((dragRect.bottom - dragRect.top) / 2); theEvent.where.h = dragRect.left + ((dragRect.right - dragRect.left) / 2); // register drag send proc which will call us back when asked for the actual // flavor data (instead of placing it all into the drag manager) ::SetDragSendProc ( theDragRef, mDragSendDataUPP, this ); // start the drag. Be careful, mDragRef will be invalid AFTER this call (it is // reset by the dragTrackingHandler). StartDragSession(); ::TrackDrag ( theDragRef, &theEvent, theDragRgn ); #ifndef MOZ_WIDGET_COCOA EndDragSession(); #else // MOZ_WIDGET_COCOA if (mDoingDrag) { // An action proc inside of TrackDrag may have already ended the drag. EndDragSession(); } #endif // MOZ_WIDGET_COCOA // clean up after ourselves ::DisposeRgn ( theDragRgn ); result = ::DisposeDrag ( theDragRef ); #if DEBUG_DD printf("**** disposing drag ref %ld\n", theDragRef); #endif NS_ASSERTION ( result == noErr, "Error disposing drag" ); mDragRef = 0L; mDataItems = nsnull; return NS_OK; } // InvokeDragSession