1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsBaseDragService.h"
7 #include "nsITransferable.h"
9 #include "nsArrayUtils.h"
10 #include "nsITransferable.h"
14 #include "nsIInterfaceRequestorUtils.h"
16 #include "nsFrameLoaderOwner.h"
17 #include "mozilla/dom/Document.h"
18 #include "nsIContent.h"
19 #include "nsViewManager.h"
21 #include "nsPresContext.h"
22 #include "nsIImageLoadingContent.h"
23 #include "imgIContainer.h"
24 #include "imgIRequest.h"
25 #include "ImageRegion.h"
26 #include "nsQueryObject.h"
28 #include "nsXULPopupManager.h"
29 #include "nsMenuPopupFrame.h"
30 #include "SVGImageContext.h"
32 # include "nsTreeBodyFrame.h"
34 #include "mozilla/MouseEvents.h"
35 #include "mozilla/Preferences.h"
36 #include "mozilla/PresShell.h"
37 #include "mozilla/dom/BindingDeclarations.h"
38 #include "mozilla/dom/DataTransferItemList.h"
39 #include "mozilla/dom/DataTransfer.h"
40 #include "mozilla/dom/DocumentInlines.h"
41 #include "mozilla/dom/DragEvent.h"
42 #include "mozilla/dom/MouseEventBinding.h"
43 #include "mozilla/dom/Selection.h"
44 #include "mozilla/gfx/2D.h"
45 #include "mozilla/Unused.h"
46 #include "nsFrameLoader.h"
47 #include "BrowserParent.h"
48 #include "nsIMutableArray.h"
49 #include "gfxContext.h"
50 #include "gfxPlatform.h"
53 using namespace mozilla
;
54 using namespace mozilla::dom
;
55 using namespace mozilla::gfx
;
56 using namespace mozilla::image
;
58 #define DRAGIMAGES_PREF "nglayout.enable_drag_images"
60 nsBaseDragService::nsBaseDragService()
62 mOnlyChromeDrop(false),
64 mSessionIsSynthesizedForTests(false),
65 mEndingSession(false),
67 mUserCancelled(false),
68 mDragEventDispatchedToChildProcess(false),
69 mDragAction(DRAGDROP_ACTION_NONE
),
70 mDragActionFromChildProcess(DRAGDROP_ACTION_UNINITIALIZED
),
71 mEffectAllowedForTests(DRAGDROP_ACTION_UNINITIALIZED
),
72 mContentPolicyType(nsIContentPolicy::TYPE_OTHER
),
74 mInputSource(MouseEvent_Binding::MOZ_SOURCE_MOUSE
) {}
76 nsBaseDragService::~nsBaseDragService() = default;
78 NS_IMPL_ISUPPORTS(nsBaseDragService
, nsIDragService
, nsIDragSession
)
80 //---------------------------------------------------------
82 nsBaseDragService::SetCanDrop(bool aCanDrop
) {
87 //---------------------------------------------------------
89 nsBaseDragService::GetCanDrop(bool* aCanDrop
) {
93 //---------------------------------------------------------
95 nsBaseDragService::SetOnlyChromeDrop(bool aOnlyChrome
) {
96 mOnlyChromeDrop
= aOnlyChrome
;
100 //---------------------------------------------------------
102 nsBaseDragService::GetOnlyChromeDrop(bool* aOnlyChrome
) {
103 *aOnlyChrome
= mOnlyChromeDrop
;
107 //---------------------------------------------------------
109 nsBaseDragService::SetDragAction(uint32_t anAction
) {
110 mDragAction
= anAction
;
114 //---------------------------------------------------------
116 nsBaseDragService::GetDragAction(uint32_t* anAction
) {
117 *anAction
= mDragAction
;
121 //-------------------------------------------------------------------------
124 nsBaseDragService::GetNumDropItems(uint32_t* aNumItems
) {
126 return NS_ERROR_FAILURE
;
132 // Returns the DOM document where the drag was initiated. This will be
133 // nullptr if the drag began outside of our application.
136 nsBaseDragService::GetSourceDocument(Document
** aSourceDocument
) {
137 *aSourceDocument
= mSourceDocument
.get();
138 NS_IF_ADDREF(*aSourceDocument
);
146 // Returns the DOM node where the drag was initiated. This will be
147 // nullptr if the drag began outside of our application.
150 nsBaseDragService::GetSourceNode(nsINode
** aSourceNode
) {
151 *aSourceNode
= do_AddRef(mSourceNode
).take();
156 nsBaseDragService::GetTriggeringPrincipal(nsIPrincipal
** aPrincipal
) {
157 NS_IF_ADDREF(*aPrincipal
= mTriggeringPrincipal
);
162 nsBaseDragService::SetTriggeringPrincipal(nsIPrincipal
* aPrincipal
) {
163 mTriggeringPrincipal
= aPrincipal
;
168 nsBaseDragService::GetCsp(nsIContentSecurityPolicy
** aCsp
) {
169 NS_IF_ADDREF(*aCsp
= mCsp
);
174 nsBaseDragService::SetCsp(nsIContentSecurityPolicy
* aCsp
) {
179 //-------------------------------------------------------------------------
182 nsBaseDragService::GetData(nsITransferable
* aTransferable
,
183 uint32_t aItemIndex
) {
184 return NS_ERROR_FAILURE
;
187 //-------------------------------------------------------------------------
189 nsBaseDragService::IsDataFlavorSupported(const char* aDataFlavor
,
191 return NS_ERROR_FAILURE
;
195 nsBaseDragService::GetDataTransferXPCOM(DataTransfer
** aDataTransfer
) {
196 *aDataTransfer
= mDataTransfer
;
197 NS_IF_ADDREF(*aDataTransfer
);
202 nsBaseDragService::SetDataTransferXPCOM(DataTransfer
* aDataTransfer
) {
203 NS_ENSURE_STATE(aDataTransfer
);
204 mDataTransfer
= aDataTransfer
;
208 DataTransfer
* nsBaseDragService::GetDataTransfer() { return mDataTransfer
; }
210 void nsBaseDragService::SetDataTransfer(DataTransfer
* aDataTransfer
) {
211 mDataTransfer
= aDataTransfer
;
214 bool nsBaseDragService::IsSynthesizedForTests() {
215 return mSessionIsSynthesizedForTests
;
218 uint32_t nsBaseDragService::GetEffectAllowedForTests() {
219 MOZ_ASSERT(mSessionIsSynthesizedForTests
);
220 return mEffectAllowedForTests
;
223 NS_IMETHODIMP
nsBaseDragService::SetDragEndPointForTests(int32_t aScreenX
,
225 MOZ_ASSERT(mDoingDrag
);
226 MOZ_ASSERT(mSourceDocument
);
227 MOZ_ASSERT(mSessionIsSynthesizedForTests
);
228 if (!mDoingDrag
|| !mSourceDocument
|| !mSessionIsSynthesizedForTests
) {
229 return NS_ERROR_FAILURE
;
231 nsPresContext
* presContext
= mSourceDocument
->GetPresContext();
232 if (NS_WARN_IF(!presContext
)) {
233 return NS_ERROR_FAILURE
;
236 LayoutDeviceIntPoint(presContext
->CSSPixelsToDevPixels(aScreenX
),
237 presContext
->CSSPixelsToDevPixels(aScreenY
)));
241 //-------------------------------------------------------------------------
243 nsBaseDragService::InvokeDragSession(
244 nsINode
* aDOMNode
, nsIPrincipal
* aPrincipal
, nsIContentSecurityPolicy
* aCsp
,
245 nsIArray
* aTransferableArray
, uint32_t aActionType
,
246 nsContentPolicyType aContentPolicyType
= nsIContentPolicy::TYPE_OTHER
) {
247 AUTO_PROFILER_LABEL("nsBaseDragService::InvokeDragSession", OTHER
);
249 NS_ENSURE_TRUE(aDOMNode
, NS_ERROR_INVALID_ARG
);
250 NS_ENSURE_TRUE(mSuppressLevel
== 0, NS_ERROR_FAILURE
);
252 // stash the document of the dom node
253 mSourceDocument
= aDOMNode
->OwnerDoc();
254 mTriggeringPrincipal
= aPrincipal
;
256 mSourceNode
= aDOMNode
;
257 mContentPolicyType
= aContentPolicyType
;
258 mEndDragPoint
= LayoutDeviceIntPoint(0, 0);
260 // When the mouse goes down, the selection code starts a mouse
261 // capture. However, this gets in the way of determining drag
262 // feedback for things like trees because the event coordinates
263 // are in the wrong coord system, so turn off mouse capture.
264 PresShell::ClearMouseCapture(nullptr);
266 if (mSessionIsSynthesizedForTests
) {
268 mDragAction
= aActionType
;
269 mEffectAllowedForTests
= aActionType
;
273 // If you're hitting this, a test is causing the browser to attempt to enter
274 // the drag-drop native nested event loop, which will put the browser in a
275 // state that won't run tests properly until there's manual intervention
276 // to exit the drag-drop loop (either by moving the mouse or hitting escape),
277 // which can't be done from script since we're in the nested loop.
279 // The best way to avoid this is to catch the dragstart event on the item
280 // being dragged, and then to call preventDefault() and stopPropagating() on
282 if (XRE_IsParentProcess()) {
284 !xpc::IsInAutomation(),
285 "About to start drag-drop native loop on which will prevent later "
286 "tests from running properly.");
290 mozilla::Unused
<< aTransferableArray
->GetLength(&length
);
292 nsCOMPtr
<nsIMutableArray
> mutableArray
=
293 do_QueryInterface(aTransferableArray
);
295 // In order to be able trigger dnd, we need to have some transferable
297 nsCOMPtr
<nsITransferable
> trans
=
298 do_CreateInstance("@mozilla.org/widget/transferable;1");
299 trans
->Init(nullptr);
300 trans
->SetRequestingPrincipal(mSourceNode
->NodePrincipal());
301 trans
->SetContentPolicyType(mContentPolicyType
);
302 mutableArray
->AppendElement(trans
);
305 for (uint32_t i
= 0; i
< length
; ++i
) {
306 nsCOMPtr
<nsITransferable
> trans
=
307 do_QueryElementAt(aTransferableArray
, i
);
309 // Set the requestingPrincipal on the transferable.
310 trans
->SetRequestingPrincipal(mSourceNode
->NodePrincipal());
311 trans
->SetContentPolicyType(mContentPolicyType
);
316 nsresult rv
= InvokeDragSessionImpl(aTransferableArray
, mRegion
, aActionType
);
319 // Set mDoingDrag so that EndDragSession cleans up and sends the dragend
320 // event after the aborted drag.
322 EndDragSession(true, 0);
329 nsBaseDragService::InvokeDragSessionWithImage(
330 nsINode
* aDOMNode
, nsIPrincipal
* aPrincipal
, nsIContentSecurityPolicy
* aCsp
,
331 nsIArray
* aTransferableArray
, uint32_t aActionType
, nsINode
* aImage
,
332 int32_t aImageX
, int32_t aImageY
, DragEvent
* aDragEvent
,
333 DataTransfer
* aDataTransfer
) {
334 NS_ENSURE_TRUE(aDragEvent
, NS_ERROR_NULL_POINTER
);
335 NS_ENSURE_TRUE(aDataTransfer
, NS_ERROR_NULL_POINTER
);
336 NS_ENSURE_TRUE(mSuppressLevel
== 0, NS_ERROR_FAILURE
);
338 mSessionIsSynthesizedForTests
=
339 aDragEvent
->WidgetEventPtr()->mFlags
.mIsSynthesizedForTests
;
340 mDataTransfer
= aDataTransfer
;
341 mSelection
= nullptr;
343 mDragPopup
= nullptr;
345 mImageOffset
= CSSIntPoint(aImageX
, aImageY
);
346 mDragStartData
= nullptr;
348 mScreenPosition
.x
= aDragEvent
->ScreenX(CallerType::System
);
349 mScreenPosition
.y
= aDragEvent
->ScreenY(CallerType::System
);
350 mInputSource
= aDragEvent
->MozInputSource();
352 // If dragging within a XUL tree and no custom drag image was
353 // set, the region argument to InvokeDragSessionWithImage needs
354 // to be set to the area encompassing the selected rows of the
355 // tree to ensure that the drag feedback gets clipped to those
356 // rows. For other content, region should be null.
359 if (aDOMNode
&& aDOMNode
->IsContent() && !aImage
) {
360 if (aDOMNode
->NodeInfo()->Equals(nsGkAtoms::treechildren
,
362 nsTreeBodyFrame
* treeBody
=
363 do_QueryFrame(aDOMNode
->AsContent()->GetPrimaryFrame());
365 mRegion
= treeBody
->GetSelectionRegion();
372 InvokeDragSession(aDOMNode
, aPrincipal
, aCsp
, aTransferableArray
,
373 aActionType
, nsIContentPolicy::TYPE_INTERNAL_IMAGE
);
379 nsBaseDragService::InvokeDragSessionWithRemoteImage(
380 nsINode
* aDOMNode
, nsIPrincipal
* aPrincipal
, nsIContentSecurityPolicy
* aCsp
,
381 nsIArray
* aTransferableArray
, uint32_t aActionType
,
382 RemoteDragStartData
* aDragStartData
, DragEvent
* aDragEvent
,
383 DataTransfer
* aDataTransfer
) {
384 NS_ENSURE_TRUE(aDragEvent
, NS_ERROR_NULL_POINTER
);
385 NS_ENSURE_TRUE(aDataTransfer
, NS_ERROR_NULL_POINTER
);
386 NS_ENSURE_TRUE(mSuppressLevel
== 0, NS_ERROR_FAILURE
);
388 mSessionIsSynthesizedForTests
=
389 aDragEvent
->WidgetEventPtr()->mFlags
.mIsSynthesizedForTests
;
390 mDataTransfer
= aDataTransfer
;
391 mSelection
= nullptr;
393 mDragPopup
= nullptr;
395 mDragStartData
= aDragStartData
;
396 mImageOffset
= CSSIntPoint(0, 0);
398 mScreenPosition
.x
= aDragEvent
->ScreenX(CallerType::System
);
399 mScreenPosition
.y
= aDragEvent
->ScreenY(CallerType::System
);
400 mInputSource
= aDragEvent
->MozInputSource();
403 InvokeDragSession(aDOMNode
, aPrincipal
, aCsp
, aTransferableArray
,
404 aActionType
, nsIContentPolicy::TYPE_INTERNAL_IMAGE
);
410 nsBaseDragService::InvokeDragSessionWithSelection(
411 Selection
* aSelection
, nsIPrincipal
* aPrincipal
,
412 nsIContentSecurityPolicy
* aCsp
, nsIArray
* aTransferableArray
,
413 uint32_t aActionType
, DragEvent
* aDragEvent
, DataTransfer
* aDataTransfer
) {
414 NS_ENSURE_TRUE(aSelection
, NS_ERROR_NULL_POINTER
);
415 NS_ENSURE_TRUE(aDragEvent
, NS_ERROR_NULL_POINTER
);
416 NS_ENSURE_TRUE(mSuppressLevel
== 0, NS_ERROR_FAILURE
);
418 mSessionIsSynthesizedForTests
=
419 aDragEvent
->WidgetEventPtr()->mFlags
.mIsSynthesizedForTests
;
420 mDataTransfer
= aDataTransfer
;
421 mSelection
= aSelection
;
423 mDragPopup
= nullptr;
425 mImageOffset
= CSSIntPoint();
426 mDragStartData
= nullptr;
429 mScreenPosition
.x
= aDragEvent
->ScreenX(CallerType::System
);
430 mScreenPosition
.y
= aDragEvent
->ScreenY(CallerType::System
);
431 mInputSource
= aDragEvent
->MozInputSource();
433 // just get the focused node from the selection
434 // XXXndeakin this should actually be the deepest node that contains both
435 // endpoints of the selection
436 nsCOMPtr
<nsINode
> node
= aSelection
->GetFocusNode();
438 return InvokeDragSession(node
, aPrincipal
, aCsp
, aTransferableArray
,
439 aActionType
, nsIContentPolicy::TYPE_OTHER
);
442 //-------------------------------------------------------------------------
444 nsBaseDragService::GetCurrentSession(nsIDragSession
** aSession
) {
445 if (!aSession
) return NS_ERROR_INVALID_ARG
;
447 // "this" also implements a drag session, so say we are one but only
448 // if there is currently a drag going on.
449 if (!mSuppressLevel
&& mDoingDrag
) {
451 NS_ADDREF(*aSession
); // addRef because we're a "getter"
458 //-------------------------------------------------------------------------
460 nsBaseDragService::StartDragSession() {
462 return NS_ERROR_FAILURE
;
465 // By default dispatch drop also to content.
466 mOnlyChromeDrop
= false;
471 NS_IMETHODIMP
nsBaseDragService::StartDragSessionForTests(
472 uint32_t aAllowedEffect
) {
473 if (NS_WARN_IF(NS_FAILED(StartDragSession()))) {
474 return NS_ERROR_FAILURE
;
476 mDragAction
= aAllowedEffect
;
477 mEffectAllowedForTests
= aAllowedEffect
;
478 mSessionIsSynthesizedForTests
= true;
482 void nsBaseDragService::OpenDragPopup() {
484 nsXULPopupManager
* pm
= nsXULPopupManager::GetInstance();
486 pm
->ShowPopupAtScreen(mDragPopup
, mScreenPosition
.x
- mImageOffset
.x
,
487 mScreenPosition
.y
- mImageOffset
.y
, false, nullptr);
492 int32_t nsBaseDragService::TakeChildProcessDragAction() {
493 // If the last event was dispatched to the child process, use the drag action
494 // assigned from it instead and return it. DRAGDROP_ACTION_UNINITIALIZED is
495 // returned otherwise.
496 int32_t retval
= DRAGDROP_ACTION_UNINITIALIZED
;
497 if (TakeDragEventDispatchedToChildProcess() &&
498 mDragActionFromChildProcess
!= DRAGDROP_ACTION_UNINITIALIZED
) {
499 retval
= mDragActionFromChildProcess
;
505 //-------------------------------------------------------------------------
507 nsBaseDragService::EndDragSession(bool aDoneDrag
, uint32_t aKeyModifiers
) {
508 if (!mDoingDrag
|| mEndingSession
) {
509 return NS_ERROR_FAILURE
;
512 mEndingSession
= true;
514 if (aDoneDrag
&& !mSuppressLevel
) {
515 FireDragEventAtSource(eDragEnd
, aKeyModifiers
);
519 nsXULPopupManager
* pm
= nsXULPopupManager::GetInstance();
521 pm
->HidePopup(mDragPopup
, false, true, false, false);
525 for (uint32_t i
= 0; i
< mChildProcesses
.Length(); ++i
) {
526 mozilla::Unused
<< mChildProcesses
[i
]->SendEndDragSession(
527 aDoneDrag
, mUserCancelled
, mEndDragPoint
, aKeyModifiers
);
528 // Continue sending input events with input priority when stopping the dnd
530 mChildProcesses
[i
]->SetInputPriorityEventEnabled(true);
532 mChildProcesses
.Clear();
534 // mDataTransfer and the items it owns are going to die anyway, but we
535 // explicitly deref the contained data here so that we don't have to wait for
536 // CC to reclaim the memory.
537 if (XRE_IsParentProcess()) {
538 DiscardInternalTransferData();
542 mSessionIsSynthesizedForTests
= false;
543 mEffectAllowedForTests
= nsIDragService::DRAGDROP_ACTION_UNINITIALIZED
;
544 mEndingSession
= false;
547 // release the source we've been holding on to.
548 mSourceDocument
= nullptr;
549 mSourceNode
= nullptr;
550 mTriggeringPrincipal
= nullptr;
552 mSelection
= nullptr;
553 mDataTransfer
= nullptr;
555 mUserCancelled
= false;
556 mDragPopup
= nullptr;
557 mDragStartData
= nullptr;
559 mImageOffset
= CSSIntPoint();
560 mScreenPosition
= CSSIntPoint();
561 mEndDragPoint
= LayoutDeviceIntPoint(0, 0);
562 mInputSource
= MouseEvent_Binding::MOZ_SOURCE_MOUSE
;
568 void nsBaseDragService::DiscardInternalTransferData() {
569 if (mDataTransfer
&& mSourceNode
) {
570 MOZ_ASSERT(mDataTransfer
);
572 DataTransferItemList
* items
= mDataTransfer
->Items();
573 for (size_t i
= 0; i
< items
->Length(); i
++) {
575 DataTransferItem
* item
= items
->IndexedGetter(i
, found
);
577 // Non-OTHER items may still be needed by JS. Skip them.
578 if (!found
|| item
->Kind() != DataTransferItem::KIND_OTHER
) {
582 nsCOMPtr
<nsIVariant
> variant
= item
->DataNoSecurityCheck();
583 nsCOMPtr
<nsIWritableVariant
> writable
= do_QueryInterface(variant
);
586 writable
->SetAsEmpty();
593 nsBaseDragService::FireDragEventAtSource(EventMessage aEventMessage
,
594 uint32_t aKeyModifiers
) {
595 if (mSourceNode
&& mSourceDocument
&& !mSuppressLevel
) {
596 RefPtr
<PresShell
> presShell
= mSourceDocument
->GetPresShell();
598 nsEventStatus status
= nsEventStatus_eIgnore
;
599 WidgetDragEvent
event(true, aEventMessage
, nullptr);
600 event
.mFlags
.mIsSynthesizedForTests
= mSessionIsSynthesizedForTests
;
601 event
.mInputSource
= mInputSource
;
602 if (aEventMessage
== eDragEnd
) {
603 event
.mRefPoint
= mEndDragPoint
;
604 event
.mUserCancelled
= mUserCancelled
;
606 event
.mModifiers
= aKeyModifiers
;
607 // Send the drag event to APZ, which needs to know about them to be
608 // able to accurately detect the end of a drag gesture.
609 if (nsPresContext
* presContext
= presShell
->GetPresContext()) {
610 if (nsCOMPtr
<nsIWidget
> widget
= presContext
->GetRootWidget()) {
611 widget
->DispatchEventToAPZOnly(&event
);
615 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(mSourceNode
);
616 return presShell
->HandleDOMEventWithTarget(content
, &event
, &status
);
623 /* This is used by Windows and Mac to update the position of a popup being
624 * used as a drag image during the drag. This isn't used on GTK as it manages
625 * the drag popup itself.
628 nsBaseDragService::DragMoved(int32_t aX
, int32_t aY
) {
630 nsIFrame
* frame
= mDragPopup
->GetPrimaryFrame();
631 if (frame
&& frame
->IsMenuPopupFrame()) {
633 RoundedToInt(LayoutDeviceIntPoint(aX
, aY
) /
634 frame
->PresContext()->CSSToDevPixelScale()) -
636 (static_cast<nsMenuPopupFrame
*>(frame
))->MoveTo(cssPos
, true);
643 static PresShell
* GetPresShellForContent(nsINode
* aDOMNode
) {
644 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(aDOMNode
);
645 if (!content
) return nullptr;
647 RefPtr
<Document
> document
= content
->GetComposedDoc();
649 document
->FlushPendingNotifications(FlushType::Display
);
650 return document
->GetPresShell();
656 nsresult
nsBaseDragService::DrawDrag(nsINode
* aDOMNode
,
657 const Maybe
<CSSIntRegion
>& aRegion
,
658 CSSIntPoint aScreenPosition
,
659 LayoutDeviceIntRect
* aScreenDragRect
,
660 RefPtr
<SourceSurface
>* aSurface
,
661 nsPresContext
** aPresContext
) {
663 *aPresContext
= nullptr;
665 // use a default size, in case of an error.
666 aScreenDragRect
->SetRect(aScreenPosition
.x
- mImageOffset
.x
,
667 aScreenPosition
.y
- mImageOffset
.y
, 1, 1);
669 // if a drag image was specified, use that, otherwise, use the source node
670 nsCOMPtr
<nsINode
> dragNode
= mImage
? mImage
.get() : aDOMNode
;
672 // get the presshell for the node being dragged. If the drag image is not in
673 // a document or has no frame, get the presshell from the source drag node
674 PresShell
* presShell
= GetPresShellForContent(dragNode
);
675 if (!presShell
&& mImage
) {
676 presShell
= GetPresShellForContent(aDOMNode
);
679 return NS_ERROR_FAILURE
;
682 *aPresContext
= presShell
->GetPresContext();
684 if (mDragStartData
) {
686 // Just clear the surface if chrome has overridden it with an image.
689 *aSurface
= mDragStartData
->TakeVisualization(aScreenDragRect
);
692 mDragStartData
= nullptr;
696 // convert mouse position to dev pixels of the prescontext
697 CSSIntPoint
screenPosition(aScreenPosition
);
698 screenPosition
.x
-= mImageOffset
.x
;
699 screenPosition
.y
-= mImageOffset
.y
;
700 LayoutDeviceIntPoint screenPoint
=
701 ConvertToUnscaledDevPixels(*aPresContext
, screenPosition
);
702 aScreenDragRect
->MoveTo(screenPoint
.x
, screenPoint
.y
);
704 // check if drag images are disabled
705 bool enableDragImages
= Preferences::GetBool(DRAGIMAGES_PREF
, true);
707 // didn't want an image, so just set the screen rectangle to the frame size
708 if (!enableDragImages
|| !mHasImage
) {
709 // if a region was specified, set the screen rectangle to the area that
710 // the region occupies
713 // the region's coordinates are relative to the root frame
714 dragRect
= aRegion
->GetBounds();
716 nsIFrame
* rootFrame
= presShell
->GetRootFrame();
717 CSSIntRect screenRect
= rootFrame
->GetScreenRect();
718 dragRect
.MoveBy(screenRect
.TopLeft());
720 // otherwise, there was no region so just set the rectangle to
721 // the size of the primary frame of the content.
722 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(dragNode
);
723 nsIFrame
* frame
= content
->GetPrimaryFrame();
725 dragRect
= frame
->GetScreenRect();
729 nsIntRect dragRectDev
=
730 ToAppUnits(dragRect
, AppUnitsPerCSSPixel())
731 .ToOutsidePixels((*aPresContext
)->AppUnitsPerDevPixel());
732 aScreenDragRect
->SizeTo(dragRectDev
.Width(), dragRectDev
.Height());
736 // draw the image for selections
738 LayoutDeviceIntPoint
pnt(aScreenDragRect
->TopLeft());
739 *aSurface
= presShell
->RenderSelection(
740 mSelection
, pnt
, aScreenDragRect
,
741 mImage
? RenderImageFlags::None
: RenderImageFlags::AutoScale
);
745 // if a custom image was specified, check if it is an image node and draw
746 // using the source rather than the displayed image. But if mImage isn't
747 // an image or canvas, fall through to RenderNode below.
749 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(dragNode
);
750 HTMLCanvasElement
* canvas
= HTMLCanvasElement::FromNodeOrNull(content
);
752 return DrawDragForImage(*aPresContext
, nullptr, canvas
, aScreenDragRect
,
756 nsCOMPtr
<nsIImageLoadingContent
> imageLoader
= do_QueryInterface(dragNode
);
757 // for image nodes, create the drag image from the actual image data
759 return DrawDragForImage(*aPresContext
, imageLoader
, nullptr,
760 aScreenDragRect
, aSurface
);
763 // If the image is a popup, use that as the image. This allows custom drag
764 // images that can change during the drag, but means that any platform
765 // default image handling won't occur.
766 // XXXndeakin this should be chrome-only
768 nsIFrame
* frame
= content
->GetPrimaryFrame();
769 if (frame
&& frame
->IsMenuPopupFrame()) {
770 mDragPopup
= content
;
775 // otherwise, just draw the node
776 RenderImageFlags renderFlags
=
777 mImage
? RenderImageFlags::None
: RenderImageFlags::AutoScale
;
778 if (renderFlags
!= RenderImageFlags::None
) {
779 // check if the dragged node itself is an img element
780 if (dragNode
->NodeName().LowerCaseEqualsLiteral("img")) {
781 renderFlags
= renderFlags
| RenderImageFlags::IsImage
;
783 nsINodeList
* childList
= dragNode
->ChildNodes();
784 uint32_t length
= childList
->Length();
785 // check every childnode for being an img element
786 // XXXbz why don't we need to check descendants recursively?
787 for (uint32_t count
= 0; count
< length
; ++count
) {
788 if (childList
->Item(count
)->NodeName().LowerCaseEqualsLiteral(
790 // if the dragnode contains an image, set RenderImageFlags::IsImage
792 renderFlags
= renderFlags
| RenderImageFlags::IsImage
;
798 LayoutDeviceIntPoint
pnt(aScreenDragRect
->TopLeft());
799 *aSurface
= presShell
->RenderNode(dragNode
, aRegion
, pnt
, aScreenDragRect
,
803 // If an image was specified, reset the position from the offset that was
806 aScreenDragRect
->MoveTo(screenPoint
.x
, screenPoint
.y
);
812 nsresult
nsBaseDragService::DrawDragForImage(
813 nsPresContext
* aPresContext
, nsIImageLoadingContent
* aImageLoader
,
814 HTMLCanvasElement
* aCanvas
, LayoutDeviceIntRect
* aScreenDragRect
,
815 RefPtr
<SourceSurface
>* aSurface
) {
816 nsCOMPtr
<imgIContainer
> imgContainer
;
818 nsCOMPtr
<imgIRequest
> imgRequest
;
819 nsresult rv
= aImageLoader
->GetRequest(
820 nsIImageLoadingContent::CURRENT_REQUEST
, getter_AddRefs(imgRequest
));
821 NS_ENSURE_SUCCESS(rv
, rv
);
822 if (!imgRequest
) return NS_ERROR_NOT_AVAILABLE
;
824 rv
= imgRequest
->GetImage(getter_AddRefs(imgContainer
));
825 NS_ENSURE_SUCCESS(rv
, rv
);
826 if (!imgContainer
) return NS_ERROR_NOT_AVAILABLE
;
828 // use the size of the image as the size of the drag image
829 int32_t imageWidth
, imageHeight
;
830 rv
= imgContainer
->GetWidth(&imageWidth
);
831 NS_ENSURE_SUCCESS(rv
, rv
);
833 rv
= imgContainer
->GetHeight(&imageHeight
);
834 NS_ENSURE_SUCCESS(rv
, rv
);
836 aScreenDragRect
->SizeTo(aPresContext
->CSSPixelsToDevPixels(imageWidth
),
837 aPresContext
->CSSPixelsToDevPixels(imageHeight
));
839 // XXX The canvas size should be converted to dev pixels.
840 NS_ASSERTION(aCanvas
, "both image and canvas are null");
841 nsIntSize sz
= aCanvas
->GetSize();
842 aScreenDragRect
->SizeTo(sz
.width
, sz
.height
);
846 destSize
.width
= aScreenDragRect
->Width();
847 destSize
.height
= aScreenDragRect
->Height();
848 if (destSize
.width
== 0 || destSize
.height
== 0) return NS_ERROR_FAILURE
;
850 nsresult result
= NS_OK
;
852 RefPtr
<DrawTarget
> dt
=
853 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
854 destSize
, SurfaceFormat::B8G8R8A8
);
855 if (!dt
|| !dt
->IsValid()) return NS_ERROR_FAILURE
;
857 RefPtr
<gfxContext
> ctx
= gfxContext::CreateOrNull(dt
);
858 if (!ctx
) return NS_ERROR_FAILURE
;
861 imgContainer
->Draw(ctx
, destSize
, ImageRegion::Create(destSize
),
862 imgIContainer::FRAME_CURRENT
, SamplingFilter::GOOD
,
863 /* no SVGImageContext */ Nothing(),
864 imgIContainer::FLAG_SYNC_DECODE
, 1.0);
865 if (res
== ImgDrawResult::BAD_IMAGE
|| res
== ImgDrawResult::BAD_ARGS
||
866 res
== ImgDrawResult::NOT_SUPPORTED
) {
867 return NS_ERROR_FAILURE
;
869 *aSurface
= dt
->Snapshot();
871 *aSurface
= aCanvas
->GetSurfaceSnapshot();
877 LayoutDeviceIntPoint
nsBaseDragService::ConvertToUnscaledDevPixels(
878 nsPresContext
* aPresContext
, CSSIntPoint aScreenPosition
) {
880 aPresContext
->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom();
881 return LayoutDeviceIntPoint(
882 nsPresContext::CSSPixelsToAppUnits(aScreenPosition
.x
) / adj
,
883 nsPresContext::CSSPixelsToAppUnits(aScreenPosition
.y
) / adj
);
887 nsBaseDragService::Suppress() {
888 EndDragSession(false, 0);
894 nsBaseDragService::Unsuppress() {
900 nsBaseDragService::UserCancelled() {
901 mUserCancelled
= true;
906 nsBaseDragService::UpdateDragEffect() {
907 mDragActionFromChildProcess
= mDragAction
;
912 nsBaseDragService::UpdateDragImage(nsINode
* aImage
, int32_t aImageX
,
914 // Don't change the image if this is a drag from another source or if there
916 if (!mSourceNode
|| mDragPopup
) return NS_OK
;
919 mImageOffset
= CSSIntPoint(aImageX
, aImageY
);
924 nsBaseDragService::DragEventDispatchedToChildProcess() {
925 mDragEventDispatchedToChildProcess
= true;
929 bool nsBaseDragService::MaybeAddChildProcess(
930 mozilla::dom::ContentParent
* aChild
) {
931 if (!mChildProcesses
.Contains(aChild
)) {
932 mChildProcesses
.AppendElement(aChild
);
938 bool nsBaseDragService::RemoveAllChildProcesses() {
939 for (uint32_t c
= 0; c
< mChildProcesses
.Length(); c
++) {
940 mozilla::Unused
<< mChildProcesses
[c
]->SendEndDragSession(
941 true, false, LayoutDeviceIntPoint(), 0);
943 mChildProcesses
.Clear();