Bug 1466208 - part 19: Group PresShell, nsIFrame and nsIContent with a struct r=smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Tue, 12 Feb 2019 23:23:27 +0000
changeset 458836 86b52991b24a
parent 458835 e957d571c91c
child 458837 1731ef0126f9
push id35548
push useropoprus@mozilla.com
push dateWed, 13 Feb 2019 09:48:26 +0000
treeherdermozilla-central@93e37c529818 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1466208
milestone67.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 1466208 - part 19: Group PresShell, nsIFrame and nsIContent with a struct r=smaug PresShell::EventHandler::HandleEvent() looks for PresShell, nsIFrame and nsIContent a lot for aGUIEvent. Sometimes part of them are modified, otherwise, all of them are modified by some reasons. Therefore, for splitting each of the modifiers into separated methods, we need a struct for making them as a group and usable for in/out parameter. (If you have some ideas of better name, let me know.) Differential Revision: https://phabricator.services.mozilla.com/D19317
layout/base/PresShell.cpp
layout/base/PresShell.h
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -6578,116 +6578,116 @@ nsresult PresShell::EventHandler::Handle
         (aGUIEvent->mMessage == eMouseExitFromWidget) &&
         (mouseEvent && mouseEvent->mExitFrom == WidgetMouseEvent::eTopLevel);
 
     // Get the frame at the event point. However, don't do this if we're
     // capturing and retargeting the event because the captured frame will
     // be used instead below. Also keep using the root frame if we're dealing
     // with a window-level mouse exit event since we want to start sending
     // mouse out events at the root EventStateManager.
-    nsIFrame* frameToHandleEvent = rootFrameToHandleEvent;
+    EventTargetData eventTargetData(mPresShell, rootFrameToHandleEvent);
     if (!isCaptureRetargeted && !isWindowLevelMouseExit &&
         !pointerCapturingContent) {
       if (aGUIEvent->mClass == eTouchEventClass) {
-        frameToHandleEvent = TouchManager::SetupTarget(
-            aGUIEvent->AsTouchEvent(), rootFrameToHandleEvent);
+        eventTargetData.SetFrameAndComputePresShell(TouchManager::SetupTarget(
+            aGUIEvent->AsTouchEvent(), rootFrameToHandleEvent));
       } else {
-        frameToHandleEvent =
-            GetFrameToHandleNonTouchEvent(rootFrameToHandleEvent, aGUIEvent);
-        if (!frameToHandleEvent) {
+        eventTargetData.SetFrameAndComputePresShell(
+            GetFrameToHandleNonTouchEvent(rootFrameToHandleEvent, aGUIEvent));
+        if (!eventTargetData.mFrame) {
           *aEventStatus = nsEventStatus_eIgnore;
           return NS_OK;
         }
       }
     }
 
     // if a node is capturing the mouse, check if the event needs to be
     // retargeted at the capturing content instead. This will be the case when
     // capture retargeting is being used, no frame was found or the frame's
     // content is not a descendant of the capturing content.
     if (capturingContent && !pointerCapturingContent &&
-        (gCaptureInfo.mRetargetToElement || !frameToHandleEvent->GetContent() ||
+        (gCaptureInfo.mRetargetToElement ||
+         !eventTargetData.mFrame->GetContent() ||
          !nsContentUtils::ContentIsCrossDocDescendantOf(
-             frameToHandleEvent->GetContent(), capturingContent))) {
+             eventTargetData.mFrame->GetContent(), capturingContent))) {
       // A check was already done above to ensure that capturingContent is
       // in this presshell.
       NS_ASSERTION(capturingContent->GetComposedDoc() == GetDocument(),
                    "Unexpected document");
       nsIFrame* capturingFrame = capturingContent->GetPrimaryFrame();
       if (capturingFrame) {
-        frameToHandleEvent = capturingFrame;
-      }
-    }
-
-    if (NS_WARN_IF(!frameToHandleEvent)) {
+        eventTargetData.SetFrameAndComputePresShell(capturingFrame);
+      }
+    }
+
+    if (NS_WARN_IF(!eventTargetData.mFrame)) {
       return NS_OK;
     }
 
     // Suppress mouse event if it's being targeted at an element inside
     // a document which needs events suppressed
-    if (MaybeDiscardOrDelayMouseEvent(frameToHandleEvent, aGUIEvent)) {
+    if (MaybeDiscardOrDelayMouseEvent(eventTargetData.mFrame, aGUIEvent)) {
       return NS_OK;
     }
 
-    RefPtr<PresShell> shell =
-        static_cast<PresShell*>(frameToHandleEvent->PresShell());
     // Check if we have an active EventStateManager which isn't the
     // EventStateManager of the current PresContext.
     // If that is the case, and mouse is over some ancestor document,
     // forward event handling to the active document.
     // This way content can get mouse events even when
     // mouse is over the chrome or outside the window.
     //
     // Note, currently for backwards compatibility we don't forward mouse events
     // to the active document when mouse is over some subdocument.
     if (EventStateManager* activeESM =
             EventStateManager::GetActiveEventStateManager()) {
       if (aGUIEvent->mClass == ePointerEventClass ||
           aGUIEvent->HasMouseEventMessage()) {
-        if (activeESM != shell->GetPresContext()->EventStateManager()) {
+        if (activeESM != eventTargetData.GetEventStateManager()) {
           if (nsPresContext* activeContext = activeESM->GetPresContext()) {
             if (nsIPresShell* activeShell = activeContext->GetPresShell()) {
               if (nsContentUtils::ContentIsCrossDocDescendantOf(
-                      activeShell->GetDocument(), shell->GetDocument())) {
-                shell = static_cast<PresShell*>(activeShell);
-                frameToHandleEvent = shell->GetRootFrame();
+                      activeShell->GetDocument(),
+                      eventTargetData.GetDocument())) {
+                eventTargetData.SetPresShellAndFrame(
+                    static_cast<PresShell*>(activeShell),
+                    activeShell->GetRootFrame());
               }
             }
           }
         }
       }
     }
 
-    if (!frameToHandleEvent) {
-      NS_WARNING("Nothing to handle this event!");
+    if (NS_WARN_IF(!eventTargetData.mFrame)) {
       return NS_OK;
     }
 
-    nsCOMPtr<nsIContent> targetElement;
-    frameToHandleEvent->GetContentForEvent(aGUIEvent,
-                                           getter_AddRefs(targetElement));
+    eventTargetData.SetContentForEventFromFrame(aGUIEvent);
 
     // If there is no content for this frame, target it anyway.  Some
     // frames can be targeted but do not have content, particularly
     // windows with scrolling off.
-    if (targetElement) {
+    if (eventTargetData.mContent) {
       // Bug 103055, bug 185889: mouse events apply to *elements*, not all
       // nodes.  Thus we get the nearest element parent here.
       // XXX we leave the frame the same even if we find an element
       // parent, so that the text frame will receive the event (selection
       // and friends are the ones who care about that anyway)
       //
       // We use weak pointers because during this tight loop, the node
       // will *not* go away.  And this happens on every mousemove.
-      while (targetElement && !targetElement->IsElement()) {
-        targetElement = targetElement->GetFlattenedTreeParent();
+      while (eventTargetData.mContent &&
+             !eventTargetData.mContent->IsElement()) {
+        eventTargetData.mContent =
+            eventTargetData.mContent->GetFlattenedTreeParent();
       }
 
       // If we found an element, target it.  Otherwise, target *nothing*.
-      if (!targetElement) {
+      if (!eventTargetData.mContent) {
         return NS_OK;
       }
     }
 
     nsCOMPtr<nsIContent> overrideClickTarget;
     if (PointerEventHandler::IsPointerEventEnabled()) {
       // Dispatch pointer events from the mouse or touch events. Regarding
       // pointer events from mouse, we should dispatch those pointer events to
@@ -6695,94 +6695,96 @@ nsresult PresShell::EventHandler::Handle
       // in hit test to PointerEventHandler and dispatch pointer events to it.
       //
       // Regarding pointer events from touch, the behavior is different. Touch
       // events are dispatched to the same target as the target of touchstart.
       // Multiple touch points must be dispatched to the same document. Pointer
       // events from touch can be dispatched to different documents. We Pass the
       // original frame to PointerEventHandler, reentry PresShell::HandleEvent,
       // and do hit test for each point.
-      nsIFrame* targetFrame =
-          aGUIEvent->mClass == eTouchEventClass ? aFrame : frameToHandleEvent;
+      nsIFrame* targetFrame = aGUIEvent->mClass == eTouchEventClass
+                                  ? aFrame
+                                  : eventTargetData.mFrame;
 
       if (pointerCapturingContent) {
         overrideClickTarget = GetOverrideClickTarget(aGUIEvent, aFrame);
-        shell =
+        eventTargetData.mPresShell =
             PresShell::GetShellForEventTarget(nullptr, pointerCapturingContent);
-        if (!shell) {
+        if (!eventTargetData.mPresShell) {
           // If we can't process event for the capturing content, release
           // the capture.
           PointerEventHandler::ReleaseIfCaptureByDescendant(
               pointerCapturingContent);
           return NS_OK;
         }
 
         targetFrame = pointerCapturingContent->GetPrimaryFrame();
-        frameToHandleEvent = targetFrame;
+        eventTargetData.mFrame = targetFrame;
       }
 
       AutoWeakFrame weakTargetFrame(targetFrame);
-      AutoWeakFrame weakFrame(frameToHandleEvent);
+      AutoWeakFrame weakFrame(eventTargetData.mFrame);
       nsCOMPtr<nsIContent> targetContent;
       PointerEventHandler::DispatchPointerFromMouseOrTouch(
-          shell, targetFrame, targetElement, aGUIEvent, aDontRetargetEvents,
+          eventTargetData.mPresShell, eventTargetData.mFrame,
+          eventTargetData.mContent, aGUIEvent, aDontRetargetEvents,
           aEventStatus, getter_AddRefs(targetContent));
 
       if (!weakTargetFrame.IsAlive() && aGUIEvent->mClass == eMouseEventClass) {
         // Spec only defines that mouse events must be dispatched to the same
         // target as the pointer event. If the target is no longer participating
         // in its ownerDocument's tree, fire the event at the original target's
         // nearest ancestor node
         if (!targetContent) {
           return NS_OK;
         }
-        frameToHandleEvent = targetContent->GetPrimaryFrame();
-        shell = PresShell::GetShellForEventTarget(frameToHandleEvent,
-                                                  targetContent);
-        if (!shell) {
+        // XXX Why don't we reset eventTargetData.mContent here?
+        eventTargetData.mFrame = targetContent->GetPrimaryFrame();
+        eventTargetData.mPresShell = PresShell::GetShellForEventTarget(
+            eventTargetData.mFrame, targetContent);
+        if (!eventTargetData.mPresShell) {
           return NS_OK;
         }
       } else if (!weakFrame.IsAlive()) {
         return NS_OK;
       }
     }
 
     // frame could be null after dispatching pointer events.
     if (aGUIEvent->mClass == eTouchEventClass) {
       if (aGUIEvent->mMessage == eTouchStart) {
         WidgetTouchEvent* touchEvent = aGUIEvent->AsTouchEvent();
         if (nsIFrame* newFrame =
                 TouchManager::SuppressInvalidPointsAndGetTargetedFrame(
                     touchEvent)) {
-          frameToHandleEvent = newFrame;
-          frameToHandleEvent->GetContentForEvent(aGUIEvent,
-                                                 getter_AddRefs(targetElement));
-          shell = static_cast<PresShell*>(frameToHandleEvent->PresShell());
+          eventTargetData.SetFrameAndComputePresShellAndContent(newFrame,
+                                                                aGUIEvent);
         }
       } else if (PresShell* newShell =
                      PresShell::GetShellForTouchEvent(aGUIEvent)) {
         // Touch events (except touchstart) are dispatching to the captured
         // element. Get correct shell from it.
-        shell = newShell;
+        eventTargetData.mPresShell = newShell;
       }
     }
 
     // Handle the event in the correct shell.
     // We pass the subshell's root frame as the frame to start from. This is
     // the only correct alternative; if the event was captured then it
     // must have been captured by us or some ancestor shell and we
     // now ask the subshell to dispatch it normally.
-    shell->PushCurrentEventInfo(frameToHandleEvent, targetElement);
-    EventHandler eventHandler(*shell);
+    eventTargetData.mPresShell->PushCurrentEventInfo(eventTargetData.mFrame,
+                                                     eventTargetData.mContent);
+    EventHandler eventHandler(*eventTargetData.mPresShell);
     nsresult rv = eventHandler.HandleEventInternal(aGUIEvent, aEventStatus,
                                                    true, overrideClickTarget);
 #ifdef DEBUG
-    shell->ShowEventTargetDebug();
+    eventTargetData.mPresShell->ShowEventTargetDebug();
 #endif
-    shell->PopCurrentEventInfo();
+    eventTargetData.mPresShell->PopCurrentEventInfo();
     return rv;
   }
 
   nsresult rv = NS_OK;
 
   if (aFrame) {
     PushCurrentEventInfo(nullptr, nullptr);
 
@@ -10730,8 +10732,44 @@ nsIContent* PresShell::EventHandler::Get
   }
 
   nsIContent* overrideClickTarget = target->GetContent();
   while (overrideClickTarget && !overrideClickTarget->IsElement()) {
     overrideClickTarget = overrideClickTarget->GetFlattenedTreeParent();
   }
   return overrideClickTarget;
 }
+
+/******************************************************************************
+ * PresShell::EventHandler::EventTargetData
+ ******************************************************************************/
+
+void PresShell::EventHandler::EventTargetData::SetFrameAndComputePresShell(
+    nsIFrame* aFrameToHandleEvent) {
+  if (aFrameToHandleEvent) {
+    mFrame = aFrameToHandleEvent;
+    mPresShell = static_cast<PresShell*>(aFrameToHandleEvent->PresShell());
+  } else {
+    mFrame = nullptr;
+    mPresShell = nullptr;
+  }
+}
+
+void PresShell::EventHandler::EventTargetData::
+    SetFrameAndComputePresShellAndContent(nsIFrame* aFrameToHandleEvent,
+                                          WidgetGUIEvent* aGUIEvent) {
+  MOZ_ASSERT(aFrameToHandleEvent);
+  MOZ_ASSERT(aGUIEvent);
+
+  SetFrameAndComputePresShell(aFrameToHandleEvent);
+  SetContentForEventFromFrame(aGUIEvent);
+}
+
+void PresShell::EventHandler::EventTargetData::SetContentForEventFromFrame(
+    WidgetGUIEvent* aGUIEvent) {
+  MOZ_ASSERT(mFrame);
+  mContent = nullptr;
+  mFrame->GetContentForEvent(aGUIEvent, getter_AddRefs(mContent));
+}
+
+nsIContent* PresShell::EventHandler::EventTargetData::GetFrameContent() const {
+  return mFrame ? mFrame->GetContent() : nullptr;
+}
--- a/layout/base/PresShell.h
+++ b/layout/base/PresShell.h
@@ -579,16 +579,54 @@ class PresShell final : public nsIPresSh
    private:
     static bool InZombieDocument(nsIContent* aContent);
     static nsIFrame* GetNearestFrameContainingPresShell(
         nsIPresShell* aPresShell);
     static already_AddRefed<nsIURI> GetDocumentURIToCompareWithBlacklist(
         PresShell& aPresShell);
 
     /**
+     * EventTargetData struct stores a set of a PresShell (event handler),
+     * a frame (to handle the event) and a content (event target for the frame).
+     */
+    struct MOZ_STACK_CLASS EventTargetData final {
+      EventTargetData() = delete;
+      EventTargetData(const EventTargetData& aOther) = delete;
+      EventTargetData(PresShell* aPresShell, nsIFrame* aFrameToHandleEvent)
+          : mPresShell(aPresShell), mFrame(aFrameToHandleEvent) {}
+
+      void SetPresShellAndFrame(PresShell* aPresShell,
+                                nsIFrame* aFrameToHandleEvent) {
+        mPresShell = aPresShell;
+        mFrame = aFrameToHandleEvent;
+        mContent = nullptr;
+      }
+      void SetFrameAndComputePresShell(nsIFrame* aFrameToHandleEvent);
+      void SetFrameAndComputePresShellAndContent(nsIFrame* aFrameToHandleEvent,
+                                                 WidgetGUIEvent* aGUIEvent);
+      void SetContentForEventFromFrame(WidgetGUIEvent* aGUIEvent);
+
+      nsPresContext* GetPresContext() const {
+        return mPresShell ? mPresShell->GetPresContext() : nullptr;
+      };
+      EventStateManager* GetEventStateManager() const {
+        nsPresContext* presContext = GetPresContext();
+        return presContext ? presContext->EventStateManager() : nullptr;
+      }
+      Document* GetDocument() const {
+        return mPresShell ? mPresShell->GetDocument() : nullptr;
+      }
+      nsIContent* GetFrameContent() const;
+
+      RefPtr<PresShell> mPresShell;
+      nsIFrame* mFrame;
+      nsCOMPtr<nsIContent> mContent;
+    };
+
+    /**
      * MaybeFlushPendingNotifications() maybe flush pending notifications if
      * aGUIEvent should be handled with the latest layout.
      *
      * @param aGUIEvent                 The handling event.
      * @return                          true if this actually flushes pending
      *                                  layout and that has caused changing the
      *                                  layout.
      */