From 2113b3347b1bcfe7485db73146a134d703c1fdbe Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Tue, 15 Nov 2016 16:13:17 -0800 Subject: [PATCH] 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 | 15 +++++ netwerk/ipc/ChannelEventQueue.h | 5 ++ netwerk/protocol/http/HttpChannelChild.cpp | 66 ++++++++++++++++++++++ netwerk/protocol/http/HttpChannelChild.h | 6 ++ 4 files changed, 92 insertions(+) diff --git a/netwerk/ipc/ChannelEventQueue.cpp b/netwerk/ipc/ChannelEventQueue.cpp index a4dbae7d5b5d..eff5f753f344 100644 --- a/netwerk/ipc/ChannelEventQueue.cpp +++ b/netwerk/ipc/ChannelEventQueue.cpp @@ -94,5 +94,20 @@ ChannelEventQueue::RetargetDeliveryTo(nsIEventTarget* aTargetThread) 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 diff --git a/netwerk/ipc/ChannelEventQueue.h b/netwerk/ipc/ChannelEventQueue.h index a843decabca1..09ed8bdee7b7 100644 --- a/netwerk/ipc/ChannelEventQueue.h +++ b/netwerk/ipc/ChannelEventQueue.h @@ -77,6 +77,11 @@ class ChannelEventQueue final // 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() diff --git a/netwerk/protocol/http/HttpChannelChild.cpp b/netwerk/protocol/http/HttpChannelChild.cpp index 25612061b3ad..aef5d24468fa 100644 --- a/netwerk/protocol/http/HttpChannelChild.cpp +++ b/netwerk/protocol/http/HttpChannelChild.cpp @@ -12,7 +12,9 @@ #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" @@ -20,6 +22,7 @@ #include "nsISupportsPrimitives.h" #include "nsChannelClassifier.h" +#include "nsGlobalWindow.h" #include "nsStringStream.h" #include "nsHttpHandler.h" #include "nsNetUtil.h" @@ -1212,6 +1215,10 @@ HttpChannelChild::RecvFinishInterceptedRedirect() RefPtr 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)); @@ -1621,6 +1628,11 @@ HttpChannelChild::ConnectParent(uint32_t registrarId) // 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(gNeckoChild->Manager()) ->GetBrowserOrId(tabChild); @@ -2020,6 +2032,55 @@ HttpChannelChild::AsyncOpen2(nsIStreamListener *aListener) 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 loadInfo; + GetLoadInfo(getter_AddRefs(loadInfo)); + if (!loadInfo) { + return; + } + + nsCOMPtr domDoc; + loadInfo->GetLoadingDocument(getter_AddRefs(domDoc)); + nsCOMPtr doc = do_QueryInterface(domDoc); + RefPtr 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 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 target = + dispatcher->EventTargetFor(TaskCategory::Network); + gNeckoChild->SetEventTargetForActor(this, target); + mEventQ->RetargetDeliveryTo(target); + } +} + nsresult HttpChannelChild::ContinueAsyncOpen() { @@ -2170,6 +2231,11 @@ HttpChannelChild::ContinueAsyncOpen() 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(); diff --git a/netwerk/protocol/http/HttpChannelChild.h b/netwerk/protocol/http/HttpChannelChild.h index dec24078e7cc..f83712fdd99e 100644 --- a/netwerk/protocol/http/HttpChannelChild.h +++ b/netwerk/protocol/http/HttpChannelChild.h @@ -182,6 +182,12 @@ private: nsAutoPtr 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);