Bug 610829 Should associate default IMC at committing or canceling composition r=emk
authorMasayuki Nakano <masayuki@d-toybox.com>
Thu, 07 Jul 2011 21:02:07 +0900
changeset 72488 78a414587aa6c9cd792820cd7a7136a28a2ecd24
parent 72459 3f9e716164ef3c1e26285ce1915d23475bbfb04c
child 72489 a3053d8e40901c9d6d015f9561debaf6b72fd1ce
push id20728
push usermak77@bonardo.net
push dateFri, 08 Jul 2011 09:54:33 +0000
treeherdermozilla-central@5479a346b95b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemk
bugs610829
milestone8.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 610829 Should associate default IMC at committing or canceling composition r=emk
widget/src/windows/nsIMM32Handler.cpp
widget/src/windows/nsIMM32Handler.h
widget/src/windows/nsWindow.cpp
widget/src/windows/nsWindow.h
--- a/widget/src/windows/nsIMM32Handler.cpp
+++ b/widget/src/windows/nsIMM32Handler.cpp
@@ -288,41 +288,61 @@ nsIMM32Handler::CommitComposition(nsWind
      aWindow, aWindow->GetWindowHandle(),
      gIMM32Handler ? gIMM32Handler->mComposingWindow : nsnull,
      gIMM32Handler && gIMM32Handler->mComposingWindow ?
        IsComposingOnOurEditor() ? " (composing on editor)" :
                                   " (composing on plug-in)" : ""));
   if (!aForce && !IsComposingWindow(aWindow)) {
     return;
   }
+
+  PRBool associated = aWindow->AssociateDefaultIMC(PR_TRUE);
+  PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
+    ("IMM32: CommitComposition, associated=%s\n",
+     associated ? "YES" : "NO"));
+
   nsIMEContext IMEContext(aWindow->GetWindowHandle());
   if (IMEContext.IsValid()) {
     ::ImmNotifyIME(IMEContext.get(), NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
     ::ImmNotifyIME(IMEContext.get(), NI_COMPOSITIONSTR, CPS_CANCEL, 0);
   }
+
+  if (associated) {
+    aWindow->AssociateDefaultIMC(PR_FALSE);
+  }
 }
 
 /* static */ void
 nsIMM32Handler::CancelComposition(nsWindow* aWindow, PRBool aForce)
 {
   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
     ("IMM32: CancelComposition, aForce=%s, aWindow=%p, hWnd=%08x, mComposingWindow=%p%s\n",
      aForce ? "TRUE" : "FALSE",
      aWindow, aWindow->GetWindowHandle(),
      gIMM32Handler ? gIMM32Handler->mComposingWindow : nsnull,
      gIMM32Handler && gIMM32Handler->mComposingWindow ?
        IsComposingOnOurEditor() ? " (composing on editor)" :
                                   " (composing on plug-in)" : ""));
   if (!aForce && !IsComposingWindow(aWindow)) {
     return;
   }
+
+  PRBool associated = aWindow->AssociateDefaultIMC(PR_TRUE);
+  PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
+    ("IMM32: CancelComposition, associated=%s\n",
+     associated ? "YES" : "NO"));
+
   nsIMEContext IMEContext(aWindow->GetWindowHandle());
   if (IMEContext.IsValid()) {
     ::ImmNotifyIME(IMEContext.get(), NI_COMPOSITIONSTR, CPS_CANCEL, 0);
   }
+
+  if (associated) {
+    aWindow->AssociateDefaultIMC(PR_FALSE);
+  }
 }
 
 /* static */ PRBool
 nsIMM32Handler::ProcessInputLangChangeMessage(nsWindow* aWindow,
                                               WPARAM wParam,
                                               LPARAM lParam,
                                               LRESULT *aRetValue,
                                               PRBool &aEatMessage)
--- a/widget/src/windows/nsIMM32Handler.h
+++ b/widget/src/windows/nsIMM32Handler.h
@@ -126,16 +126,24 @@ public:
   static PRBool IsStatusChanged() { return sIsStatusChanged; }
 
   static PRBool IsDoingKakuteiUndo(HWND aWnd);
 
   static void NotifyEndStatusChange() { sIsStatusChanged = PR_FALSE; }
 
   static PRBool CanOptimizeKeyAndIMEMessages(MSG *aNextKeyOrIMEMessage);
 
+#ifdef DEBUG
+  /**
+   * IsIMEAvailable() returns TRUE when current keyboard layout has IME.
+   * Otherwise, FALSE.
+   */
+  static PRBool IsIMEAvailable() { return sIsIME; }
+#endif
+
   // If aForce is TRUE, these methods doesn't check whether we have composition
   // or not.  If you don't set it to TRUE, these method doesn't commit/cancel
   // the composition on uexpected window.
   static void CommitComposition(nsWindow* aWindow, PRBool aForce = PR_FALSE);
   static void CancelComposition(nsWindow* aWindow, PRBool aForce = PR_FALSE);
 
 protected:
   static void EnsureHandlerInstance();
--- a/widget/src/windows/nsWindow.cpp
+++ b/widget/src/windows/nsWindow.cpp
@@ -365,17 +365,16 @@ nsWindow::nsWindow() : nsBaseWidget()
 #ifdef PR_LOGGING
   if (!gWindowsLog)
     gWindowsLog = PR_NewLogModule("nsWindowsWidgets");
 #endif
 
   mWnd                  = nsnull;
   mPaintDC              = nsnull;
   mPrevWndProc          = nsnull;
-  mOldIMC               = nsnull;
   mNativeDragTarget     = nsnull;
   mInDtor               = PR_FALSE;
   mIsVisible            = PR_FALSE;
   mIsTopWidgetWindow    = PR_FALSE;
   mUnicodeWidget        = PR_TRUE;
   mDisplayPanFeedback   = PR_FALSE;
   mTouchWindow          = PR_FALSE;
   mCustomNonClient      = PR_FALSE;
@@ -6664,34 +6663,35 @@ LRESULT nsWindow::OnKeyDown(const MSG &a
 
 #ifdef DEBUG
   //printf("In OnKeyDown virt: %d\n", DOMKeyCode);
 #endif
 
   static PRBool sRedirectedKeyDownEventPreventedDefault = PR_FALSE;
   PRBool noDefault;
   if (aFakeCharMessage || !IsRedirectedKeyDownMessage(aMsg)) {
-    HIMC oldIMC = mOldIMC;
+    nsIMEContext IMEContext(mWnd);
     noDefault =
       DispatchKeyEvent(NS_KEY_DOWN, 0, nsnull, DOMKeyCode, &aMsg, aModKeyState);
     if (aEventDispatched) {
       *aEventDispatched = PR_TRUE;
     }
 
     // If IMC wasn't associated to the window but is associated it now (i.e.,
     // focus is moved from a non-editable editor to an editor by keydown
     // event handler), WM_CHAR and WM_SYSCHAR shouldn't cause first character
     // inputting if IME is opened.  But then, we should redirect the native
     // keydown message to IME.
     // However, note that if focus has been already moved to another
     // application, we shouldn't redirect the message to it because the keydown
     // message is processed by us, so, nobody shouldn't process it.
     HWND focusedWnd = ::GetFocus();
-    if (!noDefault && !aFakeCharMessage && oldIMC && !mOldIMC && focusedWnd &&
-        !PluginHasFocus()) {
+    nsIMEContext newIMEContext(mWnd);
+    if (!noDefault && !aFakeCharMessage && focusedWnd && !PluginHasFocus() &&
+        !IMEContext.get() && newIMEContext.get()) {
       RemoveNextCharMessage(focusedWnd);
 
       INPUT keyinput;
       keyinput.type = INPUT_KEYBOARD;
       keyinput.ki.wVk = aMsg.wParam;
       keyinput.ki.wScan = GetScanCode(aMsg.lParam);
       keyinput.ki.dwFlags = KEYEVENTF_SCANCODE;
       if (IsExtendedScanCode(aMsg.lParam)) {
@@ -7310,21 +7310,18 @@ void nsWindow::OnDestroy()
   // If we're going away and for some reason we're still the rollup widget, rollup and
   // turn off capture.
   if ( this == sRollupWidget ) {
     if ( sRollupListener )
       sRollupListener->Rollup(nsnull, nsnull);
     CaptureRollupEvents(nsnull, nsnull, PR_FALSE, PR_TRUE);
   }
 
-  // If IME is disabled, restore it.
-  if (mOldIMC) {
-    mOldIMC = ::ImmAssociateContext(mWnd, mOldIMC);
-    NS_ASSERTION(!mOldIMC, "Another IMC was associated");
-  }
+  // Restore the IM context.
+  AssociateDefaultIMC(PR_TRUE);
 
   // Turn off mouse trails if enabled.
   MouseTrailer* mtrailer = nsToolkit::gMouseTrailer;
   if (mtrailer) {
     if (mtrailer->GetMouseTrailerWindow() == mWnd)
       mtrailer->DestroyTimer();
 
     if (mtrailer->GetCaptureWindow() == mWnd)
@@ -7953,21 +7950,17 @@ NS_IMETHODIMP nsWindow::SetInputMode(con
 #endif 
   if (nsIMM32Handler::IsComposing()) {
     ResetInputState();
   }
   mIMEContext = aContext;
   PRBool enable = (status == nsIWidget::IME_STATUS_ENABLED ||
                    status == nsIWidget::IME_STATUS_PLUGIN);
 
-  if (!enable != !mOldIMC)
-    return NS_OK;
-  mOldIMC = ::ImmAssociateContext(mWnd, enable ? mOldIMC : NULL);
-  NS_ASSERTION(!enable || !mOldIMC, "Another IMC was associated");
-
+  AssociateDefaultIMC(enable);
   return NS_OK;
 }
 
 NS_IMETHODIMP nsWindow::GetInputMode(IMEContext& aContext)
 {
 #ifdef DEBUG_KBSTATE
   printf("GetInputMode: %s\n", mIMEContext.mStatus ? "Enabled" : "Disabled");
 #endif 
@@ -8020,16 +8013,52 @@ nsWindow::OnIMETextChange(PRUint32 aStar
 
 NS_IMETHODIMP
 nsWindow::OnIMESelectionChange(void)
 {
   return nsTextStore::OnSelectionChange();
 }
 #endif //NS_ENABLE_TSF
 
+PRBool nsWindow::AssociateDefaultIMC(PRBool aAssociate)
+{
+  nsIMEContext IMEContext(mWnd);
+
+  if (aAssociate) {
+    BOOL ret = ::ImmAssociateContextEx(mWnd, NULL, IACE_DEFAULT);
+#ifdef DEBUG
+    // Note that if IME isn't available with current keyboard layout,
+    // IMM might not be installed on the system such as English Windows.
+    // On such system, IMM APIs always fail.
+    NS_ASSERTION(ret || !nsIMM32Handler::IsIMEAvailable(),
+                 "ImmAssociateContextEx failed to restore default IMC");
+    if (ret) {
+      nsIMEContext newIMEContext(mWnd);
+      NS_ASSERTION(!IMEContext.get() || newIMEContext.get() == IMEContext.get(),
+                   "Unknown IMC had been associated");
+    }
+#endif
+    return ret && !IMEContext.get();
+  }
+
+  if (mOnDestroyCalled) {
+    // If OnDestroy() has been called, we shouldn't disassociate the default
+    // IMC at destroying the window.
+    return PR_FALSE;
+  }
+
+  if (!IMEContext.get()) {
+    return PR_FALSE; // already disassociated
+  }
+
+  BOOL ret = ::ImmAssociateContextEx(mWnd, NULL, 0);
+  NS_ASSERTION(ret, "ImmAssociateContextEx failed to disassociate the IMC");
+  return ret != FALSE;
+}
+
 #ifdef ACCESSIBILITY
 
 #ifdef DEBUG_WMGETOBJECT
 #define NS_LOG_WMGETOBJECT_WNDACC(aWnd)                                        \
   nsAccessible* acc = aWnd ?                                                   \
     aWnd->DispatchAccessibleEvent(NS_GETACCESSIBLE) : nsnull;                  \
   printf("     acc: %p", acc);                                                 \
   if (acc) {                                                                   \
--- a/widget/src/windows/nsWindow.h
+++ b/widget/src/windows/nsWindow.h
@@ -257,16 +257,31 @@ public:
    * called.
    *
    * @param aReinitialize Call GetLayerManager on widgets to ensure D3D9 is
    *                      initialized, this is usually called when this function
    *                      is triggered by timeout and not user/web interaction.
    */
   static void             StartAllowingD3D9(bool aReinitialize);
 
+  /**
+   * AssociateDefaultIMC() associates or disassociates the default IMC for
+   * the window.
+   *
+   * @param aAssociate    TRUE, associates the default IMC with the window.
+   *                      Otherwise, disassociates the default IMC from the
+   *                      window.
+   * @return              TRUE if this method associated the default IMC with
+   *                      disassociated window or disassociated the default IMC
+   *                      from associated window.
+   *                      Otherwise, i.e., if this method did nothing actually,
+   *                      FALSE.
+   */
+  PRBool                  AssociateDefaultIMC(PRBool aAssociate);
+
 #if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_WIN7
   PRBool HasTaskbarIconBeenCreated() { return mHasTaskbarIconBeenCreated; }
   // Called when either the nsWindow or an nsITaskbarTabPreview receives the noticiation that this window
   // has its icon placed on the taskbar.
   void SetHasTaskbarIconBeenCreated(PRBool created = PR_TRUE) { mHasTaskbarIconBeenCreated = created; }
 
   // Getter/setter for the nsITaskbarWindowPreview for this nsWindow
   already_AddRefed<nsITaskbarWindowPreview> GetTaskbarPreview() {
@@ -499,17 +514,16 @@ protected:
   PRPackedBool          mDisplayPanFeedback;
   PRPackedBool          mHideChrome;
   PRPackedBool          mIsRTL;
   PRPackedBool          mFullscreenMode;
   PRPackedBool          mMousePresent;
   PRUint32              mBlurSuppressLevel;
   DWORD_PTR             mOldStyle;
   DWORD_PTR             mOldExStyle;
-  HIMC                  mOldIMC;
   IMEContext            mIMEContext;
   nsNativeDragTarget*   mNativeDragTarget;
   HKL                   mLastKeyboardLayout;
   nsPopupType           mPopupType;
   nsSizeMode            mOldSizeMode;
   WindowHook            mWindowHook;
   DWORD                 mAssumeWheelIsZoomUntil;
   static PRBool         sDropShadowEnabled;