Back out bug 1191145 - Stop blocking scripts in CPOW IPCs
authorBill McCloskey <billm@mozilla.com>
Wed, 07 Oct 2015 11:13:08 -0700
changeset 266713 f913b64862d8e2dabca38a1a1e0c6513362334da
parent 266712 83e3c8f5c53aa5e8978802f37980a4fea97a7385
child 266714 b268160e8919390c1a9f61a75d3f3b3c79491f77
push id29497
push usercbook@mozilla.com
push dateThu, 08 Oct 2015 13:27:17 +0000
treeherdermozilla-central@1f4cf75c8948 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1191145
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
Back out bug 1191145 - Stop blocking scripts in CPOW IPCs
dom/ipc/ContentChild.cpp
ipc/glue/MessageChannel.cpp
ipc/glue/MessageChannel.h
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -643,16 +643,20 @@ ContentChild::Init(MessageLoop* aIOLoop,
         return false;
     }
 
     if (!Open(aChannel, aParentPid, aIOLoop)) {
       return false;
     }
     sSingleton = this;
 
+    // Make sure there's an nsAutoScriptBlocker on the stack when dispatching
+    // urgent messages.
+    GetIPCChannel()->BlockScripts();
+
     // If communications with the parent have broken down, take the process
     // down so it's not hanging around.
     bool abortOnError = true;
 #ifdef MOZ_NUWA_PROCESS
     abortOnError &= !IsNuwaProcess();
 #endif
     GetIPCChannel()->SetAbortOnError(abortOnError);
 
--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -270,16 +270,41 @@ private:
     MessageChannel& mThat;
 
     // Disable harmful methods.
     CxxStackFrame() = delete;
     CxxStackFrame(const CxxStackFrame&) = delete;
     CxxStackFrame& operator=(const CxxStackFrame&) = delete;
 };
 
+namespace {
+
+class MOZ_RAII MaybeScriptBlocker {
+public:
+    explicit MaybeScriptBlocker(MessageChannel *aChannel, bool aBlock
+                                MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+        : mBlocked(aChannel->ShouldBlockScripts() && aBlock)
+    {
+        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+        if (mBlocked) {
+            nsContentUtils::AddScriptBlocker();
+        }
+    }
+    ~MaybeScriptBlocker() {
+        if (mBlocked) {
+            nsContentUtils::RemoveScriptBlocker();
+        }
+    }
+private:
+    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+    bool mBlocked;
+};
+
+} // namespace
+
 MessageChannel::MessageChannel(MessageListener *aListener)
   : mListener(aListener),
     mChannelState(ChannelClosed),
     mSide(UnknownSide),
     mLink(nullptr),
     mWorkerLoop(nullptr),
     mChannelErrorTask(nullptr),
     mWorkerLoopID(-1),
@@ -295,16 +320,17 @@ MessageChannel::MessageChannel(MessageLi
     mCurrentTransaction(0),
     mTimedOutMessageSeqno(0),
     mTimedOutMessagePriority(0),
     mRecvdErrors(0),
     mRemoteStackDepthGuess(false),
     mSawInterruptOutMsg(false),
     mIsWaitingForIncoming(false),
     mAbortOnError(false),
+    mBlockScripts(false),
     mFlags(REQUIRE_DEFAULT),
     mPeerPidSet(false),
     mPeerPid(-1)
 {
     MOZ_COUNT_CTOR(ipc::MessageChannel);
 
 #ifdef OS_WIN
     mTopFrame = nullptr;
@@ -810,16 +836,19 @@ MessageChannel::WasTransactionCanceled(i
     return true;
 }
 
 bool
 MessageChannel::Send(Message* aMsg, Message* aReply)
 {
     nsAutoPtr<Message> msg(aMsg);
 
+    // See comment in DispatchSyncMessage.
+    MaybeScriptBlocker scriptBlocker(this, true);
+
     // Sanity checks.
     AssertWorkerThread();
     mMonitor->AssertNotCurrentThreadOwns();
 
     if (mCurrentTransaction == 0)
         mListener->OnBeginSyncTransaction();
 
 #ifdef OS_WIN
@@ -1291,17 +1320,23 @@ MessageChannel::DispatchMessage(const Me
 }
 
 void
 MessageChannel::DispatchSyncMessage(const Message& aMsg, Message*& aReply)
 {
     AssertWorkerThread();
 
     int prio = aMsg.priority();
+
+    // We don't want to run any code that might run a nested event loop here, so
+    // we avoid running event handlers. Once we've sent the response to the
+    // urgent message, it's okay to run event handlers again since the parent is
+    // no longer blocked.
     MOZ_ASSERT_IF(prio > IPC::Message::PRIORITY_NORMAL, NS_IsMainThread());
+    MaybeScriptBlocker scriptBlocker(this, prio > IPC::Message::PRIORITY_NORMAL);
 
     MessageChannel* dummy;
     MessageChannel*& blockingVar = NS_IsMainThread() ? gMainThreadBlocker : dummy;
 
     Result rv;
     if (mTimedOutMessageSeqno && mTimedOutMessagePriority >= prio) {
         // If the other side sends a message in response to one of our messages
         // that we've timed out, then we reply with an error.
@@ -1850,16 +1885,23 @@ MessageChannel::CloseWithTimeout()
     if (ChannelConnected != mChannelState) {
         return;
     }
     SynchronouslyClose();
     mChannelState = ChannelTimeout;
 }
 
 void
+MessageChannel::BlockScripts()
+{
+    MOZ_ASSERT(NS_IsMainThread());
+    mBlockScripts = true;
+}
+
+void
 MessageChannel::Close()
 {
     AssertWorkerThread();
 
     {
         MonitorAutoLock lock(*mMonitor);
 
         if (ChannelError == mChannelState || ChannelTimeout == mChannelState) {
--- a/ipc/glue/MessageChannel.h
+++ b/ipc/glue/MessageChannel.h
@@ -102,16 +102,23 @@ class MessageChannel : HasResultCodes
       // 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
     };
     void SetChannelFlags(ChannelFlags aFlags) { mFlags = aFlags; }
     ChannelFlags GetChannelFlags() { return mFlags; }
 
+    void BlockScripts();
+
+    bool ShouldBlockScripts() const
+    {
+        return mBlockScripts;
+    }
+
     // 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
     // channel
     bool Echo(Message* aMsg);
 
     // Synchronously send |msg| (i.e., wait for |reply|)
@@ -724,16 +731,19 @@ class MessageChannel : HasResultCodes
 #ifdef OS_WIN
     HANDLE mEvent;
 #endif
 
     // Should the channel abort the process from the I/O thread when
     // a channel error occurs?
     bool mAbortOnError;
 
+    // Should we prevent scripts from running while dispatching urgent messages?
+    bool mBlockScripts;
+
     // See SetChannelFlags
     ChannelFlags mFlags;
 
     // Task and state used to asynchronously notify channel has been connected
     // safely.  This is necessary to be able to cancel notification if we are
     // closed at the same time.
     nsRefPtr<RefCountedTask> mOnChannelConnectedTask;
     DebugOnly<bool> mPeerPidSet;