bug 1212906 - don't handle windows messages while waiting for a sync a11y ipc message r=billm
authorTrevor Saunders <tbsaunde@tbsaunde.org>
Wed, 07 Oct 2015 17:38:08 -0400
changeset 267744 f753f2c6e79c956de6da6383e8f347c3b4c0c275
parent 267743 560800527fd920e4d7cebb8274addfcda5e5f4bc
child 267745 fdd5ebadfbed258d796a85410d266f533e9914b2
push id29530
push usercbook@mozilla.com
push dateThu, 15 Oct 2015 09:53:07 +0000
treeherdermozilla-central@e193b4da0a8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbillm
bugs1212906
milestone44.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 1212906 - don't handle windows messages while waiting for a sync a11y ipc message r=billm Windows messages can trigger sync ipc messages to the child process. That means if we handle windows messages while waiting for the response to a sync a11y ipc message we can end up reentering the code to send ipc messages which is bad. Try and avoid this situation by not handling windows messages while waiting for a sync a11y message.
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
ipc/glue/MessageChannel.cpp
ipc/glue/MessageChannel.h
ipc/glue/MessageLink.h
ipc/glue/WindowsMessageLoop.cpp
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -19,16 +19,17 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 #endif
 
 #include "chrome/common/process_watcher.h"
 
 #include <set>
 
+#include "mozilla/a11y/PDocAccessible.h"
 #include "AppProcessChecker.h"
 #include "AudioChannelService.h"
 #include "BlobParent.h"
 #include "CrashReporterParent.h"
 #include "GMPServiceParent.h"
 #include "IHistory.h"
 #include "imgIContainer.h"
 #include "mozIApplication.h"
@@ -5471,8 +5472,24 @@ NS_IMPL_ISUPPORTS(ParentIdleListener, ns
 
 NS_IMETHODIMP
 ParentIdleListener::Observe(nsISupports*, const char* aTopic, const char16_t* aData) {
     mozilla::unused << mParent->SendNotifyIdleObserver(mObserver,
                                                        nsDependentCString(aTopic),
                                                        nsDependentString(aData));
     return NS_OK;
 }
+
+bool
+ContentParent::HandleWindowsMessages(const Message& aMsg) const
+{
+  MOZ_ASSERT(aMsg.is_sync());
+
+  // a11y messages can be triggered by windows messages, which means if we
+  // allow handling windows messages while we wait for the response to a sync
+  // a11y message we can reenter the ipc message sending code.
+  if (a11y::PDocAccessible::PDocAccessibleStart < aMsg.type() &&
+      a11y::PDocAccessible::PDocAccessibleEnd > aMsg.type()) {
+    return false;
+  }
+
+  return true;
+}
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -422,16 +422,18 @@ public:
 
     virtual PContentPermissionRequestParent*
     AllocPContentPermissionRequestParent(const InfallibleTArray<PermissionRequest>& aRequests,
                                          const IPC::Principal& aPrincipal,
                                          const TabId& aTabId) override;
     virtual bool
     DeallocPContentPermissionRequestParent(PContentPermissionRequestParent* actor) override;
 
+    virtual bool HandleWindowsMessages(const Message& aMsg) const override;
+
     bool HasGamepadListener() const { return mHasGamepadListener; }
 
     void SetNuwaParent(NuwaParent* aNuwaParent) { mNuwaParent = aNuwaParent; }
     void ForkNewProcess(bool aBlocking);
 
 protected:
     void OnChannelConnected(int32_t pid) override;
     virtual void ActorDestroy(ActorDestroyReason why) override;
--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -917,16 +917,17 @@ MessageChannel::Send(Message* aMsg, Mess
     int32_t transaction = mCurrentTransaction;
     msg->set_transaction_id(transaction);
 
     ProcessPendingRequests();
     if (WasTransactionCanceled(transaction, prio)) {
         return false;
     }
 
+    bool handleWindowsMessages = mListener->HandleWindowsMessages(*aMsg);
     mLink->SendMessage(msg.forget());
 
     while (true) {
         ProcessPendingRequests();
         if (WasTransactionCanceled(transaction, prio)) {
             return false;
         }
 
@@ -937,17 +938,17 @@ MessageChannel::Send(Message* aMsg, Mess
         }
 
         if (mRecvd) {
             break;
         }
 
         MOZ_ASSERT(!mTimedOutMessageSeqno);
 
-        bool maybeTimedOut = !WaitForSyncNotify();
+        bool maybeTimedOut = !WaitForSyncNotify(handleWindowsMessages);
 
         if (!Connected()) {
             ReportConnectionError("MessageChannel::SendAndWait");
             return false;
         }
 
         if (WasTransactionCanceled(transaction, prio)) {
             return false;
@@ -1571,17 +1572,17 @@ MessageChannel::WaitResponse(bool aWaitT
     } else {
         mInTimeoutSecondHalf = false;
     }
     return true;
 }
 
 #ifndef OS_WIN
 bool
-MessageChannel::WaitForSyncNotify()
+MessageChannel::WaitForSyncNotify(bool /* aHandleWindowsMessages */)
 {
     PRIntervalTime timeout = (kNoTimeout == mTimeoutMs) ?
                              PR_INTERVAL_NO_TIMEOUT :
                              PR_MillisecondsToInterval(mTimeoutMs);
     // XXX could optimize away this syscall for "no timeout" case if desired
     PRIntervalTime waitStart = PR_IntervalNow();
 
     mMonitor->Wait(timeout);
@@ -1589,17 +1590,17 @@ MessageChannel::WaitForSyncNotify()
     // If the timeout didn't expire, we know we received an event. The
     // converse is not true.
     return WaitResponse(IsTimeoutExpired(waitStart, timeout));
 }
 
 bool
 MessageChannel::WaitForInterruptNotify()
 {
-    return WaitForSyncNotify();
+    return WaitForSyncNotify(true);
 }
 
 void
 MessageChannel::NotifyWorkerThread()
 {
     mMonitor->Notify();
 }
 #endif
--- a/ipc/glue/MessageChannel.h
+++ b/ipc/glue/MessageChannel.h
@@ -273,17 +273,17 @@ class MessageChannel : HasResultCodes
     // Return false if the time elapsed from when we started the process of
     // waiting until afterwards exceeded the currently allotted timeout.
     // That *DOES NOT* mean false => "no event" (== timeout); there are many
     // circumstances that could cause the measured elapsed time to exceed the
     // timeout EVEN WHEN we were notified.
     //
     // So in sum: true is a meaningful return value; false isn't,
     // necessarily.
-    bool WaitForSyncNotify();
+    bool WaitForSyncNotify(bool aHandleWindowsMessages);
     bool WaitForInterruptNotify();
 
     bool WaitResponse(bool aWaitTimedOut);
 
     bool ShouldContinueFromTimeout();
 
     void CancelCurrentTransactionInternal();
 
--- a/ipc/glue/MessageLink.h
+++ b/ipc/glue/MessageLink.h
@@ -93,16 +93,22 @@ class MessageListener
     virtual void OnBeginSyncTransaction() {
     }
     virtual RacyInterruptPolicy MediateInterruptRace(const Message& parent,
                                                      const Message& child)
     {
         return RIPChildWins;
     }
 
+    /**
+     * Return true if windows messages can be handled while waiting for a reply
+     * to a sync IPDL message.
+     */
+    virtual bool HandleWindowsMessages(const Message& aMsg) const { return true; }
+
     virtual void OnEnteredSyncSend() {
     }
     virtual void OnExitedSyncSend() {
     }
 
     virtual void ProcessRemoteNativeEventsInInterruptCall() {
     }
 
--- a/ipc/glue/WindowsMessageLoop.cpp
+++ b/ipc/glue/WindowsMessageLoop.cpp
@@ -939,25 +939,25 @@ DeneuteredWindowRegion::DeneuteredWindow
 DeneuteredWindowRegion::~DeneuteredWindowRegion()
 {
   if (mReneuter) {
     StartNeutering();
   }
 }
 
 bool
-MessageChannel::WaitForSyncNotify()
+MessageChannel::WaitForSyncNotify(bool aHandleWindowsMessages)
 {
   mMonitor->AssertCurrentThreadOwns();
 
   MOZ_ASSERT(gUIThreadId, "InitUIThread was not called!");
 
   // Use a blocking wait if this channel does not require
   // Windows message deferral behavior.
-  if (!(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION)) {
+  if (!(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION) || !aHandleWindowsMessages) {
     PRIntervalTime timeout = (kNoTimeout == mTimeoutMs) ?
                              PR_INTERVAL_NO_TIMEOUT :
                              PR_MillisecondsToInterval(mTimeoutMs);
     PRIntervalTime waitStart = 0;
 
     if (timeout != PR_INTERVAL_NO_TIMEOUT) {
       waitStart = PR_IntervalNow();
     }
@@ -1083,17 +1083,17 @@ MessageChannel::WaitForInterruptNotify()
 {
   mMonitor->AssertCurrentThreadOwns();
 
   MOZ_ASSERT(gUIThreadId, "InitUIThread was not called!");
 
   // Re-use sync notification wait code if this channel does not require
   // Windows message deferral behavior. 
   if (!(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION)) {
-    return WaitForSyncNotify();
+    return WaitForSyncNotify(true);
   }
 
   if (!InterruptStackDepth() && !AwaitingIncomingMessage()) {
     // There is currently no way to recover from this condition.
     NS_RUNTIMEABORT("StackDepth() is 0 in call to MessageChannel::WaitForNotify!");
   }
 
   NS_ASSERTION(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION,