1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/widget/xpwidgets/nsBaseDragService.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,669 @@ 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 "nsBaseDragService.h" 1.10 +#include "nsITransferable.h" 1.11 + 1.12 +#include "nsIServiceManager.h" 1.13 +#include "nsITransferable.h" 1.14 +#include "nsISupportsArray.h" 1.15 +#include "nsSize.h" 1.16 +#include "nsXPCOM.h" 1.17 +#include "nsISupportsPrimitives.h" 1.18 +#include "nsCOMPtr.h" 1.19 +#include "nsIInterfaceRequestorUtils.h" 1.20 +#include "nsIFrame.h" 1.21 +#include "nsIDocument.h" 1.22 +#include "nsIContent.h" 1.23 +#include "nsIPresShell.h" 1.24 +#include "nsViewManager.h" 1.25 +#include "nsIDOMNode.h" 1.26 +#include "nsIDOMDragEvent.h" 1.27 +#include "nsISelection.h" 1.28 +#include "nsISelectionPrivate.h" 1.29 +#include "nsPresContext.h" 1.30 +#include "nsIDOMDataTransfer.h" 1.31 +#include "nsIImageLoadingContent.h" 1.32 +#include "imgIContainer.h" 1.33 +#include "imgIRequest.h" 1.34 +#include "nsRegion.h" 1.35 +#include "nsXULPopupManager.h" 1.36 +#include "nsMenuPopupFrame.h" 1.37 +#include "mozilla/MouseEvents.h" 1.38 +#include "mozilla/Preferences.h" 1.39 +#include "mozilla/gfx/2D.h" 1.40 + 1.41 +#include "gfxContext.h" 1.42 +#include "gfxPlatform.h" 1.43 +#include <algorithm> 1.44 + 1.45 +using namespace mozilla; 1.46 +using namespace mozilla::gfx; 1.47 +using namespace mozilla::dom; 1.48 + 1.49 +#define DRAGIMAGES_PREF "nglayout.enable_drag_images" 1.50 + 1.51 +nsBaseDragService::nsBaseDragService() 1.52 + : mCanDrop(false), mOnlyChromeDrop(false), mDoingDrag(false), 1.53 + mHasImage(false), mUserCancelled(false), 1.54 + mDragAction(DRAGDROP_ACTION_NONE), mTargetSize(0,0), 1.55 + mImageX(0), mImageY(0), mScreenX(-1), mScreenY(-1), mSuppressLevel(0), 1.56 + mInputSource(nsIDOMMouseEvent::MOZ_SOURCE_MOUSE) 1.57 +{ 1.58 +} 1.59 + 1.60 +nsBaseDragService::~nsBaseDragService() 1.61 +{ 1.62 +} 1.63 + 1.64 +NS_IMPL_ISUPPORTS(nsBaseDragService, nsIDragService, nsIDragSession) 1.65 + 1.66 +//--------------------------------------------------------- 1.67 +NS_IMETHODIMP 1.68 +nsBaseDragService::SetCanDrop(bool aCanDrop) 1.69 +{ 1.70 + mCanDrop = aCanDrop; 1.71 + return NS_OK; 1.72 +} 1.73 + 1.74 +//--------------------------------------------------------- 1.75 +NS_IMETHODIMP 1.76 +nsBaseDragService::GetCanDrop(bool * aCanDrop) 1.77 +{ 1.78 + *aCanDrop = mCanDrop; 1.79 + return NS_OK; 1.80 +} 1.81 +//--------------------------------------------------------- 1.82 +NS_IMETHODIMP 1.83 +nsBaseDragService::SetOnlyChromeDrop(bool aOnlyChrome) 1.84 +{ 1.85 + mOnlyChromeDrop = aOnlyChrome; 1.86 + return NS_OK; 1.87 +} 1.88 + 1.89 +//--------------------------------------------------------- 1.90 +NS_IMETHODIMP 1.91 +nsBaseDragService::GetOnlyChromeDrop(bool* aOnlyChrome) 1.92 +{ 1.93 + *aOnlyChrome = mOnlyChromeDrop; 1.94 + return NS_OK; 1.95 +} 1.96 + 1.97 +//--------------------------------------------------------- 1.98 +NS_IMETHODIMP 1.99 +nsBaseDragService::SetDragAction(uint32_t anAction) 1.100 +{ 1.101 + mDragAction = anAction; 1.102 + return NS_OK; 1.103 +} 1.104 + 1.105 +//--------------------------------------------------------- 1.106 +NS_IMETHODIMP 1.107 +nsBaseDragService::GetDragAction(uint32_t * anAction) 1.108 +{ 1.109 + *anAction = mDragAction; 1.110 + return NS_OK; 1.111 +} 1.112 + 1.113 +//--------------------------------------------------------- 1.114 +NS_IMETHODIMP 1.115 +nsBaseDragService::SetTargetSize(nsSize aDragTargetSize) 1.116 +{ 1.117 + mTargetSize = aDragTargetSize; 1.118 + return NS_OK; 1.119 +} 1.120 + 1.121 +//--------------------------------------------------------- 1.122 +NS_IMETHODIMP 1.123 +nsBaseDragService::GetTargetSize(nsSize * aDragTargetSize) 1.124 +{ 1.125 + *aDragTargetSize = mTargetSize; 1.126 + return NS_OK; 1.127 +} 1.128 + 1.129 +//------------------------------------------------------------------------- 1.130 + 1.131 +NS_IMETHODIMP 1.132 +nsBaseDragService::GetNumDropItems(uint32_t * aNumItems) 1.133 +{ 1.134 + *aNumItems = 0; 1.135 + return NS_ERROR_FAILURE; 1.136 +} 1.137 + 1.138 + 1.139 +// 1.140 +// GetSourceDocument 1.141 +// 1.142 +// Returns the DOM document where the drag was initiated. This will be 1.143 +// nullptr if the drag began outside of our application. 1.144 +// 1.145 +NS_IMETHODIMP 1.146 +nsBaseDragService::GetSourceDocument(nsIDOMDocument** aSourceDocument) 1.147 +{ 1.148 + *aSourceDocument = mSourceDocument.get(); 1.149 + NS_IF_ADDREF(*aSourceDocument); 1.150 + 1.151 + return NS_OK; 1.152 +} 1.153 + 1.154 +// 1.155 +// GetSourceNode 1.156 +// 1.157 +// Returns the DOM node where the drag was initiated. This will be 1.158 +// nullptr if the drag began outside of our application. 1.159 +// 1.160 +NS_IMETHODIMP 1.161 +nsBaseDragService::GetSourceNode(nsIDOMNode** aSourceNode) 1.162 +{ 1.163 + *aSourceNode = mSourceNode.get(); 1.164 + NS_IF_ADDREF(*aSourceNode); 1.165 + 1.166 + return NS_OK; 1.167 +} 1.168 + 1.169 + 1.170 +//------------------------------------------------------------------------- 1.171 + 1.172 +NS_IMETHODIMP 1.173 +nsBaseDragService::GetData(nsITransferable * aTransferable, 1.174 + uint32_t aItemIndex) 1.175 +{ 1.176 + return NS_ERROR_FAILURE; 1.177 +} 1.178 + 1.179 +//------------------------------------------------------------------------- 1.180 +NS_IMETHODIMP 1.181 +nsBaseDragService::IsDataFlavorSupported(const char *aDataFlavor, 1.182 + bool *_retval) 1.183 +{ 1.184 + return NS_ERROR_FAILURE; 1.185 +} 1.186 + 1.187 +NS_IMETHODIMP 1.188 +nsBaseDragService::GetDataTransfer(nsIDOMDataTransfer** aDataTransfer) 1.189 +{ 1.190 + *aDataTransfer = mDataTransfer; 1.191 + NS_IF_ADDREF(*aDataTransfer); 1.192 + return NS_OK; 1.193 +} 1.194 + 1.195 +NS_IMETHODIMP 1.196 +nsBaseDragService::SetDataTransfer(nsIDOMDataTransfer* aDataTransfer) 1.197 +{ 1.198 + mDataTransfer = aDataTransfer; 1.199 + return NS_OK; 1.200 +} 1.201 + 1.202 +//------------------------------------------------------------------------- 1.203 +NS_IMETHODIMP 1.204 +nsBaseDragService::InvokeDragSession(nsIDOMNode *aDOMNode, 1.205 + nsISupportsArray* aTransferableArray, 1.206 + nsIScriptableRegion* aDragRgn, 1.207 + uint32_t aActionType) 1.208 +{ 1.209 + NS_ENSURE_TRUE(aDOMNode, NS_ERROR_INVALID_ARG); 1.210 + NS_ENSURE_TRUE(mSuppressLevel == 0, NS_ERROR_FAILURE); 1.211 + 1.212 + // stash the document of the dom node 1.213 + aDOMNode->GetOwnerDocument(getter_AddRefs(mSourceDocument)); 1.214 + mSourceNode = aDOMNode; 1.215 + mEndDragPoint = nsIntPoint(0, 0); 1.216 + 1.217 + // When the mouse goes down, the selection code starts a mouse 1.218 + // capture. However, this gets in the way of determining drag 1.219 + // feedback for things like trees because the event coordinates 1.220 + // are in the wrong coord system, so turn off mouse capture. 1.221 + nsIPresShell::ClearMouseCapture(nullptr); 1.222 + 1.223 + return NS_OK; 1.224 +} 1.225 + 1.226 +NS_IMETHODIMP 1.227 +nsBaseDragService::InvokeDragSessionWithImage(nsIDOMNode* aDOMNode, 1.228 + nsISupportsArray* aTransferableArray, 1.229 + nsIScriptableRegion* aRegion, 1.230 + uint32_t aActionType, 1.231 + nsIDOMNode* aImage, 1.232 + int32_t aImageX, int32_t aImageY, 1.233 + nsIDOMDragEvent* aDragEvent, 1.234 + nsIDOMDataTransfer* aDataTransfer) 1.235 +{ 1.236 + NS_ENSURE_TRUE(aDragEvent, NS_ERROR_NULL_POINTER); 1.237 + NS_ENSURE_TRUE(aDataTransfer, NS_ERROR_NULL_POINTER); 1.238 + NS_ENSURE_TRUE(mSuppressLevel == 0, NS_ERROR_FAILURE); 1.239 + 1.240 + mDataTransfer = aDataTransfer; 1.241 + mSelection = nullptr; 1.242 + mHasImage = true; 1.243 + mDragPopup = nullptr; 1.244 + mImage = aImage; 1.245 + mImageX = aImageX; 1.246 + mImageY = aImageY; 1.247 + 1.248 + aDragEvent->GetScreenX(&mScreenX); 1.249 + aDragEvent->GetScreenY(&mScreenY); 1.250 + aDragEvent->GetMozInputSource(&mInputSource); 1.251 + 1.252 + return InvokeDragSession(aDOMNode, aTransferableArray, aRegion, aActionType); 1.253 +} 1.254 + 1.255 +NS_IMETHODIMP 1.256 +nsBaseDragService::InvokeDragSessionWithSelection(nsISelection* aSelection, 1.257 + nsISupportsArray* aTransferableArray, 1.258 + uint32_t aActionType, 1.259 + nsIDOMDragEvent* aDragEvent, 1.260 + nsIDOMDataTransfer* aDataTransfer) 1.261 +{ 1.262 + NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER); 1.263 + NS_ENSURE_TRUE(aDragEvent, NS_ERROR_NULL_POINTER); 1.264 + NS_ENSURE_TRUE(mSuppressLevel == 0, NS_ERROR_FAILURE); 1.265 + 1.266 + mDataTransfer = aDataTransfer; 1.267 + mSelection = aSelection; 1.268 + mHasImage = true; 1.269 + mDragPopup = nullptr; 1.270 + mImage = nullptr; 1.271 + mImageX = 0; 1.272 + mImageY = 0; 1.273 + 1.274 + aDragEvent->GetScreenX(&mScreenX); 1.275 + aDragEvent->GetScreenY(&mScreenY); 1.276 + aDragEvent->GetMozInputSource(&mInputSource); 1.277 + 1.278 + // just get the focused node from the selection 1.279 + // XXXndeakin this should actually be the deepest node that contains both 1.280 + // endpoints of the selection 1.281 + nsCOMPtr<nsIDOMNode> node; 1.282 + aSelection->GetFocusNode(getter_AddRefs(node)); 1.283 + 1.284 + return InvokeDragSession(node, aTransferableArray, nullptr, aActionType); 1.285 +} 1.286 + 1.287 +//------------------------------------------------------------------------- 1.288 +NS_IMETHODIMP 1.289 +nsBaseDragService::GetCurrentSession(nsIDragSession ** aSession) 1.290 +{ 1.291 + if (!aSession) 1.292 + return NS_ERROR_INVALID_ARG; 1.293 + 1.294 + // "this" also implements a drag session, so say we are one but only 1.295 + // if there is currently a drag going on. 1.296 + if (!mSuppressLevel && mDoingDrag) { 1.297 + *aSession = this; 1.298 + NS_ADDREF(*aSession); // addRef because we're a "getter" 1.299 + } 1.300 + else 1.301 + *aSession = nullptr; 1.302 + 1.303 + return NS_OK; 1.304 +} 1.305 + 1.306 +//------------------------------------------------------------------------- 1.307 +NS_IMETHODIMP 1.308 +nsBaseDragService::StartDragSession() 1.309 +{ 1.310 + if (mDoingDrag) { 1.311 + return NS_ERROR_FAILURE; 1.312 + } 1.313 + mDoingDrag = true; 1.314 + // By default dispatch drop also to content. 1.315 + mOnlyChromeDrop = false; 1.316 + 1.317 + return NS_OK; 1.318 +} 1.319 + 1.320 +void 1.321 +nsBaseDragService::OpenDragPopup() 1.322 +{ 1.323 + if (mDragPopup) { 1.324 + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); 1.325 + if (pm) { 1.326 + pm->ShowPopupAtScreen(mDragPopup, mScreenX - mImageX, mScreenY - mImageY, false, nullptr); 1.327 + } 1.328 + } 1.329 +} 1.330 + 1.331 +//------------------------------------------------------------------------- 1.332 +NS_IMETHODIMP 1.333 +nsBaseDragService::EndDragSession(bool aDoneDrag) 1.334 +{ 1.335 + if (!mDoingDrag) { 1.336 + return NS_ERROR_FAILURE; 1.337 + } 1.338 + 1.339 + if (aDoneDrag && !mSuppressLevel) 1.340 + FireDragEventAtSource(NS_DRAGDROP_END); 1.341 + 1.342 + if (mDragPopup) { 1.343 + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); 1.344 + if (pm) { 1.345 + pm->HidePopup(mDragPopup, false, true, false, false); 1.346 + } 1.347 + } 1.348 + 1.349 + mDoingDrag = false; 1.350 + 1.351 + // release the source we've been holding on to. 1.352 + mSourceDocument = nullptr; 1.353 + mSourceNode = nullptr; 1.354 + mSelection = nullptr; 1.355 + mDataTransfer = nullptr; 1.356 + mHasImage = false; 1.357 + mUserCancelled = false; 1.358 + mDragPopup = nullptr; 1.359 + mImage = nullptr; 1.360 + mImageX = 0; 1.361 + mImageY = 0; 1.362 + mScreenX = -1; 1.363 + mScreenY = -1; 1.364 + mInputSource = nsIDOMMouseEvent::MOZ_SOURCE_MOUSE; 1.365 + 1.366 + return NS_OK; 1.367 +} 1.368 + 1.369 +NS_IMETHODIMP 1.370 +nsBaseDragService::FireDragEventAtSource(uint32_t aMsg) 1.371 +{ 1.372 + if (mSourceNode && !mSuppressLevel) { 1.373 + nsCOMPtr<nsIDocument> doc = do_QueryInterface(mSourceDocument); 1.374 + if (doc) { 1.375 + nsCOMPtr<nsIPresShell> presShell = doc->GetShell(); 1.376 + if (presShell) { 1.377 + nsEventStatus status = nsEventStatus_eIgnore; 1.378 + WidgetDragEvent event(true, aMsg, nullptr); 1.379 + event.inputSource = mInputSource; 1.380 + if (aMsg == NS_DRAGDROP_END) { 1.381 + event.refPoint.x = mEndDragPoint.x; 1.382 + event.refPoint.y = mEndDragPoint.y; 1.383 + event.userCancelled = mUserCancelled; 1.384 + } 1.385 + 1.386 + nsCOMPtr<nsIContent> content = do_QueryInterface(mSourceNode); 1.387 + return presShell->HandleDOMEventWithTarget(content, &event, &status); 1.388 + } 1.389 + } 1.390 + } 1.391 + 1.392 + return NS_OK; 1.393 +} 1.394 + 1.395 +/* This is used by Windows and Mac to update the position of a popup being 1.396 + * used as a drag image during the drag. This isn't used on GTK as it manages 1.397 + * the drag popup itself. 1.398 + */ 1.399 +NS_IMETHODIMP 1.400 +nsBaseDragService::DragMoved(int32_t aX, int32_t aY) 1.401 +{ 1.402 + if (mDragPopup) { 1.403 + nsIFrame* frame = mDragPopup->GetPrimaryFrame(); 1.404 + if (frame && frame->GetType() == nsGkAtoms::menuPopupFrame) { 1.405 + (static_cast<nsMenuPopupFrame *>(frame))->MoveTo(aX - mImageX, aY - mImageY, true); 1.406 + } 1.407 + } 1.408 + 1.409 + return NS_OK; 1.410 +} 1.411 + 1.412 +static nsIPresShell* 1.413 +GetPresShellForContent(nsIDOMNode* aDOMNode) 1.414 +{ 1.415 + nsCOMPtr<nsIContent> content = do_QueryInterface(aDOMNode); 1.416 + if (!content) 1.417 + return nullptr; 1.418 + 1.419 + nsCOMPtr<nsIDocument> document = content->GetCurrentDoc(); 1.420 + if (document) { 1.421 + document->FlushPendingNotifications(Flush_Display); 1.422 + 1.423 + return document->GetShell(); 1.424 + } 1.425 + 1.426 + return nullptr; 1.427 +} 1.428 + 1.429 +nsresult 1.430 +nsBaseDragService::DrawDrag(nsIDOMNode* aDOMNode, 1.431 + nsIScriptableRegion* aRegion, 1.432 + int32_t aScreenX, int32_t aScreenY, 1.433 + nsIntRect* aScreenDragRect, 1.434 + RefPtr<SourceSurface>* aSurface, 1.435 + nsPresContext** aPresContext) 1.436 +{ 1.437 + *aSurface = nullptr; 1.438 + *aPresContext = nullptr; 1.439 + 1.440 + // use a default size, in case of an error. 1.441 + aScreenDragRect->x = aScreenX - mImageX; 1.442 + aScreenDragRect->y = aScreenY - mImageY; 1.443 + aScreenDragRect->width = 1; 1.444 + aScreenDragRect->height = 1; 1.445 + 1.446 + // if a drag image was specified, use that, otherwise, use the source node 1.447 + nsCOMPtr<nsIDOMNode> dragNode = mImage ? mImage.get() : aDOMNode; 1.448 + 1.449 + // get the presshell for the node being dragged. If the drag image is not in 1.450 + // a document or has no frame, get the presshell from the source drag node 1.451 + nsIPresShell* presShell = GetPresShellForContent(dragNode); 1.452 + if (!presShell && mImage) 1.453 + presShell = GetPresShellForContent(aDOMNode); 1.454 + if (!presShell) 1.455 + return NS_ERROR_FAILURE; 1.456 + 1.457 + *aPresContext = presShell->GetPresContext(); 1.458 + 1.459 + // convert mouse position to dev pixels of the prescontext 1.460 + int32_t sx = aScreenX, sy = aScreenY; 1.461 + ConvertToUnscaledDevPixels(*aPresContext, &sx, &sy); 1.462 + 1.463 + aScreenDragRect->x = sx - mImageX; 1.464 + aScreenDragRect->y = sy - mImageY; 1.465 + 1.466 + // check if drag images are disabled 1.467 + bool enableDragImages = Preferences::GetBool(DRAGIMAGES_PREF, true); 1.468 + 1.469 + // didn't want an image, so just set the screen rectangle to the frame size 1.470 + if (!enableDragImages || !mHasImage) { 1.471 + // if a region was specified, set the screen rectangle to the area that 1.472 + // the region occupies 1.473 + if (aRegion) { 1.474 + // the region's coordinates are relative to the root frame 1.475 + nsIFrame* rootFrame = presShell->GetRootFrame(); 1.476 + if (rootFrame && *aPresContext) { 1.477 + nsIntRect dragRect; 1.478 + aRegion->GetBoundingBox(&dragRect.x, &dragRect.y, &dragRect.width, &dragRect.height); 1.479 + dragRect = dragRect.ToAppUnits(nsPresContext::AppUnitsPerCSSPixel()). 1.480 + ToOutsidePixels((*aPresContext)->AppUnitsPerDevPixel()); 1.481 + 1.482 + nsIntRect screenRect = rootFrame->GetScreenRectExternal(); 1.483 + aScreenDragRect->SetRect(screenRect.x + dragRect.x, screenRect.y + dragRect.y, 1.484 + dragRect.width, dragRect.height); 1.485 + } 1.486 + } 1.487 + else { 1.488 + // otherwise, there was no region so just set the rectangle to 1.489 + // the size of the primary frame of the content. 1.490 + nsCOMPtr<nsIContent> content = do_QueryInterface(dragNode); 1.491 + nsIFrame* frame = content->GetPrimaryFrame(); 1.492 + if (frame) { 1.493 + nsIntRect screenRect = frame->GetScreenRectExternal(); 1.494 + aScreenDragRect->SetRect(screenRect.x, screenRect.y, 1.495 + screenRect.width, screenRect.height); 1.496 + } 1.497 + } 1.498 + 1.499 + return NS_OK; 1.500 + } 1.501 + 1.502 + // draw the image for selections 1.503 + if (mSelection) { 1.504 + nsIntPoint pnt(aScreenDragRect->x, aScreenDragRect->y); 1.505 + *aSurface = presShell->RenderSelection(mSelection, pnt, aScreenDragRect); 1.506 + return NS_OK; 1.507 + } 1.508 + 1.509 + // if a custom image was specified, check if it is an image node and draw 1.510 + // using the source rather than the displayed image. But if mImage isn't 1.511 + // an image or canvas, fall through to RenderNode below. 1.512 + if (mImage) { 1.513 + nsCOMPtr<nsIContent> content = do_QueryInterface(dragNode); 1.514 + HTMLCanvasElement *canvas = HTMLCanvasElement::FromContentOrNull(content); 1.515 + if (canvas) { 1.516 + return DrawDragForImage(*aPresContext, nullptr, canvas, sx, sy, 1.517 + aScreenDragRect, aSurface); 1.518 + } 1.519 + 1.520 + nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(dragNode); 1.521 + // for image nodes, create the drag image from the actual image data 1.522 + if (imageLoader) { 1.523 + return DrawDragForImage(*aPresContext, imageLoader, nullptr, sx, sy, 1.524 + aScreenDragRect, aSurface); 1.525 + } 1.526 + 1.527 + // If the image is a popup, use that as the image. This allows custom drag 1.528 + // images that can change during the drag, but means that any platform 1.529 + // default image handling won't occur. 1.530 + // XXXndeakin this should be chrome-only 1.531 + 1.532 + nsIFrame* frame = content->GetPrimaryFrame(); 1.533 + if (frame && frame->GetType() == nsGkAtoms::menuPopupFrame) { 1.534 + mDragPopup = content; 1.535 + } 1.536 + } 1.537 + 1.538 + if (!mDragPopup) { 1.539 + // otherwise, just draw the node 1.540 + nsIntRegion clipRegion; 1.541 + if (aRegion) { 1.542 + aRegion->GetRegion(&clipRegion); 1.543 + } 1.544 + 1.545 + nsIntPoint pnt(aScreenDragRect->x, aScreenDragRect->y); 1.546 + *aSurface = presShell->RenderNode(dragNode, aRegion ? &clipRegion : nullptr, 1.547 + pnt, aScreenDragRect); 1.548 + } 1.549 + 1.550 + // if an image was specified, reposition the drag rectangle to 1.551 + // the supplied offset in mImageX and mImageY. 1.552 + if (mImage) { 1.553 + aScreenDragRect->x = sx - mImageX; 1.554 + aScreenDragRect->y = sy - mImageY; 1.555 + } 1.556 + 1.557 + return NS_OK; 1.558 +} 1.559 + 1.560 +nsresult 1.561 +nsBaseDragService::DrawDragForImage(nsPresContext* aPresContext, 1.562 + nsIImageLoadingContent* aImageLoader, 1.563 + HTMLCanvasElement* aCanvas, 1.564 + int32_t aScreenX, int32_t aScreenY, 1.565 + nsIntRect* aScreenDragRect, 1.566 + RefPtr<SourceSurface>* aSurface) 1.567 +{ 1.568 + nsCOMPtr<imgIContainer> imgContainer; 1.569 + if (aImageLoader) { 1.570 + nsCOMPtr<imgIRequest> imgRequest; 1.571 + nsresult rv = aImageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, 1.572 + getter_AddRefs(imgRequest)); 1.573 + NS_ENSURE_SUCCESS(rv, rv); 1.574 + if (!imgRequest) 1.575 + return NS_ERROR_NOT_AVAILABLE; 1.576 + 1.577 + rv = imgRequest->GetImage(getter_AddRefs(imgContainer)); 1.578 + NS_ENSURE_SUCCESS(rv, rv); 1.579 + if (!imgContainer) 1.580 + return NS_ERROR_NOT_AVAILABLE; 1.581 + 1.582 + // use the size of the image as the size of the drag image 1.583 + imgContainer->GetWidth(&aScreenDragRect->width); 1.584 + imgContainer->GetHeight(&aScreenDragRect->height); 1.585 + } 1.586 + else { 1.587 + NS_ASSERTION(aCanvas, "both image and canvas are null"); 1.588 + nsIntSize sz = aCanvas->GetSize(); 1.589 + aScreenDragRect->width = sz.width; 1.590 + aScreenDragRect->height = sz.height; 1.591 + } 1.592 + 1.593 + nsIntSize srcSize = aScreenDragRect->Size(); 1.594 + nsIntSize destSize = srcSize; 1.595 + 1.596 + if (destSize.width == 0 || destSize.height == 0) 1.597 + return NS_ERROR_FAILURE; 1.598 + 1.599 + // if the image is larger than half the screen size, scale it down. This 1.600 + // scaling algorithm is the same as is used in nsPresShell::PaintRangePaintInfo 1.601 + nsDeviceContext* deviceContext = aPresContext->DeviceContext(); 1.602 + nsRect maxSize; 1.603 + deviceContext->GetClientRect(maxSize); 1.604 + nscoord maxWidth = aPresContext->AppUnitsToDevPixels(maxSize.width >> 1); 1.605 + nscoord maxHeight = aPresContext->AppUnitsToDevPixels(maxSize.height >> 1); 1.606 + if (destSize.width > maxWidth || destSize.height > maxHeight) { 1.607 + float scale = 1.0; 1.608 + if (destSize.width > maxWidth) 1.609 + scale = std::min(scale, float(maxWidth) / destSize.width); 1.610 + if (destSize.height > maxHeight) 1.611 + scale = std::min(scale, float(maxHeight) / destSize.height); 1.612 + 1.613 + destSize.width = NSToIntFloor(float(destSize.width) * scale); 1.614 + destSize.height = NSToIntFloor(float(destSize.height) * scale); 1.615 + 1.616 + aScreenDragRect->x = NSToIntFloor(aScreenX - float(mImageX) * scale); 1.617 + aScreenDragRect->y = NSToIntFloor(aScreenY - float(mImageY) * scale); 1.618 + aScreenDragRect->width = destSize.width; 1.619 + aScreenDragRect->height = destSize.height; 1.620 + } 1.621 + 1.622 + nsresult result = NS_OK; 1.623 + if (aImageLoader) { 1.624 + RefPtr<DrawTarget> dt = 1.625 + gfxPlatform::GetPlatform()-> 1.626 + CreateOffscreenContentDrawTarget(destSize.ToIntSize(), 1.627 + SurfaceFormat::B8G8R8A8); 1.628 + if (!dt) 1.629 + return NS_ERROR_FAILURE; 1.630 + 1.631 + nsRefPtr<gfxContext> ctx = new gfxContext(dt); 1.632 + if (!ctx) 1.633 + return NS_ERROR_FAILURE; 1.634 + 1.635 + gfxRect outRect(0, 0, destSize.width, destSize.height); 1.636 + gfxMatrix scale = 1.637 + gfxMatrix().Scale(srcSize.width/outRect.Width(), srcSize.height/outRect.Height()); 1.638 + nsIntRect imgSize(0, 0, srcSize.width, srcSize.height); 1.639 + imgContainer->Draw(ctx, GraphicsFilter::FILTER_GOOD, scale, outRect, imgSize, 1.640 + destSize, nullptr, imgIContainer::FRAME_CURRENT, 1.641 + imgIContainer::FLAG_SYNC_DECODE); 1.642 + *aSurface = dt->Snapshot(); 1.643 + } else { 1.644 + *aSurface = aCanvas->GetSurfaceSnapshot(); 1.645 + } 1.646 + 1.647 + return result; 1.648 +} 1.649 + 1.650 +void 1.651 +nsBaseDragService::ConvertToUnscaledDevPixels(nsPresContext* aPresContext, 1.652 + int32_t* aScreenX, int32_t* aScreenY) 1.653 +{ 1.654 + int32_t adj = aPresContext->DeviceContext()->UnscaledAppUnitsPerDevPixel(); 1.655 + *aScreenX = nsPresContext::CSSPixelsToAppUnits(*aScreenX) / adj; 1.656 + *aScreenY = nsPresContext::CSSPixelsToAppUnits(*aScreenY) / adj; 1.657 +} 1.658 + 1.659 +NS_IMETHODIMP 1.660 +nsBaseDragService::Suppress() 1.661 +{ 1.662 + EndDragSession(false); 1.663 + ++mSuppressLevel; 1.664 + return NS_OK; 1.665 +} 1.666 + 1.667 +NS_IMETHODIMP 1.668 +nsBaseDragService::Unsuppress() 1.669 +{ 1.670 + --mSuppressLevel; 1.671 + return NS_OK; 1.672 +}