Bug 1461708 - part 5: Move EditorEventListener::HandleMiddleClickPaste() to EventStateManager r=smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Wed, 10 Oct 2018 12:04:17 +0000
changeset 496184 95d15775e123f881124b1b0b239b2dd8f837bd0e
parent 496183 219527f4c312eb25ee1103ac3bdfa38f87539e02
child 496185 a22e64bb83ed9873094e753df4f91e0f29853c13
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 5: Move EditorEventListener::HandleMiddleClickPaste() to EventStateManager r=smaug EventStateManager needs to handle middle click paste without editor. Therefore, the handler should be in EventStateManager. Differential Revision: https://phabricator.services.mozilla.com/D7852
dom/events/EventStateManager.cpp
dom/events/EventStateManager.h
editor/libeditor/EditorEventListener.cpp
editor/libeditor/EditorEventListener.h
widget/MouseEvents.h
widget/WidgetEventImpl.cpp
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -30,16 +30,17 @@
 #include "ContentEventHandler.h"
 #include "IMEContentObserver.h"
 #include "WheelHandlingHelper.h"
 
 #include "nsCommandParams.h"
 #include "nsCOMPtr.h"
 #include "nsFocusManager.h"
 #include "nsGenericHTMLElement.h"
+#include "nsIClipboard.h"
 #include "nsIContent.h"
 #include "nsIContentInlines.h"
 #include "nsIDocument.h"
 #include "nsIFrame.h"
 #include "nsIWidget.h"
 #include "nsPresContext.h"
 #include "nsIPresShell.h"
 #include "nsGkAtoms.h"
@@ -5115,16 +5116,105 @@ EventStateManager::DispatchClickEvents(n
                                    aPresShell, aMouseUpContent, currentTarget,
                                    false, aOverrideClickTarget);
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to dispatch eMouseAuxClick");
   }
 
   return rv;
 }
 
+nsresult
+EventStateManager::HandleMiddleClickPaste(nsIPresShell* aPresShell,
+                                          WidgetMouseEvent* aMouseEvent,
+                                          nsEventStatus* aStatus,
+                                          TextEditor* aTextEditor)
+{
+  MOZ_ASSERT(aPresShell);
+  MOZ_ASSERT(aMouseEvent);
+  MOZ_ASSERT(aMouseEvent->mMessage == eMouseClick &&
+             aMouseEvent->button == WidgetMouseEventBase::eMiddleButton);
+  MOZ_ASSERT(aStatus);
+  MOZ_ASSERT(aTextEditor);
+
+  if (*aStatus == nsEventStatus_eConsumeNoDefault) {
+    // Already consumed.  Do nothing.
+    return NS_OK;
+  }
+
+  RefPtr<Selection> selection = aTextEditor->GetSelection();
+  if (NS_WARN_IF(!selection)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // Move selection to the clicked point.
+  nsCOMPtr<nsIContent> container;
+  int32_t offset;
+  nsLayoutUtils::GetContainerAndOffsetAtEvent(aPresShell, aMouseEvent,
+                                              getter_AddRefs(container),
+                                              &offset);
+  if (container) {
+    // XXX If readonly or disabled <input> or <textarea> in contenteditable
+    //     designMode editor is clicked, the point is in the editor.
+    //     However, outer HTMLEditor and Selection should handle it.
+    //     So, in such case, Selection::Collapse() will fail.
+    DebugOnly<nsresult> rv = selection->Collapse(container, offset);
+    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+      "Failed to collapse Selection at middle clicked");
+  }
+
+  int32_t clipboardType = nsIClipboard::kGlobalClipboard;
+  nsresult rv = NS_OK;
+  nsCOMPtr<nsIClipboard> clipboardService =
+    do_GetService("@mozilla.org/widget/clipboard;1", &rv);
+  if (NS_SUCCEEDED(rv)) {
+    bool selectionSupported;
+    rv = clipboardService->SupportsSelectionClipboard(&selectionSupported);
+    if (NS_SUCCEEDED(rv) && selectionSupported) {
+      clipboardType = nsIClipboard::kSelectionClipboard;
+    }
+  }
+
+  // Check if the editor is still the good target to paste.
+  if (aTextEditor->Destroyed() ||
+      aTextEditor->IsReadonly() ||
+      aTextEditor->IsDisabled()) {
+    // XXX Should we consume the event when the editor is readonly and/or
+    //     disabled?
+    return NS_OK;
+  }
+
+  // The selection may have been modified during reflow.  Therefore, we
+  // should adjust event target to pass IsAcceptableInputEvent().
+  nsRange* range = selection->GetRangeAt(0);
+  if (!range) {
+    return NS_OK;
+  }
+  WidgetMouseEvent mouseEvent(*aMouseEvent);
+  mouseEvent.mOriginalTarget = range->GetStartContainer();
+  if (NS_WARN_IF(!mouseEvent.mOriginalTarget) ||
+      !aTextEditor->IsAcceptableInputEvent(&mouseEvent)) {
+    return NS_OK;
+  }
+
+  // If Control key is pressed, we should paste clipboard content as
+  // quotation.  Otherwise, paste it as is.
+  if (aMouseEvent->IsControl()) {
+    DebugOnly<nsresult> rv =
+      aTextEditor->PasteAsQuotationAsAction(clipboardType);
+    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to paste as quotation");
+  } else {
+    DebugOnly<nsresult> rv =
+      aTextEditor->PasteAsAction(clipboardType);
+    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to paste");
+  }
+  *aStatus = nsEventStatus_eConsumeNoDefault;
+
+  return NS_OK;
+}
+
 nsIFrame*
 EventStateManager::GetEventTarget()
 {
   nsIPresShell *shell;
   if (mCurrentTarget ||
       !mPresContext ||
       !(shell = mPresContext->GetPresShell())) {
     return mCurrentTarget;
--- a/dom/events/EventStateManager.h
+++ b/dom/events/EventStateManager.h
@@ -358,16 +358,34 @@ public:
    * If the absolute values of mMultiplierX and/or mMultiplierY are equal or
    * larger than this value, the computed scroll amount isn't rounded down to
    * the page width or height.
    */
   enum {
     MIN_MULTIPLIER_VALUE_ALLOWING_OVER_ONE_PAGE_SCROLL = 1000
   };
 
+  /**
+   * HandleMiddleClickPaste() handles middle mouse button event as pasting
+   * clipboard text.
+   *
+   * @param aPresShell              The PresShell for the ESM.  This lifetime
+   *                                should be guaranteed by the caller.
+   * @param aMouseEvent             The eMouseClick event which caused the
+   *                                paste.
+   * @param aStatus                 The event status of aMouseEvent.
+   * @param aTextEditor             TextEditor which may be pasted the
+   *                                clipboard text by the middle click.
+   */
+  MOZ_CAN_RUN_SCRIPT
+  nsresult HandleMiddleClickPaste(nsIPresShell* aPresShell,
+                                  WidgetMouseEvent* aMouseEvent,
+                                  nsEventStatus* aStatus,
+                                  TextEditor* aTextEditor);
+
 protected:
   /**
    * Prefs class capsules preference management.
    */
   class Prefs
   {
   public:
     static bool KeyCausesActivation() { return sKeyCausesActivation; }
--- a/editor/libeditor/EditorEventListener.cpp
+++ b/editor/libeditor/EditorEventListener.cpp
@@ -6,16 +6,17 @@
 
 #include "EditorEventListener.h"
 
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc.
 #include "mozilla/AutoRestore.h"
 #include "mozilla/ContentEvents.h"      // for InternalFocusEvent
 #include "mozilla/EditorBase.h"         // for EditorBase, etc.
 #include "mozilla/EventListenerManager.h" // for EventListenerManager
+#include "mozilla/EventStateManager.h"  // for EventStateManager
 #include "mozilla/IMEStateManager.h"    // for IMEStateManager
 #include "mozilla/Preferences.h"        // for Preferences
 #include "mozilla/TextEditor.h"         // for TextEditor
 #include "mozilla/TextEvents.h"         // for WidgetCompositionEvent
 #include "mozilla/dom/Element.h"        // for Element
 #include "mozilla/dom/Event.h"          // for Event
 #include "mozilla/dom/EventTarget.h"    // for EventTarget
 #include "mozilla/dom/MouseEvent.h"     // for MouseEvent
@@ -642,28 +643,28 @@ EditorEventListener::KeyPress(WidgetKeyb
 
 nsresult
 EditorEventListener::MouseClick(MouseEvent* aMouseEvent)
 {
   if (NS_WARN_IF(!aMouseEvent) || DetachedFromEditor()) {
     return NS_OK;
   }
   // nothing to do if editor isn't editable or clicked on out of the editor.
-  RefPtr<EditorBase> editorBase(mEditorBase);
+  RefPtr<TextEditor> textEditor = mEditorBase->AsTextEditor();
   WidgetMouseEvent* clickEvent =
     aMouseEvent->WidgetEventPtr()->AsMouseEvent();
-  if (editorBase->IsReadonly() || editorBase->IsDisabled() ||
-      !editorBase->IsAcceptableInputEvent(clickEvent)) {
+  if (textEditor->IsReadonly() || textEditor->IsDisabled() ||
+      !textEditor->IsAcceptableInputEvent(clickEvent)) {
     return NS_OK;
   }
 
   // Notifies clicking on editor to IMEStateManager even when the event was
   // consumed.
   if (EditorHasFocus()) {
-    nsPresContext* presContext = GetPresContext();
+    RefPtr<nsPresContext> presContext = GetPresContext();
     if (presContext) {
       IMEStateManager::OnClickInEditor(presContext, GetFocusedRootContent(),
                                        clickEvent);
       if (DetachedFromEditor()) {
         return NS_OK;
       }
     }
   }
@@ -674,74 +675,42 @@ EditorEventListener::MouseClick(MouseEve
   }
 
   // If we got a mouse down inside the editing area, we should force the
   // IME to commit before we change the cursor position
   if (!EnsureCommitCompoisition()) {
     return NS_OK;
   }
 
-  if (clickEvent->button == 1) {
-    return HandleMiddleClickPaste(aMouseEvent);
-  }
-  return NS_OK;
-}
-
-nsresult
-EditorEventListener::HandleMiddleClickPaste(MouseEvent* aMouseEvent)
-{
-  MOZ_ASSERT(aMouseEvent);
-
-  WidgetMouseEvent* clickEvent =
-    aMouseEvent->WidgetEventPtr()->AsMouseEvent();
-  MOZ_ASSERT(!DetachedFromEditorOrDefaultPrevented(clickEvent));
-
-  if (!Preferences::GetBool("middlemouse.paste", false)) {
-    // Middle click paste isn't enabled.
+  if (clickEvent->button != WidgetMouseEventBase::eMiddleButton ||
+      !WidgetMouseEvent::IsMiddleClickPasteEnabled()) {
     return NS_OK;
   }
 
-  // Set the selection to the point under the mouse cursor:
-  nsCOMPtr<nsINode> parent = aMouseEvent->GetRangeParent();
-  int32_t offset = aMouseEvent->RangeOffset();
-
-  RefPtr<TextEditor> textEditor = mEditorBase->AsTextEditor();
-  MOZ_ASSERT(textEditor);
-
-  RefPtr<Selection> selection = textEditor->GetSelection();
-  if (selection) {
-    selection->Collapse(parent, offset);
+  nsCOMPtr<nsIPresShell> presShell = GetPresShell();
+  if (NS_WARN_IF(!presShell)) {
+    return NS_OK;
+  }
+  nsPresContext* presContext = GetPresContext();
+  if (NS_WARN_IF(!presContext)) {
+    return NS_OK;
   }
-
-  nsresult rv;
-  int32_t clipboard = nsIClipboard::kGlobalClipboard;
-  nsCOMPtr<nsIClipboard> clipboardService =
-    do_GetService("@mozilla.org/widget/clipboard;1", &rv);
-  if (NS_SUCCEEDED(rv)) {
-    bool selectionSupported;
-    rv = clipboardService->SupportsSelectionClipboard(&selectionSupported);
-    if (NS_SUCCEEDED(rv) && selectionSupported) {
-      clipboard = nsIClipboard::kSelectionClipboard;
-    }
+  MOZ_ASSERT(!clickEvent->DefaultPrevented());
+  nsEventStatus status = nsEventStatus_eIgnore;
+  RefPtr<EventStateManager> esm = presContext->EventStateManager();
+  DebugOnly<nsresult> rv =
+    esm->HandleMiddleClickPaste(presShell, clickEvent, &status, textEditor);
+  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+                       "Failed to paste for the middle button click");
+  if (status == nsEventStatus_eConsumeNoDefault) {
+    // Prevent the event from propagating up to be possibly handled
+    // again by the containing window:
+    clickEvent->StopImmediatePropagation();
+    clickEvent->PreventDefault();
   }
-
-  // If the ctrl key is pressed, we'll do paste as quotation.
-  // Would've used the alt key, but the kde wmgr treats alt-middle specially.
-  if (clickEvent->IsControl()) {
-    textEditor->PasteAsQuotationAsAction(clipboard);
-  } else {
-    textEditor->PasteAsAction(clipboard);
-  }
-
-  // Prevent the event from propagating up to be possibly handled
-  // again by the containing window:
-  clickEvent->StopPropagation();
-  clickEvent->PreventDefault();
-
-  // We processed the event, whether drop/paste succeeded or not
   return NS_OK;
 }
 
 bool
 EditorEventListener::NotifyIMEOfMouseButtonEvent(
                        WidgetMouseEvent* aMouseEvent)
 {
   MOZ_ASSERT(aMouseEvent);
--- a/editor/libeditor/EditorEventListener.h
+++ b/editor/libeditor/EditorEventListener.h
@@ -86,18 +86,16 @@ protected:
   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/widget/MouseEvents.h
+++ b/widget/MouseEvents.h
@@ -346,16 +346,21 @@ public:
   /**
    * Returns true if the event is a real mouse event.  Otherwise, i.e., it's
    * a synthesized event by scroll or something, returns false.
    */
   bool IsReal() const
   {
     return mReason == eReal;
   }
+
+  /**
+   * Returns true if middle click paste is enabled.
+   */
+  static bool IsMiddleClickPasteEnabled();
 };
 
 /******************************************************************************
  * mozilla::WidgetDragEvent
  ******************************************************************************/
 
 class WidgetDragEvent : public WidgetMouseEvent
 {
--- a/widget/WidgetEventImpl.cpp
+++ b/widget/WidgetEventImpl.cpp
@@ -646,16 +646,26 @@ WidgetInputEvent::AccelModifier()
 #endif
         break;
     }
   }
   return sAccelModifier;
 }
 
 /******************************************************************************
+ * mozilla::WidgetMouseEvent (MouseEvents.h)
+ ******************************************************************************/
+
+/* static */ bool
+WidgetMouseEvent::IsMiddleClickPasteEnabled()
+{
+  return Preferences::GetBool("middlemouse.paste", false);
+}
+
+/******************************************************************************
  * mozilla::WidgetWheelEvent (MouseEvents.h)
  ******************************************************************************/
 
 /* static */ double
 WidgetWheelEvent::ComputeOverriddenDelta(double aDelta, bool aIsForVertical)
 {
   if (!gfxPrefs::MouseWheelHasRootScrollDeltaOverride()) {
     return aDelta;