Bug 1672330 - Move pointer lock code to PointerLockManager; r=smaug

Differential Revision: https://phabricator.services.mozilla.com/D101054
This commit is contained in:
Edgar Chen 2021-01-27 16:38:29 +00:00
Родитель 0bd4fa26f2
Коммит c2c0ea7709
21 изменённых файлов: 676 добавлений и 466 удалений

Просмотреть файл

@ -13718,7 +13718,7 @@ void Document::ExitFullscreenInDocTree(Document* aMaybeNotARootDoc) {
MOZ_ASSERT(aMaybeNotARootDoc);
// Unlock the pointer
UnlockPointer();
PointerLockManager::Unlock();
// Resolve all promises which waiting for exit fullscreen.
PendingFullscreenChangeList::Iterator<FullscreenExit> iter(
@ -13825,7 +13825,7 @@ void Document::RestorePreviousFullscreenState(UniquePtr<FullscreenExit> aExit) {
}
// If fullscreen mode is updated the pointer should be unlocked
UnlockPointer();
PointerLockManager::Unlock();
// 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<FullscreenRequest> aRequest) {
// If a document is already in fullscreen, then unlock the mouse pointer
// before setting a new document to fullscreen
UnlockPointer();
PointerLockManager::Unlock();
// Set the fullscreen element. This sets the fullscreen style on the
// element, and the fullscreen-ancestor styles on ancestors of the element
@ -14534,350 +14534,6 @@ bool Document::SetOrientationPendingPromise(Promise* aPromise) {
return true;
}
static void DispatchPointerLockChange(Document* aTarget) {
if (!aTarget) {
return;
}
RefPtr<AsyncEventDispatcher> 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<AsyncEventDispatcher> 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<Document> 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<nsPIDOMWindowOuter> ownerWindow = ownerDoc->GetWindow();
if (!ownerWindow) {
return "PointerLockDeniedHidden";
}
nsCOMPtr<nsPIDOMWindowInner> 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> element = do_QueryReferent(mElement);
nsCOMPtr<Document> 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<Element> 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> 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<nsIBrowserChild> browserChild =
do_QueryReferent(bc)) {
static_cast<BrowserChild*>(browserChild.get())
->SendReleasePointerLock();
}
}
});
nsCOMPtr<Element> element = do_QueryReferent(e);
if (!element || !document || !element->GetComposedDoc()) {
error = "PointerLockDeniedNotInDocument";
return;
}
if (element->GetComposedDoc() != document) {
error = "PointerLockDeniedMovedDocument";
return;
}
nsCOMPtr<Element> 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> 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<Element> 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<nsIRunnable> 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<Document> 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<nsIWidget> 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<EventStateManager> 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<Document> pointerLockedDoc =
do_QueryReferent(EventStateManager::sPointerLockedDoc);
if (!pointerLockedDoc || (aDoc && aDoc != pointerLockedDoc)) {
return;
}
if (!pointerLockedDoc->SetPointerLock(nullptr, StyleCursorKind::Auto)) {
return;
}
nsCOMPtr<Element> pointerLockedElement =
do_QueryReferent(EventStateManager::sPointerLockedElement);
ChangePointerLockedElement(nullptr, pointerLockedDoc, pointerLockedElement);
if (BrowserChild* browserChild =
BrowserChild::GetFrom(pointerLockedDoc->GetDocShell())) {
browserChild->SendReleasePointerLock();
}
RefPtr<AsyncEventDispatcher> 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();

Просмотреть файл

@ -34,6 +34,7 @@
#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"
@ -2008,12 +2009,6 @@ 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();
@ -3407,7 +3402,7 @@ class Document : public nsINode,
Element* GetUnretargetedFullScreenElement();
bool Fullscreen() { return !!GetFullscreenElement(); }
already_AddRefed<Promise> ExitFullscreen(ErrorResult&);
void ExitPointerLock() { UnlockPointer(this); }
void ExitPointerLock() { PointerLockManager::Unlock(this); }
void GetFgColor(nsAString& aFgColor);
void SetFgColor(const nsAString& aFgColor);
void GetLinkColor(nsAString& aLinkColor);

Просмотреть файл

@ -7,6 +7,7 @@
#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"
@ -300,7 +301,7 @@ Element* DocumentOrShadowRoot::GetRetargetedFocusedElement() {
Element* DocumentOrShadowRoot::GetPointerLockElement() {
nsCOMPtr<Element> pointerLockedElement =
do_QueryReferent(EventStateManager::sPointerLockedElement);
PointerLockManager::GetLockedElement();
if (!pointerLockedElement) {
return nullptr;
}

Просмотреть файл

@ -45,6 +45,7 @@
#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"
@ -1830,7 +1831,7 @@ void Element::UnbindFromTree(bool aNullParent) {
Document* document = GetComposedDoc();
if (HasPointerLock()) {
Document::UnlockPointer();
PointerLockManager::Unlock();
}
if (mState.HasState(NS_EVENT_STATE_FULLSCREEN)) {
// The element being removed is an ancestor of the fullscreen element,
@ -3339,7 +3340,7 @@ already_AddRefed<Promise> Element::RequestFullscreen(CallerType aCallerType,
}
void Element::RequestPointerLock(CallerType aCallerType) {
OwnerDoc()->RequestPointerLock(this, aCallerType);
PointerLockManager::RequestLock(this, aCallerType);
}
already_AddRefed<Flex> Element::GetAsFlexContainer() {

Просмотреть файл

@ -0,0 +1,443 @@
/* -*- 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<dom::Element> PointerLockManager::GetLockedElement() {
nsCOMPtr<Element> element = do_QueryReferent(sLockedElement);
return element.forget();
}
/* static */
already_AddRefed<dom::Document> PointerLockManager::GetLockedDocument() {
nsCOMPtr<Document> 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<AsyncEventDispatcher> 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<AsyncEventDispatcher> 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<Document> 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<nsPIDOMWindowOuter> ownerWindow = ownerDoc->GetWindow();
if (!ownerWindow) {
return "PointerLockDeniedHidden";
}
nsCOMPtr<nsPIDOMWindowInner> 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<Document> doc = aElement->OwnerDoc();
nsCOMPtr<Element> 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<nsIRunnable> request =
new PointerLockRequest(aElement, userInputOrSystemCaller);
doc->Dispatch(TaskCategory::Other, request.forget());
}
/* static */
void PointerLockManager::Unlock(Document* aDoc) {
if (!sIsLocked) {
return;
}
nsCOMPtr<Document> pointerLockedDoc = GetLockedDocument();
if (!pointerLockedDoc || (aDoc && aDoc != pointerLockedDoc)) {
return;
}
if (!SetPointerLock(nullptr, pointerLockedDoc, StyleCursorKind::Auto)) {
return;
}
nsCOMPtr<Element> pointerLockedElement = GetLockedElement();
ChangePointerLockedElement(nullptr, pointerLockedDoc, pointerLockedElement);
if (BrowserChild* browserChild =
BrowserChild::GetFrom(pointerLockedDoc->GetDocShell())) {
browserChild->SendReleasePointerLock();
}
RefPtr<AsyncEventDispatcher> 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<Document> 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<nsIWidget> 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<EventStateManager> 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<Document> 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> element = do_QueryReferent(mElement);
nsCOMPtr<Document> 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<Element> 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> 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<nsIBrowserChild> browserChild =
do_QueryReferent(bc)) {
static_cast<BrowserChild*>(browserChild.get())
->SendReleasePointerLock();
}
}
});
nsCOMPtr<Element> element = do_QueryReferent(e);
if (!element || !document || !element->GetComposedDoc()) {
error = "PointerLockDeniedNotInDocument";
return;
}
if (element->GetComposedDoc() != document) {
error = "PointerLockDeniedMovedDocument";
return;
}
nsCOMPtr<Element> 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> document = do_QueryReferent(doc);
if (!document) {
return;
}
DispatchPointerLockError(document, "PointerLockDeniedFailedToLock");
});
} else {
StartSetPointerLock(element, document);
}
return NS_OK;
}
} // namespace mozilla

Просмотреть файл

@ -0,0 +1,82 @@
/* -*- 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<dom::Element> GetLockedElement();
static already_AddRefed<dom::Document> 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

Просмотреть файл

@ -124,6 +124,7 @@ EXPORTS.mozilla += [
"FlushType.h",
"FullscreenChange.h",
"IdentifierMapEntry.h",
"PointerLockManager.h",
"RangeBoundary.h",
"RangeUtils.h",
"ScriptableContentIterator.h",
@ -392,6 +393,7 @@ UNIFIED_SOURCES += [
"nsWindowRoot.cpp",
"nsWrapperCache.cpp",
"ParentProcessMessageManager.cpp",
"PointerLockManager.cpp",
"PopupBlocker.cpp",
"Pose.cpp",
"PostMessageEvent.cpp",

Просмотреть файл

@ -6873,24 +6873,6 @@ Document* nsContentUtils::GetRootDocument(Document* aDoc) {
return doc;
}
/* static */
bool nsContentUtils::IsInPointerLockContext(BrowsingContext* aContext) {
if (!aContext) {
return false;
}
nsCOMPtr<Document> 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) {

Просмотреть файл

@ -2445,14 +2445,6 @@ 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);

Просмотреть файл

@ -61,6 +61,7 @@
#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"
@ -4647,11 +4648,8 @@ void nsFocusManager::GetFocusInSelection(nsPIDOMWindowOuter* aWindow,
}
static void MaybeUnlockPointer(BrowsingContext* aCurrentFocusedContext) {
nsCOMPtr<Document> pointerLockedDoc =
do_QueryReferent(EventStateManager::sPointerLockedDoc);
if (pointerLockedDoc &&
!nsContentUtils::IsInPointerLockContext(aCurrentFocusedContext)) {
Document::UnlockPointer();
if (!PointerLockManager::IsInLockContext(aCurrentFocusedContext)) {
PointerLockManager::Unlock();
}
}
@ -4881,8 +4879,8 @@ uint64_t nsFocusManager::GenerateFocusActionId() {
}
static bool IsInPointerLockContext(nsPIDOMWindowOuter* aWin) {
return nsContentUtils::IsInPointerLockContext(
aWin ? aWin->GetBrowsingContext() : nullptr);
return PointerLockManager::IsInLockContext(aWin ? aWin->GetBrowsingContext()
: nullptr);
}
void nsFocusManager::SetFocusedWindowInternal(nsPIDOMWindowOuter* aWindow) {

Просмотреть файл

@ -17,6 +17,7 @@
#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"
@ -517,7 +518,7 @@ WidgetEvent* Event::WidgetEventPtr() { return mEvent; }
CSSIntPoint Event::GetScreenCoords(nsPresContext* aPresContext,
WidgetEvent* aEvent,
LayoutDeviceIntPoint aPoint) {
if (EventStateManager::sIsPointerLocked) {
if (PointerLockManager::IsLocked()) {
return EventStateManager::sLastScreenPoint;
}
@ -587,7 +588,7 @@ CSSIntPoint Event::GetClientCoords(nsPresContext* aPresContext,
WidgetEvent* aEvent,
LayoutDeviceIntPoint aPoint,
CSSIntPoint aDefaultPoint) {
if (EventStateManager::sIsPointerLocked) {
if (PointerLockManager::IsLocked()) {
return EventStateManager::sLastClientPoint;
}

Просмотреть файл

@ -15,6 +15,7 @@
#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"
@ -223,11 +224,6 @@ 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<nsIContent> EventStateManager::sDragOverContent = nullptr;
EventStateManager::WheelPrefs* EventStateManager::WheelPrefs::sInstance =
@ -534,11 +530,10 @@ nsresult EventStateManager::PreHandleEvent(nsPresContext* aPresContext,
return NS_ERROR_NULL_POINTER;
}
#ifdef DEBUG
if (aEvent->HasDragEventMessage() && sIsPointerLocked) {
NS_ASSERTION(
sIsPointerLocked,
"sIsPointerLocked is true. Drag events should be suppressed when "
"the pointer is locked.");
if (aEvent->HasDragEventMessage() && PointerLockManager::IsLocked()) {
NS_ASSERTION(PointerLockManager::IsLocked(),
"Pointer is locked. Drag events should be suppressed when "
"the pointer is locked.");
}
#endif
// Store last known screenPoint and clientPoint so pointer lock
@ -546,7 +541,7 @@ nsresult EventStateManager::PreHandleEvent(nsPresContext* aPresContext,
if (aEvent->IsTrusted() &&
((mouseEvent && mouseEvent->IsReal()) ||
aEvent->mClass == eWheelEventClass) &&
!sIsPointerLocked) {
!PointerLockManager::IsLocked()) {
sLastScreenPoint =
Event::GetScreenCoords(aPresContext, aEvent, aEvent->mRefPoint);
sLastClientPoint = Event::GetClientCoords(
@ -572,7 +567,7 @@ nsresult EventStateManager::PreHandleEvent(nsPresContext* aPresContext,
switch (aEvent->mMessage) {
case eContextMenu:
if (sIsPointerLocked) {
if (PointerLockManager::IsLocked()) {
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
break;
@ -698,7 +693,7 @@ nsresult EventStateManager::PreHandleEvent(nsPresContext* aPresContext,
UpdateCursor(aPresContext, aEvent, mCurrentTarget, aStatus);
UpdateLastRefPointOfMouseEvent(mouseEvent);
if (sIsPointerLocked) {
if (PointerLockManager::IsLocked()) {
ResetPointerToWindowCenterWhilePointerLocked(mouseEvent);
}
UpdateLastPointerPosition(mouseEvent);
@ -1383,7 +1378,7 @@ void EventStateManager::DispatchCrossProcessEvent(WidgetEvent* aEvent,
}
if (BrowserParent* pointerLockedRemote =
BrowserParent::GetPointerLockedRemoteTarget()) {
PointerLockManager::GetLockedRemoteTarget()) {
remote = pointerLockedRemote;
} else if (BrowserParent* pointerCapturedRemote =
PointerEventHandler::GetPointerCapturingRemoteTarget(
@ -1577,7 +1572,8 @@ void EventStateManager::CreateClickHoldTimer(nsPresContext* inPresContext,
nsIFrame* inDownFrame,
WidgetGUIEvent* inMouseDownEvent) {
if (!inMouseDownEvent->IsTrusted() ||
IsTopLevelRemoteTarget(mGestureDownContent) || sIsPointerLocked) {
IsTopLevelRemoteTarget(mGestureDownContent) ||
PointerLockManager::IsLocked()) {
return;
}
@ -1644,7 +1640,7 @@ void EventStateManager::sClickHoldCallback(nsITimer* aTimer, void* aESM) {
// length of time, which is _not_ what we want.
//
void EventStateManager::FireContextClick() {
if (!mGestureDownContent || !mPresContext || sIsPointerLocked) {
if (!mGestureDownContent || !mPresContext || PointerLockManager::IsLocked()) {
return;
}
@ -4251,11 +4247,12 @@ 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 (sIsPointerLocked && (aMessage == eMouseLeave || aMessage == eMouseEnter ||
aMessage == eMouseOver || aMessage == eMouseOut)) {
if (PointerLockManager::IsLocked() &&
(aMessage == eMouseLeave || aMessage == eMouseEnter ||
aMessage == eMouseOver || aMessage == eMouseOut)) {
mCurrentTargetContent = nullptr;
nsCOMPtr<Element> pointerLockedElement =
do_QueryReferent(EventStateManager::sPointerLockedElement);
PointerLockManager::GetLockedElement();
if (!pointerLockedElement) {
NS_WARNING("Should have pointer locked element, but didn't.");
return nullptr;
@ -4543,7 +4540,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 (sIsPointerLocked && aMouseEvent->mWidget) {
if (PointerLockManager::IsLocked() && 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
@ -4567,7 +4564,7 @@ void EventStateManager::UpdateLastRefPointOfMouseEvent(
/* static */
void EventStateManager::ResetPointerToWindowCenterWhilePointerLocked(
WidgetMouseEvent* aMouseEvent) {
MOZ_ASSERT(sIsPointerLocked);
MOZ_ASSERT(PointerLockManager::IsLocked());
if ((aMouseEvent->mMessage != eMouseMove &&
aMouseEvent->mMessage != ePointerMove) ||
!aMouseEvent->mWidget) {
@ -4705,9 +4702,6 @@ 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();
@ -4715,7 +4709,7 @@ void EventStateManager::SetPointerLock(nsIWidget* aWidget,
nsCOMPtr<nsIDragService> dragService =
do_GetService("@mozilla.org/widget/dragservice;1");
if (sIsPointerLocked) {
if (PointerLockManager::IsLocked()) {
MOZ_ASSERT(aWidget, "Locking pointer requires a widget");
// Release all pointer capture when a pointer lock is successfully applied

Просмотреть файл

@ -317,10 +317,6 @@ 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

Просмотреть файл

@ -7,6 +7,7 @@
#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"
@ -195,7 +196,7 @@ bool PointerEventHandler::SetPointerCaptureRemoteTarget(
MOZ_ASSERT(sPointerCaptureRemoteTargetTable);
MOZ_ASSERT(aBrowserParent);
if (BrowserParent::GetPointerLockedRemoteTarget()) {
if (PointerLockManager::GetLockedRemoteTarget()) {
return false;
}

Просмотреть файл

@ -11,6 +11,7 @@
#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"
@ -32,7 +33,7 @@ UIEvent::UIEvent(EventTarget* aOwner, nsPresContext* aPresContext,
mLayerPoint(0, 0),
mPagePoint(0, 0),
mMovementPoint(0, 0),
mIsPointerLocked(EventStateManager::sIsPointerLocked),
mIsPointerLocked(PointerLockManager::IsLocked()),
mLastClientPoint(EventStateManager::sLastClientPoint) {
if (aEvent) {
mEventIsInternal = false;

Просмотреть файл

@ -170,8 +170,6 @@ 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.
@ -254,11 +252,6 @@ BrowserParent* BrowserParent::GetLastMouseRemoteTarget() {
return sLastMouseRemoteTarget;
}
/* static */
BrowserParent* BrowserParent::GetPointerLockedRemoteTarget() {
return sPointerLockedRemoteTarget;
}
/*static*/
BrowserParent* BrowserParent::GetFrom(nsFrameLoader* aFrameLoader) {
if (!aFrameLoader) {
@ -608,7 +601,7 @@ void BrowserParent::RemoveWindowListeners() {
void BrowserParent::DestroyInternal() {
UnsetTopLevelWebFocus(this);
UnsetLastMouseRemoteTarget(this);
UnsetPointerLockedRemoteTarget(this);
PointerLockManager::ReleaseLockedRemoteTarget(this);
PointerEventHandler::ReleasePointerCaptureRemoteTarget(this);
PresShell::ReleaseCapturingRemoteTarget(this);
@ -692,7 +685,7 @@ void BrowserParent::ActorDestroy(ActorDestroyReason why) {
// case of a crash.
BrowserParent::UnsetTopLevelWebFocus(this);
BrowserParent::UnsetLastMouseRemoteTarget(this);
BrowserParent::UnsetPointerLockedRemoteTarget(this);
PointerLockManager::ReleaseLockedRemoteTarget(this);
PointerEventHandler::ReleasePointerCaptureRemoteTarget(this);
PresShell::ReleaseCapturingRemoteTarget(this);
@ -3105,14 +3098,6 @@ 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<nsIWidget> widget = GetTextInputHandlingWidget();
@ -4082,19 +4067,10 @@ 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 (!SetPointerLock()) {
if (!PointerLockManager::SetLockedRemoteTarget(this)) {
error = "PointerLockDeniedInUse";
} else {
PointerEventHandler::ReleaseAllPointerCaptureRemoteTarget();
@ -4104,8 +4080,9 @@ mozilla::ipc::IPCResult BrowserParent::RecvRequestPointerLock(
}
mozilla::ipc::IPCResult BrowserParent::RecvReleasePointerLock() {
MOZ_ASSERT_IF(sPointerLockedRemoteTarget, sPointerLockedRemoteTarget == this);
UnsetPointerLockedRemoteTarget(this);
MOZ_ASSERT_IF(PointerLockManager::GetLockedRemoteTarget(),
PointerLockManager::GetLockedRemoteTarget() == this);
PointerLockManager::ReleaseLockedRemoteTarget(this);
return IPC_OK();
}

Просмотреть файл

@ -117,8 +117,6 @@ class BrowserParent final : public PBrowserParent,
static BrowserParent* GetLastMouseRemoteTarget();
static BrowserParent* GetPointerLockedRemoteTarget();
static BrowserParent* GetFrom(nsFrameLoader* aFrameLoader);
static BrowserParent* GetFrom(PBrowserParent* aBrowserParent);
@ -760,7 +758,6 @@ class BrowserParent final : public PBrowserParent,
mozilla::ipc::IPCResult RecvMaybeFireEmbedderLoadEvents(
EmbedderElementEventType aFireEventAtEmbeddingElement);
bool SetPointerLock();
mozilla::ipc::IPCResult RecvRequestPointerLock(
RequestPointerLockResolver&& aResolve);
mozilla::ipc::IPCResult RecvReleasePointerLock();
@ -840,13 +837,6 @@ 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 &&

Просмотреть файл

@ -0,0 +1,95 @@
<!DOCTYPE HTML>
<html>
<!--https://bugzilla.mozilla.org/show_bug.cgi?id=1672330-->
<head>
<title>Bug 1672330</title>
<script src="/tests/SimpleTest/EventUtils.js"></script>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/SimpleTest/paint_listener.js"></script>
<script src="/tests/gfx/layers/apz/test/mochitest/apz_test_utils.js"></script>
<script src="pointerlock_utils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<style>
#target {
width: 100px;
height: 100px;
background-color: green;
}
iframe {
width: 400px;
height: 300px;
border: 1px solid blue;
}
</style>
</head>
<body>
<a target="_blank"href="https://bugzilla.mozilla.org/show_bug.cgi?id=1672330">Mozilla Bug 1672330</a>
<div id="target"></div>
<iframe src="https://example.com/tests/dom/tests/mochitest/pointerlock/iframe_differentDOM.html"></iframe>
<pre id="test">
<script type="text/javascript">
/**
* Test for Bug 1672330
*/
SimpleTest.waitForExplicitFinish();
async function requestPointerLock(aElement, aExpectError = false) {
let doc = aElement.ownerDocument;
let waitForPointerLockEvent = function() {
return new Promise((aResolve) => {
let eventHandler = function(aEvent) {
is(aEvent.type, aExpectError ? 'pointerlockerror' : 'pointerlockchange',
`got ${aEvent.type}`);
doc.removeEventListener('pointerlockchange', eventHandler);
doc.removeEventListener('pointerlockerror', eventHandler);
aResolve();
};
doc.addEventListener('pointerlockchange', eventHandler);
doc.addEventListener('pointerlockerror', eventHandler);
});
};
aElement.requestPointerLock();
await waitForPointerLockEvent();
is(doc.pointerLockElement, aExpectError ? null : aElement, "target pointer locked");
}
async function start() {
await waitUntilApzStable();
let target = document.getElementById("target");
SpecialPowers.wrap(document).clearUserGestureActivation();
// Pointer lock request should be rejected due to the lack of user gesture.
await requestPointerLock(target, true);
// Test mouse event should not be dispatched to document.
document.addEventListener("mousemove", function(e) {
ok(false, "Got unexpected mousemove");
});
info("test sending mouse event to iframe");
let iframe = document.querySelector("iframe");
synthesizeMouse(iframe, 10, 10, { type: "mousemove" });
await new Promise(resolve => { SimpleTest.executeSoon(resolve); });
info("test sending mouse event to another window");
await new Promise((aResolve) => {
let win = window.open("iframe_differentDOM.html");
win.addEventListener("load", async function() {
info("win onload");
await waitUntilApzStable();
synthesizeMouse(win.document.body, 10, 10, { type: "mousemove" }, win);
win.close();
aResolve();
});
});
await new Promise(resolve => { SimpleTest.executeSoon(resolve); });
SimpleTest.finish();
}
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -2,6 +2,7 @@
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]
@ -32,3 +33,4 @@ support-files =
[test_pointerlock_xorigin_iframe.html]
support-files =
file_pointerlock_xorigin_iframe.html
file_pointerlock_xorigin_iframe_no_user_gesture.html

Просмотреть файл

@ -22,6 +22,7 @@ SimpleTest.waitForExplicitFinish();
let gTestFiles = [
"file_pointerlock_xorigin_iframe.html",
"file_pointerlock_xorigin_iframe_no_user_gesture.html",
];
let gTestWindow = null;

Просмотреть файл

@ -26,6 +26,7 @@
#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"
@ -6960,7 +6961,7 @@ nsresult PresShell::EventHandler::HandleEventUsingCoordinates(
EventHandler::GetCapturingContentFor(aGUIEvent);
if (GetDocument() && aGUIEvent->mClass == eTouchEventClass) {
Document::UnlockPointer();
PointerLockManager::Unlock();
}
nsIFrame* frameForPresShell = MaybeFlushThrottledStyles(aFrameForPresShell);
@ -8440,8 +8441,7 @@ void PresShell::EventHandler::MaybeHandleKeyboardEventBeforeDispatch(
}
}
nsCOMPtr<Document> pointerLockedDoc =
do_QueryReferent(EventStateManager::sPointerLockedDoc);
nsCOMPtr<Document> pointerLockedDoc = PointerLockManager::GetLockedDocument();
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) {
Document::UnlockPointer();
PointerLockManager::Unlock();
}
}
}