Bug 1316251 Part3: Trigger pointer event implementation in EventStateManager. r=masayuki
authorStone Shih <sshih@mozilla.com>
Fri, 08 Sep 2017 11:01:22 +0800
changeset 383624 5b0935d488462ccbc30e58a0140ad28743a1e18e
parent 383623 c60d8a8d46dfa4e4489370474ef1220ec41c8e4b
child 383625 0c8a91346605eafd3d28572f8430ed7559f25731
push id95601
push usersshih@mozilla.com
push dateFri, 29 Sep 2017 02:39:02 +0000
treeherdermozilla-inbound@5b0935d48846 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmasayuki
bugs1316251
milestone58.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1316251 Part3: Trigger pointer event implementation in EventStateManager. r=masayuki To manage pointer event "state" in ESM. MozReview-Commit-ID: HiAwvSmVTwx
dom/events/EventStateManager.cpp
dom/events/EventStateManager.h
dom/events/PointerEventHandler.cpp
dom/events/PointerEventHandler.h
layout/base/PresShell.cpp
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -611,16 +611,18 @@ EventStateManager::PreHandleEvent(nsPres
     if (touchEvent->mMessage == eTouchMove) {
       GenerateDragGesture(aPresContext, touchEvent);
     } else {
       mInTouchDrag = false;
       StopTrackingDragGesture();
     }
   }
 
+  PointerEventHandler::UpdateActivePointerState(mouseEvent);
+
   switch (aEvent->mMessage) {
   case eContextMenu:
     if (sIsPointerLocked) {
       return NS_ERROR_DOM_INVALID_STATE_ERR;
     }
     break;
   case eMouseTouchDrag:
     mInTouchDrag = true;
@@ -705,16 +707,20 @@ EventStateManager::PreHandleEvent(nsPres
       GenerateMouseEnterExit(mouseEvent);
       //This is a window level mouse exit event and should stop here
       aEvent->mMessage = eVoidEvent;
       break;
     }
     MOZ_FALLTHROUGH;
   case eMouseMove:
   case ePointerDown:
+    if (aEvent->mMessage == ePointerDown) {
+      PointerEventHandler::ImplicitlyCapturePointer(aTargetFrame, aEvent);
+    }
+    MOZ_FALLTHROUGH;
   case ePointerMove: {
     // on the Mac, GenerateDragGesture() may not return until the drag
     // has completed and so |aTargetFrame| may have been deleted (moving
     // a bookmark, for example).  If this is the case, however, we know
     // that ClearFrameRefs() has been called and it cleared out
     // |mCurrentTarget|. As a result, we should pass |mCurrentTarget|
     // into UpdateCursor().
     GenerateDragGesture(aPresContext, mouseEvent);
@@ -3192,25 +3198,38 @@ EventStateManager::PostHandleEvent(nsPre
             }
           }
         }
       }
       SetActiveManager(this, activeContent);
     }
     break;
   case ePointerCancel: {
-    if(WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent()) {
-      GenerateMouseEnterExit(mouseEvent);
+    if(WidgetMouseEvent* pointerEvent = aEvent->AsPointerEvent()) {
+      // Implicitly releasing capture for given pointer. ePointerLostCapture
+      // should be send after ePointerUp or ePointerCancel.
+      PointerEventHandler::ImplicitlyReleasePointerCapture(pointerEvent);
+      GenerateMouseEnterExit(pointerEvent);
+      // After UP/Cancel Touch pointers become invalid so we can remove relevant
+      // helper from Table. Mouse/Pen pointers are valid all the time (not only
+      // between down/up)
+      if (pointerEvent->inputSource == nsIDOMMouseEvent::MOZ_SOURCE_TOUCH) {
+        mPointersEnterLeaveHelper.Remove(pointerEvent->pointerId);
+        GenerateMouseEnterExit(pointerEvent);
+      }
     }
-    // After firing the pointercancel event, a user agent must also fire a
-    // pointerout event followed by a pointerleave event.
-    MOZ_FALLTHROUGH;
+    break;
   }
   case ePointerUp: {
     WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent();
+
+    // Implicitly releasing capture for given pointer. ePointerLostCapture
+    // should be send after ePointerUp or ePointerCancel.
+    PointerEventHandler::ImplicitlyReleasePointerCapture(pointerEvent);
+
     // After UP/Cancel Touch pointers become invalid so we can remove relevant helper from Table
     // Mouse/Pen pointers are valid all the time (not only between down/up)
     if (pointerEvent->inputSource == nsIDOMMouseEvent::MOZ_SOURCE_TOUCH) {
       mPointersEnterLeaveHelper.Remove(pointerEvent->pointerId);
       GenerateMouseEnterExit(pointerEvent);
     }
     break;
   }
@@ -5056,17 +5075,19 @@ EventStateManager::ResetLastOverForConte
   if (aElemWrapper && aElemWrapper->mLastOverElement &&
       nsContentUtils::ContentIsDescendantOf(aElemWrapper->mLastOverElement,
                                             aContent)) {
     aElemWrapper->mLastOverElement = nullptr;
   }
 }
 
 void
-EventStateManager::ContentRemoved(nsIDocument* aDocument, nsIContent* aContent)
+EventStateManager::ContentRemoved(nsIDocument* aDocument,
+                                  nsIContent* aMaybeContainer,
+                                  nsIContent* aContent)
 {
   /*
    * Anchor and area elements when focused or hovered might make the UI to show
    * the current link. We want to make sure that the UI gets informed when they
    * are actually removed from the DOM.
    */
   if (aContent->IsAnyOfHTMLElements(nsGkAtoms::a, nsGkAtoms::area) &&
       (aContent->AsElement()->State().HasAtLeastOneOfStates(NS_EVENT_STATE_FOCUS |
@@ -5099,16 +5120,18 @@ EventStateManager::ContentRemoved(nsIDoc
   }
 
   if (sDragOverContent &&
       sDragOverContent->OwnerDoc() == aContent->OwnerDoc() &&
       nsContentUtils::ContentIsDescendantOf(sDragOverContent, aContent)) {
     sDragOverContent = nullptr;
   }
 
+  PointerEventHandler::ReleaseIfCaptureByDescendant(aContent);
+
   // See bug 292146 for why we want to null this out
   ResetLastOverForContent(0, mMouseEnterLeaveHelper, aContent);
   for (auto iter = mPointersEnterLeaveHelper.Iter();
        !iter.Done();
        iter.Next()) {
     ResetLastOverForContent(iter.Key(), iter.Data(), aContent);
   }
 }
--- a/dom/events/EventStateManager.h
+++ b/dom/events/EventStateManager.h
@@ -135,17 +135,19 @@ public:
    * @return  Whether the content was able to change all states. Returns false
    *                  if a resulting DOM event causes the content node passed in
    *                  to not change states. Note, the frame for the content may
    *                  change as a result of the content state change, because of
    *                  frame reconstructions that may occur, but this does not
    *                  affect the return value.
    */
   bool SetContentState(nsIContent* aContent, EventStates aState);
-  void ContentRemoved(nsIDocument* aDocument, nsIContent* aContent);
+  void ContentRemoved(nsIDocument* aDocument, nsIContent* aMaybeContainer,
+                      nsIContent* aContent);
+
   bool EventStatusOK(WidgetGUIEvent* aEvent);
 
   /**
    * EventStateManager stores IMEContentObserver while it's observing contents.
    * Following mehtods are called by IMEContentObserver when it starts to
    * observe or stops observing the content.
    */
   void OnStartToObserveContent(IMEContentObserver* aIMEContentObserver);
--- a/dom/events/PointerEventHandler.cpp
+++ b/dom/events/PointerEventHandler.cpp
@@ -84,29 +84,26 @@ PointerEventHandler::IsPointerEventEnabl
 
 /* static */ bool
 PointerEventHandler::IsPointerEventImplicitCaptureForTouchEnabled()
 {
   return sPointerEventEnabled && sPointerEventImplicitCapture;
 }
 
 /* static */ void
-PointerEventHandler::UpdateActivePointerState(WidgetGUIEvent* aEvent)
+PointerEventHandler::UpdateActivePointerState(WidgetMouseEvent* aEvent)
 {
-  if (!IsPointerEventEnabled()) {
+  if (!IsPointerEventEnabled() || !aEvent) {
     return;
   }
   switch (aEvent->mMessage) {
   case eMouseEnterIntoWidget:
     // In this case we have to know information about available mouse pointers
-    if (WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent()) {
-      sActivePointersIds->Put(mouseEvent->pointerId,
-                              new PointerInfo(false, mouseEvent->inputSource,
-                                              true));
-    }
+    sActivePointersIds->Put(aEvent->pointerId,
+                            new PointerInfo(false, aEvent->inputSource, true));
     break;
   case ePointerDown:
     // In this case we switch pointer to active state
     if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) {
       sActivePointersIds->Put(pointerEvent->pointerId,
                               new PointerInfo(true, pointerEvent->inputSource,
                                               pointerEvent->mIsPrimary));
     }
@@ -122,19 +119,17 @@ PointerEventHandler::UpdateActivePointer
       } else {
         sActivePointersIds->Remove(pointerEvent->pointerId);
       }
     }
     break;
   case eMouseExitFromWidget:
     // In this case we have to remove information about disappeared mouse
     // pointers
-    if (WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent()) {
-      sActivePointersIds->Remove(mouseEvent->pointerId);
-    }
+    sActivePointersIds->Remove(aEvent->pointerId);
     break;
   default:
     break;
   }
 }
 
 /* static */ void
 PointerEventHandler::SetPointerCaptureById(uint32_t aPointerId,
@@ -213,16 +208,55 @@ PointerEventHandler::CheckPointerCapture
 
     captureInfo->mOverrideContent = pendingContent;
     if (captureInfo->Empty()) {
       sPointerCaptureList->Remove(aEvent->pointerId);
     }
   }
 }
 
+/* static */ void
+PointerEventHandler::ImplicitlyCapturePointer(nsIFrame* aFrame,
+                                              WidgetEvent* aEvent)
+{
+  MOZ_ASSERT(aEvent->mMessage == ePointerDown);
+  if (!aFrame || !IsPointerEventEnabled() ||
+      !IsPointerEventImplicitCaptureForTouchEnabled()) {
+    return;
+  }
+  WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent();
+  NS_WARNING_ASSERTION(pointerEvent,
+                       "Call ImplicitlyCapturePointer with non-pointer event");
+  if (pointerEvent->inputSource != nsIDOMMouseEvent::MOZ_SOURCE_TOUCH) {
+    // We only implicitly capture the pointer for touch device.
+    return;
+  }
+  nsCOMPtr<nsIContent> target;
+  aFrame->GetContentForEvent(aEvent, getter_AddRefs(target));
+  while (target && !target->IsElement()) {
+    target = target->GetParent();
+  }
+  if (NS_WARN_IF(!target)) {
+    return;
+  }
+  SetPointerCaptureById(pointerEvent->pointerId, target);
+}
+
+/* static */ void
+PointerEventHandler::ImplicitlyReleasePointerCapture(WidgetEvent* aEvent)
+{
+  MOZ_ASSERT(aEvent);
+  if (aEvent->mMessage != ePointerUp && aEvent->mMessage != ePointerCancel) {
+    return;
+  }
+  WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent();
+  ReleasePointerCaptureById(pointerEvent->pointerId);
+  CheckPointerCaptureState(pointerEvent);
+}
+
 /* static */ nsIContent*
 PointerEventHandler::GetPointerCapturingContent(uint32_t aPointerId)
 {
   PointerCaptureInfo* pointerCaptureInfo = GetPointerCaptureInfo(aPointerId);
   if (pointerCaptureInfo) {
     return pointerCaptureInfo->mOverrideContent;
   }
   return nullptr;
--- a/dom/events/PointerEventHandler.h
+++ b/dom/events/PointerEventHandler.h
@@ -54,17 +54,17 @@ public:
   // Return the preference value of pointer event enabled.
   static bool IsPointerEventEnabled();
 
   // Return the preference value of implicit capture.
   static bool IsPointerEventImplicitCaptureForTouchEnabled();
 
   // Called in ESM::PreHandleEvent to update current active pointers in a hash
   // table.
-  static void UpdateActivePointerState(WidgetGUIEvent* aEvent);
+  static void UpdateActivePointerState(WidgetMouseEvent* aEvent);
 
   // Got/release pointer capture of the specified pointer by the content.
   static void SetPointerCaptureById(uint32_t aPointerId, nsIContent* aContent);
   static void ReleasePointerCaptureById(uint32_t aPointerId);
 
   // Get the pointer captured info of the specified pointer.
   static PointerCaptureInfo* GetPointerCaptureInfo(uint32_t aPointerId);
 
@@ -73,16 +73,20 @@ public:
   // aActiveState is additional information, which shows state of pointer like
   // button state for mouse.
   static bool GetPointerInfo(uint32_t aPointerId, bool& aActiveState);
 
   // CheckPointerCaptureState checks cases, when got/lostpointercapture events
   // should be fired.
   static void CheckPointerCaptureState(WidgetPointerEvent* aEvent);
 
+  // Implicitly get and release capture of current pointer for touch.
+  static void ImplicitlyCapturePointer(nsIFrame* aFrame, WidgetEvent* aEvent);
+  static void ImplicitlyReleasePointerCapture(WidgetEvent* aEvent);
+
   /**
    * GetPointerCapturingFrame returns a target frame of aEvent. If the event is
    * a mouse or pointer event (except mousedown and pointerdown), the pointer
    * may be captured by a content. This method returns the capturing content's
    * primary frame. Otherwise, aFrameUnderCursor.
    *
    * @param aFrameUnderCursor    A frame under cursor.
    * @param aEvent               A mouse event or pointer event which may be
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -4433,17 +4433,18 @@ PresShell::ContentRemoved(nsIDocument *a
   // Notify the ESM that the content has been removed, so that
   // it can clean up any state related to the content.
 
   // XXX_jwir3: There is no null check for aDocument necessary, since, even
   //            though by nsIMutationObserver, aDocument could be null, the
   //            precondition check that mDocument == aDocument ensures that
   //            aDocument will not be null (since mDocument can't be null unless
   //            we're still intializing).
-  mPresContext->EventStateManager()->ContentRemoved(aDocument, aChild);
+  mPresContext->EventStateManager()
+    ->ContentRemoved(aDocument, aMaybeContainer, aChild);
 
   nsAutoCauseReflowNotifier crNotifier(this);
 
   // Call this here so it only happens for real content mutations and
   // not cases when the frame constructor calls its own methods to force
   // frame reconstruction.
   nsIContent* oldNextSibling = container->GetChildAt(aIndexInContainer);
 
@@ -4452,18 +4453,16 @@ PresShell::ContentRemoved(nsIDocument *a
   // After removing aChild from tree we should save information about live ancestor
   if (mPointerEventTarget) {
     MOZ_ASSERT(PointerEventHandler::IsPointerEventEnabled());
     if (nsContentUtils::ContentIsDescendantOf(mPointerEventTarget, aChild)) {
       mPointerEventTarget = aMaybeContainer;
     }
   }
 
-  PointerEventHandler::ReleaseIfCaptureByDescendant(aChild);
-
   mFrameConstructor->ContentRemoved(aMaybeContainer, aChild, oldNextSibling,
                                     nsCSSFrameConstructor::REMOVE_CONTENT);
 
   if (aChild->NodeType() == nsIDOMNode::DOCUMENT_TYPE_NODE) {
     MOZ_ASSERT(container == aDocument);
     NotifyFontSizeInflationEnabledIsDirty();
   }
 
@@ -6836,18 +6835,16 @@ PresShell::HandleEvent(nsIFrame* aFrame,
         // If the event is consumed, cancel APZC panning by setting
         // mMultipleActionsPrevented.
         aEvent->mFlags.mMultipleActionsPrevented = true;
         return NS_OK;
       }
     }
   }
 
-  PointerEventHandler::UpdateActivePointerState(aEvent);
-
   if (!nsContentUtils::IsSafeToRunScript() &&
       aEvent->IsAllowedToDispatchDOMEvent()) {
     if (aEvent->mClass == eCompositionEventClass) {
       IMEStateManager::OnCompositionEventDiscarded(
         aEvent->AsCompositionEvent());
     }
 #ifdef DEBUG
     if (aEvent->IsIMERelatedEvent()) {
@@ -7176,42 +7173,25 @@ PresShell::HandleEvent(nsIFrame* aFrame,
       NS_ASSERTION(capturingContent->GetComposedDoc() == GetDocument(),
                    "Unexpected document");
       nsIFrame* capturingFrame = capturingContent->GetPrimaryFrame();
       if (capturingFrame) {
         frame = capturingFrame;
       }
     }
 
-    if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) {
-      // Try to keep frame for following check, because
-      // frame can be damaged during CheckPointerCaptureState.
+    // Try to keep frame for following check, because frame can be damaged
+    // during CheckPointerCaptureState.
+    {
       AutoWeakFrame frameKeeper(frame);
-      // Handle pending pointer capture before any pointer events except
-      // gotpointercapture / lostpointercapture.
-      PointerEventHandler::CheckPointerCaptureState(pointerEvent);
+      PointerEventHandler::CheckPointerCaptureState(aEvent->AsPointerEvent());
       // Prevent application crashes, in case damaged frame.
       if (!frameKeeper.IsAlive()) {
         frame = nullptr;
       }
-      // Implicit pointer capture for touch
-      if (frame &&
-          PointerEventHandler::IsPointerEventImplicitCaptureForTouchEnabled() &&
-          pointerEvent->mMessage == ePointerDown &&
-          pointerEvent->inputSource == nsIDOMMouseEvent::MOZ_SOURCE_TOUCH) {
-        nsCOMPtr<nsIContent> targetContent;
-        frame->GetContentForEvent(aEvent, getter_AddRefs(targetContent));
-        while (targetContent && !targetContent->IsElement()) {
-          targetContent = targetContent->GetParent();
-        }
-        if (targetContent) {
-          PointerEventHandler::SetPointerCaptureById(pointerEvent->pointerId,
-                                                     targetContent);
-        }
-      }
     }
 
     frame = PointerEventHandler::GetPointerCapturingFrame(frame, aEvent);
 
     // Suppress mouse event if it's being targeted at an element inside
     // a document which needs events suppressed
     if (aEvent->mClass == eMouseEventClass &&
         frame->PresContext()->Document()->EventHandlingSuppressed()) {