widget/xpwidgets/nsBaseDragService.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "nsBaseDragService.h"
michael@0 7 #include "nsITransferable.h"
michael@0 8
michael@0 9 #include "nsIServiceManager.h"
michael@0 10 #include "nsITransferable.h"
michael@0 11 #include "nsISupportsArray.h"
michael@0 12 #include "nsSize.h"
michael@0 13 #include "nsXPCOM.h"
michael@0 14 #include "nsISupportsPrimitives.h"
michael@0 15 #include "nsCOMPtr.h"
michael@0 16 #include "nsIInterfaceRequestorUtils.h"
michael@0 17 #include "nsIFrame.h"
michael@0 18 #include "nsIDocument.h"
michael@0 19 #include "nsIContent.h"
michael@0 20 #include "nsIPresShell.h"
michael@0 21 #include "nsViewManager.h"
michael@0 22 #include "nsIDOMNode.h"
michael@0 23 #include "nsIDOMDragEvent.h"
michael@0 24 #include "nsISelection.h"
michael@0 25 #include "nsISelectionPrivate.h"
michael@0 26 #include "nsPresContext.h"
michael@0 27 #include "nsIDOMDataTransfer.h"
michael@0 28 #include "nsIImageLoadingContent.h"
michael@0 29 #include "imgIContainer.h"
michael@0 30 #include "imgIRequest.h"
michael@0 31 #include "nsRegion.h"
michael@0 32 #include "nsXULPopupManager.h"
michael@0 33 #include "nsMenuPopupFrame.h"
michael@0 34 #include "mozilla/MouseEvents.h"
michael@0 35 #include "mozilla/Preferences.h"
michael@0 36 #include "mozilla/gfx/2D.h"
michael@0 37
michael@0 38 #include "gfxContext.h"
michael@0 39 #include "gfxPlatform.h"
michael@0 40 #include <algorithm>
michael@0 41
michael@0 42 using namespace mozilla;
michael@0 43 using namespace mozilla::gfx;
michael@0 44 using namespace mozilla::dom;
michael@0 45
michael@0 46 #define DRAGIMAGES_PREF "nglayout.enable_drag_images"
michael@0 47
michael@0 48 nsBaseDragService::nsBaseDragService()
michael@0 49 : mCanDrop(false), mOnlyChromeDrop(false), mDoingDrag(false),
michael@0 50 mHasImage(false), mUserCancelled(false),
michael@0 51 mDragAction(DRAGDROP_ACTION_NONE), mTargetSize(0,0),
michael@0 52 mImageX(0), mImageY(0), mScreenX(-1), mScreenY(-1), mSuppressLevel(0),
michael@0 53 mInputSource(nsIDOMMouseEvent::MOZ_SOURCE_MOUSE)
michael@0 54 {
michael@0 55 }
michael@0 56
michael@0 57 nsBaseDragService::~nsBaseDragService()
michael@0 58 {
michael@0 59 }
michael@0 60
michael@0 61 NS_IMPL_ISUPPORTS(nsBaseDragService, nsIDragService, nsIDragSession)
michael@0 62
michael@0 63 //---------------------------------------------------------
michael@0 64 NS_IMETHODIMP
michael@0 65 nsBaseDragService::SetCanDrop(bool aCanDrop)
michael@0 66 {
michael@0 67 mCanDrop = aCanDrop;
michael@0 68 return NS_OK;
michael@0 69 }
michael@0 70
michael@0 71 //---------------------------------------------------------
michael@0 72 NS_IMETHODIMP
michael@0 73 nsBaseDragService::GetCanDrop(bool * aCanDrop)
michael@0 74 {
michael@0 75 *aCanDrop = mCanDrop;
michael@0 76 return NS_OK;
michael@0 77 }
michael@0 78 //---------------------------------------------------------
michael@0 79 NS_IMETHODIMP
michael@0 80 nsBaseDragService::SetOnlyChromeDrop(bool aOnlyChrome)
michael@0 81 {
michael@0 82 mOnlyChromeDrop = aOnlyChrome;
michael@0 83 return NS_OK;
michael@0 84 }
michael@0 85
michael@0 86 //---------------------------------------------------------
michael@0 87 NS_IMETHODIMP
michael@0 88 nsBaseDragService::GetOnlyChromeDrop(bool* aOnlyChrome)
michael@0 89 {
michael@0 90 *aOnlyChrome = mOnlyChromeDrop;
michael@0 91 return NS_OK;
michael@0 92 }
michael@0 93
michael@0 94 //---------------------------------------------------------
michael@0 95 NS_IMETHODIMP
michael@0 96 nsBaseDragService::SetDragAction(uint32_t anAction)
michael@0 97 {
michael@0 98 mDragAction = anAction;
michael@0 99 return NS_OK;
michael@0 100 }
michael@0 101
michael@0 102 //---------------------------------------------------------
michael@0 103 NS_IMETHODIMP
michael@0 104 nsBaseDragService::GetDragAction(uint32_t * anAction)
michael@0 105 {
michael@0 106 *anAction = mDragAction;
michael@0 107 return NS_OK;
michael@0 108 }
michael@0 109
michael@0 110 //---------------------------------------------------------
michael@0 111 NS_IMETHODIMP
michael@0 112 nsBaseDragService::SetTargetSize(nsSize aDragTargetSize)
michael@0 113 {
michael@0 114 mTargetSize = aDragTargetSize;
michael@0 115 return NS_OK;
michael@0 116 }
michael@0 117
michael@0 118 //---------------------------------------------------------
michael@0 119 NS_IMETHODIMP
michael@0 120 nsBaseDragService::GetTargetSize(nsSize * aDragTargetSize)
michael@0 121 {
michael@0 122 *aDragTargetSize = mTargetSize;
michael@0 123 return NS_OK;
michael@0 124 }
michael@0 125
michael@0 126 //-------------------------------------------------------------------------
michael@0 127
michael@0 128 NS_IMETHODIMP
michael@0 129 nsBaseDragService::GetNumDropItems(uint32_t * aNumItems)
michael@0 130 {
michael@0 131 *aNumItems = 0;
michael@0 132 return NS_ERROR_FAILURE;
michael@0 133 }
michael@0 134
michael@0 135
michael@0 136 //
michael@0 137 // GetSourceDocument
michael@0 138 //
michael@0 139 // Returns the DOM document where the drag was initiated. This will be
michael@0 140 // nullptr if the drag began outside of our application.
michael@0 141 //
michael@0 142 NS_IMETHODIMP
michael@0 143 nsBaseDragService::GetSourceDocument(nsIDOMDocument** aSourceDocument)
michael@0 144 {
michael@0 145 *aSourceDocument = mSourceDocument.get();
michael@0 146 NS_IF_ADDREF(*aSourceDocument);
michael@0 147
michael@0 148 return NS_OK;
michael@0 149 }
michael@0 150
michael@0 151 //
michael@0 152 // GetSourceNode
michael@0 153 //
michael@0 154 // Returns the DOM node where the drag was initiated. This will be
michael@0 155 // nullptr if the drag began outside of our application.
michael@0 156 //
michael@0 157 NS_IMETHODIMP
michael@0 158 nsBaseDragService::GetSourceNode(nsIDOMNode** aSourceNode)
michael@0 159 {
michael@0 160 *aSourceNode = mSourceNode.get();
michael@0 161 NS_IF_ADDREF(*aSourceNode);
michael@0 162
michael@0 163 return NS_OK;
michael@0 164 }
michael@0 165
michael@0 166
michael@0 167 //-------------------------------------------------------------------------
michael@0 168
michael@0 169 NS_IMETHODIMP
michael@0 170 nsBaseDragService::GetData(nsITransferable * aTransferable,
michael@0 171 uint32_t aItemIndex)
michael@0 172 {
michael@0 173 return NS_ERROR_FAILURE;
michael@0 174 }
michael@0 175
michael@0 176 //-------------------------------------------------------------------------
michael@0 177 NS_IMETHODIMP
michael@0 178 nsBaseDragService::IsDataFlavorSupported(const char *aDataFlavor,
michael@0 179 bool *_retval)
michael@0 180 {
michael@0 181 return NS_ERROR_FAILURE;
michael@0 182 }
michael@0 183
michael@0 184 NS_IMETHODIMP
michael@0 185 nsBaseDragService::GetDataTransfer(nsIDOMDataTransfer** aDataTransfer)
michael@0 186 {
michael@0 187 *aDataTransfer = mDataTransfer;
michael@0 188 NS_IF_ADDREF(*aDataTransfer);
michael@0 189 return NS_OK;
michael@0 190 }
michael@0 191
michael@0 192 NS_IMETHODIMP
michael@0 193 nsBaseDragService::SetDataTransfer(nsIDOMDataTransfer* aDataTransfer)
michael@0 194 {
michael@0 195 mDataTransfer = aDataTransfer;
michael@0 196 return NS_OK;
michael@0 197 }
michael@0 198
michael@0 199 //-------------------------------------------------------------------------
michael@0 200 NS_IMETHODIMP
michael@0 201 nsBaseDragService::InvokeDragSession(nsIDOMNode *aDOMNode,
michael@0 202 nsISupportsArray* aTransferableArray,
michael@0 203 nsIScriptableRegion* aDragRgn,
michael@0 204 uint32_t aActionType)
michael@0 205 {
michael@0 206 NS_ENSURE_TRUE(aDOMNode, NS_ERROR_INVALID_ARG);
michael@0 207 NS_ENSURE_TRUE(mSuppressLevel == 0, NS_ERROR_FAILURE);
michael@0 208
michael@0 209 // stash the document of the dom node
michael@0 210 aDOMNode->GetOwnerDocument(getter_AddRefs(mSourceDocument));
michael@0 211 mSourceNode = aDOMNode;
michael@0 212 mEndDragPoint = nsIntPoint(0, 0);
michael@0 213
michael@0 214 // When the mouse goes down, the selection code starts a mouse
michael@0 215 // capture. However, this gets in the way of determining drag
michael@0 216 // feedback for things like trees because the event coordinates
michael@0 217 // are in the wrong coord system, so turn off mouse capture.
michael@0 218 nsIPresShell::ClearMouseCapture(nullptr);
michael@0 219
michael@0 220 return NS_OK;
michael@0 221 }
michael@0 222
michael@0 223 NS_IMETHODIMP
michael@0 224 nsBaseDragService::InvokeDragSessionWithImage(nsIDOMNode* aDOMNode,
michael@0 225 nsISupportsArray* aTransferableArray,
michael@0 226 nsIScriptableRegion* aRegion,
michael@0 227 uint32_t aActionType,
michael@0 228 nsIDOMNode* aImage,
michael@0 229 int32_t aImageX, int32_t aImageY,
michael@0 230 nsIDOMDragEvent* aDragEvent,
michael@0 231 nsIDOMDataTransfer* aDataTransfer)
michael@0 232 {
michael@0 233 NS_ENSURE_TRUE(aDragEvent, NS_ERROR_NULL_POINTER);
michael@0 234 NS_ENSURE_TRUE(aDataTransfer, NS_ERROR_NULL_POINTER);
michael@0 235 NS_ENSURE_TRUE(mSuppressLevel == 0, NS_ERROR_FAILURE);
michael@0 236
michael@0 237 mDataTransfer = aDataTransfer;
michael@0 238 mSelection = nullptr;
michael@0 239 mHasImage = true;
michael@0 240 mDragPopup = nullptr;
michael@0 241 mImage = aImage;
michael@0 242 mImageX = aImageX;
michael@0 243 mImageY = aImageY;
michael@0 244
michael@0 245 aDragEvent->GetScreenX(&mScreenX);
michael@0 246 aDragEvent->GetScreenY(&mScreenY);
michael@0 247 aDragEvent->GetMozInputSource(&mInputSource);
michael@0 248
michael@0 249 return InvokeDragSession(aDOMNode, aTransferableArray, aRegion, aActionType);
michael@0 250 }
michael@0 251
michael@0 252 NS_IMETHODIMP
michael@0 253 nsBaseDragService::InvokeDragSessionWithSelection(nsISelection* aSelection,
michael@0 254 nsISupportsArray* aTransferableArray,
michael@0 255 uint32_t aActionType,
michael@0 256 nsIDOMDragEvent* aDragEvent,
michael@0 257 nsIDOMDataTransfer* aDataTransfer)
michael@0 258 {
michael@0 259 NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
michael@0 260 NS_ENSURE_TRUE(aDragEvent, NS_ERROR_NULL_POINTER);
michael@0 261 NS_ENSURE_TRUE(mSuppressLevel == 0, NS_ERROR_FAILURE);
michael@0 262
michael@0 263 mDataTransfer = aDataTransfer;
michael@0 264 mSelection = aSelection;
michael@0 265 mHasImage = true;
michael@0 266 mDragPopup = nullptr;
michael@0 267 mImage = nullptr;
michael@0 268 mImageX = 0;
michael@0 269 mImageY = 0;
michael@0 270
michael@0 271 aDragEvent->GetScreenX(&mScreenX);
michael@0 272 aDragEvent->GetScreenY(&mScreenY);
michael@0 273 aDragEvent->GetMozInputSource(&mInputSource);
michael@0 274
michael@0 275 // just get the focused node from the selection
michael@0 276 // XXXndeakin this should actually be the deepest node that contains both
michael@0 277 // endpoints of the selection
michael@0 278 nsCOMPtr<nsIDOMNode> node;
michael@0 279 aSelection->GetFocusNode(getter_AddRefs(node));
michael@0 280
michael@0 281 return InvokeDragSession(node, aTransferableArray, nullptr, aActionType);
michael@0 282 }
michael@0 283
michael@0 284 //-------------------------------------------------------------------------
michael@0 285 NS_IMETHODIMP
michael@0 286 nsBaseDragService::GetCurrentSession(nsIDragSession ** aSession)
michael@0 287 {
michael@0 288 if (!aSession)
michael@0 289 return NS_ERROR_INVALID_ARG;
michael@0 290
michael@0 291 // "this" also implements a drag session, so say we are one but only
michael@0 292 // if there is currently a drag going on.
michael@0 293 if (!mSuppressLevel && mDoingDrag) {
michael@0 294 *aSession = this;
michael@0 295 NS_ADDREF(*aSession); // addRef because we're a "getter"
michael@0 296 }
michael@0 297 else
michael@0 298 *aSession = nullptr;
michael@0 299
michael@0 300 return NS_OK;
michael@0 301 }
michael@0 302
michael@0 303 //-------------------------------------------------------------------------
michael@0 304 NS_IMETHODIMP
michael@0 305 nsBaseDragService::StartDragSession()
michael@0 306 {
michael@0 307 if (mDoingDrag) {
michael@0 308 return NS_ERROR_FAILURE;
michael@0 309 }
michael@0 310 mDoingDrag = true;
michael@0 311 // By default dispatch drop also to content.
michael@0 312 mOnlyChromeDrop = false;
michael@0 313
michael@0 314 return NS_OK;
michael@0 315 }
michael@0 316
michael@0 317 void
michael@0 318 nsBaseDragService::OpenDragPopup()
michael@0 319 {
michael@0 320 if (mDragPopup) {
michael@0 321 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
michael@0 322 if (pm) {
michael@0 323 pm->ShowPopupAtScreen(mDragPopup, mScreenX - mImageX, mScreenY - mImageY, false, nullptr);
michael@0 324 }
michael@0 325 }
michael@0 326 }
michael@0 327
michael@0 328 //-------------------------------------------------------------------------
michael@0 329 NS_IMETHODIMP
michael@0 330 nsBaseDragService::EndDragSession(bool aDoneDrag)
michael@0 331 {
michael@0 332 if (!mDoingDrag) {
michael@0 333 return NS_ERROR_FAILURE;
michael@0 334 }
michael@0 335
michael@0 336 if (aDoneDrag && !mSuppressLevel)
michael@0 337 FireDragEventAtSource(NS_DRAGDROP_END);
michael@0 338
michael@0 339 if (mDragPopup) {
michael@0 340 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
michael@0 341 if (pm) {
michael@0 342 pm->HidePopup(mDragPopup, false, true, false, false);
michael@0 343 }
michael@0 344 }
michael@0 345
michael@0 346 mDoingDrag = false;
michael@0 347
michael@0 348 // release the source we've been holding on to.
michael@0 349 mSourceDocument = nullptr;
michael@0 350 mSourceNode = nullptr;
michael@0 351 mSelection = nullptr;
michael@0 352 mDataTransfer = nullptr;
michael@0 353 mHasImage = false;
michael@0 354 mUserCancelled = false;
michael@0 355 mDragPopup = nullptr;
michael@0 356 mImage = nullptr;
michael@0 357 mImageX = 0;
michael@0 358 mImageY = 0;
michael@0 359 mScreenX = -1;
michael@0 360 mScreenY = -1;
michael@0 361 mInputSource = nsIDOMMouseEvent::MOZ_SOURCE_MOUSE;
michael@0 362
michael@0 363 return NS_OK;
michael@0 364 }
michael@0 365
michael@0 366 NS_IMETHODIMP
michael@0 367 nsBaseDragService::FireDragEventAtSource(uint32_t aMsg)
michael@0 368 {
michael@0 369 if (mSourceNode && !mSuppressLevel) {
michael@0 370 nsCOMPtr<nsIDocument> doc = do_QueryInterface(mSourceDocument);
michael@0 371 if (doc) {
michael@0 372 nsCOMPtr<nsIPresShell> presShell = doc->GetShell();
michael@0 373 if (presShell) {
michael@0 374 nsEventStatus status = nsEventStatus_eIgnore;
michael@0 375 WidgetDragEvent event(true, aMsg, nullptr);
michael@0 376 event.inputSource = mInputSource;
michael@0 377 if (aMsg == NS_DRAGDROP_END) {
michael@0 378 event.refPoint.x = mEndDragPoint.x;
michael@0 379 event.refPoint.y = mEndDragPoint.y;
michael@0 380 event.userCancelled = mUserCancelled;
michael@0 381 }
michael@0 382
michael@0 383 nsCOMPtr<nsIContent> content = do_QueryInterface(mSourceNode);
michael@0 384 return presShell->HandleDOMEventWithTarget(content, &event, &status);
michael@0 385 }
michael@0 386 }
michael@0 387 }
michael@0 388
michael@0 389 return NS_OK;
michael@0 390 }
michael@0 391
michael@0 392 /* This is used by Windows and Mac to update the position of a popup being
michael@0 393 * used as a drag image during the drag. This isn't used on GTK as it manages
michael@0 394 * the drag popup itself.
michael@0 395 */
michael@0 396 NS_IMETHODIMP
michael@0 397 nsBaseDragService::DragMoved(int32_t aX, int32_t aY)
michael@0 398 {
michael@0 399 if (mDragPopup) {
michael@0 400 nsIFrame* frame = mDragPopup->GetPrimaryFrame();
michael@0 401 if (frame && frame->GetType() == nsGkAtoms::menuPopupFrame) {
michael@0 402 (static_cast<nsMenuPopupFrame *>(frame))->MoveTo(aX - mImageX, aY - mImageY, true);
michael@0 403 }
michael@0 404 }
michael@0 405
michael@0 406 return NS_OK;
michael@0 407 }
michael@0 408
michael@0 409 static nsIPresShell*
michael@0 410 GetPresShellForContent(nsIDOMNode* aDOMNode)
michael@0 411 {
michael@0 412 nsCOMPtr<nsIContent> content = do_QueryInterface(aDOMNode);
michael@0 413 if (!content)
michael@0 414 return nullptr;
michael@0 415
michael@0 416 nsCOMPtr<nsIDocument> document = content->GetCurrentDoc();
michael@0 417 if (document) {
michael@0 418 document->FlushPendingNotifications(Flush_Display);
michael@0 419
michael@0 420 return document->GetShell();
michael@0 421 }
michael@0 422
michael@0 423 return nullptr;
michael@0 424 }
michael@0 425
michael@0 426 nsresult
michael@0 427 nsBaseDragService::DrawDrag(nsIDOMNode* aDOMNode,
michael@0 428 nsIScriptableRegion* aRegion,
michael@0 429 int32_t aScreenX, int32_t aScreenY,
michael@0 430 nsIntRect* aScreenDragRect,
michael@0 431 RefPtr<SourceSurface>* aSurface,
michael@0 432 nsPresContext** aPresContext)
michael@0 433 {
michael@0 434 *aSurface = nullptr;
michael@0 435 *aPresContext = nullptr;
michael@0 436
michael@0 437 // use a default size, in case of an error.
michael@0 438 aScreenDragRect->x = aScreenX - mImageX;
michael@0 439 aScreenDragRect->y = aScreenY - mImageY;
michael@0 440 aScreenDragRect->width = 1;
michael@0 441 aScreenDragRect->height = 1;
michael@0 442
michael@0 443 // if a drag image was specified, use that, otherwise, use the source node
michael@0 444 nsCOMPtr<nsIDOMNode> dragNode = mImage ? mImage.get() : aDOMNode;
michael@0 445
michael@0 446 // get the presshell for the node being dragged. If the drag image is not in
michael@0 447 // a document or has no frame, get the presshell from the source drag node
michael@0 448 nsIPresShell* presShell = GetPresShellForContent(dragNode);
michael@0 449 if (!presShell && mImage)
michael@0 450 presShell = GetPresShellForContent(aDOMNode);
michael@0 451 if (!presShell)
michael@0 452 return NS_ERROR_FAILURE;
michael@0 453
michael@0 454 *aPresContext = presShell->GetPresContext();
michael@0 455
michael@0 456 // convert mouse position to dev pixels of the prescontext
michael@0 457 int32_t sx = aScreenX, sy = aScreenY;
michael@0 458 ConvertToUnscaledDevPixels(*aPresContext, &sx, &sy);
michael@0 459
michael@0 460 aScreenDragRect->x = sx - mImageX;
michael@0 461 aScreenDragRect->y = sy - mImageY;
michael@0 462
michael@0 463 // check if drag images are disabled
michael@0 464 bool enableDragImages = Preferences::GetBool(DRAGIMAGES_PREF, true);
michael@0 465
michael@0 466 // didn't want an image, so just set the screen rectangle to the frame size
michael@0 467 if (!enableDragImages || !mHasImage) {
michael@0 468 // if a region was specified, set the screen rectangle to the area that
michael@0 469 // the region occupies
michael@0 470 if (aRegion) {
michael@0 471 // the region's coordinates are relative to the root frame
michael@0 472 nsIFrame* rootFrame = presShell->GetRootFrame();
michael@0 473 if (rootFrame && *aPresContext) {
michael@0 474 nsIntRect dragRect;
michael@0 475 aRegion->GetBoundingBox(&dragRect.x, &dragRect.y, &dragRect.width, &dragRect.height);
michael@0 476 dragRect = dragRect.ToAppUnits(nsPresContext::AppUnitsPerCSSPixel()).
michael@0 477 ToOutsidePixels((*aPresContext)->AppUnitsPerDevPixel());
michael@0 478
michael@0 479 nsIntRect screenRect = rootFrame->GetScreenRectExternal();
michael@0 480 aScreenDragRect->SetRect(screenRect.x + dragRect.x, screenRect.y + dragRect.y,
michael@0 481 dragRect.width, dragRect.height);
michael@0 482 }
michael@0 483 }
michael@0 484 else {
michael@0 485 // otherwise, there was no region so just set the rectangle to
michael@0 486 // the size of the primary frame of the content.
michael@0 487 nsCOMPtr<nsIContent> content = do_QueryInterface(dragNode);
michael@0 488 nsIFrame* frame = content->GetPrimaryFrame();
michael@0 489 if (frame) {
michael@0 490 nsIntRect screenRect = frame->GetScreenRectExternal();
michael@0 491 aScreenDragRect->SetRect(screenRect.x, screenRect.y,
michael@0 492 screenRect.width, screenRect.height);
michael@0 493 }
michael@0 494 }
michael@0 495
michael@0 496 return NS_OK;
michael@0 497 }
michael@0 498
michael@0 499 // draw the image for selections
michael@0 500 if (mSelection) {
michael@0 501 nsIntPoint pnt(aScreenDragRect->x, aScreenDragRect->y);
michael@0 502 *aSurface = presShell->RenderSelection(mSelection, pnt, aScreenDragRect);
michael@0 503 return NS_OK;
michael@0 504 }
michael@0 505
michael@0 506 // if a custom image was specified, check if it is an image node and draw
michael@0 507 // using the source rather than the displayed image. But if mImage isn't
michael@0 508 // an image or canvas, fall through to RenderNode below.
michael@0 509 if (mImage) {
michael@0 510 nsCOMPtr<nsIContent> content = do_QueryInterface(dragNode);
michael@0 511 HTMLCanvasElement *canvas = HTMLCanvasElement::FromContentOrNull(content);
michael@0 512 if (canvas) {
michael@0 513 return DrawDragForImage(*aPresContext, nullptr, canvas, sx, sy,
michael@0 514 aScreenDragRect, aSurface);
michael@0 515 }
michael@0 516
michael@0 517 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(dragNode);
michael@0 518 // for image nodes, create the drag image from the actual image data
michael@0 519 if (imageLoader) {
michael@0 520 return DrawDragForImage(*aPresContext, imageLoader, nullptr, sx, sy,
michael@0 521 aScreenDragRect, aSurface);
michael@0 522 }
michael@0 523
michael@0 524 // If the image is a popup, use that as the image. This allows custom drag
michael@0 525 // images that can change during the drag, but means that any platform
michael@0 526 // default image handling won't occur.
michael@0 527 // XXXndeakin this should be chrome-only
michael@0 528
michael@0 529 nsIFrame* frame = content->GetPrimaryFrame();
michael@0 530 if (frame && frame->GetType() == nsGkAtoms::menuPopupFrame) {
michael@0 531 mDragPopup = content;
michael@0 532 }
michael@0 533 }
michael@0 534
michael@0 535 if (!mDragPopup) {
michael@0 536 // otherwise, just draw the node
michael@0 537 nsIntRegion clipRegion;
michael@0 538 if (aRegion) {
michael@0 539 aRegion->GetRegion(&clipRegion);
michael@0 540 }
michael@0 541
michael@0 542 nsIntPoint pnt(aScreenDragRect->x, aScreenDragRect->y);
michael@0 543 *aSurface = presShell->RenderNode(dragNode, aRegion ? &clipRegion : nullptr,
michael@0 544 pnt, aScreenDragRect);
michael@0 545 }
michael@0 546
michael@0 547 // if an image was specified, reposition the drag rectangle to
michael@0 548 // the supplied offset in mImageX and mImageY.
michael@0 549 if (mImage) {
michael@0 550 aScreenDragRect->x = sx - mImageX;
michael@0 551 aScreenDragRect->y = sy - mImageY;
michael@0 552 }
michael@0 553
michael@0 554 return NS_OK;
michael@0 555 }
michael@0 556
michael@0 557 nsresult
michael@0 558 nsBaseDragService::DrawDragForImage(nsPresContext* aPresContext,
michael@0 559 nsIImageLoadingContent* aImageLoader,
michael@0 560 HTMLCanvasElement* aCanvas,
michael@0 561 int32_t aScreenX, int32_t aScreenY,
michael@0 562 nsIntRect* aScreenDragRect,
michael@0 563 RefPtr<SourceSurface>* aSurface)
michael@0 564 {
michael@0 565 nsCOMPtr<imgIContainer> imgContainer;
michael@0 566 if (aImageLoader) {
michael@0 567 nsCOMPtr<imgIRequest> imgRequest;
michael@0 568 nsresult rv = aImageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
michael@0 569 getter_AddRefs(imgRequest));
michael@0 570 NS_ENSURE_SUCCESS(rv, rv);
michael@0 571 if (!imgRequest)
michael@0 572 return NS_ERROR_NOT_AVAILABLE;
michael@0 573
michael@0 574 rv = imgRequest->GetImage(getter_AddRefs(imgContainer));
michael@0 575 NS_ENSURE_SUCCESS(rv, rv);
michael@0 576 if (!imgContainer)
michael@0 577 return NS_ERROR_NOT_AVAILABLE;
michael@0 578
michael@0 579 // use the size of the image as the size of the drag image
michael@0 580 imgContainer->GetWidth(&aScreenDragRect->width);
michael@0 581 imgContainer->GetHeight(&aScreenDragRect->height);
michael@0 582 }
michael@0 583 else {
michael@0 584 NS_ASSERTION(aCanvas, "both image and canvas are null");
michael@0 585 nsIntSize sz = aCanvas->GetSize();
michael@0 586 aScreenDragRect->width = sz.width;
michael@0 587 aScreenDragRect->height = sz.height;
michael@0 588 }
michael@0 589
michael@0 590 nsIntSize srcSize = aScreenDragRect->Size();
michael@0 591 nsIntSize destSize = srcSize;
michael@0 592
michael@0 593 if (destSize.width == 0 || destSize.height == 0)
michael@0 594 return NS_ERROR_FAILURE;
michael@0 595
michael@0 596 // if the image is larger than half the screen size, scale it down. This
michael@0 597 // scaling algorithm is the same as is used in nsPresShell::PaintRangePaintInfo
michael@0 598 nsDeviceContext* deviceContext = aPresContext->DeviceContext();
michael@0 599 nsRect maxSize;
michael@0 600 deviceContext->GetClientRect(maxSize);
michael@0 601 nscoord maxWidth = aPresContext->AppUnitsToDevPixels(maxSize.width >> 1);
michael@0 602 nscoord maxHeight = aPresContext->AppUnitsToDevPixels(maxSize.height >> 1);
michael@0 603 if (destSize.width > maxWidth || destSize.height > maxHeight) {
michael@0 604 float scale = 1.0;
michael@0 605 if (destSize.width > maxWidth)
michael@0 606 scale = std::min(scale, float(maxWidth) / destSize.width);
michael@0 607 if (destSize.height > maxHeight)
michael@0 608 scale = std::min(scale, float(maxHeight) / destSize.height);
michael@0 609
michael@0 610 destSize.width = NSToIntFloor(float(destSize.width) * scale);
michael@0 611 destSize.height = NSToIntFloor(float(destSize.height) * scale);
michael@0 612
michael@0 613 aScreenDragRect->x = NSToIntFloor(aScreenX - float(mImageX) * scale);
michael@0 614 aScreenDragRect->y = NSToIntFloor(aScreenY - float(mImageY) * scale);
michael@0 615 aScreenDragRect->width = destSize.width;
michael@0 616 aScreenDragRect->height = destSize.height;
michael@0 617 }
michael@0 618
michael@0 619 nsresult result = NS_OK;
michael@0 620 if (aImageLoader) {
michael@0 621 RefPtr<DrawTarget> dt =
michael@0 622 gfxPlatform::GetPlatform()->
michael@0 623 CreateOffscreenContentDrawTarget(destSize.ToIntSize(),
michael@0 624 SurfaceFormat::B8G8R8A8);
michael@0 625 if (!dt)
michael@0 626 return NS_ERROR_FAILURE;
michael@0 627
michael@0 628 nsRefPtr<gfxContext> ctx = new gfxContext(dt);
michael@0 629 if (!ctx)
michael@0 630 return NS_ERROR_FAILURE;
michael@0 631
michael@0 632 gfxRect outRect(0, 0, destSize.width, destSize.height);
michael@0 633 gfxMatrix scale =
michael@0 634 gfxMatrix().Scale(srcSize.width/outRect.Width(), srcSize.height/outRect.Height());
michael@0 635 nsIntRect imgSize(0, 0, srcSize.width, srcSize.height);
michael@0 636 imgContainer->Draw(ctx, GraphicsFilter::FILTER_GOOD, scale, outRect, imgSize,
michael@0 637 destSize, nullptr, imgIContainer::FRAME_CURRENT,
michael@0 638 imgIContainer::FLAG_SYNC_DECODE);
michael@0 639 *aSurface = dt->Snapshot();
michael@0 640 } else {
michael@0 641 *aSurface = aCanvas->GetSurfaceSnapshot();
michael@0 642 }
michael@0 643
michael@0 644 return result;
michael@0 645 }
michael@0 646
michael@0 647 void
michael@0 648 nsBaseDragService::ConvertToUnscaledDevPixels(nsPresContext* aPresContext,
michael@0 649 int32_t* aScreenX, int32_t* aScreenY)
michael@0 650 {
michael@0 651 int32_t adj = aPresContext->DeviceContext()->UnscaledAppUnitsPerDevPixel();
michael@0 652 *aScreenX = nsPresContext::CSSPixelsToAppUnits(*aScreenX) / adj;
michael@0 653 *aScreenY = nsPresContext::CSSPixelsToAppUnits(*aScreenY) / adj;
michael@0 654 }
michael@0 655
michael@0 656 NS_IMETHODIMP
michael@0 657 nsBaseDragService::Suppress()
michael@0 658 {
michael@0 659 EndDragSession(false);
michael@0 660 ++mSuppressLevel;
michael@0 661 return NS_OK;
michael@0 662 }
michael@0 663
michael@0 664 NS_IMETHODIMP
michael@0 665 nsBaseDragService::Unsuppress()
michael@0 666 {
michael@0 667 --mSuppressLevel;
michael@0 668 return NS_OK;
michael@0 669 }

mercurial