Bug 1318506 - Label HttpChannelChild actors with DocGroup/TabGroup (r?jduell) draft
authorBill McCloskey <billm@mozilla.com>
Tue, 15 Nov 2016 16:13:17 -0800
changeset 444812 2c717f1caf80e49dfc8a1e85f0fe2e89a5010e4d
parent 444811 f66210e2ee3ebaf86c9b4492e44cadbb18959204
child 444813 d31f24db9af5d31c5c83316b34a59098fe662668
push id37376
push userbmo:wmccloskey@mozilla.com
push dateMon, 28 Nov 2016 21:43:46 +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
@@ -71,16 +71,17 @@ class ChannelEventQueue final
   // called when the channel owning the event queue is suspended/resumed.
   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);
+  nsresult ResetDeliveryTarget();
 
  private:
   // Private destructor, to discourage deletion outside of Release():
   ~ChannelEventQueue()
   {
   }
 
   inline void MaybeFlushQueue();
--- 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"
@@ -1194,16 +1197,18 @@ 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);
 
+  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
@@ -1596,16 +1601,18 @@ 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();
 
+  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;
@@ -1987,16 +1994,55 @@ 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 {
+    // Top-level loads won't have a DocGroup yet. So instead we target them at
+    // the TabGroup, which is the best we can do at this time.
+    uint64_t outerWindowId;
+    if (NS_FAILED(loadInfo->GetOuterWindowID(&outerWindowId))) {
+      return;
+    }
+    RefPtr<nsGlobalWindow> window = nsGlobalWindow::GetOuterWindowWithId(outerWindowId);
+    if (!window) {
+      return;
+    }
+    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;
@@ -2122,16 +2168,18 @@ HttpChannelChild::ContinueAsyncOpen()
     return NS_ERROR_FAILURE;
   }
 
   ContentChild* cc = static_cast<ContentChild*>(gNeckoChild->Manager());
   if (cc->IsShuttingDown()) {
     return NS_ERROR_FAILURE;
   }
 
+  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
@@ -174,16 +174,18 @@ private:
   private:
     RefPtr<HttpChannelChild> mChannel;
     RefPtr<HttpChannelChild> mNewChannel;
     RefPtr<InterceptStreamListener> mListener;
     nsCOMPtr<nsIInputStream> mInput;
     nsAutoPtr<nsHttpResponseHead> mHead;
   };
 
+  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);