Bug 1137561 part.2 Make TSFTextStore use TextEventDispatcher r=m_kato
authorMasayuki Nakano <masayuki@d-toybox.com>
Wed, 16 Mar 2016 13:47:48 +0900
changeset 288904 a9ffc169e99cf0006cdc29810e1b41f884a1ff53
parent 288903 807b3c1dfc4799e019a04f2739c8e0f56bd0b6f1
child 288905 24f0cbec55c3dc87a826145e584aa27da2d6be2f
push id30091
push usercbook@mozilla.com
push dateWed, 16 Mar 2016 09:56:40 +0000
treeherdermozilla-central@341344bdec8f [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.2 Make TSFTextStore use TextEventDispatcher r=m_kato
widget/TextEventDispatcher.cpp
widget/windows/TSFTextStore.cpp
widget/windows/TSFTextStore.h
widget/windows/nsWindow.cpp
widget/windows/nsWindow.h
widget/windows/nsWindowBase.h
--- a/widget/TextEventDispatcher.cpp
+++ b/widget/TextEventDispatcher.cpp
@@ -314,16 +314,19 @@ TextEventDispatcher::CommitComposition(n
                                          eCompositionCommitAsIs;
   WidgetCompositionEvent compositionCommitEvent(true, message, widget);
   InitEvent(compositionCommitEvent);
   if (aEventTime) {
     compositionCommitEvent.AssignEventTime(*aEventTime);
   }
   if (message == eCompositionCommit) {
     compositionCommitEvent.mData = *aCommitString;
+    // Don't send CRLF, replace it with LF here.
+    compositionCommitEvent.mData.ReplaceSubstring(NS_LITERAL_STRING("\r\n"),
+                                                  NS_LITERAL_STRING("\n"));
   }
   rv = DispatchEvent(widget, compositionCommitEvent, aStatus);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
--- a/widget/windows/TSFTextStore.cpp
+++ b/widget/windows/TSFTextStore.cpp
@@ -8,16 +8,17 @@
 
 #include "mozilla/Logging.h"
 
 #include "nscore.h"
 #include "nsWindow.h"
 #include "nsPrintfCString.h"
 #include "WinUtils.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/TextEventDispatcher.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/WindowsVersion.h"
 #include "nsIXULRuntime.h"
 
 #define INPUTSCOPE_INIT_GUID
 #define TEXTATTRS_INIT_GUID
 #include "TSFTextStore.h"
 
@@ -1362,16 +1363,29 @@ TSFTextStore::Init(nsWindowBase* aWidget
   HRESULT hr = sThreadMgr->CreateDocumentMgr(getter_AddRefs(mDocumentMgr));
   if (FAILED(hr)) {
     MOZ_LOG(sTextStoreLog, LogLevel::Error,
       ("TSF: 0x%p   TSFTextStore::Init() FAILED to create DocumentMgr "
        "(0x%08X)", this, hr));
     return false;
   }
   mWidget = aWidget;
+  if (NS_WARN_IF(!mWidget)) {
+    MOZ_LOG(sTextStoreLog, LogLevel::Error,
+      ("TSF: 0x%p   TSFTextStore::Init() FAILED "
+       "due to aWidget is nullptr ", this));
+    return false;
+  }
+  mDispatcher = mWidget->GetTextEventDispatcher();
+  if (NS_WARN_IF(!mDispatcher)) {
+    MOZ_LOG(sTextStoreLog, LogLevel::Error,
+      ("TSF: 0x%p   TSFTextStore::Init() FAILED "
+       "due to aWidget->GetTextEventDispatcher() failure", this));
+    return false;
+  }
 
   // Create context and add it to document manager
   hr = mDocumentMgr->CreateContext(sClientId, 0,
                                    static_cast<ITextStoreACP*>(this),
                                    getter_AddRefs(mContext), &mEditCookie);
   if (FAILED(hr)) {
     MOZ_LOG(sTextStoreLog, LogLevel::Error,
       ("TSF: 0x%p   TSFTextStore::Init() FAILED to create the context "
@@ -1437,16 +1451,17 @@ TSFTextStore::Destroy()
 
   mContext = nullptr;
   if (mDocumentMgr) {
     mDocumentMgr->Pop(TF_POPF_ALL);
     mDocumentMgr = nullptr;
   }
   mSink = nullptr;
   mWidget = nullptr;
+  mDispatcher = nullptr;
 
   if (!mMouseTrackers.IsEmpty()) {
     MOZ_LOG(sTextStoreLog, LogLevel::Debug,
       ("TSF: 0x%p   TSFTextStore::Destroy(), removing a mouse tracker...",
        this));
     mMouseTrackers.Clear();
   }
 
@@ -1698,16 +1713,23 @@ TSFTextStore::FlushPendingActions()
     mPendingActions.Clear();
     mLockedContent.Clear();
     mPendingOnSelectionChange = false;
     mHasReturnedNoLayoutError = false;
     return;
   }
 
   RefPtr<nsWindowBase> kungFuDeathGrip(mWidget);
+  nsresult rv = mDispatcher->BeginNativeInputTransaction();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    MOZ_LOG(sTextStoreLog, LogLevel::Error,
+      ("TSF: 0x%p   TSFTextStore::FlushPendingActions() "
+       "FAILED due to BeginNativeInputTransaction() failure", this));
+    return;
+  }
   for (uint32_t i = 0; i < mPendingActions.Length(); i++) {
     PendingAction& action = mPendingActions[i];
     switch (action.mType) {
       case PendingAction::COMPOSITION_START: {
         MOZ_LOG(sTextStoreLog, LogLevel::Debug,
                ("TSF: 0x%p   TSFTextStore::FlushPendingActions() "
                 "flushing COMPOSITION_START={ mSelectionStart=%d, "
                 "mSelectionLength=%d }",
@@ -1723,126 +1745,100 @@ TSFTextStore::FlushPendingActions()
           DispatchEvent(selectionSet);
           if (!selectionSet.mSucceeded) {
             MOZ_LOG(sTextStoreLog, LogLevel::Error,
                    ("TSF: 0x%p   TSFTextStore::FlushPendingActions() "
                     "FAILED due to eSetSelection failure", this));
             break;
           }
         }
-        MOZ_LOG(sTextStoreLog, LogLevel::Debug,
-               ("TSF: 0x%p   TSFTextStore::FlushPendingActions() "
-                "dispatching compositionstart event...", this));
-        WidgetCompositionEvent compositionStart(true, eCompositionStart,
-                                                mWidget);
-        mWidget->InitEvent(compositionStart);
+
         // eCompositionStart always causes NOTIFY_IME_OF_COMPOSITION_UPDATE.
         // Therefore, we should wait to clear the locked content until it's
         // notified.
         mDeferClearingLockedContent = true;
-        DispatchEvent(compositionStart);
+
+        MOZ_LOG(sTextStoreLog, LogLevel::Debug,
+               ("TSF: 0x%p   TSFTextStore::FlushPendingActions() "
+                "dispatching compositionstart event...", this));
+        WidgetEventTime eventTime = mWidget->CurrentMessageWidgetEventTime();
+        nsEventStatus status;
+        rv = mDispatcher->StartComposition(status, &eventTime);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          MOZ_LOG(sTextStoreLog, LogLevel::Error,
+            ("TSF: 0x%p   TSFTextStore::FlushPendingActions() "
+             "FAILED to dispatch compositionstart event.", this));
+          mDeferClearingLockedContent = false;
+        }
         if (!mWidget || mWidget->Destroyed()) {
           break;
         }
         break;
       }
       case PendingAction::COMPOSITION_UPDATE: {
         MOZ_LOG(sTextStoreLog, LogLevel::Debug,
                ("TSF: 0x%p   TSFTextStore::FlushPendingActions() "
                 "flushing COMPOSITION_UPDATE={ mData=\"%s\", "
                 "mRanges=0x%p, mRanges->Length()=%d }",
                 this, NS_ConvertUTF16toUTF8(action.mData).get(), action.mRanges.get(),
                 action.mRanges ? action.mRanges->Length() : 0));
 
-        if (!action.mRanges) {
-          NS_WARNING("How does this case occur?");
-          action.mRanges = new TextRangeArray();
-        }
-
-        // Adjust offsets in the ranges for XP linefeed character (only \n).
-        // XXX Following code is the safest approach.  However, it wastes
-        //     a little performance.  For ensuring the clauses do not
-        //     overlap each other, we should redesign TextRange later.
-        for (uint32_t i = 0; i < action.mRanges->Length(); ++i) {
-          TextRange& range = action.mRanges->ElementAt(i);
-          TextRange nativeRange = range;
-          if (nativeRange.mStartOffset > 0) {
-            nsAutoString preText(
-              Substring(action.mData, 0, nativeRange.mStartOffset));
-            preText.ReplaceSubstring(NS_LITERAL_STRING("\r\n"),
-                                     NS_LITERAL_STRING("\n"));
-            range.mStartOffset = preText.Length();
-          }
-          if (nativeRange.Length() == 0) {
-            range.mEndOffset = range.mStartOffset;
-          } else {
-            nsAutoString clause(
-              Substring(action.mData,
-                        nativeRange.mStartOffset, nativeRange.Length()));
-            clause.ReplaceSubstring(NS_LITERAL_STRING("\r\n"),
-                                    NS_LITERAL_STRING("\n"));
-            range.mEndOffset = range.mStartOffset + clause.Length();
+        // eCompositionChange causes a DOM text event, the IME will be notified
+        // of NOTIFY_IME_OF_COMPOSITION_UPDATE.  In this case, we should not
+        // clear the locked content until we notify the IME of the composition
+        // update.
+        mDeferClearingLockedContent = true;
+
+        rv = mDispatcher->SetPendingComposition(action.mData,
+                                                action.mRanges);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          MOZ_LOG(sTextStoreLog, LogLevel::Error,
+            ("TSF: 0x%p   TSFTextStore::FlushPendingActions() "
+             "FAILED to setting pending composition...", this));
+          mDeferClearingLockedContent = false;
+        } else {
+          MOZ_LOG(sTextStoreLog, LogLevel::Debug,
+            ("TSF: 0x%p   TSFTextStore::FlushPendingActions() "
+             "dispatching compositionchange event...", this));
+          WidgetEventTime eventTime = mWidget->CurrentMessageWidgetEventTime();
+          nsEventStatus status;
+          rv = mDispatcher->FlushPendingComposition(status, &eventTime);
+          if (NS_WARN_IF(NS_FAILED(rv))) {
+            MOZ_LOG(sTextStoreLog, LogLevel::Error,
+              ("TSF: 0x%p   TSFTextStore::FlushPendingActions() "
+               "FAILED to dispatch compositionchange event.", this));
+            mDeferClearingLockedContent = false;
           }
+          // Be aware, the mWidget might already have been destroyed.
         }
-
-        action.mData.ReplaceSubstring(NS_LITERAL_STRING("\r\n"),
-                                      NS_LITERAL_STRING("\n"));
-
-        MOZ_LOG(sTextStoreLog, LogLevel::Debug,
-               ("TSF: 0x%p   TSFTextStore::FlushPendingActions(), "
-                "dispatching compositionchange event...", this));
-        WidgetCompositionEvent compositionChange(true, eCompositionChange,
-                                                 mWidget);
-        mWidget->InitEvent(compositionChange);
-        compositionChange.mData = action.mData;
-        if (action.mRanges->IsEmpty()) {
-          TextRange wholeRange;
-          wholeRange.mStartOffset = 0;
-          wholeRange.mEndOffset = compositionChange.mData.Length();
-          wholeRange.mRangeType = NS_TEXTRANGE_RAWINPUT;
-          action.mRanges->AppendElement(wholeRange);
-        }
-        compositionChange.mRanges = action.mRanges;
-        // When the eCompositionChange causes a DOM text event,
-        // the IME will be notified of NOTIFY_IME_OF_COMPOSITION_UPDATE.  In
-        // such case, we should not clear the locked content until we notify
-        // the IME of the composition update.
-        if (compositionChange.CausesDOMTextEvent()) {
-          mDeferClearingLockedContent = true;
-        }
-        DispatchEvent(compositionChange);
-        // Be aware, the mWidget might already have been destroyed.
         break;
       }
       case PendingAction::COMPOSITION_END: {
         MOZ_LOG(sTextStoreLog, LogLevel::Debug,
                ("TSF: 0x%p   TSFTextStore::FlushPendingActions() "
                 "flushing COMPOSITION_END={ mData=\"%s\" }",
                 this, NS_ConvertUTF16toUTF8(action.mData).get()));
 
-        action.mData.ReplaceSubstring(NS_LITERAL_STRING("\r\n"),
-                                      NS_LITERAL_STRING("\n"));
+        // Dispatching eCompositionCommit causes a DOM text event, then,
+        // the IME will be notified of NOTIFY_IME_OF_COMPOSITION_UPDATE.  In
+        // this case, we should not clear the locked content until we notify
+        // the IME of the composition update.
+        mDeferClearingLockedContent = true;
 
         MOZ_LOG(sTextStoreLog, LogLevel::Debug,
                ("TSF: 0x%p   TSFTextStore::FlushPendingActions(), "
                 "dispatching compositioncommit event...", this));
-        WidgetCompositionEvent compositionCommit(true, eCompositionCommit,
-                                                 mWidget);
-        mWidget->InitEvent(compositionCommit);
-        compositionCommit.mData = action.mData;
-        // When the eCompositionCommit causes a DOM text event,
-        // the IME will be notified of NOTIFY_IME_OF_COMPOSITION_UPDATE.  In
-        // such case, we should not clear the locked content until we notify
-        // the IME of the composition update.
-        if (compositionCommit.CausesDOMTextEvent()) {
-          mDeferClearingLockedContent = true;
-        }
-        DispatchEvent(compositionCommit);
-        if (!mWidget || mWidget->Destroyed()) {
-          break;
+        WidgetEventTime eventTime = mWidget->CurrentMessageWidgetEventTime();
+        nsEventStatus status;
+        rv = mDispatcher->CommitComposition(status, &action.mData, &eventTime);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          MOZ_LOG(sTextStoreLog, LogLevel::Error,
+            ("TSF: 0x%p   TSFTextStore::FlushPendingActions() "
+             "FAILED to dispatch compositioncommit event.", this));
+          mDeferClearingLockedContent = false;
         }
         break;
       }
       case PendingAction::SET_SELECTION: {
         MOZ_LOG(sTextStoreLog, LogLevel::Debug,
                ("TSF: 0x%p   TSFTextStore::FlushPendingActions() "
                 "flushing SET_SELECTION={ mSelectionStart=%d, "
                 "mSelectionLength=%d, mSelectionReversed=%s }",
--- a/widget/windows/TSFTextStore.h
+++ b/widget/windows/TSFTextStore.h
@@ -9,16 +9,17 @@
 #include "nsAutoPtr.h"
 #include "nsString.h"
 #include "nsCOMPtr.h"
 #include "nsIWidget.h"
 #include "nsWindowBase.h"
 #include "WinUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/StaticPtr.h"
+#include "mozilla/TextEventDispatcher.h"
 #include "mozilla/TextRange.h"
 #include "mozilla/WindowsVersion.h"
 
 #include <msctf.h>
 #include <textstor.h>
 
 // GUID_PROP_INPUTSCOPE is declared in inputscope.h using INIT_GUID.
 // With initguid.h, we get its instance instead of extern declaration.
@@ -308,16 +309,18 @@ protected:
   // Creates native caret over our caret.  This method only works on desktop
   // application.  Otherwise, this does nothing.
   void     CreateNativeCaret();
   // Destroys native caret if there is.
   void     MaybeDestroyNativeCaret();
 
   // Holds the pointer to our current win32 widget
   RefPtr<nsWindowBase>       mWidget;
+  // mDispatcher is a helper class to dispatch composition events.
+  RefPtr<TextEventDispatcher> mDispatcher;
   // Document manager for the currently focused editor
   RefPtr<ITfDocumentMgr>     mDocumentMgr;
   // Edit cookie associated with the current editing context
   DWORD                        mEditCookie;
   // Editing context at the bottom of mDocumentMgr's context stack
   RefPtr<ITfContext>         mContext;
   // Currently installed notification sink
   RefPtr<ITextStoreACPSink>  mSink;
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -3835,18 +3835,24 @@ void nsWindow::InitEvent(WidgetGUIEvent&
       event.refPoint.x = 0;
       event.refPoint.y = 0;
     }
   } else {
     // use the point override if provided
     event.refPoint = *aPoint;
   }
 
-  event.time = ::GetMessageTime();
-  event.timeStamp = GetMessageTimeStamp(event.time);
+  event.AssignEventTime(CurrentMessageWidgetEventTime());
+}
+
+WidgetEventTime
+nsWindow::CurrentMessageWidgetEventTime() const
+{
+  LONG messageTime = ::GetMessageTime();
+  return WidgetEventTime(messageTime, GetMessageTimeStamp(messageTime));
 }
 
 /**************************************************************
  *
  * SECTION: Moz event dispatch helpers
  *
  * Helpers for dispatching different types of moz events.
  *
@@ -5944,17 +5950,17 @@ nsWindow::ClientMarginHitTestPoint(int32
     mCachedHitTestTime = TimeStamp::Now();
     mCachedHitTestResult = testResult;
   }
 
   return testResult;
 }
 
 TimeStamp
-nsWindow::GetMessageTimeStamp(LONG aEventTime)
+nsWindow::GetMessageTimeStamp(LONG aEventTime) const
 {
   CurrentWindowsTimeGetter getCurrentTime(mWnd);
   return TimeConverter().GetTimeStampFromSystemTime(aEventTime,
                                                     getCurrentTime);
 }
 
 void nsWindow::PostSleepWakeNotification(const bool aIsSleepMode)
 {
--- a/widget/windows/nsWindow.h
+++ b/widget/windows/nsWindow.h
@@ -81,16 +81,17 @@ public:
 
   NS_DECL_ISUPPORTS_INHERITED
 
   friend class nsWindowGfx;
 
   // nsWindowBase
   virtual void InitEvent(mozilla::WidgetGUIEvent& aEvent,
                          LayoutDeviceIntPoint* aPoint = nullptr) override;
+  virtual WidgetEventTime CurrentMessageWidgetEventTime() const override;
   virtual bool DispatchWindowEvent(mozilla::WidgetGUIEvent* aEvent) override;
   virtual bool DispatchKeyboardEvent(mozilla::WidgetKeyboardEvent* aEvent) override;
   virtual bool DispatchWheelEvent(mozilla::WidgetWheelEvent* aEvent) override;
   virtual bool DispatchContentCommandEvent(mozilla::WidgetContentCommandEvent* aEvent) override;
   virtual nsWindowBase* GetParentWindowBase(bool aIncludeOwner) override;
   virtual bool IsTopLevelWidget() override { return mIsTopWidgetWindow; }
 
   using nsWindowBase::DispatchPluginEvent;
@@ -401,17 +402,17 @@ protected:
                                               bool *aEventDispatched);
   LRESULT                 ProcessKeyDownMessage(const MSG &aMsg,
                                                 bool *aEventDispatched);
   static bool             EventIsInsideWindow(nsWindow* aWindow);
   // Convert nsEventStatus value to a windows boolean
   static bool             ConvertStatus(nsEventStatus aStatus);
   static void             PostSleepWakeNotification(const bool aIsSleepMode);
   int32_t                 ClientMarginHitTestPoint(int32_t mx, int32_t my);
-  TimeStamp               GetMessageTimeStamp(LONG aEventTime);
+  TimeStamp               GetMessageTimeStamp(LONG aEventTime) const;
   static void             UpdateFirstEventTime(DWORD aEventTime);
   void                    FinishLiveResizing(ResizeState aNewState);
 
   /**
    * Event handlers
    */
   virtual void            OnDestroy() override;
   virtual bool            OnResize(nsIntRect &aWindowRect);
--- a/widget/windows/nsWindowBase.h
+++ b/widget/windows/nsWindowBase.h
@@ -15,16 +15,18 @@
 
 /*
  * nsWindowBase - Base class of common methods other classes need to access
  * in both win32 and winrt window classes.
  */
 class nsWindowBase : public nsBaseWidget
 {
 public:
+  typedef mozilla::WidgetEventTime WidgetEventTime;
+
   /*
    * Return the HWND or null for this widget.
    */
   virtual HWND GetWindowHandle() final {
     return static_cast<HWND>(GetNativeData(NS_NATIVE_WINDOW));
   }
 
   /*
@@ -41,16 +43,22 @@ public:
    * Init a standard gecko event for this widget.
    * @param aEvent the event to initialize.
    * @param aPoint message position in physical coordinates.
    */
   virtual void InitEvent(mozilla::WidgetGUIEvent& aEvent,
                          LayoutDeviceIntPoint* aPoint = nullptr) = 0;
 
   /*
+   * Returns WidgetEventTime instance which is initialized with current message
+   * time.
+   */
+  virtual WidgetEventTime CurrentMessageWidgetEventTime() const = 0;
+
+  /*
    * Dispatch a gecko event for this widget.
    * Returns true if it's consumed.  Otherwise, false.
    */
   virtual bool DispatchWindowEvent(mozilla::WidgetGUIEvent* aEvent) = 0;
 
   /*
    * Dispatch a gecko keyboard event for this widget. This
    * is called by KeyboardLayout to dispatch gecko events.