Bug 1316251 Part2: Revise pointer event implementation. r?masayuki draft
authorStone Shih <sshih@mozilla.com>
Wed, 06 Sep 2017 17:28:28 +0800
changeset 669757 6ea3756cd98f4ec70040ad9d819a635c711629e9
parent 669756 7e2e98137c24ba962bc133d0a99704b287ede9b8
child 669758 18dca2d6a89d8512481e29cdddbfd5c05bb10f2d
push id81424
push usersshih@mozilla.com
push dateMon, 25 Sep 2017 11:35:25 +0000
reviewersmasayuki
bugs1316251
milestone58.0a1
Bug 1316251 Part2: Revise pointer event implementation. r?masayuki Revise the pointer event implementation, includes - Revise the implementation to fetch preference values. - Merge the logic to generate pointerout and pointerleave when handling pointercancel and pointerup for touch device. - Separate the logic to get the pointer capturing frame to PointerEventHandler. MozReview-Commit-ID: 7pdAr0XFNT2
dom/events/EventStateManager.cpp
dom/events/PointerEventHandler.cpp
dom/events/PointerEventHandler.h
layout/base/PresShell.cpp
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -257,17 +257,16 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 /******************************************************************/
 /* mozilla::EventStateManager                                     */
 /******************************************************************/
 
 static uint32_t sESMInstanceCount = 0;
-static bool sPointerEventEnabled = false;
 
 uint64_t EventStateManager::sUserInputCounter = 0;
 int32_t EventStateManager::sUserInputEventDepth = 0;
 bool EventStateManager::sNormalLMouseEventInProcess = false;
 EventStateManager* EventStateManager::sActiveESM = nullptr;
 nsIDocument* EventStateManager::sMouseOverDocument = nullptr;
 AutoWeakFrame EventStateManager::sLastDragOverFrame = nullptr;
 LayoutDeviceIntPoint EventStateManager::sPreLockPoint = LayoutDeviceIntPoint(0, 0);
@@ -305,23 +304,16 @@ EventStateManager::EventStateManager()
 {
   if (sESMInstanceCount == 0) {
     gUserInteractionTimerCallback = new UITimerCallback();
     if (gUserInteractionTimerCallback)
       NS_ADDREF(gUserInteractionTimerCallback);
     UpdateUserActivityTimer();
   }
   ++sESMInstanceCount;
-
-  static bool sAddedPointerEventEnabled = false;
-  if (!sAddedPointerEventEnabled) {
-    Preferences::AddBoolVarCache(&sPointerEventEnabled,
-                                 "dom.w3c_pointer_events.enabled", false);
-    sAddedPointerEventEnabled = true;
-  }
   WheelTransaction::InitializeStatics();
 }
 
 nsresult
 EventStateManager::UpdateUserActivityTimer()
 {
   if (!gUserInteractionTimerCallback)
     return NS_OK;
@@ -703,20 +695,18 @@ EventStateManager::PreHandleEvent(nsPres
     if (mouseEvent->mExitFrom != WidgetMouseEvent::eTopLevel) {
       // Treat it as a synthetic move so we don't generate spurious
       // "exit" or "move" events.  Any necessary "out" or "over" events
       // will be generated by GenerateMouseEnterExit
       mouseEvent->mMessage = eMouseMove;
       mouseEvent->mReason = WidgetMouseEvent::eSynthesized;
       // then fall through...
     } else {
-      if (sPointerEventEnabled) {
-        // We should synthetize corresponding pointer events
-        GeneratePointerEnterExit(ePointerLeave, mouseEvent);
-      }
+      // We should synthetize corresponding pointer events
+      GeneratePointerEnterExit(ePointerLeave, mouseEvent);
       GenerateMouseEnterExit(mouseEvent);
       //This is a window level mouse exit event and should stop here
       aEvent->mMessage = eVoidEvent;
       break;
     }
     MOZ_FALLTHROUGH;
   case eMouseMove:
   case ePointerDown:
@@ -3201,31 +3191,28 @@ EventStateManager::PostHandleEvent(nsPre
               fm->SetFocusedWindow(mDocument->GetWindow());
             }
           }
         }
       }
       SetActiveManager(this, activeContent);
     }
     break;
-  case ePointerCancel: {
-    if(WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent()) {
-      GenerateMouseEnterExit(mouseEvent);
-    }
-    // After firing the pointercancel event, a user agent must also fire a
-    // pointerout event followed by a pointerleave event.
-    MOZ_FALLTHROUGH;
-  }
+  case ePointerCancel:
   case ePointerUp: {
     WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent();
-    // 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) {
+      // After firing the pointercancel event, a user agent must also fire a
+      // pointerout event followed by a pointerleave event.
+      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)
       mPointersEnterLeaveHelper.Remove(pointerEvent->pointerId);
-      GenerateMouseEnterExit(pointerEvent);
     }
     break;
   }
   case eMouseUp:
     {
       ClearGlobalActiveContent(this);
       WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
       if (mouseEvent && mouseEvent->IsReal()) {
@@ -4258,16 +4245,19 @@ GetWindowClientRectCenter(nsIWidget* aWi
   point.y = point.y / round * round;
   return point - aWidget->WidgetToScreenOffset();
 }
 
 void
 EventStateManager::GeneratePointerEnterExit(EventMessage aMessage,
                                             WidgetMouseEvent* aEvent)
 {
+  if (!PointerEventHandler::IsPointerEventEnabled()) {
+    return;
+  }
   WidgetPointerEvent pointerEvent(*aEvent);
   pointerEvent.mMessage = aMessage;
   GenerateMouseEnterExit(&pointerEvent);
 }
 
 void
 EventStateManager::GenerateMouseEnterExit(WidgetMouseEvent* aMouseEvent)
 {
--- a/dom/events/PointerEventHandler.cpp
+++ b/dom/events/PointerEventHandler.cpp
@@ -41,29 +41,25 @@ static nsClassHashtable<nsUint32HashKey,
 
 // Keeps information about pointers such as pointerId, activeState, pointerType,
 // primaryState
 static nsClassHashtable<nsUint32HashKey, PointerInfo>* sActivePointersIds;
 
 /* static */ void
 PointerEventHandler::Initialize()
 {
-  static bool addedPointerEventEnabled = false;
-  if (!addedPointerEventEnabled) {
-    Preferences::AddBoolVarCache(&sPointerEventEnabled,
-                                 "dom.w3c_pointer_events.enabled", true);
-    addedPointerEventEnabled = true;
+  static bool initialized = false;
+  if (initialized) {
+    return;
   }
-  static bool addedPointerEventImplicitCapture = false;
-  if (!addedPointerEventImplicitCapture) {
-    Preferences::AddBoolVarCache(&sPointerEventImplicitCapture,
-                                 "dom.w3c_pointer_events.implicit_capture",
-                                 true);
-    addedPointerEventImplicitCapture = true;
-  }
+  initialized = true;
+  Preferences::AddBoolVarCache(&sPointerEventEnabled,
+                               "dom.w3c_pointer_events.enabled", true);
+  Preferences::AddBoolVarCache(&sPointerEventImplicitCapture,
+                               "dom.w3c_pointer_events.implicit_capture", true);
 }
 
 /* static */ void
 PointerEventHandler::InitializeStatics()
 {
   MOZ_ASSERT(!sPointerCaptureList, "InitializeStatics called multiple times!");
   sPointerCaptureList =
     new nsClassHashtable<nsUint32HashKey, PointerCaptureInfo>;
@@ -227,16 +223,45 @@ PointerEventHandler::GetPointerCapturing
 {
   PointerCaptureInfo* pointerCaptureInfo = GetPointerCaptureInfo(aPointerId);
   if (pointerCaptureInfo) {
     return pointerCaptureInfo->mOverrideContent;
   }
   return nullptr;
 }
 
+/* static */ nsIFrame*
+PointerEventHandler::GetPointerCapturingFrame(nsIFrame* aFrameUnderCursor,
+                                              WidgetGUIEvent* aEvent)
+{
+  if (!IsPointerEventEnabled() || (aEvent->mClass != ePointerEventClass &&
+                                   aEvent->mClass != eMouseEventClass) ||
+      (aEvent->mMessage == ePointerDown && aEvent->mMessage == eMouseDown)) {
+    // Pointer capture should only be applied to all pointer events and mouse
+    // events except ePointerDown and eMouseDown;
+    return aFrameUnderCursor;
+  }
+
+  WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
+  if (!mouseEvent) {
+    return aFrameUnderCursor;
+  }
+
+  // Find the content which captures the pointer.
+  nsIContent* capturingContent =
+    GetPointerCapturingContent(mouseEvent->pointerId);
+
+  if (!capturingContent) {
+    return aFrameUnderCursor;
+  }
+  // Return the content's primary frame as the target frame.
+  nsIFrame* capturingFrame = capturingContent->GetPrimaryFrame();
+  return capturingFrame ? capturingFrame : aFrameUnderCursor;
+}
+
 /* static */ void
 PointerEventHandler::ReleaseIfCaptureByDescendant(nsIContent* aContent)
 {
   // We should check that aChild does not contain pointer capturing elements.
   // If it does we should release the pointer capture for the elements.
   for (auto iter = sPointerCaptureList->Iter(); !iter.Done(); iter.Next()) {
     PointerCaptureInfo* data = iter.UserData();
     if (data && data->mPendingContent &&
@@ -297,51 +322,55 @@ PointerEventHandler::PostHandlePointerEv
   // PreventDefault only applied for active pointers.
   if (!pointerInfo->mActiveState) {
     return;
   }
   aMouseOrTouchEvent->PreventDefault(false);
   pointerInfo->mPreventMouseEventByContent = true;
 }
 
-/* static */ nsresult
+/* static */ void
 PointerEventHandler::DispatchPointerFromMouseOrTouch(
                        PresShell* aShell,
                        nsIFrame* aFrame,
                        WidgetGUIEvent* aEvent,
                        bool aDontRetargetEvents,
                        nsEventStatus* aStatus,
                        nsIContent** aTargetContent)
 {
+  MOZ_ASSERT(IsPointerEventEnabled());
+  MOZ_ASSERT(aFrame);
+  MOZ_ASSERT(aEvent);
+
   EventMessage pointerMessage = eVoidEvent;
   if (aEvent->mClass == eMouseEventClass) {
     WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
     // 1. If it is not mouse then it is likely will come as touch event
     // 2. We don't synthesize pointer events for those events that are not
     //    dispatched to DOM.
     if (!mouseEvent->convertToPointer ||
         !aEvent->IsAllowedToDispatchDOMEvent()) {
-      return NS_OK;
+      return;
     }
     int16_t button = mouseEvent->button;
     switch (mouseEvent->mMessage) {
     case eMouseMove:
       button = WidgetMouseEvent::eNoButton;
       pointerMessage = ePointerMove;
       break;
     case eMouseUp:
       pointerMessage = mouseEvent->buttons ? ePointerMove : ePointerUp;
       break;
     case eMouseDown:
       pointerMessage =
         mouseEvent->buttons & ~nsContentUtils::GetButtonsFlagForButton(button) ?
         ePointerMove : ePointerDown;
       break;
     default:
-      return NS_OK;
+      return;
     }
 
     WidgetPointerEvent event(*mouseEvent);
     event.pointerId = mouseEvent->pointerId;
     event.inputSource = mouseEvent->inputSource;
     event.mMessage = pointerMessage;
     event.button = button;
     event.buttons = mouseEvent->buttons;
@@ -372,17 +401,17 @@ PointerEventHandler::DispatchPointerFrom
     case eTouchStart:
       pointerMessage = ePointerDown;
       break;
     case eTouchCancel:
     case eTouchPointerCancel:
       pointerMessage = ePointerCancel;
       break;
     default:
-      return NS_OK;
+      return;
     }
 
     RefPtr<PresShell> shell(aShell);
     for (uint32_t i = 0; i < touchEvent->mTouches.Length(); ++i) {
       Touch* touch = touchEvent->mTouches[i];
       if (!TouchManager::ShouldConvertTouchToPointer(touch, touchEvent)) {
         continue;
       }
@@ -405,17 +434,16 @@ PointerEventHandler::DispatchPointerFrom
       event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
       event.convertToPointer = touch->convertToPointer = false;
       PreHandlePointerEventsPreventDefault(&event, aEvent);
       shell->HandleEvent(aFrame, &event, aDontRetargetEvents, aStatus,
                          aTargetContent);
       PostHandlePointerEventsPreventDefault(&event, aEvent);
     }
   }
-  return NS_OK;
 }
 
 /* static */ uint16_t
 PointerEventHandler::GetPointerType(uint32_t aPointerId)
 {
   PointerInfo* pointerInfo = nullptr;
   if (sActivePointersIds->Get(aPointerId, &pointerInfo) && pointerInfo) {
     return pointerInfo->mPointerType;
--- a/dom/events/PointerEventHandler.h
+++ b/dom/events/PointerEventHandler.h
@@ -73,16 +73,29 @@ 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);
 
+  /**
+   * 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.
+   * @return                     Target frame for aEvent.
+   */
+  static nsIFrame* GetPointerCapturingFrame(nsIFrame* aFrameUnderCursor,
+                                            WidgetGUIEvent* aEvent);
+
   static nsIContent* GetPointerCapturingContent(uint32_t aPointerId);
 
   // Release pointer capture if captured by the specified content or it's
   // descendant. This is called to handle the case that the pointer capturing
   // content or it's parent is removed from the document.
   static void ReleaseIfCaptureByDescendant(nsIContent* aContent);
 
   /*
@@ -111,22 +124,22 @@ public:
    * We add mPreventMouseEventByContent flag in PointerInfo to represent the
    * active pointer won't firing compatible mouse events. It's set to true when
    * content preventDefault on pointerdown
    */
   static void PostHandlePointerEventsPreventDefault(
                 WidgetPointerEvent* aPointerEvent,
                 WidgetGUIEvent* aMouseOrTouchEvent);
 
-  static nsresult DispatchPointerFromMouseOrTouch(PresShell* aShell,
-                                                  nsIFrame* aFrame,
-                                                  WidgetGUIEvent* aEvent,
-                                                  bool aDontRetargetEvents,
-                                                  nsEventStatus* aStatus,
-                                                  nsIContent** aTargetContent);
+  static void DispatchPointerFromMouseOrTouch(PresShell* aShell,
+                                              nsIFrame* aFrame,
+                                              WidgetGUIEvent* aEvent,
+                                              bool aDontRetargetEvents,
+                                              nsEventStatus* aStatus,
+                                              nsIContent** aTargetContent);
 
 private:
   // GetPointerType returns pointer type like mouse, pen or touch for pointer
   // event with pointerId. The return value must be one of
   // nsIDOMMouseEvent::MOZ_SOURCE_*
   static uint16_t GetPointerType(uint32_t aPointerId);
 
   // GetPointerPrimaryState returns state of attribute isPrimary for pointer
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -4446,16 +4446,17 @@ PresShell::ContentRemoved(nsIDocument *a
   // not cases when the frame constructor calls its own methods to force
   // frame reconstruction.
   nsIContent* oldNextSibling = container->GetChildAt(aIndexInContainer);
 
   mPresContext->RestyleManager()->ContentRemoved(container, aChild, oldNextSibling);
 
   // 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,
@@ -7203,33 +7204,17 @@ PresShell::HandleEvent(nsIFrame* aFrame,
         }
         if (targetContent) {
           PointerEventHandler::SetPointerCaptureById(pointerEvent->pointerId,
                                                      targetContent);
         }
       }
     }
 
-    // Mouse events should be fired to the same target as their mapped pointer
-    // events
-    if ((aEvent->mClass == ePointerEventClass ||
-         aEvent->mClass == eMouseEventClass) &&
-        aEvent->mMessage != ePointerDown && aEvent->mMessage != eMouseDown) {
-      if (WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent()) {
-        uint32_t pointerId = mouseEvent->pointerId;
-        nsIContent* pointerCapturingContent =
-          PointerEventHandler::GetPointerCapturingContent(pointerId);
-
-        if (pointerCapturingContent) {
-          if (nsIFrame* capturingFrame = pointerCapturingContent->GetPrimaryFrame()) {
-            frame = capturingFrame;
-          }
-        }
-      }
-    }
+    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()) {
       if (aEvent->mMessage == eMouseDown) {
         mNoDelayedMouseEvents = true;
       } else if (!mNoDelayedMouseEvents && (aEvent->mMessage == eMouseUp ||
@@ -7315,16 +7300,17 @@ PresShell::HandleEvent(nsIFrame* aFrame,
         }
       }
     }
 
     // Before HandlePositionedEvent we should save mPointerEventTarget in some
     // cases
     AutoWeakFrame weakFrame;
     if (aTargetContent && ePointerEventClass == aEvent->mClass) {
+      MOZ_ASSERT(PointerEventHandler::IsPointerEventEnabled());
       weakFrame = frame;
       shell->mPointerEventTarget = frame->GetContent();
       MOZ_ASSERT(!frame->GetContent() ||
                  shell->GetDocument() == frame->GetContent()->OwnerDoc());
     }
 
     // Prevent deletion until we're done with event handling (bug 336582) and
     // swap mPointerEventTarget to *aTargetContent
@@ -7339,16 +7325,17 @@ PresShell::HandleEvent(nsIFrame* aFrame,
       rv = shell->HandlePositionedEvent(frame, aEvent, aEventStatus);
     } else {
       rv = HandlePositionedEvent(frame, aEvent, aEventStatus);
     }
 
     // After HandlePositionedEvent we should reestablish
     // content (which still live in tree) in some cases
     if (aTargetContent && ePointerEventClass == aEvent->mClass) {
+      MOZ_ASSERT(PointerEventHandler::IsPointerEventEnabled());
       if (!weakFrame.IsAlive()) {
         shell->mPointerEventTarget.swap(*aTargetContent);
       }
     }
 
     return rv;
   }