Bug 1133351 - Part 1: Make Windows IPC play nicely with COM STA marshaling. r=bsmedberg, a=lizzard
authorAaron Klotz <aklotz@mozilla.com>
Wed, 25 Mar 2015 20:54:23 -0700
changeset 266179 8f1677195e6f
parent 266178 bb5ac2094352
child 266180 59793d7e1b7e
push id4776
push userryanvm@gmail.com
push date2015-06-03 20:24 +0000
treeherdermozilla-beta@59793d7e1b7e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbsmedberg, lizzard
bugs1133351
milestone39.0
Bug 1133351 - Part 1: Make Windows IPC play nicely with COM STA marshaling. r=bsmedberg, a=lizzard
dom/plugins/ipc/COMMessageFilter.cpp
dom/plugins/ipc/COMMessageFilter.h
dom/plugins/ipc/PluginModuleChild.cpp
dom/plugins/ipc/moz.build
ipc/glue/WindowsMessageLoop.cpp
widget/windows/nsWindow.cpp
deleted file mode 100644
--- a/dom/plugins/ipc/COMMessageFilter.cpp
+++ /dev/null
@@ -1,84 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "base/basictypes.h"
-
-#include "COMMessageFilter.h"
-#include "base/message_loop.h"
-#include "mozilla/plugins/PluginModuleChild.h"
-
-#include <stdio.h>
-
-namespace mozilla {
-namespace plugins {
-
-HRESULT
-COMMessageFilter::QueryInterface(REFIID riid, void** ppv)
-{
-  if (riid == IID_IUnknown || riid == IID_IMessageFilter) {
-    *ppv = static_cast<IMessageFilter*>(this);
-    AddRef();
-    return S_OK;
-  }
-  return E_NOINTERFACE;
-}
-
-DWORD COMMessageFilter::AddRef()
-{
-  ++mRefCnt;
-  return mRefCnt;
-}
-
-DWORD COMMessageFilter::Release()
-{
-  DWORD r = --mRefCnt;
-  if (0 == r)
-    delete this;
-  return r;
-}
-
-DWORD 
-COMMessageFilter::HandleInComingCall(DWORD dwCallType,
-				     HTASK htaskCaller,
-				     DWORD dwTickCount,
-				     LPINTERFACEINFO lpInterfaceInfo)
-{
-  if (mPreviousFilter)
-    return mPreviousFilter->HandleInComingCall(dwCallType, htaskCaller,
-					       dwTickCount, lpInterfaceInfo);
-  return SERVERCALL_ISHANDLED;
-}
-
-DWORD
-COMMessageFilter::RetryRejectedCall(HTASK htaskCallee,
-				    DWORD dwTickCount,
-				    DWORD dwRejectType)
-{
-  if (mPreviousFilter)
-    return mPreviousFilter->RetryRejectedCall(htaskCallee, dwTickCount,
-					      dwRejectType);
-  return -1;
-}
-
-DWORD
-COMMessageFilter::MessagePending(HTASK htaskCallee,
-				 DWORD dwTickCount,
-				 DWORD dwPendingType)
-{
-  mPlugin->FlushPendingInterruptQueue();
-  if (mPreviousFilter)
-    return mPreviousFilter->MessagePending(htaskCallee, dwTickCount,
-					   dwPendingType);
-  return PENDINGMSG_WAITNOPROCESS;
-}
-
-void
-COMMessageFilter::Initialize(PluginModuleChild* module)
-{
-  nsRefPtr<COMMessageFilter> f = new COMMessageFilter(module);
-  ::CoRegisterMessageFilter(f, getter_AddRefs(f->mPreviousFilter));
-}
-
-} // namespace plugins
-} // namespace mozilla
deleted file mode 100644
--- a/dom/plugins/ipc/COMMessageFilter.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef mozilla_plugins_COMMessageFilter_h
-#define mozilla_plugins_COMMessageFilter_h
-
-#include <objidl.h>
-#include "nsISupportsImpl.h"
-#include "nsAutoPtr.h"
-
-namespace mozilla {
-namespace plugins {
-
-class PluginModuleChild;
-
-class COMMessageFilter final : public IMessageFilter
-{
-public:
-  static void Initialize(PluginModuleChild* plugin);
-
-  COMMessageFilter(PluginModuleChild* plugin)
-    : mPlugin(plugin)
-  { }
-
-  HRESULT WINAPI QueryInterface(REFIID riid, void** ppv);
-  DWORD WINAPI AddRef();
-  DWORD WINAPI Release();
-
-  DWORD WINAPI HandleInComingCall(DWORD dwCallType,
-                                  HTASK htaskCaller,
-                                  DWORD dwTickCount,
-                                  LPINTERFACEINFO lpInterfaceInfo);
-  DWORD WINAPI RetryRejectedCall(HTASK htaskCallee,
-                                 DWORD dwTickCount,
-                                 DWORD dwRejectType);
-  DWORD WINAPI MessagePending(HTASK htaskCallee,
-                              DWORD dwTickCount,
-                              DWORD dwPendingType);
-
-private:
-  nsAutoRefCnt mRefCnt;
-  PluginModuleChild* mPlugin;
-  nsRefPtr<IMessageFilter> mPreviousFilter;
-};
-
-} // namespace plugins
-} // namespace mozilla
-
-#endif // COMMessageFilter_h
--- a/dom/plugins/ipc/PluginModuleChild.cpp
+++ b/dom/plugins/ipc/PluginModuleChild.cpp
@@ -37,17 +37,16 @@
 #include "mozilla/plugins/StreamNotifyChild.h"
 #include "mozilla/plugins/BrowserStreamChild.h"
 #include "mozilla/plugins/PluginStreamChild.h"
 #include "mozilla/dom/CrashReporterChild.h"
 
 #include "nsNPAPIPlugin.h"
 
 #ifdef XP_WIN
-#include "COMMessageFilter.h"
 #include "nsWindowsDllInterceptor.h"
 #include "mozilla/widget/AudioSession.h"
 #endif
 
 #ifdef MOZ_WIDGET_COCOA
 #include "PluginInterposeOSX.h"
 #include "PluginUtilsOSX.h"
 #endif
@@ -259,20 +258,16 @@ PluginModuleChild::RecvDisableFlashProte
 }
 
 bool
 PluginModuleChild::InitForChrome(const std::string& aPluginFilename,
                                  base::ProcessHandle aParentProcessHandle,
                                  MessageLoop* aIOLoop,
                                  IPC::Channel* aChannel)
 {
-#ifdef XP_WIN
-    COMMessageFilter::Initialize(this);
-#endif
-
     NS_ASSERTION(aChannel, "need a channel");
 
     if (!InitGraphics())
         return false;
 
     mPluginFilename = aPluginFilename.c_str();
     nsCOMPtr<nsIFile> localFile;
     NS_NewLocalFile(NS_ConvertUTF8toUTF16(mPluginFilename),
--- a/dom/plugins/ipc/moz.build
+++ b/dom/plugins/ipc/moz.build
@@ -45,17 +45,16 @@ EXPORTS.mozilla.plugins += [
     'TaskFactory.h',
 ]
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     EXPORTS.mozilla.plugins += [
         'PluginSurfaceParent.h',
     ]
     UNIFIED_SOURCES += [
-        'COMMessageFilter.cpp',
         'PluginHangUIParent.cpp',
         'PluginSurfaceParent.cpp',
     ]
     SOURCES += [
         'MiniShmParent.cpp', # Issues with CreateEvent
     ]
     DEFINES['MOZ_HANGUI_PROCESS_NAME'] = '"plugin-hang-ui%s"' % CONFIG['BIN_SUFFIX']
     LOCAL_INCLUDES += [
--- a/ipc/glue/WindowsMessageLoop.cpp
+++ b/ipc/glue/WindowsMessageLoop.cpp
@@ -99,16 +99,17 @@ nsTArray<HWND>* gNeuteredWindows = nullp
 
 typedef nsTArray<nsAutoPtr<DeferredMessage> > DeferredMessageArray;
 DeferredMessageArray* gDeferredMessages = nullptr;
 
 HHOOK gDeferredGetMsgHook = nullptr;
 HHOOK gDeferredCallWndProcHook = nullptr;
 
 DWORD gUIThreadId = 0;
+HWND gCOMWindow = 0;
 
 // WM_GETOBJECT id pulled from uia headers
 #define MOZOBJID_UIAROOT -25
 
 LRESULT CALLBACK
 DeferredMessageHook(int nCode,
                     WPARAM wParam,
                     LPARAM lParam)
@@ -335,23 +336,25 @@ ProcessOrDeferMessage(HWND hwnd,
       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);
+        if ((objId == OBJID_CLIENT || objId == MOZOBJID_UIAROOT)) {
+          WNDPROC oldWndProc = (WNDPROC)GetProp(hwnd, kOldWndProcProp);
+          if (oldWndProc) {
+            return CallWindowProcW(oldWndProc, hwnd, uMsg, wParam, lParam);
+          }
         }
       }
-      break;
-    }
+      return DefWindowProc(hwnd, uMsg, wParam, lParam);
+   }
 #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);
@@ -642,17 +645,20 @@ namespace windows {
 
 void
 InitUIThread()
 {
   // If we aren't setup before a call to NotifyWorkerThread, we'll hang
   // on startup.
   if (!gUIThreadId) {
     gUIThreadId = GetCurrentThreadId();
+
+    CoInitialize(nullptr);
   }
+
   MOZ_ASSERT(gUIThreadId);
   MOZ_ASSERT(gUIThreadId == GetCurrentThreadId(),
              "Called InitUIThread multiple times on different threads!");
 }
 
 } // namespace windows
 } // namespace ipc
 } // namespace mozilla
@@ -799,16 +805,31 @@ MessageChannel::SpinInternalEventLoop()
 
 static inline bool
 IsTimeoutExpired(PRIntervalTime aStart, PRIntervalTime aTimeout)
 {
   return (aTimeout != PR_INTERVAL_NO_TIMEOUT) &&
     (aTimeout <= (PR_IntervalNow() - aStart));
 }
 
+HWND
+FindCOMWindow()
+{
+  MOZ_ASSERT(gUIThreadId);
+
+  HWND last = 0;
+  while ((last = FindWindowExW(HWND_MESSAGE, last, L"OleMainThreadWndClass", NULL))) {
+    if (GetWindowThreadProcessId(last, NULL) == gUIThreadId) {
+      return last;
+    }
+  }
+
+  return (HWND)0;
+}
+
 bool
 MessageChannel::WaitForSyncNotify()
 {
   mMonitor->AssertCurrentThreadOwns();
 
   MOZ_ASSERT(gUIThreadId, "InitUIThread was not called!");
 
   // Use a blocking wait if this channel does not require
@@ -839,16 +860,22 @@ MessageChannel::WaitForSyncNotify()
 
   NS_ASSERTION(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION,
                "Shouldn't be here for channels that don't use message deferral!");
   NS_ASSERTION(mTopFrame && !mTopFrame->mInterrupt,
                "Top frame is not a sync frame!");
 
   MonitorAutoUnlock unlock(*mMonitor);
 
+  MOZ_ASSERT_IF(gCOMWindow, FindCOMWindow() == gCOMWindow);
+
+  if (!gCOMWindow) {
+    gCOMWindow = FindCOMWindow();
+  }
+
   bool timedout = false;
 
   UINT_PTR timerId = 0;
   TimeoutData timeoutData = { 0 };
 
   if (mTimeoutMs != kNoTimeout) {
     InitTimeoutData(&timeoutData, mTimeoutMs);
 
@@ -907,21 +934,30 @@ MessageChannel::WaitForSyncNotify()
       // PeekMessage will return false if there are no "queued" messages, but it
       // will run all "nonqueued" messages before returning. So if PeekMessage
       // returns false and there are no "nonqueued" messages that were run then
       // we know that the message we woke for was intended for a window on
       // another thread.
       bool haveSentMessagesPending =
         (HIWORD(GetQueueStatus(QS_SENDMESSAGE)) & QS_SENDMESSAGE) != 0;
 
-      // This PeekMessage call will actually process all "nonqueued" messages
-      // that are pending before returning. If we have "nonqueued" messages
-      // pending then we should have switched out all the window procedures
-      // above. In that case this PeekMessage call won't actually cause any
-      // mozilla code (or plugin code) to run.
+      // Either of the PeekMessage calls below will actually process all
+      // "nonqueued" messages that are pending before returning. If we have
+      // "nonqueued" messages pending then we should have switched out all the
+      // window procedures above. In that case this PeekMessage call won't
+      // actually cause any mozilla code (or plugin code) to run.
+
+      // We have to manually pump all COM messages *after* looking at the queue
+      // queue status but before yielding our thread below.
+      if (gCOMWindow) {
+        if (PeekMessageW(&msg, gCOMWindow, 0, 0, PM_REMOVE)) {
+          TranslateMessage(&msg);
+          ::DispatchMessageW(&msg);
+        }
+      }
 
       // If the following PeekMessage call fails to return a message for us (and
       // returns false) and we didn't run any "nonqueued" messages then we must
       // have woken up for a message designated for a window in another thread.
       // If we loop immediately then we could enter a tight loop, so we'll give
       // up our time slice here to let the child process its message.
       if (!PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE) &&
           !haveSentMessagesPending) {
@@ -973,16 +1009,22 @@ MessageChannel::WaitForInterruptNotify()
 
   NS_ASSERTION(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION,
                "Shouldn't be here for channels that don't use message deferral!");
   NS_ASSERTION(mTopFrame && mTopFrame->mInterrupt,
                "Top frame is not a sync frame!");
 
   MonitorAutoUnlock unlock(*mMonitor);
 
+  MOZ_ASSERT_IF(gCOMWindow, FindCOMWindow() == gCOMWindow);
+
+  if (!gCOMWindow) {
+    gCOMWindow = FindCOMWindow();
+  }
+
   bool timedout = false;
 
   UINT_PTR timerId = 0;
   TimeoutData timeoutData = { 0 };
 
   // windowHook is used as a flag variable for the loop below: if it is set
   // and we start to spin a nested event loop, we need to clear the hook and
   // process deferred/pending messages.
@@ -1062,16 +1104,24 @@ MessageChannel::WaitForInterruptNotify()
       timedout = true;
       break;
     }
 
     // See MessageChannel's WaitFor*Notify for details.
     bool haveSentMessagesPending =
       (HIWORD(GetQueueStatus(QS_SENDMESSAGE)) & QS_SENDMESSAGE) != 0;
 
+    // Run all COM messages *after* looking at the queue status.
+    if (gCOMWindow) {
+        if (PeekMessageW(&msg, gCOMWindow, 0, 0, PM_REMOVE)) {
+            TranslateMessage(&msg);
+            ::DispatchMessageW(&msg);
+        }
+    }
+
     // PeekMessage markes the messages as "old" so that they don't wake up
     // MsgWaitForMultipleObjects every time.
     if (!PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE) &&
         !haveSentMessagesPending) {
       // Message was for child, we should wait a bit.
       SwitchToThread();
     }
   }
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -4142,18 +4142,18 @@ nsWindow::IsAsyncResponseEvent(UINT aMsg
 #endif
 
   return false;
 }
 
 void
 nsWindow::IPCWindowProcHandler(UINT& msg, WPARAM& wParam, LPARAM& lParam)
 {
-  NS_ASSERTION(!mozilla::ipc::MessageChannel::IsPumpingMessages(),
-               "Failed to prevent a nonqueued message from running!");
+  MOZ_ASSERT_IF(msg != WM_GETOBJECT,
+                !mozilla::ipc::MessageChannel::IsPumpingMessages());
 
   // Modal UI being displayed in windowless plugins.
   if (mozilla::ipc::MessageChannel::IsSpinLoopActive() &&
       (InSendMessageEx(nullptr) & (ISMEX_REPLIED|ISMEX_SEND)) == ISMEX_SEND) {
     LRESULT res;
     if (IsAsyncResponseEvent(msg, res)) {
       ReplyMessage(res);
     }