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 "nsIContent.h"
18 #include "nsViewManager.h"
20 #include "nsPresContext.h"
21 #include "nsIImageLoadingContent.h"
22 #include "imgIContainer.h"
23 #include "imgIRequest.h"
24 #include "ImageRegion.h"
25 #include "nsQueryObject.h"
27 #include "nsXULPopupManager.h"
28 #include "nsMenuPopupFrame.h"
30 # include "nsTreeBodyFrame.h"
32 #include "mozilla/MouseEvents.h"
33 #include "mozilla/Preferences.h"
34 #include "mozilla/PresShell.h"
35 #include "mozilla/ProfilerLabels.h"
36 #include "mozilla/SVGImageContext.h"
37 #include "mozilla/TextControlElement.h"
38 #include "mozilla/Unused.h"
39 #include "mozilla/ViewportUtils.h"
40 #include "mozilla/dom/BindingDeclarations.h"
41 #include "mozilla/dom/DataTransferItemList.h"
42 #include "mozilla/dom/DataTransfer.h"
43 #include "mozilla/dom/Document.h"
44 #include "mozilla/dom/DocumentInlines.h"
45 #include "mozilla/dom/DragEvent.h"
46 #include "mozilla/dom/MouseEventBinding.h"
47 #include "mozilla/dom/Selection.h"
48 #include "mozilla/gfx/2D.h"
49 #include "nsFrameLoader.h"
50 #include "BrowserParent.h"
51 #include "nsIMutableArray.h"
52 #include "gfxContext.h"
53 #include "gfxPlatform.h"
56 using namespace mozilla
;
57 using namespace mozilla::dom
;
58 using namespace mozilla::gfx
;
59 using namespace mozilla::image
;
61 #define DRAGIMAGES_PREF "nglayout.enable_drag_images"
63 nsBaseDragService::nsBaseDragService()
65 mOnlyChromeDrop(false),
67 mSessionIsSynthesizedForTests(false),
68 mIsDraggingTextInTextControl(false),
69 mEndingSession(false),
71 mUserCancelled(false),
72 mDragEventDispatchedToChildProcess(false),
73 mDragAction(DRAGDROP_ACTION_NONE
),
74 mDragActionFromChildProcess(DRAGDROP_ACTION_UNINITIALIZED
),
75 mEffectAllowedForTests(DRAGDROP_ACTION_UNINITIALIZED
),
76 mContentPolicyType(nsIContentPolicy::TYPE_OTHER
),
78 mInputSource(MouseEvent_Binding::MOZ_SOURCE_MOUSE
) {}
80 nsBaseDragService::~nsBaseDragService() = default;
82 NS_IMPL_ISUPPORTS(nsBaseDragService
, nsIDragService
, nsIDragSession
)
84 //---------------------------------------------------------
86 nsBaseDragService::SetCanDrop(bool aCanDrop
) {
91 //---------------------------------------------------------
93 nsBaseDragService::GetCanDrop(bool* aCanDrop
) {
97 //---------------------------------------------------------
99 nsBaseDragService::SetOnlyChromeDrop(bool aOnlyChrome
) {
100 mOnlyChromeDrop
= aOnlyChrome
;
104 //---------------------------------------------------------
106 nsBaseDragService::GetOnlyChromeDrop(bool* aOnlyChrome
) {
107 *aOnlyChrome
= mOnlyChromeDrop
;
111 //---------------------------------------------------------
113 nsBaseDragService::SetDragAction(uint32_t anAction
) {
114 mDragAction
= anAction
;
118 //---------------------------------------------------------
120 nsBaseDragService::GetDragAction(uint32_t* anAction
) {
121 *anAction
= mDragAction
;
125 //-------------------------------------------------------------------------
128 nsBaseDragService::GetNumDropItems(uint32_t* aNumItems
) {
130 return NS_ERROR_FAILURE
;
134 // GetSourceWindowContext
136 // Returns the window context where the drag was initiated. This will be
137 // nullptr if the drag began outside of our application.
140 nsBaseDragService::GetSourceWindowContext(
141 WindowContext
** aSourceWindowContext
) {
142 *aSourceWindowContext
= mSourceWindowContext
.get();
143 NS_IF_ADDREF(*aSourceWindowContext
);
148 nsBaseDragService::SetSourceWindowContext(WindowContext
* aSourceWindowContext
) {
149 // This should only be called in a child process.
150 MOZ_ASSERT(!XRE_IsParentProcess());
151 mSourceWindowContext
= aSourceWindowContext
;
158 // Returns the DOM node where the drag was initiated. This will be
159 // nullptr if the drag began outside of our application.
162 nsBaseDragService::GetSourceNode(nsINode
** aSourceNode
) {
163 *aSourceNode
= do_AddRef(mSourceNode
).take();
167 void nsBaseDragService::UpdateSource(nsINode
* aNewSourceNode
,
168 Selection
* aNewSelection
) {
169 MOZ_ASSERT(mSourceNode
);
170 MOZ_ASSERT(aNewSourceNode
);
171 MOZ_ASSERT(mSourceNode
->IsInNativeAnonymousSubtree() ||
172 aNewSourceNode
->IsInNativeAnonymousSubtree());
173 MOZ_ASSERT(mSourceDocument
== aNewSourceNode
->OwnerDoc());
174 mSourceNode
= aNewSourceNode
;
175 // Don't set mSelection if the session was invoked without selection or
176 // making it becomes nullptr. The latter occurs when the old frame is
178 if (mSelection
&& aNewSelection
) {
179 // XXX If the dragging image is created once (e.g., at drag start), the
180 // image won't be updated unless we notify `DrawDrag` callers.
181 // However, it must be okay for now to keep using older image of
183 mSelection
= aNewSelection
;
188 nsBaseDragService::GetTriggeringPrincipal(nsIPrincipal
** aPrincipal
) {
189 NS_IF_ADDREF(*aPrincipal
= mTriggeringPrincipal
);
194 nsBaseDragService::SetTriggeringPrincipal(nsIPrincipal
* aPrincipal
) {
195 mTriggeringPrincipal
= aPrincipal
;
200 nsBaseDragService::GetCsp(nsIContentSecurityPolicy
** aCsp
) {
201 NS_IF_ADDREF(*aCsp
= mCsp
);
206 nsBaseDragService::SetCsp(nsIContentSecurityPolicy
* aCsp
) {
211 //-------------------------------------------------------------------------
214 nsBaseDragService::GetData(nsITransferable
* aTransferable
,
215 uint32_t aItemIndex
) {
216 return NS_ERROR_FAILURE
;
219 //-------------------------------------------------------------------------
221 nsBaseDragService::IsDataFlavorSupported(const char* aDataFlavor
,
223 return NS_ERROR_FAILURE
;
227 nsBaseDragService::GetDataTransferXPCOM(DataTransfer
** aDataTransfer
) {
228 *aDataTransfer
= mDataTransfer
;
229 NS_IF_ADDREF(*aDataTransfer
);
234 nsBaseDragService::SetDataTransferXPCOM(DataTransfer
* aDataTransfer
) {
235 NS_ENSURE_STATE(aDataTransfer
);
236 mDataTransfer
= aDataTransfer
;
240 DataTransfer
* nsBaseDragService::GetDataTransfer() { return mDataTransfer
; }
242 void nsBaseDragService::SetDataTransfer(DataTransfer
* aDataTransfer
) {
243 mDataTransfer
= aDataTransfer
;
246 bool nsBaseDragService::IsSynthesizedForTests() {
247 return mSessionIsSynthesizedForTests
;
250 bool nsBaseDragService::IsDraggingTextInTextControl() {
251 return mIsDraggingTextInTextControl
;
254 uint32_t nsBaseDragService::GetEffectAllowedForTests() {
255 MOZ_ASSERT(mSessionIsSynthesizedForTests
);
256 return mEffectAllowedForTests
;
259 NS_IMETHODIMP
nsBaseDragService::SetDragEndPointForTests(int32_t aScreenX
,
261 MOZ_ASSERT(mDoingDrag
);
262 MOZ_ASSERT(mSourceDocument
);
263 MOZ_ASSERT(mSessionIsSynthesizedForTests
);
264 if (!mDoingDrag
|| !mSourceDocument
|| !mSessionIsSynthesizedForTests
) {
265 return NS_ERROR_FAILURE
;
267 nsPresContext
* presContext
= mSourceDocument
->GetPresContext();
268 if (NS_WARN_IF(!presContext
)) {
269 return NS_ERROR_FAILURE
;
272 LayoutDeviceIntPoint(presContext
->CSSPixelsToDevPixels(aScreenX
),
273 presContext
->CSSPixelsToDevPixels(aScreenY
)));
277 //-------------------------------------------------------------------------
279 nsBaseDragService::InvokeDragSession(
280 nsINode
* aDOMNode
, nsIPrincipal
* aPrincipal
, nsIContentSecurityPolicy
* aCsp
,
281 nsICookieJarSettings
* aCookieJarSettings
, nsIArray
* aTransferableArray
,
282 uint32_t aActionType
,
283 nsContentPolicyType aContentPolicyType
= nsIContentPolicy::TYPE_OTHER
) {
284 AUTO_PROFILER_LABEL("nsBaseDragService::InvokeDragSession", OTHER
);
286 NS_ENSURE_TRUE(aDOMNode
, NS_ERROR_INVALID_ARG
);
287 NS_ENSURE_TRUE(mSuppressLevel
== 0, NS_ERROR_FAILURE
);
289 // stash the document of the dom node
290 mSourceDocument
= aDOMNode
->OwnerDoc();
291 mTriggeringPrincipal
= aPrincipal
;
293 mSourceNode
= aDOMNode
;
294 mIsDraggingTextInTextControl
=
295 mSourceNode
->IsInNativeAnonymousSubtree() &&
296 TextControlElement::FromNodeOrNull(
297 mSourceNode
->GetClosestNativeAnonymousSubtreeRootParent());
298 mContentPolicyType
= aContentPolicyType
;
299 mEndDragPoint
= LayoutDeviceIntPoint(0, 0);
301 // When the mouse goes down, the selection code starts a mouse
302 // capture. However, this gets in the way of determining drag
303 // feedback for things like trees because the event coordinates
304 // are in the wrong coord system, so turn off mouse capture.
305 PresShell::ClearMouseCapture();
307 if (mSessionIsSynthesizedForTests
) {
309 mDragAction
= aActionType
;
310 mEffectAllowedForTests
= aActionType
;
314 // If you're hitting this, a test is causing the browser to attempt to enter
315 // the drag-drop native nested event loop, which will put the browser in a
316 // state that won't run tests properly until there's manual intervention
317 // to exit the drag-drop loop (either by moving the mouse or hitting escape),
318 // which can't be done from script since we're in the nested loop.
320 // The best way to avoid this is to catch the dragstart event on the item
321 // being dragged, and then to call preventDefault() and stopPropagating() on
323 if (XRE_IsParentProcess()) {
325 !xpc::IsInAutomation(),
326 "About to start drag-drop native loop on which will prevent later "
327 "tests from running properly.");
331 mozilla::Unused
<< aTransferableArray
->GetLength(&length
);
333 nsCOMPtr
<nsIMutableArray
> mutableArray
=
334 do_QueryInterface(aTransferableArray
);
336 // In order to be able trigger dnd, we need to have some transferable
338 nsCOMPtr
<nsITransferable
> trans
=
339 do_CreateInstance("@mozilla.org/widget/transferable;1");
340 trans
->Init(nullptr);
341 trans
->SetRequestingPrincipal(mSourceNode
->NodePrincipal());
342 trans
->SetContentPolicyType(mContentPolicyType
);
343 trans
->SetCookieJarSettings(aCookieJarSettings
);
344 mutableArray
->AppendElement(trans
);
347 for (uint32_t i
= 0; i
< length
; ++i
) {
348 nsCOMPtr
<nsITransferable
> trans
=
349 do_QueryElementAt(aTransferableArray
, i
);
351 // Set the requestingPrincipal on the transferable.
352 trans
->SetRequestingPrincipal(mSourceNode
->NodePrincipal());
353 trans
->SetContentPolicyType(mContentPolicyType
);
354 trans
->SetCookieJarSettings(aCookieJarSettings
);
359 nsresult rv
= InvokeDragSessionImpl(aTransferableArray
, mRegion
, aActionType
);
362 // Set mDoingDrag so that EndDragSession cleans up and sends the dragend
363 // event after the aborted drag.
365 EndDragSession(true, 0);
372 nsBaseDragService::InvokeDragSessionWithImage(
373 nsINode
* aDOMNode
, nsIPrincipal
* aPrincipal
, nsIContentSecurityPolicy
* aCsp
,
374 nsICookieJarSettings
* aCookieJarSettings
, nsIArray
* aTransferableArray
,
375 uint32_t aActionType
, nsINode
* aImage
, int32_t aImageX
, int32_t aImageY
,
376 DragEvent
* aDragEvent
, DataTransfer
* aDataTransfer
) {
377 NS_ENSURE_TRUE(aDragEvent
, NS_ERROR_NULL_POINTER
);
378 NS_ENSURE_TRUE(aDataTransfer
, NS_ERROR_NULL_POINTER
);
379 NS_ENSURE_TRUE(mSuppressLevel
== 0, NS_ERROR_FAILURE
);
381 mSessionIsSynthesizedForTests
=
382 aDragEvent
->WidgetEventPtr()->mFlags
.mIsSynthesizedForTests
;
383 mDataTransfer
= aDataTransfer
;
384 mSelection
= nullptr;
386 mDragPopup
= nullptr;
388 mImageOffset
= CSSIntPoint(aImageX
, aImageY
);
389 mDragStartData
= nullptr;
390 mSourceWindowContext
=
391 aDOMNode
? aDOMNode
->OwnerDoc()->GetWindowContext() : nullptr;
393 mScreenPosition
.x
= aDragEvent
->ScreenX(CallerType::System
);
394 mScreenPosition
.y
= aDragEvent
->ScreenY(CallerType::System
);
395 mInputSource
= aDragEvent
->MozInputSource();
397 // If dragging within a XUL tree and no custom drag image was
398 // set, the region argument to InvokeDragSessionWithImage needs
399 // to be set to the area encompassing the selected rows of the
400 // tree to ensure that the drag feedback gets clipped to those
401 // rows. For other content, region should be null.
404 if (aDOMNode
&& aDOMNode
->IsContent() && !aImage
) {
405 if (aDOMNode
->NodeInfo()->Equals(nsGkAtoms::treechildren
,
407 nsTreeBodyFrame
* treeBody
=
408 do_QueryFrame(aDOMNode
->AsContent()->GetPrimaryFrame());
410 mRegion
= treeBody
->GetSelectionRegion();
416 nsresult rv
= InvokeDragSession(
417 aDOMNode
, aPrincipal
, aCsp
, aCookieJarSettings
, aTransferableArray
,
418 aActionType
, nsIContentPolicy::TYPE_INTERNAL_IMAGE
);
424 nsBaseDragService::InvokeDragSessionWithRemoteImage(
425 nsINode
* aDOMNode
, nsIPrincipal
* aPrincipal
, nsIContentSecurityPolicy
* aCsp
,
426 nsICookieJarSettings
* aCookieJarSettings
, nsIArray
* aTransferableArray
,
427 uint32_t aActionType
, RemoteDragStartData
* aDragStartData
,
428 DragEvent
* aDragEvent
, DataTransfer
* aDataTransfer
) {
429 NS_ENSURE_TRUE(aDragEvent
, NS_ERROR_NULL_POINTER
);
430 NS_ENSURE_TRUE(aDataTransfer
, NS_ERROR_NULL_POINTER
);
431 NS_ENSURE_TRUE(mSuppressLevel
== 0, NS_ERROR_FAILURE
);
433 mSessionIsSynthesizedForTests
=
434 aDragEvent
->WidgetEventPtr()->mFlags
.mIsSynthesizedForTests
;
435 mDataTransfer
= aDataTransfer
;
436 mSelection
= nullptr;
438 mDragPopup
= nullptr;
440 mDragStartData
= aDragStartData
;
441 mImageOffset
= CSSIntPoint(0, 0);
442 mSourceWindowContext
= mDragStartData
->GetSourceWindowContext();
444 mScreenPosition
.x
= aDragEvent
->ScreenX(CallerType::System
);
445 mScreenPosition
.y
= aDragEvent
->ScreenY(CallerType::System
);
446 mInputSource
= aDragEvent
->MozInputSource();
448 nsresult rv
= InvokeDragSession(
449 aDOMNode
, aPrincipal
, aCsp
, aCookieJarSettings
, aTransferableArray
,
450 aActionType
, nsIContentPolicy::TYPE_INTERNAL_IMAGE
);
456 nsBaseDragService::InvokeDragSessionWithSelection(
457 Selection
* aSelection
, nsIPrincipal
* aPrincipal
,
458 nsIContentSecurityPolicy
* aCsp
, nsICookieJarSettings
* aCookieJarSettings
,
459 nsIArray
* aTransferableArray
, uint32_t aActionType
, DragEvent
* aDragEvent
,
460 DataTransfer
* aDataTransfer
) {
461 NS_ENSURE_TRUE(aSelection
, NS_ERROR_NULL_POINTER
);
462 NS_ENSURE_TRUE(aDragEvent
, NS_ERROR_NULL_POINTER
);
463 NS_ENSURE_TRUE(mSuppressLevel
== 0, NS_ERROR_FAILURE
);
465 mSessionIsSynthesizedForTests
=
466 aDragEvent
->WidgetEventPtr()->mFlags
.mIsSynthesizedForTests
;
467 mDataTransfer
= aDataTransfer
;
468 mSelection
= aSelection
;
470 mDragPopup
= nullptr;
472 mImageOffset
= CSSIntPoint();
473 mDragStartData
= nullptr;
476 mScreenPosition
.x
= aDragEvent
->ScreenX(CallerType::System
);
477 mScreenPosition
.y
= aDragEvent
->ScreenY(CallerType::System
);
478 mInputSource
= aDragEvent
->MozInputSource();
480 // just get the focused node from the selection
481 // XXXndeakin this should actually be the deepest node that contains both
482 // endpoints of the selection
483 nsCOMPtr
<nsINode
> node
= aSelection
->GetFocusNode();
484 mSourceWindowContext
= node
? node
->OwnerDoc()->GetWindowContext() : nullptr;
486 return InvokeDragSession(node
, aPrincipal
, aCsp
, aCookieJarSettings
,
487 aTransferableArray
, aActionType
,
488 nsIContentPolicy::TYPE_OTHER
);
491 //-------------------------------------------------------------------------
493 nsBaseDragService::GetCurrentSession(nsIDragSession
** aSession
) {
494 if (!aSession
) return NS_ERROR_INVALID_ARG
;
496 // "this" also implements a drag session, so say we are one but only
497 // if there is currently a drag going on.
498 if (!mSuppressLevel
&& mDoingDrag
) {
500 NS_ADDREF(*aSession
); // addRef because we're a "getter"
507 //-------------------------------------------------------------------------
509 nsBaseDragService::StartDragSession() {
511 return NS_ERROR_FAILURE
;
514 // By default dispatch drop also to content.
515 mOnlyChromeDrop
= false;
520 NS_IMETHODIMP
nsBaseDragService::StartDragSessionForTests(
521 uint32_t aAllowedEffect
) {
522 if (NS_WARN_IF(NS_FAILED(StartDragSession()))) {
523 return NS_ERROR_FAILURE
;
525 mDragAction
= aAllowedEffect
;
526 mEffectAllowedForTests
= aAllowedEffect
;
527 mSessionIsSynthesizedForTests
= true;
531 void nsBaseDragService::OpenDragPopup() {
533 nsXULPopupManager
* pm
= nsXULPopupManager::GetInstance();
535 pm
->ShowPopupAtScreen(mDragPopup
, mScreenPosition
.x
- mImageOffset
.x
,
536 mScreenPosition
.y
- mImageOffset
.y
, false, nullptr);
541 int32_t nsBaseDragService::TakeChildProcessDragAction() {
542 // If the last event was dispatched to the child process, use the drag action
543 // assigned from it instead and return it. DRAGDROP_ACTION_UNINITIALIZED is
544 // returned otherwise.
545 int32_t retval
= DRAGDROP_ACTION_UNINITIALIZED
;
546 if (TakeDragEventDispatchedToChildProcess() &&
547 mDragActionFromChildProcess
!= DRAGDROP_ACTION_UNINITIALIZED
) {
548 retval
= mDragActionFromChildProcess
;
554 //-------------------------------------------------------------------------
556 nsBaseDragService::EndDragSession(bool aDoneDrag
, uint32_t aKeyModifiers
) {
557 if (!mDoingDrag
|| mEndingSession
) {
558 return NS_ERROR_FAILURE
;
561 mEndingSession
= true;
563 if (aDoneDrag
&& !mSuppressLevel
) {
564 FireDragEventAtSource(eDragEnd
, aKeyModifiers
);
568 nsXULPopupManager
* pm
= nsXULPopupManager::GetInstance();
570 pm
->HidePopup(mDragPopup
, false, true, false, false);
574 for (uint32_t i
= 0; i
< mChildProcesses
.Length(); ++i
) {
575 mozilla::Unused
<< mChildProcesses
[i
]->SendEndDragSession(
576 aDoneDrag
, mUserCancelled
, mEndDragPoint
, aKeyModifiers
);
577 // Continue sending input events with input priority when stopping the dnd
579 mChildProcesses
[i
]->SetInputPriorityEventEnabled(true);
581 mChildProcesses
.Clear();
583 // mDataTransfer and the items it owns are going to die anyway, but we
584 // explicitly deref the contained data here so that we don't have to wait for
585 // CC to reclaim the memory.
586 if (XRE_IsParentProcess()) {
587 DiscardInternalTransferData();
591 mSessionIsSynthesizedForTests
= false;
592 mIsDraggingTextInTextControl
= false;
593 mEffectAllowedForTests
= nsIDragService::DRAGDROP_ACTION_UNINITIALIZED
;
594 mEndingSession
= false;
597 // release the source we've been holding on to.
598 mSourceDocument
= nullptr;
599 mSourceNode
= nullptr;
600 mSourceWindowContext
= nullptr;
601 mTriggeringPrincipal
= nullptr;
603 mSelection
= nullptr;
604 mDataTransfer
= nullptr;
606 mUserCancelled
= false;
607 mDragPopup
= nullptr;
608 mDragStartData
= nullptr;
610 mImageOffset
= CSSIntPoint();
611 mScreenPosition
= CSSIntPoint();
612 mEndDragPoint
= LayoutDeviceIntPoint(0, 0);
613 mInputSource
= MouseEvent_Binding::MOZ_SOURCE_MOUSE
;
619 void nsBaseDragService::DiscardInternalTransferData() {
620 if (mDataTransfer
&& mSourceNode
) {
621 MOZ_ASSERT(mDataTransfer
);
623 DataTransferItemList
* items
= mDataTransfer
->Items();
624 for (size_t i
= 0; i
< items
->Length(); i
++) {
626 DataTransferItem
* item
= items
->IndexedGetter(i
, found
);
628 // Non-OTHER items may still be needed by JS. Skip them.
629 if (!found
|| item
->Kind() != DataTransferItem::KIND_OTHER
) {
633 nsCOMPtr
<nsIVariant
> variant
= item
->DataNoSecurityCheck();
634 nsCOMPtr
<nsIWritableVariant
> writable
= do_QueryInterface(variant
);
637 writable
->SetAsEmpty();
644 nsBaseDragService::FireDragEventAtSource(EventMessage aEventMessage
,
645 uint32_t aKeyModifiers
) {
646 if (mSourceNode
&& mSourceDocument
&& !mSuppressLevel
) {
647 RefPtr
<PresShell
> presShell
= mSourceDocument
->GetPresShell();
649 nsEventStatus status
= nsEventStatus_eIgnore
;
650 WidgetDragEvent
event(true, aEventMessage
, nullptr);
651 event
.mFlags
.mIsSynthesizedForTests
= mSessionIsSynthesizedForTests
;
652 event
.mInputSource
= mInputSource
;
653 if (aEventMessage
== eDragEnd
) {
654 event
.mRefPoint
= mEndDragPoint
;
655 event
.mUserCancelled
= mUserCancelled
;
657 event
.mModifiers
= aKeyModifiers
;
658 // Send the drag event to APZ, which needs to know about them to be
659 // able to accurately detect the end of a drag gesture.
660 if (nsPresContext
* presContext
= presShell
->GetPresContext()) {
661 if (nsCOMPtr
<nsIWidget
> widget
= presContext
->GetRootWidget()) {
662 widget
->DispatchEventToAPZOnly(&event
);
666 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(mSourceNode
);
667 return presShell
->HandleDOMEventWithTarget(content
, &event
, &status
);
674 /* This is used by Windows and Mac to update the position of a popup being
675 * used as a drag image during the drag. This isn't used on GTK as it manages
676 * the drag popup itself.
679 nsBaseDragService::DragMoved(int32_t aX
, int32_t aY
) {
681 nsIFrame
* frame
= mDragPopup
->GetPrimaryFrame();
682 if (frame
&& frame
->IsMenuPopupFrame()) {
684 RoundedToInt(LayoutDeviceIntPoint(aX
, aY
) /
685 frame
->PresContext()->CSSToDevPixelScale()) -
687 (static_cast<nsMenuPopupFrame
*>(frame
))->MoveTo(cssPos
, true);
694 static PresShell
* GetPresShellForContent(nsINode
* aDOMNode
) {
695 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(aDOMNode
);
696 if (!content
) return nullptr;
698 RefPtr
<Document
> document
= content
->GetComposedDoc();
700 document
->FlushPendingNotifications(FlushType::Display
);
701 return document
->GetPresShell();
707 nsresult
nsBaseDragService::DrawDrag(nsINode
* aDOMNode
,
708 const Maybe
<CSSIntRegion
>& aRegion
,
709 CSSIntPoint aScreenPosition
,
710 LayoutDeviceIntRect
* aScreenDragRect
,
711 RefPtr
<SourceSurface
>* aSurface
,
712 nsPresContext
** aPresContext
) {
714 *aPresContext
= nullptr;
716 // use a default size, in case of an error.
717 aScreenDragRect
->SetRect(aScreenPosition
.x
- mImageOffset
.x
,
718 aScreenPosition
.y
- mImageOffset
.y
, 1, 1);
720 // if a drag image was specified, use that, otherwise, use the source node
721 nsCOMPtr
<nsINode
> dragNode
= mImage
? mImage
.get() : aDOMNode
;
723 // get the presshell for the node being dragged. If the drag image is not in
724 // a document or has no frame, get the presshell from the source drag node
725 PresShell
* presShell
= GetPresShellForContent(dragNode
);
726 if (!presShell
&& mImage
) {
727 presShell
= GetPresShellForContent(aDOMNode
);
730 return NS_ERROR_FAILURE
;
733 *aPresContext
= presShell
->GetPresContext();
735 if (mDragStartData
) {
737 // Just clear the surface if chrome has overridden it with an image.
740 *aSurface
= mDragStartData
->TakeVisualization(aScreenDragRect
);
743 mDragStartData
= nullptr;
747 // convert mouse position to dev pixels of the prescontext
748 CSSIntPoint
screenPosition(aScreenPosition
);
749 screenPosition
.x
-= mImageOffset
.x
;
750 screenPosition
.y
-= mImageOffset
.y
;
751 LayoutDeviceIntPoint screenPoint
=
752 ConvertToUnscaledDevPixels(*aPresContext
, screenPosition
);
753 aScreenDragRect
->MoveTo(screenPoint
.x
, screenPoint
.y
);
755 // check if drag images are disabled
756 bool enableDragImages
= Preferences::GetBool(DRAGIMAGES_PREF
, true);
758 // didn't want an image, so just set the screen rectangle to the frame size
759 if (!enableDragImages
|| !mHasImage
) {
760 // This holds a quantity in RelativeTo{presShell->GetRootFrame(),
761 // ViewportType::Layout} space.
762 nsRect presLayoutRect
;
764 // if a region was specified, set the screen rectangle to the area that
765 // the region occupies
766 presLayoutRect
= ToAppUnits(aRegion
->GetBounds(), AppUnitsPerCSSPixel());
768 // otherwise, there was no region so just set the rectangle to
769 // the size of the primary frame of the content.
770 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(dragNode
);
771 if (nsIFrame
* frame
= content
->GetPrimaryFrame()) {
772 presLayoutRect
= frame
->GetBoundingClientRect();
776 LayoutDeviceRect screenVisualRect
= ViewportUtils::ToScreenRelativeVisual(
777 LayoutDeviceRect::FromAppUnits(presLayoutRect
,
778 (*aPresContext
)->AppUnitsPerDevPixel()),
780 aScreenDragRect
->SizeTo(screenVisualRect
.Width(),
781 screenVisualRect
.Height());
785 // draw the image for selections
787 LayoutDeviceIntPoint
pnt(aScreenDragRect
->TopLeft());
788 *aSurface
= presShell
->RenderSelection(
789 mSelection
, pnt
, aScreenDragRect
,
790 mImage
? RenderImageFlags::None
: RenderImageFlags::AutoScale
);
794 // if a custom image was specified, check if it is an image node and draw
795 // using the source rather than the displayed image. But if mImage isn't
796 // an image or canvas, fall through to RenderNode below.
798 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(dragNode
);
799 HTMLCanvasElement
* canvas
= HTMLCanvasElement::FromNodeOrNull(content
);
801 return DrawDragForImage(*aPresContext
, nullptr, canvas
, aScreenDragRect
,
805 nsCOMPtr
<nsIImageLoadingContent
> imageLoader
= do_QueryInterface(dragNode
);
806 // for image nodes, create the drag image from the actual image data
808 return DrawDragForImage(*aPresContext
, imageLoader
, nullptr,
809 aScreenDragRect
, aSurface
);
812 // If the image is a popup, use that as the image. This allows custom drag
813 // images that can change during the drag, but means that any platform
814 // default image handling won't occur.
815 // XXXndeakin this should be chrome-only
817 nsIFrame
* frame
= content
->GetPrimaryFrame();
818 if (frame
&& frame
->IsMenuPopupFrame()) {
819 mDragPopup
= content
;
824 // otherwise, just draw the node
825 RenderImageFlags renderFlags
=
826 mImage
? RenderImageFlags::None
: RenderImageFlags::AutoScale
;
827 if (renderFlags
!= RenderImageFlags::None
) {
828 // check if the dragged node itself is an img element
829 if (dragNode
->NodeName().LowerCaseEqualsLiteral("img")) {
830 renderFlags
= renderFlags
| RenderImageFlags::IsImage
;
832 nsINodeList
* childList
= dragNode
->ChildNodes();
833 uint32_t length
= childList
->Length();
834 // check every childnode for being an img element
835 // XXXbz why don't we need to check descendants recursively?
836 for (uint32_t count
= 0; count
< length
; ++count
) {
837 if (childList
->Item(count
)->NodeName().LowerCaseEqualsLiteral(
839 // if the dragnode contains an image, set RenderImageFlags::IsImage
841 renderFlags
= renderFlags
| RenderImageFlags::IsImage
;
847 LayoutDeviceIntPoint
pnt(aScreenDragRect
->TopLeft());
848 *aSurface
= presShell
->RenderNode(dragNode
, aRegion
, pnt
, aScreenDragRect
,
852 // If an image was specified, reset the position from the offset that was
855 aScreenDragRect
->MoveTo(screenPoint
.x
, screenPoint
.y
);
861 nsresult
nsBaseDragService::DrawDragForImage(
862 nsPresContext
* aPresContext
, nsIImageLoadingContent
* aImageLoader
,
863 HTMLCanvasElement
* aCanvas
, LayoutDeviceIntRect
* aScreenDragRect
,
864 RefPtr
<SourceSurface
>* aSurface
) {
865 nsCOMPtr
<imgIContainer
> imgContainer
;
867 nsCOMPtr
<imgIRequest
> imgRequest
;
868 nsresult rv
= aImageLoader
->GetRequest(
869 nsIImageLoadingContent::CURRENT_REQUEST
, getter_AddRefs(imgRequest
));
870 NS_ENSURE_SUCCESS(rv
, rv
);
871 if (!imgRequest
) return NS_ERROR_NOT_AVAILABLE
;
873 rv
= imgRequest
->GetImage(getter_AddRefs(imgContainer
));
874 NS_ENSURE_SUCCESS(rv
, rv
);
875 if (!imgContainer
) return NS_ERROR_NOT_AVAILABLE
;
877 // use the size of the image as the size of the drag image
878 int32_t imageWidth
, imageHeight
;
879 rv
= imgContainer
->GetWidth(&imageWidth
);
880 NS_ENSURE_SUCCESS(rv
, rv
);
882 rv
= imgContainer
->GetHeight(&imageHeight
);
883 NS_ENSURE_SUCCESS(rv
, rv
);
885 aScreenDragRect
->SizeTo(aPresContext
->CSSPixelsToDevPixels(imageWidth
),
886 aPresContext
->CSSPixelsToDevPixels(imageHeight
));
888 // XXX The canvas size should be converted to dev pixels.
889 NS_ASSERTION(aCanvas
, "both image and canvas are null");
890 nsIntSize sz
= aCanvas
->GetSize();
891 aScreenDragRect
->SizeTo(sz
.width
, sz
.height
);
895 destSize
.width
= aScreenDragRect
->Width();
896 destSize
.height
= aScreenDragRect
->Height();
897 if (destSize
.width
== 0 || destSize
.height
== 0) return NS_ERROR_FAILURE
;
899 nsresult result
= NS_OK
;
901 RefPtr
<DrawTarget
> dt
=
902 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
903 destSize
, SurfaceFormat::B8G8R8A8
);
904 if (!dt
|| !dt
->IsValid()) return NS_ERROR_FAILURE
;
906 RefPtr
<gfxContext
> ctx
= gfxContext::CreateOrNull(dt
);
907 if (!ctx
) return NS_ERROR_FAILURE
;
910 imgContainer
->Draw(ctx
, destSize
, ImageRegion::Create(destSize
),
911 imgIContainer::FRAME_CURRENT
, SamplingFilter::GOOD
,
912 /* no SVGImageContext */ Nothing(),
913 imgIContainer::FLAG_SYNC_DECODE
, 1.0);
914 if (res
== ImgDrawResult::BAD_IMAGE
|| res
== ImgDrawResult::BAD_ARGS
||
915 res
== ImgDrawResult::NOT_SUPPORTED
) {
916 return NS_ERROR_FAILURE
;
918 *aSurface
= dt
->Snapshot();
920 *aSurface
= aCanvas
->GetSurfaceSnapshot();
926 LayoutDeviceIntPoint
nsBaseDragService::ConvertToUnscaledDevPixels(
927 nsPresContext
* aPresContext
, CSSIntPoint aScreenPosition
) {
929 aPresContext
->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom();
930 return LayoutDeviceIntPoint(
931 nsPresContext::CSSPixelsToAppUnits(aScreenPosition
.x
) / adj
,
932 nsPresContext::CSSPixelsToAppUnits(aScreenPosition
.y
) / adj
);
936 nsBaseDragService::Suppress() {
937 EndDragSession(false, 0);
943 nsBaseDragService::Unsuppress() {
949 nsBaseDragService::UserCancelled() {
950 mUserCancelled
= true;
955 nsBaseDragService::UpdateDragEffect() {
956 mDragActionFromChildProcess
= mDragAction
;
961 nsBaseDragService::UpdateDragImage(nsINode
* aImage
, int32_t aImageX
,
963 // Don't change the image if this is a drag from another source or if there
965 if (!mSourceNode
|| mDragPopup
) return NS_OK
;
968 mImageOffset
= CSSIntPoint(aImageX
, aImageY
);
973 nsBaseDragService::DragEventDispatchedToChildProcess() {
974 mDragEventDispatchedToChildProcess
= true;
978 bool nsBaseDragService::MaybeAddChildProcess(
979 mozilla::dom::ContentParent
* aChild
) {
980 if (!mChildProcesses
.Contains(aChild
)) {
981 mChildProcesses
.AppendElement(aChild
);
987 bool nsBaseDragService::RemoveAllChildProcesses() {
988 for (uint32_t c
= 0; c
< mChildProcesses
.Length(); c
++) {
989 mozilla::Unused
<< mChildProcesses
[c
]->SendEndDragSession(
990 true, false, LayoutDeviceIntPoint(), 0);
992 mChildProcesses
.Clear();