Bug 543789 part.3 Implement DOM3 composition event on Windows r=jimm+smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Thu, 22 Sep 2011 18:17:40 +0900
changeset 77309 61f11b38c268fe1c9e6337e7c699fe932185832c
parent 77308 6432301d41ce969204eb1d2aeae808b6f2afeeb2
child 77310 71ffc9bbcb31e08cdbd72338251f8ead42655b37
push id2056
push usermasayuki@d-toybox.com
push dateThu, 22 Sep 2011 09:18:20 +0000
treeherdermozilla-inbound@cc42e81d78b2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimm
bugs543789
milestone9.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 543789 part.3 Implement DOM3 composition event on Windows r=jimm+smaug
widget/src/windows/nsIMM32Handler.cpp
widget/src/windows/nsIMM32Handler.h
widget/src/windows/nsTextStore.cpp
widget/src/windows/nsTextStore.h
widget/src/xpwidgets/nsBaseWidget.h
--- a/widget/src/windows/nsIMM32Handler.cpp
+++ b/widget/src/windows/nsIMM32Handler.cpp
@@ -1045,16 +1045,17 @@ nsIMM32Handler::HandleStartComposition(n
   aWindow->DispatchWindowEvent(&selection);
   if (!selection.mSucceeded) {
     PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
       ("IMM32: HandleStartComposition, FAILED (NS_QUERY_SELECTED_TEXT)\n"));
     return;
   }
 
   mCompositionStart = selection.mReply.mOffset;
+  mLastDispatchedCompositionString.Truncate();
 
   nsCompositionEvent event(PR_TRUE, NS_COMPOSITION_START, aWindow);
   aWindow->InitEvent(event, &point);
   aWindow->DispatchWindowEvent(&event);
 
   SetIMERelatedWindowsPos(aWindow, aIMEContext);
 
   mIsComposing = PR_TRUE;
@@ -1308,19 +1309,22 @@ nsIMM32Handler::HandleEndComposition(nsW
   nsIntPoint point(0, 0);
 
   if (mNativeCaretIsCreated) {
     ::DestroyCaret();
     mNativeCaretIsCreated = PR_FALSE;
   }
 
   aWindow->InitEvent(event, &point);
+  // The last dispatched composition string must be the committed string.
+  event.data = mLastDispatchedCompositionString;
   aWindow->DispatchWindowEvent(&event);
   mIsComposing = PR_FALSE;
   mComposingWindow = nsnull;
+  mLastDispatchedCompositionString.Truncate();
 }
 
 static void
 DumpReconvertString(RECONVERTSTRING* aReconv)
 {
   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
     ("  dwSize=%ld, dwVersion=%ld, dwStrLen=%ld, dwStrOffset=%ld\n",
      aReconv->dwSize, aReconv->dwVersion,
@@ -1665,18 +1669,36 @@ nsIMM32Handler::DispatchTextEvent(nsWind
   // commit event (i.e., under composing), we don't need to fire text event
   // during composing.
   if (aCheckAttr && !ShouldDrawCompositionStringOurselves()) {
     // But we need to adjust composition window pos and native caret pos, here.
     SetIMERelatedWindowsPos(aWindow, aIMEContext);
     return;
   }
 
+  nsRefPtr<nsWindow> kungFuDeathGrip(aWindow);
+
+  nsIntPoint point(0, 0);
+
+  if (mCompositionString != mLastDispatchedCompositionString) {
+    nsCompositionEvent compositionUpdate(PR_TRUE, NS_COMPOSITION_UPDATE,
+                                         aWindow);
+    aWindow->InitEvent(compositionUpdate, &point);
+    compositionUpdate.data = mCompositionString;
+    mLastDispatchedCompositionString = mCompositionString;
+
+    aWindow->DispatchWindowEvent(&compositionUpdate);
+
+    if (!mIsComposing || aWindow->Destroyed()) {
+      return;
+    }
+    SetIMERelatedWindowsPos(aWindow, aIMEContext);
+  }
+
   nsTextEvent event(PR_TRUE, NS_TEXT_TEXT, aWindow);
-  nsIntPoint point(0, 0);
 
   aWindow->InitEvent(event, &point);
 
   nsAutoTArray<nsTextRange, 4> textRanges;
 
   if (aCheckAttr) {
     SetTextRangeList(textRanges);
   }
--- a/widget/src/windows/nsIMM32Handler.h
+++ b/widget/src/windows/nsIMM32Handler.h
@@ -317,16 +317,17 @@ protected:
     MSG msg;
     msg.wParam = wParam;
     msg.lParam = lParam;
     mPassedIMEChar.AppendElement(msg);
   }
 
   nsWindow* mComposingWindow;
   nsString  mCompositionString;
+  nsString  mLastDispatchedCompositionString;
   nsTArray<PRUint32> mClauseArray;
   nsTArray<PRUint8> mAttributeArray;
 
   PRInt32 mCursorPosition;
   PRUint32 mCompositionStart;
 
   PRPackedBool mIsComposing;
   PRPackedBool mIsComposingOnPlugin;
--- a/widget/src/windows/nsTextStore.cpp
+++ b/widget/src/windows/nsTextStore.cpp
@@ -798,19 +798,33 @@ nsTextStore::SendTextEventForComposition
   // If we are already send same text event, we should not resend it.  Because
   // it can be a cause of flickering.
   if (IsSameTextEvent(mLastDispatchedTextEvent, &event)) {
     PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
            ("TSF: SendTextEventForCompositionString does not dispatch\n"));
     return S_OK;
   }
 
-  mWindow->DispatchWindowEvent(&event);
-  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
-         ("TSF: SendTextEventForCompositionString DISPATCHED\n"));
+  if (mCompositionString != mLastDispatchedCompositionString) {
+    nsCompositionEvent compositionUpdate(PR_TRUE, NS_COMPOSITION_UPDATE,
+                                         mWindow);
+    mWindow->InitEvent(compositionUpdate);
+    compositionUpdate.data = mCompositionString;
+    mLastDispatchedCompositionString = mCompositionString;
+    mWindow->DispatchWindowEvent(&compositionUpdate);
+    PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
+           ("TSF: SendTextEventForCompositionString compositionupdate "
+            "DISPATCHED\n"));
+  }
+
+  if (mWindow && !mWindow->Destroyed()) {
+    mWindow->DispatchWindowEvent(&event);
+    PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
+           ("TSF: SendTextEventForCompositionString text event DISPATCHED\n"));
+  }
   return SaveTextEvent(&event);
 }
 
 HRESULT
 nsTextStore::SetSelectionInternal(const TS_SELECTION_ACP* pSelection,
                                   PRBool aDispatchTextEvent)
 {
   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
@@ -1239,29 +1253,40 @@ nsTextStore::InsertTextAtSelection(DWORD
              ("TSF: InsertTextAtSelection, replaced=%lu-%lu\n",
               sel.acpStart - mCompositionStart,
               sel.acpEnd - mCompositionStart));
     } else {
       // Use a temporary composition to contain the text
       nsCompositionEvent compEvent(PR_TRUE, NS_COMPOSITION_START, mWindow);
       mWindow->InitEvent(compEvent);
       mWindow->DispatchWindowEvent(&compEvent);
-      nsTextEvent event(PR_TRUE, NS_TEXT_TEXT, mWindow);
-      mWindow->InitEvent(event);
-      if (!cch) {
-        // XXX See OnEndComposition comment on inserting empty strings
-        event.theText = NS_LITERAL_STRING(" ");
-        mWindow->DispatchWindowEvent(&event);
+      if (mWindow && !mWindow->Destroyed()) {
+        compEvent.message = NS_COMPOSITION_UPDATE;
+        compEvent.data.Assign(pchText, cch);
+        mWindow->DispatchWindowEvent(&compEvent);
+        if (mWindow && !mWindow->Destroyed()) {
+          nsTextEvent event(PR_TRUE, NS_TEXT_TEXT, mWindow);
+          mWindow->InitEvent(event);
+          if (!cch) {
+            // XXX See OnEndComposition comment on inserting empty strings
+            event.theText = NS_LITERAL_STRING(" ");
+            mWindow->DispatchWindowEvent(&event);
+          }
+          if (mWindow && !mWindow->Destroyed()) {
+            event.theText.Assign(pchText, cch);
+            event.theText.ReplaceSubstring(NS_LITERAL_STRING("\r\n"),
+                                           NS_LITERAL_STRING("\n"));
+            mWindow->DispatchWindowEvent(&event);
+            if (mWindow && !mWindow->Destroyed()) {
+              compEvent.message = NS_COMPOSITION_END;
+              mWindow->DispatchWindowEvent(&compEvent);
+            }
+          }
+        }
       }
-      event.theText.Assign(pchText, cch);
-      event.theText.ReplaceSubstring(NS_LITERAL_STRING("\r\n"),
-                                     NS_LITERAL_STRING("\n"));
-      mWindow->DispatchWindowEvent(&event);
-      compEvent.message = NS_COMPOSITION_END;
-      mWindow->DispatchWindowEvent(&compEvent);
     }
     pChange->acpStart = sel.acpStart;
     pChange->acpOldEnd = sel.acpEnd;
     // Get new selection
     NS_ENSURE_TRUE(SUCCEEDED(GetSelection(
         TS_DEFAULT_SELECTION, 1, &sel, &selFetched)) && selFetched, E_FAIL);
     pChange->acpNewEnd = sel.acpEnd;
     if (TS_IAS_NOQUERY != dwFlags) {
@@ -1393,16 +1418,30 @@ nsTextStore::OnEndComposition(ITfComposi
   // Clear the saved text event
   SaveTextEvent(nsnull);
 
   if (mCompositionTimer) {
     mCompositionTimer->Cancel();
     mCompositionTimer = nsnull;
   }
 
+  if (mCompositionString != mLastDispatchedCompositionString) {
+    nsCompositionEvent compositionUpdate(PR_TRUE, NS_COMPOSITION_UPDATE,
+                                         mWindow);
+    mWindow->InitEvent(compositionUpdate);
+    compositionUpdate.data = mCompositionString;
+    mLastDispatchedCompositionString = mCompositionString;
+    mWindow->DispatchWindowEvent(&compositionUpdate);
+    if (!mWindow || mWindow->Destroyed()) {
+      PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
+             ("TSF: CompositionUpdate caused aborting compositionend\n"));
+      return S_OK;
+    }
+  }
+
   // Use NS_TEXT_TEXT to commit composition string
   nsTextEvent textEvent(PR_TRUE, NS_TEXT_TEXT, mWindow);
   mWindow->InitEvent(textEvent);
   if (!mCompositionString.Length()) {
     // XXX HACK! HACK! NS_TEXT_TEXT handler specifically rejects
     // first-time empty strings as workaround for another IME bug
     // and our request will be rejected if this is the first time
     // we are sending NS_TEXT_TEXT. The workaround is to send it a
@@ -1410,22 +1449,31 @@ nsTextStore::OnEndComposition(ITfComposi
     textEvent.theText = NS_LITERAL_STRING(" ");
     mWindow->DispatchWindowEvent(&textEvent);
   }
   textEvent.theText = mCompositionString;
   textEvent.theText.ReplaceSubstring(NS_LITERAL_STRING("\r\n"),
                                      NS_LITERAL_STRING("\n"));
   mWindow->DispatchWindowEvent(&textEvent);
 
+  if (!mWindow || mWindow->Destroyed()) {
+    PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
+           ("TSF: Text event caused aborting compositionend\n"));
+    return S_OK;
+  }
+
   nsCompositionEvent event(PR_TRUE, NS_COMPOSITION_END, mWindow);
+  event.data = mLastDispatchedCompositionString;
   mWindow->InitEvent(event);
   mWindow->DispatchWindowEvent(&event);
 
   mCompositionView = NULL;
   mCompositionString.Truncate(0);
+  mLastDispatchedCompositionString.Truncate();
+
   // Maintain selection
   SetSelectionInternal(&mCompositionSelection);
   return S_OK;
 }
 
 nsresult
 nsTextStore::OnFocusChange(PRBool aFocus,
                            nsWindow* aWindow,
--- a/widget/src/windows/nsTextStore.h
+++ b/widget/src/windows/nsTextStore.h
@@ -236,16 +236,19 @@ protected:
   // during GetSelection/SetSelection calls.
   TS_SELECTION_ACP             mCompositionSelection;
   // The start and length of the current active composition, in ACP offsets
   LONG                         mCompositionStart;
   LONG                         mCompositionLength;
   // The latest text event which was dispatched for composition string
   // of the current composing transaction.
   nsTextEvent*                 mLastDispatchedTextEvent;
+  // The latest composition string which was dispatched by composition update
+  // event.
+  nsString                     mLastDispatchedCompositionString;
   // Timer for calling ITextStoreACPSink::OnLayoutChange. This is only used
   // during composing.
   nsCOMPtr<nsITimer>           mCompositionTimer;
 
   // TSF thread manager object for the current application
   static ITfThreadMgr*  sTsfThreadMgr;
   // TSF display attribute manager
   static ITfDisplayAttributeMgr* sDisplayAttrMgr;
--- a/widget/src/xpwidgets/nsBaseWidget.h
+++ b/widget/src/xpwidgets/nsBaseWidget.h
@@ -214,16 +214,18 @@ public:
   public:
     AutoUseBasicLayerManager(nsBaseWidget* aWidget);
     ~AutoUseBasicLayerManager();
   private:
     nsBaseWidget* mWidget;
   };
   friend class AutoUseBasicLayerManager;
 
+  PRBool                  Destroyed() { return mOnDestroyCalled; }
+
 protected:
 
   virtual void            ResolveIconName(const nsAString &aIconName,
                                           const nsAString &aIconSuffix,
                                           nsILocalFile **aResult);
   virtual void            OnDestroy();
   virtual void            BaseCreate(nsIWidget *aParent,
                                      const nsIntRect &aRect,