From 39b609a350569d282a81d1da96c12fe7219a4f02 Mon Sep 17 00:00:00 2001 From: Aaron Klotz Date: Wed, 14 Dec 2016 12:20:48 -0700 Subject: [PATCH] Bug 1323521: Prevent Windows touchscreen support from instantiating a11y; r=jimm MozReview-Commit-ID: 3xV0b97lEre --HG-- extra : rebase_source : 7edcc93d409ffcd50e0fe4307573c2660a274b28 extra : amend_source : 0b8f3fbc292b632b448c135ea55fa6f63d292b35 --- widget/windows/nsWindow.cpp | 123 ++++++++++++++++++++++++++++++++++-- 1 file changed, 119 insertions(+), 4 deletions(-) diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 0dd39eea3e10..d896ecf933d8 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -329,6 +329,118 @@ private: Maybe CurrentWindowsTimeGetter::sBackwardsSkewStamp; DWORD CurrentWindowsTimeGetter::sLastPostTime = 0; +#if defined(ACCESSIBILITY) +/** + * Windows touchscreen code works by setting a global WH_GETMESSAGE hook and + * injecting tiptsf.dll. The touchscreen process then posts registered messages + * to our main thread. The tiptsf hook picks up those registered messages and + * uses them as commands, some of which call into UIA, which then calls into + * MSAA, which then sends WM_GETOBJECT to us. + * + * We can get ahead of this by installing our own thread-local WH_GETMESSAGE + * hook. Since thread-local hooks are called ahead of global hooks, we will + * see these registered messages before tiptsf does. At this point we can then + * raise a flag that blocks a11y before invoking CallNextHookEx which will then + * invoke the global tiptsf hook. Then when we see WM_GETOBJECT, we check the + * flag by calling TIPMessageHandler::IsA11yBlocked(). + */ +class TIPMessageHandler +{ +public: + ~TIPMessageHandler() + { + if (mHook) { + ::UnhookWindowsHookEx(mHook); + } + } + + static void Initialize() + { + MOZ_ASSERT(!sInstance); + sInstance = new TIPMessageHandler(); + ClearOnShutdown(&sInstance); + } + + static bool IsA11yBlocked() + { + MOZ_ASSERT(sInstance); + if (!sInstance) { + return false; + } + + return sInstance->mA11yBlockCount > 0; + } + +private: + TIPMessageHandler() + : mHook(nullptr) + , mA11yBlockCount(0) + { + MOZ_ASSERT(NS_IsMainThread()); + + // Registered messages used by tiptsf + mMessages[0] = ::RegisterWindowMessage(L"ImmersiveFocusNotification"); + mMessages[1] = ::RegisterWindowMessage(L"TipCloseMenus"); + mMessages[2] = ::RegisterWindowMessage(L"TabletInputPanelOpening"); + mMessages[3] = ::RegisterWindowMessage(L"IHM Pen or Touch Event noticed"); + mMessages[4] = ::RegisterWindowMessage(L"ProgrammabilityCaretVisibility"); + mMessages[5] = ::RegisterWindowMessage(L"CaretTrackingUpdateIPHidden"); + mMessages[6] = ::RegisterWindowMessage(L"CaretTrackingUpdateIPInfo"); + + mHook = ::SetWindowsHookEx(WH_GETMESSAGE, &TIPHook, nullptr, + ::GetCurrentThreadId()); + MOZ_ASSERT(mHook); + } + + class MOZ_RAII A11yInstantiationBlocker + { + public: + A11yInstantiationBlocker() + { + MOZ_ASSERT(TIPMessageHandler::sInstance); + ++TIPMessageHandler::sInstance->mA11yBlockCount; + } + + ~A11yInstantiationBlocker() + { + MOZ_ASSERT(TIPMessageHandler::sInstance && + TIPMessageHandler::sInstance->mA11yBlockCount > 0); + --TIPMessageHandler::sInstance->mA11yBlockCount; + } + }; + + friend class A11yInstantiationBlocker; + + static LRESULT CALLBACK TIPHook(int aCode, WPARAM aWParam, LPARAM aLParam) + { + if (aCode < 0 || !sInstance) { + return ::CallNextHookEx(nullptr, aCode, aWParam, aLParam); + } + + MSG* msg = reinterpret_cast(aLParam); + UINT& msgCode = msg->message; + + for (uint32_t i = 0; i < ArrayLength(sInstance->mMessages); ++i) { + if (msgCode == sInstance->mMessages[i]) { + A11yInstantiationBlocker block; + return ::CallNextHookEx(nullptr, aCode, aWParam, aLParam); + } + } + + return ::CallNextHookEx(nullptr, aCode, aWParam, aLParam); + } + + static StaticAutoPtr sInstance; + + HHOOK mHook; + UINT mMessages[7]; + uint32_t mA11yBlockCount; +}; + +StaticAutoPtr TIPMessageHandler::sInstance; + +#endif // defined(ACCESSIBILITY) + } // namespace mozilla /************************************************************** @@ -456,6 +568,9 @@ nsWindow::nsWindow() // WinTaskbar.cpp for details. mozilla::widget::WinTaskbar::RegisterAppUserModelID(); KeyboardLayout::GetInstance()->OnLayoutChange(::GetKeyboardLayout(0)); +#if defined(ACCESSIBILITY) + mozilla::TIPMessageHandler::Initialize(); +#endif // defined(ACCESSIBILITY) IMEHandler::Initialize(); if (SUCCEEDED(::OleInitialize(nullptr))) { sIsOleInitialized = TRUE; @@ -3281,9 +3396,9 @@ nsWindow::MakeFullScreen(bool aFullScreen, nsIScreen* aTargetScreen) if (aFullScreen && !sCurrentWindow) { sCurrentWindow = this; LPARAM pos = sCurrentWindow->lParamToClient(sMouseExitlParamScreen); - sCurrentWindow->DispatchMouseEvent(eMouseEnterIntoWidget, - sMouseExitwParam, pos, false, - WidgetMouseEvent::eLeftButton, + sCurrentWindow->DispatchMouseEvent(eMouseEnterIntoWidget, + sMouseExitwParam, pos, false, + WidgetMouseEvent::eLeftButton, MOUSE_INPUT_SOURCE(), MOUSE_POINTERID()); } @@ -5599,7 +5714,7 @@ nsWindow::ProcessMessage(UINT msg, WPARAM& wParam, LPARAM& lParam, // Do explicit casting to make it working on 64bit systems (see bug 649236 // for details). int32_t objId = static_cast(lParam); - if (objId == OBJID_CLIENT) { // oleacc.dll will be loaded dynamically + if (!TIPMessageHandler::IsA11yBlocked() && objId == OBJID_CLIENT) { // oleacc.dll will be loaded dynamically a11y::Accessible* rootAccessible = GetAccessible(); // Held by a11y cache if (rootAccessible) { IAccessible *msaaAccessible = nullptr;