Bug 1318506 - Label HttpChannelChild actors with DocGroup/TabGroup (r=jduell)
authorBill McCloskey <billm@mozilla.com>
Tue, 15 Nov 2016 16:13:17 -0800
changeset 453586 83c36290906985c3f6a4a211cb7ed45ab902f9c2
parent 453585 ecd1cb767041b22a56db4e2284440bb0e7523f45
child 453587 32a005fd81f39ec493a735c71d3c3848078bbd44
push id39711
push userdmitchell@mozilla.com
push dateFri, 23 Dec 2016 21:59:47 +0000
reviewersjduell
bugs1318506
milestone53.0a1
Bug 1318506 - Label HttpChannelChild actors with DocGroup/TabGroup (r=jduell) This patch tries to figure out which DocGroup or TabGroup a network request belongs to and then assign the IPC actor to that group. A DocGroup roughly corresponds to a document and a TabGroup to a tab. Once the assignment is made, all incoming IPC messages will be labeled with that DocGroup/TabGroup. MozReview-Commit-ID: EzGCeGdREHl
netwerk/ipc/ChannelEventQueue.cpp
netwerk/ipc/ChannelEventQueue.h
netwerk/protocol/http/HttpChannelChild.cpp
netwerk/protocol/http/HttpChannelChild.h
--- a/netwerk/ipc/ChannelEventQueue.cpp
+++ b/netwerk/ipc/ChannelEventQueue.cpp
@@ -89,10 +89,25 @@ ChannelEventQueue::RetargetDeliveryTo(ns
   MOZ_RELEASE_ASSERT(aTargetThread);
 
   mTargetThread = do_QueryInterface(aTargetThread);
   MOZ_RELEASE_ASSERT(mTargetThread);
 
   return NS_OK;
 }
 
+nsresult
+ChannelEventQueue::ResetDeliveryTarget()
+{
+  MutexAutoLock lock(mMutex);
+
+  MOZ_RELEASE_ASSERT(mEventQueue.IsEmpty());
+  MOZ_RELEASE_ASSERT(mSuspendCount == 0);
+  MOZ_RELEASE_ASSERT(!mSuspended);
+  MOZ_RELEASE_ASSERT(!mForced);
+  MOZ_RELEASE_ASSERT(!mFlushing);
+  mTargetThread = nullptr;
+
+  return NS_OK;
+}
+
 } // namespace net
 } // namespace mozilla
--- a/netwerk/ipc/ChannelEventQueue.h
+++ b/netwerk/ipc/ChannelEventQueue.h
@@ -72,16 +72,21 @@ class ChannelEventQueue final
   inline void Suspend();
   // Resume flushes the queue asynchronously, i.e. items in queue will be
   // dispatched in a new event on the current thread.
   void Resume();
 
   // Retargets delivery of events to the target thread specified.
   nsresult RetargetDeliveryTo(nsIEventTarget* aTargetThread);
 
+  // Nulls out the delivery target so events are delivered to the main
+  // thread. Should only be called when the queue is known to be empty.
+  // Useful if the queue will be re-used.
+  nsresult ResetDeliveryTarget();
+
  private:
   // Private destructor, to discourage deletion outside of Release():
   ~ChannelEventQueue()
   {
   }
 
   inline void MaybeFlushQueue();
   void FlushQueue();
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -7,24 +7,27 @@
 
 // HttpLog.h should generally be included first
 #include "HttpLog.h"
 
 #include "nsHttp.h"
 #include "nsICacheEntry.h"
 #include "mozilla/Unused.h"
 #include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/TabChild.h"
+#include "mozilla/dom/TabGroup.h"
 #include "mozilla/ipc/FileDescriptorSetChild.h"
 #include "mozilla/ipc/IPCStreamUtils.h"
 #include "mozilla/net/NeckoChild.h"
 #include "mozilla/net/HttpChannelChild.h"
 
 #include "nsISupportsPrimitives.h"
 #include "nsChannelClassifier.h"
+#include "nsGlobalWindow.h"
 #include "nsStringStream.h"
 #include "nsHttpHandler.h"
 #include "nsNetUtil.h"
 #include "nsSerializationHelper.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/Performance.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 #include "mozilla/ipc/URIUtils.h"
@@ -1207,16 +1210,20 @@ HttpChannelChild::OverrideRunnable::Run(
 
 mozilla::ipc::IPCResult
 HttpChannelChild::RecvFinishInterceptedRedirect()
 {
   // Hold a ref to this to keep it from being deleted by Send__delete__()
   RefPtr<HttpChannelChild> self(this);
   Send__delete__(this);
 
+  // Reset the event target to which queued messages are delivered. Otherwise
+  // we'll get an assertion when we re-use the channel later on.
+  mEventQ->ResetDeliveryTarget();
+
   // The IPDL connection was torn down by a interception logic in
   // CompleteRedirectSetup, and we need to call FinishInterceptedRedirect.
   NS_DispatchToMainThread(NewRunnableMethod(this, &HttpChannelChild::FinishInterceptedRedirect));
 
   return IPC_OK();
 }
 
 void
@@ -1616,16 +1623,21 @@ HttpChannelChild::ConnectParent(uint32_t
   }
 
   HttpBaseChannel::SetDocshellUserAgentOverride();
 
   // The socket transport in the chrome process now holds a logical ref to us
   // until OnStopRequest, or we do a redirect, or we hit an IPDL error.
   AddIPDLReference();
 
+  // This must happen before the constructor message is sent. Otherwise messages
+  // from the parent could arrive quickly and be delivered to the wrong event
+  // target.
+  SetEventTarget();
+
   HttpChannelConnectArgs connectArgs(registrarId, mShouldParentIntercept);
   PBrowserOrId browser = static_cast<ContentChild*>(gNeckoChild->Manager())
                          ->GetBrowserOrId(tabChild);
   if (!gNeckoChild->
         SendPHttpChannelConstructor(this, browser,
                                     IPC::SerializedLoadContext(this),
                                     connectArgs)) {
     return NS_ERROR_FAILURE;
@@ -2015,16 +2027,65 @@ NS_IMETHODIMP
 HttpChannelChild::AsyncOpen2(nsIStreamListener *aListener)
 {
   nsCOMPtr<nsIStreamListener> listener = aListener;
   nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
   NS_ENSURE_SUCCESS(rv, rv);
   return AsyncOpen(listener, nullptr);
 }
 
+// Assigns an nsIEventTarget to our IPDL actor so that IPC messages are sent to
+// the correct DocGroup/TabGroup.
+void
+HttpChannelChild::SetEventTarget()
+{
+  nsCOMPtr<nsILoadInfo> loadInfo;
+  GetLoadInfo(getter_AddRefs(loadInfo));
+  if (!loadInfo) {
+    return;
+  }
+
+  nsCOMPtr<nsIDOMDocument> domDoc;
+  loadInfo->GetLoadingDocument(getter_AddRefs(domDoc));
+  nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
+  RefPtr<Dispatcher> dispatcher;
+  if (doc) {
+    dispatcher = doc->GetDocGroup();
+  } else {
+    // There's no document yet, but this might be a top-level load where we can
+    // find a TabGroup.
+    uint64_t outerWindowId;
+    if (NS_FAILED(loadInfo->GetOuterWindowID(&outerWindowId))) {
+      // No window. This might be an add-on XHR, a service worker request, or
+      // something else.
+      return;
+    }
+    RefPtr<nsGlobalWindow> window = nsGlobalWindow::GetOuterWindowWithId(outerWindowId);
+    if (!window) {
+      return;
+    }
+
+#ifdef DEBUG
+    // We have a TabGroup. This must be a top-level load.
+    bool isMainDocumentChannel;
+    GetIsMainDocumentChannel(&isMainDocumentChannel);
+    MOZ_ASSERT(isMainDocumentChannel);
+#endif
+
+    dispatcher = window->TabGroup();
+  }
+
+  if (dispatcher) {
+    nsCOMPtr<nsIEventTarget> target =
+      dispatcher->EventTargetFor(TaskCategory::Network);
+    gNeckoChild->SetEventTargetForActor(this, target);
+    mEventQ->RetargetDeliveryTo(target);
+  }
+}
+
 nsresult
 HttpChannelChild::ContinueAsyncOpen()
 {
   nsCString appCacheClientId;
   if (mInheritApplicationCache) {
     // Pick up an application cache from the notification
     // callbacks if available
     nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer;
@@ -2165,16 +2226,21 @@ HttpChannelChild::ContinueAsyncOpen()
     return NS_ERROR_FAILURE;
   }
 
   ContentChild* cc = static_cast<ContentChild*>(gNeckoChild->Manager());
   if (cc->IsShuttingDown()) {
     return NS_ERROR_FAILURE;
   }
 
+  // This must happen before the constructor message is sent. Otherwise messages
+  // from the parent could arrive quickly and be delivered to the wrong event
+  // target.
+  SetEventTarget();
+
   // The socket transport in the chrome process now holds a logical ref to us
   // until OnStopRequest, or we do a redirect, or we hit an IPDL error.
   AddIPDLReference();
 
   PBrowserOrId browser = cc->GetBrowserOrId(tabChild);
   if (!gNeckoChild->SendPHttpChannelConstructor(this, browser,
                                                 IPC::SerializedLoadContext(this),
                                                 openArgs)) {
--- a/netwerk/protocol/http/HttpChannelChild.h
+++ b/netwerk/protocol/http/HttpChannelChild.h
@@ -177,16 +177,22 @@ private:
   private:
     RefPtr<HttpChannelChild> mChannel;
     RefPtr<HttpChannelChild> mNewChannel;
     RefPtr<InterceptStreamListener> mListener;
     nsCOMPtr<nsIInputStream> mInput;
     nsAutoPtr<nsHttpResponseHead> mHead;
   };
 
+  // Sets the event target for future IPC messages. Messages will either be
+  // directed to the TabGroup or DocGroup, depending on the LoadInfo associated
+  // with the channel. Should be called when a new channel is being set up,
+  // before the constructor message is sent to the parent.
+  void SetEventTarget();
+
   nsresult ContinueAsyncOpen();
 
   void DoOnStartRequest(nsIRequest* aRequest, nsISupports* aContext);
   void DoOnStatus(nsIRequest* aRequest, nsresult status);
   void DoOnProgress(nsIRequest* aRequest, int64_t progress, int64_t progressMax);
   void DoOnDataAvailable(nsIRequest* aRequest, nsISupports* aContext, nsIInputStream* aStream,
                          uint64_t offset, uint32_t count);
   void DoPreOnStopRequest(nsresult aStatus);