Bug 1137561 part.3 Make IMMHandler use TextEventDispatcher r=m_kato
authorMasayuki Nakano <masayuki@d-toybox.com>
Wed, 16 Mar 2016 13:47:48 +0900
changeset 328766 24f0cbec55c3dc87a826145e584aa27da2d6be2f
parent 328765 a9ffc169e99cf0006cdc29810e1b41f884a1ff53
child 328767 545efe0fba8180b3443cbd9a21d7a60dcb68bfaf
push id1146
push userCallek@gmail.com
push dateMon, 25 Jul 2016 16:35:44 +0000
treeherdermozilla-release@a55778f9cd5a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersm_kato
bugs1137561
milestone48.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 1137561 part.3 Make IMMHandler use TextEventDispatcher r=m_kato
widget/TextEventDispatcher.h
widget/windows/IMMHandler.cpp
widget/windows/IMMHandler.h
--- a/widget/TextEventDispatcher.h
+++ b/widget/TextEventDispatcher.h
@@ -220,16 +220,25 @@ public:
    * ClearPendingComposition() makes this instance forget pending composition.
    */
   void ClearPendingComposition()
   {
     mPendingComposition.Clear();
   }
 
   /**
+   * GetPendingCompositionClauses() returns text ranges which was appended by
+   * AppendClauseToPendingComposition() or SetPendingComposition().
+   */
+  const TextRangeArray* GetPendingCompositionClauses() const
+  {
+    return mPendingComposition.GetClauses();
+  }
+
+  /**
    * @see nsIWidget::NotifyIME()
    */
   nsresult NotifyIME(const IMENotification& aIMENotification);
 
   /**
    * DispatchKeyboardEvent() maybe dispatches aKeyboardEvent.
    *
    * @param aMessage        Must be eKeyDown or eKeyUp.
@@ -296,16 +305,17 @@ private:
     PendingComposition();
     nsresult SetString(const nsAString& aString);
     nsresult AppendClause(uint32_t aLength, uint32_t aAttribute);
     nsresult SetCaret(uint32_t aOffset, uint32_t aLength);
     nsresult Set(const nsAString& aString, const TextRangeArray* aRanges);
     nsresult Flush(TextEventDispatcher* aDispatcher,
                    nsEventStatus& aStatus,
                    const WidgetEventTime* aEventTime);
+    const TextRangeArray* GetClauses() const { return mClauses; }
     void Clear();
 
   private:
     nsString mString;
     RefPtr<TextRangeArray> mClauses;
     TextRange mCaret;
 
     void EnsureClauseArray();
--- a/widget/windows/IMMHandler.cpp
+++ b/widget/windows/IMMHandler.cpp
@@ -1101,16 +1101,17 @@ void
 IMMHandler::OnIMEStartCompositionOnPlugin(nsWindow* aWindow,
                                           WPARAM wParam,
                                           LPARAM lParam)
 {
   MOZ_LOG(gIMMLog, LogLevel::Info,
     ("IMM: OnIMEStartCompositionOnPlugin, hWnd=%08x, mIsComposingOnPlugin=%s",
      aWindow->GetWindowHandle(), GetBoolName(mIsComposingOnPlugin)));
   mIsComposingOnPlugin = true;
+  mDispatcher = GetTextEventDispatcherFor(aWindow);
   mComposingWindow = aWindow;
   IMEContext context(aWindow);
   SetIMERelatedWindowsPosOnPlugin(aWindow, context);
   // On widnowless plugin, we should assume that the focused editor is always
   // in horizontal writing mode.
   AdjustCompositionFont(aWindow, context, WritingMode());
 }
 
@@ -1126,21 +1127,23 @@ IMMHandler::OnIMECompositionOnPlugin(nsW
      aWindow->GetWindowHandle(), lParam, GetBoolName(mIsComposingOnPlugin),
      GetBoolName(lParam & GCS_RESULTSTR), GetBoolName(lParam & GCS_COMPSTR),
      GetBoolName(lParam & GCS_COMPATTR), GetBoolName(lParam & GCS_COMPCLAUSE),
      GetBoolName(lParam & GCS_CURSORPOS)));
   // We should end composition if there is a committed string.
   if (IS_COMMITTING_LPARAM(lParam)) {
     mIsComposingOnPlugin = false;
     mComposingWindow = nullptr;
+    mDispatcher = nullptr;
     return;
   }
   // Continue composition if there is still a string being composed.
   if (IS_COMPOSING_LPARAM(lParam)) {
     mIsComposingOnPlugin = true;
+    mDispatcher = GetTextEventDispatcherFor(aWindow);
     mComposingWindow = aWindow;
     IMEContext context(aWindow);
     SetIMERelatedWindowsPosOnPlugin(aWindow, context);
   }
 }
 
 void
 IMMHandler::OnIMEEndCompositionOnPlugin(nsWindow* aWindow,
@@ -1148,16 +1151,17 @@ IMMHandler::OnIMEEndCompositionOnPlugin(
                                         LPARAM lParam)
 {
   MOZ_LOG(gIMMLog, LogLevel::Info,
     ("IMM: OnIMEEndCompositionOnPlugin, hWnd=%08x, mIsComposingOnPlugin=%s",
      aWindow->GetWindowHandle(), GetBoolName(mIsComposingOnPlugin)));
 
   mIsComposingOnPlugin = false;
   mComposingWindow = nullptr;
+  mDispatcher = nullptr;
 
   if (mNativeCaretIsCreated) {
     ::DestroyCaret();
     mNativeCaretIsCreated = false;
   }
 }
 
 bool
@@ -1253,16 +1257,23 @@ IMMHandler::OnCharOnPlugin(nsWindow* aWi
   // WM_CHAR on plug-in is always handled by nsWindow.
   return false;
 }
 
 /****************************************************************************
  * others
  ****************************************************************************/
 
+TextEventDispatcher*
+IMMHandler::GetTextEventDispatcherFor(nsWindow* aWindow)
+{
+  return aWindow == mComposingWindow && mDispatcher ?
+    mDispatcher : aWindow->GetTextEventDispatcher();
+}
+
 void
 IMMHandler::HandleStartComposition(nsWindow* aWindow,
                                    const IMEContext& aContext)
 {
   NS_PRECONDITION(!mIsComposing,
     "HandleStartComposition is called but mIsComposing is TRUE");
 
   Selection& selection = GetSelection();
@@ -1273,23 +1284,37 @@ IMMHandler::HandleStartComposition(nsWin
     return;
   }
 
   AdjustCompositionFont(aWindow, aContext, selection.mWritingMode);
 
   mCompositionStart = selection.mOffset;
   mCursorPosition = NO_IME_CARET;
 
-  WidgetCompositionEvent event(true, eCompositionStart, aWindow);
-  LayoutDeviceIntPoint point(0, 0);
-  aWindow->InitEvent(event, &point);
-  DispatchEvent(aWindow, event);
+  RefPtr<TextEventDispatcher> dispatcher = GetTextEventDispatcherFor(aWindow);
+  nsresult rv = dispatcher->BeginNativeInputTransaction();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    MOZ_LOG(gIMMLog, LogLevel::Error,
+      ("IMM: HandleStartComposition, FAILED due to "
+       "TextEventDispatcher::BeginNativeInputTransaction() failure"));
+    return;
+  }
+  WidgetEventTime eventTime = aWindow->CurrentMessageWidgetEventTime();
+  nsEventStatus status;
+  rv = dispatcher->StartComposition(status, &eventTime);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    MOZ_LOG(gIMMLog, LogLevel::Error,
+      ("IMM: HandleStartComposition, FAILED, due to "
+       "TextEventDispatcher::StartComposition() failure"));
+    return;
+  }
 
   mIsComposing = true;
   mComposingWindow = aWindow;
+  mDispatcher = dispatcher;
 
   MOZ_LOG(gIMMLog, LogLevel::Info,
     ("IMM: HandleStartComposition, START composition, mCompositionStart=%ld",
      mCompositionStart));
 }
 
 bool
 IMMHandler::HandleComposition(nsWindow* aWindow,
@@ -1542,27 +1567,37 @@ IMMHandler::HandleEndComposition(nsWindo
      aWindow, aCommitString,
      aCommitString ? NS_ConvertUTF16toUTF8(*aCommitString).get() : ""));
 
   if (mNativeCaretIsCreated) {
     ::DestroyCaret();
     mNativeCaretIsCreated = false;
   }
 
-  EventMessage message =
-    aCommitString ? eCompositionCommit : eCompositionCommitAsIs;
-  WidgetCompositionEvent compositionCommitEvent(true, message, aWindow);
-  LayoutDeviceIntPoint point(0, 0);
-  aWindow->InitEvent(compositionCommitEvent, &point);
-  if (aCommitString) {
-    compositionCommitEvent.mData = *aCommitString;
+  RefPtr<TextEventDispatcher> dispatcher = GetTextEventDispatcherFor(aWindow);
+  nsresult rv = dispatcher->BeginNativeInputTransaction();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    MOZ_LOG(gIMMLog, LogLevel::Error,
+      ("IMM: HandleEndComposition, FAILED due to "
+       "TextEventDispatcher::BeginNativeInputTransaction() failure"));
+    return;
   }
-  DispatchEvent(aWindow, compositionCommitEvent);
+  WidgetEventTime eventTime = aWindow->CurrentMessageWidgetEventTime();
+  nsEventStatus status;
+  rv = dispatcher->CommitComposition(status, aCommitString, &eventTime);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    MOZ_LOG(gIMMLog, LogLevel::Error,
+      ("IMM: HandleStartComposition, FAILED, due to "
+       "TextEventDispatcher::CommitComposition() failure"));
+    return;
+  }
   mIsComposing = false;
+  // XXX aWindow and mComposingWindow are always same??
   mComposingWindow = nullptr;
+  mDispatcher = nullptr;
 }
 
 bool
 IMMHandler::HandleReconvert(nsWindow* aWindow,
                             LPARAM lParam,
                             LRESULT* oResult)
 {
   *oResult = 0;
@@ -1924,126 +1959,137 @@ IMMHandler::DispatchCompositionChangeEve
   // fire compositionchange event during composing.
   if (!ShouldDrawCompositionStringOurselves()) {
     // But we need to adjust composition window pos and native caret pos, here.
     SetIMERelatedWindowsPos(aWindow, aContext);
     return;
   }
 
   RefPtr<nsWindow> kungFuDeathGrip(aWindow);
-
-  LayoutDeviceIntPoint point(0, 0);
-
-  WidgetCompositionEvent event(true, eCompositionChange, aWindow);
-
-  aWindow->InitEvent(event, &point);
-
-  event.mRanges = CreateTextRangeArray();
-  event.mData = mCompositionString;
-
-  DispatchEvent(aWindow, event);
+  RefPtr<TextEventDispatcher> dispatcher = GetTextEventDispatcherFor(aWindow);
+  nsresult rv = dispatcher->BeginNativeInputTransaction();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    MOZ_LOG(gIMMLog, LogLevel::Error,
+      ("IMM: DispatchCompositionChangeEvent, FAILED due to "
+       "TextEventDispatcher::BeginNativeInputTransaction() failure"));
+    return;
+  }
 
-  // Calling SetIMERelatedWindowsPos will be failure on e10s at this point.
-  // compositionchange event will notify NOTIFY_IME_OF_COMPOSITION_UPDATE, then
-  // it will call SetIMERelatedWindowsPos.
-}
+  // NOTE: Calling SetIMERelatedWindowsPos() from this method will be failure
+  //       in e10s mode.  compositionchange event will notify this of
+  //       NOTIFY_IME_OF_COMPOSITION_UPDATE, then SetIMERelatedWindowsPos()
+  //       will be called.
 
-already_AddRefed<TextRangeArray>
-IMMHandler::CreateTextRangeArray()
-{
-  // Sogou (Simplified Chinese IME) returns contradictory values: The cursor
-  // position is actual cursor position. However, other values (composition
-  // string and attributes) are empty. So, if you want to remove following
-  // assertion, be careful.
-  NS_ASSERTION(ShouldDrawCompositionStringOurselves(),
-    "CreateTextRangeArray is called when we don't need to fire "
-    "compositionchange event");
+  // XXX Sogou (Simplified Chinese IME) returns contradictory values:
+  //     The cursor position is actual cursor position. However, other values
+  //     (composition string and attributes) are empty.
 
-  RefPtr<TextRangeArray> textRangeArray = new TextRangeArray();
-
-  TextRange range;
   if (mCompositionString.IsEmpty()) {
     // Don't append clause information if composition string is empty.
-  } else if (mClauseArray.Length() == 0) {
+  } else if (mClauseArray.IsEmpty()) {
     // Some IMEs don't return clause array information, then, we assume that
     // all characters in the composition string are in one clause.
-    range.mStartOffset = 0;
-    range.mEndOffset = mCompositionString.Length();
-    range.mRangeType = NS_TEXTRANGE_RAWINPUT;
-    textRangeArray->AppendElement(range);
-
     MOZ_LOG(gIMMLog, LogLevel::Info,
-      ("IMM: CreateTextRangeArray, mClauseLength=0"));
+      ("IMM: DispatchCompositionChangeEvent, mClauseArray.Length()=0"));
+    rv =dispatcher->SetPendingComposition(mCompositionString, nullptr);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      MOZ_LOG(gIMMLog, LogLevel::Error,
+        ("IMM: DispatchCompositionChangeEvent, FAILED due to"
+         "TextEventDispatcher::SetPendingComposition() failure"));
+      return;
+    }
   } else {
     // iterate over the attributes
+    rv = dispatcher->SetPendingCompositionString(mCompositionString);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      MOZ_LOG(gIMMLog, LogLevel::Error,
+        ("IMM: DispatchCompositionChangeEvent, FAILED due to"
+         "TextEventDispatcher::SetPendingCompositionString() failure"));
+      return;
+    }
     uint32_t lastOffset = 0;
     for (uint32_t i = 0; i < mClauseArray.Length() - 1; i++) {
       uint32_t current = mClauseArray[i + 1];
       if (current > mCompositionString.Length()) {
         MOZ_LOG(gIMMLog, LogLevel::Info,
-          ("IMM: CreateTextRangeArray, mClauseArray[%ld]=%lu. "
+          ("IMM: DispatchCompositionChangeEvent, mClauseArray[%ld]=%lu. "
            "This is larger than mCompositionString.Length()=%lu",
            i + 1, current, mCompositionString.Length()));
         current = int32_t(mCompositionString.Length());
       }
 
-      range.mRangeType = PlatformToNSAttr(mAttributeArray[lastOffset]);
-      range.mStartOffset = lastOffset;
-      range.mEndOffset = current;
-      textRangeArray->AppendElement(range);
+      uint32_t length = current - lastOffset;
+      uint32_t attr = PlatformToNSAttr(mAttributeArray[lastOffset]);
+      rv = dispatcher->AppendClauseToPendingComposition(length, attr);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        MOZ_LOG(gIMMLog, LogLevel::Error,
+          ("IMM: DispatchCompositionChangeEvent, FAILED due to"
+           "TextEventDispatcher::AppendClauseToPendingComposition() failure"));
+        return;
+      }
 
       lastOffset = current;
 
       MOZ_LOG(gIMMLog, LogLevel::Info,
-        ("IMM: CreateTextRangeArray, index=%ld, rangeType=%s, range=[%lu-%lu]",
-         i, GetRangeTypeName(range.mRangeType), range.mStartOffset,
-         range.mEndOffset));
+        ("IMM: DispatchCompositionChangeEvent, index=%ld, rangeType=%s, "
+         "range length=%lu",
+         i, GetRangeTypeName(attr), length));
     }
   }
 
   if (mCursorPosition == NO_IME_CARET) {
     MOZ_LOG(gIMMLog, LogLevel::Info,
-      ("IMM: CreateTextRangeArray, no caret"));
-    return textRangeArray.forget();
-  }
+      ("IMM: DispatchCompositionChangeEvent, no caret"));
+  } else {
+    uint32_t cursor = static_cast<uint32_t>(mCursorPosition);
+    if (cursor > mCompositionString.Length()) {
+      MOZ_LOG(gIMMLog, LogLevel::Info,
+        ("IMM: CreateTextRangeArray, mCursorPosition=%ld. "
+         "This is larger than mCompositionString.Length()=%lu",
+         mCursorPosition, mCompositionString.Length()));
+      cursor = mCompositionString.Length();
+    }
 
-  uint32_t cursor = static_cast<uint32_t>(mCursorPosition);
-  if (cursor > mCompositionString.Length()) {
-    MOZ_LOG(gIMMLog, LogLevel::Info,
-      ("IMM: CreateTextRangeArray, mCursorPosition=%ld. "
-       "This is larger than mCompositionString.Length()=%lu",
-       mCursorPosition, mCompositionString.Length()));
-    cursor = mCompositionString.Length();
+    // If caret is in the target clause, the target clause will be painted as
+    // normal selection range.  Since caret shouldn't be in selection range on
+    // Windows, we shouldn't append caret range in such case.
+    const TextRangeArray* clauses = dispatcher->GetPendingCompositionClauses();
+    const TextRange* targetClause =
+      clauses ? clauses->GetTargetClause() : nullptr;
+    if (targetClause &&
+        cursor >= targetClause->mStartOffset &&
+        cursor <= targetClause->mEndOffset) {
+      // Forget the caret position specified by IME since Gecko's caret position
+      // will be at the end of composition string.
+      mCursorPosition = NO_IME_CARET;
+      MOZ_LOG(gIMMLog, LogLevel::Info,
+        ("IMM: CreateTextRangeArray, no caret due to it's in the target "
+         "clause, now, mCursorPosition is NO_IME_CARET"));
+    }
+
+    if (mCursorPosition != NO_IME_CARET) {
+      rv = dispatcher->SetCaretInPendingComposition(cursor, 0);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        MOZ_LOG(gIMMLog, LogLevel::Error,
+          ("IMM: DispatchCompositionChangeEvent, FAILED due to"
+           "TextEventDispatcher::SetCaretInPendingComposition() failure"));
+        return;
+      }
+    }
   }
 
-  // If caret is in the target clause, the target clause will be painted as
-  // normal selection range.  Since caret shouldn't be in selection range on
-  // Windows, we shouldn't append caret range in such case.
-  const TextRange* targetClause = textRangeArray->GetTargetClause();
-  if (targetClause &&
-      cursor >= targetClause->mStartOffset &&
-      cursor <= targetClause->mEndOffset) {
-    // Forget the caret position specified by IME since Gecko's caret position
-    // will be at the end of composition string.
-    mCursorPosition = NO_IME_CARET;
-    MOZ_LOG(gIMMLog, LogLevel::Info,
-      ("IMM: CreateTextRangeArray, no caret due to it's in the target clause, "
-       "now, mCursorPosition is NO_IME_CARET"));
-    return textRangeArray.forget();
+  WidgetEventTime eventTime = aWindow->CurrentMessageWidgetEventTime();
+  nsEventStatus status;
+  rv = dispatcher->FlushPendingComposition(status, &eventTime);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    MOZ_LOG(gIMMLog, LogLevel::Error,
+      ("IMM: DispatchCompositionChangeEvent, FAILED due to"
+       "TextEventDispatcher::FlushPendingComposition() failure"));
+    return;
   }
-
-  range.mStartOffset = range.mEndOffset = cursor;
-  range.mRangeType = NS_TEXTRANGE_CARETPOSITION;
-  textRangeArray->AppendElement(range);
-
-  MOZ_LOG(gIMMLog, LogLevel::Info,
-    ("IMM: CreateTextRangeArray, caret position=%ld",
-     range.mStartOffset));
-
-  return textRangeArray.forget();
 }
 
 void
 IMMHandler::GetCompositionString(const IMEContext& aContext,
                                  DWORD aIndex,
                                  nsAString& aCompositionString) const
 {
   aCompositionString.Truncate();
--- a/widget/windows/IMMHandler.h
+++ b/widget/windows/IMMHandler.h
@@ -8,16 +8,17 @@
 
 #include "nscore.h"
 #include <windows.h>
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsIWidget.h"
 #include "mozilla/EventForwards.h"
+#include "mozilla/TextEventDispatcher.h"
 #include "nsRect.h"
 #include "WritingModes.h"
 #include "npapi.h"
 
 class nsWindow;
 
 namespace mozilla {
 namespace widget {
@@ -369,17 +370,16 @@ protected:
    * I.e., this should be called only during composing.  If a composition is
    * being committed, only HandleCompositionEnd() should be called.
    *
    * @param aWindow     The window which has the composition.
    * @param aContext    Native IME context which has the composition.
    */
   void DispatchCompositionChangeEvent(nsWindow* aWindow,
                                       const IMEContext& aContext);
-  already_AddRefed<mozilla::TextRangeArray> CreateTextRangeArray();
 
   nsresult EnsureClauseArray(int32_t aCount);
   nsresult EnsureAttributeArray(int32_t aCount);
 
   /**
    * When WM_IME_CHAR is received and passed to DefWindowProc, we need to
    * record the messages.  In other words, we should record the messages
    * when we receive WM_IME_CHAR on windowless plug-in (if we have focus,
@@ -409,17 +409,20 @@ protected:
   void EnqueueIMECharRecords(WPARAM wParam, LPARAM lParam)
   {
     MSG msg;
     msg.wParam = wParam;
     msg.lParam = lParam;
     mPassedIMEChar.AppendElement(msg);
   }
 
+  TextEventDispatcher* GetTextEventDispatcherFor(nsWindow* aWindow);
+
   nsWindow* mComposingWindow;
+  RefPtr<TextEventDispatcher> mDispatcher;
   nsString  mCompositionString;
   InfallibleTArray<uint32_t> mClauseArray;
   InfallibleTArray<uint8_t> mAttributeArray;
 
   int32_t mCursorPosition;
   uint32_t mCompositionStart;
 
   struct Selection