From b5c5a81751ed4aabf32900a583aaa0fe94e5ff76 Mon Sep 17 00:00:00 2001 From: Dorel Luca Date: Wed, 27 Jan 2021 14:05:29 +0200 Subject: [PATCH] Backed out changeset 4e317086de97 (bug 1672330) for Mochitest failures in dom/tests/mochitest/pointerlock/test_pointerlock_focus.html. CLOSED TREE --- dom/base/Document.cpp | 350 +++++++++++++- dom/base/Document.h | 9 +- dom/base/DocumentOrShadowRoot.cpp | 3 +- dom/base/Element.cpp | 5 +- dom/base/PointerLockManager.cpp | 443 ------------------ dom/base/PointerLockManager.h | 82 ---- dom/base/moz.build | 2 - dom/base/nsContentUtils.cpp | 18 + dom/base/nsContentUtils.h | 8 + dom/base/nsFocusManager.cpp | 12 +- dom/events/Event.cpp | 5 +- dom/events/EventStateManager.cpp | 44 +- dom/events/EventStateManager.h | 4 + dom/events/PointerEventHandler.cpp | 3 +- dom/events/UIEvent.cpp | 3 +- dom/ipc/BrowserParent.cpp | 35 +- dom/ipc/BrowserParent.h | 10 + ...erlock_xorigin_iframe_no_user_gesture.html | 95 ---- dom/tests/mochitest/pointerlock/mochitest.ini | 2 - .../test_pointerlock_xorigin_iframe.html | 1 - layout/base/PresShell.cpp | 8 +- 21 files changed, 466 insertions(+), 676 deletions(-) delete mode 100644 dom/base/PointerLockManager.cpp delete mode 100644 dom/base/PointerLockManager.h delete mode 100644 dom/tests/mochitest/pointerlock/file_pointerlock_xorigin_iframe_no_user_gesture.html diff --git a/dom/base/Document.cpp b/dom/base/Document.cpp index 269d7b911a89..945e61f6261f 100644 --- a/dom/base/Document.cpp +++ b/dom/base/Document.cpp @@ -13718,7 +13718,7 @@ void Document::ExitFullscreenInDocTree(Document* aMaybeNotARootDoc) { MOZ_ASSERT(aMaybeNotARootDoc); // Unlock the pointer - PointerLockManager::Unlock(); + UnlockPointer(); // Resolve all promises which waiting for exit fullscreen. PendingFullscreenChangeList::Iterator iter( @@ -13825,7 +13825,7 @@ void Document::RestorePreviousFullscreenState(UniquePtr aExit) { } // If fullscreen mode is updated the pointer should be unlocked - PointerLockManager::Unlock(); + UnlockPointer(); // All documents listed in the array except the last one are going to // completely exit from the fullscreen state. for (auto i : IntegerRange(exitElements.Length() - 1)) { @@ -14428,7 +14428,7 @@ bool Document::ApplyFullscreen(UniquePtr aRequest) { // If a document is already in fullscreen, then unlock the mouse pointer // before setting a new document to fullscreen - PointerLockManager::Unlock(); + UnlockPointer(); // Set the fullscreen element. This sets the fullscreen style on the // element, and the fullscreen-ancestor styles on ancestors of the element @@ -14534,6 +14534,350 @@ bool Document::SetOrientationPendingPromise(Promise* aPromise) { return true; } +static void DispatchPointerLockChange(Document* aTarget) { + if (!aTarget) { + return; + } + + RefPtr asyncDispatcher = + new AsyncEventDispatcher(aTarget, u"pointerlockchange"_ns, + CanBubble::eYes, ChromeOnlyDispatch::eNo); + asyncDispatcher->PostDOMEvent(); +} + +static void DispatchPointerLockError(Document* aTarget, const char* aMessage) { + if (!aTarget) { + return; + } + + RefPtr asyncDispatcher = + new AsyncEventDispatcher(aTarget, u"pointerlockerror"_ns, CanBubble::eYes, + ChromeOnlyDispatch::eNo); + asyncDispatcher->PostDOMEvent(); + nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "DOM"_ns, + aTarget, nsContentUtils::eDOM_PROPERTIES, + aMessage); +} + +static const char* GetPointerLockError(Element* aElement, Element* aCurrentLock, + bool aNoFocusCheck = false) { + // Check if pointer lock pref is enabled + if (!StaticPrefs::full_screen_api_pointer_lock_enabled()) { + return "PointerLockDeniedDisabled"; + } + + nsCOMPtr ownerDoc = aElement->OwnerDoc(); + if (aCurrentLock && aCurrentLock->OwnerDoc() != ownerDoc) { + return "PointerLockDeniedInUse"; + } + + if (!aElement->IsInComposedDoc()) { + return "PointerLockDeniedNotInDocument"; + } + + if (ownerDoc->GetSandboxFlags() & SANDBOXED_POINTER_LOCK) { + return "PointerLockDeniedSandboxed"; + } + + // Check if the element is in a document with a docshell. + if (!ownerDoc->GetContainer()) { + return "PointerLockDeniedHidden"; + } + nsCOMPtr ownerWindow = ownerDoc->GetWindow(); + if (!ownerWindow) { + return "PointerLockDeniedHidden"; + } + nsCOMPtr ownerInnerWindow = ownerDoc->GetInnerWindow(); + if (!ownerInnerWindow) { + return "PointerLockDeniedHidden"; + } + if (ownerWindow->GetCurrentInnerWindow() != ownerInnerWindow) { + return "PointerLockDeniedHidden"; + } + + BrowsingContext* bc = ownerDoc->GetBrowsingContext(); + BrowsingContext* topBC = bc ? bc->Top() : nullptr; + WindowContext* topWC = ownerDoc->GetTopLevelWindowContext(); + if (!topBC || !topBC->IsActive() || !topWC || + topWC != topBC->GetCurrentWindowContext()) { + return "PointerLockDeniedHidden"; + } + + if (!aNoFocusCheck) { + if (!IsInActiveTab(ownerDoc)) { + return "PointerLockDeniedNotFocused"; + } + } + + return nullptr; +} + +static void ChangePointerLockedElement(Element* aElement, Document* aDocument, + Element* aPointerLockedElement) { + // aDocument here is not really necessary, as it is the uncomposed + // document of both aElement and aPointerLockedElement as far as one + // is not nullptr, and they wouldn't both be nullptr in any case. + // But since the caller of this function should have known what the + // document is, we just don't try to figure out what it should be. + MOZ_ASSERT(aDocument); + MOZ_ASSERT(aElement != aPointerLockedElement); + if (aPointerLockedElement) { + MOZ_ASSERT(aPointerLockedElement->GetComposedDoc() == aDocument); + aPointerLockedElement->ClearPointerLock(); + } + if (aElement) { + MOZ_ASSERT(aElement->GetComposedDoc() == aDocument); + aElement->SetPointerLock(); + EventStateManager::sPointerLockedElement = do_GetWeakReference(aElement); + EventStateManager::sPointerLockedDoc = do_GetWeakReference(aDocument); + NS_ASSERTION(EventStateManager::sPointerLockedElement && + EventStateManager::sPointerLockedDoc, + "aElement and this should support weak references!"); + } else { + EventStateManager::sPointerLockedElement = nullptr; + EventStateManager::sPointerLockedDoc = nullptr; + } + // Retarget all events to aElement via capture or + // stop retargeting if aElement is nullptr. + PresShell::SetCapturingContent(aElement, CaptureFlags::PointerLock); + DispatchPointerLockChange(aDocument); +} + +MOZ_CAN_RUN_SCRIPT_BOUNDARY static bool StartSetPointerLock( + Element* aElement, Document* aDocument) { + if (!aDocument->SetPointerLock(aElement, StyleCursorKind::None)) { + DispatchPointerLockError(aDocument, "PointerLockDeniedFailedToLock"); + return false; + } + + ChangePointerLockedElement(aElement, aDocument, nullptr); + nsContentUtils::DispatchEventOnlyToChrome( + aDocument, ToSupports(aElement), u"MozDOMPointerLock:Entered"_ns, + CanBubble::eYes, Cancelable::eNo, /* DefaultAction */ nullptr); + + return true; +} + +class PointerLockRequest final : public Runnable { + public: + PointerLockRequest(Element* aElement, bool aUserInputOrChromeCaller) + : mozilla::Runnable("PointerLockRequest"), + mElement(do_GetWeakReference(aElement)), + mDocument(do_GetWeakReference(aElement->OwnerDoc())), + mUserInputOrChromeCaller(aUserInputOrChromeCaller) {} + + NS_IMETHOD Run() final { + nsCOMPtr element = do_QueryReferent(mElement); + nsCOMPtr document = do_QueryReferent(mDocument); + + const char* error = nullptr; + if (!element || !document || !element->GetComposedDoc()) { + error = "PointerLockDeniedNotInDocument"; + } else if (element->GetComposedDoc() != document) { + error = "PointerLockDeniedMovedDocument"; + } + if (!error) { + nsCOMPtr pointerLockedElement = + do_QueryReferent(EventStateManager::sPointerLockedElement); + if (element == pointerLockedElement) { + DispatchPointerLockChange(document); + return NS_OK; + } + // Note, we must bypass focus change, so pass true as the last parameter! + error = GetPointerLockError(element, pointerLockedElement, true); + // Another element in the same document is requesting pointer lock, + // just grant it without user input check. + if (!error && pointerLockedElement) { + ChangePointerLockedElement(element, document, pointerLockedElement); + return NS_OK; + } + } + // If it is neither user input initiated, nor requested in fullscreen, + // it should be rejected. + if (!error && !mUserInputOrChromeCaller && + !document->GetUnretargetedFullScreenElement()) { + error = "PointerLockDeniedNotInputDriven"; + } + + if (error) { + DispatchPointerLockError(document, error); + return NS_OK; + } + + if (BrowserChild* browserChild = + BrowserChild::GetFrom(document->GetDocShell())) { + nsWeakPtr e = do_GetWeakReference(element); + nsWeakPtr doc = do_GetWeakReference(element->OwnerDoc()); + nsWeakPtr bc = do_GetWeakReference(browserChild); + browserChild->SendRequestPointerLock( + [e, doc, bc](const nsCString& aError) { + nsCOMPtr document = do_QueryReferent(doc); + if (!aError.IsEmpty()) { + DispatchPointerLockError(document, aError.get()); + return; + } + + const char* error = nullptr; + auto autoCleanup = MakeScopeExit([&] { + if (error) { + DispatchPointerLockError(document, error); + // If we are failed to set pointer lock, notify parent to stop + // redirect mouse event to this process. + if (nsCOMPtr browserChild = + do_QueryReferent(bc)) { + static_cast(browserChild.get()) + ->SendReleasePointerLock(); + } + } + }); + + nsCOMPtr element = do_QueryReferent(e); + if (!element || !document || !element->GetComposedDoc()) { + error = "PointerLockDeniedNotInDocument"; + return; + } + + if (element->GetComposedDoc() != document) { + error = "PointerLockDeniedMovedDocument"; + return; + } + + nsCOMPtr pointerLockedElement = + do_QueryReferent(EventStateManager::sPointerLockedElement); + error = GetPointerLockError(element, pointerLockedElement, true); + if (error) { + return; + } + + if (!StartSetPointerLock(element, document)) { + error = "PointerLockDeniedFailedToLock"; + return; + } + }, + [doc](mozilla::ipc::ResponseRejectReason) { + // IPC layer error + nsCOMPtr document = do_QueryReferent(doc); + if (!document) { + return; + } + + DispatchPointerLockError(document, "PointerLockDeniedFailedToLock"); + }); + } else { + StartSetPointerLock(element, document); + } + + return NS_OK; + }; + + private: + nsWeakPtr mElement; + nsWeakPtr mDocument; + bool mUserInputOrChromeCaller; +}; + +void Document::RequestPointerLock(Element* aElement, CallerType aCallerType) { + NS_ASSERTION(aElement, + "Must pass non-null element to Document::RequestPointerLock"); + + nsCOMPtr pointerLockedElement = + do_QueryReferent(EventStateManager::sPointerLockedElement); + if (aElement == pointerLockedElement) { + DispatchPointerLockChange(this); + return; + } + + if (const char* msg = GetPointerLockError(aElement, pointerLockedElement)) { + DispatchPointerLockError(this, msg); + return; + } + + bool userInputOrSystemCaller = HasValidTransientUserGestureActivation() || + aCallerType == CallerType::System; + nsCOMPtr request = + new PointerLockRequest(aElement, userInputOrSystemCaller); + Dispatch(TaskCategory::Other, request.forget()); +} + +bool Document::SetPointerLock(Element* aElement, StyleCursorKind aCursorStyle) { + MOZ_ASSERT(!aElement || aElement->OwnerDoc() == this, + "We should be either unlocking pointer (aElement is nullptr), " + "or locking pointer to an element in this document"); +#ifdef DEBUG + if (!aElement) { + nsCOMPtr pointerLockedDoc = + do_QueryReferent(EventStateManager::sPointerLockedDoc); + MOZ_ASSERT(pointerLockedDoc == this); + } +#endif + + PresShell* presShell = GetPresShell(); + if (!presShell) { + NS_WARNING("SetPointerLock(): No PresShell"); + if (!aElement) { + // If we are unlocking pointer lock, but for some reason the doc + // has already detached from the presshell, just ask the event + // state manager to release the pointer. + EventStateManager::SetPointerLock(nullptr, nullptr); + return true; + } + return false; + } + nsPresContext* presContext = presShell->GetPresContext(); + if (!presContext) { + NS_WARNING("SetPointerLock(): Unable to get PresContext"); + return false; + } + + nsCOMPtr widget; + nsIFrame* rootFrame = presShell->GetRootFrame(); + if (!NS_WARN_IF(!rootFrame)) { + widget = rootFrame->GetNearestWidget(); + NS_WARNING_ASSERTION(widget, + "SetPointerLock(): Unable to find widget in " + "presShell->GetRootFrame()->GetNearestWidget();"); + if (aElement && !widget) { + return false; + } + } + + // Hide the cursor and set pointer lock for future mouse events + RefPtr esm = presContext->EventStateManager(); + esm->SetCursor(aCursorStyle, nullptr, Nothing(), widget, true); + EventStateManager::SetPointerLock(widget, aElement); + + return true; +} + +void Document::UnlockPointer(Document* aDoc) { + if (!EventStateManager::sIsPointerLocked) { + return; + } + + nsCOMPtr pointerLockedDoc = + do_QueryReferent(EventStateManager::sPointerLockedDoc); + if (!pointerLockedDoc || (aDoc && aDoc != pointerLockedDoc)) { + return; + } + if (!pointerLockedDoc->SetPointerLock(nullptr, StyleCursorKind::Auto)) { + return; + } + + nsCOMPtr pointerLockedElement = + do_QueryReferent(EventStateManager::sPointerLockedElement); + ChangePointerLockedElement(nullptr, pointerLockedDoc, pointerLockedElement); + + if (BrowserChild* browserChild = + BrowserChild::GetFrom(pointerLockedDoc->GetDocShell())) { + browserChild->SendReleasePointerLock(); + } + + RefPtr asyncDispatcher = new AsyncEventDispatcher( + pointerLockedElement, u"MozDOMPointerLock:Exited"_ns, CanBubble::eYes, + ChromeOnlyDispatch::eYes); + asyncDispatcher->RunDOMEventWhenSafe(); +} + void Document::UpdateVisibilityState(DispatchVisibilityChange aDispatchEvent) { dom::VisibilityState oldState = mVisibilityState; mVisibilityState = ComputeVisibilityState(); diff --git a/dom/base/Document.h b/dom/base/Document.h index 29bec3014275..8e5cd23676c2 100644 --- a/dom/base/Document.h +++ b/dom/base/Document.h @@ -34,7 +34,6 @@ #include "mozilla/Maybe.h" #include "mozilla/MozPromise.h" #include "mozilla/NotNull.h" -#include "mozilla/PointerLockManager.h" #include "mozilla/PreloadService.h" #include "mozilla/RefPtr.h" #include "mozilla/Result.h" @@ -2009,6 +2008,12 @@ class Document : public nsINode, */ static bool HandlePendingFullscreenRequests(Document* aDocument); + void RequestPointerLock(Element* aElement, CallerType); + MOZ_CAN_RUN_SCRIPT bool SetPointerLock(Element* aElement, StyleCursorKind); + + MOZ_CAN_RUN_SCRIPT_BOUNDARY + static void UnlockPointer(Document* aDoc = nullptr); + // ScreenOrientation related APIs void ClearOrientationPendingPromise(); @@ -3402,7 +3407,7 @@ class Document : public nsINode, Element* GetUnretargetedFullScreenElement(); bool Fullscreen() { return !!GetFullscreenElement(); } already_AddRefed ExitFullscreen(ErrorResult&); - void ExitPointerLock() { PointerLockManager::Unlock(this); } + void ExitPointerLock() { UnlockPointer(this); } void GetFgColor(nsAString& aFgColor); void SetFgColor(const nsAString& aFgColor); void GetLinkColor(nsAString& aLinkColor); diff --git a/dom/base/DocumentOrShadowRoot.cpp b/dom/base/DocumentOrShadowRoot.cpp index 1b00a0087d54..39a1a5762efa 100644 --- a/dom/base/DocumentOrShadowRoot.cpp +++ b/dom/base/DocumentOrShadowRoot.cpp @@ -7,7 +7,6 @@ #include "DocumentOrShadowRoot.h" #include "mozilla/AnimationComparator.h" #include "mozilla/EventStateManager.h" -#include "mozilla/PointerLockManager.h" #include "mozilla/PresShell.h" #include "mozilla/SVGUtils.h" #include "mozilla/dom/AnimatableBinding.h" @@ -301,7 +300,7 @@ Element* DocumentOrShadowRoot::GetRetargetedFocusedElement() { Element* DocumentOrShadowRoot::GetPointerLockElement() { nsCOMPtr pointerLockedElement = - PointerLockManager::GetLockedElement(); + do_QueryReferent(EventStateManager::sPointerLockedElement); if (!pointerLockedElement) { return nullptr; } diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp index d08c6abac977..624eba7e9247 100644 --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -45,7 +45,6 @@ #include "mozilla/LookAndFeel.h" #include "mozilla/MouseEvents.h" #include "mozilla/NotNull.h" -#include "mozilla/PointerLockManager.h" #include "mozilla/PresShell.h" #include "mozilla/PresShellForwards.h" #include "mozilla/ReflowOutput.h" @@ -1831,7 +1830,7 @@ void Element::UnbindFromTree(bool aNullParent) { Document* document = GetComposedDoc(); if (HasPointerLock()) { - PointerLockManager::Unlock(); + Document::UnlockPointer(); } if (mState.HasState(NS_EVENT_STATE_FULLSCREEN)) { // The element being removed is an ancestor of the fullscreen element, @@ -3340,7 +3339,7 @@ already_AddRefed Element::RequestFullscreen(CallerType aCallerType, } void Element::RequestPointerLock(CallerType aCallerType) { - PointerLockManager::RequestLock(this, aCallerType); + OwnerDoc()->RequestPointerLock(this, aCallerType); } already_AddRefed Element::GetAsFlexContainer() { diff --git a/dom/base/PointerLockManager.cpp b/dom/base/PointerLockManager.cpp deleted file mode 100644 index 03cf83a5600b..000000000000 --- a/dom/base/PointerLockManager.cpp +++ /dev/null @@ -1,443 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "PointerLockManager.h" - -#include "mozilla/AsyncEventDispatcher.h" -#include "mozilla/EventStateManager.h" -#include "mozilla/PresShell.h" -#include "mozilla/StaticPrefs_full_screen_api.h" -#include "mozilla/dom/BindingDeclarations.h" -#include "mozilla/dom/BrowserChild.h" -#include "mozilla/dom/BrowserParent.h" -#include "mozilla/dom/BrowsingContext.h" -#include "mozilla/dom/Document.h" -#include "mozilla/dom/Element.h" -#include "mozilla/dom/WindowContext.h" -#include "nsCOMPtr.h" -#include "nsSandboxFlags.h" - -namespace mozilla { - -using mozilla::dom::BrowserChild; -using mozilla::dom::BrowserParent; -using mozilla::dom::BrowsingContext; -using mozilla::dom::CallerType; -using mozilla::dom::Document; -using mozilla::dom::Element; -using mozilla::dom::WindowContext; - -// Reference to the pointer locked element. -static nsWeakPtr sLockedElement; - -// Reference to the document which requested pointer lock. -static nsWeakPtr sLockedDoc; - -// Reference to the BrowserParent requested pointer lock. -static BrowserParent* sLockedRemoteTarget = nullptr; - -/* static */ -bool PointerLockManager::sIsLocked = false; - -/* static */ -already_AddRefed PointerLockManager::GetLockedElement() { - nsCOMPtr element = do_QueryReferent(sLockedElement); - return element.forget(); -} - -/* static */ -already_AddRefed PointerLockManager::GetLockedDocument() { - nsCOMPtr document = do_QueryReferent(sLockedDoc); - return document.forget(); -} - -/* static */ -BrowserParent* PointerLockManager::GetLockedRemoteTarget() { - MOZ_ASSERT(XRE_IsParentProcess()); - return sLockedRemoteTarget; -} - -static void DispatchPointerLockChange(Document* aTarget) { - if (!aTarget) { - return; - } - - RefPtr asyncDispatcher = - new AsyncEventDispatcher(aTarget, u"pointerlockchange"_ns, - CanBubble::eYes, ChromeOnlyDispatch::eNo); - asyncDispatcher->PostDOMEvent(); -} - -static void DispatchPointerLockError(Document* aTarget, const char* aMessage) { - if (!aTarget) { - return; - } - - RefPtr asyncDispatcher = - new AsyncEventDispatcher(aTarget, u"pointerlockerror"_ns, CanBubble::eYes, - ChromeOnlyDispatch::eNo); - asyncDispatcher->PostDOMEvent(); - nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "DOM"_ns, - aTarget, nsContentUtils::eDOM_PROPERTIES, - aMessage); -} - -static const char* GetPointerLockError(Element* aElement, Element* aCurrentLock, - bool aNoFocusCheck = false) { - // Check if pointer lock pref is enabled - if (!StaticPrefs::full_screen_api_pointer_lock_enabled()) { - return "PointerLockDeniedDisabled"; - } - - nsCOMPtr ownerDoc = aElement->OwnerDoc(); - if (aCurrentLock && aCurrentLock->OwnerDoc() != ownerDoc) { - return "PointerLockDeniedInUse"; - } - - if (!aElement->IsInComposedDoc()) { - return "PointerLockDeniedNotInDocument"; - } - - if (ownerDoc->GetSandboxFlags() & SANDBOXED_POINTER_LOCK) { - return "PointerLockDeniedSandboxed"; - } - - // Check if the element is in a document with a docshell. - if (!ownerDoc->GetContainer()) { - return "PointerLockDeniedHidden"; - } - nsCOMPtr ownerWindow = ownerDoc->GetWindow(); - if (!ownerWindow) { - return "PointerLockDeniedHidden"; - } - nsCOMPtr ownerInnerWindow = ownerDoc->GetInnerWindow(); - if (!ownerInnerWindow) { - return "PointerLockDeniedHidden"; - } - if (ownerWindow->GetCurrentInnerWindow() != ownerInnerWindow) { - return "PointerLockDeniedHidden"; - } - - BrowsingContext* bc = ownerDoc->GetBrowsingContext(); - BrowsingContext* topBC = bc ? bc->Top() : nullptr; - WindowContext* topWC = ownerDoc->GetTopLevelWindowContext(); - if (!topBC || !topBC->IsActive() || !topWC || - topWC != topBC->GetCurrentWindowContext()) { - return "PointerLockDeniedHidden"; - } - - if (!aNoFocusCheck) { - if (!IsInActiveTab(ownerDoc)) { - return "PointerLockDeniedNotFocused"; - } - } - - return nullptr; -} - -/* static */ -void PointerLockManager::RequestLock(Element* aElement, - CallerType aCallerType) { - NS_ASSERTION(aElement, - "Must pass non-null element to PointerLockManager::RequestLock"); - - RefPtr doc = aElement->OwnerDoc(); - nsCOMPtr pointerLockedElement = GetLockedElement(); - if (aElement == pointerLockedElement) { - DispatchPointerLockChange(doc); - return; - } - - if (const char* msg = GetPointerLockError(aElement, pointerLockedElement)) { - DispatchPointerLockError(doc, msg); - return; - } - - bool userInputOrSystemCaller = - doc->HasValidTransientUserGestureActivation() || - aCallerType == CallerType::System; - nsCOMPtr request = - new PointerLockRequest(aElement, userInputOrSystemCaller); - doc->Dispatch(TaskCategory::Other, request.forget()); -} - -/* static */ -void PointerLockManager::Unlock(Document* aDoc) { - if (!sIsLocked) { - return; - } - - nsCOMPtr pointerLockedDoc = GetLockedDocument(); - if (!pointerLockedDoc || (aDoc && aDoc != pointerLockedDoc)) { - return; - } - if (!SetPointerLock(nullptr, pointerLockedDoc, StyleCursorKind::Auto)) { - return; - } - - nsCOMPtr pointerLockedElement = GetLockedElement(); - ChangePointerLockedElement(nullptr, pointerLockedDoc, pointerLockedElement); - - if (BrowserChild* browserChild = - BrowserChild::GetFrom(pointerLockedDoc->GetDocShell())) { - browserChild->SendReleasePointerLock(); - } - - RefPtr asyncDispatcher = new AsyncEventDispatcher( - pointerLockedElement, u"MozDOMPointerLock:Exited"_ns, CanBubble::eYes, - ChromeOnlyDispatch::eYes); - asyncDispatcher->RunDOMEventWhenSafe(); -} - -/* static */ -void PointerLockManager::ChangePointerLockedElement( - Element* aElement, Document* aDocument, Element* aPointerLockedElement) { - // aDocument here is not really necessary, as it is the uncomposed - // document of both aElement and aPointerLockedElement as far as one - // is not nullptr, and they wouldn't both be nullptr in any case. - // But since the caller of this function should have known what the - // document is, we just don't try to figure out what it should be. - MOZ_ASSERT(aDocument); - MOZ_ASSERT(aElement != aPointerLockedElement); - if (aPointerLockedElement) { - MOZ_ASSERT(aPointerLockedElement->GetComposedDoc() == aDocument); - aPointerLockedElement->ClearPointerLock(); - } - if (aElement) { - MOZ_ASSERT(aElement->GetComposedDoc() == aDocument); - aElement->SetPointerLock(); - sLockedElement = do_GetWeakReference(aElement); - sLockedDoc = do_GetWeakReference(aDocument); - NS_ASSERTION(sLockedElement && sLockedDoc, - "aElement and this should support weak references!"); - } else { - sLockedElement = nullptr; - sLockedDoc = nullptr; - } - // Retarget all events to aElement via capture or - // stop retargeting if aElement is nullptr. - PresShell::SetCapturingContent(aElement, CaptureFlags::PointerLock); - DispatchPointerLockChange(aDocument); -} - -/* static */ -bool PointerLockManager::StartSetPointerLock(Element* aElement, - Document* aDocument) { - if (!SetPointerLock(aElement, aDocument, StyleCursorKind::None)) { - DispatchPointerLockError(aDocument, "PointerLockDeniedFailedToLock"); - return false; - } - - ChangePointerLockedElement(aElement, aDocument, nullptr); - nsContentUtils::DispatchEventOnlyToChrome( - aDocument, ToSupports(aElement), u"MozDOMPointerLock:Entered"_ns, - CanBubble::eYes, Cancelable::eNo, /* DefaultAction */ nullptr); - - return true; -} - -/* static */ -bool PointerLockManager::SetPointerLock(Element* aElement, Document* aDocument, - StyleCursorKind aCursorStyle) { - MOZ_ASSERT(!aElement || aElement->OwnerDoc() == aDocument, - "We should be either unlocking pointer (aElement is nullptr), " - "or locking pointer to an element in this document"); -#ifdef DEBUG - if (!aElement) { - nsCOMPtr pointerLockedDoc = GetLockedDocument(); - MOZ_ASSERT(pointerLockedDoc == aDocument); - } -#endif - - PresShell* presShell = aDocument->GetPresShell(); - if (!presShell) { - NS_WARNING("SetPointerLock(): No PresShell"); - if (!aElement) { - sIsLocked = false; - // If we are unlocking pointer lock, but for some reason the doc - // has already detached from the presshell, just ask the event - // state manager to release the pointer. - EventStateManager::SetPointerLock(nullptr, nullptr); - return true; - } - return false; - } - nsPresContext* presContext = presShell->GetPresContext(); - if (!presContext) { - NS_WARNING("SetPointerLock(): Unable to get PresContext"); - return false; - } - - nsCOMPtr widget; - nsIFrame* rootFrame = presShell->GetRootFrame(); - if (!NS_WARN_IF(!rootFrame)) { - widget = rootFrame->GetNearestWidget(); - NS_WARNING_ASSERTION(widget, - "SetPointerLock(): Unable to find widget in " - "presShell->GetRootFrame()->GetNearestWidget();"); - if (aElement && !widget) { - return false; - } - } - - sIsLocked = !!aElement; - - // Hide the cursor and set pointer lock for future mouse events - RefPtr esm = presContext->EventStateManager(); - esm->SetCursor(aCursorStyle, nullptr, Nothing(), widget, true); - EventStateManager::SetPointerLock(widget, aElement); - - return true; -} - -/* static */ -bool PointerLockManager::IsInLockContext(BrowsingContext* aContext) { - if (!aContext) { - return false; - } - - nsCOMPtr pointerLockedDoc = GetLockedDocument(); - if (!pointerLockedDoc || !pointerLockedDoc->GetBrowsingContext()) { - return false; - } - - BrowsingContext* lockTop = pointerLockedDoc->GetBrowsingContext()->Top(); - BrowsingContext* top = aContext->Top(); - - return top == lockTop; -} - -/* static */ -bool PointerLockManager::SetLockedRemoteTarget(BrowserParent* aBrowserParent) { - MOZ_ASSERT(XRE_IsParentProcess()); - if (sLockedRemoteTarget) { - return sLockedRemoteTarget == aBrowserParent; - } - - sLockedRemoteTarget = aBrowserParent; - return true; -} - -/* static */ -void PointerLockManager::ReleaseLockedRemoteTarget( - BrowserParent* aBrowserParent) { - MOZ_ASSERT(XRE_IsParentProcess()); - if (sLockedRemoteTarget == aBrowserParent) { - sLockedRemoteTarget = nullptr; - } -} - -PointerLockManager::PointerLockRequest::PointerLockRequest( - Element* aElement, bool aUserInputOrChromeCaller) - : mozilla::Runnable("PointerLockRequest"), - mElement(do_GetWeakReference(aElement)), - mDocument(do_GetWeakReference(aElement->OwnerDoc())), - mUserInputOrChromeCaller(aUserInputOrChromeCaller) {} - -NS_IMETHODIMP -PointerLockManager::PointerLockRequest::Run() { - nsCOMPtr element = do_QueryReferent(mElement); - nsCOMPtr document = do_QueryReferent(mDocument); - - const char* error = nullptr; - if (!element || !document || !element->GetComposedDoc()) { - error = "PointerLockDeniedNotInDocument"; - } else if (element->GetComposedDoc() != document) { - error = "PointerLockDeniedMovedDocument"; - } - if (!error) { - nsCOMPtr pointerLockedElement = do_QueryReferent(sLockedElement); - if (element == pointerLockedElement) { - DispatchPointerLockChange(document); - return NS_OK; - } - // Note, we must bypass focus change, so pass true as the last parameter! - error = GetPointerLockError(element, pointerLockedElement, true); - // Another element in the same document is requesting pointer lock, - // just grant it without user input check. - if (!error && pointerLockedElement) { - ChangePointerLockedElement(element, document, pointerLockedElement); - return NS_OK; - } - } - // If it is neither user input initiated, nor requested in fullscreen, - // it should be rejected. - if (!error && !mUserInputOrChromeCaller && - !document->GetUnretargetedFullScreenElement()) { - error = "PointerLockDeniedNotInputDriven"; - } - - if (error) { - DispatchPointerLockError(document, error); - return NS_OK; - } - - if (BrowserChild* browserChild = - BrowserChild::GetFrom(document->GetDocShell())) { - nsWeakPtr e = do_GetWeakReference(element); - nsWeakPtr doc = do_GetWeakReference(element->OwnerDoc()); - nsWeakPtr bc = do_GetWeakReference(browserChild); - browserChild->SendRequestPointerLock( - [e, doc, bc](const nsCString& aError) { - nsCOMPtr document = do_QueryReferent(doc); - if (!aError.IsEmpty()) { - DispatchPointerLockError(document, aError.get()); - return; - } - - const char* error = nullptr; - auto autoCleanup = MakeScopeExit([&] { - if (error) { - DispatchPointerLockError(document, error); - // If we are failed to set pointer lock, notify parent to stop - // redirect mouse event to this process. - if (nsCOMPtr browserChild = - do_QueryReferent(bc)) { - static_cast(browserChild.get()) - ->SendReleasePointerLock(); - } - } - }); - - nsCOMPtr element = do_QueryReferent(e); - if (!element || !document || !element->GetComposedDoc()) { - error = "PointerLockDeniedNotInDocument"; - return; - } - - if (element->GetComposedDoc() != document) { - error = "PointerLockDeniedMovedDocument"; - return; - } - - nsCOMPtr pointerLockedElement = GetLockedElement(); - error = GetPointerLockError(element, pointerLockedElement, true); - if (error) { - return; - } - - if (!StartSetPointerLock(element, document)) { - error = "PointerLockDeniedFailedToLock"; - return; - } - }, - [doc](mozilla::ipc::ResponseRejectReason) { - // IPC layer error - nsCOMPtr document = do_QueryReferent(doc); - if (!document) { - return; - } - - DispatchPointerLockError(document, "PointerLockDeniedFailedToLock"); - }); - } else { - StartSetPointerLock(element, document); - } - - return NS_OK; -} - -} // namespace mozilla diff --git a/dom/base/PointerLockManager.h b/dom/base/PointerLockManager.h deleted file mode 100644 index 0b229bd42941..000000000000 --- a/dom/base/PointerLockManager.h +++ /dev/null @@ -1,82 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_PointerLockManager_h -#define mozilla_PointerLockManager_h - -#include "mozilla/AlreadyAddRefed.h" -#include "nsIWeakReferenceUtils.h" -#include "nsThreadUtils.h" - -namespace mozilla { -enum class StyleCursorKind : uint8_t; - -namespace dom { -class BrowsingContext; -class BrowserParent; -enum class CallerType : uint32_t; -class Document; -class Element; -} // namespace dom - -class PointerLockManager final { - public: - static void RequestLock(dom::Element* aElement, dom::CallerType aCallerType); - - MOZ_CAN_RUN_SCRIPT_BOUNDARY - static void Unlock(dom::Document* aDoc = nullptr); - - static bool IsLocked() { return sIsLocked; } - - static already_AddRefed GetLockedElement(); - - static already_AddRefed GetLockedDocument(); - - static dom::BrowserParent* GetLockedRemoteTarget(); - - /** - * Returns true if aContext and the current pointer locked document - * have common top BrowsingContext. - * Note that this method returns true only if caller is in the same process - * as pointer locked document. - */ - static bool IsInLockContext(mozilla::dom::BrowsingContext* aContext); - - // Set/release pointer lock remote target. Should only be called in parent - // process. - static bool SetLockedRemoteTarget(dom::BrowserParent* aBrowserParent); - static void ReleaseLockedRemoteTarget(dom::BrowserParent* aBrowserParent); - - private: - class PointerLockRequest final : public Runnable { - public: - PointerLockRequest(dom::Element* aElement, bool aUserInputOrChromeCaller); - MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD Run() final; - - private: - nsWeakPtr mElement; - nsWeakPtr mDocument; - bool mUserInputOrChromeCaller; - }; - - static void ChangePointerLockedElement(dom::Element* aElement, - dom::Document* aDocument, - dom::Element* aPointerLockedElement); - - MOZ_CAN_RUN_SCRIPT_BOUNDARY - static bool StartSetPointerLock(dom::Element* aElement, - dom::Document* aDocument); - - MOZ_CAN_RUN_SCRIPT - static bool SetPointerLock(dom::Element* aElement, dom::Document* aDocument, - StyleCursorKind); - - static bool sIsLocked; -}; - -} // namespace mozilla - -#endif // mozilla_PointerLockManager_h diff --git a/dom/base/moz.build b/dom/base/moz.build index b486bfdf7d84..6d6605ea4d8f 100644 --- a/dom/base/moz.build +++ b/dom/base/moz.build @@ -124,7 +124,6 @@ EXPORTS.mozilla += [ "FlushType.h", "FullscreenChange.h", "IdentifierMapEntry.h", - "PointerLockManager.h", "RangeBoundary.h", "RangeUtils.h", "ScriptableContentIterator.h", @@ -393,7 +392,6 @@ UNIFIED_SOURCES += [ "nsWindowRoot.cpp", "nsWrapperCache.cpp", "ParentProcessMessageManager.cpp", - "PointerLockManager.cpp", "PopupBlocker.cpp", "Pose.cpp", "PostMessageEvent.cpp", diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index a7a8e39fa673..5c10fe3e1f05 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -6873,6 +6873,24 @@ Document* nsContentUtils::GetRootDocument(Document* aDoc) { return doc; } +/* static */ +bool nsContentUtils::IsInPointerLockContext(BrowsingContext* aContext) { + if (!aContext) { + return false; + } + + nsCOMPtr pointerLockedDoc = + do_QueryReferent(EventStateManager::sPointerLockedDoc); + if (!pointerLockedDoc || !pointerLockedDoc->GetBrowsingContext()) { + return false; + } + + BrowsingContext* lockTop = pointerLockedDoc->GetBrowsingContext()->Top(); + BrowsingContext* top = aContext->Top(); + + return top == lockTop; +} + // static int32_t nsContentUtils::GetAdjustedOffsetInTextControl(nsIFrame* aOffsetFrame, int32_t aOffset) { diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h index dd24a21f4d23..dfa1cb090bbc 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -2445,6 +2445,14 @@ class nsContentUtils { */ static Document* GetRootDocument(Document* aDoc); + /** + * Returns true if aContext and the current pointer lock document + * have common top BrowsingContext. + * Note that this method returns true only if caller is in the same process + * as pointer lock document. + */ + static bool IsInPointerLockContext(mozilla::dom::BrowsingContext* aContext); + static void GetShiftText(nsAString& text); static void GetControlText(nsAString& text); static void GetMetaText(nsAString& text); diff --git a/dom/base/nsFocusManager.cpp b/dom/base/nsFocusManager.cpp index 7f8ac4078e12..a69755a0324b 100644 --- a/dom/base/nsFocusManager.cpp +++ b/dom/base/nsFocusManager.cpp @@ -61,7 +61,6 @@ #include "mozilla/HTMLEditor.h" #include "mozilla/IMEStateManager.h" #include "mozilla/LookAndFeel.h" -#include "mozilla/PointerLockManager.h" #include "mozilla/Preferences.h" #include "mozilla/PresShell.h" #include "mozilla/Services.h" @@ -4648,8 +4647,11 @@ void nsFocusManager::GetFocusInSelection(nsPIDOMWindowOuter* aWindow, } static void MaybeUnlockPointer(BrowsingContext* aCurrentFocusedContext) { - if (PointerLockManager::IsInLockContext(aCurrentFocusedContext)) { - PointerLockManager::Unlock(); + nsCOMPtr pointerLockedDoc = + do_QueryReferent(EventStateManager::sPointerLockedDoc); + if (pointerLockedDoc && + !nsContentUtils::IsInPointerLockContext(aCurrentFocusedContext)) { + Document::UnlockPointer(); } } @@ -4879,8 +4881,8 @@ uint64_t nsFocusManager::GenerateFocusActionId() { } static bool IsInPointerLockContext(nsPIDOMWindowOuter* aWin) { - return PointerLockManager::IsInLockContext(aWin ? aWin->GetBrowsingContext() - : nullptr); + return nsContentUtils::IsInPointerLockContext( + aWin ? aWin->GetBrowsingContext() : nullptr); } void nsFocusManager::SetFocusedWindowInternal(nsPIDOMWindowOuter* aWindow) { diff --git a/dom/events/Event.cpp b/dom/events/Event.cpp index 183c7e0c6094..bbd8efb2ab6f 100644 --- a/dom/events/Event.cpp +++ b/dom/events/Event.cpp @@ -17,7 +17,6 @@ #include "mozilla/dom/WorkerPrivate.h" #include "mozilla/MiscEvents.h" #include "mozilla/MouseEvents.h" -#include "mozilla/PointerLockManager.h" #include "mozilla/Preferences.h" #include "mozilla/PresShell.h" #include "mozilla/TextEvents.h" @@ -518,7 +517,7 @@ WidgetEvent* Event::WidgetEventPtr() { return mEvent; } CSSIntPoint Event::GetScreenCoords(nsPresContext* aPresContext, WidgetEvent* aEvent, LayoutDeviceIntPoint aPoint) { - if (PointerLockManager::IsLocked()) { + if (EventStateManager::sIsPointerLocked) { return EventStateManager::sLastScreenPoint; } @@ -588,7 +587,7 @@ CSSIntPoint Event::GetClientCoords(nsPresContext* aPresContext, WidgetEvent* aEvent, LayoutDeviceIntPoint aPoint, CSSIntPoint aDefaultPoint) { - if (PointerLockManager::IsLocked()) { + if (EventStateManager::sIsPointerLocked) { return EventStateManager::sLastClientPoint; } diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp index fa34ef55fe55..0c2e72b2dca5 100644 --- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -15,7 +15,6 @@ #include "mozilla/MiscEvents.h" #include "mozilla/MathAlgorithms.h" #include "mozilla/MouseEvents.h" -#include "mozilla/PointerLockManager.h" #include "mozilla/PresShell.h" #include "mozilla/ScopeExit.h" #include "mozilla/ScrollTypes.h" @@ -224,6 +223,11 @@ LayoutDeviceIntPoint EventStateManager::sLastRefPoint = kInvalidRefPoint; CSSIntPoint EventStateManager::sLastScreenPoint = CSSIntPoint(0, 0); LayoutDeviceIntPoint EventStateManager::sSynthCenteringPoint = kInvalidRefPoint; CSSIntPoint EventStateManager::sLastClientPoint = CSSIntPoint(0, 0); +bool EventStateManager::sIsPointerLocked = false; +// Reference to the pointer locked element. +nsWeakPtr EventStateManager::sPointerLockedElement; +// Reference to the document which requested pointer lock. +nsWeakPtr EventStateManager::sPointerLockedDoc; nsCOMPtr EventStateManager::sDragOverContent = nullptr; EventStateManager::WheelPrefs* EventStateManager::WheelPrefs::sInstance = @@ -530,10 +534,11 @@ nsresult EventStateManager::PreHandleEvent(nsPresContext* aPresContext, return NS_ERROR_NULL_POINTER; } #ifdef DEBUG - if (aEvent->HasDragEventMessage() && PointerLockManager::IsLocked()) { - NS_ASSERTION(PointerLockManager::IsLocked(), - "Pointer is locked. Drag events should be suppressed when " - "the pointer is locked."); + if (aEvent->HasDragEventMessage() && sIsPointerLocked) { + NS_ASSERTION( + sIsPointerLocked, + "sIsPointerLocked is true. Drag events should be suppressed when " + "the pointer is locked."); } #endif // Store last known screenPoint and clientPoint so pointer lock @@ -541,7 +546,7 @@ nsresult EventStateManager::PreHandleEvent(nsPresContext* aPresContext, if (aEvent->IsTrusted() && ((mouseEvent && mouseEvent->IsReal()) || aEvent->mClass == eWheelEventClass) && - !PointerLockManager::IsLocked()) { + !sIsPointerLocked) { sLastScreenPoint = Event::GetScreenCoords(aPresContext, aEvent, aEvent->mRefPoint); sLastClientPoint = Event::GetClientCoords( @@ -567,7 +572,7 @@ nsresult EventStateManager::PreHandleEvent(nsPresContext* aPresContext, switch (aEvent->mMessage) { case eContextMenu: - if (PointerLockManager::IsLocked()) { + if (sIsPointerLocked) { return NS_ERROR_DOM_INVALID_STATE_ERR; } break; @@ -693,7 +698,7 @@ nsresult EventStateManager::PreHandleEvent(nsPresContext* aPresContext, UpdateCursor(aPresContext, aEvent, mCurrentTarget, aStatus); UpdateLastRefPointOfMouseEvent(mouseEvent); - if (PointerLockManager::IsLocked()) { + if (sIsPointerLocked) { ResetPointerToWindowCenterWhilePointerLocked(mouseEvent); } UpdateLastPointerPosition(mouseEvent); @@ -1378,7 +1383,7 @@ void EventStateManager::DispatchCrossProcessEvent(WidgetEvent* aEvent, } if (BrowserParent* pointerLockedRemote = - PointerLockManager::GetLockedRemoteTarget()) { + BrowserParent::GetPointerLockedRemoteTarget()) { remote = pointerLockedRemote; } else if (BrowserParent* pointerCapturedRemote = PointerEventHandler::GetPointerCapturingRemoteTarget( @@ -1572,8 +1577,7 @@ void EventStateManager::CreateClickHoldTimer(nsPresContext* inPresContext, nsIFrame* inDownFrame, WidgetGUIEvent* inMouseDownEvent) { if (!inMouseDownEvent->IsTrusted() || - IsTopLevelRemoteTarget(mGestureDownContent) || - PointerLockManager::IsLocked()) { + IsTopLevelRemoteTarget(mGestureDownContent) || sIsPointerLocked) { return; } @@ -1640,7 +1644,7 @@ void EventStateManager::sClickHoldCallback(nsITimer* aTimer, void* aESM) { // length of time, which is _not_ what we want. // void EventStateManager::FireContextClick() { - if (!mGestureDownContent || !mPresContext || PointerLockManager::IsLocked()) { + if (!mGestureDownContent || !mPresContext || sIsPointerLocked) { return; } @@ -4247,12 +4251,11 @@ nsIFrame* EventStateManager::DispatchMouseOrPointerEvent( // "[When the mouse is locked on an element...e]vents that require the concept // of a mouse cursor must not be dispatched (for example: mouseover, // mouseout). - if (PointerLockManager::IsLocked() && - (aMessage == eMouseLeave || aMessage == eMouseEnter || - aMessage == eMouseOver || aMessage == eMouseOut)) { + if (sIsPointerLocked && (aMessage == eMouseLeave || aMessage == eMouseEnter || + aMessage == eMouseOver || aMessage == eMouseOut)) { mCurrentTargetContent = nullptr; nsCOMPtr pointerLockedElement = - PointerLockManager::GetLockedElement(); + do_QueryReferent(EventStateManager::sPointerLockedElement); if (!pointerLockedElement) { NS_WARNING("Should have pointer locked element, but didn't."); return nullptr; @@ -4540,7 +4543,7 @@ void EventStateManager::UpdateLastRefPointOfMouseEvent( // Mouse movement is reported on the MouseEvent.movement{X,Y} fields. // Movement is calculated in UIEvent::GetMovementPoint() as: // previous_mousemove_mRefPoint - current_mousemove_mRefPoint. - if (PointerLockManager::IsLocked() && aMouseEvent->mWidget) { + if (sIsPointerLocked && aMouseEvent->mWidget) { // The pointer is locked. If the pointer is not located at the center of // the window, dispatch a synthetic mousemove to return the pointer there. // Doing this between "real" pointer moves gives the impression that the @@ -4564,7 +4567,7 @@ void EventStateManager::UpdateLastRefPointOfMouseEvent( /* static */ void EventStateManager::ResetPointerToWindowCenterWhilePointerLocked( WidgetMouseEvent* aMouseEvent) { - MOZ_ASSERT(PointerLockManager::IsLocked()); + MOZ_ASSERT(sIsPointerLocked); if ((aMouseEvent->mMessage != eMouseMove && aMouseEvent->mMessage != ePointerMove) || !aMouseEvent->mWidget) { @@ -4702,6 +4705,9 @@ OverOutElementsWrapper* EventStateManager::GetWrapperByEventID( /* static */ void EventStateManager::SetPointerLock(nsIWidget* aWidget, nsIContent* aElement) { + // NOTE: aElement will be nullptr when unlocking. + sIsPointerLocked = !!aElement; + // Reset mouse wheel transaction WheelTransaction::EndTransaction(); @@ -4709,7 +4715,7 @@ void EventStateManager::SetPointerLock(nsIWidget* aWidget, nsCOMPtr dragService = do_GetService("@mozilla.org/widget/dragservice;1"); - if (PointerLockManager::IsLocked()) { + if (sIsPointerLocked) { MOZ_ASSERT(aWidget, "Locking pointer requires a widget"); // Release all pointer capture when a pointer lock is successfully applied diff --git a/dom/events/EventStateManager.h b/dom/events/EventStateManager.h index 9f4b6422c8eb..ca303285b85d 100644 --- a/dom/events/EventStateManager.h +++ b/dom/events/EventStateManager.h @@ -317,6 +317,10 @@ class EventStateManager : public nsSupportsWeakReference, public nsIObserver { // frozen at the last mouse position while the pointer is locked. static CSSIntPoint sLastClientPoint; + static bool sIsPointerLocked; + static nsWeakPtr sPointerLockedElement; + static nsWeakPtr sPointerLockedDoc; + /** * If the absolute values of mMultiplierX and/or mMultiplierY are equal or * larger than this value, the computed scroll amount isn't rounded down to diff --git a/dom/events/PointerEventHandler.cpp b/dom/events/PointerEventHandler.cpp index 61e678864bb0..e194f5bf58ae 100644 --- a/dom/events/PointerEventHandler.cpp +++ b/dom/events/PointerEventHandler.cpp @@ -7,7 +7,6 @@ #include "PointerEventHandler.h" #include "nsIFrame.h" #include "PointerEvent.h" -#include "PointerLockManager.h" #include "mozilla/PresShell.h" #include "mozilla/StaticPrefs_dom.h" #include "mozilla/dom/BrowserChild.h" @@ -196,7 +195,7 @@ bool PointerEventHandler::SetPointerCaptureRemoteTarget( MOZ_ASSERT(sPointerCaptureRemoteTargetTable); MOZ_ASSERT(aBrowserParent); - if (PointerLockManager::GetLockedRemoteTarget()) { + if (BrowserParent::GetPointerLockedRemoteTarget()) { return false; } diff --git a/dom/events/UIEvent.cpp b/dom/events/UIEvent.cpp index e75cab88122c..ba85aff8f8a3 100644 --- a/dom/events/UIEvent.cpp +++ b/dom/events/UIEvent.cpp @@ -11,7 +11,6 @@ #include "mozilla/Assertions.h" #include "mozilla/ContentEvents.h" #include "mozilla/EventStateManager.h" -#include "mozilla/PointerLockManager.h" #include "mozilla/PresShell.h" #include "mozilla/TextEvents.h" #include "nsCOMPtr.h" @@ -33,7 +32,7 @@ UIEvent::UIEvent(EventTarget* aOwner, nsPresContext* aPresContext, mLayerPoint(0, 0), mPagePoint(0, 0), mMovementPoint(0, 0), - mIsPointerLocked(PointerLockManager::IsLocked()), + mIsPointerLocked(EventStateManager::sIsPointerLocked), mLastClientPoint(EventStateManager::sLastClientPoint) { if (aEvent) { mEventIsInternal = false; diff --git a/dom/ipc/BrowserParent.cpp b/dom/ipc/BrowserParent.cpp index 7b4917a71cc0..5ede68022d04 100644 --- a/dom/ipc/BrowserParent.cpp +++ b/dom/ipc/BrowserParent.cpp @@ -170,6 +170,8 @@ BrowserParent* BrowserParent::sFocus = nullptr; BrowserParent* BrowserParent::sTopLevelWebFocus = nullptr; /* static */ BrowserParent* BrowserParent::sLastMouseRemoteTarget = nullptr; +/* static */ +BrowserParent* BrowserParent::sPointerLockedRemoteTarget = nullptr; // The flags passed by the webProgress notifications are 16 bits shifted // from the ones registered by webProgressListeners. @@ -252,6 +254,11 @@ BrowserParent* BrowserParent::GetLastMouseRemoteTarget() { return sLastMouseRemoteTarget; } +/* static */ +BrowserParent* BrowserParent::GetPointerLockedRemoteTarget() { + return sPointerLockedRemoteTarget; +} + /*static*/ BrowserParent* BrowserParent::GetFrom(nsFrameLoader* aFrameLoader) { if (!aFrameLoader) { @@ -601,7 +608,7 @@ void BrowserParent::RemoveWindowListeners() { void BrowserParent::DestroyInternal() { UnsetTopLevelWebFocus(this); UnsetLastMouseRemoteTarget(this); - PointerLockManager::ReleaseLockedRemoteTarget(this); + UnsetPointerLockedRemoteTarget(this); PointerEventHandler::ReleasePointerCaptureRemoteTarget(this); PresShell::ReleaseCapturingRemoteTarget(this); @@ -685,7 +692,7 @@ void BrowserParent::ActorDestroy(ActorDestroyReason why) { // case of a crash. BrowserParent::UnsetTopLevelWebFocus(this); BrowserParent::UnsetLastMouseRemoteTarget(this); - PointerLockManager::ReleaseLockedRemoteTarget(this); + BrowserParent::UnsetPointerLockedRemoteTarget(this); PointerEventHandler::ReleasePointerCaptureRemoteTarget(this); PresShell::ReleaseCapturingRemoteTarget(this); @@ -3098,6 +3105,14 @@ void BrowserParent::UnsetLastMouseRemoteTarget(BrowserParent* aBrowserParent) { } } +/* static */ +void BrowserParent::UnsetPointerLockedRemoteTarget( + BrowserParent* aBrowserParent) { + if (sPointerLockedRemoteTarget == aBrowserParent) { + sPointerLockedRemoteTarget = nullptr; + } +} + mozilla::ipc::IPCResult BrowserParent::RecvRequestIMEToCommitComposition( const bool& aCancel, bool* aIsCommitted, nsString* aCommittedString) { nsCOMPtr widget = GetTextInputHandlingWidget(); @@ -4067,10 +4082,19 @@ mozilla::ipc::IPCResult BrowserParent::RecvIsWindowSupportingWebVR( return IPC_OK(); } +bool BrowserParent::SetPointerLock() { + if (sPointerLockedRemoteTarget) { + return sPointerLockedRemoteTarget == this; + } + + sPointerLockedRemoteTarget = this; + return true; +} + mozilla::ipc::IPCResult BrowserParent::RecvRequestPointerLock( RequestPointerLockResolver&& aResolve) { nsCString error; - if (!PointerLockManager::SetLockedRemoteTarget(this)) { + if (!SetPointerLock()) { error = "PointerLockDeniedInUse"; } else { PointerEventHandler::ReleaseAllPointerCaptureRemoteTarget(); @@ -4080,9 +4104,8 @@ mozilla::ipc::IPCResult BrowserParent::RecvRequestPointerLock( } mozilla::ipc::IPCResult BrowserParent::RecvReleasePointerLock() { - MOZ_ASSERT_IF(PointerLockManager::GetLockedRemoteTarget(), - PointerLockManager::GetLockedRemoteTarget() == this); - PointerLockManager::ReleaseLockedRemoteTarget(this); + MOZ_ASSERT_IF(sPointerLockedRemoteTarget, sPointerLockedRemoteTarget == this); + UnsetPointerLockedRemoteTarget(this); return IPC_OK(); } diff --git a/dom/ipc/BrowserParent.h b/dom/ipc/BrowserParent.h index b3c1078baa23..db4ace2dc5fc 100644 --- a/dom/ipc/BrowserParent.h +++ b/dom/ipc/BrowserParent.h @@ -117,6 +117,8 @@ class BrowserParent final : public PBrowserParent, static BrowserParent* GetLastMouseRemoteTarget(); + static BrowserParent* GetPointerLockedRemoteTarget(); + static BrowserParent* GetFrom(nsFrameLoader* aFrameLoader); static BrowserParent* GetFrom(PBrowserParent* aBrowserParent); @@ -758,6 +760,7 @@ class BrowserParent final : public PBrowserParent, mozilla::ipc::IPCResult RecvMaybeFireEmbedderLoadEvents( EmbedderElementEventType aFireEventAtEmbeddingElement); + bool SetPointerLock(); mozilla::ipc::IPCResult RecvRequestPointerLock( RequestPointerLockResolver&& aResolve); mozilla::ipc::IPCResult RecvReleasePointerLock(); @@ -837,6 +840,13 @@ class BrowserParent final : public PBrowserParent, // current sLastMouseRemoteTarget. static void UnsetLastMouseRemoteTarget(BrowserParent* aBrowserParent); + // Keeps track of which BrowserParent requested pointer lock. + static BrowserParent* sPointerLockedRemoteTarget; + + // Unsetter for sPointerLockedRemoteTarget; only unsets if argument matches + // current sPointerLockedRemoteTarget. + static void UnsetPointerLockedRemoteTarget(BrowserParent* aBrowserParent); + struct APZData { bool operator==(const APZData& aOther) { return aOther.guid == guid && aOther.blockId == blockId && diff --git a/dom/tests/mochitest/pointerlock/file_pointerlock_xorigin_iframe_no_user_gesture.html b/dom/tests/mochitest/pointerlock/file_pointerlock_xorigin_iframe_no_user_gesture.html deleted file mode 100644 index 68977ffadac6..000000000000 --- a/dom/tests/mochitest/pointerlock/file_pointerlock_xorigin_iframe_no_user_gesture.html +++ /dev/null @@ -1,95 +0,0 @@ - - - - -Bug 1672330 - - - - - - - - - -Mozilla Bug 1672330 -
- - -
-
-
- - diff --git a/dom/tests/mochitest/pointerlock/mochitest.ini b/dom/tests/mochitest/pointerlock/mochitest.ini index c8de78b90ec3..5fcb5bae3bed 100644 --- a/dom/tests/mochitest/pointerlock/mochitest.ini +++ b/dom/tests/mochitest/pointerlock/mochitest.ini @@ -2,7 +2,6 @@ support-files = pointerlock_utils.js iframe_differentDOM.html - !/gfx/layers/apz/test/mochitest/apz_test_utils.js [test_closewindow-with-pointerlock.html] [test_pointerlock_target_not_in_active_document.html] @@ -33,4 +32,3 @@ support-files = [test_pointerlock_xorigin_iframe.html] support-files = file_pointerlock_xorigin_iframe.html - file_pointerlock_xorigin_iframe_no_user_gesture.html diff --git a/dom/tests/mochitest/pointerlock/test_pointerlock_xorigin_iframe.html b/dom/tests/mochitest/pointerlock/test_pointerlock_xorigin_iframe.html index 1f30e9d64e0e..36f9ae7d3554 100644 --- a/dom/tests/mochitest/pointerlock/test_pointerlock_xorigin_iframe.html +++ b/dom/tests/mochitest/pointerlock/test_pointerlock_xorigin_iframe.html @@ -22,7 +22,6 @@ SimpleTest.waitForExplicitFinish(); let gTestFiles = [ "file_pointerlock_xorigin_iframe.html", - "file_pointerlock_xorigin_iframe_no_user_gesture.html", ]; let gTestWindow = null; diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp index 3c610ed106ae..250be6e66525 100644 --- a/layout/base/PresShell.cpp +++ b/layout/base/PresShell.cpp @@ -26,7 +26,6 @@ #include "mozilla/Logging.h" #include "mozilla/MouseEvents.h" #include "mozilla/PerfStats.h" -#include "mozilla/PointerLockManager.h" #include "mozilla/PresShellInlines.h" #include "mozilla/RangeUtils.h" #include "mozilla/ScopeExit.h" @@ -6961,7 +6960,7 @@ nsresult PresShell::EventHandler::HandleEventUsingCoordinates( EventHandler::GetCapturingContentFor(aGUIEvent); if (GetDocument() && aGUIEvent->mClass == eTouchEventClass) { - PointerLockManager::Unlock(); + Document::UnlockPointer(); } nsIFrame* frameForPresShell = MaybeFlushThrottledStyles(aFrameForPresShell); @@ -8441,7 +8440,8 @@ void PresShell::EventHandler::MaybeHandleKeyboardEventBeforeDispatch( } } - nsCOMPtr pointerLockedDoc = PointerLockManager::GetLockedDocument(); + nsCOMPtr pointerLockedDoc = + do_QueryReferent(EventStateManager::sPointerLockedDoc); if (!mPresShell->mIsLastChromeOnlyEscapeKeyConsumed && pointerLockedDoc) { // XXX See above comment to understand the reason why this needs // to claim that the Escape key event is consumed by content @@ -8449,7 +8449,7 @@ void PresShell::EventHandler::MaybeHandleKeyboardEventBeforeDispatch( aKeyboardEvent->PreventDefaultBeforeDispatch(CrossProcessForwarding::eStop); aKeyboardEvent->mFlags.mOnlyChromeDispatch = true; if (aKeyboardEvent->mMessage == eKeyUp) { - PointerLockManager::Unlock(); + Document::UnlockPointer(); } } }