Bug 1456377 - Move composition event handlers from EditorBase to TextEditor r?m_kato draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Thu, 19 Apr 2018 00:31:51 +0900
changeset 787057 1ee16be383f4f9b44da7d5f8ca7bb640fb938703
parent 786967 4df5a53ff4206b317b5e1038cae6465f2640e5e8
child 787061 338894042a359dc68ab7fcf0faa46978c61cbf0d
push id107619
push usermasayuki@d-toybox.com
push dateTue, 24 Apr 2018 05:11:33 +0000
reviewersm_kato
bugs1456377
milestone61.0a1
Bug 1456377 - Move composition event handlers from EditorBase to TextEditor r?m_kato Currently, EditorBase handles most composition events. However, only eCompositionChange event handler is in TextEditor and it's the main event of handling composition. Therefore, we should make all composition event handlers in one place for easier to read, and make them non-virtual. MozReview-Commit-ID: BYDhiPyGKvo
editor/libeditor/EditorBase.cpp
editor/libeditor/EditorBase.h
editor/libeditor/EditorEventListener.cpp
editor/libeditor/TextEditor.cpp
editor/libeditor/TextEditor.h
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -2330,75 +2330,16 @@ EditorBase::RestorePreservedSelection(Se
 
 void
 EditorBase::StopPreservingSelection()
 {
   mRangeUpdater.DropSelectionState(mSavedSel);
   mSavedSel.MakeEmpty();
 }
 
-bool
-EditorBase::EnsureComposition(WidgetCompositionEvent* aCompositionEvent)
-{
-  if (mComposition) {
-    return true;
-  }
-  // The compositionstart event must cause creating new TextComposition
-  // instance at being dispatched by IMEStateManager.
-  mComposition = IMEStateManager::GetTextCompositionFor(aCompositionEvent);
-  if (!mComposition) {
-    // However, TextComposition may be committed before the composition
-    // event comes here.
-    return false;
-  }
-  mComposition->StartHandlingComposition(this);
-  return true;
-}
-
-nsresult
-EditorBase::BeginIMEComposition(WidgetCompositionEvent* aCompositionEvent)
-{
-  MOZ_ASSERT(!mComposition, "There is composition already");
-  if (!EnsureComposition(aCompositionEvent)) {
-    return NS_OK;
-  }
-  return NS_OK;
-}
-
-void
-EditorBase::EndIMEComposition()
-{
-  NS_ENSURE_TRUE_VOID(mComposition); // nothing to do
-
-  // commit the IME transaction..we can get at it via the transaction mgr.
-  // Note that this means IME won't work without an undo stack!
-  if (mTransactionManager) {
-    nsCOMPtr<nsITransaction> txn = mTransactionManager->PeekUndoStack();
-    nsCOMPtr<nsIAbsorbingTransaction> plcTxn = do_QueryInterface(txn);
-    if (plcTxn) {
-      DebugOnly<nsresult> rv = plcTxn->Commit();
-      NS_ASSERTION(NS_SUCCEEDED(rv),
-                   "nsIAbsorbingTransaction::Commit() failed");
-    }
-  }
-
-  // Composition string may have hidden the caret.  Therefore, we need to
-  // cancel it here.
-  HideCaret(false);
-
-  // FYI: mComposition still keeps storing container text node of committed
-  //      string, its offset and length.  However, they will be invalidated
-  //      soon since its Destroy() will be called by IMEStateManager.
-  mComposition->EndHandlingComposition(this);
-  mComposition = nullptr;
-
-  // notify editor observers of action
-  NotifyEditorObservers(eNotifyEditorObserversOfEnd);
-}
-
 NS_IMETHODIMP
 EditorBase::ForceCompositionEnd()
 {
   return CommitComposition();
 }
 
 nsresult
 EditorBase::CommitComposition()
--- a/editor/libeditor/EditorBase.h
+++ b/editor/libeditor/EditorBase.h
@@ -653,24 +653,16 @@ public:
 
   /**
    * Creates text node which is marked as "maybe modified frequently".
    */
   static already_AddRefed<nsTextNode> CreateTextNode(nsIDocument& aDocument,
                                                      const nsAString& aData);
 
   /**
-   * IME event handlers.
-   */
-  virtual nsresult BeginIMEComposition(WidgetCompositionEvent* aEvent);
-  virtual nsresult UpdateIMEComposition(
-                     WidgetCompositionEvent* aCompositionChangeEvet) = 0;
-  void EndIMEComposition();
-
-  /**
    * Get preferred IME status of current widget.
    */
   virtual nsresult GetPreferredIMEState(widget::IMEState* aState);
 
   /**
    * Commit composition if there is.
    * Note that when there is a composition, this requests to commit composition
    * to native IME.  Therefore, when there is composition, this can do anything.
@@ -939,28 +931,16 @@ protected:
   {
     // Check for password/readonly/disabled, which are not spellchecked
     // regardless of DOM. Also, check to see if spell check should be skipped
     // or not.
     return !IsPasswordEditor() && !IsReadonly() && !IsDisabled() &&
            !ShouldSkipSpellCheck();
   }
 
-  /**
-   * EnsureComposition() should be called by composition event handlers.  This
-   * tries to get the composition for the event and set it to mComposition.
-   * However, this may fail because the composition may be committed before
-   * the event comes to the editor.
-   *
-   * @return            true if there is a composition.  Otherwise, for example,
-   *                    a composition event handler in web contents moved focus
-   *                    for committing the composition, returns false.
-   */
-  bool EnsureComposition(WidgetCompositionEvent* aCompositionEvent);
-
   nsresult GetSelection(SelectionType aSelectionType,
                         nsISelection** aSelection);
 
   /**
    * (Begin|End)PlaceholderTransaction() are called by AutoPlaceholderBatch.
    * This set of methods are similar to the (Begin|End)Transaction(), but do
    * not use the transaction managers batching feature.  Instead we use a
    * placeholder transaction to wrap up any further transaction while the
--- a/editor/libeditor/EditorEventListener.cpp
+++ b/editor/libeditor/EditorEventListener.cpp
@@ -757,36 +757,16 @@ EditorEventListener::MouseDown(MouseEven
   //      when the event is not acceptable for committing composition.
   if (DetachedFromEditor()) {
     return NS_OK;
   }
   Unused << EnsureCommitCompoisition();
   return NS_OK;
 }
 
-nsresult
-EditorEventListener::HandleChangeComposition(
-                       WidgetCompositionEvent* aCompositionChangeEvent)
-{
-  MOZ_ASSERT(!aCompositionChangeEvent->DefaultPrevented(),
-             "eCompositionChange event shouldn't be cancelable");
-  RefPtr<EditorBase> editorBase(mEditorBase);
-  if (DetachedFromEditor() ||
-      !editorBase->IsAcceptableInputEvent(aCompositionChangeEvent)) {
-    return NS_OK;
-  }
-
-  // if we are readonly or disabled, then do nothing.
-  if (editorBase->IsReadonly() || editorBase->IsDisabled()) {
-    return NS_OK;
-  }
-
-  return editorBase->UpdateIMEComposition(aCompositionChangeEvent);
-}
-
 /**
  * Drag event implementation
  */
 
 nsresult
 EditorEventListener::DragEnter(DragEvent* aDragEvent)
 {
   if (NS_WARN_IF(!aDragEvent) || DetachedFromEditor()) {
@@ -1013,40 +993,73 @@ EditorEventListener::CanDrop(DragEvent* 
   }
   return true;
 }
 
 nsresult
 EditorEventListener::HandleStartComposition(
                        WidgetCompositionEvent* aCompositionStartEvent)
 {
+  if (NS_WARN_IF(!aCompositionStartEvent)) {
+    return NS_ERROR_FAILURE;
+  }
   RefPtr<EditorBase> editorBase(mEditorBase);
   if (DetachedFromEditor() ||
       !editorBase->IsAcceptableInputEvent(aCompositionStartEvent)) {
     return NS_OK;
   }
   // Although, "compositionstart" should be cancelable, but currently,
   // eCompositionStart event coming from widget is not cancelable.
   MOZ_ASSERT(!aCompositionStartEvent->DefaultPrevented(),
              "eCompositionStart shouldn't be cancelable");
-  return editorBase->BeginIMEComposition(aCompositionStartEvent);
+  TextEditor* textEditor = editorBase->AsTextEditor();
+  return textEditor->OnCompositionStart(*aCompositionStartEvent);
+}
+
+nsresult
+EditorEventListener::HandleChangeComposition(
+                       WidgetCompositionEvent* aCompositionChangeEvent)
+{
+  if (NS_WARN_IF(!aCompositionChangeEvent)) {
+    return NS_ERROR_FAILURE;
+  }
+  MOZ_ASSERT(!aCompositionChangeEvent->DefaultPrevented(),
+             "eCompositionChange event shouldn't be cancelable");
+  RefPtr<EditorBase> editorBase(mEditorBase);
+  if (DetachedFromEditor() ||
+      !editorBase->IsAcceptableInputEvent(aCompositionChangeEvent)) {
+    return NS_OK;
+  }
+
+  // if we are readonly or disabled, then do nothing.
+  if (editorBase->IsReadonly() || editorBase->IsDisabled()) {
+    return NS_OK;
+  }
+
+  TextEditor* textEditor = editorBase->AsTextEditor();
+  return textEditor->OnCompositionChange(*aCompositionChangeEvent);
 }
 
 void
 EditorEventListener::HandleEndComposition(
                        WidgetCompositionEvent* aCompositionEndEvent)
 {
+  if (NS_WARN_IF(!aCompositionEndEvent)) {
+    return;
+  }
   RefPtr<EditorBase> editorBase(mEditorBase);
   if (DetachedFromEditor() ||
       !editorBase->IsAcceptableInputEvent(aCompositionEndEvent)) {
     return;
   }
   MOZ_ASSERT(!aCompositionEndEvent->DefaultPrevented(),
              "eCompositionEnd shouldn't be cancelable");
-  editorBase->EndIMEComposition();
+
+  TextEditor* textEditor = editorBase->AsTextEditor();
+  textEditor->OnCompositionEnd(*aCompositionEndEvent);
 }
 
 nsresult
 EditorEventListener::Focus(InternalFocusEvent* aFocusEvent)
 {
   if (NS_WARN_IF(!aFocusEvent) || DetachedFromEditor()) {
     return NS_OK;
   }
--- a/editor/libeditor/TextEditor.cpp
+++ b/editor/libeditor/TextEditor.cpp
@@ -10,16 +10,17 @@
 #include "InternetCiter.h"
 #include "TextEditUtils.h"
 #include "gfxFontUtils.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/EditAction.h"
 #include "mozilla/EditorDOMPoint.h"
 #include "mozilla/EditorUtils.h" // AutoPlaceholderBatch, AutoRules
 #include "mozilla/HTMLEditor.h"
+#include "mozilla/IMEStateManager.h"
 #include "mozilla/mozalloc.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/TextEditRules.h"
 #include "mozilla/TextComposition.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/TextServicesDocument.h"
 #include "mozilla/dom/Selection.h"
 #include "mozilla/dom/Event.h"
@@ -31,16 +32,17 @@
 #include "nsComponentManagerUtils.h"
 #include "nsContentCID.h"
 #include "nsContentList.h"
 #include "nsCopySupport.h"
 #include "nsDebug.h"
 #include "nsDependentSubstring.h"
 #include "nsError.h"
 #include "nsGkAtoms.h"
+#include "nsIAbsorbingTransaction.h"
 #include "nsIClipboard.h"
 #include "nsIContent.h"
 #include "nsIContentIterator.h"
 #include "nsIDOMNode.h"
 #include "nsIDocumentEncoder.h"
 #include "nsINode.h"
 #include "nsIPresShell.h"
 #include "nsISelectionController.h"
@@ -1130,96 +1132,150 @@ TextEditor::SetText(const nsAString& aSt
         NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to insert the new text");
       }
     }
   }
   // post-process
   return rules->DidDoAction(selection, &ruleInfo, rv);
 }
 
+bool
+TextEditor::EnsureComposition(WidgetCompositionEvent& aCompositionEvent)
+{
+  if (mComposition) {
+    return true;
+  }
+  // The compositionstart event must cause creating new TextComposition
+  // instance at being dispatched by IMEStateManager.
+  mComposition = IMEStateManager::GetTextCompositionFor(&aCompositionEvent);
+  if (!mComposition) {
+    // However, TextComposition may be committed before the composition
+    // event comes here.
+    return false;
+  }
+  mComposition->StartHandlingComposition(this);
+  return true;
+}
+
 nsresult
-TextEditor::BeginIMEComposition(WidgetCompositionEvent* aEvent)
+TextEditor::OnCompositionStart(WidgetCompositionEvent& aCompositionStartEvent)
 {
-  NS_ENSURE_TRUE(!mComposition, NS_OK);
+  if (NS_WARN_IF(mComposition)) {
+    return NS_OK;
+  }
 
   if (IsPasswordEditor()) {
-    NS_ENSURE_TRUE(mRules, NS_ERROR_NULL_POINTER);
+    if (NS_WARN_IF(!mRules)) {
+      return NS_ERROR_FAILURE;
+    }
     // Protect the edit rules object from dying
     RefPtr<TextEditRules> rules(mRules);
     rules->ResetIMETextPWBuf();
   }
 
-  return EditorBase::BeginIMEComposition(aEvent);
+  EnsureComposition(aCompositionStartEvent);
+  NS_WARNING_ASSERTION(mComposition, "Failed to get TextComposition instance?");
+  return NS_OK;
 }
 
 nsresult
-TextEditor::UpdateIMEComposition(WidgetCompositionEvent* aCompsitionChangeEvent)
+TextEditor::OnCompositionChange(WidgetCompositionEvent& aCompsitionChangeEvent)
 {
-  MOZ_ASSERT(aCompsitionChangeEvent,
-             "aCompsitionChangeEvent must not be nullptr");
-
-  if (NS_WARN_IF(!aCompsitionChangeEvent)) {
-    return NS_ERROR_INVALID_ARG;
-  }
-
-  MOZ_ASSERT(aCompsitionChangeEvent->mMessage == eCompositionChange,
+  MOZ_ASSERT(aCompsitionChangeEvent.mMessage == eCompositionChange,
              "The event should be eCompositionChange");
 
   if (!EnsureComposition(aCompsitionChangeEvent)) {
     return NS_OK;
   }
 
-  nsCOMPtr<nsIPresShell> ps = GetPresShell();
-  NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED);
+  nsIPresShell* presShell = GetPresShell();
+  if (NS_WARN_IF(!presShell)) {
+    return NS_ERROR_NOT_INITIALIZED;
+  }
 
   RefPtr<Selection> selection = GetSelection();
-  NS_ENSURE_STATE(selection);
+  if (NS_WARN_IF(!selection)) {
+    return NS_ERROR_FAILURE;
+  }
 
   // NOTE: TextComposition should receive selection change notification before
   //       CompositionChangeEventHandlingMarker notifies TextComposition of the
   //       end of handling compositionchange event because TextComposition may
   //       need to ignore selection changes caused by composition.  Therefore,
   //       CompositionChangeEventHandlingMarker must be destroyed after a call
   //       of NotifiyEditorObservers(eNotifyEditorObserversOfEnd) or
   //       NotifiyEditorObservers(eNotifyEditorObserversOfCancel) which notifies
   //       TextComposition of a selection change.
   MOZ_ASSERT(!mPlaceholderBatch,
     "UpdateIMEComposition() must be called without place holder batch");
   TextComposition::CompositionChangeEventHandlingMarker
-    compositionChangeEventHandlingMarker(mComposition, aCompsitionChangeEvent);
+    compositionChangeEventHandlingMarker(mComposition, &aCompsitionChangeEvent);
 
-  RefPtr<nsCaret> caretP = ps->GetCaret();
+  RefPtr<nsCaret> caretP = presShell->GetCaret();
 
   nsresult rv;
   {
     AutoPlaceholderBatch batch(this, nsGkAtoms::IMETxnName);
 
     MOZ_ASSERT(mIsInEditAction,
       "AutoPlaceholderBatch should've notified the observes of before-edit");
-    rv = InsertTextAsAction(aCompsitionChangeEvent->mData);
+    rv = InsertTextAsAction(aCompsitionChangeEvent.mData);
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
       "Failed to insert new composition string");
 
     if (caretP) {
       caretP->SetSelection(selection);
     }
   }
 
   // If still composing, we should fire input event via observer.
   // Note that if the composition will be committed by the following
   // compositionend event, we don't need to notify editor observes of this
   // change.
   // NOTE: We must notify after the auto batch will be gone.
-  if (!aCompsitionChangeEvent->IsFollowedByCompositionEnd()) {
+  if (!aCompsitionChangeEvent.IsFollowedByCompositionEnd()) {
     NotifyEditorObservers(eNotifyEditorObserversOfEnd);
   }
 
   return rv;
 }
 
+void
+TextEditor::OnCompositionEnd(WidgetCompositionEvent& aCompositionEndEvent)
+{
+  if (NS_WARN_IF(!mComposition)) {
+    return;
+  }
+
+  // commit the IME transaction..we can get at it via the transaction mgr.
+  // Note that this means IME won't work without an undo stack!
+  if (mTransactionManager) {
+    nsCOMPtr<nsITransaction> txn = mTransactionManager->PeekUndoStack();
+    nsCOMPtr<nsIAbsorbingTransaction> plcTxn = do_QueryInterface(txn);
+    if (plcTxn) {
+      DebugOnly<nsresult> rv = plcTxn->Commit();
+      NS_ASSERTION(NS_SUCCEEDED(rv),
+                   "nsIAbsorbingTransaction::Commit() failed");
+    }
+  }
+
+  // Composition string may have hidden the caret.  Therefore, we need to
+  // cancel it here.
+  HideCaret(false);
+
+  // FYI: mComposition still keeps storing container text node of committed
+  //      string, its offset and length.  However, they will be invalidated
+  //      soon since its Destroy() will be called by IMEStateManager.
+  mComposition->EndHandlingComposition(this);
+  mComposition = nullptr;
+
+  // notify editor observers of action
+  NotifyEditorObservers(eNotifyEditorObserversOfEnd);
+}
+
 already_AddRefed<nsIContent>
 TextEditor::GetInputEventTargetContent()
 {
   nsCOMPtr<nsIContent> target = do_QueryInterface(mEventTarget);
   return target.forget();
 }
 
 nsresult
--- a/editor/libeditor/TextEditor.h
+++ b/editor/libeditor/TextEditor.h
@@ -116,20 +116,16 @@ public:
    */
   virtual nsresult SelectEntireDocument(Selection* aSelection) override;
 
   virtual nsresult HandleKeyPressEvent(
                      WidgetKeyboardEvent* aKeyboardEvent) override;
 
   virtual dom::EventTarget* GetDOMEventTarget() override;
 
-  virtual nsresult BeginIMEComposition(WidgetCompositionEvent* aEvent) override;
-  virtual nsresult UpdateIMEComposition(
-                     WidgetCompositionEvent* aCompositionChangeEvet) override;
-
   virtual already_AddRefed<nsIContent> GetInputEventTargetContent() override;
 
   /**
    * DeleteSelectionAsAction() removes selection content or content around
    * caret with transactions.  This should be used for handling it as an
    * edit action.
    *
    * @param aDirection          How much range should be removed.
@@ -224,16 +220,39 @@ public:
   /**
    * Replace existed string with a string.
    * This is fast path to replace all string when using single line control.
    *
    * @ param aString   the string to be set
    */
   nsresult SetText(const nsAString& aString);
 
+  /**
+   * OnCompositionStart() is called when editor receives eCompositionStart
+   * event which should be handled in this editor.
+   */
+  nsresult OnCompositionStart(WidgetCompositionEvent& aCompositionStartEvent);
+
+  /**
+   * OnCompositionChange() is called when editor receives an eCompositioChange
+   * event which should be handled in this editor.
+   *
+   * @param aCompositionChangeEvent     eCompositionChange event which should
+   *                                    be handled in this editor.
+   */
+  nsresult
+  OnCompositionChange(WidgetCompositionEvent& aCompositionChangeEvent);
+
+  /**
+   * OnCompositionEnd() is called when editor receives an eCompositionChange
+   * event and it's followed by eCompositionEnd event and after
+   * OnCompositionChange() is called.
+   */
+  void OnCompositionEnd(WidgetCompositionEvent& aCompositionEndEvent);
+
 protected:
   virtual ~TextEditor();
 
   NS_IMETHOD InitRules();
   void BeginEditorInit();
   nsresult EndEditorInit();
 
   already_AddRefed<nsIDocumentEncoder> GetAndInitDocEncoder(
@@ -304,16 +323,28 @@ protected:
   bool CanCutOrCopy(PasswordFieldAllowed aPasswordFieldAllowed);
   bool FireClipboardEvent(EventMessage aEventMessage,
                           int32_t aSelectionType,
                           bool* aActionTaken = nullptr);
 
   bool UpdateMetaCharset(nsIDocument& aDocument,
                          const nsACString& aCharacterSet);
 
+  /**
+   * EnsureComposition() should be called by composition event handlers.  This
+   * tries to get the composition for the event and set it to mComposition.
+   * However, this may fail because the composition may be committed before
+   * the event comes to the editor.
+   *
+   * @return            true if there is a composition.  Otherwise, for example,
+   *                    a composition event handler in web contents moved focus
+   *                    for committing the composition, returns false.
+   */
+  bool EnsureComposition(WidgetCompositionEvent& aCompositionEvent);
+
 protected:
   nsCOMPtr<nsIDocumentEncoder> mCachedDocumentEncoder;
   nsString mCachedDocumentEncoderType;
   int32_t mWrapColumn;
   int32_t mMaxTextLength;
   int32_t mInitTriggerCounter;
   int32_t mNewlineHandling;
   int32_t mCaretStyle;