Bug 1376551 - Track whether there are pointer movement listeners in a window. r?smaug
This commit adds a flag for whether a nsPIDOMWindow contains a node with a
mouse event listener that is triggered by the mouse moving. Note, this is more
than just mousemove listeners, because mouseenter, mouseleave, mouseover,
mouseout can also be triggered by the mouse moving. The flag is added on
nsPIDOMWindow and it's propagated to it's parent windows.
This flag will be used to determine whether keyboard APZ can keep it's focus
target after the mouse moves, or if it needs to wait until content runs event
listeners and reconfirms what to scroll.
MozReview-Commit-ID: CuVcUB7qAo9
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -1022,16 +1022,17 @@ NextWindowID();
template<class T>
nsPIDOMWindow<T>::nsPIDOMWindow(nsPIDOMWindowOuter *aOuterWindow)
: mFrameElement(nullptr), mDocShell(nullptr), mModalStateDepth(0),
mMutationBits(0), mIsDocumentLoaded(false),
mIsHandlingResizeEvent(false), mIsInnerWindow(aOuterWindow != nullptr),
mMayHavePaintEventListener(false), mMayHaveTouchEventListener(false),
mMayHaveSelectionChangeEventListener(false),
+ mMayHavePointerMovementEventListener(false),
mMayHaveMouseEnterLeaveEventListener(false),
mMayHavePointerEnterLeaveEventListener(false),
mInnerObjectsFreed(false),
mIsModalContentWindow(false),
mIsActive(false), mIsBackground(false),
mMediaSuspend(Preferences::GetBool("media.block-autoplay-until-in-foreground", true) ?
nsISuspendedTypes::SUSPENDED_BLOCK : nsISuspendedTypes::NONE_SUSPENDED),
mAudioMuted(false), mAudioVolume(1.0), mAudioCaptured(false),
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -299,19 +299,16 @@ public:
static const nsGlobalWindow* Cast(const nsPIDOMWindowOuter* aPIWin) {
return static_cast<const nsGlobalWindow*>(
reinterpret_cast<const nsPIDOMWindow<nsISupports>*>(aPIWin));
}
static nsGlobalWindow* Cast(mozIDOMWindowProxy* aWin) {
return Cast(nsPIDOMWindowOuter::From(aWin));
}
- // public methods
- nsPIDOMWindowOuter* GetPrivateParent();
-
// callback for close event
void ReallyCloseWindow();
// nsISupports
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
// nsWrapperCache
virtual JSObject *WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override
@@ -374,16 +371,17 @@ public:
if (IsOuterWindow()) {
return GetCurrentInnerWindowInternal();
}
return const_cast<nsGlobalWindow*>(this);
}
// nsPIDOMWindow
+ virtual nsPIDOMWindowOuter* GetPrivateParent() override;
virtual nsPIDOMWindowOuter* GetPrivateRoot() override;
// Outer windows only.
virtual void ActivateOrDeactivate(bool aActivate) override;
virtual void SetActive(bool aActive) override;
virtual bool IsTopLevelWindowActive() override;
virtual void SetIsBackground(bool aIsBackground) override;
virtual void SetChromeEventHandler(mozilla::dom::EventTarget* aChromeEventHandler) override;
--- a/dom/base/nsNodeUtils.cpp
+++ b/dom/base/nsNodeUtils.cpp
@@ -535,16 +535,19 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNod
if (elm) {
window->SetMutationListeners(elm->MutationListenerBits());
if (elm->MayHavePaintEventListener()) {
window->SetHasPaintEventListeners();
}
if (elm->MayHaveTouchEventListener()) {
window->SetHasTouchEventListeners();
}
+ if (elm->MayHavePointerMovementEventListener()) {
+ window->SetHasPointerMovementEventListeners();
+ }
if (elm->MayHaveMouseEnterLeaveEventListener()) {
window->SetHasMouseEnterLeaveEventListeners();
}
if (elm->MayHavePointerEnterLeaveEventListener()) {
window->SetHasPointerEnterLeaveEventListeners();
}
if (elm->MayHaveSelectionChangeEventListener()) {
window->SetHasSelectionChangeEventListeners();
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -125,16 +125,17 @@ template<class T>
class nsPIDOMWindow : public T
{
public:
nsPIDOMWindowInner* AsInner();
const nsPIDOMWindowInner* AsInner() const;
nsPIDOMWindowOuter* AsOuter();
const nsPIDOMWindowOuter* AsOuter() const;
+ virtual nsPIDOMWindowOuter* GetPrivateParent() = 0;
virtual nsPIDOMWindowOuter* GetPrivateRoot() = 0;
virtual mozilla::dom::CustomElementRegistry* CustomElements() = 0;
// Outer windows only.
virtual void ActivateOrDeactivate(bool aActivate) = 0;
// this is called GetTopWindowRoot to avoid conflicts with nsIDOMWindow::GetWindowRoot
/**
* |top| gets the root of the window hierarchy.
@@ -682,16 +683,17 @@ protected:
uint32_t mMutationBits;
bool mIsDocumentLoaded;
bool mIsHandlingResizeEvent;
bool mIsInnerWindow;
bool mMayHavePaintEventListener;
bool mMayHaveTouchEventListener;
bool mMayHaveSelectionChangeEventListener;
+ bool mMayHavePointerMovementEventListener;
bool mMayHaveMouseEnterLeaveEventListener;
bool mMayHavePointerEnterLeaveEventListener;
// Used to detect whether we have called FreeInnerObjects() (e.g. to ensure
// that a call to ResumeTimeouts() after FreeInnerObjects() does nothing).
// This member is only used by inner windows.
bool mInnerObjectsFreed;
@@ -837,16 +839,35 @@ public:
return;
}
mMutationBits |= aType;
}
/**
+ * Call this to check whether some node (this window, its child windows,
+ * their documents, and the content in their documents) has event listeners
+ * that are triggered by mouse or pointer movement (e.g mousemove,
+ * pointermove, mouseenter, pointerenter).
+ */
+ bool HasPointerMovementEventListeners() const
+ {
+ return mMayHavePointerMovementEventListener;
+ }
+
+ /**
+ * Call this to indicate that some node (this window, its child windows,
+ * their documents, and the content in their documents) has event listeners
+ * that are triggered by mouse or pointer movement (e.g mousemove,
+ * pointermove, mouseenter, pointerenter).
+ */
+ inline void SetHasPointerMovementEventListeners();
+
+ /**
* Call this to check whether some node (this window, its document,
* or content in that document) has a mouseenter/leave event listener.
*/
bool HasMouseEnterLeaveEventListeners()
{
return mMayHaveMouseEnterLeaveEventListener;
}
--- a/dom/base/nsPIDOMWindowInlines.h
+++ b/dom/base/nsPIDOMWindowInlines.h
@@ -115,8 +115,27 @@ nsIContent*
nsPIDOMWindow<T>::GetFocusedNode() const
{
if (IsOuterWindow()) {
return mInnerWindow ? mInnerWindow->GetFocusedNode() : nullptr;
}
return mFocusedNode;
}
+
+void
+nsPIDOMWindowInner::SetHasPointerMovementEventListeners()
+{
+ // Don't recursively set this flag on our parents if we've already done this
+ if (mMayHavePointerMovementEventListener) {
+ return;
+ }
+
+ mMayHavePointerMovementEventListener = true;
+
+ if (nsCOMPtr<nsPIDOMWindowOuter> outer = GetOuterWindow()) {
+ if (nsCOMPtr<nsPIDOMWindowOuter> parentOuter = outer->GetPrivateParent()) {
+ if (nsCOMPtr<nsPIDOMWindowInner> parentInner = parentOuter->GetCurrentInnerWindow()) {
+ parentInner->SetHasPointerMovementEventListeners();
+ }
+ }
+ }
+}
--- a/dom/events/EventListenerManager.cpp
+++ b/dom/events/EventListenerManager.cpp
@@ -119,16 +119,17 @@ IsWebkitPrefixSupportEnabled()
EventListenerManagerBase::EventListenerManagerBase()
: mNoListenerForEvent(eVoidEvent)
, mMayHavePaintEventListener(false)
, mMayHaveMutationListeners(false)
, mMayHaveCapturingListeners(false)
, mMayHaveSystemGroupListeners(false)
, mMayHaveTouchEventListener(false)
+ , mMayHavePointerMovementEventListener(false)
, mMayHaveMouseEnterLeaveEventListener(false)
, mMayHavePointerEnterLeaveEventListener(false)
, mMayHaveKeyEventListener(false)
, mMayHaveInputOrCompositionEventListener(false)
, mMayHaveSelectionChangeEventListener(false)
, mClearingListeners(false)
, mIsMainThreadELM(NS_IsMainThread())
{
@@ -363,45 +364,68 @@ EventListenerManager::AddEventListenerIn
aTypeAtom == nsGkAtoms::ontouchcancel) {
mMayHaveTouchEventListener = true;
nsPIDOMWindowInner* window = GetInnerWindowForTarget();
// we don't want touchevent listeners added by scrollbars to flip this flag
// so we ignore listeners created with system event flag
if (window && !aFlags.mInSystemGroup) {
window->SetHasTouchEventListeners();
}
- } else if (aEventMessage >= ePointerEventFirst &&
- aEventMessage <= ePointerEventLast) {
+ } else if ((aEventMessage >= ePointerEventFirst &&
+ aEventMessage <= ePointerEventLast) ||
+ (aEventMessage >= eMouseEventFirst &&
+ aEventMessage <= eMouseEventLast)) {
nsPIDOMWindowInner* window = GetInnerWindowForTarget();
+
+ if ((aTypeAtom == nsGkAtoms::onpointermove ||
+ aTypeAtom == nsGkAtoms::onpointerenter ||
+ aTypeAtom == nsGkAtoms::onpointerleave ||
+ aTypeAtom == nsGkAtoms::onpointerover ||
+ aTypeAtom == nsGkAtoms::onpointerout ||
+ aTypeAtom == nsGkAtoms::onmousemove ||
+ aTypeAtom == nsGkAtoms::onmouseenter ||
+ aTypeAtom == nsGkAtoms::onmouseleave ||
+ aTypeAtom == nsGkAtoms::onmouseover ||
+ aTypeAtom == nsGkAtoms::onmouseout) &&
+ !aFlags.mInSystemGroup &&
+ aFlags.mAllowUntrustedEvents) {
+ mMayHavePointerMovementEventListener = true;
+ if (window) {
+ window->SetHasPointerMovementEventListeners();
+ }
+ }
+
if (aTypeAtom == nsGkAtoms::onpointerenter ||
aTypeAtom == nsGkAtoms::onpointerleave) {
mMayHavePointerEnterLeaveEventListener = true;
if (window) {
#ifdef DEBUG
nsCOMPtr<nsIDocument> d = window->GetExtantDoc();
NS_WARNING_ASSERTION(
!nsContentUtils::IsChromeDoc(d),
"Please do not use pointerenter/leave events in chrome. "
"They are slower than pointerover/out!");
#endif
window->SetHasPointerEnterLeaveEventListeners();
}
}
- } else if (aTypeAtom == nsGkAtoms::onmouseenter ||
- aTypeAtom == nsGkAtoms::onmouseleave) {
- mMayHaveMouseEnterLeaveEventListener = true;
- if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
+
+ if (aTypeAtom == nsGkAtoms::onmouseenter ||
+ aTypeAtom == nsGkAtoms::onmouseleave) {
+ mMayHaveMouseEnterLeaveEventListener = true;
+ if (window) {
#ifdef DEBUG
- nsCOMPtr<nsIDocument> d = window->GetExtantDoc();
- NS_WARNING_ASSERTION(
- !nsContentUtils::IsChromeDoc(d),
- "Please do not use mouseenter/leave events in chrome. "
- "They are slower than mouseover/out!");
+ nsCOMPtr<nsIDocument> d = window->GetExtantDoc();
+ NS_WARNING_ASSERTION(
+ !nsContentUtils::IsChromeDoc(d),
+ "Please do not use mouseenter/leave events in chrome. "
+ "They are slower than mouseover/out!");
#endif
- window->SetHasMouseEnterLeaveEventListeners();
+ window->SetHasMouseEnterLeaveEventListeners();
+ }
}
} else if (aEventMessage >= eGamepadEventFirst &&
aEventMessage <= eGamepadEventLast) {
if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
window->SetHasGamepadEventListener();
}
} else if (aTypeAtom == nsGkAtoms::onkeydown ||
aTypeAtom == nsGkAtoms::onkeypress ||
--- a/dom/events/EventListenerManager.h
+++ b/dom/events/EventListenerManager.h
@@ -156,24 +156,25 @@ protected:
EventListenerManagerBase();
EventMessage mNoListenerForEvent;
uint16_t mMayHavePaintEventListener : 1;
uint16_t mMayHaveMutationListeners : 1;
uint16_t mMayHaveCapturingListeners : 1;
uint16_t mMayHaveSystemGroupListeners : 1;
uint16_t mMayHaveTouchEventListener : 1;
+ uint16_t mMayHavePointerMovementEventListener : 1;
uint16_t mMayHaveMouseEnterLeaveEventListener : 1;
uint16_t mMayHavePointerEnterLeaveEventListener : 1;
uint16_t mMayHaveKeyEventListener : 1;
uint16_t mMayHaveInputOrCompositionEventListener : 1;
uint16_t mMayHaveSelectionChangeEventListener : 1;
uint16_t mClearingListeners : 1;
uint16_t mIsMainThreadELM : 1;
- // uint16_t mUnused : 4;
+ // uint16_t mUnused : 3;
};
/*
* Event listener manager
*/
class EventListenerManager final : public EventListenerManagerBase
{
@@ -435,16 +436,17 @@ public:
bool MayHavePaintEventListener() { return mMayHavePaintEventListener; }
/**
* Returns true if there may be a touch event listener registered,
* false if there definitely isn't.
*/
bool MayHaveTouchEventListener() { return mMayHaveTouchEventListener; }
+ bool MayHavePointerMovementEventListener() { return mMayHavePointerMovementEventListener; }
bool MayHaveMouseEnterLeaveEventListener() { return mMayHaveMouseEnterLeaveEventListener; }
bool MayHavePointerEnterLeaveEventListener() { return mMayHavePointerEnterLeaveEventListener; }
bool MayHaveSelectionChangeEventListener() { return mMayHaveSelectionChangeEventListener; }
/**
* Returns true if there may be a key event listener (keydown, keypress,
* or keyup) registered, or false if there definitely isn't.
*/