diff --git a/ipc/glue/MessageChannel.cpp b/ipc/glue/MessageChannel.cpp index 93cef0f9d385..48b574a7475d 100644 --- a/ipc/glue/MessageChannel.cpp +++ b/ipc/glue/MessageChannel.cpp @@ -853,7 +853,6 @@ MessageChannel::Send(Message* aMsg, Message* aReply) #ifdef OS_WIN SyncStackFrame frame(this, false); - NeuteredWindowRegion neuteredRgn(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION); #endif CxxStackFrame f(*this, OUT_MESSAGE, msg); @@ -995,7 +994,6 @@ MessageChannel::Call(Message* aMsg, Message* aReply) #ifdef OS_WIN SyncStackFrame frame(this, true); - NeuteredWindowRegion neuteredRgn(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION); #endif // This must come before MonitorAutoLock, as its destructor acquires the @@ -1034,12 +1032,6 @@ MessageChannel::Call(Message* aMsg, Message* aReply) return false; } -#ifdef OS_WIN - /* We should pump messages at this point to ensure that the IPC peer - does not become deadlocked on a pending inter-thread SendMessage() */ - neuteredRgn.PumpOnce(); -#endif - // Now might be the time to process a message deferred because of race // resolution. MaybeUndeferIncall(); @@ -1156,7 +1148,6 @@ MessageChannel::WaitForIncomingMessage() { #ifdef OS_WIN SyncStackFrame frame(this, true); - NeuteredWindowRegion neuteredRgn(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION); #endif { // Scope for lock diff --git a/ipc/glue/MessageChannel.h b/ipc/glue/MessageChannel.h index 243446debba4..519525d6e6b3 100644 --- a/ipc/glue/MessageChannel.h +++ b/ipc/glue/MessageChannel.h @@ -15,9 +15,6 @@ #include "mozilla/Monitor.h" #include "mozilla/Vector.h" #include "mozilla/WeakPtr.h" -#if defined(OS_WIN) -#include "mozilla/ipc/Neutering.h" -#endif // defined(OS_WIN) #include "mozilla/ipc/Transport.h" #include "MessageLink.h" #include "nsAutoPtr.h" diff --git a/ipc/glue/Neutering.h b/ipc/glue/Neutering.h deleted file mode 100644 index ea1a2bf5e9ab..000000000000 --- a/ipc/glue/Neutering.h +++ /dev/null @@ -1,64 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* 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_ipc_Neutering_h -#define mozilla_ipc_Neutering_h - -#include "mozilla/GuardObjects.h" - -/** - * This header declares RAII wrappers for Window neutering. See - * WindowsMessageLoop.cpp for more details. - */ - -namespace mozilla { -namespace ipc { - -/** - * This class is a RAII wrapper around Window neutering. As long as a - * NeuteredWindowRegion object is instantiated, Win32 windows belonging to the - * current thread will be neutered. It is safe to nest multiple instances of - * this class. - */ -class MOZ_STACK_CLASS NeuteredWindowRegion -{ -public: - explicit NeuteredWindowRegion(bool aDoNeuter MOZ_GUARD_OBJECT_NOTIFIER_PARAM); - ~NeuteredWindowRegion(); - - /** - * This function clears any backlog of nonqueued messages that are pending for - * the current thread. - */ - void PumpOnce(); - -private: - MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER - bool mNeuteredByThis; -}; - -/** - * This class is analagous to MutexAutoUnlock for Mutex; it is an RAII class - * that is to be instantiated within a NeuteredWindowRegion, thus temporarily - * disabling neutering for the remainder of its enclosing block. - * @see NeuteredWindowRegion - */ -class MOZ_STACK_CLASS DeneuteredWindowRegion -{ -public: - DeneuteredWindowRegion(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM); - ~DeneuteredWindowRegion(); - -private: - MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER - bool mReneuter; -}; - -} // namespace ipc -} // namespace mozilla - -#endif // mozilla_ipc_Neutering_h - diff --git a/ipc/glue/WindowsMessageLoop.cpp b/ipc/glue/WindowsMessageLoop.cpp index 072455973860..3f7afaaa1865 100644 --- a/ipc/glue/WindowsMessageLoop.cpp +++ b/ipc/glue/WindowsMessageLoop.cpp @@ -8,7 +8,6 @@ #include "mozilla/DebugOnly.h" #include "WindowsMessageLoop.h" -#include "Neutering.h" #include "MessageChannel.h" #include "nsAutoPtr.h" @@ -863,85 +862,6 @@ IsTimeoutExpired(PRIntervalTime aStart, PRIntervalTime aTimeout) (aTimeout <= (PR_IntervalNow() - aStart)); } -static HHOOK gWindowHook; - -static inline void -StartNeutering() -{ - MOZ_ASSERT(gUIThreadId); - MOZ_ASSERT(!gWindowHook); - NS_ASSERTION(!MessageChannel::IsPumpingMessages(), - "Shouldn't be pumping already!"); - MessageChannel::SetIsPumpingMessages(true); - gWindowHook = ::SetWindowsHookEx(WH_CALLWNDPROC, CallWindowProcedureHook, - nullptr, gUIThreadId); - NS_ASSERTION(gWindowHook, "Failed to set hook!"); -} - -static void -StopNeutering() -{ - MOZ_ASSERT(MessageChannel::IsPumpingMessages()); - ::UnhookWindowsHookEx(gWindowHook); - gWindowHook = NULL; - ::UnhookNeuteredWindows(); - // Before returning we need to set a hook to run any deferred messages that - // we received during the IPC call. The hook will unset itself as soon as - // someone else calls GetMessage, PeekMessage, or runs code that generates - // a "nonqueued" message. - ::ScheduleDeferredMessageRun(); - MessageChannel::SetIsPumpingMessages(false); -} - -NeuteredWindowRegion::NeuteredWindowRegion(bool aDoNeuter MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL) - : mNeuteredByThis(!gWindowHook) -{ - MOZ_GUARD_OBJECT_NOTIFIER_INIT; - if (aDoNeuter && mNeuteredByThis) { - StartNeutering(); - } -} - -NeuteredWindowRegion::~NeuteredWindowRegion() -{ - if (gWindowHook && mNeuteredByThis) { - StopNeutering(); - } -} - -void -NeuteredWindowRegion::PumpOnce() -{ - MSG msg = {0}; - if (gCOMWindow) { - // Pump any COM messages so that we don't hang due to STA marshaling. - // This call will also expunge any nonqueued messages on the current thread - if (::PeekMessageW(&msg, gCOMWindow, 0, 0, PM_REMOVE)) { - ::TranslateMessage(&msg); - ::DispatchMessageW(&msg); - } - } else { - // Expunge any nonqueued messages on the current thread - ::PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE); - } -} - -DeneuteredWindowRegion::DeneuteredWindowRegion(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL) - : mReneuter(gWindowHook != NULL) -{ - MOZ_GUARD_OBJECT_NOTIFIER_INIT; - if (mReneuter) { - StopNeutering(); - } -} - -DeneuteredWindowRegion::~DeneuteredWindowRegion() -{ - if (mReneuter) { - StartNeutering(); - } -} - bool MessageChannel::WaitForSyncNotify() { @@ -996,6 +916,15 @@ MessageChannel::WaitForSyncNotify() NS_ASSERTION(timerId, "SetTimer failed!"); } + // Setup deferred processing of native events while we wait for a response. + NS_ASSERTION(!MessageChannel::IsPumpingMessages(), + "Shouldn't be pumping already!"); + + MessageChannel::SetIsPumpingMessages(true); + HHOOK windowHook = SetWindowsHookEx(WH_CALLWNDPROC, CallWindowProcedureHook, + nullptr, gUIThreadId); + NS_ASSERTION(windowHook, "Failed to set hook!"); + { while (1) { MSG msg = { 0 }; @@ -1069,11 +998,25 @@ MessageChannel::WaitForSyncNotify() } } + // Unhook the neutered window procedure hook. + UnhookWindowsHookEx(windowHook); + + // Unhook any neutered windows procedures so messages can be delivered + // normally. + UnhookNeuteredWindows(); + + // Before returning we need to set a hook to run any deferred messages that + // we received during the IPC call. The hook will unset itself as soon as + // someone else calls GetMessage, PeekMessage, or runs code that generates + // a "nonqueued" message. + ScheduleDeferredMessageRun(); + if (timerId) { KillTimer(nullptr, timerId); - timerId = 0; } + MessageChannel::SetIsPumpingMessages(false); + return WaitResponse(timedout); } @@ -1107,28 +1050,56 @@ MessageChannel::WaitForInterruptNotify() UINT_PTR timerId = 0; TimeoutData timeoutData = { 0 }; - // gWindowHook is used as a flag variable for the loop below: if it is set + // 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. + // If windowHook is nullptr, MessageChannel::IsPumpingMessages should be false. + HHOOK windowHook = nullptr; + while (1) { - NS_ASSERTION((!!gWindowHook) == MessageChannel::IsPumpingMessages(), - "gWindowHook out of sync with reality"); + NS_ASSERTION((!!windowHook) == MessageChannel::IsPumpingMessages(), + "windowHook out of sync with reality"); if (mTopFrame->mSpinNestedEvents) { - if (gWindowHook && timerId) { - KillTimer(nullptr, timerId); - timerId = 0; + if (windowHook) { + UnhookWindowsHookEx(windowHook); + windowHook = nullptr; + + if (timerId) { + KillTimer(nullptr, timerId); + timerId = 0; + } + + // Used by widget to assert on incoming native events + MessageChannel::SetIsPumpingMessages(false); + + // Unhook any neutered windows procedures so messages can be delievered + // normally. + UnhookNeuteredWindows(); + + // Send all deferred "nonqueued" message to the intended receiver. + // We're dropping into SpinInternalEventLoop so we should be fairly + // certain these will get delivered soohn. + ScheduleDeferredMessageRun(); } - DeneuteredWindowRegion deneuteredRgn; SpinInternalEventLoop(); ResetEvent(mEvent); return true; } - if (mTimeoutMs != kNoTimeout && !timerId) { - InitTimeoutData(&timeoutData, mTimeoutMs); - timerId = SetTimer(nullptr, 0, mTimeoutMs, nullptr); - NS_ASSERTION(timerId, "SetTimer failed!"); + if (!windowHook) { + MessageChannel::SetIsPumpingMessages(true); + windowHook = SetWindowsHookEx(WH_CALLWNDPROC, CallWindowProcedureHook, + nullptr, gUIThreadId); + NS_ASSERTION(windowHook, "Failed to set hook!"); + + NS_ASSERTION(!timerId, "Timer already initialized?"); + + if (mTimeoutMs != kNoTimeout) { + InitTimeoutData(&timeoutData, mTimeoutMs); + timerId = SetTimer(nullptr, 0, mTimeoutMs, nullptr); + NS_ASSERTION(timerId, "SetTimer failed!"); + } } MSG msg = { 0 }; @@ -1180,11 +1151,27 @@ MessageChannel::WaitForInterruptNotify() } } - if (timerId) { - KillTimer(nullptr, timerId); - timerId = 0; + if (windowHook) { + // Unhook the neutered window procedure hook. + UnhookWindowsHookEx(windowHook); + + // Unhook any neutered windows procedures so messages can be delivered + // normally. + UnhookNeuteredWindows(); + + // Before returning we need to set a hook to run any deferred messages that + // we received during the IPC call. The hook will unset itself as soon as + // someone else calls GetMessage, PeekMessage, or runs code that generates + // a "nonqueued" message. + ScheduleDeferredMessageRun(); + + if (timerId) { + KillTimer(nullptr, timerId); + } } + MessageChannel::SetIsPumpingMessages(false); + return WaitResponse(timedout); } diff --git a/ipc/glue/moz.build b/ipc/glue/moz.build index 2c0f541696b1..fc4f0be808e2 100644 --- a/ipc/glue/moz.build +++ b/ipc/glue/moz.build @@ -25,7 +25,6 @@ EXPORTS.mozilla.ipc += [ 'IOThreadChild.h', 'MessageChannel.h', 'MessageLink.h', - 'Neutering.h', 'ProcessChild.h', 'ProtocolUtils.h', 'ScopedXREEmbed.h',