Bug 1942006 - Upstream a variety of Servo-specific code from Servo's downstream fork...
[gecko.git] / widget / nsBaseDragService.cpp
blobb9566a94dc2a4bba9481f38f416db8ca2e02d0a8
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"
11 #include "nsSize.h"
12 #include "nsXPCOM.h"
13 #include "nsCOMPtr.h"
14 #include "nsIInterfaceRequestorUtils.h"
15 #include "nsIFrame.h"
16 #include "nsFrameLoaderOwner.h"
17 #include "nsIContent.h"
18 #include "nsViewManager.h"
19 #include "nsINode.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"
26 #include "nsRegion.h"
27 #include "nsXULPopupManager.h"
28 #include "nsMenuPopupFrame.h"
29 #include "nsTreeBodyFrame.h"
30 #include "mozilla/MouseEvents.h"
31 #include "mozilla/Preferences.h"
32 #include "mozilla/PresShell.h"
33 #include "mozilla/ProfilerLabels.h"
34 #include "mozilla/SVGImageContext.h"
35 #include "mozilla/TextControlElement.h"
36 #include "mozilla/Unused.h"
37 #include "mozilla/ViewportUtils.h"
38 #include "mozilla/dom/BindingDeclarations.h"
39 #include "mozilla/dom/BrowserParent.h"
40 #include "mozilla/dom/CanonicalBrowsingContext.h"
41 #include "mozilla/dom/ContentParent.h"
42 #include "mozilla/dom/DataTransferItemList.h"
43 #include "mozilla/dom/DataTransfer.h"
44 #include "mozilla/dom/Document.h"
45 #include "mozilla/dom/DocumentInlines.h"
46 #include "mozilla/dom/DragEvent.h"
47 #include "mozilla/dom/Selection.h"
48 #include "mozilla/gfx/2D.h"
49 #include "nsFrameLoader.h"
50 #include "nsIMutableArray.h"
51 #include "gfxContext.h"
52 #include "gfxPlatform.h"
53 #include "nscore.h"
54 #include "MockDragServiceController.h"
56 #include <algorithm>
58 using namespace mozilla;
59 using namespace mozilla::dom;
60 using namespace mozilla::gfx;
61 using namespace mozilla::image;
63 LazyLogModule sWidgetDragServiceLog("WidgetDragService");
65 #define DRAGIMAGES_PREF "nglayout.enable_drag_images"
67 // TODO: Temporary hack to share drag session suppression level.
68 // Removed later in this patch series.
69 uint32_t GetSuppressLevel() {
70 nsCOMPtr<nsIDragService> svc =
71 do_GetService("@mozilla.org/widget/dragservice;1");
72 NS_ENSURE_TRUE(svc, 0);
73 return static_cast<nsBaseDragService*>(svc.get())->GetSuppressLevel();
76 nsBaseDragService::nsBaseDragService() = default;
77 nsBaseDragService::~nsBaseDragService() = default;
79 nsBaseDragSession::nsBaseDragSession() { TakeSessionBrowserListFromService(); }
80 nsBaseDragSession::~nsBaseDragSession() = default;
82 NS_IMPL_ISUPPORTS(nsBaseDragService, nsIDragService)
83 NS_IMPL_ISUPPORTS(nsBaseDragSession, nsIDragSession)
85 NS_IMETHODIMP nsBaseDragService::GetIsMockService(bool* aRet) {
86 *aRet = false;
87 return NS_OK;
90 //---------------------------------------------------------
91 NS_IMETHODIMP
92 nsBaseDragSession::SetCanDrop(bool aCanDrop) {
93 mCanDrop = aCanDrop;
94 return NS_OK;
97 //---------------------------------------------------------
98 NS_IMETHODIMP
99 nsBaseDragSession::GetCanDrop(bool* aCanDrop) {
100 *aCanDrop = mCanDrop;
101 return NS_OK;
103 //---------------------------------------------------------
104 NS_IMETHODIMP
105 nsBaseDragSession::SetOnlyChromeDrop(bool aOnlyChrome) {
106 mOnlyChromeDrop = aOnlyChrome;
107 return NS_OK;
110 //---------------------------------------------------------
111 NS_IMETHODIMP
112 nsBaseDragSession::GetOnlyChromeDrop(bool* aOnlyChrome) {
113 *aOnlyChrome = mOnlyChromeDrop;
114 return NS_OK;
117 //---------------------------------------------------------
118 NS_IMETHODIMP
119 nsBaseDragSession::SetDragAction(uint32_t anAction) {
120 mDragAction = anAction;
121 return NS_OK;
124 //---------------------------------------------------------
125 NS_IMETHODIMP
126 nsBaseDragSession::GetDragAction(uint32_t* anAction) {
127 *anAction = mDragAction;
128 return NS_OK;
131 //-------------------------------------------------------------------------
133 NS_IMETHODIMP
134 nsBaseDragSession::GetNumDropItems(uint32_t* aNumItems) {
135 *aNumItems = 0;
136 return NS_ERROR_FAILURE;
140 // GetSourceWindowContext
142 // Returns the window context where the drag was initiated. This will be
143 // nullptr if the drag began outside of our application.
145 NS_IMETHODIMP
146 nsBaseDragSession::GetSourceWindowContext(
147 WindowContext** aSourceWindowContext) {
148 *aSourceWindowContext = mSourceWindowContext.get();
149 NS_IF_ADDREF(*aSourceWindowContext);
150 return NS_OK;
153 NS_IMETHODIMP
154 nsBaseDragSession::SetSourceWindowContext(WindowContext* aSourceWindowContext) {
155 // This should only be called in a child process.
156 MOZ_ASSERT(!XRE_IsParentProcess());
157 mSourceWindowContext = aSourceWindowContext;
158 return NS_OK;
162 // GetSourceTopWindowContext
164 // Returns the top-level window context where the drag was initiated. This will
165 // be nullptr if the drag began outside of our application.
167 NS_IMETHODIMP
168 nsBaseDragSession::GetSourceTopWindowContext(
169 WindowContext** aSourceTopWindowContext) {
170 *aSourceTopWindowContext = mSourceTopWindowContext.get();
171 NS_IF_ADDREF(*aSourceTopWindowContext);
172 return NS_OK;
175 NS_IMETHODIMP
176 nsBaseDragSession::SetSourceTopWindowContext(
177 WindowContext* aSourceTopWindowContext) {
178 // This should only be called in a child process.
179 MOZ_ASSERT(!XRE_IsParentProcess());
180 mSourceTopWindowContext = aSourceTopWindowContext;
181 return NS_OK;
185 // GetSourceNode
187 // Returns the DOM node where the drag was initiated. This will be
188 // nullptr if the drag began outside of our application.
190 NS_IMETHODIMP
191 nsBaseDragSession::GetSourceNode(nsINode** aSourceNode) {
192 *aSourceNode = do_AddRef(mSourceNode).take();
193 return NS_OK;
196 void nsBaseDragSession::UpdateSource(nsINode* aNewSourceNode,
197 Selection* aNewSelection) {
198 MOZ_ASSERT(mSourceNode);
199 MOZ_ASSERT(aNewSourceNode);
200 MOZ_ASSERT(mSourceNode->IsInNativeAnonymousSubtree() ||
201 aNewSourceNode->IsInNativeAnonymousSubtree());
202 MOZ_ASSERT(mSourceDocument == aNewSourceNode->OwnerDoc());
203 mSourceNode = aNewSourceNode;
204 // Don't set mSelection if the session was invoked without selection or
205 // making it becomes nullptr. The latter occurs when the old frame is
206 // being destroyed.
207 if (mSelection && aNewSelection) {
208 // XXX If the dragging image is created once (e.g., at drag start), the
209 // image won't be updated unless we notify `DrawDrag` callers.
210 // However, it must be okay for now to keep using older image of
211 // Selection.
212 mSelection = aNewSelection;
216 NS_IMETHODIMP
217 nsBaseDragSession::GetTriggeringPrincipal(nsIPrincipal** aPrincipal) {
218 NS_IF_ADDREF(*aPrincipal = mTriggeringPrincipal);
219 return NS_OK;
222 NS_IMETHODIMP
223 nsBaseDragSession::SetTriggeringPrincipal(nsIPrincipal* aPrincipal) {
224 mTriggeringPrincipal = aPrincipal;
225 return NS_OK;
228 NS_IMETHODIMP
229 nsBaseDragSession::GetCsp(nsIContentSecurityPolicy** aCsp) {
230 NS_IF_ADDREF(*aCsp = mCsp);
231 return NS_OK;
234 NS_IMETHODIMP
235 nsBaseDragSession::SetCsp(nsIContentSecurityPolicy* aCsp) {
236 mCsp = aCsp;
237 return NS_OK;
240 //-------------------------------------------------------------------------
242 NS_IMETHODIMP
243 nsBaseDragSession::GetData(nsITransferable* aTransferable,
244 uint32_t aItemIndex) {
245 return NS_ERROR_FAILURE;
248 //-------------------------------------------------------------------------
249 NS_IMETHODIMP
250 nsBaseDragSession::IsDataFlavorSupported(const char* aDataFlavor,
251 bool* _retval) {
252 return NS_ERROR_FAILURE;
255 NS_IMETHODIMP
256 nsBaseDragSession::GetDataTransferXPCOM(DataTransfer** aDataTransfer) {
257 *aDataTransfer = mDataTransfer;
258 NS_IF_ADDREF(*aDataTransfer);
259 return NS_OK;
262 NS_IMETHODIMP
263 nsBaseDragSession::SetDataTransferXPCOM(DataTransfer* aDataTransfer) {
264 NS_ENSURE_STATE(aDataTransfer);
265 mDataTransfer = aDataTransfer;
266 return NS_OK;
269 DataTransfer* nsBaseDragSession::GetDataTransfer() { return mDataTransfer; }
271 void nsBaseDragSession::SetDataTransfer(DataTransfer* aDataTransfer) {
272 mDataTransfer = aDataTransfer;
275 bool nsBaseDragSession::IsSynthesizedForTests() {
276 return mSessionIsSynthesizedForTests;
279 bool nsBaseDragSession::IsDraggingTextInTextControl() {
280 return mIsDraggingTextInTextControl;
283 uint32_t nsBaseDragSession::GetEffectAllowedForTests() {
284 MOZ_ASSERT(mSessionIsSynthesizedForTests);
285 return mEffectAllowedForTests;
288 NS_IMETHODIMP nsBaseDragSession::SetDragEndPointForTests(int32_t aScreenX,
289 int32_t aScreenY) {
290 MOZ_ASSERT(mDoingDrag);
291 MOZ_ASSERT(mSourceDocument);
292 MOZ_ASSERT(mSessionIsSynthesizedForTests);
294 if (!mDoingDrag || !mSourceDocument || !mSessionIsSynthesizedForTests) {
295 return NS_ERROR_FAILURE;
297 nsPresContext* pc = mSourceDocument->GetPresContext();
298 if (NS_WARN_IF(!pc)) {
299 return NS_ERROR_FAILURE;
301 auto p = LayoutDeviceIntPoint::Round(CSSIntPoint(aScreenX, aScreenY) *
302 pc->CSSToDevPixelScale());
303 // p is screen-relative, and we want them to be top-level-widget-relative.
304 if (nsCOMPtr<nsIWidget> widget = pc->GetRootWidget()) {
305 p -= widget->WidgetToScreenOffset();
306 p += widget->WidgetToTopLevelWidgetOffset();
308 SetDragEndPoint(p);
309 return NS_OK;
312 //-------------------------------------------------------------------------
313 nsresult nsBaseDragSession::InvokeDragSession(
314 nsIWidget* aWidget, nsINode* aDOMNode, nsIPrincipal* aPrincipal,
315 nsIContentSecurityPolicy* aCsp, nsICookieJarSettings* aCookieJarSettings,
316 nsIArray* aTransferableArray, uint32_t aActionType,
317 nsContentPolicyType aContentPolicyType) {
318 AUTO_PROFILER_LABEL("nsBaseDragService::InvokeDragSession", OTHER);
320 NS_ENSURE_TRUE(aDOMNode, NS_ERROR_INVALID_ARG);
322 // stash the document of the dom node
323 mSourceDocument = aDOMNode->OwnerDoc();
324 mTriggeringPrincipal = aPrincipal;
325 mCsp = aCsp;
326 mSourceNode = aDOMNode;
327 mIsDraggingTextInTextControl =
328 mSourceNode->IsInNativeAnonymousSubtree() &&
329 TextControlElement::FromNodeOrNull(
330 mSourceNode->GetClosestNativeAnonymousSubtreeRootParentOrHost());
331 mContentPolicyType = aContentPolicyType;
332 mEndDragPoint = LayoutDeviceIntPoint(0, 0);
334 // When the mouse goes down, the selection code starts a mouse
335 // capture. However, this gets in the way of determining drag
336 // feedback for things like trees because the event coordinates
337 // are in the wrong coord system, so turn off mouse capture.
338 PresShell::ClearMouseCapture();
340 if (mSessionIsSynthesizedForTests) {
341 mDoingDrag = true;
342 mDragAction = aActionType;
343 mEffectAllowedForTests = aActionType;
344 return NS_OK;
347 if (XRE_IsParentProcess()) {
348 // If you're hitting this, a test is causing the browser to attempt to enter
349 // the drag-drop native nested event loop, which will put the browser in a
350 // state that won't run tests properly until there's manual intervention
351 // to exit the drag-drop loop (either by moving the mouse or hitting
352 // escape), which can't be done from script since we're in the nested loop.
354 // The best way to avoid this is to use the mock service in tests. See
355 // synthesizeMockDragAndDrop.
356 nsCOMPtr<nsIDragService> dragService =
357 do_GetService("@mozilla.org/widget/dragservice;1");
358 MOZ_ASSERT(dragService);
359 MOZ_ASSERT(
360 !xpc::IsInAutomation() || dragService->IsMockService(),
361 "About to start drag-drop native loop on which will prevent later "
362 "tests from running properly.");
365 uint32_t length = 0;
366 mozilla::Unused << aTransferableArray->GetLength(&length);
367 if (!length) {
368 nsCOMPtr<nsIMutableArray> mutableArray =
369 do_QueryInterface(aTransferableArray);
370 if (mutableArray) {
371 // In order to be able trigger dnd, we need to have some transferable
372 // object.
373 nsCOMPtr<nsITransferable> trans =
374 do_CreateInstance("@mozilla.org/widget/transferable;1");
375 trans->Init(nullptr);
376 trans->SetDataPrincipal(mSourceNode->NodePrincipal());
377 trans->SetContentPolicyType(mContentPolicyType);
378 trans->SetCookieJarSettings(aCookieJarSettings);
379 mutableArray->AppendElement(trans);
381 } else {
382 for (uint32_t i = 0; i < length; ++i) {
383 nsCOMPtr<nsITransferable> trans =
384 do_QueryElementAt(aTransferableArray, i);
385 if (trans) {
386 // Set the dataPrincipal on the transferable.
387 trans->SetDataPrincipal(mSourceNode->NodePrincipal());
388 trans->SetContentPolicyType(mContentPolicyType);
389 trans->SetCookieJarSettings(aCookieJarSettings);
394 nsresult rv =
395 InvokeDragSessionImpl(aWidget, aTransferableArray, mRegion, aActionType);
397 if (NS_FAILED(rv)) {
398 // Set mDoingDrag so that EndDragSession cleans up and sends the dragend
399 // event after the aborted drag.
400 mDoingDrag = true;
401 EndDragSession(true, 0);
404 return rv;
407 NS_IMETHODIMP
408 nsBaseDragService::InvokeDragSessionWithImage(
409 nsINode* aDOMNode, nsIPrincipal* aPrincipal, nsIContentSecurityPolicy* aCsp,
410 nsICookieJarSettings* aCookieJarSettings, nsIArray* aTransferableArray,
411 uint32_t aActionType, nsINode* aImage, int32_t aImageX, int32_t aImageY,
412 DragEvent* aDragEvent, DataTransfer* aDataTransfer) {
413 nsCOMPtr<nsIWidget> widget =
414 aDragEvent->WidgetEventPtr()->AsDragEvent()->mWidget;
415 MOZ_ASSERT(widget);
417 NS_ENSURE_TRUE(aDragEvent, NS_ERROR_NULL_POINTER);
418 NS_ENSURE_TRUE(aDataTransfer, NS_ERROR_NULL_POINTER);
419 NS_ENSURE_TRUE(mSuppressLevel == 0, NS_ERROR_FAILURE);
421 RefPtr<nsBaseDragSession> session =
422 CreateDragSession().downcast<nsBaseDragSession>();
423 if (XRE_IsParentProcess()) {
424 mCurrentParentDragSession = session;
426 bool isSynthesized =
427 aDragEvent->WidgetEventPtr()->mFlags.mIsSynthesizedForTests &&
428 !GetNeverAllowSessionIsSynthesizedForTests();
429 return session->InitWithImage(widget, aDOMNode, aPrincipal, aCsp,
430 aCookieJarSettings, aTransferableArray,
431 aActionType, aImage, aImageX, aImageY,
432 aDragEvent, aDataTransfer, isSynthesized);
435 nsresult nsBaseDragSession::InitWithImage(
436 nsIWidget* aWidget, nsINode* aDOMNode, nsIPrincipal* aPrincipal,
437 nsIContentSecurityPolicy* aCsp, nsICookieJarSettings* aCookieJarSettings,
438 nsIArray* aTransferableArray, uint32_t aActionType, nsINode* aImage,
439 int32_t aImageX, int32_t aImageY, DragEvent* aDragEvent,
440 DataTransfer* aDataTransfer, bool aIsSynthesizedForTests) {
441 mSessionIsSynthesizedForTests = aIsSynthesizedForTests;
442 mDataTransfer = aDataTransfer;
443 mSelection = nullptr;
444 mHasImage = true;
445 mDragPopup = nullptr;
446 mImage = aImage;
447 mImageOffset = CSSIntPoint(aImageX, aImageY);
448 mDragStartData = nullptr;
449 mSourceWindowContext =
450 aDOMNode ? aDOMNode->OwnerDoc()->GetWindowContext() : nullptr;
451 mSourceTopWindowContext =
452 mSourceWindowContext ? mSourceWindowContext->TopWindowContext() : nullptr;
454 mScreenPosition = RoundedToInt(aDragEvent->ScreenPoint(CallerType::System));
455 mInputSource = aDragEvent->InputSource(CallerType::System);
457 // If dragging within a XUL tree and no custom drag image was
458 // set, the region argument to InvokeDragSessionWithImage needs
459 // to be set to the area encompassing the selected rows of the
460 // tree to ensure that the drag feedback gets clipped to those
461 // rows. For other content, region should be null.
462 mRegion = Nothing();
463 if (aDOMNode && aDOMNode->IsContent() && !aImage) {
464 if (aDOMNode->NodeInfo()->Equals(nsGkAtoms::treechildren,
465 kNameSpaceID_XUL)) {
466 nsTreeBodyFrame* treeBody =
467 do_QueryFrame(aDOMNode->AsContent()->GetPrimaryFrame());
468 if (treeBody) {
469 mRegion = treeBody->GetSelectionRegion();
474 nsresult rv = InvokeDragSession(
475 aWidget, aDOMNode, aPrincipal, aCsp, aCookieJarSettings,
476 aTransferableArray, aActionType, nsIContentPolicy::TYPE_INTERNAL_IMAGE);
477 mRegion = Nothing();
478 return rv;
481 NS_IMETHODIMP
482 nsBaseDragService::InvokeDragSessionWithRemoteImage(
483 nsINode* aDOMNode, nsIPrincipal* aPrincipal, nsIContentSecurityPolicy* aCsp,
484 nsICookieJarSettings* aCookieJarSettings, nsIArray* aTransferableArray,
485 uint32_t aActionType, RemoteDragStartData* aDragStartData,
486 DragEvent* aDragEvent, DataTransfer* aDataTransfer) {
487 nsCOMPtr<nsIWidget> widget =
488 aDragEvent->WidgetEventPtr()->AsDragEvent()->mWidget;
489 MOZ_ASSERT(widget);
491 NS_ENSURE_TRUE(aDragEvent, NS_ERROR_NULL_POINTER);
492 NS_ENSURE_TRUE(aDataTransfer, NS_ERROR_NULL_POINTER);
493 NS_ENSURE_TRUE(mSuppressLevel == 0, NS_ERROR_FAILURE);
495 RefPtr<nsBaseDragSession> session =
496 CreateDragSession().downcast<nsBaseDragSession>();
497 if (XRE_IsParentProcess()) {
498 mCurrentParentDragSession = session;
500 bool isSynthesized =
501 aDragEvent->WidgetEventPtr()->mFlags.mIsSynthesizedForTests &&
502 !GetNeverAllowSessionIsSynthesizedForTests();
503 return session->InitWithRemoteImage(widget, aDOMNode, aPrincipal, aCsp,
504 aCookieJarSettings, aTransferableArray,
505 aActionType, aDragStartData, aDragEvent,
506 aDataTransfer, isSynthesized);
509 nsresult nsBaseDragSession::InitWithRemoteImage(
510 nsIWidget* aWidget, nsINode* aDOMNode, nsIPrincipal* aPrincipal,
511 nsIContentSecurityPolicy* aCsp, nsICookieJarSettings* aCookieJarSettings,
512 nsIArray* aTransferableArray, uint32_t aActionType,
513 RemoteDragStartData* aDragStartData, DragEvent* aDragEvent,
514 DataTransfer* aDataTransfer, bool aIsSynthesizedForTests) {
515 mSessionIsSynthesizedForTests = aIsSynthesizedForTests;
516 mDataTransfer = aDataTransfer;
517 mSelection = nullptr;
518 mHasImage = true;
519 mDragPopup = nullptr;
520 mImage = nullptr;
521 mDragStartData = aDragStartData;
522 mImageOffset = CSSIntPoint(0, 0);
523 mSourceWindowContext = mDragStartData->GetSourceWindowContext();
524 mSourceTopWindowContext = mDragStartData->GetSourceTopWindowContext();
526 mScreenPosition = RoundedToInt(aDragEvent->ScreenPoint(CallerType::System));
527 mInputSource = aDragEvent->InputSource(CallerType::System);
529 nsresult rv = InvokeDragSession(
530 aWidget, aDOMNode, aPrincipal, aCsp, aCookieJarSettings,
531 aTransferableArray, aActionType, nsIContentPolicy::TYPE_INTERNAL_IMAGE);
532 mRegion = Nothing();
533 return rv;
536 NS_IMETHODIMP
537 nsBaseDragService::InvokeDragSessionWithSelection(
538 Selection* aSelection, nsIPrincipal* aPrincipal,
539 nsIContentSecurityPolicy* aCsp, nsICookieJarSettings* aCookieJarSettings,
540 nsIArray* aTransferableArray, uint32_t aActionType, DragEvent* aDragEvent,
541 DataTransfer* aDataTransfer, nsINode* aTargetContent) {
542 nsCOMPtr<nsIWidget> widget =
543 aDragEvent->WidgetEventPtr()->AsDragEvent()->mWidget;
544 MOZ_ASSERT(widget);
546 NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
547 NS_ENSURE_TRUE(aDragEvent, NS_ERROR_NULL_POINTER);
548 NS_ENSURE_TRUE(aTargetContent, NS_ERROR_NULL_POINTER);
549 NS_ENSURE_TRUE(mSuppressLevel == 0, NS_ERROR_FAILURE);
551 RefPtr<nsBaseDragSession> session =
552 CreateDragSession().downcast<nsBaseDragSession>();
553 if (XRE_IsParentProcess()) {
554 mCurrentParentDragSession = session;
556 bool isSynthesized =
557 aDragEvent->WidgetEventPtr()->mFlags.mIsSynthesizedForTests &&
558 !GetNeverAllowSessionIsSynthesizedForTests();
559 return session->InitWithSelection(widget, aSelection, aPrincipal, aCsp,
560 aCookieJarSettings, aTransferableArray,
561 aActionType, aDragEvent, aDataTransfer,
562 aTargetContent, isSynthesized);
565 nsresult nsBaseDragSession::InitWithSelection(
566 nsIWidget* aWidget, Selection* aSelection, nsIPrincipal* aPrincipal,
567 nsIContentSecurityPolicy* aCsp, nsICookieJarSettings* aCookieJarSettings,
568 nsIArray* aTransferableArray, uint32_t aActionType, DragEvent* aDragEvent,
569 DataTransfer* aDataTransfer, nsINode* aTargetContent,
570 bool aIsSynthesizedForTests) {
571 mSessionIsSynthesizedForTests = aIsSynthesizedForTests;
572 mDataTransfer = aDataTransfer;
573 mSelection = aSelection;
574 mHasImage = true;
575 mDragPopup = nullptr;
576 mImage = nullptr;
577 mImageOffset = CSSIntPoint();
578 mDragStartData = nullptr;
579 mRegion = Nothing();
581 mScreenPosition = RoundedToInt(aDragEvent->ScreenPoint(CallerType::System));
582 mInputSource = aDragEvent->InputSource(CallerType::System);
584 // XXXndeakin this should actually be the deepest node that contains both
585 // endpoints of the selection
586 nsCOMPtr<nsINode> node = aTargetContent;
587 mSourceWindowContext = node->OwnerDoc()->GetWindowContext();
588 mSourceTopWindowContext =
589 mSourceWindowContext ? mSourceWindowContext->TopWindowContext() : nullptr;
591 return InvokeDragSession(aWidget, node, aPrincipal, aCsp, aCookieJarSettings,
592 aTransferableArray, aActionType,
593 nsIContentPolicy::TYPE_OTHER);
596 //-------------------------------------------------------------------------
597 NS_IMETHODIMP
598 nsBaseDragService::GetCurrentSession(nsISupports* aWidgetProvider,
599 nsIDragSession** aSession) {
600 MOZ_ASSERT(XRE_IsParentProcess());
601 if (!aSession) {
602 return NS_ERROR_INVALID_ARG;
605 if (!mSuppressLevel && mCurrentParentDragSession) {
606 RefPtr<nsIDragSession> session = mCurrentParentDragSession;
607 session.forget(aSession);
608 } else {
609 *aSession = nullptr;
612 return NS_OK;
615 //-------------------------------------------------------------------------
616 nsIDragSession* nsBaseDragService::StartDragSession(
617 nsISupports* aWidgetProvider) {
618 MOZ_ASSERT(XRE_IsParentProcess());
619 if (!aWidgetProvider) {
620 return nullptr;
622 if (mCurrentParentDragSession) {
623 return mCurrentParentDragSession;
626 RefPtr<nsIDragSession> session = CreateDragSession();
627 mCurrentParentDragSession = session;
628 return session;
631 NS_IMETHODIMP
632 nsBaseDragSession::InitForTests(uint32_t aAllowedEffect) {
633 mDragAction = aAllowedEffect;
634 mEffectAllowedForTests = aAllowedEffect;
635 mSessionIsSynthesizedForTests = true;
636 return NS_OK;
639 NS_IMETHODIMP nsBaseDragService::StartDragSessionForTests(
640 nsISupports* aWidgetProvider, uint32_t aAllowedEffect) {
641 // This method must set mSessionIsSynthesizedForTests
642 MOZ_ASSERT(!mNeverAllowSessionIsSynthesizedForTests);
644 RefPtr<nsIDragSession> session = StartDragSession(aWidgetProvider);
645 MOZ_ASSERT(session);
646 session->InitForTests(aAllowedEffect);
647 return NS_OK;
650 void nsBaseDragSession::OpenDragPopup() {
651 if (mDragPopup) {
652 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
653 if (pm) {
654 pm->ShowPopupAtScreen(mDragPopup, mScreenPosition.x - mImageOffset.x,
655 mScreenPosition.y - mImageOffset.y, false, nullptr);
660 int32_t nsBaseDragSession::TakeChildProcessDragAction() {
661 // If the last event was dispatched to the child process, use the drag action
662 // assigned from it instead and return it. DRAGDROP_ACTION_UNINITIALIZED is
663 // returned otherwise.
664 int32_t retval = nsIDragService::DRAGDROP_ACTION_UNINITIALIZED;
665 if (TakeDragEventDispatchedToChildProcess() &&
666 mDragActionFromChildProcess !=
667 nsIDragService::DRAGDROP_ACTION_UNINITIALIZED) {
668 retval = mDragActionFromChildProcess;
671 return retval;
674 //-------------------------------------------------------------------------
675 NS_IMETHODIMP
676 nsBaseDragSession::EndDragSession(bool aDoneDrag, uint32_t aKeyModifiers) {
677 if (mDelayedDropTarget) {
678 if (!mEndDragSessionData) {
679 EndDragSessionData edsData = {aDoneDrag, aKeyModifiers};
680 mEndDragSessionData = Some(edsData);
682 return NS_OK;
684 return EndDragSessionImpl(aDoneDrag, aKeyModifiers);
687 nsresult nsBaseDragSession::EndDragSessionImpl(bool aDoneDrag,
688 uint32_t aKeyModifiers) {
689 MOZ_DRAGSERVICE_LOG("[%p] EndDragSession | mDoingDrag %s", this,
690 mDoingDrag ? "true" : "false");
691 if (!mDoingDrag || mEndingSession) {
692 return NS_ERROR_FAILURE;
695 mEndingSession = true;
697 if (aDoneDrag && !GetSuppressLevel()) {
698 FireDragEventAtSource(eDragEnd, aKeyModifiers);
701 if (mDragPopup) {
702 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
703 if (pm) {
704 pm->HidePopup(mDragPopup, {HidePopupOption::DeselectMenu});
708 uint32_t dropEffect = nsIDragService::DRAGDROP_ACTION_NONE;
709 if (mDataTransfer) {
710 dropEffect = mDataTransfer->DropEffectInt();
713 for (nsWeakPtr& browser : mBrowsers) {
714 nsCOMPtr<BrowserParent> bp = do_QueryReferent(browser);
715 if (NS_WARN_IF(!bp)) {
716 continue;
718 mozilla::Unused << bp->SendEndDragSession(
719 aDoneDrag, mUserCancelled, mEndDragPoint, aKeyModifiers, dropEffect);
720 // Continue sending input events with input priority when stopping the dnd
721 // session.
722 bp->Manager()->SetInputPriorityEventEnabled(true);
724 mBrowsers.Clear();
726 // mDataTransfer and the items it owns are going to die anyway, but we
727 // explicitly deref the contained data here so that we don't have to wait for
728 // CC to reclaim the memory.
729 if (XRE_IsParentProcess()) {
730 DiscardInternalTransferData();
731 nsCOMPtr<nsIDragService> svc =
732 do_GetService("@mozilla.org/widget/dragservice;1");
733 if (svc) {
734 static_cast<nsBaseDragService*>(svc.get())
735 ->ClearCurrentParentDragSession();
739 mDoingDrag = false;
740 mSessionIsSynthesizedForTests = false;
741 mIsDraggingTextInTextControl = false;
742 mEffectAllowedForTests = nsIDragService::DRAGDROP_ACTION_UNINITIALIZED;
743 mEndingSession = false;
744 mCanDrop = false;
746 // release the source we've been holding on to.
747 mSourceDocument = nullptr;
748 mSourceNode = nullptr;
749 mSourceWindowContext = nullptr;
750 mSourceTopWindowContext = nullptr;
751 mTriggeringPrincipal = nullptr;
752 mCsp = nullptr;
753 mSelection = nullptr;
754 mDataTransfer = nullptr;
755 mHasImage = false;
756 mUserCancelled = false;
757 mDragPopup = nullptr;
758 mDragStartData = nullptr;
759 mImage = nullptr;
760 mImageOffset = CSSIntPoint();
761 mScreenPosition = CSSIntPoint();
762 mEndDragPoint = LayoutDeviceIntPoint(0, 0);
763 mInputSource = MouseEvent_Binding::MOZ_SOURCE_MOUSE;
764 mRegion = Nothing();
766 return NS_OK;
769 void nsBaseDragSession::DiscardInternalTransferData() {
770 if (mDataTransfer && mSourceNode) {
771 MOZ_ASSERT(mDataTransfer);
773 DataTransferItemList* items = mDataTransfer->Items();
774 for (size_t i = 0; i < items->Length(); i++) {
775 bool found;
776 DataTransferItem* item = items->IndexedGetter(i, found);
778 // Non-OTHER items may still be needed by JS. Skip them.
779 if (!found || item->Kind() != DataTransferItem::KIND_OTHER) {
780 continue;
783 nsCOMPtr<nsIVariant> variant = item->DataNoSecurityCheck();
784 nsCOMPtr<nsIWritableVariant> writable = do_QueryInterface(variant);
786 if (writable) {
787 writable->SetAsEmpty();
793 NS_IMETHODIMP
794 nsBaseDragSession::FireDragEventAtSource(EventMessage aEventMessage,
795 uint32_t aKeyModifiers) {
796 if (!mSourceNode || !mSourceDocument || GetSuppressLevel()) {
797 return NS_OK;
799 RefPtr<PresShell> presShell = mSourceDocument->GetPresShell();
800 if (!presShell) {
801 return NS_OK;
804 RefPtr<nsPresContext> pc = presShell->GetPresContext();
805 nsCOMPtr<nsIWidget> widget = pc ? pc->GetRootWidget() : nullptr;
807 nsEventStatus status = nsEventStatus_eIgnore;
808 WidgetDragEvent event(true, aEventMessage, widget);
809 event.mFlags.mIsSynthesizedForTests = mSessionIsSynthesizedForTests;
810 event.mInputSource = mInputSource;
811 if (aEventMessage == eDragEnd) {
812 event.mRefPoint = mEndDragPoint;
813 if (widget) {
814 event.mRefPoint -= widget->WidgetToTopLevelWidgetOffset();
816 event.mUserCancelled = mUserCancelled;
818 event.mModifiers = aKeyModifiers;
820 // Most drag events aren't able to converted to MouseEvent except to
821 // eDragStart and eDragEnd.
822 if (widget && event.CanConvertToInputData()) {
823 // Send the drag event to APZ, which needs to know about them to be
824 // able to accurately detect the end of a drag gesture.
825 widget->DispatchEventToAPZOnly(&event);
828 nsCOMPtr<nsIContent> content = do_QueryInterface(mSourceNode);
829 return presShell->HandleDOMEventWithTarget(content, &event, &status);
832 /* This is used by Windows and Mac to update the position of a popup being
833 * used as a drag image during the drag. This isn't used on GTK as it manages
834 * the drag popup itself.
836 NS_IMETHODIMP
837 nsBaseDragSession::DragMoved(int32_t aX, int32_t aY) {
838 if (mDragPopup) {
839 nsIFrame* frame = mDragPopup->GetPrimaryFrame();
840 if (frame && frame->IsMenuPopupFrame()) {
841 CSSIntPoint cssPos =
842 RoundedToInt(LayoutDeviceIntPoint(aX, aY) /
843 frame->PresContext()->CSSToDevPixelScale()) -
844 mImageOffset;
845 static_cast<nsMenuPopupFrame*>(frame)->MoveTo(cssPos, true);
849 return NS_OK;
852 static PresShell* GetPresShellForContent(nsINode* aDOMNode) {
853 nsCOMPtr<nsIContent> content = do_QueryInterface(aDOMNode);
854 if (!content) return nullptr;
856 RefPtr<Document> document = content->GetComposedDoc();
857 if (document) {
858 document->FlushPendingNotifications(FlushType::Layout);
859 return document->GetPresShell();
862 return nullptr;
865 nsresult nsBaseDragSession::DrawDrag(nsINode* aDOMNode,
866 const Maybe<CSSIntRegion>& aRegion,
867 CSSIntPoint aScreenPosition,
868 LayoutDeviceIntRect* aScreenDragRect,
869 RefPtr<SourceSurface>* aSurface,
870 nsPresContext** aPresContext) {
871 *aSurface = nullptr;
872 *aPresContext = nullptr;
874 // use a default size, in case of an error.
875 aScreenDragRect->SetRect(aScreenPosition.x - mImageOffset.x,
876 aScreenPosition.y - mImageOffset.y, 1, 1);
878 // if a drag image was specified, use that, otherwise, use the source node
879 nsCOMPtr<nsINode> dragNode = mImage ? mImage.get() : aDOMNode;
881 // get the presshell for the node being dragged. If the drag image is not in
882 // a document or has no frame, get the presshell from the source drag node
883 PresShell* presShell = GetPresShellForContent(dragNode);
884 if (!presShell && mImage) {
885 presShell = GetPresShellForContent(aDOMNode);
887 if (!presShell) {
888 return NS_ERROR_FAILURE;
891 *aPresContext = presShell->GetPresContext();
893 if (mDragStartData) {
894 if (mImage) {
895 // Just clear the surface if chrome has overridden it with an image.
896 *aSurface = nullptr;
897 } else {
898 *aSurface = mDragStartData->TakeVisualization(aScreenDragRect);
901 mDragStartData = nullptr;
902 return NS_OK;
905 // convert mouse position to dev pixels of the prescontext
906 const CSSIntPoint screenPosition = aScreenPosition - mImageOffset;
907 const auto screenPoint = LayoutDeviceIntPoint::Round(
908 screenPosition * (*aPresContext)->CSSToDevPixelScale());
909 aScreenDragRect->MoveTo(screenPoint.x, screenPoint.y);
911 // check if drag images are disabled
912 bool enableDragImages = Preferences::GetBool(DRAGIMAGES_PREF, true);
914 // didn't want an image, so just set the screen rectangle to the frame size
915 if (!enableDragImages || !mHasImage) {
916 // This holds a quantity in RelativeTo{presShell->GetRootFrame(),
917 // ViewportType::Layout} space.
918 nsRect presLayoutRect;
919 if (aRegion) {
920 // if a region was specified, set the screen rectangle to the area that
921 // the region occupies
922 presLayoutRect = ToAppUnits(aRegion->GetBounds(), AppUnitsPerCSSPixel());
923 } else {
924 // otherwise, there was no region so just set the rectangle to
925 // the size of the primary frame of the content.
926 nsCOMPtr<nsIContent> content = do_QueryInterface(dragNode);
927 if (nsIFrame* frame = content->GetPrimaryFrame()) {
928 presLayoutRect = frame->GetBoundingClientRect();
932 LayoutDeviceRect screenVisualRect = ViewportUtils::ToScreenRelativeVisual(
933 LayoutDeviceRect::FromAppUnits(presLayoutRect,
934 (*aPresContext)->AppUnitsPerDevPixel()),
935 *aPresContext);
936 aScreenDragRect->SizeTo(screenVisualRect.Width(),
937 screenVisualRect.Height());
938 return NS_OK;
941 // draw the image for selections
942 if (mSelection) {
943 LayoutDeviceIntPoint pnt(aScreenDragRect->TopLeft());
944 *aSurface = presShell->RenderSelection(
945 mSelection, pnt, aScreenDragRect,
946 mImage ? RenderImageFlags::None : RenderImageFlags::AutoScale);
947 return NS_OK;
950 // if a custom image was specified, check if it is an image node and draw
951 // using the source rather than the displayed image. But if mImage isn't
952 // an image or canvas, fall through to RenderNode below.
953 if (mImage) {
954 nsCOMPtr<nsIContent> content = do_QueryInterface(dragNode);
955 HTMLCanvasElement* canvas = HTMLCanvasElement::FromNodeOrNull(content);
956 if (canvas) {
957 return DrawDragForImage(*aPresContext, nullptr, canvas, aScreenDragRect,
958 aSurface);
961 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(dragNode);
962 // for image nodes, create the drag image from the actual image data
963 if (imageLoader) {
964 return DrawDragForImage(*aPresContext, imageLoader, nullptr,
965 aScreenDragRect, aSurface);
968 // If the image is a popup, use that as the image. This allows custom drag
969 // images that can change during the drag, but means that any platform
970 // default image handling won't occur.
971 // XXXndeakin this should be chrome-only
973 nsIFrame* frame = content->GetPrimaryFrame();
974 if (frame && frame->IsMenuPopupFrame()) {
975 mDragPopup = content->AsElement();
979 if (!mDragPopup) {
980 // otherwise, just draw the node
981 RenderImageFlags renderFlags =
982 mImage ? RenderImageFlags::None : RenderImageFlags::AutoScale;
983 if (renderFlags != RenderImageFlags::None) {
984 // check if the dragged node itself is an img element
985 if (dragNode->NodeName().LowerCaseEqualsLiteral("img")) {
986 renderFlags = renderFlags | RenderImageFlags::IsImage;
987 } else {
988 nsINodeList* childList = dragNode->ChildNodes();
989 uint32_t length = childList->Length();
990 // check every childnode for being an img element
991 // XXXbz why don't we need to check descendants recursively?
992 for (uint32_t count = 0; count < length; ++count) {
993 if (childList->Item(count)->NodeName().LowerCaseEqualsLiteral(
994 "img")) {
995 // if the dragnode contains an image, set RenderImageFlags::IsImage
996 // flag
997 renderFlags = renderFlags | RenderImageFlags::IsImage;
998 break;
1003 LayoutDeviceIntPoint pnt(aScreenDragRect->TopLeft());
1004 *aSurface = presShell->RenderNode(dragNode, aRegion, pnt, aScreenDragRect,
1005 renderFlags);
1008 // If an image was specified, reset the position from the offset that was
1009 // supplied.
1010 if (mImage) {
1011 aScreenDragRect->MoveTo(screenPoint.x, screenPoint.y);
1014 return NS_OK;
1017 nsresult nsBaseDragSession::DrawDragForImage(
1018 nsPresContext* aPresContext, nsIImageLoadingContent* aImageLoader,
1019 HTMLCanvasElement* aCanvas, LayoutDeviceIntRect* aScreenDragRect,
1020 RefPtr<SourceSurface>* aSurface) {
1021 nsCOMPtr<imgIContainer> imgContainer;
1022 if (aImageLoader) {
1023 nsCOMPtr<imgIRequest> imgRequest;
1024 nsresult rv = aImageLoader->GetRequest(
1025 nsIImageLoadingContent::CURRENT_REQUEST, getter_AddRefs(imgRequest));
1026 NS_ENSURE_SUCCESS(rv, rv);
1027 if (!imgRequest) return NS_ERROR_NOT_AVAILABLE;
1029 rv = imgRequest->GetImage(getter_AddRefs(imgContainer));
1030 NS_ENSURE_SUCCESS(rv, rv);
1031 if (!imgContainer) return NS_ERROR_NOT_AVAILABLE;
1033 // use the size of the image as the size of the drag image
1034 int32_t imageWidth, imageHeight;
1035 rv = imgContainer->GetWidth(&imageWidth);
1036 NS_ENSURE_SUCCESS(rv, rv);
1038 rv = imgContainer->GetHeight(&imageHeight);
1039 NS_ENSURE_SUCCESS(rv, rv);
1041 aScreenDragRect->SizeTo(aPresContext->CSSPixelsToDevPixels(imageWidth),
1042 aPresContext->CSSPixelsToDevPixels(imageHeight));
1043 } else {
1044 // Bug 1907668: The canvas size should be converted to dev pixels.
1045 NS_ASSERTION(aCanvas, "both image and canvas are null");
1046 CSSIntSize sz = aCanvas->GetSize();
1047 aScreenDragRect->SizeTo(sz.width, sz.height);
1050 nsIntSize destSize;
1051 destSize.width = aScreenDragRect->Width();
1052 destSize.height = aScreenDragRect->Height();
1053 if (destSize.width == 0 || destSize.height == 0) return NS_ERROR_FAILURE;
1055 nsresult result = NS_OK;
1056 if (aImageLoader) {
1057 RefPtr<DrawTarget> dt =
1058 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
1059 destSize, SurfaceFormat::B8G8R8A8);
1060 if (!dt || !dt->IsValid()) return NS_ERROR_FAILURE;
1062 gfxContext ctx(dt);
1064 ImgDrawResult res = imgContainer->Draw(
1065 &ctx, destSize, ImageRegion::Create(destSize),
1066 imgIContainer::FRAME_CURRENT, SamplingFilter::GOOD, SVGImageContext(),
1067 imgIContainer::FLAG_SYNC_DECODE, 1.0);
1068 if (res == ImgDrawResult::BAD_IMAGE || res == ImgDrawResult::BAD_ARGS ||
1069 res == ImgDrawResult::NOT_SUPPORTED) {
1070 return NS_ERROR_FAILURE;
1072 *aSurface = dt->Snapshot();
1073 } else {
1074 *aSurface = aCanvas->GetSurfaceSnapshot();
1077 return result;
1080 NS_IMETHODIMP
1081 nsBaseDragService::Suppress() {
1082 RefPtr<nsIDragSession> session = mCurrentParentDragSession;
1083 if (session) {
1084 session->EndDragSession(false, 0);
1086 ++mSuppressLevel;
1087 return NS_OK;
1090 NS_IMETHODIMP
1091 nsBaseDragService::Unsuppress() {
1092 --mSuppressLevel;
1093 return NS_OK;
1096 NS_IMETHODIMP
1097 nsBaseDragSession::UserCancelled() {
1098 mUserCancelled = true;
1099 return NS_OK;
1102 NS_IMETHODIMP
1103 nsBaseDragSession::UpdateDragEffect() {
1104 mDragActionFromChildProcess = mDragAction;
1105 return NS_OK;
1108 NS_IMETHODIMP
1109 nsBaseDragSession::UpdateDragImage(nsINode* aImage, int32_t aImageX,
1110 int32_t aImageY) {
1111 // Don't change the image if this is a drag from another source or if there
1112 // is a drag popup.
1113 if (!mSourceNode || mDragPopup) return NS_OK;
1115 mImage = aImage;
1116 mImageOffset = CSSIntPoint(aImageX, aImageY);
1117 return NS_OK;
1120 NS_IMETHODIMP
1121 nsBaseDragSession::DragEventDispatchedToChildProcess() {
1122 mDragEventDispatchedToChildProcess = true;
1123 return NS_OK;
1126 static bool MaybeAddBrowser(nsTArray<nsWeakPtr>& aBrowsers,
1127 BrowserParent* aBP) {
1128 nsWeakPtr browser = do_GetWeakReference(aBP);
1130 // Equivalent to `InsertElementSorted`, avoiding inserting a duplicate
1131 // element. See bug 1896166.
1132 size_t index = aBrowsers.IndexOfFirstElementGt(browser);
1133 if (index == 0 || aBrowsers[index - 1] != browser) {
1134 aBrowsers.InsertElementAt(index, browser);
1135 return true;
1137 return false;
1140 static bool RemoveAllBrowsers(nsTArray<nsWeakPtr>& aBrowsers) {
1141 for (auto& weakBrowser : aBrowsers) {
1142 nsCOMPtr<BrowserParent> browser = do_QueryReferent(weakBrowser);
1143 if (NS_WARN_IF(!browser)) {
1144 continue;
1146 mozilla::Unused << browser->SendEndDragSession(
1147 true, false, LayoutDeviceIntPoint(), 0,
1148 nsIDragService::DRAGDROP_ACTION_NONE);
1151 aBrowsers.Clear();
1152 return true;
1155 bool nsBaseDragService::MaybeAddBrowser(BrowserParent* aBP) {
1156 nsCOMPtr<nsIDragSession> session;
1157 GetCurrentSession(nullptr, getter_AddRefs(session));
1158 if (session) {
1159 return session->MaybeAddBrowser(aBP);
1161 return ::MaybeAddBrowser(mBrowsers, aBP);
1164 bool nsBaseDragService::RemoveAllBrowsers() {
1165 nsCOMPtr<nsIDragSession> session;
1166 GetCurrentSession(nullptr, getter_AddRefs(session));
1167 if (session) {
1168 return session->RemoveAllBrowsers();
1170 return ::RemoveAllBrowsers(mBrowsers);
1173 bool nsBaseDragSession::MaybeAddBrowser(BrowserParent* aBP) {
1174 return ::MaybeAddBrowser(mBrowsers, aBP);
1177 bool nsBaseDragSession::RemoveAllBrowsers() {
1178 return ::RemoveAllBrowsers(mBrowsers);
1181 bool nsBaseDragSession::MustUpdateDataTransfer(EventMessage aMessage) {
1182 return false;
1185 NS_IMETHODIMP
1186 nsBaseDragSession::MaybeEditorDeletedSourceNode(Element* aEditingHost) {
1187 // If builtin editor of Blink and WebKit deletes the source node,they retarget
1188 // the source node to the editing host.
1189 // https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/page/drag_controller.cc;l=724;drc=d9ba13b8cd8ac0faed7afc3d1f7e4b67ebac2a0b
1190 // That allows editor apps listens to "dragend" event in editing host or its
1191 // ancestors. Therefore, we should follow them for compatibility.
1192 if (mSourceNode && !mSourceNode->IsInComposedDoc()) {
1193 mSourceNode = aEditingHost;
1195 return NS_OK;
1198 NS_IMETHODIMP
1199 nsBaseDragService::GetMockDragController(
1200 nsIMockDragServiceController** aController) {
1201 #ifdef ENABLE_TESTS
1202 if (XRE_IsContentProcess()) {
1203 // The mock drag controller is only available in the parent process.
1204 MOZ_ASSERT(!XRE_IsContentProcess());
1205 return NS_ERROR_NOT_AVAILABLE;
1207 if (!mMockController) {
1208 mMockController = new mozilla::test::MockDragServiceController();
1210 auto controller = mMockController;
1211 controller.forget(aController);
1212 return NS_OK;
1213 #else
1214 *aController = nullptr;
1215 MOZ_ASSERT(false, "CreateMockDragController may only be called for testing");
1216 return NS_ERROR_NOT_AVAILABLE;
1217 #endif
1220 NS_IMETHODIMP
1221 nsBaseDragService::GetNeverAllowSessionIsSynthesizedForTests(
1222 bool* aNeverAllow) {
1223 *aNeverAllow = mNeverAllowSessionIsSynthesizedForTests;
1224 return NS_OK;
1227 NS_IMETHODIMP
1228 nsBaseDragService::SetNeverAllowSessionIsSynthesizedForTests(bool aNeverAllow) {
1229 mNeverAllowSessionIsSynthesizedForTests = aNeverAllow;
1230 return NS_OK;
1233 NS_IMETHODIMP
1234 nsBaseDragSession::SetDragEndPoint(int32_t aScreenX, int32_t aScreenY) {
1235 SetDragEndPoint(LayoutDeviceIntPoint(aScreenX, aScreenY));
1236 return NS_OK;
1239 void nsBaseDragSession::TakeSessionBrowserListFromService() {
1240 nsCOMPtr<nsIDragService> svc =
1241 do_GetService("@mozilla.org/widget/dragservice;1");
1242 NS_ENSURE_TRUE_VOID(svc);
1243 mBrowsers =
1244 static_cast<nsBaseDragService*>(svc.get())->TakeSessionBrowserList();
1247 /* static */
1248 nsIWidget* nsBaseDragService::GetWidgetFromWidgetProvider(
1249 nsISupports* aWidgetProvider) {
1250 nsCOMPtr<nsIWidget> widget = do_QueryObject(aWidgetProvider);
1251 if (widget) {
1252 return widget;
1255 nsPIDOMWindowOuter* outer;
1256 if (aWidgetProvider) {
1257 nsCOMPtr<mozIDOMWindow> window = do_GetInterface(aWidgetProvider);
1258 NS_ENSURE_TRUE(window, nullptr);
1259 RefPtr<nsPIDOMWindowInner> innerWin = nsGlobalWindowInner::Cast(window);
1260 NS_ENSURE_TRUE(innerWin, nullptr);
1261 outer = innerWin->GetOuterWindow();
1262 } else {
1263 nsCOMPtr<nsPIDOMWindowInner> winInner;
1264 winInner = do_QueryInterface(GetEntryGlobal());
1265 NS_ENSURE_TRUE(winInner, nullptr);
1266 outer = winInner->GetOuterWindow();
1268 NS_ENSURE_TRUE(outer, nullptr);
1269 nsIDocShell* docShell = outer->GetDocShell();
1270 NS_ENSURE_TRUE(docShell, nullptr);
1271 PresShell* presShell = docShell->GetPresShell();
1272 NS_ENSURE_TRUE(presShell, nullptr);
1273 nsViewManager* vm = presShell->GetViewManager();
1274 NS_ENSURE_TRUE(vm, nullptr);
1275 return vm->GetRootWidget();
1278 NS_IMETHODIMP
1279 nsBaseDragSession::SendStoreDropTargetAndDelayEndDragSession(
1280 DragEvent* aEvent) {
1281 mDelayedDropBrowserParent = dom::BrowserParent::GetBrowserParentFromLayersId(
1282 aEvent->WidgetEventPtr()->mLayersId);
1283 NS_ENSURE_TRUE(mDelayedDropBrowserParent, NS_ERROR_FAILURE);
1284 uint32_t dropEffect = nsIDragService::DRAGDROP_ACTION_NONE;
1285 if (mDataTransfer) {
1286 dropEffect = mDataTransfer->DropEffectInt();
1288 Unused
1289 << mDelayedDropBrowserParent->SendStoreDropTargetAndDelayEndDragSession(
1290 aEvent->WidgetEventPtr()->mRefPoint, dropEffect, mDragAction,
1291 mTriggeringPrincipal, mCsp);
1292 return NS_OK;
1295 NS_IMETHODIMP
1296 nsBaseDragSession::SendDispatchToDropTargetAndResumeEndDragSession(
1297 bool aShouldDrop) {
1298 MOZ_ASSERT(mDelayedDropBrowserParent);
1299 Unused << mDelayedDropBrowserParent
1300 ->SendDispatchToDropTargetAndResumeEndDragSession(aShouldDrop);
1301 mDelayedDropBrowserParent = nullptr;
1302 return NS_OK;
1305 NS_IMETHODIMP
1306 nsBaseDragSession::StoreDropTargetAndDelayEndDragSession(
1307 mozilla::dom::Element* aElement, nsIFrame* aFrame) {
1308 MOZ_ASSERT(XRE_IsContentProcess());
1309 MOZ_DRAGSERVICE_LOG(
1310 "[%p] StoreDropTargetAndDelayEndDragSession | aElement: %p | aFrame: %p",
1311 this, aElement, aFrame);
1312 mDelayedDropTarget = do_GetWeakReference(aElement);
1313 mDelayedDropFrame = aFrame;
1314 return NS_OK;
1317 NS_IMETHODIMP
1318 nsBaseDragSession::DispatchToDropTargetAndResumeEndDragSession(
1319 nsIWidget* aWidget, const LayoutDeviceIntPoint& aPt, bool aShouldDrop) {
1320 MOZ_ASSERT(XRE_IsContentProcess());
1321 MOZ_DRAGSERVICE_LOG(
1322 "[%p] DispatchToDropTargetAndResumeEndDragSession | pt=(%d, %d) | "
1323 "shouldDrop: %s",
1324 this, static_cast<int32_t>(aPt.x), static_cast<int32_t>(aPt.y),
1325 aShouldDrop ? "true" : "false");
1327 RefPtr<Element> delayedDropTarget = do_QueryReferent(mDelayedDropTarget);
1328 mDelayedDropTarget = nullptr;
1329 nsIFrame* delayedDropFrame = mDelayedDropFrame;
1330 mDelayedDropFrame = nullptr;
1331 auto edsData = std::move(mEndDragSessionData);
1333 if (!delayedDropTarget) {
1334 MOZ_ASSERT(!edsData && !delayedDropFrame);
1335 return NS_OK;
1337 if (!delayedDropFrame) {
1338 // Weak frame was deleted
1339 return NS_OK;
1342 nsEventStatus status = nsEventStatus_eIgnore;
1343 RefPtr<PresShell> ps = delayedDropFrame->PresContext()->GetPresShell();
1344 auto event = MakeUnique<WidgetDragEvent>(
1345 true, aShouldDrop ? eDrop : eDragExit, aWidget);
1346 event->mRefPoint = aPt;
1347 ps->HandleEventWithTarget(event.get(), delayedDropFrame, delayedDropTarget,
1348 &status);
1350 // If EndDragSession was delayed, issue it now.
1351 if (edsData) {
1352 EndDragSession(edsData->mDoneDrag, edsData->mKeyModifiers);
1354 return NS_OK;