Bug 1213567 - Prevent neutering from occurring during CreateWindow(Ex) calls; r=jimm, a=sylvestre
☠☠ backed out by f2ec86a8b1a8 ☠ ☠
authorAaron Klotz <aklotz@mozilla.com>
Sat, 17 Oct 2015 13:28:53 -0600
changeset 289607 aac4b4aaa5f4
parent 289606 e1261e250e85
child 289608 f2ec86a8b1a8
push id5216
push usercbook@mozilla.com
push date2015-10-22 07:53 +0000
treeherdermozilla-beta@aac4b4aaa5f4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimm, sylvestre
bugs1213567
milestone42.0
Bug 1213567 - Prevent neutering from occurring during CreateWindow(Ex) calls; r=jimm, a=sylvestre
ipc/glue/Neutering.h
ipc/glue/WindowsMessageLoop.cpp
--- a/ipc/glue/Neutering.h
+++ b/ipc/glue/Neutering.h
@@ -52,13 +52,28 @@ public:
   DeneuteredWindowRegion(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM);
   ~DeneuteredWindowRegion();
 
 private:
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
   bool mReneuter;
 };
 
+class MOZ_RAII SuppressedNeuteringRegion
+{
+public:
+  SuppressedNeuteringRegion(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM);
+  ~SuppressedNeuteringRegion();
+
+  static inline bool IsNeuteringSuppressed() { return sSuppressNeutering; }
+
+private:
+  MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+  bool mReenable;
+
+  static bool sSuppressNeutering;
+};
+
 } // namespace ipc
 } // namespace mozilla
 
 #endif // mozilla_ipc_Neutering_h
 
--- a/ipc/glue/WindowsMessageLoop.cpp
+++ b/ipc/glue/WindowsMessageLoop.cpp
@@ -10,16 +10,17 @@
 #include "WindowsMessageLoop.h"
 #include "Neutering.h"
 #include "MessageChannel.h"
 
 #include "nsAutoPtr.h"
 #include "nsServiceManagerUtils.h"
 #include "nsString.h"
 #include "nsIXULAppInfo.h"
+#include "nsWindowsDllInterceptor.h"
 #include "WinUtils.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/PaintTracker.h"
 
 using namespace mozilla;
 using namespace mozilla::ipc;
 using namespace mozilla::ipc::windows;
@@ -431,16 +432,103 @@ ProcessOrDeferMessage(HWND hwnd,
     NS_ASSERTION(gDeferredMessages, "Out of memory!");
   }
 
   // Save for later. The array takes ownership of |deferred|.
   gDeferredMessages->AppendElement(deferred);
   return res;
 }
 
+/*
+ * It is bad to subclass a window when neutering is active because you'll end
+ * up subclassing the *neutered* window procedure instead of the real window
+ * procedure. Since CreateWindow* fires WM_CREATE (and could thus trigger
+ * neutering), we intercept these calls and suppress neutering for the duration
+ * of the call. This ensures that any subsequent subclassing replaces the
+ * correct window procedure.
+ */
+WindowsDllInterceptor sUser32Interceptor;
+typedef HWND (WINAPI *CreateWindowExWPtr)(DWORD,LPCWSTR,LPCWSTR,DWORD,int,int,int,int,HWND,HMENU,HINSTANCE,LPVOID);
+typedef HWND (WINAPI *CreateWindowExAPtr)(DWORD,LPCSTR,LPCSTR,DWORD,int,int,int,int,HWND,HMENU,HINSTANCE,LPVOID);
+typedef HWND (WINAPI *CreateWindowWPtr)(LPCWSTR,LPCWSTR,DWORD,int,int,int,int,HWND,HMENU,HINSTANCE,LPVOID);
+typedef HWND (WINAPI *CreateWindowAPtr)(LPCSTR,LPCSTR,DWORD,int,int,int,int,HWND,HMENU,HINSTANCE,LPVOID);
+
+CreateWindowExWPtr sCreateWindowExWStub = nullptr;
+CreateWindowExAPtr sCreateWindowExAStub = nullptr;
+CreateWindowWPtr sCreateWindowWStub = nullptr;
+CreateWindowAPtr sCreateWindowAStub = nullptr;
+
+HWND WINAPI
+CreateWindowExWHook(DWORD aExStyle, LPCWSTR aClassName, LPCWSTR aWindowName,
+                    DWORD aStyle, int aX, int aY, int aWidth, int aHeight,
+                    HWND aParent, HMENU aMenu, HINSTANCE aInstance,
+                    LPVOID aParam)
+{
+  SuppressedNeuteringRegion doNotNeuterThisWindowYet;
+  return sCreateWindowExWStub(aExStyle, aClassName, aWindowName, aStyle, aX, aY,
+                              aWidth, aHeight, aParent, aMenu, aInstance, aParam);
+}
+
+HWND WINAPI
+CreateWindowExAHook(DWORD aExStyle, LPCSTR aClassName, LPCSTR aWindowName,
+                    DWORD aStyle, int aX, int aY, int aWidth, int aHeight,
+                    HWND aParent, HMENU aMenu, HINSTANCE aInstance,
+                    LPVOID aParam)
+{
+  SuppressedNeuteringRegion doNotNeuterThisWindowYet;
+  return sCreateWindowExAStub(aExStyle, aClassName, aWindowName, aStyle, aX, aY,
+                              aWidth, aHeight, aParent, aMenu, aInstance, aParam);
+}
+
+HWND WINAPI
+CreateWindowWHook(LPCWSTR aClassName, LPCWSTR aWindowName, DWORD aStyle, int aX,
+                  int aY, int aWidth, int aHeight, HWND aParent, HMENU aMenu,
+                  HINSTANCE aInstance, LPVOID aParam)
+{
+  SuppressedNeuteringRegion doNotNeuterThisWindowYet;
+  return sCreateWindowWStub(aClassName, aWindowName, aStyle, aX, aY, aWidth,
+                            aHeight, aParent, aMenu, aInstance, aParam);
+}
+
+HWND WINAPI
+CreateWindowAHook(LPCSTR aClassName, LPCSTR aWindowName, DWORD aStyle, int aX,
+                  int aY, int aWidth, int aHeight, HWND aParent, HMENU aMenu,
+                  HINSTANCE aInstance, LPVOID aParam)
+{
+  SuppressedNeuteringRegion doNotNeuterThisWindowYet;
+  return sCreateWindowAStub(aClassName, aWindowName, aStyle, aX, aY, aWidth,
+                            aHeight, aParent, aMenu, aInstance, aParam);
+}
+
+void
+InitCreateWindowHook()
+{
+  sUser32Interceptor.Init("user32.dll");
+  if (!sCreateWindowExWStub) {
+    sUser32Interceptor.AddHook("CreateWindowExW",
+                               reinterpret_cast<intptr_t>(CreateWindowExWHook),
+                               (void**) &sCreateWindowExWStub);
+  }
+  if (!sCreateWindowExAStub) {
+    sUser32Interceptor.AddHook("CreateWindowExA",
+                               reinterpret_cast<intptr_t>(CreateWindowExAHook),
+                               (void**) &sCreateWindowExAStub);
+  }
+  if (!sCreateWindowWStub) {
+    sUser32Interceptor.AddHook("CreateWindowW",
+                               reinterpret_cast<intptr_t>(CreateWindowWHook),
+                               (void**) &sCreateWindowWStub);
+  }
+  if (!sCreateWindowAStub) {
+    sUser32Interceptor.AddHook("CreateWindowA",
+                               reinterpret_cast<intptr_t>(CreateWindowAHook),
+                               (void**) &sCreateWindowAStub);
+  }
+}
+
 } // namespace
 
 // We need the pointer value of this in PluginInstanceChild.
 LRESULT CALLBACK
 NeuteredWindowProc(HWND hwnd,
                    UINT uMsg,
                    WPARAM wParam,
                    LPARAM lParam)
@@ -605,17 +693,19 @@ CallWindowProcedureHook(int nCode,
                         WPARAM wParam,
                         LPARAM lParam)
 {
   if (nCode >= 0) {
     NS_ASSERTION(gNeuteredWindows, "This should never be null!");
 
     HWND hWnd = reinterpret_cast<CWPSTRUCT*>(lParam)->hwnd;
 
-    if (!gNeuteredWindows->Contains(hWnd) && NeuterWindowProcedure(hWnd)) {
+    if (!gNeuteredWindows->Contains(hWnd) &&
+        !SuppressedNeuteringRegion::IsNeuteringSuppressed() &&
+        NeuterWindowProcedure(hWnd)) {
       if (!gNeuteredWindows->AppendElement(hWnd)) {
         NS_ERROR("Out of memory!");
         RestoreWindowProcedure(hWnd);
       }
     }
   }
   return CallNextHookEx(nullptr, nCode, wParam, lParam);
 }
@@ -705,16 +795,18 @@ InitUIThread()
                                     NULL, &WinEventHook, GetCurrentProcessId(),
                                     gUIThreadId, WINEVENT_OUTOFCONTEXT);
 
     // We need to execute this after setting the hook in case the OLE window
     // already existed.
     gCOMWindow = FindCOMWindow();
   }
   MOZ_ASSERT(gWinEventHook);
+
+  InitCreateWindowHook();
 }
 
 } // namespace windows
 } // namespace ipc
 } // namespace mozilla
 
 // See SpinInternalEventLoop below
 MessageChannel::SyncStackFrame::SyncStackFrame(MessageChannel* channel, bool interrupt)
@@ -938,16 +1030,36 @@ DeneuteredWindowRegion::DeneuteredWindow
 
 DeneuteredWindowRegion::~DeneuteredWindowRegion()
 {
   if (mReneuter) {
     StartNeutering();
   }
 }
 
+SuppressedNeuteringRegion::SuppressedNeuteringRegion(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL)
+  : mReenable(::gUIThreadId == ::GetCurrentThreadId() && ::gWindowHook)
+{
+  MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+  if (mReenable) {
+    MOZ_ASSERT(!sSuppressNeutering);
+    sSuppressNeutering = true;
+  }
+}
+
+SuppressedNeuteringRegion::~SuppressedNeuteringRegion()
+{
+  if (mReenable) {
+    MOZ_ASSERT(sSuppressNeutering);
+    sSuppressNeutering = false;
+  }
+}
+
+bool SuppressedNeuteringRegion::sSuppressNeutering = false;
+
 bool
 MessageChannel::WaitForSyncNotify()
 {
   mMonitor->AssertCurrentThreadOwns();
 
   MOZ_ASSERT(gUIThreadId, "InitUIThread was not called!");
 
   // Use a blocking wait if this channel does not require
@@ -992,16 +1104,18 @@ MessageChannel::WaitForSyncNotify()
     InitTimeoutData(&timeoutData, mTimeoutMs);
 
     // We only do this to ensure that we won't get stuck in
     // MsgWaitForMultipleObjects below.
     timerId = SetTimer(nullptr, 0, mTimeoutMs, nullptr);
     NS_ASSERTION(timerId, "SetTimer failed!");
   }
 
+  NeuteredWindowRegion neuteredRgn(true);
+
   {
     while (1) {
       MSG msg = { 0 };
       // Don't get wrapped up in here if the child connection dies.
       {
         MonitorAutoLock lock(*mMonitor);
         if (!Connected()) {
           break;