Bug 1329616: Use intercepted SendMessageTimeoutW to check for WM_GETOBJECT originating from tiptsf; r=jimm a=jcristau
authorAaron Klotz <aklotz@mozilla.com>
Mon, 09 Jan 2017 16:06:01 -0700
changeset 353577 de66a249a38b540ea34e9eb36a8d6a72c9fde4ef
parent 353576 ce6c527c87f0580b9f7171ac2e25aea9a707ac5d
child 353578 06ae7f59eff7c9ec09d176d8e3b02929b9157ec7
push id6795
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 14:19:46 +0000
treeherdermozilla-esr52@76101b503191 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimm, jcristau
bugs1329616
milestone52.0a2
Bug 1329616: Use intercepted SendMessageTimeoutW to check for WM_GETOBJECT originating from tiptsf; r=jimm a=jcristau
toolkit/xre/test/win/TestDllInterceptor.cpp
widget/windows/nsWindow.cpp
--- a/toolkit/xre/test/win/TestDllInterceptor.cpp
+++ b/toolkit/xre/test/win/TestDllInterceptor.cpp
@@ -220,15 +220,18 @@ int main()
       TestHook("user32.dll", "InSendMessageEx") &&
       TestHook("imm32.dll", "ImmGetContext") &&
       TestHook("imm32.dll", "ImmGetCompositionStringW") &&
       TestHook("imm32.dll", "ImmSetCandidateWindow") &&
 #ifdef _M_X64
       TestHook("user32.dll", "GetKeyState") &&
 #endif
       MaybeTestHook(ShouldTestTipTsf(), "tiptsf.dll", "ProcessCaretEvents") &&
+#ifdef _M_IX86
+      TestHook("user32.dll", "SendMessageTimeoutW") &&
+#endif
       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
@@ -324,17 +324,70 @@ private:
   static Maybe<TimeStamp> sBackwardsSkewStamp;
   static DWORD sLastPostTime;
   HWND mWnd;
 };
 
 Maybe<TimeStamp> CurrentWindowsTimeGetter::sBackwardsSkewStamp;
 DWORD CurrentWindowsTimeGetter::sLastPostTime = 0;
 
-#if defined(ACCESSIBILITY)
+} // namespace mozilla
+
+/**************************************************************
+ *
+ * SECTION: globals variables
+ *
+ **************************************************************/
+
+static const char *sScreenManagerContractID       = "@mozilla.org/gfx/screenmanager;1";
+
+extern PRLogModuleInfo* gWindowsLog;
+
+// Global used in Show window enumerations.
+static bool     gWindowsVisible                   = false;
+
+// True if we have sent a notification that we are suspending/sleeping.
+static bool     gIsSleepMode                      = false;
+
+static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
+
+// General purpose user32.dll hook object
+static WindowsDllInterceptor sUser32Intercept;
+
+// 2 pixel offset for eTransparencyBorderlessGlass which equals the size of
+// the default window border Windows paints. Glass will be extended inward
+// this distance to remove the border.
+static const int32_t kGlassMarginAdjustment = 2;
+
+// When the client area is extended out into the default window frame area,
+// this is the minimum amount of space along the edge of resizable windows
+// we will always display a resize cursor in, regardless of the underlying
+// content.
+static const int32_t kResizableBorderMinSize = 3;
+
+// Cached pointer events enabler value, True if pointer events are enabled.
+static bool gIsPointerEventsEnabled = false;
+
+// We should never really try to accelerate windows bigger than this. In some
+// cases this might lead to no D3D9 acceleration where we could have had it
+// but D3D9 does not reliably report when it supports bigger windows. 8192
+// is as safe as we can get, we know at least D3D10 hardware always supports
+// this, other hardware we expect to report correctly in D3D9.
+#define MAX_ACCELERATED_DIMENSION 8192
+
+// On window open (as well as after), Windows has an unfortunate habit of
+// sending rather a lot of WM_NCHITTEST messages. Because we have to do point
+// to DOM target conversions for these, we cache responses for a given
+// coordinate this many milliseconds:
+#define HITTEST_CACHE_LIFETIME_MS 50
+
+#if defined(ACCESSIBILITY) && defined(_M_IX86)
+
+namespace mozilla {
+
 /**
  * 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
@@ -392,26 +445,31 @@ private:
     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 defined(_M_IX86)
     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);
     }
-#endif // defined(_M_IX86)
+
+    MOZ_ASSERT(!sSendMessageTimeoutWStub);
+    sUser32Intercept.Init("user32.dll");
+    DebugOnly<bool> hooked = sUser32Intercept.AddHook("SendMessageTimeoutW",
+        reinterpret_cast<intptr_t>(&SendMessageTimeoutWHook),
+        (void**) &sSendMessageTimeoutWStub);
+    MOZ_ASSERT(hooked);
   }
 
   class MOZ_RAII A11yInstantiationBlocker
   {
   public:
     A11yInstantiationBlocker()
     {
       if (!TIPMessageHandler::sInstance) {
@@ -446,98 +504,70 @@ private:
         A11yInstantiationBlocker block;
         return ::CallNextHookEx(nullptr, aCode, aWParam, aLParam);
       }
     }
 
     return ::CallNextHookEx(nullptr, aCode, aWParam, aLParam);
   }
 
-#if defined(_M_IX86)
   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 LRESULT WINAPI SendMessageTimeoutWHook(HWND aHwnd, UINT aMsgCode,
+                                                WPARAM aWParam, LPARAM aLParam,
+                                                UINT aFlags, UINT aTimeout,
+                                                PDWORD_PTR aMsgResult)
+  {
+    // We don't want to handle this unless the message is a WM_GETOBJECT that we
+    // want to block, and the aHwnd is a nsWindow that belongs to the current
+    // thread.
+    if (!aMsgResult || aMsgCode != WM_GETOBJECT || aLParam != OBJID_CLIENT ||
+        !WinUtils::GetNSWindowPtr(aHwnd) ||
+        ::GetWindowThreadProcessId(aHwnd, nullptr) != ::GetCurrentThreadId() ||
+        !IsA11yBlocked()) {
+      return sSendMessageTimeoutWStub(aHwnd, aMsgCode, aWParam, aLParam,
+                                      aFlags, aTimeout, aMsgResult);
+    }
+
+    // In this case we want to fake the result that would happen if we had
+    // decided not to handle WM_GETOBJECT in our WndProc. We hand the message
+    // off to DefWindowProc to accomplish this.
+    *aMsgResult = static_cast<DWORD_PTR>(::DefWindowProcW(aHwnd, aMsgCode,
+                                                          aWParam, aLParam));
+
+    return static_cast<LRESULT>(TRUE);
+  }
+
   static WindowsDllInterceptor sTipTsfInterceptor;
   static WINEVENTPROC sProcessCaretEventsStub;
-#endif // defined(_M_IX86)
-
+  static decltype(&SendMessageTimeoutW) sSendMessageTimeoutWStub;
   static StaticAutoPtr<TIPMessageHandler> sInstance;
 
   HHOOK                 mHook;
   UINT                  mMessages[7];
   uint32_t              mA11yBlockCount;
 };
 
-#if defined(_M_IX86)
 WindowsDllInterceptor TIPMessageHandler::sTipTsfInterceptor;
 WINEVENTPROC TIPMessageHandler::sProcessCaretEventsStub;
-#endif // defined(_M_IX86)
-
+decltype(&SendMessageTimeoutW) TIPMessageHandler::sSendMessageTimeoutWStub;
 StaticAutoPtr<TIPMessageHandler> TIPMessageHandler::sInstance;
 
-#endif // defined(ACCESSIBILITY)
-
 } // namespace mozilla
 
-/**************************************************************
- *
- * SECTION: globals variables
- *
- **************************************************************/
-
-static const char *sScreenManagerContractID       = "@mozilla.org/gfx/screenmanager;1";
-
-extern PRLogModuleInfo* gWindowsLog;
-
-// Global used in Show window enumerations.
-static bool     gWindowsVisible                   = false;
-
-// True if we have sent a notification that we are suspending/sleeping.
-static bool     gIsSleepMode                      = false;
-
-static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
-
-// General purpose user32.dll hook object
-static WindowsDllInterceptor sUser32Intercept;
-
-// 2 pixel offset for eTransparencyBorderlessGlass which equals the size of
-// the default window border Windows paints. Glass will be extended inward
-// this distance to remove the border.
-static const int32_t kGlassMarginAdjustment = 2;
-
-// When the client area is extended out into the default window frame area,
-// this is the minimum amount of space along the edge of resizable windows
-// we will always display a resize cursor in, regardless of the underlying
-// content.
-static const int32_t kResizableBorderMinSize = 3;
-
-// Cached pointer events enabler value, True if pointer events are enabled.
-static bool gIsPointerEventsEnabled = false;
-
-// We should never really try to accelerate windows bigger than this. In some
-// cases this might lead to no D3D9 acceleration where we could have had it
-// but D3D9 does not reliably report when it supports bigger windows. 8192
-// is as safe as we can get, we know at least D3D10 hardware always supports
-// this, other hardware we expect to report correctly in D3D9.
-#define MAX_ACCELERATED_DIMENSION 8192
-
-// On window open (as well as after), Windows has an unfortunate habit of
-// sending rather a lot of WM_NCHITTEST messages. Because we have to do point
-// to DOM target conversions for these, we cache responses for a given
-// coordinate this many milliseconds:
-#define HITTEST_CACHE_LIFETIME_MS 50
-
+#endif // defined(ACCESSIBILITY) && defined(_M_IX86)
 
 /**************************************************************
  **************************************************************
  **
  ** BLOCK: nsIWidget impl.
  **
  ** nsIWidget interface implementation, broken down into
  ** sections.
@@ -605,19 +635,19 @@ nsWindow::nsWindow()
   mTaskbarPreview = nullptr;
 
   // Global initialization
   if (!sInstanceCount) {
     // Global app registration id for Win7 and up. See
     // WinTaskbar.cpp for details.
     mozilla::widget::WinTaskbar::RegisterAppUserModelID();
     KeyboardLayout::GetInstance()->OnLayoutChange(::GetKeyboardLayout(0));
-#if defined(ACCESSIBILITY)
+#if defined(ACCESSIBILITY) && defined(_M_IX86)
     mozilla::TIPMessageHandler::Initialize();
-#endif // defined(ACCESSIBILITY)
+#endif // defined(ACCESSIBILITY) && defined(_M_IX86)
     IMEHandler::Initialize();
     if (SUCCEEDED(::OleInitialize(nullptr))) {
       sIsOleInitialized = TRUE;
     }
     NS_ASSERTION(sIsOleInitialized, "***** OLE is not initialized!\n");
     MouseScrollHandler::Initialize();
     // Init titlebar button info for custom frames.
     nsUXThemeData::InitTitlebarInfo();
@@ -5755,17 +5785,17 @@ nsWindow::ProcessMessage(UINT msg, WPARA
 
 #ifdef ACCESSIBILITY
     case WM_GETOBJECT:
     {
       *aRetValue = 0;
       // Do explicit casting to make it working on 64bit systems (see bug 649236
       // for details).
       int32_t objId = static_cast<DWORD>(lParam);
-      if (!TIPMessageHandler::IsA11yBlocked() && objId == OBJID_CLIENT) { // oleacc.dll will be loaded dynamically
+      if (objId == OBJID_CLIENT) { // oleacc.dll will be loaded dynamically
         a11y::Accessible* rootAccessible = GetAccessible(); // Held by a11y cache
         if (rootAccessible) {
           IAccessible *msaaAccessible = nullptr;
           rootAccessible->GetNativeInterface((void**)&msaaAccessible); // does an addref
           if (msaaAccessible) {
             *aRetValue = LresultFromObject(IID_IAccessible, wParam, msaaAccessible); // does an addref
             msaaAccessible->Release(); // release extra addref
             result = true;  // We handled the WM_GETOBJECT message