Bug 1316251 Part3: Trigger pointer event implementation in EventStateManager. r=masayuki draft
authorStone Shih <sshih@mozilla.com>
Fri, 08 Sep 2017 11:01:22 +0800
changeset 670218 e00149a21eeb6d912423babd33e143a4d27cf3a5
parent 670215 72c000a84a2722a2c7d6311685b37c63a4064110
child 733172 86d4600d16645d6064247b6d8b24e52df7bdd8ae
push id81562
push usersshih@mozilla.com
push dateTue, 26 Sep 2017 02:47:58 +0000
reviewersmasayuki
bugs1316251
milestone58.0a1
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, 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.
--- 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()) {