Bug 1014673 - Allow WM_GETOBJECT calls to succeed when mozilla window procedures are neutered by ipc WaitForNotify calls. r=bent
authorJim Mathies <jmathies@mozilla.com>
Fri, 30 May 2014 14:30:54 -0500
changeset 185865 69f12c69463081c9c6b70c7a17d54776888234f3
parent 185864 33ebbb506ea436eed43613757b83ccae359fa834
child 185931 7e3dbe00b1e7f23250fc109717ff203a899780f1
push id7074
push userjmathies@mozilla.com
push dateFri, 30 May 2014 19:31:02 +0000
treeherderfx-team@69f12c694630 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbent
bugs1014673
milestone32.0a1
Bug 1014673 - Allow WM_GETOBJECT calls to succeed when mozilla window procedures are neutered by ipc WaitForNotify calls. r=bent
ipc/glue/WindowsMessageLoop.cpp
widget/windows/winrt/MetroWidget.cpp
--- a/ipc/glue/WindowsMessageLoop.cpp
+++ b/ipc/glue/WindowsMessageLoop.cpp
@@ -81,16 +81,17 @@ extern UINT sAppShellGeckoMsgId;
 extern UINT sDefaultBrowserMsgId;
 #endif
 }
 }
 
 namespace {
 
 const wchar_t kOldWndProcProp[] = L"MozillaIPCOldWndProc";
+const wchar_t k3rdPartyWindowProp[] = L"Mozilla3rdPartyWindow";
 
 // This isn't defined before Windows XP.
 enum { WM_XP_THEMECHANGED = 0x031A };
 
 char16_t gAppMessageWindowName[256] = { 0 };
 int32_t gAppMessageWindowNameLength = 0;
 
 nsTArray<HWND>* gNeuteredWindows = nullptr;
@@ -98,16 +99,19 @@ nsTArray<HWND>* gNeuteredWindows = nullp
 typedef nsTArray<nsAutoPtr<DeferredMessage> > DeferredMessageArray;
 DeferredMessageArray* gDeferredMessages = nullptr;
 
 HHOOK gDeferredGetMsgHook = nullptr;
 HHOOK gDeferredCallWndProcHook = nullptr;
 
 DWORD gUIThreadId = 0;
 
+// WM_GETOBJECT id pulled from uia headers
+#define MOZOBJID_UIAROOT -25
+
 LRESULT CALLBACK
 DeferredMessageHook(int nCode,
                     WPARAM wParam,
                     LPARAM lParam)
 {
   // XXX This function is called for *both* the WH_CALLWNDPROC hook and the
   //     WH_GETMESSAGE hook, but they have different parameters. We don't
   //     use any of them except nCode which has the same meaning.
@@ -156,16 +160,38 @@ ScheduleDeferredMessageRun()
     gDeferredCallWndProcHook = ::SetWindowsHookEx(WH_CALLWNDPROC,
                                                   DeferredMessageHook, nullptr,
                                                   gUIThreadId);
     NS_ASSERTION(gDeferredGetMsgHook && gDeferredCallWndProcHook,
                  "Failed to set hooks!");
   }
 }
 
+static void
+DumpNeuteredMessage(HWND hwnd, UINT uMsg)
+{
+#ifdef DEBUG
+  nsAutoCString log("Received \"nonqueued\" message ");
+  log.AppendInt(uMsg);
+  log.AppendLiteral(" during a synchronous IPC message for window ");
+  log.AppendInt((int64_t)hwnd);
+
+  wchar_t className[256] = { 0 };
+  if (GetClassNameW(hwnd, className, sizeof(className) - 1) > 0) {
+    log.AppendLiteral(" (\"");
+    log.Append(NS_ConvertUTF16toUTF8((char16_t*)className));
+    log.AppendLiteral("\")");
+  }
+
+  log.AppendLiteral(", sending it to DefWindowProc instead of the normal "
+                    "window procedure.");
+  NS_ERROR(log.get());
+#endif
+}
+
 LRESULT
 ProcessOrDeferMessage(HWND hwnd,
                       UINT uMsg,
                       WPARAM wParam,
                       LPARAM lParam)
 {
   DeferredMessage* deferred = nullptr;
 
@@ -280,50 +306,52 @@ ProcessOrDeferMessage(HWND hwnd,
     case WM_SYNCPAINT:
       return 0;
 
     // This message causes QuickTime to make re-entrant calls.
     // Simply discarding it doesn't seem to hurt anything.
     case WM_APP-1:
       return 0;
 
+    // We only support a query for our IAccessible or UIA pointers.
+    // This should be safe, and needs to be sync.
+#if defined(ACCESSIBILITY)
+   case WM_GETOBJECT: {
+      if (!::GetPropW(hwnd, k3rdPartyWindowProp)) {
+        DWORD objId = static_cast<DWORD>(lParam);
+        WNDPROC oldWndProc = (WNDPROC)GetProp(hwnd, kOldWndProcProp);
+        if ((objId == OBJID_CLIENT || objId == MOZOBJID_UIAROOT) && oldWndProc) {
+          return CallWindowProcW(oldWndProc, hwnd, uMsg, wParam, lParam);
+        }
+      }
+      break;
+    }
+#endif // ACCESSIBILITY
+
     default: {
+      // Unknown messages only are logged in debug builds and sent to
+      // DefWindowProc.
       if (uMsg && uMsg == mozilla::widget::sAppShellGeckoMsgId) {
         // Widget's registered native event callback
         deferred = new DeferredSendMessage(hwnd, uMsg, wParam, lParam);
 #ifdef MOZ_METRO
       } else if (uMsg && uMsg == mozilla::widget::sDefaultBrowserMsgId) {
         // Metro widget's system shutdown message
         deferred = new DeferredSendMessage(hwnd, uMsg, wParam, lParam);
 #endif
-      } else {
-        // Unknown messages only
-#ifdef DEBUG
-        nsAutoCString log("Received \"nonqueued\" message ");
-        log.AppendInt(uMsg);
-        log.AppendLiteral(" during a synchronous IPC message for window ");
-        log.AppendInt((int64_t)hwnd);
-
-        wchar_t className[256] = { 0 };
-        if (GetClassNameW(hwnd, className, sizeof(className) - 1) > 0) {
-          log.AppendLiteral(" (\"");
-          log.Append(NS_ConvertUTF16toUTF8((char16_t*)className));
-          log.AppendLiteral("\")");
-        }
-
-        log.AppendLiteral(", sending it to DefWindowProc instead of the normal "
-                          "window procedure.");
-        NS_ERROR(log.get());
-#endif
-        return DefWindowProc(hwnd, uMsg, wParam, lParam);
       }
     }
   }
 
-  NS_ASSERTION(deferred, "Must have a message here!");
+  // No deferred message was created and we land here, this is an
+  // unhandled message.
+  if (!deferred) {
+    DumpNeuteredMessage(hwnd, uMsg);
+    return DefWindowProc(hwnd, uMsg, wParam, lParam);
+  }
 
   // Create the deferred message array if it doesn't exist already.
   if (!gDeferredMessages) {
     gDeferredMessages = new nsTArray<nsAutoPtr<DeferredMessage> >(20);
     NS_ASSERTION(gDeferredMessages, "Out of memory!");
   }
 
   // Save for later. The array takes ownership of |deferred|.
@@ -395,23 +423,25 @@ WindowIsDeferredWindow(HWND hWnd)
 
   // Plugin windows that can trigger ipc calls in child:
   // 'ShockwaveFlashFullScreen' - flash fullscreen window
   // 'QTNSHIDDEN' - QuickTime
   // 'AGFullScreenWinClass' - silverlight fullscreen window
   if (className.EqualsLiteral("ShockwaveFlashFullScreen") ||
       className.EqualsLiteral("QTNSHIDDEN") ||
       className.EqualsLiteral("AGFullScreenWinClass")) {
+    SetPropW(hWnd, k3rdPartyWindowProp, (HANDLE)1);
     return true;
   }
 
   // Google Earth bridging msg window between the plugin instance and a separate
   // earth process. The earth process can trigger a plugin incall on the browser
   // at any time, which is badness if the instance is already making an incall.
   if (className.EqualsLiteral("__geplugin_bridge_window__")) {
+    SetPropW(hWnd, k3rdPartyWindowProp, (HANDLE)1);
     return true;
   }
 
   // nsNativeAppSupport makes a window like "FirefoxMessageWindow" based on the
   // toolkit app's name. It's pretty expensive to calculate this so we only try
   // once.
   if (gAppMessageWindowNameLength == 0) {
     nsCOMPtr<nsIXULAppInfo> appInfo =
@@ -468,17 +498,18 @@ NeuterWindowProcedure(HWND hWnd)
 
   NS_ASSERTION(currentWndProc != (LONG_PTR)NeuteredWindowProc,
                "This shouldn't be possible!");
 
   if (!SetProp(hWnd, kOldWndProcProp, (HANDLE)currentWndProc)) {
     // Cleanup
     NS_WARNING("SetProp failed!");
     SetWindowLongPtr(hWnd, GWLP_WNDPROC, currentWndProc);
-    RemoveProp(hWnd, kOldWndProcProp);
+    RemovePropW(hWnd, kOldWndProcProp);
+    RemovePropW(hWnd, k3rdPartyWindowProp);
     return false;
   }
 
   return true;
 }
 
 void
 RestoreWindowProcedure(HWND hWnd)
@@ -490,17 +521,18 @@ RestoreWindowProcedure(HWND hWnd)
     NS_ASSERTION(oldWndProc != (LONG_PTR)NeuteredWindowProc,
                  "This shouldn't be possible!");
 
     DebugOnly<LONG_PTR> currentWndProc =
       SetWindowLongPtr(hWnd, GWLP_WNDPROC, oldWndProc);
     NS_ASSERTION(currentWndProc == (LONG_PTR)NeuteredWindowProc,
                  "This should never be switched out from under us!");
   }
-  RemoveProp(hWnd, kOldWndProcProp);
+  RemovePropW(hWnd, kOldWndProcProp);
+  RemovePropW(hWnd, k3rdPartyWindowProp);
 }
 
 LRESULT CALLBACK
 CallWindowProcedureHook(int nCode,
                         WPARAM wParam,
                         LPARAM lParam)
 {
   if (nCode >= 0) {
--- a/widget/windows/winrt/MetroWidget.cpp
+++ b/widget/windows/winrt/MetroWidget.cpp
@@ -72,17 +72,17 @@ const char16_t* kMetroSubclassThisProp =
 HWND MetroWidget::sICoreHwnd = nullptr;
 
 namespace mozilla {
 namespace widget {
 UINT sDefaultBrowserMsgId = RegisterWindowMessageW(L"DefaultBrowserClosing");
 } }
 
 // WM_GETOBJECT id pulled from uia headers
-#define UiaRootObjectId -25
+#define MOZOBJID_UIAROOT -25
 
 namespace mozilla {
 namespace widget {
 namespace winrt {
 extern ComPtr<MetroApp> sMetroApp;
 extern ComPtr<IUIABridge> gProviderRoot;
 } } }
 
@@ -887,17 +887,17 @@ MetroWidget::WindowProcedure(HWND aWnd, 
       DWORD dwObjId = (LPARAM)(DWORD) aLParam;
       // Passing this to CallWindowProc can result in a failure due to a timing issue
       // in winrt core window server code, so we call it directly here. Also, it's not
       // clear Windows::UI::Core::WindowServer::OnAutomationProviderRequestedEvent is
       // compatible with metro enabled desktop browsers, it makes an initial call to
       // UiaReturnRawElementProvider passing the return result from FrameworkView
       // OnAutomationProviderRequested as the hwnd (me scratches head) which results in
       // GetLastError always being set to invalid handle (6) after CallWindowProc returns.
-      if (dwObjId == UiaRootObjectId && gProviderRoot) {
+      if (dwObjId == MOZOBJID_UIAROOT && gProviderRoot) {
         ComPtr<IRawElementProviderSimple> simple;
         gProviderRoot.As(&simple);
         if (simple) {
           LRESULT res = UiaReturnRawElementProvider(aWnd, aWParam, aLParam, simple.Get());
           if (res) {
             return res;
           }
           NS_ASSERTION(res, "UiaReturnRawElementProvider failed!");