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 "nsBaseDragService.h" michael@0: #include "nsITransferable.h" michael@0: michael@0: #include "nsIServiceManager.h" michael@0: #include "nsITransferable.h" michael@0: #include "nsISupportsArray.h" michael@0: #include "nsSize.h" michael@0: #include "nsXPCOM.h" michael@0: #include "nsISupportsPrimitives.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsIInterfaceRequestorUtils.h" michael@0: #include "nsIFrame.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsIContent.h" michael@0: #include "nsIPresShell.h" michael@0: #include "nsViewManager.h" michael@0: #include "nsIDOMNode.h" michael@0: #include "nsIDOMDragEvent.h" michael@0: #include "nsISelection.h" michael@0: #include "nsISelectionPrivate.h" michael@0: #include "nsPresContext.h" michael@0: #include "nsIDOMDataTransfer.h" michael@0: #include "nsIImageLoadingContent.h" michael@0: #include "imgIContainer.h" michael@0: #include "imgIRequest.h" michael@0: #include "nsRegion.h" michael@0: #include "nsXULPopupManager.h" michael@0: #include "nsMenuPopupFrame.h" michael@0: #include "mozilla/MouseEvents.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/gfx/2D.h" michael@0: michael@0: #include "gfxContext.h" michael@0: #include "gfxPlatform.h" michael@0: #include michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::gfx; michael@0: using namespace mozilla::dom; michael@0: michael@0: #define DRAGIMAGES_PREF "nglayout.enable_drag_images" michael@0: michael@0: nsBaseDragService::nsBaseDragService() michael@0: : mCanDrop(false), mOnlyChromeDrop(false), mDoingDrag(false), michael@0: mHasImage(false), mUserCancelled(false), michael@0: mDragAction(DRAGDROP_ACTION_NONE), mTargetSize(0,0), michael@0: mImageX(0), mImageY(0), mScreenX(-1), mScreenY(-1), mSuppressLevel(0), michael@0: mInputSource(nsIDOMMouseEvent::MOZ_SOURCE_MOUSE) michael@0: { michael@0: } michael@0: michael@0: nsBaseDragService::~nsBaseDragService() michael@0: { michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsBaseDragService, nsIDragService, nsIDragSession) michael@0: michael@0: //--------------------------------------------------------- michael@0: NS_IMETHODIMP michael@0: nsBaseDragService::SetCanDrop(bool aCanDrop) michael@0: { michael@0: mCanDrop = aCanDrop; michael@0: return NS_OK; michael@0: } michael@0: michael@0: //--------------------------------------------------------- michael@0: NS_IMETHODIMP michael@0: nsBaseDragService::GetCanDrop(bool * aCanDrop) michael@0: { michael@0: *aCanDrop = mCanDrop; michael@0: return NS_OK; michael@0: } michael@0: //--------------------------------------------------------- michael@0: NS_IMETHODIMP michael@0: nsBaseDragService::SetOnlyChromeDrop(bool aOnlyChrome) michael@0: { michael@0: mOnlyChromeDrop = aOnlyChrome; michael@0: return NS_OK; michael@0: } michael@0: michael@0: //--------------------------------------------------------- michael@0: NS_IMETHODIMP michael@0: nsBaseDragService::GetOnlyChromeDrop(bool* aOnlyChrome) michael@0: { michael@0: *aOnlyChrome = mOnlyChromeDrop; michael@0: return NS_OK; michael@0: } michael@0: michael@0: //--------------------------------------------------------- michael@0: NS_IMETHODIMP michael@0: nsBaseDragService::SetDragAction(uint32_t anAction) michael@0: { michael@0: mDragAction = anAction; michael@0: return NS_OK; michael@0: } michael@0: michael@0: //--------------------------------------------------------- michael@0: NS_IMETHODIMP michael@0: nsBaseDragService::GetDragAction(uint32_t * anAction) michael@0: { michael@0: *anAction = mDragAction; michael@0: return NS_OK; michael@0: } michael@0: michael@0: //--------------------------------------------------------- michael@0: NS_IMETHODIMP michael@0: nsBaseDragService::SetTargetSize(nsSize aDragTargetSize) michael@0: { michael@0: mTargetSize = aDragTargetSize; michael@0: return NS_OK; michael@0: } michael@0: michael@0: //--------------------------------------------------------- michael@0: NS_IMETHODIMP michael@0: nsBaseDragService::GetTargetSize(nsSize * aDragTargetSize) michael@0: { michael@0: *aDragTargetSize = mTargetSize; michael@0: return NS_OK; michael@0: } michael@0: michael@0: //------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseDragService::GetNumDropItems(uint32_t * aNumItems) michael@0: { michael@0: *aNumItems = 0; michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: michael@0: // michael@0: // GetSourceDocument michael@0: // michael@0: // Returns the DOM document where the drag was initiated. This will be michael@0: // nullptr if the drag began outside of our application. michael@0: // michael@0: NS_IMETHODIMP michael@0: nsBaseDragService::GetSourceDocument(nsIDOMDocument** aSourceDocument) michael@0: { michael@0: *aSourceDocument = mSourceDocument.get(); michael@0: NS_IF_ADDREF(*aSourceDocument); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // michael@0: // GetSourceNode michael@0: // michael@0: // Returns the DOM node where the drag was initiated. This will be michael@0: // nullptr if the drag began outside of our application. michael@0: // michael@0: NS_IMETHODIMP michael@0: nsBaseDragService::GetSourceNode(nsIDOMNode** aSourceNode) michael@0: { michael@0: *aSourceNode = mSourceNode.get(); michael@0: NS_IF_ADDREF(*aSourceNode); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: //------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseDragService::GetData(nsITransferable * aTransferable, michael@0: uint32_t aItemIndex) michael@0: { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: //------------------------------------------------------------------------- michael@0: NS_IMETHODIMP michael@0: nsBaseDragService::IsDataFlavorSupported(const char *aDataFlavor, michael@0: bool *_retval) michael@0: { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseDragService::GetDataTransfer(nsIDOMDataTransfer** aDataTransfer) michael@0: { michael@0: *aDataTransfer = mDataTransfer; michael@0: NS_IF_ADDREF(*aDataTransfer); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseDragService::SetDataTransfer(nsIDOMDataTransfer* aDataTransfer) michael@0: { michael@0: mDataTransfer = aDataTransfer; michael@0: return NS_OK; michael@0: } michael@0: michael@0: //------------------------------------------------------------------------- michael@0: NS_IMETHODIMP michael@0: nsBaseDragService::InvokeDragSession(nsIDOMNode *aDOMNode, michael@0: nsISupportsArray* aTransferableArray, michael@0: nsIScriptableRegion* aDragRgn, michael@0: uint32_t aActionType) michael@0: { michael@0: NS_ENSURE_TRUE(aDOMNode, NS_ERROR_INVALID_ARG); michael@0: NS_ENSURE_TRUE(mSuppressLevel == 0, NS_ERROR_FAILURE); michael@0: michael@0: // stash the document of the dom node michael@0: aDOMNode->GetOwnerDocument(getter_AddRefs(mSourceDocument)); michael@0: mSourceNode = aDOMNode; michael@0: mEndDragPoint = nsIntPoint(0, 0); michael@0: michael@0: // When the mouse goes down, the selection code starts a mouse michael@0: // capture. However, this gets in the way of determining drag michael@0: // feedback for things like trees because the event coordinates michael@0: // are in the wrong coord system, so turn off mouse capture. michael@0: nsIPresShell::ClearMouseCapture(nullptr); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseDragService::InvokeDragSessionWithImage(nsIDOMNode* aDOMNode, michael@0: nsISupportsArray* aTransferableArray, michael@0: nsIScriptableRegion* aRegion, michael@0: uint32_t aActionType, michael@0: nsIDOMNode* aImage, michael@0: int32_t aImageX, int32_t aImageY, michael@0: nsIDOMDragEvent* aDragEvent, michael@0: nsIDOMDataTransfer* aDataTransfer) michael@0: { michael@0: NS_ENSURE_TRUE(aDragEvent, NS_ERROR_NULL_POINTER); michael@0: NS_ENSURE_TRUE(aDataTransfer, NS_ERROR_NULL_POINTER); michael@0: NS_ENSURE_TRUE(mSuppressLevel == 0, NS_ERROR_FAILURE); michael@0: michael@0: mDataTransfer = aDataTransfer; michael@0: mSelection = nullptr; michael@0: mHasImage = true; michael@0: mDragPopup = nullptr; michael@0: mImage = aImage; michael@0: mImageX = aImageX; michael@0: mImageY = aImageY; michael@0: michael@0: aDragEvent->GetScreenX(&mScreenX); michael@0: aDragEvent->GetScreenY(&mScreenY); michael@0: aDragEvent->GetMozInputSource(&mInputSource); michael@0: michael@0: return InvokeDragSession(aDOMNode, aTransferableArray, aRegion, aActionType); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseDragService::InvokeDragSessionWithSelection(nsISelection* aSelection, michael@0: nsISupportsArray* aTransferableArray, michael@0: uint32_t aActionType, michael@0: nsIDOMDragEvent* aDragEvent, michael@0: nsIDOMDataTransfer* aDataTransfer) michael@0: { michael@0: NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER); michael@0: NS_ENSURE_TRUE(aDragEvent, NS_ERROR_NULL_POINTER); michael@0: NS_ENSURE_TRUE(mSuppressLevel == 0, NS_ERROR_FAILURE); michael@0: michael@0: mDataTransfer = aDataTransfer; michael@0: mSelection = aSelection; michael@0: mHasImage = true; michael@0: mDragPopup = nullptr; michael@0: mImage = nullptr; michael@0: mImageX = 0; michael@0: mImageY = 0; michael@0: michael@0: aDragEvent->GetScreenX(&mScreenX); michael@0: aDragEvent->GetScreenY(&mScreenY); michael@0: aDragEvent->GetMozInputSource(&mInputSource); michael@0: michael@0: // just get the focused node from the selection michael@0: // XXXndeakin this should actually be the deepest node that contains both michael@0: // endpoints of the selection michael@0: nsCOMPtr node; michael@0: aSelection->GetFocusNode(getter_AddRefs(node)); michael@0: michael@0: return InvokeDragSession(node, aTransferableArray, nullptr, aActionType); michael@0: } michael@0: michael@0: //------------------------------------------------------------------------- michael@0: NS_IMETHODIMP michael@0: nsBaseDragService::GetCurrentSession(nsIDragSession ** aSession) michael@0: { michael@0: if (!aSession) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: // "this" also implements a drag session, so say we are one but only michael@0: // if there is currently a drag going on. michael@0: if (!mSuppressLevel && mDoingDrag) { michael@0: *aSession = this; michael@0: NS_ADDREF(*aSession); // addRef because we're a "getter" michael@0: } michael@0: else michael@0: *aSession = nullptr; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //------------------------------------------------------------------------- michael@0: NS_IMETHODIMP michael@0: nsBaseDragService::StartDragSession() michael@0: { michael@0: if (mDoingDrag) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: mDoingDrag = true; michael@0: // By default dispatch drop also to content. michael@0: mOnlyChromeDrop = false; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsBaseDragService::OpenDragPopup() michael@0: { michael@0: if (mDragPopup) { michael@0: nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); michael@0: if (pm) { michael@0: pm->ShowPopupAtScreen(mDragPopup, mScreenX - mImageX, mScreenY - mImageY, false, nullptr); michael@0: } michael@0: } michael@0: } michael@0: michael@0: //------------------------------------------------------------------------- michael@0: NS_IMETHODIMP michael@0: nsBaseDragService::EndDragSession(bool aDoneDrag) michael@0: { michael@0: if (!mDoingDrag) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: if (aDoneDrag && !mSuppressLevel) michael@0: FireDragEventAtSource(NS_DRAGDROP_END); michael@0: michael@0: if (mDragPopup) { michael@0: nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); michael@0: if (pm) { michael@0: pm->HidePopup(mDragPopup, false, true, false, false); michael@0: } michael@0: } michael@0: michael@0: mDoingDrag = false; michael@0: michael@0: // release the source we've been holding on to. michael@0: mSourceDocument = nullptr; michael@0: mSourceNode = nullptr; michael@0: mSelection = nullptr; michael@0: mDataTransfer = nullptr; michael@0: mHasImage = false; michael@0: mUserCancelled = false; michael@0: mDragPopup = nullptr; michael@0: mImage = nullptr; michael@0: mImageX = 0; michael@0: mImageY = 0; michael@0: mScreenX = -1; michael@0: mScreenY = -1; michael@0: mInputSource = nsIDOMMouseEvent::MOZ_SOURCE_MOUSE; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseDragService::FireDragEventAtSource(uint32_t aMsg) michael@0: { michael@0: if (mSourceNode && !mSuppressLevel) { michael@0: nsCOMPtr doc = do_QueryInterface(mSourceDocument); michael@0: if (doc) { michael@0: nsCOMPtr presShell = doc->GetShell(); michael@0: if (presShell) { michael@0: nsEventStatus status = nsEventStatus_eIgnore; michael@0: WidgetDragEvent event(true, aMsg, nullptr); michael@0: event.inputSource = mInputSource; michael@0: if (aMsg == NS_DRAGDROP_END) { michael@0: event.refPoint.x = mEndDragPoint.x; michael@0: event.refPoint.y = mEndDragPoint.y; michael@0: event.userCancelled = mUserCancelled; michael@0: } michael@0: michael@0: nsCOMPtr content = do_QueryInterface(mSourceNode); michael@0: return presShell->HandleDOMEventWithTarget(content, &event, &status); michael@0: } michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* This is used by Windows and Mac to update the position of a popup being michael@0: * used as a drag image during the drag. This isn't used on GTK as it manages michael@0: * the drag popup itself. michael@0: */ michael@0: NS_IMETHODIMP michael@0: nsBaseDragService::DragMoved(int32_t aX, int32_t aY) michael@0: { michael@0: if (mDragPopup) { michael@0: nsIFrame* frame = mDragPopup->GetPrimaryFrame(); michael@0: if (frame && frame->GetType() == nsGkAtoms::menuPopupFrame) { michael@0: (static_cast(frame))->MoveTo(aX - mImageX, aY - mImageY, true); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: static nsIPresShell* michael@0: GetPresShellForContent(nsIDOMNode* aDOMNode) michael@0: { michael@0: nsCOMPtr content = do_QueryInterface(aDOMNode); michael@0: if (!content) michael@0: return nullptr; michael@0: michael@0: nsCOMPtr document = content->GetCurrentDoc(); michael@0: if (document) { michael@0: document->FlushPendingNotifications(Flush_Display); michael@0: michael@0: return document->GetShell(); michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: nsresult michael@0: nsBaseDragService::DrawDrag(nsIDOMNode* aDOMNode, michael@0: nsIScriptableRegion* aRegion, michael@0: int32_t aScreenX, int32_t aScreenY, michael@0: nsIntRect* aScreenDragRect, michael@0: RefPtr* aSurface, michael@0: nsPresContext** aPresContext) michael@0: { michael@0: *aSurface = nullptr; michael@0: *aPresContext = nullptr; michael@0: michael@0: // use a default size, in case of an error. michael@0: aScreenDragRect->x = aScreenX - mImageX; michael@0: aScreenDragRect->y = aScreenY - mImageY; michael@0: aScreenDragRect->width = 1; michael@0: aScreenDragRect->height = 1; michael@0: michael@0: // if a drag image was specified, use that, otherwise, use the source node michael@0: nsCOMPtr dragNode = mImage ? mImage.get() : aDOMNode; michael@0: michael@0: // get the presshell for the node being dragged. If the drag image is not in michael@0: // a document or has no frame, get the presshell from the source drag node michael@0: nsIPresShell* presShell = GetPresShellForContent(dragNode); michael@0: if (!presShell && mImage) michael@0: presShell = GetPresShellForContent(aDOMNode); michael@0: if (!presShell) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: *aPresContext = presShell->GetPresContext(); michael@0: michael@0: // convert mouse position to dev pixels of the prescontext michael@0: int32_t sx = aScreenX, sy = aScreenY; michael@0: ConvertToUnscaledDevPixels(*aPresContext, &sx, &sy); michael@0: michael@0: aScreenDragRect->x = sx - mImageX; michael@0: aScreenDragRect->y = sy - mImageY; michael@0: michael@0: // check if drag images are disabled michael@0: bool enableDragImages = Preferences::GetBool(DRAGIMAGES_PREF, true); michael@0: michael@0: // didn't want an image, so just set the screen rectangle to the frame size michael@0: if (!enableDragImages || !mHasImage) { michael@0: // if a region was specified, set the screen rectangle to the area that michael@0: // the region occupies michael@0: if (aRegion) { michael@0: // the region's coordinates are relative to the root frame michael@0: nsIFrame* rootFrame = presShell->GetRootFrame(); michael@0: if (rootFrame && *aPresContext) { michael@0: nsIntRect dragRect; michael@0: aRegion->GetBoundingBox(&dragRect.x, &dragRect.y, &dragRect.width, &dragRect.height); michael@0: dragRect = dragRect.ToAppUnits(nsPresContext::AppUnitsPerCSSPixel()). michael@0: ToOutsidePixels((*aPresContext)->AppUnitsPerDevPixel()); michael@0: michael@0: nsIntRect screenRect = rootFrame->GetScreenRectExternal(); michael@0: aScreenDragRect->SetRect(screenRect.x + dragRect.x, screenRect.y + dragRect.y, michael@0: dragRect.width, dragRect.height); michael@0: } michael@0: } michael@0: else { michael@0: // otherwise, there was no region so just set the rectangle to michael@0: // the size of the primary frame of the content. michael@0: nsCOMPtr content = do_QueryInterface(dragNode); michael@0: nsIFrame* frame = content->GetPrimaryFrame(); michael@0: if (frame) { michael@0: nsIntRect screenRect = frame->GetScreenRectExternal(); michael@0: aScreenDragRect->SetRect(screenRect.x, screenRect.y, michael@0: screenRect.width, screenRect.height); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // draw the image for selections michael@0: if (mSelection) { michael@0: nsIntPoint pnt(aScreenDragRect->x, aScreenDragRect->y); michael@0: *aSurface = presShell->RenderSelection(mSelection, pnt, aScreenDragRect); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // if a custom image was specified, check if it is an image node and draw michael@0: // using the source rather than the displayed image. But if mImage isn't michael@0: // an image or canvas, fall through to RenderNode below. michael@0: if (mImage) { michael@0: nsCOMPtr content = do_QueryInterface(dragNode); michael@0: HTMLCanvasElement *canvas = HTMLCanvasElement::FromContentOrNull(content); michael@0: if (canvas) { michael@0: return DrawDragForImage(*aPresContext, nullptr, canvas, sx, sy, michael@0: aScreenDragRect, aSurface); michael@0: } michael@0: michael@0: nsCOMPtr imageLoader = do_QueryInterface(dragNode); michael@0: // for image nodes, create the drag image from the actual image data michael@0: if (imageLoader) { michael@0: return DrawDragForImage(*aPresContext, imageLoader, nullptr, sx, sy, michael@0: aScreenDragRect, aSurface); michael@0: } michael@0: michael@0: // If the image is a popup, use that as the image. This allows custom drag michael@0: // images that can change during the drag, but means that any platform michael@0: // default image handling won't occur. michael@0: // XXXndeakin this should be chrome-only michael@0: michael@0: nsIFrame* frame = content->GetPrimaryFrame(); michael@0: if (frame && frame->GetType() == nsGkAtoms::menuPopupFrame) { michael@0: mDragPopup = content; michael@0: } michael@0: } michael@0: michael@0: if (!mDragPopup) { michael@0: // otherwise, just draw the node michael@0: nsIntRegion clipRegion; michael@0: if (aRegion) { michael@0: aRegion->GetRegion(&clipRegion); michael@0: } michael@0: michael@0: nsIntPoint pnt(aScreenDragRect->x, aScreenDragRect->y); michael@0: *aSurface = presShell->RenderNode(dragNode, aRegion ? &clipRegion : nullptr, michael@0: pnt, aScreenDragRect); michael@0: } michael@0: michael@0: // if an image was specified, reposition the drag rectangle to michael@0: // the supplied offset in mImageX and mImageY. michael@0: if (mImage) { michael@0: aScreenDragRect->x = sx - mImageX; michael@0: aScreenDragRect->y = sy - mImageY; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsBaseDragService::DrawDragForImage(nsPresContext* aPresContext, michael@0: nsIImageLoadingContent* aImageLoader, michael@0: HTMLCanvasElement* aCanvas, michael@0: int32_t aScreenX, int32_t aScreenY, michael@0: nsIntRect* aScreenDragRect, michael@0: RefPtr* aSurface) michael@0: { michael@0: nsCOMPtr imgContainer; michael@0: if (aImageLoader) { michael@0: nsCOMPtr imgRequest; michael@0: nsresult rv = aImageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, michael@0: getter_AddRefs(imgRequest)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: if (!imgRequest) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: rv = imgRequest->GetImage(getter_AddRefs(imgContainer)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: if (!imgContainer) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: // use the size of the image as the size of the drag image michael@0: imgContainer->GetWidth(&aScreenDragRect->width); michael@0: imgContainer->GetHeight(&aScreenDragRect->height); michael@0: } michael@0: else { michael@0: NS_ASSERTION(aCanvas, "both image and canvas are null"); michael@0: nsIntSize sz = aCanvas->GetSize(); michael@0: aScreenDragRect->width = sz.width; michael@0: aScreenDragRect->height = sz.height; michael@0: } michael@0: michael@0: nsIntSize srcSize = aScreenDragRect->Size(); michael@0: nsIntSize destSize = srcSize; michael@0: michael@0: if (destSize.width == 0 || destSize.height == 0) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: // if the image is larger than half the screen size, scale it down. This michael@0: // scaling algorithm is the same as is used in nsPresShell::PaintRangePaintInfo michael@0: nsDeviceContext* deviceContext = aPresContext->DeviceContext(); michael@0: nsRect maxSize; michael@0: deviceContext->GetClientRect(maxSize); michael@0: nscoord maxWidth = aPresContext->AppUnitsToDevPixels(maxSize.width >> 1); michael@0: nscoord maxHeight = aPresContext->AppUnitsToDevPixels(maxSize.height >> 1); michael@0: if (destSize.width > maxWidth || destSize.height > maxHeight) { michael@0: float scale = 1.0; michael@0: if (destSize.width > maxWidth) michael@0: scale = std::min(scale, float(maxWidth) / destSize.width); michael@0: if (destSize.height > maxHeight) michael@0: scale = std::min(scale, float(maxHeight) / destSize.height); michael@0: michael@0: destSize.width = NSToIntFloor(float(destSize.width) * scale); michael@0: destSize.height = NSToIntFloor(float(destSize.height) * scale); michael@0: michael@0: aScreenDragRect->x = NSToIntFloor(aScreenX - float(mImageX) * scale); michael@0: aScreenDragRect->y = NSToIntFloor(aScreenY - float(mImageY) * scale); michael@0: aScreenDragRect->width = destSize.width; michael@0: aScreenDragRect->height = destSize.height; michael@0: } michael@0: michael@0: nsresult result = NS_OK; michael@0: if (aImageLoader) { michael@0: RefPtr dt = michael@0: gfxPlatform::GetPlatform()-> michael@0: CreateOffscreenContentDrawTarget(destSize.ToIntSize(), michael@0: SurfaceFormat::B8G8R8A8); michael@0: if (!dt) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsRefPtr ctx = new gfxContext(dt); michael@0: if (!ctx) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: gfxRect outRect(0, 0, destSize.width, destSize.height); michael@0: gfxMatrix scale = michael@0: gfxMatrix().Scale(srcSize.width/outRect.Width(), srcSize.height/outRect.Height()); michael@0: nsIntRect imgSize(0, 0, srcSize.width, srcSize.height); michael@0: imgContainer->Draw(ctx, GraphicsFilter::FILTER_GOOD, scale, outRect, imgSize, michael@0: destSize, nullptr, imgIContainer::FRAME_CURRENT, michael@0: imgIContainer::FLAG_SYNC_DECODE); michael@0: *aSurface = dt->Snapshot(); michael@0: } else { michael@0: *aSurface = aCanvas->GetSurfaceSnapshot(); michael@0: } michael@0: michael@0: return result; michael@0: } michael@0: michael@0: void michael@0: nsBaseDragService::ConvertToUnscaledDevPixels(nsPresContext* aPresContext, michael@0: int32_t* aScreenX, int32_t* aScreenY) michael@0: { michael@0: int32_t adj = aPresContext->DeviceContext()->UnscaledAppUnitsPerDevPixel(); michael@0: *aScreenX = nsPresContext::CSSPixelsToAppUnits(*aScreenX) / adj; michael@0: *aScreenY = nsPresContext::CSSPixelsToAppUnits(*aScreenY) / adj; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseDragService::Suppress() michael@0: { michael@0: EndDragSession(false); michael@0: ++mSuppressLevel; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseDragService::Unsuppress() michael@0: { michael@0: --mSuppressLevel; michael@0: return NS_OK; michael@0: }