Bug 1461708 - part 4: Move implementation of UIEvent::GetRangeParent() and UIEvent::RangeOffset() to nsLayoutUtils r=smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Wed, 10 Oct 2018 12:03:34 +0000
changeset 496183 219527f4c312eb25ee1103ac3bdfa38f87539e02
parent 496182 d61721276fcc478dd8f672dcd63749e775283d32
child 496184 95d15775e123f881124b1b0b239b2dd8f837bd0e
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1461708
milestone64.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 1461708 - part 4: Move implementation of UIEvent::GetRangeParent() and UIEvent::RangeOffset() to nsLayoutUtils r=smaug We need to move EditorEventListener::HandleMiddleClickPaste() into EventStateManager to handle middle click paste after all click events are dispatched. This is preparation of the change. HandleMiddleClickPaste() uses UIEvent::GetRangeParent() and UIEvent::RangeOffset() to collapse Selection at clicked point. However, EventStateManager cannot access them since EventStateManager can handle it with WidgetMouseEvent. Fortunately, only WidgetMouseEvent is necessary for implementing them. Therefore, we can move the implementation into nsLayoutUtils and merge them. Differential Revision: https://phabricator.services.mozilla.com/D7851
dom/events/UIEvent.cpp
dom/events/UIEvent.h
editor/libeditor/EditorEventListener.cpp
editor/libeditor/EditorEventListener.h
editor/libeditor/HTMLEditorEventListener.h
editor/libeditor/TextEditor.h
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/xul/nsXULPopupManager.cpp
layout/xul/nsXULPopupManager.h
--- a/dom/events/UIEvent.cpp
+++ b/dom/events/UIEvent.cpp
@@ -179,64 +179,44 @@ UIEvent::PageY() const
 
   return Event::GetPageCoords(mPresContext, mEvent, mEvent->mRefPoint,
                               mClientPoint).y;
 }
 
 already_AddRefed<nsINode>
 UIEvent::GetRangeParent()
 {
-  nsIFrame* targetFrame = nullptr;
-
-  if (mPresContext) {
-    nsCOMPtr<nsIPresShell> shell = mPresContext->GetPresShell();
-    if (shell) {
-      shell->FlushPendingNotifications(FlushType::Layout);
-      targetFrame = mPresContext->EventStateManager()->GetEventTarget();
-    }
+  if (NS_WARN_IF(!mPresContext)) {
+    return nullptr;
   }
-
-  if (targetFrame) {
-    nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mEvent,
-                                                              targetFrame);
-    nsCOMPtr<nsIContent> parent = targetFrame->GetContentOffsetsFromPoint(pt).content;
-    if (parent) {
-      if (parent->ChromeOnlyAccess() &&
-          !nsContentUtils::CanAccessNativeAnon()) {
-        return nullptr;
-      }
-      return parent.forget();
-    }
+  nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
+  if (NS_WARN_IF(!presShell)) {
+    return nullptr;
   }
-
-  return nullptr;
+  nsCOMPtr<nsIContent> container;
+  nsLayoutUtils::GetContainerAndOffsetAtEvent(presShell, mEvent,
+                                              getter_AddRefs(container),
+                                              nullptr);
+  return container.forget();
 }
 
 int32_t
 UIEvent::RangeOffset() const
 {
-  if (!mPresContext) {
-    return 0;
-  }
-
-  nsCOMPtr<nsIPresShell> shell = mPresContext->GetPresShell();
-  if (!shell) {
+  if (NS_WARN_IF(!mPresContext)) {
     return 0;
   }
-
-  shell->FlushPendingNotifications(FlushType::Layout);
-
-  nsIFrame* targetFrame = mPresContext->EventStateManager()->GetEventTarget();
-  if (!targetFrame) {
+  nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
+  if (NS_WARN_IF(!presShell)) {
     return 0;
   }
-
-  nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mEvent,
-                                                            targetFrame);
-  return targetFrame->GetContentOffsetsFromPoint(pt).offset;
+  int32_t offset = 0;
+  nsLayoutUtils::GetContainerAndOffsetAtEvent(presShell, mEvent,
+                                              nullptr, &offset);
+  return offset;
 }
 
 nsIntPoint
 UIEvent::GetLayerPoint() const
 {
   if (mEvent->mFlags.mIsPositionless) {
     return nsIntPoint(0, 0);
   }
--- a/dom/events/UIEvent.h
+++ b/dom/events/UIEvent.h
@@ -83,18 +83,20 @@ public:
   {
     MOZ_ASSERT(mEvent->mClass != eKeyboardEventClass,
                "Key events should override Which()");
     MOZ_ASSERT(mEvent->mClass != eMouseEventClass,
                "Mouse events should override Which()");
     return 0;
   }
 
+  MOZ_CAN_RUN_SCRIPT
   already_AddRefed<nsINode> GetRangeParent();
 
+  MOZ_CAN_RUN_SCRIPT
   int32_t RangeOffset() const;
 
 protected:
   ~UIEvent() {}
 
   // Internal helper functions
   nsIntPoint GetMovementPoint();
   nsIntPoint GetLayerPoint() const;
--- a/editor/libeditor/EditorEventListener.cpp
+++ b/editor/libeditor/EditorEventListener.cpp
@@ -375,29 +375,36 @@ EditorEventListener::HandleEvent(Event* 
   // NOTE: Each event handler may require specific event interface.  Before
   //       calling it, this queries the specific interface.  If it would fail,
   //       each event handler would just ignore the event.  So, in this method,
   //       you don't need to check if the QI succeeded before each call.
   WidgetEvent* internalEvent = aEvent->WidgetEventPtr();
   switch (internalEvent->mMessage) {
     // dragenter
     case eDragEnter: {
-      return DragEnter(aEvent->AsDragEvent());
+      // aEvent should be grabbed by the caller since this is
+      // nsIDOMEventListener method.  However, our clang plugin cannot check it
+      // if we use Event::As*Event().  So, we need to grab it by ourselves.
+      RefPtr<DragEvent> dragEvent = aEvent->AsDragEvent();
+      return DragEnter(dragEvent);
     }
     // dragover
     case eDragOver: {
-      return DragOver(aEvent->AsDragEvent());
+      RefPtr<DragEvent> dragEvent = aEvent->AsDragEvent();
+      return DragOver(dragEvent);
     }
     // dragexit
     case eDragExit: {
-      return DragExit(aEvent->AsDragEvent());
+      RefPtr<DragEvent> dragEvent = aEvent->AsDragEvent();
+      return DragExit(dragEvent);
     }
     // drop
     case eDrop: {
-      return Drop(aEvent->AsDragEvent());
+      RefPtr<DragEvent> dragEvent = aEvent->AsDragEvent();
+      return Drop(dragEvent);
     }
 #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
     // keydown
     case eKeyDown: {
       return KeyDown(internalEvent->AsKeyboardEvent());
     }
     // keyup
     case eKeyUp:
@@ -415,17 +422,17 @@ EditorEventListener::HandleEvent(Event* 
       // Therefore, even if case #2 or case #3 occurs,
       // mMouseDownOrUpConsumedByIME is true here.  Therefore, we should always
       // overwrite it here.
       mMouseDownOrUpConsumedByIME =
         NotifyIMEOfMouseButtonEvent(internalEvent->AsMouseEvent());
       if (mMouseDownOrUpConsumedByIME) {
         return NS_OK;
       }
-      MouseEvent* mouseEvent = aEvent->AsMouseEvent();
+      RefPtr<MouseEvent> mouseEvent = aEvent->AsMouseEvent();
       return NS_WARN_IF(!mouseEvent) ? NS_OK : MouseDown(mouseEvent);
     }
     // mouseup
     case eMouseUp: {
       // See above comment in the eMouseDown case, first.
       // This code assumes that case #1 is occuring.  However, if case #3 may
       // occurs after case #2 and the mousedown is consumed,
       // mMouseDownOrUpConsumedByIME is true even though EditorEventListener
@@ -436,23 +443,25 @@ EditorEventListener::HandleEvent(Event* 
       // So, before a click event is fired, mMouseDownOrUpConsumedByIME is
       // always initialized in the eMouseDown case if it's referred.
       if (NotifyIMEOfMouseButtonEvent(internalEvent->AsMouseEvent())) {
         mMouseDownOrUpConsumedByIME = true;
       }
       if (mMouseDownOrUpConsumedByIME) {
         return NS_OK;
       }
-      MouseEvent* mouseEvent = aEvent->AsMouseEvent();
+      RefPtr<MouseEvent> mouseEvent = aEvent->AsMouseEvent();
       return NS_WARN_IF(!mouseEvent) ? NS_OK : MouseUp(mouseEvent);
     }
     // click
     case eMouseClick: {
-      MouseEvent* mouseEvent = aEvent->AsMouseEvent();
-      NS_ENSURE_TRUE(mouseEvent, NS_OK);
+      RefPtr<MouseEvent> mouseEvent = aEvent->AsMouseEvent();
+      if (NS_WARN_IF(!mouseEvent)) {
+        return NS_OK;
+      }
       // If the preceding mousedown event or mouseup event was consumed,
       // editor shouldn't handle this click event.
       if (mMouseDownOrUpConsumedByIME) {
         mMouseDownOrUpConsumedByIME = false;
         mouseEvent->PreventDefault();
         return NS_OK;
       }
       return MouseClick(mouseEvent);
--- a/editor/libeditor/EditorEventListener.h
+++ b/editor/libeditor/EditorEventListener.h
@@ -43,17 +43,20 @@ class EditorEventListener : public nsIDO
 public:
   EditorEventListener();
 
   virtual nsresult Connect(EditorBase* aEditorBase);
 
   void Disconnect();
 
   NS_DECL_ISUPPORTS
-  NS_DECL_NSIDOMEVENTLISTENER
+
+  // nsIDOMEventListener
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY
+  NS_IMETHOD HandleEvent(dom::Event *aEvent) override;
 
   void SpellCheckIfNeeded();
 
 protected:
   virtual ~EditorEventListener();
 
   nsresult InstallToEditor();
   void UninstallFromEditor();
@@ -61,36 +64,39 @@ protected:
 #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
   nsresult KeyDown(const WidgetKeyboardEvent* aKeyboardEvent);
   nsresult KeyUp(const WidgetKeyboardEvent* aKeyboardEvent);
 #endif
   nsresult KeyPress(WidgetKeyboardEvent* aKeyboardEvent);
   nsresult HandleChangeComposition(WidgetCompositionEvent* aCompositionEvent);
   nsresult HandleStartComposition(WidgetCompositionEvent* aCompositionEvent);
   void HandleEndComposition(WidgetCompositionEvent* aCompositionEvent);
+  MOZ_CAN_RUN_SCRIPT
   virtual nsresult MouseDown(dom::MouseEvent* aMouseEvent);
   virtual nsresult MouseUp(dom::MouseEvent* aMouseEvent) { return NS_OK; }
+  MOZ_CAN_RUN_SCRIPT
   virtual nsresult MouseClick(dom::MouseEvent* aMouseEvent);
   nsresult Focus(InternalFocusEvent* aFocusEvent);
   nsresult Blur(InternalFocusEvent* aBlurEvent);
-  nsresult DragEnter(dom::DragEvent* aDragEvent);
-  nsresult DragOver(dom::DragEvent* aDragEvent);
+  MOZ_CAN_RUN_SCRIPT nsresult DragEnter(dom::DragEvent* aDragEvent);
+  MOZ_CAN_RUN_SCRIPT nsresult DragOver(dom::DragEvent* aDragEvent);
   nsresult DragExit(dom::DragEvent* aDragEvent);
-  nsresult Drop(dom::DragEvent* aDragEvent);
+  MOZ_CAN_RUN_SCRIPT nsresult Drop(dom::DragEvent* aDragEvent);
 
-  bool CanDrop(dom::DragEvent* aEvent);
+  MOZ_CAN_RUN_SCRIPT bool CanDrop(dom::DragEvent* aEvent);
   void CleanupDragDropCaret();
   nsIPresShell* GetPresShell() const;
   nsPresContext* GetPresContext() const;
   nsIContent* GetFocusedRootContent();
   // Returns true if IME consumes the mouse event.
   bool NotifyIMEOfMouseButtonEvent(WidgetMouseEvent* aMouseEvent);
   bool EditorHasFocus();
   bool IsFileControlTextBox();
   bool ShouldHandleNativeKeyBindings(WidgetKeyboardEvent* aKeyboardEvent);
+  MOZ_CAN_RUN_SCRIPT
   nsresult HandleMiddleClickPaste(dom::MouseEvent* aMouseEvent);
 
   /**
    * DetachedFromEditor() returns true if editor was detached.
    * Otherwise, false.
    */
   bool DetachedFromEditor() const;
 
--- a/editor/libeditor/HTMLEditorEventListener.h
+++ b/editor/libeditor/HTMLEditorEventListener.h
@@ -25,16 +25,18 @@ public:
   }
 
   /**
    * Connect() fails if aEditorBase isn't an HTMLEditor instance.
    */
   virtual nsresult Connect(EditorBase* aEditorBase) override;
 
 protected:
+  MOZ_CAN_RUN_SCRIPT
   virtual nsresult MouseDown(dom::MouseEvent* aMouseEvent) override;
   virtual nsresult MouseUp(dom::MouseEvent* aMouseEvent) override;
+  MOZ_CAN_RUN_SCRIPT
   virtual nsresult MouseClick(dom::MouseEvent* aMouseEvent) override;
 };
 
 } // namespace mozilla
 
 #endif // #ifndef HTMLEditorEventListener_h
--- a/editor/libeditor/TextEditor.h
+++ b/editor/libeditor/TextEditor.h
@@ -208,16 +208,17 @@ public:
    * OnCompositionChange() is called.
    */
   void OnCompositionEnd(WidgetCompositionEvent& aCompositionEndEvent);
 
   /**
    * OnDrop() is called from EditorEventListener::Drop that is handler of drop
    * event.
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult OnDrop(dom::DragEvent* aDropEvent);
 
   /**
    * ComputeTextValue() computes plaintext value of this editor.  This may be
    * too expensive if it's in hot path.
    *
    * @param aDocumentEncoderFlags   Flags of nsIDocumentEncoder.
    * @param aCharset                Encoding of the document.
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -7,16 +7,17 @@
 #include "nsLayoutUtils.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/BasicEvents.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/EffectCompositor.h"
 #include "mozilla/EffectSet.h"
 #include "mozilla/EventDispatcher.h"
+#include "mozilla/EventStateManager.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/gfx/gfxVars.h"
 #include "mozilla/gfx/PathHelpers.h"
 #include "mozilla/layers/PAPZ.h"
 #include "mozilla/Likely.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/ContentChild.h"
@@ -2225,16 +2226,69 @@ nsLayoutUtils::GetPopupFrameForEventCoor
           GetEventCoordinatesRelativeTo(aEvent, popup))) {
       return popup;
     }
   }
 #endif
   return nullptr;
 }
 
+void
+nsLayoutUtils::GetContainerAndOffsetAtEvent(nsIPresShell* aPresShell,
+                                            const WidgetEvent* aEvent,
+                                            nsIContent** aContainer,
+                                            int32_t* aOffset)
+{
+  MOZ_ASSERT(aContainer || aOffset);
+
+  if (aContainer) {
+    *aContainer = nullptr;
+  }
+  if (aOffset) {
+    *aOffset = 0;
+  }
+
+  if (!aPresShell) {
+    return;
+  }
+
+  aPresShell->FlushPendingNotifications(FlushType::Layout);
+
+  RefPtr<nsPresContext> presContext = aPresShell->GetPresContext();
+  if (!presContext) {
+    return;
+  }
+
+  nsIFrame* targetFrame = presContext->EventStateManager()->GetEventTarget();
+  if (!targetFrame) {
+    return;
+  }
+
+  nsPoint point =
+    nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, targetFrame);
+
+  if (aContainer) {
+    // TODO: This result may be useful to change to Selection.  However, this
+    //       may return improper node (e.g., native anonymous node) for the
+    //       Selection.  Perhaps, this should take Selection optionally and
+    //       if it's specified, needs to check if it's proper for the
+    //       Selection.
+    nsCOMPtr<nsIContent> container =
+      targetFrame->GetContentOffsetsFromPoint(point).content;
+    if (container &&
+        (!container->ChromeOnlyAccess() ||
+         nsContentUtils::CanAccessNativeAnon())) {
+      container.forget(aContainer);
+    }
+  }
+  if (aOffset) {
+    *aOffset = targetFrame->GetContentOffsetsFromPoint(point).offset;
+  }
+}
+
 static void ConstrainToCoordValues(float& aStart, float& aSize)
 {
   MOZ_ASSERT(aSize >= 0);
 
   // Here we try to make sure that the resulting nsRect will continue to cover
   // as much of the area that was covered by the original gfx Rect as possible.
 
   // We clamp the bounds of the rect to {nscoord_MIN,nscoord_MAX} since
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -772,16 +772,32 @@ public:
    * @return        Null, if there is no popup frame at the point, otherwise,
    *                returns top-most popup frame at the point.
    */
   static nsIFrame* GetPopupFrameForEventCoordinates(
                      nsPresContext* aPresContext,
                      const mozilla::WidgetEvent* aEvent);
 
   /**
+   * Get container and offset if aEvent collapses Selection.
+   * @param aPresShell      The PresShell handling aEvent.
+   * @param aEvent          The event having coordinates where you want to
+   *                        collapse Selection.
+   * @param aContainer      Returns the container node at the point.
+   *                        Set nullptr if you don't need this.
+   * @param aOffset         Returns offset in the container node at the point.
+   *                        Set nullptr if you don't need this.
+   */
+  MOZ_CAN_RUN_SCRIPT
+  static void GetContainerAndOffsetAtEvent(nsIPresShell* aPresShell,
+                                           const mozilla::WidgetEvent* aEvent,
+                                           nsIContent** aContainer,
+                                           int32_t* aOffset);
+
+  /**
    * Translate from widget coordinates to the view's coordinates
    * @param aPresContext the PresContext for the view
    * @param aWidget the widget
    * @param aPt the point relative to the widget
    * @param aView  view to which returned coordinates are relative
    * @return the point in the view's coordinates
    */
   static nsPoint TranslateWidgetToView(nsPresContext* aPresContext,
--- a/layout/xul/nsXULPopupManager.cpp
+++ b/layout/xul/nsXULPopupManager.cpp
@@ -627,17 +627,17 @@ nsXULPopupManager::InitTriggerEvent(Even
       // get the trigger content from the event
       nsCOMPtr<nsIContent> target = do_QueryInterface(aEvent->GetTarget());
       target.forget(aTriggerContent);
     }
   }
 
   mCachedModifiers = 0;
 
-  UIEvent* uiEvent = aEvent ? aEvent->AsUIEvent() : nullptr;
+  RefPtr<UIEvent> uiEvent = aEvent ? aEvent->AsUIEvent() : nullptr;
   if (uiEvent) {
     mRangeParent = uiEvent->GetRangeParent();
     mRangeOffset = uiEvent->RangeOffset();
 
     // get the event coordinates relative to the root frame of the document
     // containing the popup.
     NS_ASSERTION(aPopup, "Expected a popup node");
     WidgetEvent* event = aEvent->WidgetEventPtr();
--- a/layout/xul/nsXULPopupManager.h
+++ b/layout/xul/nsXULPopupManager.h
@@ -709,16 +709,17 @@ protected:
   nsMenuChainItem* GetTopVisibleMenu();
 
   // Hide all of the visible popups from the given list. This function can
   // cause style changes and frame destruction.
   void HidePopupsInList(const nsTArray<nsMenuPopupFrame *> &aFrames);
 
   // set the event that was used to trigger the popup, or null to clear the
   // event details. aTriggerContent will be set to the target of the event.
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY
   void InitTriggerEvent(mozilla::dom::Event* aEvent, nsIContent* aPopup, nsIContent** aTriggerContent);
 
   // callbacks for ShowPopup and HidePopup as events may be done asynchronously
   void ShowPopupCallback(nsIContent* aPopup,
                          nsMenuPopupFrame* aPopupFrame,
                          bool aIsContextMenu,
                          bool aSelectFirstItem);
   void HidePopupCallback(nsIContent* aPopup,