Bug 1376551 - Don't lose APZ focus state when the mouse moves and there are no listeners. r?kats draft
authorRyan Hunt <rhunt@eqrion.net>
Thu, 03 Aug 2017 05:35:21 -0400
changeset 620851 c2a7cbb128967a86435fabde3eacc98863fc5a81
parent 620850 d04ea1001d742a45d9baa9e474a988ca0ba4d5c1
child 640822 ecd6b216985604115f5767b3e5132860ea380e62
push id72169
push userbmo:rhunt@eqrion.net
push dateFri, 04 Aug 2017 00:10:37 +0000
reviewerskats
bugs1376551
milestone57.0a1
Bug 1376551 - Don't lose APZ focus state when the mouse moves and there are no listeners. r?kats MozReview-Commit-ID: 8hjoFBcOHuZ
gfx/layers/apz/src/APZCTreeManager.cpp
gfx/layers/apz/src/FocusState.cpp
gfx/layers/apz/src/FocusState.h
gfx/layers/apz/src/FocusTarget.cpp
gfx/layers/apz/src/FocusTarget.h
gfx/layers/ipc/LayersMessageUtils.h
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -1000,16 +1000,21 @@ APZCTreeManager::ReceiveInputEvent(Input
       result = ProcessTouchInput(touchInput, aOutTargetGuid, aOutInputBlockId);
       break;
     } case MOUSE_INPUT: {
       MouseInput& mouseInput = aEvent.AsMouseInput();
       mouseInput.mHandledByAPZ = true;
 
       mCurrentMousePosition = mouseInput.mOrigin;
 
+      if (mouseInput.mType == MouseInput::MOUSE_MOVE &&
+          mFocusState.CanIgnoreMouseMoveEvents()) {
+        focusSetter.MarkAsNonFocusChanging();
+      }
+
       bool startsDrag = DragTracker::StartsDrag(mouseInput);
       if (startsDrag) {
         // If this is the start of a drag we need to unambiguously know if it's
         // going to land on a scrollbar or not. We can't apply an untransform
         // here without knowing that, so we need to ensure the untransform is
         // a no-op.
         FlushRepaintsToClearScreenToGeckoTransform();
       }
--- a/gfx/layers/apz/src/FocusState.cpp
+++ b/gfx/layers/apz/src/FocusState.cpp
@@ -10,16 +10,17 @@
 
 namespace mozilla {
 namespace layers {
 
 FocusState::FocusState()
   : mLastAPZProcessedEvent(1)
   , mLastContentProcessedEvent(0)
   , mFocusHasKeyEventListeners(false)
+  , mHasPointerMovementEventListeners(false)
   , mFocusLayersId(0)
   , mFocusHorizontalTarget(FrameMetrics::NULL_SCROLL_ID)
   , mFocusVerticalTarget(FrameMetrics::NULL_SCROLL_ID)
 {
 }
 
 bool
 FocusState::IsCurrent() const
@@ -49,20 +50,28 @@ FocusState::Update(uint64_t aRootLayerTr
          static_cast<int>(aState.mType),
          aState.mSequenceNumber);
 
   // Update the focus tree with the latest target
   mFocusTree[aOriginatingLayersId] = aState;
 
   // Reset our internal state so we can recalculate it
   mFocusHasKeyEventListeners = false;
+  mHasPointerMovementEventListeners = false;
   mFocusLayersId = aRootLayerTreeId;
   mFocusHorizontalTarget = FrameMetrics::NULL_SCROLL_ID;
   mFocusVerticalTarget = FrameMetrics::NULL_SCROLL_ID;
 
+  // Accumulate whether there are listeners triggered for mouse movement
+  // anywhere in the document
+  for (const auto& currentNode : mFocusTree) {
+    const FocusTarget& target = currentNode.second;
+    mHasPointerMovementEventListeners |= target.mHasPointerMovementEventListeners;
+  }
+
   // To update the focus state for the entire APZCTreeManager, we need
   // to traverse the focus tree to find the current leaf which is the global
   // focus target we can use for async keyboard scrolling
   while (true) {
     auto currentNode = mFocusTree.find(mFocusLayersId);
     if (currentNode == mFocusTree.end()) {
       FS_LOG("Setting target to nil (cannot find lt=%" PRIu64 ")\n",
              mFocusLayersId);
--- a/gfx/layers/apz/src/FocusState.h
+++ b/gfx/layers/apz/src/FocusState.h
@@ -135,16 +135,25 @@ public:
    * Gets whether it is safe to not increment the focus sequence number for an
    * unmatched keyboard event.
    */
   bool CanIgnoreKeyboardShortcutMisses() const
   {
     return IsCurrent() && !mFocusHasKeyEventListeners;
   }
 
+  /**
+   * Gets whether it is safe to not increment the focus sequence number for
+   * mousemove inputs.
+   */
+  bool CanIgnoreMouseMoveEvents() const
+  {
+    return IsCurrent() && !mHasPointerMovementEventListeners;
+  }
+
 private:
   // The set of focus targets received indexed by their layer tree ID
   std::unordered_map<uint64_t, FocusTarget> mFocusTree;
 
   // The focus sequence number of the last potentially focus changing event
   // processed by APZ. This number starts at one and increases monotonically.
   // We don't worry about wrap around here because at a pace of 100 increments/sec,
   // it would take 5.85*10^9 years before we would wrap around. This number will
@@ -152,16 +161,19 @@ private:
   // on input events.
   uint64_t mLastAPZProcessedEvent;
   // The focus sequence number last received in a focus update.
   uint64_t mLastContentProcessedEvent;
 
   // A flag whether there is a key listener on the event target chain for the
   // focused element
   bool mFocusHasKeyEventListeners;
+  // A flag whether there event listeners that are triggered by pointer
+  // movement.
+  bool mHasPointerMovementEventListeners;
 
   // The layer tree ID which contains the scrollable frame of the focused element
   uint64_t mFocusLayersId;
   // The scrollable layer corresponding to the scrollable frame that is used to
   // scroll the focused element. This depends on the direction the user is
   // scrolling.
   FrameMetrics::ViewID mFocusHorizontalTarget;
   FrameMetrics::ViewID mFocusVerticalTarget;
--- a/gfx/layers/apz/src/FocusTarget.cpp
+++ b/gfx/layers/apz/src/FocusTarget.cpp
@@ -75,24 +75,26 @@ static bool
 IsEditableNode(nsINode* aNode)
 {
   return aNode && aNode->IsEditable();
 }
 
 FocusTarget::FocusTarget()
   : mSequenceNumber(0)
   , mFocusHasKeyEventListeners(false)
+  , mHasPointerMovementEventListeners(false)
   , mType(FocusTarget::eNone)
 {
 }
 
 FocusTarget::FocusTarget(nsIPresShell* aRootPresShell,
                          uint64_t aFocusSequenceNumber)
   : mSequenceNumber(aFocusSequenceNumber)
   , mFocusHasKeyEventListeners(false)
+  , mHasPointerMovementEventListeners(false)
 {
   MOZ_ASSERT(aRootPresShell);
   MOZ_ASSERT(NS_IsMainThread());
 
   // Key events can be retargeted to a child PresShell when there is an iframe
   nsCOMPtr<nsIPresShell> presShell = GetRetargetEventPresShell(aRootPresShell);
 
   if (!presShell) {
@@ -118,61 +120,71 @@ FocusTarget::FocusTarget(nsIPresShell* a
   nsCOMPtr<nsIContent> focusedContent = presShell->GetFocusedContentInOurWindow();
 
   // Check if there are key event listeners that could prevent default or change
   // the focus or selection of the page.
   mFocusHasKeyEventListeners =
     HasListenersForKeyEvents(focusedContent ? focusedContent.get()
                                             : document->GetUnfocusedKeyEventTarget());
 
-  // Check if the focused element is content editable or if the document
+  // Check if there are mouse movement event listeners that could change the
+  // focus or selection of the page.
+  if (nsCOMPtr<nsPIDOMWindowInner> window = document->GetInnerWindow()) {
+    mHasPointerMovementEventListeners = window->HasPointerMovementEventListeners();
+  }
+
+  // Check if the key event target is content editable or if the document
   // is in design mode.
   if (IsEditableNode(focusedContent) ||
       IsEditableNode(document)) {
-    FT_LOG("Creating nil target with seq=%" PRIu64 ", kl=%d (disabling for editable node)\n",
+    FT_LOG("Creating nil target with seq=%" PRIu64 ", kl=%d, pl=%d (disabling for editable node)\n",
            aFocusSequenceNumber,
-           static_cast<int>(mFocusHasKeyEventListeners));
+           mFocusHasKeyEventListeners,
+           mHasPointerMovementEventListeners);
 
     mType = FocusTarget::eNone;
     return;
   }
 
   // Check if the focused element is a remote browser
   if (TabParent* browserParent = TabParent::GetFrom(focusedContent)) {
     RenderFrameParent* rfp = browserParent->GetRenderFrame();
 
     // The globally focused element for scrolling is in a remote layer tree
     if (rfp) {
-      FT_LOG("Creating reflayer target with seq=%" PRIu64 ", kl=%d, lt=%" PRIu64 "\n",
+      FT_LOG("Creating reflayer target with seq=%" PRIu64 ", kl=%d, pl=%d, lt=%" PRIu64 "\n",
              aFocusSequenceNumber,
              mFocusHasKeyEventListeners,
+             mHasPointerMovementEventListeners,
              rfp->GetLayersId());
 
       mType = FocusTarget::eRefLayer;
       mData.mRefLayerId = rfp->GetLayersId();
       return;
     }
 
-    FT_LOG("Creating nil target with seq=%" PRIu64 ", kl=%d (remote browser missing layers id)\n",
+    FT_LOG("Creating nil target with seq=%" PRIu64 ", kl=%d, pl=%d (remote browser missing layers id)\n",
            aFocusSequenceNumber,
-           mFocusHasKeyEventListeners);
+           mFocusHasKeyEventListeners,
+           mHasPointerMovementEventListeners);
 
     mType = FocusTarget::eNone;
     return;
   }
 
   // The content to scroll is either the focused element or the focus node of
   // the selection. It's difficult to determine if an element is an interactive
   // element requiring async keyboard scrolling to be disabled. So we only
   // allow async key scrolling based on the selection, which doesn't have
   // this problem and is more common.
   if (focusedContent) {
-    FT_LOG("Creating nil target with seq=%" PRIu64 ", kl=%d (disabling for focusing an element)\n",
+    FT_LOG("Creating nil target with seq=%" PRIu64 ", kl=%d, pl=%d  (disabling for focusing an element)\n",
            aFocusSequenceNumber,
-           mFocusHasKeyEventListeners);
+           mFocusHasKeyEventListeners,
+           mHasPointerMovementEventListeners);
 
     mType = FocusTarget::eNone;
     return;
   }
 
   nsCOMPtr<nsIContent> selectedContent = presShell->GetSelectedContentForScrolling();
 
   // Gather the scrollable frames that would be scrolled in each direction
@@ -187,28 +199,30 @@ FocusTarget::FocusTarget(nsIPresShell* a
   // We might have the globally focused element for scrolling. Gather a ViewID for
   // the horizontal and vertical scroll targets of this element.
   mType = FocusTarget::eScrollLayer;
   mData.mScrollTargets.mHorizontal =
     nsLayoutUtils::FindIDForScrollableFrame(horizontal);
   mData.mScrollTargets.mVertical =
     nsLayoutUtils::FindIDForScrollableFrame(vertical);
 
-  FT_LOG("Creating scroll target with seq=%" PRIu64 ", kl=%d, h=%" PRIu64 ", v=%" PRIu64 "\n",
+  FT_LOG("Creating scroll target with seq=%" PRIu64 ", kl=%d, pl=%d, h=%" PRIu64 ", v=%" PRIu64 "\n",
          aFocusSequenceNumber,
          mFocusHasKeyEventListeners,
+         mHasPointerMovementEventListeners,
          mData.mScrollTargets.mHorizontal,
          mData.mScrollTargets.mVertical);
 }
 
 bool
 FocusTarget::operator==(const FocusTarget& aRhs) const
 {
   if (mSequenceNumber != aRhs.mSequenceNumber ||
       mFocusHasKeyEventListeners != aRhs.mFocusHasKeyEventListeners ||
+      mHasPointerMovementEventListeners != aRhs.mHasPointerMovementEventListeners ||
       mType != aRhs.mType) {
     return false;
   }
 
   if (mType == FocusTarget::eRefLayer) {
       return mData.mRefLayerId == aRhs.mData.mRefLayerId;
   } else if (mType == FocusTarget::eScrollLayer) {
       return mData.mScrollTargets.mHorizontal == aRhs.mData.mScrollTargets.mHorizontal &&
--- a/gfx/layers/apz/src/FocusTarget.h
+++ b/gfx/layers/apz/src/FocusTarget.h
@@ -56,16 +56,18 @@ public:
 
 public:
   // The content sequence number recorded at the time of this class's creation
   uint64_t mSequenceNumber;
 
   // Whether there are keydown, keypress, or keyup event listeners
   // in the event target chain of the focused element
   bool mFocusHasKeyEventListeners;
+  // Whether there are event listeners that are triggered by pointer movement
+  bool mHasPointerMovementEventListeners;
 
   FocusTargetType mType;
   FocusTargetData mData;
 };
 
 } // namespace layers
 } // namespace mozilla
 
--- a/gfx/layers/ipc/LayersMessageUtils.h
+++ b/gfx/layers/ipc/LayersMessageUtils.h
@@ -451,28 +451,30 @@ template <>
 struct ParamTraits<mozilla::layers::FocusTarget>
 {
   typedef mozilla::layers::FocusTarget paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg, aParam.mSequenceNumber);
     WriteParam(aMsg, aParam.mFocusHasKeyEventListeners);
+    WriteParam(aMsg, aParam.mHasPointerMovementEventListeners);
     WriteParam(aMsg, aParam.mType);
     if (aParam.mType == mozilla::layers::FocusTarget::eRefLayer) {
       WriteParam(aMsg, aParam.mData.mRefLayerId);
     } else if (aParam.mType == mozilla::layers::FocusTarget::eScrollLayer) {
       WriteParam(aMsg, aParam.mData.mScrollTargets);
     }
   }
 
   static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
   {
     if (!ReadParam(aMsg, aIter, &aResult->mSequenceNumber) ||
         !ReadParam(aMsg, aIter, &aResult->mFocusHasKeyEventListeners) ||
+        !ReadParam(aMsg, aIter, &aResult->mHasPointerMovementEventListeners) ||
         !ReadParam(aMsg, aIter, &aResult->mType)) {
       return false;
     }
 
     if (aResult->mType == mozilla::layers::FocusTarget::eRefLayer) {
       return ReadParam(aMsg, aIter, &aResult->mData.mRefLayerId);
     } else if (aResult->mType == mozilla::layers::FocusTarget::eScrollLayer) {
       return ReadParam(aMsg, aIter, &aResult->mData.mScrollTargets);