From c99efa61bf199d2b1c2152de8b88cf22e1687905 Mon Sep 17 00:00:00 2001 From: "masayuki%d-toybox.com" Date: Thu, 8 Jan 2009 04:08:58 +0000 Subject: [PATCH] Bug 424663 crashes [@ CalcCharacterPositionAtoW][@ imm32.dll@0x3e24] involving Chinese IME r=VYV03354+ere,sr=roc,a1906=dveditz git-svn-id: svn://10.0.0.236/trunk@255763 18797224-902f-48f8-a5cc-f745e15eee43 --- .../widget/src/windows/nsKeyboardLayout.cpp | 17 +- mozilla/widget/src/windows/nsKeyboardLayout.h | 6 + mozilla/widget/src/windows/nsWindow.cpp | 157 +++++++++++------- mozilla/widget/src/windows/nsWindow.h | 7 +- 4 files changed, 122 insertions(+), 65 deletions(-) diff --git a/mozilla/widget/src/windows/nsKeyboardLayout.cpp b/mozilla/widget/src/windows/nsKeyboardLayout.cpp index c0324bf5ccf..c33fd481fcd 100644 --- a/mozilla/widget/src/windows/nsKeyboardLayout.cpp +++ b/mozilla/widget/src/windows/nsKeyboardLayout.cpp @@ -209,7 +209,8 @@ PRUint32 VirtualKey::GetNativeUniChars (PRUint8 aShiftState, PRUint16* aUniChars -KeyboardLayout::KeyboardLayout () +KeyboardLayout::KeyboardLayout () : + mKeyboardLayout(0), mCodePage(0) { #ifndef WINCE mDeadKeyTableListHead = nsnull; @@ -361,7 +362,18 @@ KeyboardLayout::GetUniCharsWithShiftState(PRUint8 aVirtualKey, void KeyboardLayout::LoadLayout (HKL aLayout) { + if (mKeyboardLayout == aLayout) + return; + + mKeyboardLayout = aLayout; + mIMEProperty = ::ImmGetProperty(aLayout, IGP_PROPERTY); + #ifndef WINCE + WORD langID = LOWORD(aLayout); + ::GetLocaleInfoA(MAKELCID(langID, SORT_DEFAULT), + LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER, + (PSTR)&mCodePage, sizeof(mCodePage)); + PRUint32 shiftState; BYTE kbdState [256]; BYTE originalKbdState [256]; @@ -372,7 +384,6 @@ void KeyboardLayout::LoadLayout (HKL aLayout) mActiveDeadKey = -1; mNumOfChars = 0; - mKeyboardLayout = aLayout; ReleaseDeadKeyTables (); @@ -452,6 +463,8 @@ void KeyboardLayout::LoadLayout (HKL aLayout) } ::SetKeyboardState (originalKbdState); +#else + mCodePage = ::GetACP(); #endif } diff --git a/mozilla/widget/src/windows/nsKeyboardLayout.h b/mozilla/widget/src/windows/nsKeyboardLayout.h index 401d37c4199..6d0ea7f8a2a 100644 --- a/mozilla/widget/src/windows/nsKeyboardLayout.h +++ b/mozilla/widget/src/windows/nsKeyboardLayout.h @@ -137,6 +137,8 @@ class KeyboardLayout #define NUM_OF_KEYS 50 HKL mKeyboardLayout; + UINT mCodePage; + DWORD mIMEProperty; VirtualKey mVirtualKeys [NUM_OF_KEYS]; DeadKeyTableListEntry* mDeadKeyTableListHead; @@ -183,6 +185,10 @@ public: PRUint32 GetUniCharsWithShiftState(PRUint8 aVirtualKey, PRUint8 aShiftStates, PRUint16* aUniChars, PRUint32 aMaxChars) const; + + HKL GetLayout() { return mKeyboardLayout; } + UINT GetCodePage() { return mCodePage; } + DWORD GetIMEProperty() { return mIMEProperty; } }; #endif diff --git a/mozilla/widget/src/windows/nsWindow.cpp b/mozilla/widget/src/windows/nsWindow.cpp index cf519b00c55..001701db8b2 100644 --- a/mozilla/widget/src/windows/nsWindow.cpp +++ b/mozilla/widget/src/windows/nsWindow.cpp @@ -312,7 +312,6 @@ PRUint32 nsWindow::sInstanceCount = 0; PRBool nsWindow::sIMEIsComposing = PR_FALSE; PRBool nsWindow::sIMEIsStatusChanged = PR_FALSE; -DWORD nsWindow::sIMEProperty = 0; nsString* nsWindow::sIMECompUnicode = NULL; PRUint8* nsWindow::sIMEAttributeArray = NULL; PRInt32 nsWindow::sIMEAttributeArrayLength = 0; @@ -326,6 +325,10 @@ PRUnichar* nsWindow::sIMEReconvertUnicode = NULL; RECT* nsWindow::sIMECompCharPos = nsnull; PRInt32 nsWindow::sIMECaretHeight = 0; +PRBool nsWindow::gSwitchKeyboardLayout = PR_FALSE; + +static KeyboardLayout gKbdLayout; + PRBool nsWindow::sIsInEndSession = PR_FALSE; BOOL nsWindow::sIsRegistered = FALSE; @@ -708,7 +711,7 @@ nsWindow::nsWindow() : nsBaseWidget() static BOOL gbInitGlobalValue = FALSE; if (! gbInitGlobalValue) { gbInitGlobalValue = TRUE; - gKeyboardLayout = GetKeyboardLayout(0); + gKbdLayout.LoadLayout(GetKeyboardLayout(0)); // mouse message of MSIME98/2000 nsWindow::uWM_MSIME_MOUSE = ::RegisterWindowMessage(RWM_MOUSE); @@ -735,10 +738,6 @@ nsWindow::nsWindow() : nsBaseWidget() } -HKL nsWindow::gKeyboardLayout = 0; -PRBool nsWindow::gSwitchKeyboardLayout = PR_FALSE; -static KeyboardLayout gKbdLayout; - //------------------------------------------------------------------------- // // nsWindow destructor @@ -3509,10 +3508,12 @@ BOOL nsWindow::OnChar(UINT charCode, UINT aScanCode, PRUint32 aFlags) // Keep the characters unshifted for shortcuts and accesskeys and make sure // that numbers are always passed as such (among others: bugs 50255 and 351310) if (uniChar && (mIsControlDown || mIsAltDown)) { - UINT virtualKeyCode = ::MapVirtualKeyEx(aScanCode, MAPVK_VSC_TO_VK, gKeyboardLayout); + UINT virtualKeyCode = ::MapVirtualKeyEx(aScanCode, MAPVK_VSC_TO_VK, + gKbdLayout.GetLayout()); UINT unshiftedCharCode = virtualKeyCode >= '0' && virtualKeyCode <= '9' ? virtualKeyCode : - mIsShiftDown ? ::MapVirtualKeyEx(virtualKeyCode, MAPVK_VK_TO_CHAR, gKeyboardLayout) : 0; + mIsShiftDown ? ::MapVirtualKeyEx(virtualKeyCode, MAPVK_VK_TO_CHAR, + gKbdLayout.GetLayout()) : 0; // ignore diacritics (top bit set) and key mapping errors (char code 0) if ((INT)unshiftedCharCode > 0) uniChar = unshiftedCharCode; @@ -3581,9 +3582,8 @@ nsWindow::SynthesizeNativeKeyEvent(PRInt32 aNativeKeyboardLayout, // This changes the state of the keyboard for the current thread only, // and we'll restore it soon, so this should be OK. ::SetKeyboardState(kbdState); - HKL oldLayout = gKeyboardLayout; - gKeyboardLayout = loadedLayout; - gKbdLayout.LoadLayout(gKeyboardLayout); + HKL oldLayout = gKbdLayout.GetLayout(); + gKbdLayout.LoadLayout(loadedLayout); nsAutoTArray keySequence; SetupKeyModifiersSequence(&keySequence, aModifierFlags); @@ -3602,7 +3602,8 @@ nsWindow::SynthesizeNativeKeyEvent(PRInt32 aNativeKeyboardLayout, ::SetKeyboardState(kbdState); SetupModKeyState(); if (i == keySequence.Length() - 1 && aCharacters.Length() > 0) { - UINT scanCode = ::MapVirtualKeyEx(aNativeKeyCode, MAPVK_VK_TO_VSC, gKeyboardLayout); + UINT scanCode = ::MapVirtualKeyEx(aNativeKeyCode, MAPVK_VK_TO_VSC, + gKbdLayout.GetLayout()); nsFakeCharMessage msg = { aCharacters.CharAt(0), scanCode }; OnKeyDown(key, 0, &msg); } else { @@ -3623,8 +3624,7 @@ nsWindow::SynthesizeNativeKeyEvent(PRInt32 aNativeKeyboardLayout, // Restore old key state and layout ::SetKeyboardState(originalKbdState); - gKeyboardLayout = oldLayout; - gKbdLayout.LoadLayout(gKeyboardLayout); + gKbdLayout.LoadLayout(oldLayout); SetupModKeyState(); UnloadKeyboardLayout(loadedLayout); @@ -4708,7 +4708,7 @@ PRBool nsWindow::ProcessMessage(UINT msg, WPARAM wParam, LPARAM lParam, LRESULT if (WA_INACTIVE == fActive) { gJustGotDeactivate = PR_TRUE; if (mIsTopWidgetWindow) - mLastKeyboardLayout = gKeyboardLayout; + mLastKeyboardLayout = gKbdLayout.GetLayout(); } else { gJustGotActivate = PR_TRUE; nsMouseEvent event(PR_TRUE, NS_MOUSE_ACTIVATE, this, @@ -5000,7 +5000,7 @@ PRBool nsWindow::ProcessMessage(UINT msg, WPARAM wParam, LPARAM lParam, LRESULT break; case WM_INPUTLANGCHANGE: - result = OnInputLangChange((HKL)lParam, aRetValue); + result = OnInputLangChange((HKL)lParam); break; case WM_IME_STARTCOMPOSITION: @@ -6432,6 +6432,8 @@ NS_METHOD nsWindow::SetPreferredSize(PRInt32 aWidth, PRInt32 aHeight) return NS_OK; } +// XXX itABC v5.30 on Vista is E0210804. Probably, we should not use these +// values for checking the current IME. #define ZH_CN_INTELLEGENT_ABC_IME ((HKL)0xe0040804L) #define ZH_CN_MS_PINYIN_IME_3_0 ((HKL)0xe00e0804L) #define ZH_CN_NEIMA_IME ((HKL)0xe0050804L) @@ -6495,10 +6497,10 @@ nsWindow::HandleTextEvent(HIMC hIMEContext,PRBool aCheckAttr) ::ImmSetCandidateWindow(hIMEContext, &candForm); - // somehow the "Intellegent ABC IME" in Simplified Chinese + // somehow the "Intelligent ABC IME" in Simplified Chinese // window listen to the caret position to decide where to put the // candidate window - if (gKeyboardLayout == ZH_CN_INTELLEGENT_ABC_IME) + if (gKbdLayout.GetLayout() == ZH_CN_INTELLEGENT_ABC_IME) { CreateCaret(mWnd, nsnull, 1, 1); SetCaretPos(candForm.ptCurrentPos.x, candForm.ptCurrentPos.y); @@ -6572,7 +6574,7 @@ nsWindow::HandleStartComposition(HIMC hIMEContext) printf("Candidate window position: x=%d, y=%d\n", candForm.ptCurrentPos.x, candForm.ptCurrentPos.y); #endif - if (!gPinYinIMECaretCreated && PINYIN_IME_ON_XP(gKeyboardLayout)) + if (!gPinYinIMECaretCreated && PINYIN_IME_ON_XP(gKbdLayout.GetLayout())) { gPinYinIMECaretCreated = CreateCaret(mWnd, nsnull, 1, 1); SetCaretPos(candForm.ptCurrentPos.x, candForm.ptCurrentPos.y); @@ -6709,17 +6711,12 @@ nsWindow::GetTextRangeList(PRUint32* textRangeListLengthResult,nsTextRangeArray* //========================================================================== -BOOL nsWindow::OnInputLangChange(HKL aHKL, LRESULT *oRetValue) +BOOL nsWindow::OnInputLangChange(HKL aHKL) { #ifdef KE_DEBUG printf("OnInputLanguageChange\n"); #endif - - if (gKeyboardLayout != aHKL) - { - gKeyboardLayout = aHKL; - gKbdLayout.LoadLayout(gKeyboardLayout); - } + gKbdLayout.LoadLayout(aHKL); ResetInputState(); @@ -6787,6 +6784,21 @@ void nsWindow::GetCompositionString(HIMC aHIMC, DWORD aIndex, nsString* aStrUnic aStrUnicode->SetLength(lRtn); } +PRBool nsWindow::ConvertToANSIString(const nsAFlatString& aStr, UINT aCodePage, + nsACString& aANSIStr) +{ + int len = ::WideCharToMultiByte(aCodePage, 0, + (LPCWSTR)aStr.get(), aStr.Length(), + NULL, 0, NULL, NULL); + NS_ENSURE_TRUE(len >= 0, PR_FALSE); + + if (!EnsureStringLength(aANSIStr, len)) + return PR_FALSE; + ::WideCharToMultiByte(aCodePage, 0, (LPCWSTR)aStr.get(), aStr.Length(), + (LPSTR)aANSIStr.BeginWriting(), len, NULL, NULL); + return PR_TRUE; +} + //========================================================================== BOOL nsWindow::OnIMEComposition(LPARAM aGCS) { @@ -6874,29 +6886,61 @@ BOOL nsWindow::OnIMEComposition(LPARAM aGCS) //-------------------------------------------------------- // 2. Get GCS_COMPCLAUSE //-------------------------------------------------------- - long compClauseLen, compClauseLen2; - compClauseLen = ::ImmGetCompositionStringW(hIMEContext, GCS_COMPCLAUSE, NULL, 0); + long compClauseArrayByteCount = + ::ImmGetCompositionStringW(hIMEContext, GCS_COMPCLAUSE, NULL, 0); #ifdef DEBUG_IME - printf("GCS_COMPCLAUSE compClauseLen = %d\n", compClauseLen); + printf("GCS_COMPCLAUSE compClauseArrayByteCount = %d\n", + compClauseArrayByteCount); #endif - compClauseLen = compClauseLen / sizeof(PRUint32); + long compClauseArrayLength = compClauseArrayByteCount / sizeof(PRUint32); + if (compClauseArrayLength > 0) { + if (compClauseArrayByteCount > sIMECompClauseArraySize) { + if (sIMECompClauseArray) + delete [] sIMECompClauseArray; + // Allocate some extra space to avoid reallocations. + PRInt32 arrayLength = compClauseArrayLength + 32; + sIMECompClauseArray = new PRUint32[arrayLength]; + sIMECompClauseArraySize = arrayLength * sizeof(PRUint32); + } - if (compClauseLen > sIMECompClauseArraySize) { - if (sIMECompClauseArray) - delete [] sIMECompClauseArray; - // Allocate some extra space to avoid reallocations. - sIMECompClauseArray = new PRUint32[compClauseLen + 32]; - sIMECompClauseArraySize = compClauseLen + 32; + // Intelligent ABC IME (Simplified Chinese IME, the code page is 936) + // will crash in ImmGetCompositionStringW for GCS_COMPCLAUSE (bug 424663). + // See comment 35 of the bug for the detail. Therefore, we should use A + // API for it, however, we should not kill Unicode support on all IMEs. + PRBool useA_API = !(gKbdLayout.GetIMEProperty() & IME_PROP_UNICODE); + + long compClauseArrayByteCount2 = useA_API ? + ::ImmGetCompositionStringA(hIMEContext, GCS_COMPCLAUSE, + sIMECompClauseArray, + sIMECompClauseArraySize) : + ::ImmGetCompositionStringW(hIMEContext, GCS_COMPCLAUSE, + sIMECompClauseArray, + sIMECompClauseArraySize); + NS_ASSERTION(compClauseArrayByteCount2 == compClauseArrayByteCount, + "strange result"); + if (compClauseArrayByteCount > compClauseArrayByteCount2) + compClauseArrayLength = compClauseArrayByteCount2 / sizeof(PRUint32); + + if (useA_API) { + // Convert each values of sIMECompClauseArray. The values mean offset of + // the clauses in ANSI string. But we need the values in Unicode string. + nsCAutoString compANSIStr; + if (ConvertToANSIString(*sIMECompUnicode, + gKbdLayout.GetCodePage(), compANSIStr)) { + PRUint32 maxlen = compANSIStr.Length(); + sIMECompClauseArray[0] = 0; // first value must be 0 + for (PRInt32 i = 1; i < compClauseArrayLength; i++) { + PRUint32 len = PR_MIN(sIMECompClauseArray[i], maxlen); + sIMECompClauseArray[i] = + ::MultiByteToWideChar(gKbdLayout.GetCodePage(), MB_PRECOMPOSED, + (LPCSTR)compANSIStr.get(), len, NULL, 0); + } + } + } } - - compClauseLen2 = ::ImmGetCompositionStringW(hIMEContext, GCS_COMPCLAUSE, sIMECompClauseArray, - sIMECompClauseArraySize * sizeof(PRUint32)); - - compClauseLen2 = compClauseLen2 / sizeof(PRUint32); - NS_ASSERTION(compClauseLen2 == compClauseLen, "strange result"); - if (compClauseLen > compClauseLen2) - compClauseLen = compClauseLen2; - sIMECompClauseArrayLength = compClauseLen; + // compClauseArrayLength may be negative. I.e., ImmGetCompositionStringW + // may return an error code. + sIMECompClauseArrayLength = PR_MAX(0, compClauseArrayLength); //-------------------------------------------------------- // 3. Get GCS_COMPATTR @@ -6912,12 +6956,15 @@ BOOL nsWindow::OnIMEComposition(LPARAM aGCS) if (sIMEAttributeArray) delete [] sIMEAttributeArray; // Allocate some extra space to avoid reallocations. - sIMEAttributeArray = new PRUint8[attrStrLen + 64]; - sIMEAttributeArraySize = attrStrLen + 64; + PRInt32 arrayLength = attrStrLen + 64; + sIMEAttributeArray = new PRUint8[arrayLength]; + sIMEAttributeArraySize = arrayLength * sizeof(PRUint8); } attrStrLen = ::ImmGetCompositionStringW(hIMEContext, GCS_COMPATTR, sIMEAttributeArray, sIMEAttributeArraySize); - sIMEAttributeArrayLength = attrStrLen; + // attrStrLen may be negative. I.e., ImmGetCompositionStringW may return an + // error code. + sIMEAttributeArrayLength = PR_MAX(0, attrStrLen); //-------------------------------------------------------- // 4. Get GCS_CURSOPOS @@ -6967,12 +7014,7 @@ BOOL nsWindow::OnIMEEndComposition() printf("OnIMEEndComposition\n"); #endif if (sIMEIsComposing) { - HIMC hIMEContext; - - if (sIMEProperty & (IME_PROP_SPECIAL_UI | IME_PROP_AT_CARET)) - return PR_FALSE; - - hIMEContext = ::ImmGetContext(mWnd); + HIMC hIMEContext = ::ImmGetContext(mWnd); if (hIMEContext==NULL) return PR_TRUE; @@ -7278,12 +7320,7 @@ BOOL nsWindow::OnIMEStartComposition() #ifdef DEBUG_IME printf("OnIMEStartComposition\n"); #endif - HIMC hIMEContext; - - if (sIMEProperty & (IME_PROP_SPECIAL_UI | IME_PROP_AT_CARET)) - return PR_FALSE; - - hIMEContext = ::ImmGetContext(mWnd); + HIMC hIMEContext = ::ImmGetContext(mWnd); if (hIMEContext == NULL) return PR_TRUE; diff --git a/mozilla/widget/src/windows/nsWindow.h b/mozilla/widget/src/windows/nsWindow.h index 4f5fffd38c0..979543de893 100644 --- a/mozilla/widget/src/windows/nsWindow.h +++ b/mozilla/widget/src/windows/nsWindow.h @@ -323,7 +323,7 @@ protected: UINT MapFromNativeToDOM(UINT aNativeKeyCode); - BOOL OnInputLangChange(HKL aHKL, LRESULT *oResult); + BOOL OnInputLangChange(HKL aHKL); BOOL OnIMEChar(BYTE aByte1, BYTE aByte2, LPARAM aKeyState); BOOL OnIMEComposition(LPARAM aGCS); BOOL OnIMECompositionFull(); @@ -340,6 +340,9 @@ protected: void ResolveIMECaretPos(nsWindow* aClient, nsRect& aEventResult, nsRect& aResult); + PRBool ConvertToANSIString(const nsAFlatString& aStr, + UINT aCodePage, + nsACString& aANSIStr); virtual PRBool DispatchKeyEvent(PRUint32 aEventType, WORD aCharCode, const nsTArray* aAlternativeChars, @@ -396,7 +399,6 @@ protected: static PRBool sIMEIsComposing; static PRBool sIMEIsStatusChanged; - static DWORD sIMEProperty; static nsString* sIMECompUnicode; static PRUint8* sIMEAttributeArray; static PRInt32 sIMEAttributeArrayLength; @@ -466,7 +468,6 @@ protected: HIMC mOldIMC; PRUint32 mIMEEnabled; - static HKL gKeyboardLayout; static PRBool gSwitchKeyboardLayout; HKL mLastKeyboardLayout;