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