diff --git a/widget/windows/WinIMEHandler.cpp b/widget/windows/WinIMEHandler.cpp index 6e15d42612a4..c0e9b87b650a 100644 --- a/widget/windows/WinIMEHandler.cpp +++ b/widget/windows/WinIMEHandler.cpp @@ -11,6 +11,7 @@ #endif // #ifdef NS_ENABLE_TSF #include "nsWindow.h" +#include "WinUtils.h" namespace mozilla { namespace widget { @@ -98,6 +99,18 @@ IMEHandler::IsIMEEnabled(IMEState::Enabled aIMEState) aIMEState == mozilla::widget::IMEState::PLUGIN); } +// static +bool +IMEHandler::ProcessRawKeyMessage(const MSG& aMsg) +{ +#ifdef NS_ENABLE_TSF + if (IsTSFAvailable()) { + return nsTextStore::ProcessRawKeyMessage(aMsg); + } +#endif // #ifdef NS_ENABLE_TSF + return false; // noting to do in IMM mode. +} + // static bool IMEHandler::ProcessMessage(nsWindow* aWindow, UINT aMessage, @@ -369,13 +382,13 @@ IMEHandler::IsDoingKakuteiUndo(HWND aWnd) // https://bugzilla.mozilla.gr.jp/show_bug.cgi?id=2885 (written in Japanese) // https://bugzilla.mozilla.org/show_bug.cgi?id=194559 (written in English) MSG startCompositionMsg, compositionMsg, charMsg; - return ::PeekMessageW(&startCompositionMsg, aWnd, - WM_IME_STARTCOMPOSITION, WM_IME_STARTCOMPOSITION, - PM_NOREMOVE | PM_NOYIELD) && - ::PeekMessageW(&compositionMsg, aWnd, WM_IME_COMPOSITION, - WM_IME_COMPOSITION, PM_NOREMOVE | PM_NOYIELD) && - ::PeekMessageW(&charMsg, aWnd, WM_CHAR, WM_CHAR, - PM_NOREMOVE | PM_NOYIELD) && + return WinUtils::PeekMessage(&startCompositionMsg, aWnd, + WM_IME_STARTCOMPOSITION, WM_IME_STARTCOMPOSITION, + PM_NOREMOVE | PM_NOYIELD) && + WinUtils::PeekMessage(&compositionMsg, aWnd, WM_IME_COMPOSITION, + WM_IME_COMPOSITION, PM_NOREMOVE | PM_NOYIELD) && + WinUtils::PeekMessage(&charMsg, aWnd, WM_CHAR, WM_CHAR, + PM_NOREMOVE | PM_NOYIELD) && startCompositionMsg.wParam == 0x0 && startCompositionMsg.lParam == 0x0 && compositionMsg.wParam == 0x0 && diff --git a/widget/windows/WinIMEHandler.h b/widget/windows/WinIMEHandler.h index b9b219e43d47..0f371e3b456a 100644 --- a/widget/windows/WinIMEHandler.h +++ b/widget/windows/WinIMEHandler.h @@ -48,6 +48,13 @@ public: static bool IsIMEEnabled(const InputContext& aInputContext); static bool IsIMEEnabled(IMEState::Enabled aIMEState); + /** + * ProcessRawKeyMessage() message is called before calling TranslateMessage() + * and DispatchMessage(). If this returns true, the message is consumed. + * Then, caller must not perform TranslateMessage() nor DispatchMessage(). + */ + static bool ProcessRawKeyMessage(const MSG& aMsg); + /** * When the message is not needed to handle anymore by the caller, this * returns true. Otherwise, false. diff --git a/widget/windows/WinUtils.cpp b/widget/windows/WinUtils.cpp index 472449ebc87c..98ac5009ae2d 100644 --- a/widget/windows/WinUtils.cpp +++ b/widget/windows/WinUtils.cpp @@ -27,6 +27,11 @@ #include "nsIObserver.h" #include "imgIEncoder.h" +#ifdef NS_ENABLE_TSF +#include +#include "nsTextStore.h" +#endif // #ifdef NS_ENABLE_TSF + namespace mozilla { namespace widget { @@ -68,6 +73,42 @@ WinUtils::GetWindowsVersion() return static_cast(version); } +/* static */ +bool +WinUtils::PeekMessage(LPMSG aMsg, HWND aWnd, UINT aFirstMessage, + UINT aLastMessage, UINT aOption) +{ +#ifdef NS_ENABLE_TSF + ITfMessagePump* msgPump = nsTextStore::GetMessagePump(); + if (msgPump) { + BOOL ret = FALSE; + HRESULT hr = msgPump->PeekMessageW(aMsg, aWnd, aFirstMessage, aLastMessage, + aOption, &ret); + NS_ENSURE_TRUE(SUCCEEDED(hr), false); + return ret; + } +#endif // #ifdef NS_ENABLE_TSF + return ::PeekMessageW(aMsg, aWnd, aFirstMessage, aLastMessage, aOption); +} + +/* static */ +bool +WinUtils::GetMessage(LPMSG aMsg, HWND aWnd, UINT aFirstMessage, + UINT aLastMessage) +{ +#ifdef NS_ENABLE_TSF + ITfMessagePump* msgPump = nsTextStore::GetMessagePump(); + if (msgPump) { + BOOL ret = FALSE; + HRESULT hr = msgPump->GetMessageW(aMsg, aWnd, aFirstMessage, aLastMessage, + &ret); + NS_ENSURE_TRUE(SUCCEEDED(hr), false); + return ret; + } +#endif // #ifdef NS_ENABLE_TSF + return ::GetMessageW(aMsg, aWnd, aFirstMessage, aLastMessage); +} + /* static */ bool WinUtils::GetRegistryKey(HKEY aRoot, diff --git a/widget/windows/WinUtils.h b/widget/windows/WinUtils.h index b5e52d3bd315..27c9aec7dd5e 100644 --- a/widget/windows/WinUtils.h +++ b/widget/windows/WinUtils.h @@ -49,6 +49,16 @@ public: }; static WinVersion GetWindowsVersion(); + /** + * PeekMessage() and GetMessage() are wrapper methods for PeekMessageW(), + * GetMessageW(), ITfMessageMgr::PeekMessageW() and + * ITfMessageMgr::GetMessageW(). + * Don't call the native APIs directly. You MUST use these methods instead. + */ + static bool PeekMessage(LPMSG aMsg, HWND aWnd, UINT aFirstMessage, + UINT aLastMessage, UINT aOption); + static bool GetMessage(LPMSG aMsg, HWND aWnd, UINT aFirstMessage, + UINT aLastMessage); /** * Gets the value of a string-typed registry value. * diff --git a/widget/windows/nsAppShell.cpp b/widget/windows/nsAppShell.cpp index 2e7240713e97..e9596eb7fb82 100644 --- a/widget/windows/nsAppShell.cpp +++ b/widget/windows/nsAppShell.cpp @@ -7,6 +7,7 @@ #include "nsAppShell.h" #include "nsToolkit.h" #include "nsThreadUtils.h" +#include "WinUtils.h" #include "WinTaskbar.h" #include "WinMouseScrollHandler.h" #include "nsWindowDefs.h" @@ -15,6 +16,8 @@ #include "mozilla/widget/AudioSession.h" #include "mozilla/HangMonitor.h" +using namespace mozilla::widget; + const PRUnichar* kAppShellEventId = L"nsAppShell:EventID"; const PRUnichar* kTaskbarButtonEventId = L"TaskbarButtonCreated"; @@ -47,17 +50,20 @@ static bool PeekUIMessage(MSG* aMsg) // it may make different modifier key state or mouse cursor position between // them. if (mozilla::widget::MouseScrollHandler::IsWaitingInternalMessage() && - ::PeekMessageW(aMsg, NULL, MOZ_WM_MOUSEWHEEL_FIRST, - MOZ_WM_MOUSEWHEEL_LAST, PM_REMOVE)) { + WinUtils::PeekMessage(aMsg, NULL, MOZ_WM_MOUSEWHEEL_FIRST, + MOZ_WM_MOUSEWHEEL_LAST, PM_REMOVE)) { return true; } MSG keyMsg, imeMsg, mouseMsg, *pMsg = 0; bool haveKeyMsg, haveIMEMsg, haveMouseMsg; - haveKeyMsg = ::PeekMessageW(&keyMsg, NULL, WM_KEYFIRST, WM_IME_KEYLAST, PM_NOREMOVE); - haveIMEMsg = ::PeekMessageW(&imeMsg, NULL, NS_WM_IMEFIRST, NS_WM_IMELAST, PM_NOREMOVE); - haveMouseMsg = ::PeekMessageW(&mouseMsg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_NOREMOVE); + haveKeyMsg = WinUtils::PeekMessage(&keyMsg, NULL, WM_KEYFIRST, + WM_IME_KEYLAST, PM_NOREMOVE); + haveIMEMsg = WinUtils::PeekMessage(&imeMsg, NULL, NS_WM_IMEFIRST, + NS_WM_IMELAST, PM_NOREMOVE); + haveMouseMsg = WinUtils::PeekMessage(&mouseMsg, NULL, WM_MOUSEFIRST, + WM_MOUSELAST, PM_NOREMOVE); if (haveKeyMsg) { pMsg = &keyMsg; @@ -78,7 +84,8 @@ static bool PeekUIMessage(MSG* aMsg) return false; } - return ::PeekMessageW(aMsg, NULL, pMsg->message, pMsg->message, PM_REMOVE); + return WinUtils::PeekMessage(aMsg, NULL, pMsg->message, + pMsg->message, PM_REMOVE); } /*static*/ LRESULT CALLBACK @@ -223,7 +230,7 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait) // Give priority to keyboard and mouse messages. if (uiMessage || - PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { + WinUtils::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { gotMessage = true; if (msg.message == WM_QUIT) { ::PostQuitMessage(msg.wParam); @@ -234,6 +241,12 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait) mozilla::HangMonitor::NotifyActivity( uiMessage ? mozilla::HangMonitor::kUIActivity : mozilla::HangMonitor::kActivityNoUIAVail); + + if (msg.message >= WM_KEYFIRST && msg.message <= WM_KEYLAST && + IMEHandler::ProcessRawKeyMessage(msg)) { + continue; // the message is consumed. + } + ::TranslateMessage(&msg); ::DispatchMessageW(&msg); } diff --git a/widget/windows/nsIMM32Handler.cpp b/widget/windows/nsIMM32Handler.cpp index 944a1dcbc5ab..54688765133c 100644 --- a/widget/windows/nsIMM32Handler.cpp +++ b/widget/windows/nsIMM32Handler.cpp @@ -526,9 +526,9 @@ nsIMM32Handler::OnIMEEndComposition(nsWindow* aWindow) // composition. Then, we should ignore the message and commit the composition // string at following WM_IME_COMPOSITION. MSG compositionMsg; - if (::PeekMessageW(&compositionMsg, aWindow->GetWindowHandle(), - WM_IME_STARTCOMPOSITION, WM_IME_COMPOSITION, - PM_NOREMOVE) && + if (WinUtils::PeekMessage(&compositionMsg, aWindow->GetWindowHandle(), + WM_IME_STARTCOMPOSITION, WM_IME_COMPOSITION, + PM_NOREMOVE) && compositionMsg.message == WM_IME_COMPOSITION && IS_COMMITTING_LPARAM(compositionMsg.lParam)) { PR_LOG(gIMM32Log, PR_LOG_ALWAYS, @@ -1012,11 +1012,11 @@ nsIMM32Handler::HandleComposition(nsWindow* aWindow, if (!mIsComposing) { MSG msg1, msg2; HWND wnd = aWindow->GetWindowHandle(); - if (::PeekMessageW(&msg1, wnd, WM_IME_STARTCOMPOSITION, WM_IME_COMPOSITION, - PM_NOREMOVE) && + if (WinUtils::PeekMessage(&msg1, wnd, WM_IME_STARTCOMPOSITION, + WM_IME_COMPOSITION, PM_NOREMOVE) && msg1.message == WM_IME_STARTCOMPOSITION && - ::PeekMessageW(&msg2, wnd, WM_IME_ENDCOMPOSITION, WM_IME_COMPOSITION, - PM_NOREMOVE) && + WinUtils::PeekMessage(&msg2, wnd, WM_IME_ENDCOMPOSITION, + WM_IME_COMPOSITION, PM_NOREMOVE) && msg2.message == WM_IME_COMPOSITION) { PR_LOG(gIMM32Log, PR_LOG_ALWAYS, ("IMM32: HandleComposition, Ignores due to find a WM_IME_STARTCOMPOSITION\n")); diff --git a/widget/windows/nsTextStore.cpp b/widget/windows/nsTextStore.cpp index a918d9da4245..4f632e64863e 100644 --- a/widget/windows/nsTextStore.cpp +++ b/widget/windows/nsTextStore.cpp @@ -132,6 +132,8 @@ private: /******************************************************************/ ITfThreadMgr* nsTextStore::sTsfThreadMgr = NULL; +ITfMessagePump* nsTextStore::sMessagePump = NULL; +ITfKeystrokeMgr* nsTextStore::sKeystrokeMgr = NULL; ITfDisplayAttributeMgr* nsTextStore::sDisplayAttrMgr = NULL; ITfCategoryMgr* nsTextStore::sCategoryMgr = NULL; DWORD nsTextStore::sTsfClientId = 0; @@ -579,9 +581,9 @@ nsTextStore::Destroy(void) // But by that time the text store is already destroyed, // so try to get the message early MSG msg; - if (::PeekMessageW(&msg, mWidget->GetWindowHandle(), - sFlushTIPInputMessage, sFlushTIPInputMessage, - PM_REMOVE)) { + if (WinUtils::PeekMessage(&msg, mWidget->GetWindowHandle(), + sFlushTIPInputMessage, sFlushTIPInputMessage, + PM_REMOVE)) { ::DispatchMessageW(&msg); } } @@ -3156,6 +3158,16 @@ nsTextStore::Initialize(void) if (SUCCEEDED(CoCreateInstance(CLSID_TF_ThreadMgr, NULL, CLSCTX_INPROC_SERVER, IID_ITfThreadMgr, reinterpret_cast(&sTsfThreadMgr)))) { + DebugOnly hr = + sTsfThreadMgr->QueryInterface(IID_ITfMessagePump, + reinterpret_cast(&sMessagePump)); + MOZ_ASSERT(SUCCEEDED(hr)); + MOZ_ASSERT(sMessagePump); + hr = + sTsfThreadMgr->QueryInterface(IID_ITfKeystrokeMgr, + reinterpret_cast(&sKeystrokeMgr)); + MOZ_ASSERT(SUCCEEDED(hr)); + MOZ_ASSERT(sKeystrokeMgr); PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, ("TSF: nsTextStore::Initialize() succeeded to " "create the thread manager, activating...")); @@ -3216,6 +3228,11 @@ nsTextStore::Initialize(void) NS_LITERAL_STRING("Flush TIP Input Message").get()); } + if (!sTsfThreadMgr) { + NS_IF_RELEASE(sMessagePump); + NS_IF_RELEASE(sKeystrokeMgr); + } + PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, ("TSF: nsTextStore::Initialize(), sTsfThreadMgr=0x%p, " "sTsfClientId=0x%08X, sTsfTextStore=0x%p, sDisplayAttrMgr=0x%p, " @@ -3237,9 +3254,40 @@ nsTextStore::Terminate(void) if (sTsfThreadMgr) { sTsfThreadMgr->Deactivate(); NS_RELEASE(sTsfThreadMgr); + NS_RELEASE(sMessagePump); + NS_RELEASE(sKeystrokeMgr); } } +// static +bool +nsTextStore::ProcessRawKeyMessage(const MSG& aMsg) +{ + if (!sKeystrokeMgr) { + return false; // not in TSF mode + } + + if (aMsg.message == WM_KEYDOWN) { + BOOL eaten; + HRESULT hr = sKeystrokeMgr->TestKeyDown(aMsg.wParam, aMsg.lParam, &eaten); + if (FAILED(hr) || !eaten) { + return false; + } + hr = sKeystrokeMgr->KeyDown(aMsg.wParam, aMsg.lParam, &eaten); + return SUCCEEDED(hr) && eaten; + } + if (aMsg.message == WM_KEYUP) { + BOOL eaten; + HRESULT hr = sKeystrokeMgr->TestKeyUp(aMsg.wParam, aMsg.lParam, &eaten); + if (FAILED(hr) || !eaten) { + return false; + } + hr = sKeystrokeMgr->KeyUp(aMsg.wParam, aMsg.lParam, &eaten); + return SUCCEEDED(hr) && eaten; + } + return false; +} + /******************************************************************/ /* nsTextStore::Composition */ /******************************************************************/ diff --git a/widget/windows/nsTextStore.h b/widget/windows/nsTextStore.h index 5df666896c22..0262f49216a3 100644 --- a/widget/windows/nsTextStore.h +++ b/widget/windows/nsTextStore.h @@ -96,6 +96,9 @@ protected: public: static void Initialize(void); static void Terminate(void); + + static bool ProcessRawKeyMessage(const MSG& aMsg); + static void SetIMEOpenState(bool); static bool GetIMEOpenState(void); @@ -162,6 +165,11 @@ public: } } + static ITfMessagePump* GetMessagePump() + { + return sMessagePump; + } + static void* GetTextStore() { return static_cast(sTsfTextStore); @@ -630,6 +638,10 @@ protected: // TSF thread manager object for the current application static ITfThreadMgr* sTsfThreadMgr; + // sMessagePump is QI'ed from sTsfThreadMgr + static ITfMessagePump* sMessagePump; + // sKeystrokeMgr is QI'ed from sTsfThreadMgr + static ITfKeystrokeMgr* sKeystrokeMgr; // TSF display attribute manager static ITfDisplayAttributeMgr* sDisplayAttrMgr; // TSF category manager diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 8ae839f8f854..e75a1bacc1f0 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -3806,7 +3806,7 @@ void nsWindow::RemoveMessageAndDispatchPluginEvent(UINT aFirstMsg, } msg = aFakeCharMessage->GetCharMessage(mWnd); } else { - ::GetMessageW(&msg, mWnd, aFirstMsg, aLastMsg); + WinUtils::GetMessage(&msg, mWnd, aFirstMsg, aLastMsg); } DispatchPluginEvent(msg); } @@ -5639,10 +5639,10 @@ void nsWindow::PostSleepWakeNotification(const bool aIsSleepMode) void nsWindow::RemoveNextCharMessage(HWND aWnd) { MSG msg; - if (::PeekMessageW(&msg, aWnd, - WM_KEYFIRST, WM_KEYLAST, PM_NOREMOVE | PM_NOYIELD) && + if (WinUtils::PeekMessage(&msg, aWnd, WM_KEYFIRST, WM_KEYLAST, + PM_NOREMOVE | PM_NOYIELD) && (msg.message == WM_CHAR || msg.message == WM_SYSCHAR)) { - ::GetMessageW(&msg, aWnd, msg.message, msg.message); + WinUtils::GetMessage(&msg, aWnd, msg.message, msg.message); } } @@ -6564,7 +6564,8 @@ LRESULT nsWindow::OnKeyDown(const MSG &aMsg, extraFlags.mDefaultPrevented = noDefault; MSG msg; BOOL gotMsg = aFakeCharMessage || - ::PeekMessageW(&msg, mWnd, WM_KEYFIRST, WM_KEYLAST, PM_NOREMOVE | PM_NOYIELD); + WinUtils::PeekMessage(&msg, mWnd, WM_KEYFIRST, WM_KEYLAST, + PM_NOREMOVE | PM_NOYIELD); // Enter and backspace are always handled here to avoid for example the // confusion between ctrl-enter and ctrl-J. if (DOMKeyCode == NS_VK_RETURN || DOMKeyCode == NS_VK_BACK || @@ -6591,7 +6592,8 @@ LRESULT nsWindow::OnKeyDown(const MSG &aMsg, RemoveMessageAndDispatchPluginEvent(WM_KEYFIRST, WM_KEYLAST); anyCharMessagesRemoved = true; - gotMsg = ::PeekMessageW (&msg, mWnd, WM_KEYFIRST, WM_KEYLAST, PM_NOREMOVE | PM_NOYIELD); + gotMsg = WinUtils::PeekMessage(&msg, mWnd, WM_KEYFIRST, WM_KEYLAST, + PM_NOREMOVE | PM_NOYIELD); } } @@ -6633,7 +6635,7 @@ LRESULT nsWindow::OnKeyDown(const MSG &aMsg, } // If prevent default set for keydown, do same for keypress - ::GetMessageW(&msg, mWnd, msg.message, msg.message); + WinUtils::GetMessage(&msg, mWnd, msg.message, msg.message); if (msg.message == WM_DEADCHAR) { if (!PluginHasFocus()) diff --git a/widget/windows/winrt/MetroAppShell.cpp b/widget/windows/winrt/MetroAppShell.cpp index c308f1088902..092e98d7946a 100644 --- a/widget/windows/winrt/MetroAppShell.cpp +++ b/widget/windows/winrt/MetroAppShell.cpp @@ -10,7 +10,9 @@ #include "MetroApp.h" #include "nsIObserverService.h" #include "nsServiceManagerUtils.h" +#include "WinUtils.h" +using namespace mozilla::widget; using namespace mozilla::widget::winrt; using namespace Microsoft::WRL; using namespace Microsoft::WRL::Wrappers; @@ -140,14 +142,14 @@ MetroAppShell::ProcessNextNativeEvent(bool mayWait) MSG msg; if (mayWait) { - if (!PeekMessageW(&msg, NULL, 0, 0, PM_NOREMOVE)) { + if (!WinUtils::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { WaitMessage(); } ProcessOneNativeEventIfPresent(); return true; } - if (PeekMessageW(&msg, NULL, 0, 0, PM_NOREMOVE)) { + if (WinUtils::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { ProcessOneNativeEventIfPresent(); return true; } diff --git a/widget/windows/winrt/MetroWidget.cpp b/widget/windows/winrt/MetroWidget.cpp index 6d986aefc8d7..a8ded2a28b95 100644 --- a/widget/windows/winrt/MetroWidget.cpp +++ b/widget/windows/winrt/MetroWidget.cpp @@ -119,7 +119,10 @@ namespace { // processed. Log(L" Inputs sent. Waiting for input messages to clear"); MSG msg; - while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + while (WinUtils::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + if (nsTextStore::ProcessRawKeyMessage(msg)) { + continue; // the message is consumed by TSF + } ::TranslateMessage(&msg); ::DispatchMessage(&msg); Log(L" Dispatched 0x%x 0x%x 0x%x", msg.message, msg.wParam, msg.lParam);