From 5197db74447895a347b7c6085b3eb44c5372d2b9 Mon Sep 17 00:00:00 2001 From: Edgar Chen Date: Fri, 4 Sep 2026 14:55:30 +0000 Subject: [PATCH] Bug 2064287 - Harden drag handling; r=smaug Differential Revision: https://phabricator.services.mozilla.com/D321984 --- dom/base/PointerLockManager.cpp | 11 +++++++ dom/base/PointerLockManager.h | 1 + dom/events/EventStateManager.cpp | 5 ++++ dom/events/EventStateManager.h | 5 ++++ dom/ipc/BrowserParent.cpp | 50 +++++++++++++++++++++++++------- dom/ipc/BrowserParent.h | 2 ++ widget/nsBaseDragService.cpp | 2 ++ widget/nsIDragService.idl | 5 ++++ 8 files changed, 71 insertions(+), 10 deletions(-) diff --git a/dom/base/PointerLockManager.cpp b/dom/base/PointerLockManager.cpp index 288361efd550..4f1e1b983113 100644 --- a/dom/base/PointerLockManager.cpp +++ b/dom/base/PointerLockManager.cpp @@ -510,6 +510,12 @@ void PointerLockManager::SetLockedRemoteTarget(BrowserParent* aBrowserParent, return; } + if (nsCOMPtr dragService = + do_GetService("@mozilla.org/widget/dragservice;1")) { + dragService->Suppress(); + } + presContext->EventStateManager()->StopTrackingDragGesture(true); + MOZ_POINTERLOCK_LOG("Set locked remote target to 0x%p", aBrowserParent); sLockedRemoteTarget = aBrowserParent; PointerEventHandler::ReleaseAllPointerCaptureRemoteTarget(); @@ -539,6 +545,11 @@ void PointerLockManager::ReleaseLockedRemoteTarget( nsCOMPtr widget = aBrowserParent->GetTopLevelWidget(); EventStateManager::ReleaseLockedPointer(widget); + + if (nsCOMPtr dragService = + do_GetService("@mozilla.org/widget/dragservice;1")) { + dragService->Unsuppress(); + } } } diff --git a/dom/base/PointerLockManager.h b/dom/base/PointerLockManager.h index 8ea5d76f5e6c..c9179ac5d6f2 100644 --- a/dom/base/PointerLockManager.h +++ b/dom/base/PointerLockManager.h @@ -54,6 +54,7 @@ class PointerLockManager final { // Set/release pointer lock remote target. Should only be called in parent // process. + MOZ_CAN_RUN_SCRIPT static void SetLockedRemoteTarget(dom::BrowserParent* aBrowserParent, const bool& aUnadjustedMovement, nsACString& aError); diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp index 3ef7a8a883be..881b2b41408d 100644 --- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -2636,6 +2636,8 @@ void EventStateManager::BeginTrackingDragGesture( if (!mGestureDownFrameOwner) { mGestureDownFrameOwner = mGestureDownContent; } + mGestureDownTopLevelRemoteTarget = + BrowserParent::GetFrom(mGestureDownContent); } mGestureModifiers = aMouseDownOrTouchDragEvent.mModifiers; mGestureDownButtons = aMouseDownOrTouchDragEvent.mButtons; @@ -2687,6 +2689,8 @@ void EventStateManager::StopTrackingDragGesture(bool aClearInChildProcesses) { return; } + mGestureDownTopLevelRemoteTarget = nullptr; + // Only notify if there is NOT a drag session active in the parent. RefPtr dragSession = nsContentUtils::GetDragSession(mPresContext); @@ -7990,6 +7994,7 @@ bool EventStateManager::WheelPrefs::IsOverOnePageScrollAllowedY( void EventStateManager::UpdateGestureContent(nsIContent* aContent) { mGestureDownContent = aContent; mGestureDownFrameOwner = aContent; + mGestureDownTopLevelRemoteTarget = BrowserParent::GetFrom(aContent); } void EventStateManager::NotifyContentWillBeRemovedForGesture( diff --git a/dom/events/EventStateManager.h b/dom/events/EventStateManager.h index e9963a401d27..af59f10e4a77 100644 --- a/dom/events/EventStateManager.h +++ b/dom/events/EventStateManager.h @@ -561,6 +561,10 @@ class EventStateManager : public nsSupportsWeakReference, public nsIObserver { return mGestureDownContent; } + dom::BrowserParent* GetTrackingDragGestureTopLevelRemoteTarget() const { + return mGestureDownTopLevelRemoteTarget; + } + // Update the tracked gesture content to the parent of its frame when it's // removed, so that the gesture can be continued. void NotifyContentWillBeRemovedForGesture(nsIContent& aContent); @@ -1446,6 +1450,7 @@ class EventStateManager : public nsSupportsWeakReference, public nsIObserver { // as the target in most cases but not always - for example when dragging // an of an image map this is the image. (bug 289667) nsCOMPtr mGestureDownFrameOwner; + RefPtr mGestureDownTopLevelRemoteTarget; // Data associated with a drag started in a content process. RefPtr mGestureDownDragStartData; // State of keys when the original gesture-down happened diff --git a/dom/ipc/BrowserParent.cpp b/dom/ipc/BrowserParent.cpp index 924bee2a2c57..d640d0f26539 100644 --- a/dom/ipc/BrowserParent.cpp +++ b/dom/ipc/BrowserParent.cpp @@ -1060,6 +1060,20 @@ mozilla::ipc::IPCResult BrowserParent::RecvSetDimensions( nsCOMPtr treeOwnerAsWin = do_QueryInterface(treeOwner); NS_ENSURE_TRUE(treeOwnerAsWin, IPC_OK()); + if (nsCOMPtr dragService = + do_GetService("@mozilla.org/widget/dragservice;1")) { + RefPtr widget = GetTopLevelWidget(); + if (RefPtr session = + dragService->GetCurrentSession(widget)) { + session->EndDragSession(false, 0); + } + } + + if (nsPresContext* presContext = + mFrameElement->OwnerDoc()->GetPresContext()) { + presContext->EventStateManager()->StopTrackingDragGesture(true); + } + // `BrowserChild` only sends the values to actually be changed, see more // details in `BrowserChild::SetDimensions()`. // Note that `BrowserChild::SetDimensions()` may be called before receiving @@ -3883,8 +3897,29 @@ mozilla::ipc::IPCResult BrowserParent::RecvInvokeDragSession( const CookieJarSettingsArgs& aCookieJarSettingsArgs, const MaybeDiscarded& aSourceWindowContext, const MaybeDiscarded& aSourceTopWindowContext) { - PresShell* presShell = mFrameElement->OwnerDoc()->GetPresShell(); - if (!presShell) { + nsCOMPtr dragService = + do_GetService("@mozilla.org/widget/dragservice;1"); + nsPresContext* presContext = mFrameElement->OwnerDoc()->GetPresContext(); + const bool isValidRemoteDrag = [&]() { + if (!dragService || !presContext) { + return false; + } + + if (dragService->GetIsSuppressed()) { + return false; + } + + BrowserParent* dragTopLevelRemoteTarget = + presContext->EventStateManager() + ->GetTrackingDragGestureTopLevelRemoteTarget(); + if (NS_WARN_IF(dragTopLevelRemoteTarget != TopLevelBrowserParent())) { + return false; + } + + return true; + }(); + + if (!isValidRemoteDrag) { (void)SendEndDragSession(true, true, LayoutDeviceIntPoint(), 0, nsIDragService::DRAGDROP_ACTION_NONE); // Continue sending input events with input priority when stopping the dnd @@ -3922,15 +3957,10 @@ mozilla::ipc::IPCResult BrowserParent::RecvInvokeDragSession( } } - nsCOMPtr dragService = - do_GetService("@mozilla.org/widget/dragservice;1"); - if (dragService) { - dragService->MaybeAddBrowser(this); - } + dragService->MaybeAddBrowser(this); - presShell->GetPresContext() - ->EventStateManager() - ->BeginTrackingRemoteDragGesture(mFrameElement, dragStartData); + presContext->EventStateManager()->BeginTrackingRemoteDragGesture( + mFrameElement, dragStartData); nsCOMPtr os = services::GetObserverService(); os->NotifyObservers(nullptr, "content-invoked-drag", nullptr); diff --git a/dom/ipc/BrowserParent.h b/dom/ipc/BrowserParent.h index 8461c1a392a5..6869c41f6549 100644 --- a/dom/ipc/BrowserParent.h +++ b/dom/ipc/BrowserParent.h @@ -739,6 +739,7 @@ class BrowserParent final : public PBrowserParent, mozilla::ipc::IPCResult RecvRemoteIsReadyToHandleInputEvents(); + MOZ_CAN_RUN_SCRIPT_BOUNDARY mozilla::ipc::IPCResult RecvSetDimensions(mozilla::DimensionRequest aRequest, const double& aScale); @@ -758,6 +759,7 @@ class BrowserParent final : public PBrowserParent, mozilla::ipc::IPCResult RecvMaybeFireEmbedderLoadEvents( EmbedderElementEventType aFireEventAtEmbeddingElement); + MOZ_CAN_RUN_SCRIPT_BOUNDARY mozilla::ipc::IPCResult RecvRequestPointerLock( const bool& aUnadjustedMovement, RequestPointerLockResolver&& aResolve); mozilla::ipc::IPCResult RecvReleasePointerLock(); diff --git a/widget/nsBaseDragService.cpp b/widget/nsBaseDragService.cpp index a67b8af99752..68cbacea55a3 100644 --- a/widget/nsBaseDragService.cpp +++ b/widget/nsBaseDragService.cpp @@ -1169,6 +1169,8 @@ nsBaseDragService::Unsuppress() { return NS_OK; } +bool nsBaseDragService::GetIsSuppressed() { return mSuppressLevel > 0; } + NS_IMETHODIMP nsBaseDragSession::UserCancelled() { mUserCancelled = true; diff --git a/widget/nsIDragService.idl b/widget/nsIDragService.idl index 5ef0fa0e6aa8..8ebc5a3b8af9 100644 --- a/widget/nsIDragService.idl +++ b/widget/nsIDragService.idl @@ -172,6 +172,11 @@ interface nsIDragService : nsISupports void suppress(); void unsuppress(); + /** + * True if the dragging is disabled. + */ + [notxpcom, nostdcall] readonly attribute boolean isSuppressed; + [notxpcom, nostdcall] boolean maybeAddBrowser(in BrowserParentPtr aBP); [notxpcom, nostdcall] boolean removeAllBrowsers();