Bug 1396395 - Part 1, resume input pump after callback finished. r?dragana draft
authorShih-Chiang Chien <schien@mozilla.com>
Wed, 13 Sep 2017 18:34:04 +0800
changeset 669612 e6eb7fbd71e86c1672eea8ea03e02142bd541e15
parent 669596 7e962631ba4298bcefa571008661983d77c3e652
child 669613 d4ac84dc086139c8858e227c570da5ff26c09177
push id81380
push userschien@mozilla.com
push dateMon, 25 Sep 2017 03:13:28 +0000
reviewersdragana
bugs1396395
milestone58.0a1
Bug 1396395 - Part 1, resume input pump after callback finished. r?dragana OnStartRequest callback chain is interrupted by add-on during the "http-on-modify-request" observer event. Therefore, nsInputStreamPump think OnStartRequest is finished. After resuming http channel, nsHttpChannel asynchronously continue the OnStartRequest procedure and synchronously resume the nsInputStreamPump. Before nsDocumentOpenInfo invoke the next OnStartRequest on the listener chain, sync XHR in web content is executed on the call stack. This will spin main thread event queue and will eventually callback OnDataAvailable/OnStopRequest on the same call stack. nsHttpChannel should not resume the nsInputStreamPump before |mCallOnResume| is complete, to ensure that no input stream event can interrupt the resumed call stack before it finished. MozReview-Commit-ID: 6Q9EtMhcff9
netwerk/protocol/http/nsHttpChannel.cpp
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -9030,19 +9030,42 @@ nsHttpChannel::ResumeInternal()
 
     LOG(("nsHttpChannel::ResumeInternal [this=%p]\n", this));
 
     if (--mSuspendCount == 0) {
         mSuspendTotalTime += (TimeStamp::NowLoRes() - mSuspendTimestamp).
                                ToMilliseconds();
 
         if (mCallOnResume) {
-            nsresult rv = AsyncCall(mCallOnResume);
+            // Resume the interrupted procedure first, then resume
+            // the pump to continue process the input stream.
+            RefPtr<nsRunnableMethod<nsHttpChannel>> callOnResume=
+                NewRunnableMethod("CallOnResume", this, mCallOnResume);
+            // Should not resume pump that created after resumption.
+            RefPtr<nsInputStreamPump> transactionPump = mTransactionPump;
+            RefPtr<nsInputStreamPump> cachePump = mCachePump;
+
+            nsresult rv =
+                NS_DispatchToCurrentThread(NS_NewRunnableFunction(
+                    "nsHttpChannel::CallOnResume",
+                    [callOnResume, transactionPump, cachePump]() {
+                        callOnResume->Run();
+
+                        if (transactionPump) {
+                            transactionPump->Resume();
+                        }
+
+                        if (cachePump) {
+                            cachePump->Resume();
+                        }
+                    })
+                );
             mCallOnResume = nullptr;
             NS_ENSURE_SUCCESS(rv, rv);
+            return rv;
         }
     }
 
     nsresult rvTransaction = NS_OK;
     if (mTransactionPump) {
         rvTransaction = mTransactionPump->Resume();
     }