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 205226 69f12c69463081c9c6b70c7a17d54776888234f3
parent 205225 33ebbb506ea436eed43613757b83ccae359fa834
child 205227 7e3dbe00b1e7f23250fc109717ff203a899780f1
push id3741
push userasasaki@mozilla.com
push dateMon, 21 Jul 2014 20:25:18 +0000
treeherdermozilla-beta@4d6f46f5af68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbent
bugs1014673
milestone32.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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!");