Bug 1325676: Prevent Windows 8 touchscreen support from instantiating a11y; r?jimm draft
authorAaron Klotz <aklotz@mozilla.com>
Thu, 29 Dec 2016 15:48:52 -0700
changeset 455386 e0aece7a7fcb031aaab06e9935991d6a1bbe23b0
parent 454222 a6d29e9432f5f88a941c0ea5284cb082f34bd097
child 540971 7c699fb41602e475bf40a3bdf5b6bb06213de258
push id40218
push useraklotz@mozilla.com
push dateTue, 03 Jan 2017 17:32:16 +0000
reviewersjimm
bugs1325676
milestone53.0a1
Bug 1325676: Prevent Windows 8 touchscreen support from instantiating a11y; r?jimm MozReview-Commit-ID: 5qgjBPSVrAh
toolkit/xre/test/win/TestDllInterceptor.cpp
widget/windows/nsWindow.cpp
--- a/toolkit/xre/test/win/TestDllInterceptor.cpp
+++ b/toolkit/xre/test/win/TestDllInterceptor.cpp
@@ -162,15 +162,16 @@ int main()
       TestDetour("user32.dll", "CreateWindowExW") &&
       TestHook("user32.dll", "InSendMessageEx") &&
       TestHook("imm32.dll", "ImmGetContext") &&
       TestHook("imm32.dll", "ImmGetCompositionStringW") &&
       TestHook("imm32.dll", "ImmSetCandidateWindow") &&
 #ifdef _M_X64
       TestHook("user32.dll", "GetKeyState") &&
 #endif
+      TestHook("user32.dll", "SetWinEventHook") &&
       TestDetour("ntdll.dll", "LdrLoadDll")) {
     printf("TEST-PASS | WindowsDllInterceptor | all checks passed\n");
     return 0;
   }
 
   return 1;
 }
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -341,37 +341,43 @@ DWORD CurrentWindowsTimeGetter::sLastPos
  * 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().
+ *
+ * For Windows 8, we also hook tiptsf!ProcessCaretEvents, which is an a11y hook
+ * function that also calls into UIA.
  */
 class TIPMessageHandler
 {
 public:
   ~TIPMessageHandler()
   {
     if (mHook) {
       ::UnhookWindowsHookEx(mHook);
     }
   }
 
   static void Initialize()
   {
     MOZ_ASSERT(!sInstance);
+    if (!IsWin8OrLater()) {
+      return;
+    }
+
     sInstance = new TIPMessageHandler();
     ClearOnShutdown(&sInstance);
   }
 
   static bool IsA11yBlocked()
   {
-    MOZ_ASSERT(sInstance);
     if (!sInstance) {
       return false;
     }
 
     return sInstance->mA11yBlockCount > 0;
   }
 
 private:
@@ -388,31 +394,44 @@ private:
     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);
+
+    if (!IsWin10OrLater()) {
+      // tiptsf loads when STA COM is first initialized, so it should be present
+      sTipTsfInterceptor.Init("tiptsf.dll");
+      DebugOnly<bool> ok = sTipTsfInterceptor.AddHook("ProcessCaretEvents",
+          reinterpret_cast<intptr_t>(&ProcessCaretEventsHook),
+          (void**) &sProcessCaretEventsStub);
+      MOZ_ASSERT(ok);
+    }
   }
 
   class MOZ_RAII A11yInstantiationBlocker
   {
   public:
     A11yInstantiationBlocker()
     {
-      MOZ_ASSERT(TIPMessageHandler::sInstance);
+      if (!TIPMessageHandler::sInstance) {
+        return;
+      }
       ++TIPMessageHandler::sInstance->mA11yBlockCount;
     }
 
     ~A11yInstantiationBlocker()
     {
-      MOZ_ASSERT(TIPMessageHandler::sInstance &&
-                 TIPMessageHandler::sInstance->mA11yBlockCount > 0);
+      if (!TIPMessageHandler::sInstance) {
+        return;
+      }
+      MOZ_ASSERT(TIPMessageHandler::sInstance->mA11yBlockCount > 0);
       --TIPMessageHandler::sInstance->mA11yBlockCount;
     }
   };
 
   friend class A11yInstantiationBlocker;
 
   static LRESULT CALLBACK TIPHook(int aCode, WPARAM aWParam, LPARAM aLParam)
   {
@@ -428,24 +447,39 @@ private:
         A11yInstantiationBlocker block;
         return ::CallNextHookEx(nullptr, aCode, aWParam, aLParam);
       }
     }
 
     return ::CallNextHookEx(nullptr, aCode, aWParam, aLParam);
   }
 
+  static void CALLBACK ProcessCaretEventsHook(HWINEVENTHOOK aWinEventHook,
+                                              DWORD aEvent, HWND aHwnd,
+                                              LONG aObjectId, LONG aChildId,
+                                              DWORD aGeneratingTid,
+                                              DWORD aEventTime)
+  {
+    A11yInstantiationBlocker block;
+    sProcessCaretEventsStub(aWinEventHook, aEvent, aHwnd, aObjectId, aChildId,
+                            aGeneratingTid, aEventTime);
+  }
+
   static StaticAutoPtr<TIPMessageHandler> sInstance;
-
-  HHOOK     mHook;
-  UINT      mMessages[7];
-  uint32_t  mA11yBlockCount;
+  static WindowsDllInterceptor sTipTsfInterceptor;
+  static WINEVENTPROC sProcessCaretEventsStub;
+
+  HHOOK                 mHook;
+  UINT                  mMessages[7];
+  uint32_t              mA11yBlockCount;
 };
 
 StaticAutoPtr<TIPMessageHandler> TIPMessageHandler::sInstance;
+WindowsDllInterceptor TIPMessageHandler::sTipTsfInterceptor;
+WINEVENTPROC TIPMessageHandler::sProcessCaretEventsStub;
 
 #endif // defined(ACCESSIBILITY)
 
 } // namespace mozilla
 
 /**************************************************************
  *
  * SECTION: globals variables