Bug 1376551 - Track whether there are pointer movement listeners in a window. r?smaug draft
authorRyan Hunt <rhunt@eqrion.net>
Thu, 03 Aug 2017 05:33:53 -0400
changeset 620850 d04ea1001d742a45d9baa9e474a988ca0ba4d5c1
parent 620112 d942ef54fdf726840a698d2ddcaf989d2c00edbc
child 620851 c2a7cbb128967a86435fabde3eacc98863fc5a81
push id72169
push userbmo:rhunt@eqrion.net
push dateFri, 04 Aug 2017 00:10:37 +0000
reviewerssmaug
bugs1376551
milestone57.0a1
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
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
dom/base/nsNodeUtils.cpp
dom/base/nsPIDOMWindow.h
dom/base/nsPIDOMWindowInlines.h
dom/events/EventListenerManager.cpp
dom/events/EventListenerManager.h
--- 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.
    */