Bug 1602318 - Associate a current DocumentLoadListener with CanonicalBrowsingContext. r=nika,necko-reviewers,dragana
authorMatt Woodrow <mwoodrow@mozilla.com>
Sun, 26 Apr 2020 00:49:19 +0000
changeset 526095 0b56956c62425763ad077209ec5c060cd003ae97
parent 526094 e9e35474d57aff2f1c4b5b7bceae642e87b334c5
child 526096 f64350664fbf25ef48abc71bd452084ef0505e81
push id37350
push usernbeleuzu@mozilla.com
push dateSun, 26 Apr 2020 09:43:12 +0000
treeherdermozilla-central@21659f178a12 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnika, necko-reviewers, dragana
bugs1602318
milestone77.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
Bug 1602318 - Associate a current DocumentLoadListener with CanonicalBrowsingContext. r=nika,necko-reviewers,dragana Differential Revision: https://phabricator.services.mozilla.com/D67094
docshell/base/CanonicalBrowsingContext.cpp
docshell/base/CanonicalBrowsingContext.h
netwerk/ipc/DocumentLoadListener.cpp
netwerk/ipc/DocumentLoadListener.h
--- a/docshell/base/CanonicalBrowsingContext.cpp
+++ b/docshell/base/CanonicalBrowsingContext.cpp
@@ -10,16 +10,17 @@
 #include "mozilla/dom/BrowsingContextGroup.h"
 #include "mozilla/dom/WindowGlobalParent.h"
 #include "mozilla/dom/ContentProcessManager.h"
 #include "mozilla/dom/MediaController.h"
 #include "mozilla/dom/MediaControlService.h"
 #include "mozilla/dom/PlaybackController.h"
 #include "mozilla/ipc/ProtocolUtils.h"
 #include "mozilla/NullPrincipal.h"
+#include "mozilla/net/DocumentLoadListener.h"
 
 #include "nsGlobalWindowOuter.h"
 
 using namespace mozilla::ipc;
 
 extern mozilla::LazyLogModule gAutoplayPermissionLog;
 
 #define AUTOPLAY_LOG(msg, ...) \
@@ -519,16 +520,27 @@ MediaController* CanonicalBrowsingContex
   // Only content browsing context can create media controller, we won't create
   // controller for chrome document, such as the browser UI.
   if (!mTabMediaController && !IsDiscarded() && IsContent()) {
     mTabMediaController = new MediaController(Id());
   }
   return mTabMediaController;
 }
 
+void CanonicalBrowsingContext::StartDocumentLoad(
+    net::DocumentLoadListener* aLoad) {
+  mCurrentLoad = aLoad;
+}
+void CanonicalBrowsingContext::EndDocumentLoad(
+    net::DocumentLoadListener* aLoad) {
+  if (mCurrentLoad == aLoad) {
+    mCurrentLoad = nullptr;
+  }
+}
+
 NS_IMPL_CYCLE_COLLECTION_INHERITED(CanonicalBrowsingContext, BrowsingContext,
                                    mSessionHistory)
 
 NS_IMPL_ADDREF_INHERITED(CanonicalBrowsingContext, BrowsingContext)
 NS_IMPL_RELEASE_INHERITED(CanonicalBrowsingContext, BrowsingContext)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CanonicalBrowsingContext)
 NS_INTERFACE_MAP_END_INHERITING(BrowsingContext)
--- a/docshell/base/CanonicalBrowsingContext.h
+++ b/docshell/base/CanonicalBrowsingContext.h
@@ -14,16 +14,20 @@
 #include "nsCycleCollectionParticipant.h"
 #include "nsWrapperCache.h"
 #include "nsTHashtable.h"
 #include "nsHashKeys.h"
 #include "nsISHistory.h"
 #include "nsISHEntry.h"
 
 namespace mozilla {
+namespace net {
+class DocumentLoadListener;
+}
+
 namespace dom {
 
 class WindowGlobalParent;
 class BrowserParent;
 class MediaController;
 class WindowGlobalParent;
 
 // CanonicalBrowsingContext is a BrowsingContext living in the parent
@@ -165,16 +169,25 @@ class CanonicalBrowsingContext final : p
     void Clear();
 
     RefPtr<CanonicalBrowsingContext> mTarget;
     RefPtr<RemotenessPromise::Private> mPromise;
 
     uint64_t mPendingSwitchId;
   };
 
+  friend class net::DocumentLoadListener;
+  // Called when a DocumentLoadListener is created to start a load for
+  // this browsing context.
+  void StartDocumentLoad(net::DocumentLoadListener* aLoad);
+  // Called once DocumentLoadListener completes handling a load, and it
+  // is either complete, or handed off to the final channel to deliver
+  // data to the destination docshell.
+  void EndDocumentLoad(net::DocumentLoadListener* aLoad);
+
   // XXX(farre): Store a ContentParent pointer here rather than mProcessId?
   // Indicates which process owns the docshell.
   uint64_t mProcessId;
 
   // Indicates which process owns the embedder element.
   uint64_t mEmbedderProcessId;
 
   // The ID of the former owner process during an ownership change, which may
@@ -186,16 +199,18 @@ class CanonicalBrowsingContext final : p
 
   nsCOMPtr<nsISHistory> mSessionHistory;
 
   // Tab media controller is used to control all media existing in the same
   // browsing context tree, so it would only exist in the top level browsing
   // context.
   RefPtr<MediaController> mTabMediaController;
 
+  RefPtr<net::DocumentLoadListener> mCurrentLoad;
+
   // These are being mirrored from docshell
   nsCOMPtr<nsISHEntry> mOSHE;
   nsCOMPtr<nsISHEntry> mLSHE;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
--- a/netwerk/ipc/DocumentLoadListener.cpp
+++ b/netwerk/ipc/DocumentLoadListener.cpp
@@ -28,16 +28,17 @@
 #include "nsExternalHelperAppService.h"
 #include "nsHttpChannel.h"
 #include "nsIBrowser.h"
 #include "nsIE10SUtils.h"
 #include "nsIStreamConverterService.h"
 #include "nsIViewSourceChannel.h"
 #include "nsImportModule.h"
 #include "nsMimeTypes.h"
+#include "mozilla/dom/CanonicalBrowsingContext.h"
 #include "nsRedirectHistoryEntry.h"
 #include "nsURILoader.h"
 #include "nsWebNavigationInfo.h"
 
 #ifdef ANDROID
 #  include "mozilla/widget/nsWindow.h"
 #endif /* ANDROID */
 
@@ -335,16 +336,23 @@ GetTopWindowExcludingExtensionAccessible
     prev = parent;
   }
   if (!prev) {
     prev = bc->GetCurrentWindowGlobal();
   }
   return prev.forget();
 }
 
+CanonicalBrowsingContext* DocumentLoadListener::GetBrowsingContext() {
+  if (!mParentChannelListener) {
+    return nullptr;
+  }
+  return mParentChannelListener->GetBrowsingContext();
+}
+
 bool DocumentLoadListener::Open(
     nsDocShellLoadState* aLoadState, nsLoadFlags aLoadFlags, uint32_t aCacheKey,
     const uint64_t& aChannelId, const TimeStamp& aAsyncOpenTime,
     nsDOMNavigationTiming* aTiming, Maybe<ClientInfo>&& aInfo,
     uint64_t aOuterWindowId, bool aHasGesture, nsresult* aRv) {
   LOG(("DocumentLoadListener Open [this=%p, uri=%s]", this,
        aLoadState->URI()->GetSpecOrDefault().get()));
   RefPtr<CanonicalBrowsingContext> browsingContext =
@@ -501,49 +509,69 @@ bool DocumentLoadListener::Open(
   }
 
   mChannelCreationURI = aLoadState->URI();
   mLoadStateLoadFlags = aLoadState->LoadFlags();
   mLoadStateLoadType = aLoadState->LoadType();
   mTiming = aTiming;
   mSrcdocData = aLoadState->SrcdocData();
   mBaseURI = aLoadState->BaseURI();
+
+  if (auto* ctx = GetBrowsingContext()) {
+    ctx->StartDocumentLoad(this);
+  }
   return true;
 }
 
 void DocumentLoadListener::DocumentChannelBridgeDisconnected() {
   LOG(("DocumentLoadListener DocumentChannelBridgeDisconnected [this=%p]",
        this));
   // The nsHttpChannel may have a reference to this parent, release it
   // to avoid circular references.
   RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(mChannel);
   if (httpChannelImpl) {
     httpChannelImpl->SetWarningReporter(nullptr);
   }
   mDocumentChannelBridge = nullptr;
+
+  if (auto* ctx = GetBrowsingContext()) {
+    ctx->EndDocumentLoad(this);
+  }
 }
 
 void DocumentLoadListener::Cancel(const nsresult& aStatusCode) {
   LOG(
       ("DocumentLoadListener Cancel [this=%p, "
        "aStatusCode=%" PRIx32 " ]",
        this, static_cast<uint32_t>(aStatusCode)));
-  if (mChannel && !mDoingProcessSwitch) {
+  if (mDoingProcessSwitch) {
+    // If we've already initiated process-switching
+    // then we can no longer be cancelled and we'll
+    // disconnect the old listeners when done.
+    return;
+  }
+
+  if (mChannel) {
     mChannel->Cancel(aStatusCode);
   }
+
+  DisconnectChildListeners(aStatusCode, aStatusCode);
 }
 
 void DocumentLoadListener::DisconnectChildListeners(nsresult aStatus,
                                                     nsresult aLoadGroupStatus) {
   LOG(
       ("DocumentLoadListener DisconnectChildListener [this=%p, "
        "aStatus=%" PRIx32 " aLoadGroupStatus=%" PRIx32 " ]",
        this, static_cast<uint32_t>(aStatus),
        static_cast<uint32_t>(aLoadGroupStatus)));
+  RefPtr<DocumentLoadListener> keepAlive(this);
   if (mDocumentChannelBridge) {
+    // This will drop the bridge's reference to us, so we use keepAlive to
+    // make sure we don't get deleted until we exit the function.
     mDocumentChannelBridge->DisconnectChildListeners(aStatus, aLoadGroupStatus);
   }
   DocumentChannelBridgeDisconnected();
 
   // If we're not going to send anything else to the content process, and
   // we haven't yet consumed a stream filter promise, then we're never going
   // to.
   // TODO: This might be because we retargeted the stream to the download
@@ -646,16 +674,19 @@ void DocumentLoadListener::FinishReplace
   // be kept in the registrar from this moment.
   registrar->DeregisterChannels(mRedirectChannelId);
   mRedirectChannelId = 0;
   if (!aSucceeded) {
     if (redirectChannel) {
       redirectChannel->Delete();
     }
     mChannel->Resume();
+    if (auto* ctx = GetBrowsingContext()) {
+      ctx->EndDocumentLoad(this);
+    }
     return;
   }
 
   MOZ_ASSERT(
       !SameCOMIdentity(redirectChannel, static_cast<nsIParentChannel*>(this)));
 
   Delete();
   redirectChannel->SetParentListener(mParentChannelListener);
@@ -826,16 +857,21 @@ bool DocumentLoadListener::ResumeSuspend
   }
   // We don't expect to get new stream listener functions added
   // via re-entrancy. If this ever happens, we should understand
   // exactly why before allowing it.
   NS_ASSERTION(mStreamListenerFunctions.IsEmpty(),
                "Should not have added new stream listener function!");
 
   mChannel->Resume();
+
+  if (auto* ctx = GetBrowsingContext()) {
+    ctx->EndDocumentLoad(this);
+  }
+
   return !mIsFinished;
 }
 
 void DocumentLoadListener::SerializeRedirectData(
     RedirectToRealChannelArgs& aArgs, bool aIsCrossProcess,
     uint32_t aRedirectFlags, uint32_t aLoadFlags) const {
   // Use the original URI of the current channel, as this is what
   // we'll use to construct the channel in the content process.
@@ -1285,46 +1321,34 @@ DocumentLoadListener::OnStartRequest(nsI
   if (multiPartChannel) {
     multiPartChannel->GetBaseChannel(getter_AddRefs(mChannel));
   } else {
     mChannel = do_QueryInterface(aRequest);
   }
   MOZ_DIAGNOSTIC_ASSERT(mChannel);
   RefPtr<nsHttpChannel> httpChannel = do_QueryObject(mChannel);
 
-  // If this is a download, then redirect entirely within the parent.
-  // TODO, see bug 1574372.
-
   if (!mDocumentChannelBridge) {
     return NS_ERROR_UNEXPECTED;
   }
 
   // Enforce CSP frame-ancestors and x-frame-options checks which
   // might cancel the channel.
   nsContentSecurityUtils::PerformCSPFrameAncestorAndXFOCheck(mChannel);
 
-  // Once we initiate a process switch, we ask the child to notify the
-  // listeners that we have completed. If the switch promise then gets
-  // rejected we also cancel the parent, which results in this being called.
-  // We don't need to forward it on though, since the child side is already
-  // completed.
-  if (mDoingProcessSwitch) {
-    return NS_OK;
-  }
-
   // Generally we want to switch to a real channel even if the request failed,
   // since the listener might want to access protocol-specific data (like http
   // response headers) in its error handling.
   // An exception to this is when nsExtProtocolChannel handled the request and
   // returned NS_ERROR_NO_CONTENT, since creating a real one in the content
   // process will attempt to handle the URI a second time.
   nsresult status = NS_OK;
   aRequest->GetStatus(&status);
   if (status == NS_ERROR_NO_CONTENT) {
-    mDocumentChannelBridge->DisconnectChildListeners(status, status);
+    DisconnectChildListeners(status, status);
     return NS_OK;
   }
 
   mStreamListenerFunctions.AppendElement(StreamListenerFunction{
       VariantIndex<0>{}, OnStartRequestParams{aRequest}});
 
   if (!mInitiatedRedirectToRealChannel) {
     mChannel->Suspend();
--- a/netwerk/ipc/DocumentLoadListener.h
+++ b/netwerk/ipc/DocumentLoadListener.h
@@ -9,32 +9,34 @@
 
 #include "mozilla/MozPromise.h"
 #include "mozilla/Variant.h"
 #include "mozilla/net/NeckoCommon.h"
 #include "mozilla/net/NeckoParent.h"
 #include "mozilla/net/PDocumentChannelParent.h"
 #include "mozilla/net/ParentChannelListener.h"
 #include "mozilla/net/ADocumentChannelBridge.h"
-#include "mozilla/dom/CanonicalBrowsingContext.h"
 #include "nsDOMNavigationTiming.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIParentChannel.h"
 #include "nsIParentRedirectingChannel.h"
 #include "nsIRedirectResultListener.h"
 #include "nsIMultiPartChannel.h"
 
 #define DOCUMENT_LOAD_LISTENER_IID                   \
   {                                                  \
     0x3b393c56, 0x9e01, 0x11e9, {                    \
       0xa2, 0xa3, 0x2a, 0x2a, 0xe2, 0xdb, 0xcc, 0xe4 \
     }                                                \
   }
 
 namespace mozilla {
+namespace dom {
+class CanonicalBrowsingContext;
+}
 namespace net {
 using ChildEndpointPromise =
     MozPromise<ipc::Endpoint<extensions::PStreamFilterChild>, bool, true>;
 
 // If we've been asked to attach a stream filter to our channel,
 // then we return this promise and defer until we know the final
 // content process. At that point we setup Endpoints between
 // mStramFilterProcessId and the new content process, and send
@@ -218,16 +220,18 @@ class DocumentLoadListener : public nsII
                         const Maybe<uint64_t>& aDestinationProcess,
                         nsTArray<ParentEndpoint>&& aStreamFilterEndpoints);
 
   // Construct a LoadInfo object to use for the internal channel.
   already_AddRefed<LoadInfo> CreateLoadInfo(
       dom::CanonicalBrowsingContext* aBrowsingContext,
       nsDocShellLoadState* aLoadState, uint64_t aOuterWindowId);
 
+  dom::CanonicalBrowsingContext* GetBrowsingContext();
+
   bool HasCrossOriginOpenerPolicyMismatch() const;
   void ApplyPendingFunctions(nsISupports* aChannel) const;
 
   // This defines a variant that describes all the attribute setters (and their
   // parameters) from nsIParentChannel
   //
   // NotifyFlashPluginStateChanged(nsIHttpChannel::FlashPluginState aState) = 0;
   // SetClassifierMatchedInfo(const nsACString& aList, const nsACString&