Bug 1461708 - part 2: Split EventStateManager::CheckForAndDispatchClick() r=smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Wed, 10 Oct 2018 12:01:57 +0000
changeset 488846 588859b2d5ccac8b371eed74dc751b2ec0b834cc
parent 488845 973ea169460670805ba5dbe8bc0051313a1a2979
child 488847 d61721276fcc478dd8f672dcd63749e775283d32
push id246
push userfmarier@mozilla.com
push dateSat, 13 Oct 2018 00:15:40 +0000
reviewerssmaug
bugs1461708
milestone64.0a1
Bug 1461708 - part 2: Split EventStateManager::CheckForAndDispatchClick() r=smaug This patch splits EventStateManager::CheckForAndDispatchClick(). One is for handling default action of eMouseUp, the other is for dispatching click events. This makes it easier to add other default actions after dispatching click events. Differential Revision: https://phabricator.services.mozilla.com/D7849
dom/events/EventStateManager.cpp
dom/events/EventStateManager.h
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -3373,27 +3373,24 @@ EventStateManager::PostHandleEvent(nsPre
       GenerateMouseEnterExit(pointerEvent);
       mPointersEnterLeaveHelper.Remove(pointerEvent->pointerId);
     }
     break;
   }
   case eMouseUp:
     {
       ClearGlobalActiveContent(this);
-      WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
-      if (mouseEvent && mouseEvent->IsReal()) {
-        if (!mCurrentTarget) {
-          GetEventTarget();
-        }
+      WidgetMouseEvent* mouseUpEvent = aEvent->AsMouseEvent();
+      if (mouseUpEvent && EventCausesClickEvents(*mouseUpEvent)) {
         // Make sure to dispatch the click even if there is no frame for
         // the current target element. This is required for Web compatibility.
         RefPtr<EventStateManager> esm =
           ESMFromContentOrThis(aOverrideClickTarget);
-        ret = esm->CheckForAndDispatchClick(mouseEvent, aStatus,
-                                            aOverrideClickTarget);
+        ret = esm->PostHandleMouseUp(mouseUpEvent, aStatus,
+                                     aOverrideClickTarget);
       }
 
       nsIPresShell *shell = presContext->GetPresShell();
       if (shell) {
         RefPtr<nsFrameSelection> frameSelection = shell->FrameSelection();
         frameSelection->SetDragState(false);
       }
     }
@@ -4950,120 +4947,159 @@ EventStateManager::SetClickCount(WidgetM
       mLastRightMouseDownContentParent = nullptr;
     }
     break;
   }
 
   return NS_OK;
 }
 
+// static
+bool
+EventStateManager::EventCausesClickEvents(const WidgetMouseEvent& aMouseEvent)
+{
+  if (NS_WARN_IF(aMouseEvent.mMessage != eMouseUp)) {
+    return false;
+  }
+  // If the mouseup event is synthesized event, we don't need to dispatch
+  // click events.
+  if (!aMouseEvent.IsReal()) {
+    return false;
+  }
+  // If mouse is still over same element, clickcount will be > 1.
+  // If it has moved it will be zero, so no click.
+  if (!aMouseEvent.mClickCount) {
+    return false;
+  }
+  // Check that the window isn't disabled before firing a click
+  // (see bug 366544).
+  return !(aMouseEvent.mWidget && !aMouseEvent.mWidget->IsEnabled());
+}
+
 nsresult
-EventStateManager::InitAndDispatchClickEvent(WidgetMouseEvent* aEvent,
+EventStateManager::InitAndDispatchClickEvent(WidgetMouseEvent* aMouseUpEvent,
                                              nsEventStatus* aStatus,
                                              EventMessage aMessage,
                                              nsIPresShell* aPresShell,
-                                             nsIContent* aMouseTarget,
+                                             nsIContent* aMouseUpContent,
                                              AutoWeakFrame aCurrentTarget,
                                              bool aNoContentDispatch,
                                              nsIContent* aOverrideClickTarget)
 {
-  WidgetMouseEvent event(aEvent->IsTrusted(), aMessage,
-                         aEvent->mWidget, WidgetMouseEvent::eReal);
-
-  event.mRefPoint = aEvent->mRefPoint;
-  event.mClickCount = aEvent->mClickCount;
-  event.mModifiers = aEvent->mModifiers;
-  event.buttons = aEvent->buttons;
-  event.mTime = aEvent->mTime;
-  event.mTimeStamp = aEvent->mTimeStamp;
+  MOZ_ASSERT(aMouseUpEvent);
+  MOZ_ASSERT(EventCausesClickEvents(*aMouseUpEvent));
+  MOZ_ASSERT(aMouseUpContent || aCurrentTarget || aOverrideClickTarget);
+
+  WidgetMouseEvent event(aMouseUpEvent->IsTrusted(), aMessage,
+                         aMouseUpEvent->mWidget, WidgetMouseEvent::eReal);
+
+  event.mRefPoint = aMouseUpEvent->mRefPoint;
+  event.mClickCount = aMouseUpEvent->mClickCount;
+  event.mModifiers = aMouseUpEvent->mModifiers;
+  event.buttons = aMouseUpEvent->buttons;
+  event.mTime = aMouseUpEvent->mTime;
+  event.mTimeStamp = aMouseUpEvent->mTimeStamp;
   event.mFlags.mNoContentDispatch = aNoContentDispatch;
-  event.button = aEvent->button;
-  event.pointerId = aEvent->pointerId;
-  event.inputSource = aEvent->inputSource;
-  nsIContent* target = aMouseTarget;
+  event.button = aMouseUpEvent->button;
+  event.pointerId = aMouseUpEvent->pointerId;
+  event.inputSource = aMouseUpEvent->inputSource;
+  nsIContent* target = aMouseUpContent;
   nsIFrame* targetFrame = aCurrentTarget;
   if (aOverrideClickTarget) {
     target = aOverrideClickTarget;
     targetFrame = aOverrideClickTarget->GetPrimaryFrame();
   }
 
   return aPresShell->HandleEventWithTarget(&event, targetFrame,
                                            target, aStatus);
 }
 
 nsresult
-EventStateManager::CheckForAndDispatchClick(WidgetMouseEvent* aEvent,
-                                            nsEventStatus* aStatus,
-                                            nsIContent* aOverrideClickTarget)
+EventStateManager::PostHandleMouseUp(WidgetMouseEvent* aMouseUpEvent,
+                                     nsEventStatus* aStatus,
+                                     nsIContent* aOverrideClickTarget)
 {
-  // If mouse is still over same element, clickcount will be > 1.
-  // If it has moved it will be zero, so no click.
-  if (!aEvent->mClickCount) {
+  MOZ_ASSERT(aMouseUpEvent);
+  MOZ_ASSERT(EventCausesClickEvents(*aMouseUpEvent));
+  MOZ_ASSERT(aStatus);
+
+  nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
+  if (!presShell) {
     return NS_OK;
   }
 
-  // Check that the window isn't disabled before firing a click
-  // (see bug 366544).
-  if (aEvent->mWidget && !aEvent->mWidget->IsEnabled()) {
+  nsCOMPtr<nsIContent> mouseUpContent = GetEventTargetContent(aMouseUpEvent);
+  // Click events apply to *elements* not nodes. At this point the target
+  // content may have been reset to some non-element content, and so we need
+  // to walk up the closest ancestor element, just like we do in
+  // nsPresShell::HandleEvent.
+  while (mouseUpContent && !mouseUpContent->IsElement()) {
+    mouseUpContent = mouseUpContent->GetFlattenedTreeParent();
+  }
+
+  if (!mouseUpContent && !mCurrentTarget && !aOverrideClickTarget) {
     return NS_OK;
   }
 
   // Fire click events if the event target is still available.
+  nsresult rv = DispatchClickEvents(presShell, aMouseUpEvent, aStatus,
+                                    mouseUpContent, aOverrideClickTarget);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  return NS_OK;
+}
+
+nsresult
+EventStateManager::DispatchClickEvents(nsIPresShell* aPresShell,
+                                       WidgetMouseEvent* aMouseUpEvent,
+                                       nsEventStatus* aStatus,
+                                       nsIContent* aMouseUpContent,
+                                       nsIContent* aOverrideClickTarget)
+{
+  MOZ_ASSERT(aPresShell);
+  MOZ_ASSERT(aMouseUpEvent);
+  MOZ_ASSERT(EventCausesClickEvents(*aMouseUpEvent));
+  MOZ_ASSERT(aStatus);
+  MOZ_ASSERT(aMouseUpContent || mCurrentTarget || aOverrideClickTarget);
+
   bool notDispatchToContents =
-   (aEvent->button == WidgetMouseEvent::eMiddleButton ||
-    aEvent->button == WidgetMouseEvent::eRightButton);
+   (aMouseUpEvent->button == WidgetMouseEvent::eMiddleButton ||
+    aMouseUpEvent->button == WidgetMouseEvent::eRightButton);
 
   bool fireAuxClick = notDispatchToContents;
 
-  nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
-  if (!presShell) {
-    return NS_OK;
-  }
-
-  nsCOMPtr<nsIContent> mouseContent = GetEventTargetContent(aEvent);
-  // Click events apply to *elements* not nodes. At this point the target
-  // content may have been reset to some non-element content, and so we need
-  // to walk up the closest ancestor element, just like we do in
-  // nsPresShell::HandleEvent.
-  while (mouseContent && !mouseContent->IsElement()) {
-    mouseContent = mouseContent->GetFlattenedTreeParent();
-  }
-
-  if (!mouseContent && !mCurrentTarget && !aOverrideClickTarget) {
-    return NS_OK;
-  }
-
   // HandleEvent clears out mCurrentTarget which we might need again
   AutoWeakFrame currentTarget = mCurrentTarget;
   nsresult rv =
-    InitAndDispatchClickEvent(aEvent, aStatus, eMouseClick,
-                              presShell, mouseContent, currentTarget,
+    InitAndDispatchClickEvent(aMouseUpEvent, aStatus, eMouseClick,
+                              aPresShell, aMouseUpContent, currentTarget,
                               notDispatchToContents,
                               aOverrideClickTarget);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // Fire double click event if click count is 2.
-  if (aEvent->mClickCount == 2 &&
-      mouseContent && mouseContent->IsInComposedDoc()) {
-    rv = InitAndDispatchClickEvent(aEvent, aStatus, eMouseDoubleClick,
-                                   presShell, mouseContent, currentTarget,
+  if (aMouseUpEvent->mClickCount == 2 &&
+      aMouseUpContent && aMouseUpContent->IsInComposedDoc()) {
+    rv = InitAndDispatchClickEvent(aMouseUpEvent, aStatus, eMouseDoubleClick,
+                                   aPresShell, aMouseUpContent, currentTarget,
                                    notDispatchToContents,
                                    aOverrideClickTarget);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   // Fire auxclick even if necessary.
   if (fireAuxClick &&
-      mouseContent && mouseContent->IsInComposedDoc()) {
-    rv = InitAndDispatchClickEvent(aEvent, aStatus, eMouseAuxClick,
-                                   presShell, mouseContent, currentTarget,
+      aMouseUpContent && aMouseUpContent->IsInComposedDoc()) {
+    rv = InitAndDispatchClickEvent(aMouseUpEvent, aStatus, eMouseAuxClick,
+                                   aPresShell, aMouseUpContent, currentTarget,
                                    false, aOverrideClickTarget);
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to dispatch eMouseAuxClick");
   }
 
   return rv;
 }
 
 nsIFrame*
--- a/dom/events/EventStateManager.h
+++ b/dom/events/EventStateManager.h
@@ -107,16 +107,17 @@ public:
                           nsEventStatus* aStatus,
                           nsIContent* aOverrideClickTarget);
 
   /* The PostHandleEvent method should contain all system processing which
    * should occur conditionally based on DOM or frame processing.  It should
    * also contain any centralized event processing which must occur after
    * DOM and frame processing.
    */
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY
   nsresult PostHandleEvent(nsPresContext* aPresContext,
                            WidgetEvent* aEvent,
                            nsIFrame* aTargetFrame,
                            nsEventStatus* aStatus,
                            nsIContent* aOverrideClickTarget);
 
   void PostHandleKeyboardEvent(WidgetKeyboardEvent* aKeyboardEvent,
                                nsIFrame* aTargetFrame, nsEventStatus& aStatus);
@@ -456,29 +457,100 @@ protected:
                            nsIContent* aTargetContent,
                            AutoWeakFrame& aTargetFrame);
   /**
    * Update the initial drag session data transfer with any changes that occur
    * on cloned data transfer objects used for events.
    */
   void UpdateDragDataTransfer(WidgetDragEvent* dragEvent);
 
-  static nsresult InitAndDispatchClickEvent(WidgetMouseEvent* aEvent,
+  /**
+   * InitAndDispatchClickEvent() dispatches a click event.
+   *
+   * @param aMouseUpEvent           eMouseUp event which causes the click event.
+   *                                EventCausesClickEvents() must return true
+   *                                if this event is set to it.
+   * @param aStatus                 Returns the result of click event.
+   * @param aMessage                Should be eMouseClick, eMouseDoubleClick or
+   *                                eMouseAuxClick.
+   * @param aPresShell              The PresShell.
+   * @param aMouseUpContent         The event target of aMouseUpEvent.
+   * @param aCurrentTarget          Current target of the caller.
+   * @param aNoContentDispatch      true if the event shouldn't be exposed to
+   *                                web contents (although will be fired on
+   *                                document and window).
+   * @param aOverrideClickTarget    Preferred click event target.  If this is
+   *                                not nullptr, aMouseUpContent and
+   *                                aCurrentTarget are ignored.
+   */
+  MOZ_CAN_RUN_SCRIPT
+  static nsresult InitAndDispatchClickEvent(WidgetMouseEvent* aMouseUpEvent,
                                             nsEventStatus* aStatus,
                                             EventMessage aMessage,
                                             nsIPresShell* aPresShell,
-                                            nsIContent* aMouseTarget,
+                                            nsIContent* aMouseUpContent,
                                             AutoWeakFrame aCurrentTarget,
                                             bool aNoContentDispatch,
                                             nsIContent* aOverrideClickTarget);
+
   nsresult SetClickCount(WidgetMouseEvent* aEvent, nsEventStatus* aStatus,
                          nsIContent* aOverrideClickTarget = nullptr);
-  nsresult CheckForAndDispatchClick(WidgetMouseEvent* aEvent,
-                                    nsEventStatus* aStatus,
-                                    nsIContent* aOverrideClickTarget);
+
+  /**
+   * EventCausesClickEvents() returns true when aMouseEvent is an eMouseUp
+   * event and it should cause eMouseClick, eMouseDoubleClick and/or
+   * eMouseAuxClick events.  Note that this method assumes that
+   * aMouseEvent.mClickCount has already been initialized with SetClickCount().
+   */
+  static bool EventCausesClickEvents(const WidgetMouseEvent& aMouseEvent);
+
+  /**
+   * PostHandleMouseUp() handles default actions of eMouseUp event.
+   *
+   * @param aMouseUpEvent           eMouseUp event which causes the click event.
+   *                                EventCausesClickEvents() must return true
+   *                                if this event is set to it.
+   * @param aStatus                 Returns the result of event status.
+   *                                If one of dispatching event is consumed or
+   *                                this does something as default action,
+   *                                returns nsEventStatus_eConsumeNoDefault.
+   * @param aOverrideClickTarget    Preferred click event target.  If nullptr,
+   *                                aMouseUpEvent target and current target
+   *                                are used.
+   */
+  MOZ_CAN_RUN_SCRIPT
+  nsresult PostHandleMouseUp(WidgetMouseEvent* aMouseUpEvent,
+                             nsEventStatus* aStatus,
+                             nsIContent* aOverrideClickTarget);
+
+  /**
+   * DispatchClickEvents() dispatches eMouseClick, eMouseDoubleClick and
+   * eMouseAuxClick events for aMouseUpEvent.  aMouseUpEvent should cause
+   * click event.
+   *
+   * @param aPresShell              The PresShell.
+   * @param aMouseUpEvent           eMouseUp event which causes the click event.
+   *                                EventCausesClickEvents() must return true
+   *                                if this event is set to it.
+   * @param aStatus                 Returns the result of event status.
+   *                                If one of dispatching click event is
+   *                                consumed, returns
+   *                                nsEventStatus_eConsumeNoDefault.
+   * @param aMouseUpContent         The event target of aMouseUpEvent.
+   * @param aOverrideClickTarget    Preferred click event target.  If this is
+   *                                not nullptr, aMouseUpContent and
+   *                                current target frame of the ESM are ignored.
+   */
+  MOZ_CAN_RUN_SCRIPT
+  nsresult DispatchClickEvents(nsIPresShell* aPresShell,
+                               WidgetMouseEvent* aMouseUpEvent,
+                               nsEventStatus* aStatus,
+                               nsIContent* aMouseUpContent,
+                               nsIContent* aOverrideClickTarget);
+
   void EnsureDocument(nsPresContext* aPresContext);
   void FlushPendingEvents(nsPresContext* aPresContext);
 
   /**
    * The phases of WalkESMTreeToHandleAccessKey processing. See below.
    */
   typedef enum {
     eAccessKeyProcessingNormal = 0,