Bug 1273635: Enable alertable waits in content process main thread; r?jimm
MozReview-Commit-ID: 2qGdGj41M0n
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -583,16 +583,19 @@ ContentChild::Init(MessageLoop* aIOLoop,
if (!Open(aChannel, aParentPid, aIOLoop)) {
return false;
}
sSingleton = this;
// If communications with the parent have broken down, take the process
// down so it's not hanging around.
GetIPCChannel()->SetAbortOnError(true);
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+ GetIPCChannel()->SetChannelFlags(MessageChannel::REQUIRE_A11Y_REENTRY);
+#endif
#ifdef MOZ_X11
// Send the parent our X socket to act as a proxy reference for our X
// resources.
int xSocketFd = ConnectionNumber(DefaultXDisplay());
SendBackUpXResources(FileDescriptor(xSocketFd));
#endif
--- a/ipc/glue/MessageChannel.h
+++ b/ipc/glue/MessageChannel.h
@@ -122,17 +122,22 @@ class MessageChannel : HasResultCodes
// Misc. behavioral traits consumers can request for this channel
enum ChannelFlags {
REQUIRE_DEFAULT = 0,
// Windows: if this channel operates on the UI thread, indicates
// WindowsMessageLoop code should enable deferred native message
// handling to prevent deadlocks. Should only be used for protocols
// that manage child processes which might create native UI, like
// plugins.
- REQUIRE_DEFERRED_MESSAGE_PROTECTION = 1 << 0
+ REQUIRE_DEFERRED_MESSAGE_PROTECTION = 1 << 0,
+ // Windows: When this flag is specified, any wait that occurs during
+ // synchronous IPC will be alertable, thus allowing a11y code in the
+ // chrome process to reenter content while content is waiting on a
+ // synchronous call.
+ REQUIRE_A11Y_REENTRY = 1 << 1,
};
void SetChannelFlags(ChannelFlags aFlags) { mFlags = aFlags; }
ChannelFlags GetChannelFlags() { return mFlags; }
// Asynchronously send a message to the other side of the channel
bool Send(Message* aMsg);
// Asynchronously deliver a message back to this side of the
@@ -238,17 +243,20 @@ class MessageChannel : HasResultCodes
static SyncStackFrame* sStaticTopFrame;
public:
void ProcessNativeEventsInInterruptCall();
static void NotifyGeckoEventDispatch();
private:
void SpinInternalEventLoop();
-#endif
+#if defined(ACCESSIBILITY)
+ bool WaitForSyncNotifyWithA11yReentry();
+#endif // defined(ACCESSIBILITY)
+#endif // defined(OS_WIN)
private:
void CommonThreadOpenInit(MessageChannel *aTargetChan, Side aSide);
void OnOpenAsSlave(MessageChannel *aTargetChan, Side aSide);
void PostErrorNotifyTask();
void OnNotifyMaybeChannelError();
void ReportConnectionError(const char* aChannelName, Message* aMsg = nullptr) const;
--- a/ipc/glue/WindowsMessageLoop.cpp
+++ b/ipc/glue/WindowsMessageLoop.cpp
@@ -14,16 +14,17 @@
#include "nsServiceManagerUtils.h"
#include "nsString.h"
#include "nsIXULAppInfo.h"
#include "nsWindowsDllInterceptor.h"
#include "WinUtils.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/PaintTracker.h"
+#include "mozilla/WindowsVersion.h"
using namespace mozilla;
using namespace mozilla::ipc;
using namespace mozilla::ipc::windows;
/**
* The Windows-only code below exists to solve a general problem with deadlocks
* that we experience when sending synchronous IPC messages to processes that
@@ -1051,23 +1052,82 @@ SuppressedNeuteringRegion::~SuppressedNe
if (mReenable) {
MOZ_ASSERT(sSuppressNeutering);
sSuppressNeutering = false;
}
}
bool SuppressedNeuteringRegion::sSuppressNeutering = false;
+#if defined(ACCESSIBILITY)
+bool
+MessageChannel::WaitForSyncNotifyWithA11yReentry()
+{
+ mMonitor->AssertCurrentThreadOwns();
+ MonitorAutoUnlock unlock(*mMonitor);
+
+ const DWORD waitStart = ::GetTickCount();
+ DWORD elapsed = 0;
+ DWORD timeout = mTimeoutMs == kNoTimeout ? INFINITE :
+ static_cast<DWORD>(mTimeoutMs);
+ bool timedOut = false;
+
+ while (true) {
+ { // Scope for lock
+ MonitorAutoLock lock(*mMonitor);
+ if (!Connected()) {
+ break;
+ }
+ }
+ if (timeout != static_cast<DWORD>(kNoTimeout)) {
+ elapsed = ::GetTickCount() - waitStart;
+ }
+ if (elapsed >= timeout) {
+ timedOut = true;
+ break;
+ }
+ DWORD waitResult = 0;
+ ::SetLastError(ERROR_SUCCESS);
+ HRESULT hr = ::CoWaitForMultipleHandles(COWAIT_ALERTABLE,
+ timeout - elapsed,
+ 1, &mEvent, &waitResult);
+ if (hr == RPC_S_CALLPENDING) {
+ timedOut = true;
+ break;
+ }
+ if (hr == S_OK) {
+ if (waitResult == 0) {
+ // mEvent is signaled
+ break;
+ }
+ if (waitResult == WAIT_IO_COMPLETION) {
+ // APC fired, keep waiting
+ continue;
+ }
+ }
+ NS_WARN_IF_FALSE(SUCCEEDED(hr), "CoWaitForMultipleHandles failed");
+ }
+
+ return WaitResponse(timedOut);
+}
+#endif
+
bool
MessageChannel::WaitForSyncNotify(bool aHandleWindowsMessages)
{
mMonitor->AssertCurrentThreadOwns();
MOZ_ASSERT(gUIThreadId, "InitUIThread was not called!");
+#if defined(ACCESSIBILITY)
+ if (IsVistaOrLater() && (mFlags & REQUIRE_A11Y_REENTRY)) {
+ return WaitForSyncNotifyWithA11yReentry();
+ }
+#endif
+
// Use a blocking wait if this channel does not require
// Windows message deferral behavior.
if (!(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION) || !aHandleWindowsMessages) {
PRIntervalTime timeout = (kNoTimeout == mTimeoutMs) ?
PR_INTERVAL_NO_TIMEOUT :
PR_MillisecondsToInterval(mTimeoutMs);
PRIntervalTime waitStart = 0;
--- a/widget/windows/WinUtils.cpp
+++ b/widget/windows/WinUtils.cpp
@@ -721,36 +721,56 @@ WinUtils::GetMessage(LPMSG aMsg, HWND aW
&ret);
NS_ENSURE_TRUE(SUCCEEDED(hr), false);
return ret;
}
#endif // #ifdef NS_ENABLE_TSF
return ::GetMessageW(aMsg, aWnd, aFirstMessage, aLastMessage);
}
+#if defined(ACCESSIBILITY)
+static DWORD
+GetWaitFlags()
+{
+ DWORD result = MWMO_INPUTAVAILABLE;
+ if (IsVistaOrLater() && XRE_IsContentProcess()) {
+ result |= MWMO_ALERTABLE;
+ }
+ return result;
+}
+#endif
+
/* static */
void
WinUtils::WaitForMessage(DWORD aTimeoutMs)
{
+#if defined(ACCESSIBILITY)
+ static const DWORD waitFlags = GetWaitFlags();
+#else
+ const DWORD waitFlags = MWMO_INPUTAVAILABLE;
+#endif
+
const DWORD waitStart = ::GetTickCount();
DWORD elapsed = 0;
while (true) {
if (aTimeoutMs != INFINITE) {
elapsed = ::GetTickCount() - waitStart;
}
if (elapsed >= aTimeoutMs) {
break;
}
DWORD result = ::MsgWaitForMultipleObjectsEx(0, NULL, aTimeoutMs - elapsed,
- MOZ_QS_ALLEVENT,
- MWMO_INPUTAVAILABLE);
+ MOZ_QS_ALLEVENT, waitFlags);
NS_WARN_IF_FALSE(result != WAIT_FAILED, "Wait failed");
if (result == WAIT_TIMEOUT) {
break;
}
+ if (result == WAIT_IO_COMPLETION) {
+ continue;
+ }
// Sent messages (via SendMessage and friends) are processed differently
// than queued messages (via PostMessage); the destination window procedure
// of the sent message is called during (Get|Peek)Message. Since PeekMessage
// does not tell us whether it processed any sent messages, we need to query
// this ahead of time.
bool haveSentMessagesPending =
(HIWORD(::GetQueueStatus(QS_SENDMESSAGE)) & QS_SENDMESSAGE) != 0;