Bug 1597015 - Split DocumentChannelParent into two separate classes (DocumentLoadListener and DocumentChannelParent). r=mayhemer,jya
authorMatt Woodrow <mwoodrow@mozilla.com>
Wed, 20 Nov 2019 03:29:25 +0000
changeset 503083 e76dbab2aea8354660281221c1aa08356107881c
parent 503082 a6ea48838875c53f8639816068c220e42b4d3101
child 503084 e6eb852a873940ea78c2a588bfefc6bee72f03c3
child 503173 69a232cb693503d98c04ddc5acace1d930bf4898
push id101100
push usermwoodrow@mozilla.com
push dateWed, 20 Nov 2019 18:31:53 +0000
treeherderautoland@e76dbab2aea8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmayhemer, jya
bugs1597015
milestone72.0a1
first release with
nightly linux32
e76dbab2aea8 / 72.0a1 / 20191120215217 / files
nightly linux64
e76dbab2aea8 / 72.0a1 / 20191120215217 / files
nightly mac
e76dbab2aea8 / 72.0a1 / 20191120215217 / files
nightly win32
e76dbab2aea8 / 72.0a1 / 20191120215217 / files
nightly win64
e76dbab2aea8 / 72.0a1 / 20191120215217 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1597015 - Split DocumentChannelParent into two separate classes (DocumentLoadListener and DocumentChannelParent). r=mayhemer,jya Currently DocumentChannelParent serves two fairly separate functions. It acts as a logic controller for a connecting load (ultimately on behalf of a CanonicalBrowsingContext), that creates a channel and once it gets a response, figures out the right thing to do with it (send to the originating process, send to a new process, handle as a download etc). It's the parent-side IPDL actor for an nsIChannel implementation that we hold in the docshell as a placeholder while the above happens. This is largely a backwards compatibility hack since docshell expects to have a channel when a load has been initiated (but in the future we could do loads more directly through BrowsingContext and rewrite docshell to understand waiting on that). This patch splits this into two separate classes (and adds more documentation explaining exactly what each one does). It shouldn't affect any behaviour. Differential Revision: https://phabricator.services.mozilla.com/D53383
netwerk/ipc/ADocumentChannelBridge.h
netwerk/ipc/DocumentChannelParent.cpp
netwerk/ipc/DocumentChannelParent.h
netwerk/ipc/DocumentLoadListener.cpp
netwerk/ipc/DocumentLoadListener.h
netwerk/ipc/moz.build
netwerk/protocol/http/nsHttpChannel.cpp
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/ADocumentChannelBridge.h
@@ -0,0 +1,59 @@
+/* vim: set sw=2 ts=8 et tw=80 : */
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_net_ADocumentChannelBridge_h
+#define mozilla_net_ADocumentChannelBridge_h
+
+#include "mozilla/net/PDocumentChannelParent.h"
+
+namespace mozilla {
+namespace net {
+
+/**
+ * ADocumentChannelBridge is the interface for DocumentLoadListener to
+ * communicate with the nsIChannel placeholder in the docshell. It may be
+ * implemented over IPDL.
+ */
+class ADocumentChannelBridge {
+ public:
+  NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
+
+  // Notify the destination docshell that we're not going to send
+  // a response to it (usually because we've redirected to a different
+  // process).
+  // This should remove the nsIChannel from the loadgroup, and
+  // fire OnStart/StopRequest with aStatus.
+  virtual void DisconnectChildListeners(nsresult aStatus) = 0;
+
+  // Delete the bridge, and drop any refs to the DocumentLoadListener
+  virtual void Delete() = 0;
+
+  // Checks if we should allow a redirect to aNewURI.
+  // We need this because we currently can only do CSP checks in the content
+  // process in order to get the right events fired.
+  virtual RefPtr<PDocumentChannelParent::ConfirmRedirectPromise>
+  ConfirmRedirect(const LoadInfoArgs& aLoadInfo, nsIURI* aNewURI) = 0;
+
+  // Initate a switch from the DocumentChannel to the protocol-specific
+  // real channel.
+  virtual RefPtr<PDocumentChannelParent::RedirectToRealChannelPromise>
+  RedirectToRealChannel(uint32_t aRedirectFlags, uint32_t aLoadFlags) = 0;
+
+  // Returns the process id that this bridge is connected to.
+  virtual base::ProcessId OtherPid() const = 0;
+
+  // Attach a StreamFilterParent to the remote-side nsIChannel of this bridge.
+  virtual bool AttachStreamFilter(
+      ipc::Endpoint<mozilla::extensions::PStreamFilterParent>&& aEndpoint) = 0;
+
+ protected:
+  virtual ~ADocumentChannelBridge() = default;
+};
+
+}  // namespace net
+}  // namespace mozilla
+
+#endif  // mozilla_net_ADocumentChannelBridge_h
--- a/netwerk/ipc/DocumentChannelParent.cpp
+++ b/netwerk/ipc/DocumentChannelParent.cpp
@@ -1,938 +1,45 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set sw=2 ts=8 et tw=80 : */
 
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DocumentChannelParent.h"
-#include "mozilla/DebugOnly.h"
-#include "mozilla/LoadInfo.h"
-#include "mozilla/MozPromiseInlines.h"  // For MozPromise::FromDomPromise
-#include "mozilla/dom/BrowserParent.h"
-#include "mozilla/dom/ClientChannelHelper.h"
-#include "mozilla/dom/ContentParent.h"
-#include "mozilla/dom/ContentProcessManager.h"
-#include "mozilla/dom/ipc/IdType.h"
-#include "mozilla/dom/ServiceWorkerManager.h"
-#include "mozilla/net/HttpChannelParent.h"
-#include "mozilla/net/RedirectChannelRegistrar.h"
-#include "nsDocShell.h"
-#include "nsDocShellLoadState.h"
-#include "nsHttpChannel.h"
-#include "nsIHttpProtocolHandler.h"
-#include "nsISecureBrowserUI.h"
-#include "nsRedirectHistoryEntry.h"
-#include "nsSerializationHelper.h"
-#include "nsIPrompt.h"
-#include "nsIWindowWatcher.h"
-
-using namespace mozilla::dom;
 
 namespace mozilla {
 namespace net {
 
-NS_IMPL_ADDREF(DocumentChannelParent)
-NS_IMPL_RELEASE(DocumentChannelParent)
-
-NS_INTERFACE_MAP_BEGIN(DocumentChannelParent)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInterfaceRequestor)
-  NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
-  NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
-  NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
-  NS_INTERFACE_MAP_ENTRY(nsIParentChannel)
-  NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectReadyCallback)
-  NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
-  NS_INTERFACE_MAP_ENTRY(nsIProcessSwitchRequestor)
-  NS_INTERFACE_MAP_ENTRY_CONCRETE(DocumentChannelParent)
-NS_INTERFACE_MAP_END
-
-DocumentChannelParent::DocumentChannelParent(
-    const PBrowserOrId& iframeEmbedding, nsILoadContext* aLoadContext,
-    PBOverrideStatus aOverrideStatus)
-    : mLoadContext(aLoadContext), mPBOverride(aOverrideStatus) {
-  RefPtr<dom::BrowserParent> parent;
-  if (iframeEmbedding.type() == PBrowserOrId::TPBrowserParent) {
-    parent =
-        static_cast<dom::BrowserParent*>(iframeEmbedding.get_PBrowserParent());
-  }
-  mListener = new ParentChannelListener(this, parent);
-}
-
 bool DocumentChannelParent::Init(const DocumentChannelCreationArgs& aArgs) {
   RefPtr<nsDocShellLoadState> loadState =
       new nsDocShellLoadState(aArgs.loadState());
 
-  RefPtr<LoadInfo> loadInfo;
+  RefPtr<class LoadInfo> loadInfo;
   nsresult rv = mozilla::ipc::LoadInfoArgsToLoadInfo(Some(aArgs.loadInfo()),
                                                      getter_AddRefs(loadInfo));
   MOZ_ASSERT(NS_SUCCEEDED(rv));
 
-  bool result = nsDocShell::CreateChannelForLoadState(
-      loadState, loadInfo, mListener, nullptr,
-      aArgs.initiatorType().ptrOr(nullptr), aArgs.loadFlags(), aArgs.loadType(),
-      aArgs.cacheKey(), aArgs.isActive(), aArgs.isTopLevelDoc(),
-      aArgs.hasNonEmptySandboxingFlags(), rv, getter_AddRefs(mChannel));
-  if (!result) {
-    mListener = nullptr;
+  rv = NS_ERROR_UNEXPECTED;
+  if (!mParent->Open(loadState, loadInfo, aArgs.initiatorType().ptrOr(nullptr),
+                     aArgs.loadFlags(), aArgs.loadType(), aArgs.cacheKey(),
+                     aArgs.isActive(), aArgs.isTopLevelDoc(),
+                     aArgs.hasNonEmptySandboxingFlags(), aArgs.topWindowURI(),
+                     aArgs.contentBlockingAllowListPrincipal(),
+                     aArgs.customUserAgent(), aArgs.channelId(),
+                     aArgs.asyncOpenTime(), &rv)) {
     return SendFailedAsyncOpen(rv);
   }
 
-  nsDocShell::ConfigureChannel(
-      mChannel, loadState, aArgs.initiatorType().ptrOr(nullptr),
-      aArgs.loadType(), aArgs.cacheKey(), aArgs.hasNonEmptySandboxingFlags());
-
-  // Computation of the top window uses the docshell tree, so only
-  // works in the source process. We compute it manually and override
-  // it so that it gets the right value.
-  // This probably isn't fission compatible.
-  RefPtr<HttpBaseChannel> httpBaseChannel = do_QueryObject(mChannel, &rv);
-  if (httpBaseChannel) {
-    nsCOMPtr<nsIURI> topWindowURI = DeserializeURI(aArgs.topWindowURI());
-    rv = httpBaseChannel->SetTopWindowURI(topWindowURI);
-    MOZ_ASSERT(NS_SUCCEEDED(rv));
-
-    if (aArgs.contentBlockingAllowListPrincipal()) {
-      nsCOMPtr<nsIPrincipal> contentBlockingAllowListPrincipal =
-          PrincipalInfoToPrincipal(*aArgs.contentBlockingAllowListPrincipal());
-      httpBaseChannel->SetContentBlockingAllowListPrincipal(
-          contentBlockingAllowListPrincipal);
-    }
-
-    // The content process docshell can specify a custom override
-    // for the user agent value. If we had one, then add it to
-    // the header now.
-    if (!aArgs.customUserAgent().IsEmpty()) {
-      NS_ConvertUTF16toUTF8 utf8CustomUserAgent(aArgs.customUserAgent());
-      rv = httpBaseChannel->SetRequestHeader(NS_LITERAL_CSTRING("User-Agent"),
-                                             utf8CustomUserAgent, false);
-    }
-  }
-
-  nsCOMPtr<nsIPrivateBrowsingChannel> privateChannel =
-      do_QueryInterface(mChannel);
-  if (mPBOverride != kPBOverride_Unset) {
-    privateChannel->SetPrivate(mPBOverride == kPBOverride_Private);
-  }
-
-  nsCOMPtr<nsIIdentChannel> identChannel = do_QueryInterface(mChannel);
-  if (identChannel) {
-    Unused << identChannel->SetChannelId(aArgs.channelId());
-  }
-
-  RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(mChannel);
-  if (httpChannelImpl) {
-    httpChannelImpl->SetWarningReporter(this);
-  }
-
-  nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(mChannel);
-  if (timedChannel) {
-    timedChannel->SetAsyncOpen(aArgs.asyncOpenTime());
-  }
-
-  // Setup a ClientChannelHelper to watch for redirects, and copy
-  // across any serviceworker related data between channels as needed.
-  AddClientChannelHelperInParent(mChannel, GetMainThreadSerialEventTarget());
-
-  rv = mChannel->AsyncOpen(mListener);
-  if (NS_FAILED(rv)) {
-    mListener = nullptr;
-    return SendFailedAsyncOpen(rv);
-  }
-
-  mChannelCreationURI = loadState->URI();
-
   return true;
 }
 
-void DocumentChannelParent::ActorDestroy(ActorDestroyReason why) {
-  // 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);
-  }
-}
-
-bool DocumentChannelParent::RecvCancel(const nsresult& aStatusCode) {
-  if (mChannel && !mDoingProcessSwitch) {
-    mChannel->Cancel(aStatusCode);
-  }
-  return true;
-}
-
-bool DocumentChannelParent::RecvSuspend() {
-  if (mChannel && !mDoingProcessSwitch) {
-    mChannel->Suspend();
-  }
-  return true;
-}
-
-bool DocumentChannelParent::RecvResume() {
-  if (mChannel && !mDoingProcessSwitch) {
-    mChannel->Resume();
-  }
-  return true;
-}
-
-void DocumentChannelParent::RedirectToRealChannelFinished(nsresult aRv) {
-  if (NS_FAILED(aRv)) {
-    FinishReplacementChannelSetup(false);
-    return;
-  }
-
-  // Wait for background channel ready on target channel
-  nsCOMPtr<nsIRedirectChannelRegistrar> redirectReg =
-      RedirectChannelRegistrar::GetOrCreate();
-  MOZ_ASSERT(redirectReg);
-
-  nsCOMPtr<nsIParentChannel> redirectParentChannel;
-  redirectReg->GetParentChannel(mRedirectChannelId,
-                                getter_AddRefs(redirectParentChannel));
-  if (!redirectParentChannel) {
-    FinishReplacementChannelSetup(false);
-    return;
-  }
-
-  nsCOMPtr<nsIParentRedirectingChannel> redirectingParent =
-      do_QueryInterface(redirectParentChannel);
-  if (!redirectingParent) {
-    // Continue verification procedure if redirecting to non-Http protocol
-    FinishReplacementChannelSetup(true);
-    return;
-  }
-
-  // Ask redirected channel if verification can proceed.
-  // ReadyToVerify will be invoked when redirected channel is ready.
-  redirectingParent->ContinueVerification(this);
-  return;
-}
-
-NS_IMETHODIMP
-DocumentChannelParent::ReadyToVerify(nsresult aResultCode) {
-  FinishReplacementChannelSetup(NS_SUCCEEDED(aResultCode));
-  return NS_OK;
-}
-
-void DocumentChannelParent::FinishReplacementChannelSetup(bool aSucceeded) {
-  nsresult rv;
-
-  if (mDoingProcessSwitch && CanSend()) {
-    Unused << SendDisconnectChildListeners(NS_BINDING_ABORTED);
-  }
-
-  nsCOMPtr<nsIParentChannel> redirectChannel;
-  if (mRedirectChannelId) {
-    nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
-        RedirectChannelRegistrar::GetOrCreate();
-    MOZ_ASSERT(registrar);
-
-    rv = registrar->GetParentChannel(mRedirectChannelId,
-                                     getter_AddRefs(redirectChannel));
-    if (NS_FAILED(rv) || !redirectChannel) {
-      // Redirect might get canceled before we got AsyncOnChannelRedirect
-      nsCOMPtr<nsIChannel> newChannel;
-      rv = registrar->GetRegisteredChannel(mRedirectChannelId,
-                                           getter_AddRefs(newChannel));
-      MOZ_ASSERT(newChannel, "Already registered channel not found");
-
-      if (NS_SUCCEEDED(rv)) {
-        newChannel->Cancel(NS_BINDING_ABORTED);
-      }
-    }
-    // Release all previously registered channels, they are no longer needed to
-    // be kept in the registrar from this moment.
-    registrar->DeregisterChannels(mRedirectChannelId);
-
-    mRedirectChannelId = 0;
-  }
-
-  if (!redirectChannel) {
-    aSucceeded = false;
-  }
-
-  if (!aSucceeded) {
-    if (redirectChannel) {
-      redirectChannel->Delete();
-    }
-    if (mSuspendedChannel) {
-      mChannel->Resume();
-    }
-    return;
-  }
-
-  MOZ_ASSERT(
-      !SameCOMIdentity(redirectChannel, static_cast<nsIParentChannel*>(this)));
-
-  Delete();
-  if (!mStopRequestValue) {
-    mListener->SetListenerAfterRedirect(redirectChannel);
-  }
-  redirectChannel->SetParentListener(mListener);
-
-  // We stored the values from all nsIParentChannel functions called since we
-  // couldn't handle them. Copy them across to the real channel since it should
-  // know what to do.
-  for (auto& variant : mIParentChannelFunctions) {
-    variant.match(
-        [redirectChannel](const nsIHttpChannel::FlashPluginState& aState) {
-          redirectChannel->NotifyFlashPluginStateChanged(aState);
-        },
-        [redirectChannel](const ClassifierMatchedInfoParams& aParams) {
-          redirectChannel->SetClassifierMatchedInfo(
-              aParams.mList, aParams.mProvider, aParams.mFullHash);
-        },
-        [redirectChannel](const ClassifierMatchedTrackingInfoParams& aParams) {
-          redirectChannel->SetClassifierMatchedTrackingInfo(
-              aParams.mLists, aParams.mFullHashes);
-        },
-        [redirectChannel](const ClassificationFlagsParams& aParams) {
-          redirectChannel->NotifyClassificationFlags(
-              aParams.mClassificationFlags, aParams.mIsThirdParty);
-        },
-        [redirectChannel](
-            const NotifyChannelClassifierProtectionDisabledParams& aParams) {
-          redirectChannel->NotifyChannelClassifierProtectionDisabled(
-              aParams.mAcceptedReason);
-        },
-        [redirectChannel](const NotifyCookieAllowedParams&) {
-          redirectChannel->NotifyCookieAllowed();
-        },
-        [redirectChannel](const NotifyCookieBlockedParams& aParams) {
-          redirectChannel->NotifyCookieBlocked(aParams.mRejectedReason);
-        });
-  }
-
-  RefPtr<HttpChannelParent> httpParent = do_QueryObject(redirectChannel);
-  if (httpParent) {
-    RefPtr<HttpChannelSecurityWarningReporter> reporter = httpParent;
-    for (auto& variant : mSecurityWarningFunctions) {
-      variant.match(
-          [reporter](const ReportSecurityMessageParams& aParams) {
-            Unused << reporter->ReportSecurityMessage(aParams.mMessageTag,
-                                                      aParams.mMessageCategory);
-          },
-          [reporter](const LogBlockedCORSRequestParams& aParams) {
-            Unused << reporter->LogBlockedCORSRequest(aParams.mMessage,
-                                                      aParams.mCategory);
-          },
-          [reporter](const LogMimeTypeMismatchParams& aParams) {
-            Unused << reporter->LogMimeTypeMismatch(
-                aParams.mMessageName, aParams.mWarning, aParams.mURL,
-                aParams.mContentType);
-          });
-    }
-  }
-
-  if (mSuspendedChannel) {
-    nsTArray<OnDataAvailableRequest> pendingRequests =
-        std::move(mPendingRequests);
-    MOZ_ASSERT(mPendingRequests.IsEmpty());
-
-    nsCOMPtr<nsHttpChannel> httpChannel = do_QueryInterface(mChannel);
-    if (httpChannel) {
-      httpChannel->SetApplyConversion(mOldApplyConversion);
-    }
-    rv = redirectChannel->OnStartRequest(mChannel);
-    if (NS_FAILED(rv)) {
-      mChannel->Cancel(rv);
-    }
-
-    // If we failed to suspend the channel, then we might have received
-    // some messages while the redirected was being handled.
-    // Manually send them on now.
-    if (NS_SUCCEEDED(rv) &&
-        (!mStopRequestValue || NS_SUCCEEDED(*mStopRequestValue))) {
-      for (auto& request : pendingRequests) {
-        nsCOMPtr<nsIInputStream> stringStream;
-        rv = NS_NewByteInputStream(
-            getter_AddRefs(stringStream),
-            Span<const char>(request.data.get(), request.count),
-            NS_ASSIGNMENT_DEPEND);
-        if (NS_SUCCEEDED(rv)) {
-          rv = redirectChannel->OnDataAvailable(mChannel, stringStream,
-                                                request.offset, request.count);
-        }
-        if (NS_FAILED(rv)) {
-          mChannel->Cancel(rv);
-          mStopRequestValue = Some(rv);
-          break;
-        }
-      }
-    }
-
-    if (mStopRequestValue) {
-      redirectChannel->OnStopRequest(mChannel, *mStopRequestValue);
-    }
-
-    mChannel->Resume();
-  }
-}
-
-void DocumentChannelParent::SerializeRedirectData(
-    RedirectToRealChannelArgs& aArgs, bool aIsCrossProcess,
-    uint32_t aRedirectFlags, uint32_t aLoadFlags) {
-  // Use the original URI of the current channel, as this is what
-  // we'll use to construct the channel in the content process.
-  aArgs.uri() = mChannelCreationURI;
-  if (!aArgs.uri()) {
-    mChannel->GetOriginalURI(getter_AddRefs(aArgs.uri()));
-  }
-
-  // I previously used HttpBaseChannel::CloneLoadInfoForRedirect, but that
-  // clears the principal to inherit, which fails tests (probably because this
-  // 'redirect' is usually just an implementation detail). It's also http only,
-  // and mChannel can be anything that we redirected to.
-  nsCOMPtr<nsILoadInfo> channelLoadInfo;
-  mChannel->GetLoadInfo(getter_AddRefs(channelLoadInfo));
-
-  nsCOMPtr<nsIPrincipal> principalToInherit;
-  channelLoadInfo->GetPrincipalToInherit(getter_AddRefs(principalToInherit));
-
-  RefPtr<nsHttpChannel> baseChannel = do_QueryObject(mChannel);
-  nsCOMPtr<nsILoadInfo> redirectLoadInfo;
-  if (baseChannel) {
-    redirectLoadInfo = baseChannel->CloneLoadInfoForRedirect(
-        aArgs.uri(), nsIChannelEventSink::REDIRECT_INTERNAL);
-    redirectLoadInfo->SetResultPrincipalURI(aArgs.uri());
-
-    // The clone process clears this, and then we fail tests..
-    // docshell/test/mochitest/test_forceinheritprincipal_overrule_owner.html
-    if (principalToInherit) {
-      redirectLoadInfo->SetPrincipalToInherit(principalToInherit);
-    }
-  } else {
-    redirectLoadInfo =
-        static_cast<mozilla::net::LoadInfo*>(channelLoadInfo.get())->Clone();
-
-    nsCOMPtr<nsIPrincipal> uriPrincipal;
-    nsIScriptSecurityManager* sm = nsContentUtils::GetSecurityManager();
-    sm->GetChannelURIPrincipal(mChannel, getter_AddRefs(uriPrincipal));
-
-    nsCOMPtr<nsIRedirectHistoryEntry> entry =
-        new nsRedirectHistoryEntry(uriPrincipal, nullptr, EmptyCString());
-
-    redirectLoadInfo->AppendRedirectHistoryEntry(entry, true);
-  }
-
-  if (!aIsCrossProcess) {
-    // When we're switching into a new process, the destination
-    // docshell will create a new initial client source which conflicts
-    // with this. We should probably use this one, but need to update
-    // docshell to understand that.
-    const Maybe<ClientInfo>& reservedClientInfo =
-        channelLoadInfo->GetReservedClientInfo();
-    if (reservedClientInfo) {
-      redirectLoadInfo->SetReservedClientInfo(*reservedClientInfo);
-    }
-  }
-
-  // Register the new channel and obtain id for it
-  nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
-      RedirectChannelRegistrar::GetOrCreate();
-  MOZ_ASSERT(registrar);
-  nsresult rv = registrar->RegisterChannel(mChannel, &mRedirectChannelId);
-  NS_ENSURE_SUCCESS_VOID(rv);
-  aArgs.registrarId() = mRedirectChannelId;
-
-  MOZ_ALWAYS_SUCCEEDS(
-      ipc::LoadInfoToLoadInfoArgs(redirectLoadInfo, &aArgs.loadInfo()));
-
-  mChannel->GetOriginalURI(getter_AddRefs(aArgs.originalURI()));
-
-  // mChannel can be a nsHttpChannel as well as InterceptedHttpChannel so we
-  // can't use baseChannel here.
-  if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel)) {
-    MOZ_ALWAYS_SUCCEEDS(httpChannel->GetChannelId(&aArgs.channelId()));
-  }
-
-  aArgs.redirectMode() = nsIHttpChannelInternal::REDIRECT_MODE_FOLLOW;
-  nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
-      do_QueryInterface(mChannel);
-  if (httpChannelInternal) {
-    MOZ_ALWAYS_SUCCEEDS(
-        httpChannelInternal->GetRedirectMode(&aArgs.redirectMode()));
-  }
-
-  if (baseChannel) {
-    uint32_t loadFlags = 0;
-    if (!aIsCrossProcess) {
-      loadFlags |= nsIChannel::LOAD_REPLACE;
-    }
-
-    aArgs.init() = Some(
-        baseChannel
-            ->CloneReplacementChannelConfig(true, aRedirectFlags, loadFlags)
-            .Serialize());
-  }
-
-  uint32_t contentDispositionTemp;
-  rv = mChannel->GetContentDisposition(&contentDispositionTemp);
-  if (NS_SUCCEEDED(rv)) {
-    aArgs.contentDisposition() = Some(contentDispositionTemp);
-  }
-
-  nsString contentDispositionFilenameTemp;
-  rv = mChannel->GetContentDispositionFilename(contentDispositionFilenameTemp);
-  if (NS_SUCCEEDED(rv)) {
-    aArgs.contentDispositionFilename() = Some(contentDispositionFilenameTemp);
-  }
-
-  aArgs.newLoadFlags() = aLoadFlags;
-  aArgs.redirectFlags() = aRedirectFlags;
-  aArgs.redirects() = mRedirects;
-  aArgs.redirectIdentifier() = mCrossProcessRedirectIdentifier;
-}
-
-void DocumentChannelParent::TriggerCrossProcessSwitch() {
-  MOZ_ASSERT(mRedirectContentProcessIdPromise);
-  MOZ_ASSERT(!mDoingProcessSwitch, "Already in the middle of switching?");
-  MOZ_ASSERT(NS_IsMainThread());
-
-  mDoingProcessSwitch = true;
-
-  RefPtr<DocumentChannelParent> self = this;
-  mRedirectContentProcessIdPromise->Then(
-      GetMainThreadSerialEventTarget(), __func__,
-      [self, this](uint64_t aCpId) {
-        MOZ_ASSERT(mChannel, "Something went wrong, channel got cancelled");
-        TriggerRedirectToRealChannel(Some(aCpId));
-      },
-      [self](nsresult aStatusCode) {
-        MOZ_ASSERT(NS_FAILED(aStatusCode), "Status should be error");
-        self->RedirectToRealChannelFinished(aStatusCode);
-      });
-}
-
 RefPtr<PDocumentChannelParent::RedirectToRealChannelPromise>
-DocumentChannelParent::RedirectToRealChannel(
-    uint32_t aRedirectFlags, uint32_t aLoadFlags,
-    const Maybe<uint64_t>& aDestinationProcess) {
+DocumentChannelParent::RedirectToRealChannel(uint32_t aRedirectFlags,
+                                             uint32_t aLoadFlags) {
   RedirectToRealChannelArgs args;
-  SerializeRedirectData(args, !!aDestinationProcess, aRedirectFlags,
-                        aLoadFlags);
-
-  if (aDestinationProcess) {
-    dom::ContentParent* cp =
-        dom::ContentProcessManager::GetSingleton()->GetContentProcessById(
-            ContentParentId{*aDestinationProcess});
-    if (!cp) {
-      return PDocumentChannelParent::RedirectToRealChannelPromise::
-          CreateAndReject(ipc::ResponseRejectReason::SendError, __func__);
-    }
-
-    return cp->SendCrossProcessRedirect(args);
-  }
+  mParent->SerializeRedirectData(args, false, aRedirectFlags, aLoadFlags);
   return SendRedirectToRealChannel(args);
 }
 
-void DocumentChannelParent::TriggerRedirectToRealChannel(
-    const Maybe<uint64_t>& aDestinationProcess) {
-  // This initiates replacing the current DocumentChannel with a
-  // protocol specific 'real' channel, maybe in a different process than
-  // the current DocumentChannelChild, if aDestinationProces is set.
-  // It registers the current mChannel with the registrar to get an ID
-  // so that the remote end can setup a new IPDL channel and lookup
-  // the same underlying channel.
-  // We expect this process to finish with FinishReplacementChannelSetup
-  // (for both in-process and process switch cases), where we cleanup
-  // the registrar and copy across any needed state to the replacing
-  // IPDL parent object.
-
-  // If we didn't have any redirects, then we pass the REDIRECT_INTERNAL flag
-  // for this channel switch so that it isn't recorded in session history etc.
-  // If there were redirect(s), then we want this switch to be recorded as a
-  // real one, since we have a new URI.
-  uint32_t redirectFlags = 0;
-  if (mRedirects.IsEmpty()) {
-    redirectFlags = nsIChannelEventSink::REDIRECT_INTERNAL;
-  }
-
-  uint32_t newLoadFlags = nsIRequest::LOAD_NORMAL;
-  MOZ_ALWAYS_SUCCEEDS(mChannel->GetLoadFlags(&newLoadFlags));
-  if (!aDestinationProcess) {
-    newLoadFlags |= nsIChannel::LOAD_REPLACE;
-  }
-
-  RefPtr<DocumentChannelParent> self = this;
-  RedirectToRealChannel(redirectFlags, newLoadFlags, aDestinationProcess)
-      ->Then(
-          GetCurrentThreadSerialEventTarget(), __func__,
-          [self](Tuple<nsresult, Maybe<LoadInfoArgs>>&& aResponse) {
-            if (NS_SUCCEEDED(Get<0>(aResponse))) {
-              nsCOMPtr<nsILoadInfo> newLoadInfo;
-              MOZ_ALWAYS_SUCCEEDS(LoadInfoArgsToLoadInfo(
-                  Get<1>(aResponse), getter_AddRefs(newLoadInfo)));
-
-              if (newLoadInfo) {
-                // Since the old reservedClientInfo might be controlled by a
-                // ServiceWorker when opening the channel, we need to update
-                // the controlled ClientInfo in ServiceWorkerManager to make
-                // sure the same origin subresource load can be intercepted by
-                // the ServiceWorker.
-                nsCOMPtr<nsILoadInfo> oldLoadInfo;
-                self->mChannel->GetLoadInfo(getter_AddRefs(oldLoadInfo));
-                MOZ_ASSERT(oldLoadInfo);
-                Maybe<ClientInfo> oldClientInfo =
-                    oldLoadInfo->GetReservedClientInfo();
-                Maybe<ServiceWorkerDescriptor> oldController =
-                    oldLoadInfo->GetController();
-                Maybe<ClientInfo> newClientInfo =
-                    newLoadInfo->GetReservedClientInfo();
-                Maybe<ServiceWorkerDescriptor> newController =
-                    newLoadInfo->GetController();
-
-                if (oldClientInfo.isSome() && newClientInfo.isSome() &&
-                    newController.isSome() && oldController.isSome() &&
-                    newController.ref() == oldController.ref()) {
-                  RefPtr<ServiceWorkerManager> swMgr =
-                      ServiceWorkerManager::GetInstance();
-                  MOZ_ASSERT(swMgr);
-                  swMgr->UpdateControlledClient(oldClientInfo.ref(),
-                                                newClientInfo.ref(),
-                                                newController.ref());
-                }
-
-                self->mChannel->SetLoadInfo(newLoadInfo);
-              }
-            }
-            self->RedirectToRealChannelFinished(Get<0>(aResponse));
-          },
-          [self](const mozilla::ipc::ResponseRejectReason) {
-            self->RedirectToRealChannelFinished(NS_ERROR_FAILURE);
-          });
-}
-
-NS_IMETHODIMP
-DocumentChannelParent::OnStartRequest(nsIRequest* aRequest) {
-  nsCOMPtr<nsHttpChannel> channel = do_QueryInterface(aRequest);
-  mChannel = do_QueryInterface(aRequest);
-  MOZ_DIAGNOSTIC_ASSERT(mChannel);
-
-  // If this is a download, then redirect entirely within the parent.
-  // TODO, see bug 1574372.
-
-  if (!CanSend()) {
-    return NS_ERROR_UNEXPECTED;
-  }
-
-  // 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;
-  }
-
-  mChannel->Suspend();
-  mSuspendedChannel = true;
-
-  // The caller of this OnStartRequest will install a conversion
-  // helper after we return if we haven't disabled conversion. Normally
-  // HttpChannelParent::OnStartRequest would disable conversion, but we're
-  // defering calling that until later. Manually disable it now to prevent the
-  // converter from being installed (since we want the child to do it), and
-  // also save the value so that when we do call
-  // HttpChannelParent::OnStartRequest, we can have the value as it originally
-  // was.
-  if (channel) {
-    Unused << channel->GetApplyConversion(&mOldApplyConversion);
-    channel->SetApplyConversion(false);
-
-    // notify "http-on-may-change-process" observers which is typically
-    // SessionStore.jsm. This will determine if a new process needs to be
-    // spawned and if so SwitchProcessTo() will be called which will set a
-    // ContentProcessIdPromise.
-    gHttpHandler->OnMayChangeProcess(this);
-    if (mRedirectContentProcessIdPromise) {
-      TriggerCrossProcessSwitch();
-      return NS_OK;
-    }
-  }
-
-  TriggerRedirectToRealChannel();
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-DocumentChannelParent::OnStopRequest(nsIRequest* aRequest,
-                                     nsresult aStatusCode) {
-  mStopRequestValue = Some(aStatusCode);
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-DocumentChannelParent::OnDataAvailable(nsIRequest* aRequest,
-                                       nsIInputStream* aInputStream,
-                                       uint64_t aOffset, uint32_t aCount) {
-  // This isn't supposed to happen, since we suspended the channel, but
-  // sometimes Suspend just doesn't work. This can happen when we're routing
-  // through nsUnknownDecoder to sniff the content type, and it doesn't handle
-  // being suspended. Let's just store the data and manually forward it to our
-  // redirected channel when it's ready.
-  nsCString data;
-  nsresult rv = NS_ReadInputStreamToString(aInputStream, data, aCount);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  mPendingRequests.AppendElement(
-      OnDataAvailableRequest({data, aOffset, aCount}));
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-DocumentChannelParent::SetParentListener(
-    mozilla::net::ParentChannelListener* listener) {
-  // We don't need this (do we?)
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-DocumentChannelParent::GetInterface(const nsIID& aIID, void** result) {
-  // Only support nsILoadContext if child channel's callbacks did too
-  if (aIID.Equals(NS_GET_IID(nsILoadContext)) && mLoadContext) {
-    nsCOMPtr<nsILoadContext> copy = mLoadContext;
-    copy.forget(result);
-    return NS_OK;
-  }
-
-  return QueryInterface(aIID, result);
-}
-
-// Rather than forwarding all these nsIParentChannel functions to the child, we
-// cache a list of them, and then ask the 'real' channel to forward them for us
-// after it's created.
-NS_IMETHODIMP
-DocumentChannelParent::NotifyFlashPluginStateChanged(
-    nsIHttpChannel::FlashPluginState aState) {
-  mIParentChannelFunctions.AppendElement(
-      IParentChannelFunction{VariantIndex<0>{}, aState});
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-DocumentChannelParent::SetClassifierMatchedInfo(const nsACString& aList,
-                                                const nsACString& aProvider,
-                                                const nsACString& aFullHash) {
-  ClassifierMatchedInfoParams params;
-  params.mList = aList;
-  params.mProvider = aProvider;
-  params.mFullHash = aFullHash;
-
-  mIParentChannelFunctions.AppendElement(
-      IParentChannelFunction{VariantIndex<1>{}, std::move(params)});
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-DocumentChannelParent::SetClassifierMatchedTrackingInfo(
-    const nsACString& aLists, const nsACString& aFullHash) {
-  ClassifierMatchedTrackingInfoParams params;
-  params.mLists = aLists;
-  params.mFullHashes = aFullHash;
-
-  mIParentChannelFunctions.AppendElement(
-      IParentChannelFunction{VariantIndex<2>{}, std::move(params)});
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-DocumentChannelParent::NotifyClassificationFlags(uint32_t aClassificationFlags,
-                                                 bool aIsThirdParty) {
-  mIParentChannelFunctions.AppendElement(IParentChannelFunction{
-      VariantIndex<3>{},
-      ClassificationFlagsParams{aClassificationFlags, aIsThirdParty}});
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-DocumentChannelParent::NotifyChannelClassifierProtectionDisabled(
-    uint32_t aAcceptedReason) {
-  mIParentChannelFunctions.AppendElement(IParentChannelFunction{
-      VariantIndex<4>{},
-      NotifyChannelClassifierProtectionDisabledParams{aAcceptedReason}});
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-DocumentChannelParent::NotifyCookieAllowed() {
-  mIParentChannelFunctions.AppendElement(
-      IParentChannelFunction{VariantIndex<5>{}, NotifyCookieAllowedParams()});
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-DocumentChannelParent::NotifyCookieBlocked(uint32_t aRejectedReason) {
-  mIParentChannelFunctions.AppendElement(IParentChannelFunction{
-      VariantIndex<6>{}, NotifyCookieBlockedParams{aRejectedReason}});
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-DocumentChannelParent::Delete() {
-  // TODO - not sure we need it, but should delete the child or call on the
-  // child to release the parent.
-  if (!CanSend()) {
-    return NS_ERROR_UNEXPECTED;
-  }
-  Unused << SendDeleteSelf();
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-DocumentChannelParent::AsyncOnChannelRedirect(
-    nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aFlags,
-    nsIAsyncVerifyRedirectCallback* aCallback) {
-  // We generally don't want to notify the content process about redirects,
-  // so just update our channel and tell the callback that we're good to go.
-  mChannel = aNewChannel;
-
-  // We need the original URI of the current channel to use to open the real
-  // channel in the content process. Unfortunately we overwrite the original
-  // uri of the new channel with the original pre-redirect URI, so grab
-  // a copy of it now.
-  aNewChannel->GetOriginalURI(getter_AddRefs(mChannelCreationURI));
-
-  // Since we're redirecting away from aOldChannel, we should check if it
-  // had a COOP mismatch, since we want the final result for this to
-  // include the state of all channels we redirected through.
-  nsCOMPtr<nsHttpChannel> httpChannel = do_QueryInterface(aOldChannel);
-  if (httpChannel) {
-    bool mismatch = false;
-    MOZ_ALWAYS_SUCCEEDS(
-        httpChannel->HasCrossOriginOpenerPolicyMismatch(&mismatch));
-    mHasCrossOriginOpenerPolicyMismatch |= mismatch;
-  }
-
-  // We don't need to confirm internal redirects or record any
-  // history for them, so just immediately verify and return.
-  if (aFlags & nsIChannelEventSink::REDIRECT_INTERNAL) {
-    aCallback->OnRedirectVerifyCallback(NS_OK);
-    return NS_OK;
-  } else {
-    nsCOMPtr<nsIURI> oldURI;
-    aOldChannel->GetURI(getter_AddRefs(oldURI));
-    uint32_t responseStatus = 0;
-    bool isPost = false;
-    if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aOldChannel)) {
-      Unused << httpChannel->GetResponseStatus(&responseStatus);
-      nsAutoCString method;
-      Unused << httpChannel->GetRequestMethod(method);
-      isPost = method.EqualsLiteral("POST");
-    }
-    mRedirects.AppendElement(
-        DocumentChannelRedirect{oldURI, aFlags, responseStatus, isPost});
-  }
-
-  if (!CanSend()) {
-    return NS_BINDING_ABORTED;
-  }
-
-  // Currently the CSP code expects to run in the content
-  // process so that it can send events. Send a message to
-  // our content process to ask CSP if we should allow this
-  // redirect, and wait for confirmation.
-  nsCOMPtr<nsILoadInfo> loadInfo = aOldChannel->LoadInfo();
-  Maybe<LoadInfoArgs> loadInfoArgs;
-  MOZ_ALWAYS_SUCCEEDS(ipc::LoadInfoToLoadInfoArgs(loadInfo, &loadInfoArgs));
-  MOZ_ASSERT(loadInfoArgs.isSome());
-
-  nsCOMPtr<nsIURI> newUri;
-  nsresult rv = aNewChannel->GetURI(getter_AddRefs(newUri));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsIAsyncVerifyRedirectCallback> callback(aCallback);
-  nsCOMPtr<nsIChannel> oldChannel(aOldChannel);
-  SendConfirmRedirect(*loadInfoArgs, newUri)
-      ->Then(
-          GetCurrentThreadSerialEventTarget(), __func__,
-          [callback,
-           oldChannel](const Tuple<nsresult, Maybe<nsresult>>& aResult) {
-            if (Get<1>(aResult)) {
-              oldChannel->Cancel(*Get<1>(aResult));
-            }
-            callback->OnRedirectVerifyCallback(Get<0>(aResult));
-          },
-          [callback, oldChannel](const mozilla::ipc::ResponseRejectReason) {
-            oldChannel->Cancel(NS_ERROR_DOM_BAD_URI);
-            callback->OnRedirectVerifyCallback(NS_BINDING_ABORTED);
-          });
-
-  // Clear out our nsIParentChannel functions, since a normal parent
-  // channel would actually redirect and not have those values on the new one.
-  // We expect the URI classifier to run on the redirected channel with
-  // the new URI and set these again.
-  mIParentChannelFunctions.Clear();
-  return NS_OK;
-}
-
-//-----------------------------------------------------------------------------
-// DocumentChannelParent::nsIProcessSwitchRequestor
-//-----------------------------------------------------------------------------
-
-NS_IMETHODIMP DocumentChannelParent::GetChannel(nsIChannel** aChannel) {
-  MOZ_ASSERT(mChannel);
-  nsCOMPtr<nsIChannel> channel(mChannel);
-  channel.forget(aChannel);
-  return NS_OK;
-}
-
-NS_IMETHODIMP DocumentChannelParent::SwitchProcessTo(
-    dom::Promise* aContentProcessIdPromise, uint64_t aIdentifier) {
-  MOZ_ASSERT(NS_IsMainThread());
-  NS_ENSURE_ARG(aContentProcessIdPromise);
-
-  mRedirectContentProcessIdPromise =
-      ContentProcessIdPromise::FromDomPromise(aContentProcessIdPromise);
-  mCrossProcessRedirectIdentifier = aIdentifier;
-  return NS_OK;
-}
-
-// This method returns the cached result of running the Cross-Origin-Opener
-// policy compare algorithm by calling ComputeCrossOriginOpenerPolicyMismatch
-NS_IMETHODIMP
-DocumentChannelParent::HasCrossOriginOpenerPolicyMismatch(bool* aMismatch) {
-  MOZ_ASSERT(aMismatch);
-
-  if (!aMismatch) {
-    return NS_ERROR_INVALID_ARG;
-  }
-
-  // If we found a COOP mismatch on an earlier channel and then
-  // redirected away from that, we should use that result.
-  if (mHasCrossOriginOpenerPolicyMismatch) {
-    *aMismatch = true;
-    return NS_OK;
-  }
-
-  nsCOMPtr<nsHttpChannel> channel = do_QueryInterface(mChannel);
-  if (!channel) {
-    // Not an nsHttpChannel assume it's okay to switch.
-    *aMismatch = false;
-    return NS_OK;
-  }
-
-  return channel->HasCrossOriginOpenerPolicyMismatch(aMismatch);
-}
-
-NS_IMETHODIMP
-DocumentChannelParent::GetCrossOriginOpenerPolicy(
-    nsILoadInfo::CrossOriginOpenerPolicy* aPolicy) {
-  MOZ_ASSERT(aPolicy);
-  if (!aPolicy) {
-    return NS_ERROR_INVALID_ARG;
-  }
-
-  nsCOMPtr<nsHttpChannel> channel = do_QueryInterface(mChannel);
-  if (!channel) {
-    *aPolicy = nsILoadInfo::OPENER_POLICY_NULL;
-    return NS_OK;
-  }
-
-  return channel->GetCrossOriginOpenerPolicy(aPolicy);
-}
-
 }  // namespace net
 }  // namespace mozilla
--- a/netwerk/ipc/DocumentChannelParent.h
+++ b/netwerk/ipc/DocumentChannelParent.h
@@ -2,267 +2,88 @@
 
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_net_DocumentChannelParent_h
 #define mozilla_net_DocumentChannelParent_h
 
-#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 "nsIInterfaceRequestor.h"
-#include "nsIObserver.h"
-#include "nsIParentChannel.h"
-#include "nsIParentRedirectingChannel.h"
-#include "nsIProcessSwitchRequestor.h"
-#include "nsIRedirectResultListener.h"
-
-#define DOCUMENT_CHANNEL_PARENT_IID                  \
-  {                                                  \
-    0x3b393c56, 0x9e01, 0x11e9, {                    \
-      0xa2, 0xa3, 0x2a, 0x2a, 0xe2, 0xdb, 0xcc, 0xe4 \
-    }                                                \
-  }
+#include "mozilla/net/DocumentLoadListener.h"
 
 namespace mozilla {
 namespace net {
 
-// TODO: We currently don't implement nsIProgressEventSink and forward those
-// to the child. Should we? We get the interface requested.
-class DocumentChannelParent : public nsIInterfaceRequestor,
-                              public PDocumentChannelParent,
-                              public nsIAsyncVerifyRedirectReadyCallback,
-                              public nsIParentChannel,
-                              public nsIChannelEventSink,
-                              public HttpChannelSecurityWarningReporter,
-                              public nsIProcessSwitchRequestor {
+/**
+ * An implementation of ADocumentChannelBridge that forwards all changes across
+ * to DocumentChannelChild, the nsIChannel implementation owned by a content
+ * process docshell.
+ */
+class DocumentChannelParent final : public ADocumentChannelBridge,
+                                    public PDocumentChannelParent {
  public:
-  explicit DocumentChannelParent(const dom::PBrowserOrId& iframeEmbedding,
+  NS_INLINE_DECL_REFCOUNTING(DocumentChannelParent, override);
+
+  explicit DocumentChannelParent(const dom::PBrowserOrId& aIframeEmbedding,
                                  nsILoadContext* aLoadContext,
-                                 PBOverrideStatus aOverrideStatus);
-
+                                 PBOverrideStatus aOverrideStatus) {
+    mParent = new DocumentLoadListener(aIframeEmbedding, aLoadContext,
+                                       aOverrideStatus, this);
+  }
   bool Init(const DocumentChannelCreationArgs& aArgs);
 
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIREQUESTOBSERVER
-  NS_DECL_NSISTREAMLISTENER
-  NS_DECL_NSIPARENTCHANNEL
-  NS_DECL_NSIINTERFACEREQUESTOR
-  NS_DECL_NSIASYNCVERIFYREDIRECTREADYCALLBACK
-  NS_DECL_NSICHANNELEVENTSINK
-  NS_DECL_NSIPROCESSSWITCHREQUESTOR
-
-  NS_DECLARE_STATIC_IID_ACCESSOR(DOCUMENT_CHANNEL_PARENT_IID)
-
-  bool RecvCancel(const nsresult& status);
-  bool RecvSuspend();
-  bool RecvResume();
-
-  nsresult ReportSecurityMessage(const nsAString& aMessageTag,
-                                 const nsAString& aMessageCategory) override {
-    ReportSecurityMessageParams params;
-    params.mMessageTag = aMessageTag;
-    params.mMessageCategory = aMessageCategory;
-    mSecurityWarningFunctions.AppendElement(
-        SecurityWarningFunction{VariantIndex<0>{}, std::move(params)});
-    return NS_OK;
+  // PDocumentChannelParent
+  bool RecvCancel(const nsresult& aStatus) {
+    mParent->Cancel(aStatus);
+    return true;
+  }
+  bool RecvSuspend() {
+    mParent->Suspend();
+    return true;
   }
-  nsresult LogBlockedCORSRequest(const nsAString& aMessage,
-                                 const nsACString& aCategory) override {
-    LogBlockedCORSRequestParams params;
-    params.mMessage = aMessage;
-    params.mCategory = aCategory;
-    mSecurityWarningFunctions.AppendElement(
-        SecurityWarningFunction{VariantIndex<1>{}, std::move(params)});
-    return NS_OK;
+  bool RecvResume() {
+    mParent->Resume();
+    return true;
   }
-  nsresult LogMimeTypeMismatch(const nsACString& aMessageName, bool aWarning,
-                               const nsAString& aURL,
-                               const nsAString& aContentType) override {
-    LogMimeTypeMismatchParams params;
-    params.mMessageName = aMessageName;
-    params.mWarning = aWarning;
-    params.mURL = aURL;
-    params.mContentType = aContentType;
-    mSecurityWarningFunctions.AppendElement(
-        SecurityWarningFunction{VariantIndex<2>{}, std::move(params)});
-    return NS_OK;
+  void ActorDestroy(ActorDestroyReason aWhy) override {
+    mParent->DocumentChannelBridgeDisconnected();
+    mParent = nullptr;
   }
 
-  virtual void ActorDestroy(ActorDestroyReason why) override;
-
  private:
-  virtual ~DocumentChannelParent() = default;
-
-  // Initiates the switch from DocumentChannel to the real protocol-specific
-  // channel, and ensures that RedirectToRealChannelFinished is called when
-  // this is complete.
-  void TriggerRedirectToRealChannel(
-      const Maybe<uint64_t>& aDestinationProcess = Nothing());
-
-  // Called once the content-process side on setting up a replacement
-  // channel is complete. May wait for the new parent channel to
-  // finish, and then calls into FinishReplacementChannelSetup.
-  void RedirectToRealChannelFinished(nsresult aRv);
-
-  // Completes the replacement of the new channel.
-  // This redirects the ParentChannelListener to forward any future
-  // messages to the new channel, manually forwards any being held
-  // by us, and resumes the underlying source channel.
-  void FinishReplacementChannelSetup(bool aSucceeded);
-
-  // Called when we have a cross-process switch promise. Waits on the
-  // promise, and then call TriggerRedirectToRealChannel with the
-  // provided content process id.
-  void TriggerCrossProcessSwitch();
-
-  // A helper for TriggerRedirectToRealChannel that abstracts over
-  // the same-process and cross-process switch cases and returns
-  // a single promise to wait on.
-  RefPtr<PDocumentChannelParent::RedirectToRealChannelPromise>
-  RedirectToRealChannel(uint32_t aRedirectFlags, uint32_t aLoadFlags,
-                        const Maybe<uint64_t>& aDestinationProcess);
-
-  // Serializes all data needed to setup the new replacement channel
-  // in the content process into the RedirectToRealChannelArgs struct.
-  void SerializeRedirectData(RedirectToRealChannelArgs& aArgs,
-                             bool aIsCrossProcess, uint32_t aRedirectFlags,
-                             uint32_t aLoadFlags);
+  // DocumentChannelListener
+  void DisconnectChildListeners(nsresult aStatus) override {
+    if (CanSend()) {
+      Unused << SendDisconnectChildListeners(aStatus);
+    }
+  }
 
-  // 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&
-  // aProvider, const nsACString& aFullHash) = 0;
-  // SetClassifierMatchedTrackingInfo(const nsACString& aLists, const
-  // nsACString& aFullHashes) = 0; NotifyClassificationFlags(uint32_t
-  // aClassificationFlags, bool aIsThirdParty) = 0;
-  struct ClassifierMatchedInfoParams {
-    nsCString mList;
-    nsCString mProvider;
-    nsCString mFullHash;
-  };
-  struct ClassifierMatchedTrackingInfoParams {
-    nsCString mLists;
-    nsCString mFullHashes;
-  };
-  struct ClassificationFlagsParams {
-    uint32_t mClassificationFlags;
-    bool mIsThirdParty;
-  };
-
-  struct NotifyChannelClassifierProtectionDisabledParams {
-    uint32_t mAcceptedReason;
-  };
-
-  struct NotifyCookieAllowedParams {};
-
-  struct NotifyCookieBlockedParams {
-    uint32_t mRejectedReason;
-  };
-
-  typedef mozilla::Variant<
-      nsIHttpChannel::FlashPluginState, ClassifierMatchedInfoParams,
-      ClassifierMatchedTrackingInfoParams, ClassificationFlagsParams,
-      NotifyChannelClassifierProtectionDisabledParams,
-      NotifyCookieAllowedParams, NotifyCookieBlockedParams>
-      IParentChannelFunction;
-
-  // Store a list of all the attribute setters that have been called on this
-  // channel, so that we can repeat them on the real channel that we redirect
-  // to.
-  nsTArray<IParentChannelFunction> mIParentChannelFunctions;
+  void Delete() override {
+    if (CanSend()) {
+      Unused << SendDeleteSelf();
+    }
+  }
 
-  // This defines a variant this describes all the functions
-  // from HttpChannelSecurityWarningReporter so that we can forward
-  // them on to the real channel.
-
-  struct ReportSecurityMessageParams {
-    nsString mMessageTag;
-    nsString mMessageCategory;
-  };
-  struct LogBlockedCORSRequestParams {
-    nsString mMessage;
-    nsCString mCategory;
-  };
-  struct LogMimeTypeMismatchParams {
-    nsCString mMessageName;
-    bool mWarning;
-    nsString mURL;
-    nsString mContentType;
-  };
+  RefPtr<PDocumentChannelParent::ConfirmRedirectPromise> ConfirmRedirect(
+      const LoadInfoArgs& aLoadInfo, nsIURI* aNewURI) override {
+    return SendConfirmRedirect(aLoadInfo, aNewURI);
+  }
 
-  typedef mozilla::Variant<ReportSecurityMessageParams,
-                           LogBlockedCORSRequestParams,
-                           LogMimeTypeMismatchParams>
-      SecurityWarningFunction;
-  nsTArray<SecurityWarningFunction> mSecurityWarningFunctions;
-
-  struct OnDataAvailableRequest {
-    nsCString data;
-    uint64_t offset;
-    uint32_t count;
-  };
-  // TODO Backtrack this.
-  nsTArray<OnDataAvailableRequest> mPendingRequests;
-  Maybe<nsresult> mStopRequestValue;
-
-  nsCOMPtr<nsIChannel> mChannel;
-  RefPtr<ParentChannelListener> mListener;
-
-  nsCOMPtr<nsILoadContext> mLoadContext;
-
-  PBOverrideStatus mPBOverride;
+  virtual ProcessId OtherPid() const override { return IProtocol::OtherPid(); }
 
-  // The original URI of the current channel. If there are redirects,
-  // then the value on the channel gets overwritten with the original
-  // URI of the first channel in the redirect chain, so we cache the
-  // value we need here.
-  nsCOMPtr<nsIURI> mChannelCreationURI;
-
-  nsTArray<DocumentChannelRedirect> mRedirects;
+  virtual bool AttachStreamFilter(
+      Endpoint<mozilla::extensions::PStreamFilterParent>&& aEndpoint) override {
+    return SendAttachStreamFilter(std::move(aEndpoint));
+  }
 
-  // Corresponding redirect channel registrar Id for the final channel that
-  // we want to use when redirecting the child, or doing a process switch.
-  // 0 means redirection is not started.
-  uint32_t mRedirectChannelId = 0;
-  // Set to true if we called Suspend on mChannel to initiate our redirect.
-  // This isn't currently set when we do a process swap, since that gets
-  // initiated in nsHttpChannel.
-  bool mSuspendedChannel = false;
-  // Set to true if we're currently in the middle of replacing this with
-  // a new channel connected a different process.
-  bool mDoingProcessSwitch = false;
-  // The value of GetApplyConversion on mChannel when OnStartRequest
-  // was called. We override it to false to prevent a conversion
-  // helper from being installed, but we need to restore the value
-  // later.
-  bool mOldApplyConversion = false;
-  // Set to true if any previous channel that we redirected away
-  // from had a COOP mismatch.
-  bool mHasCrossOriginOpenerPolicyMismatch = false;
+  RefPtr<PDocumentChannelParent::RedirectToRealChannelPromise>
+  RedirectToRealChannel(uint32_t aRedirectFlags, uint32_t aLoadFlags) override;
 
-  typedef MozPromise<uint64_t, nsresult, true /* exclusive */>
-      ContentProcessIdPromise;
-  // This promise is set following a on-may-change-process observer
-  // notification when the associated channel is getting relocated to another
-  // process. It will be resolved when that process is set up.
-  RefPtr<ContentProcessIdPromise> mRedirectContentProcessIdPromise;
-  // This identifier is set at the same time as the
-  // mRedirectContentProcessIdPromise.
-  // This identifier is later passed to the childChannel in order to identify it
-  // once the promise is resolved.
-  uint64_t mCrossProcessRedirectIdentifier = 0;
+  ~DocumentChannelParent() = default;
+
+  RefPtr<DocumentLoadListener> mParent;
 };
 
-NS_DEFINE_STATIC_IID_ACCESSOR(DocumentChannelParent,
-                              DOCUMENT_CHANNEL_PARENT_IID)
-
 }  // namespace net
 }  // namespace mozilla
 
 #endif  // mozilla_net_DocumentChannelParent_h
copy from netwerk/ipc/DocumentChannelParent.cpp
copy to netwerk/ipc/DocumentLoadListener.cpp
--- a/netwerk/ipc/DocumentChannelParent.cpp
+++ b/netwerk/ipc/DocumentLoadListener.cpp
@@ -1,16 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set sw=2 ts=8 et tw=80 : */
 
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "DocumentChannelParent.h"
+#include "DocumentLoadListener.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/LoadInfo.h"
 #include "mozilla/MozPromiseInlines.h"  // For MozPromise::FromDomPromise
 #include "mozilla/dom/BrowserParent.h"
 #include "mozilla/dom/ClientChannelHelper.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ContentProcessManager.h"
 #include "mozilla/dom/ipc/IdType.h"
@@ -27,160 +27,156 @@
 #include "nsIPrompt.h"
 #include "nsIWindowWatcher.h"
 
 using namespace mozilla::dom;
 
 namespace mozilla {
 namespace net {
 
-NS_IMPL_ADDREF(DocumentChannelParent)
-NS_IMPL_RELEASE(DocumentChannelParent)
+NS_IMPL_ADDREF(DocumentLoadListener)
+NS_IMPL_RELEASE(DocumentLoadListener)
 
-NS_INTERFACE_MAP_BEGIN(DocumentChannelParent)
+NS_INTERFACE_MAP_BEGIN(DocumentLoadListener)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInterfaceRequestor)
   NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
   NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
   NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
   NS_INTERFACE_MAP_ENTRY(nsIParentChannel)
   NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectReadyCallback)
   NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
   NS_INTERFACE_MAP_ENTRY(nsIProcessSwitchRequestor)
-  NS_INTERFACE_MAP_ENTRY_CONCRETE(DocumentChannelParent)
+  NS_INTERFACE_MAP_ENTRY_CONCRETE(DocumentLoadListener)
 NS_INTERFACE_MAP_END
 
-DocumentChannelParent::DocumentChannelParent(
-    const PBrowserOrId& iframeEmbedding, nsILoadContext* aLoadContext,
-    PBOverrideStatus aOverrideStatus)
+DocumentLoadListener::DocumentLoadListener(const PBrowserOrId& aIframeEmbedding,
+                                           nsILoadContext* aLoadContext,
+                                           PBOverrideStatus aOverrideStatus,
+                                           ADocumentChannelBridge* aBridge)
     : mLoadContext(aLoadContext), mPBOverride(aOverrideStatus) {
   RefPtr<dom::BrowserParent> parent;
-  if (iframeEmbedding.type() == PBrowserOrId::TPBrowserParent) {
+  if (aIframeEmbedding.type() == PBrowserOrId::TPBrowserParent) {
     parent =
-        static_cast<dom::BrowserParent*>(iframeEmbedding.get_PBrowserParent());
+        static_cast<dom::BrowserParent*>(aIframeEmbedding.get_PBrowserParent());
   }
-  mListener = new ParentChannelListener(this, parent);
+  mParentChannelListener = new ParentChannelListener(this, parent);
+  mDocumentChannelBridge = aBridge;
 }
 
-bool DocumentChannelParent::Init(const DocumentChannelCreationArgs& aArgs) {
-  RefPtr<nsDocShellLoadState> loadState =
-      new nsDocShellLoadState(aArgs.loadState());
-
-  RefPtr<LoadInfo> loadInfo;
-  nsresult rv = mozilla::ipc::LoadInfoArgsToLoadInfo(Some(aArgs.loadInfo()),
-                                                     getter_AddRefs(loadInfo));
-  MOZ_ASSERT(NS_SUCCEEDED(rv));
-
-  bool result = nsDocShell::CreateChannelForLoadState(
-      loadState, loadInfo, mListener, nullptr,
-      aArgs.initiatorType().ptrOr(nullptr), aArgs.loadFlags(), aArgs.loadType(),
-      aArgs.cacheKey(), aArgs.isActive(), aArgs.isTopLevelDoc(),
-      aArgs.hasNonEmptySandboxingFlags(), rv, getter_AddRefs(mChannel));
-  if (!result) {
-    mListener = nullptr;
-    return SendFailedAsyncOpen(rv);
+bool DocumentLoadListener::Open(
+    nsDocShellLoadState* aLoadState, class LoadInfo* aLoadInfo,
+    const nsString* aInitiatorType, nsLoadFlags aLoadFlags, uint32_t aLoadType,
+    uint32_t aCacheKey, bool aIsActive, bool aIsTopLevelDoc,
+    bool aHasNonEmptySandboxingFlags, const Maybe<URIParams>& aTopWindowURI,
+    const Maybe<PrincipalInfo>& aContentBlockingAllowListPrincipal,
+    const nsString& aCustomUserAgent, const uint64_t& aChannelId,
+    const TimeStamp& aAsyncOpenTime, nsresult* aRv) {
+  if (!nsDocShell::CreateChannelForLoadState(
+          aLoadState, aLoadInfo, mParentChannelListener, nullptr,
+          aInitiatorType, aLoadFlags, aLoadType, aCacheKey, aIsActive,
+          aIsTopLevelDoc, aHasNonEmptySandboxingFlags, *aRv,
+          getter_AddRefs(mChannel))) {
+    mParentChannelListener = nullptr;
+    return false;
   }
 
-  nsDocShell::ConfigureChannel(
-      mChannel, loadState, aArgs.initiatorType().ptrOr(nullptr),
-      aArgs.loadType(), aArgs.cacheKey(), aArgs.hasNonEmptySandboxingFlags());
+  nsDocShell::ConfigureChannel(mChannel, aLoadState, aInitiatorType, aLoadType,
+                               aCacheKey, aHasNonEmptySandboxingFlags);
 
   // Computation of the top window uses the docshell tree, so only
   // works in the source process. We compute it manually and override
   // it so that it gets the right value.
   // This probably isn't fission compatible.
-  RefPtr<HttpBaseChannel> httpBaseChannel = do_QueryObject(mChannel, &rv);
+  RefPtr<HttpBaseChannel> httpBaseChannel = do_QueryObject(mChannel, aRv);
   if (httpBaseChannel) {
-    nsCOMPtr<nsIURI> topWindowURI = DeserializeURI(aArgs.topWindowURI());
-    rv = httpBaseChannel->SetTopWindowURI(topWindowURI);
-    MOZ_ASSERT(NS_SUCCEEDED(rv));
+    nsCOMPtr<nsIURI> topWindowURI = DeserializeURI(aTopWindowURI);
+    *aRv = httpBaseChannel->SetTopWindowURI(topWindowURI);
+    MOZ_ASSERT(NS_SUCCEEDED(*aRv));
 
-    if (aArgs.contentBlockingAllowListPrincipal()) {
+    if (aContentBlockingAllowListPrincipal) {
       nsCOMPtr<nsIPrincipal> contentBlockingAllowListPrincipal =
-          PrincipalInfoToPrincipal(*aArgs.contentBlockingAllowListPrincipal());
+          PrincipalInfoToPrincipal(*aContentBlockingAllowListPrincipal);
       httpBaseChannel->SetContentBlockingAllowListPrincipal(
           contentBlockingAllowListPrincipal);
     }
 
     // The content process docshell can specify a custom override
     // for the user agent value. If we had one, then add it to
     // the header now.
-    if (!aArgs.customUserAgent().IsEmpty()) {
-      NS_ConvertUTF16toUTF8 utf8CustomUserAgent(aArgs.customUserAgent());
-      rv = httpBaseChannel->SetRequestHeader(NS_LITERAL_CSTRING("User-Agent"),
-                                             utf8CustomUserAgent, false);
+    if (!aCustomUserAgent.IsEmpty()) {
+      NS_ConvertUTF16toUTF8 utf8CustomUserAgent(aCustomUserAgent);
+      *aRv = httpBaseChannel->SetRequestHeader(NS_LITERAL_CSTRING("User-Agent"),
+                                               utf8CustomUserAgent, false);
     }
   }
 
   nsCOMPtr<nsIPrivateBrowsingChannel> privateChannel =
       do_QueryInterface(mChannel);
   if (mPBOverride != kPBOverride_Unset) {
     privateChannel->SetPrivate(mPBOverride == kPBOverride_Private);
   }
 
   nsCOMPtr<nsIIdentChannel> identChannel = do_QueryInterface(mChannel);
   if (identChannel) {
-    Unused << identChannel->SetChannelId(aArgs.channelId());
+    Unused << identChannel->SetChannelId(aChannelId);
   }
 
   RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(mChannel);
   if (httpChannelImpl) {
     httpChannelImpl->SetWarningReporter(this);
   }
 
   nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(mChannel);
   if (timedChannel) {
-    timedChannel->SetAsyncOpen(aArgs.asyncOpenTime());
+    timedChannel->SetAsyncOpen(aAsyncOpenTime);
   }
 
   // Setup a ClientChannelHelper to watch for redirects, and copy
   // across any serviceworker related data between channels as needed.
   AddClientChannelHelperInParent(mChannel, GetMainThreadSerialEventTarget());
 
-  rv = mChannel->AsyncOpen(mListener);
-  if (NS_FAILED(rv)) {
-    mListener = nullptr;
-    return SendFailedAsyncOpen(rv);
+  *aRv = mChannel->AsyncOpen(mParentChannelListener);
+  if (NS_FAILED(*aRv)) {
+    mParentChannelListener = nullptr;
+    return false;
   }
 
-  mChannelCreationURI = loadState->URI();
-
+  mChannelCreationURI = aLoadState->URI();
   return true;
 }
 
-void DocumentChannelParent::ActorDestroy(ActorDestroyReason why) {
+void DocumentLoadListener::DocumentChannelBridgeDisconnected() {
   // 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;
 }
 
-bool DocumentChannelParent::RecvCancel(const nsresult& aStatusCode) {
+void DocumentLoadListener::Cancel(const nsresult& aStatusCode) {
   if (mChannel && !mDoingProcessSwitch) {
     mChannel->Cancel(aStatusCode);
   }
-  return true;
 }
 
-bool DocumentChannelParent::RecvSuspend() {
+void DocumentLoadListener::Suspend() {
   if (mChannel && !mDoingProcessSwitch) {
     mChannel->Suspend();
   }
-  return true;
 }
 
-bool DocumentChannelParent::RecvResume() {
+void DocumentLoadListener::Resume() {
   if (mChannel && !mDoingProcessSwitch) {
     mChannel->Resume();
   }
-  return true;
 }
 
-void DocumentChannelParent::RedirectToRealChannelFinished(nsresult aRv) {
+void DocumentLoadListener::RedirectToRealChannelFinished(nsresult aRv) {
   if (NS_FAILED(aRv)) {
     FinishReplacementChannelSetup(false);
     return;
   }
 
   // Wait for background channel ready on target channel
   nsCOMPtr<nsIRedirectChannelRegistrar> redirectReg =
       RedirectChannelRegistrar::GetOrCreate();
@@ -204,26 +200,26 @@ void DocumentChannelParent::RedirectToRe
 
   // Ask redirected channel if verification can proceed.
   // ReadyToVerify will be invoked when redirected channel is ready.
   redirectingParent->ContinueVerification(this);
   return;
 }
 
 NS_IMETHODIMP
-DocumentChannelParent::ReadyToVerify(nsresult aResultCode) {
+DocumentLoadListener::ReadyToVerify(nsresult aResultCode) {
   FinishReplacementChannelSetup(NS_SUCCEEDED(aResultCode));
   return NS_OK;
 }
 
-void DocumentChannelParent::FinishReplacementChannelSetup(bool aSucceeded) {
+void DocumentLoadListener::FinishReplacementChannelSetup(bool aSucceeded) {
   nsresult rv;
 
-  if (mDoingProcessSwitch && CanSend()) {
-    Unused << SendDisconnectChildListeners(NS_BINDING_ABORTED);
+  if (mDoingProcessSwitch && mDocumentChannelBridge) {
+    mDocumentChannelBridge->DisconnectChildListeners(NS_BINDING_ABORTED);
   }
 
   nsCOMPtr<nsIParentChannel> redirectChannel;
   if (mRedirectChannelId) {
     nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
         RedirectChannelRegistrar::GetOrCreate();
     MOZ_ASSERT(registrar);
 
@@ -261,19 +257,19 @@ void DocumentChannelParent::FinishReplac
     return;
   }
 
   MOZ_ASSERT(
       !SameCOMIdentity(redirectChannel, static_cast<nsIParentChannel*>(this)));
 
   Delete();
   if (!mStopRequestValue) {
-    mListener->SetListenerAfterRedirect(redirectChannel);
+    mParentChannelListener->SetListenerAfterRedirect(redirectChannel);
   }
-  redirectChannel->SetParentListener(mListener);
+  redirectChannel->SetParentListener(mParentChannelListener);
 
   // We stored the values from all nsIParentChannel functions called since we
   // couldn't handle them. Copy them across to the real channel since it should
   // know what to do.
   for (auto& variant : mIParentChannelFunctions) {
     variant.match(
         [redirectChannel](const nsIHttpChannel::FlashPluginState& aState) {
           redirectChannel->NotifyFlashPluginStateChanged(aState);
@@ -319,62 +315,69 @@ void DocumentChannelParent::FinishReplac
           [reporter](const LogMimeTypeMismatchParams& aParams) {
             Unused << reporter->LogMimeTypeMismatch(
                 aParams.mMessageName, aParams.mWarning, aParams.mURL,
                 aParams.mContentType);
           });
     }
   }
 
-  if (mSuspendedChannel) {
-    nsTArray<OnDataAvailableRequest> pendingRequests =
-        std::move(mPendingRequests);
-    MOZ_ASSERT(mPendingRequests.IsEmpty());
+  ResumeSuspendedChannel(redirectChannel);
+}
 
-    nsCOMPtr<nsHttpChannel> httpChannel = do_QueryInterface(mChannel);
-    if (httpChannel) {
-      httpChannel->SetApplyConversion(mOldApplyConversion);
-    }
-    rv = redirectChannel->OnStartRequest(mChannel);
-    if (NS_FAILED(rv)) {
-      mChannel->Cancel(rv);
-    }
+void DocumentLoadListener::ResumeSuspendedChannel(
+    nsIStreamListener* aListener) {
+  if (!mSuspendedChannel) {
+    return;
+  }
+
+  nsTArray<OnDataAvailableRequest> pendingRequests =
+      std::move(mPendingRequests);
+  MOZ_ASSERT(mPendingRequests.IsEmpty());
 
-    // If we failed to suspend the channel, then we might have received
-    // some messages while the redirected was being handled.
-    // Manually send them on now.
-    if (NS_SUCCEEDED(rv) &&
-        (!mStopRequestValue || NS_SUCCEEDED(*mStopRequestValue))) {
-      for (auto& request : pendingRequests) {
-        nsCOMPtr<nsIInputStream> stringStream;
-        rv = NS_NewByteInputStream(
-            getter_AddRefs(stringStream),
-            Span<const char>(request.data.get(), request.count),
-            NS_ASSIGNMENT_DEPEND);
-        if (NS_SUCCEEDED(rv)) {
-          rv = redirectChannel->OnDataAvailable(mChannel, stringStream,
-                                                request.offset, request.count);
-        }
-        if (NS_FAILED(rv)) {
-          mChannel->Cancel(rv);
-          mStopRequestValue = Some(rv);
-          break;
-        }
+  nsCOMPtr<nsHttpChannel> httpChannel = do_QueryInterface(mChannel);
+  if (httpChannel) {
+    httpChannel->SetApplyConversion(mOldApplyConversion);
+  }
+  nsresult rv = aListener->OnStartRequest(mChannel);
+  if (NS_FAILED(rv)) {
+    mChannel->Cancel(rv);
+  }
+
+  // If we failed to suspend the channel, then we might have received
+  // some messages while the redirected was being handled.
+  // Manually send them on now.
+  if (NS_SUCCEEDED(rv) &&
+      (!mStopRequestValue || NS_SUCCEEDED(*mStopRequestValue))) {
+    for (auto& request : pendingRequests) {
+      nsCOMPtr<nsIInputStream> stringStream;
+      rv = NS_NewByteInputStream(
+          getter_AddRefs(stringStream),
+          Span<const char>(request.data.get(), request.count),
+          NS_ASSIGNMENT_DEPEND);
+      if (NS_SUCCEEDED(rv)) {
+        rv = aListener->OnDataAvailable(mChannel, stringStream, request.offset,
+                                        request.count);
+      }
+      if (NS_FAILED(rv)) {
+        mChannel->Cancel(rv);
+        mStopRequestValue = Some(rv);
+        break;
       }
     }
+  }
 
-    if (mStopRequestValue) {
-      redirectChannel->OnStopRequest(mChannel, *mStopRequestValue);
-    }
+  if (mStopRequestValue) {
+    aListener->OnStopRequest(mChannel, *mStopRequestValue);
+  }
 
-    mChannel->Resume();
-  }
+  mChannel->Resume();
 }
 
-void DocumentChannelParent::SerializeRedirectData(
+void DocumentLoadListener::SerializeRedirectData(
     RedirectToRealChannelArgs& aArgs, bool aIsCrossProcess,
     uint32_t aRedirectFlags, uint32_t aLoadFlags) {
   // Use the original URI of the current channel, as this is what
   // we'll use to construct the channel in the content process.
   aArgs.uri() = mChannelCreationURI;
   if (!aArgs.uri()) {
     mChannel->GetOriginalURI(getter_AddRefs(aArgs.uri()));
   }
@@ -479,59 +482,61 @@ void DocumentChannelParent::SerializeRed
   }
 
   aArgs.newLoadFlags() = aLoadFlags;
   aArgs.redirectFlags() = aRedirectFlags;
   aArgs.redirects() = mRedirects;
   aArgs.redirectIdentifier() = mCrossProcessRedirectIdentifier;
 }
 
-void DocumentChannelParent::TriggerCrossProcessSwitch() {
+void DocumentLoadListener::TriggerCrossProcessSwitch() {
   MOZ_ASSERT(mRedirectContentProcessIdPromise);
   MOZ_ASSERT(!mDoingProcessSwitch, "Already in the middle of switching?");
   MOZ_ASSERT(NS_IsMainThread());
 
   mDoingProcessSwitch = true;
 
-  RefPtr<DocumentChannelParent> self = this;
+  RefPtr<DocumentLoadListener> self = this;
   mRedirectContentProcessIdPromise->Then(
       GetMainThreadSerialEventTarget(), __func__,
       [self, this](uint64_t aCpId) {
         MOZ_ASSERT(mChannel, "Something went wrong, channel got cancelled");
         TriggerRedirectToRealChannel(Some(aCpId));
       },
       [self](nsresult aStatusCode) {
         MOZ_ASSERT(NS_FAILED(aStatusCode), "Status should be error");
         self->RedirectToRealChannelFinished(aStatusCode);
       });
 }
 
 RefPtr<PDocumentChannelParent::RedirectToRealChannelPromise>
-DocumentChannelParent::RedirectToRealChannel(
+DocumentLoadListener::RedirectToRealChannel(
     uint32_t aRedirectFlags, uint32_t aLoadFlags,
     const Maybe<uint64_t>& aDestinationProcess) {
-  RedirectToRealChannelArgs args;
-  SerializeRedirectData(args, !!aDestinationProcess, aRedirectFlags,
-                        aLoadFlags);
-
   if (aDestinationProcess) {
     dom::ContentParent* cp =
         dom::ContentProcessManager::GetSingleton()->GetContentProcessById(
             ContentParentId{*aDestinationProcess});
     if (!cp) {
       return PDocumentChannelParent::RedirectToRealChannelPromise::
           CreateAndReject(ipc::ResponseRejectReason::SendError, __func__);
     }
 
+    RedirectToRealChannelArgs args;
+    SerializeRedirectData(args, !!aDestinationProcess, aRedirectFlags,
+                          aLoadFlags);
+
     return cp->SendCrossProcessRedirect(args);
   }
-  return SendRedirectToRealChannel(args);
+  MOZ_ASSERT(mDocumentChannelBridge);
+  return mDocumentChannelBridge->RedirectToRealChannel(aRedirectFlags,
+                                                       aLoadFlags);
 }
 
-void DocumentChannelParent::TriggerRedirectToRealChannel(
+void DocumentLoadListener::TriggerRedirectToRealChannel(
     const Maybe<uint64_t>& aDestinationProcess) {
   // This initiates replacing the current DocumentChannel with a
   // protocol specific 'real' channel, maybe in a different process than
   // the current DocumentChannelChild, if aDestinationProces is set.
   // It registers the current mChannel with the registrar to get an ID
   // so that the remote end can setup a new IPDL channel and lookup
   // the same underlying channel.
   // We expect this process to finish with FinishReplacementChannelSetup
@@ -549,26 +554,25 @@ void DocumentChannelParent::TriggerRedir
   }
 
   uint32_t newLoadFlags = nsIRequest::LOAD_NORMAL;
   MOZ_ALWAYS_SUCCEEDS(mChannel->GetLoadFlags(&newLoadFlags));
   if (!aDestinationProcess) {
     newLoadFlags |= nsIChannel::LOAD_REPLACE;
   }
 
-  RefPtr<DocumentChannelParent> self = this;
+  RefPtr<DocumentLoadListener> self = this;
   RedirectToRealChannel(redirectFlags, newLoadFlags, aDestinationProcess)
       ->Then(
           GetCurrentThreadSerialEventTarget(), __func__,
           [self](Tuple<nsresult, Maybe<LoadInfoArgs>>&& aResponse) {
             if (NS_SUCCEEDED(Get<0>(aResponse))) {
               nsCOMPtr<nsILoadInfo> newLoadInfo;
               MOZ_ALWAYS_SUCCEEDS(LoadInfoArgsToLoadInfo(
                   Get<1>(aResponse), getter_AddRefs(newLoadInfo)));
-
               if (newLoadInfo) {
                 // Since the old reservedClientInfo might be controlled by a
                 // ServiceWorker when opening the channel, we need to update
                 // the controlled ClientInfo in ServiceWorkerManager to make
                 // sure the same origin subresource load can be intercepted by
                 // the ServiceWorker.
                 nsCOMPtr<nsILoadInfo> oldLoadInfo;
                 self->mChannel->GetLoadInfo(getter_AddRefs(oldLoadInfo));
@@ -576,48 +580,46 @@ void DocumentChannelParent::TriggerRedir
                 Maybe<ClientInfo> oldClientInfo =
                     oldLoadInfo->GetReservedClientInfo();
                 Maybe<ServiceWorkerDescriptor> oldController =
                     oldLoadInfo->GetController();
                 Maybe<ClientInfo> newClientInfo =
                     newLoadInfo->GetReservedClientInfo();
                 Maybe<ServiceWorkerDescriptor> newController =
                     newLoadInfo->GetController();
-
                 if (oldClientInfo.isSome() && newClientInfo.isSome() &&
                     newController.isSome() && oldController.isSome() &&
                     newController.ref() == oldController.ref()) {
                   RefPtr<ServiceWorkerManager> swMgr =
                       ServiceWorkerManager::GetInstance();
                   MOZ_ASSERT(swMgr);
                   swMgr->UpdateControlledClient(oldClientInfo.ref(),
                                                 newClientInfo.ref(),
                                                 newController.ref());
                 }
-
                 self->mChannel->SetLoadInfo(newLoadInfo);
               }
             }
             self->RedirectToRealChannelFinished(Get<0>(aResponse));
           },
           [self](const mozilla::ipc::ResponseRejectReason) {
             self->RedirectToRealChannelFinished(NS_ERROR_FAILURE);
           });
 }
 
 NS_IMETHODIMP
-DocumentChannelParent::OnStartRequest(nsIRequest* aRequest) {
+DocumentLoadListener::OnStartRequest(nsIRequest* aRequest) {
   nsCOMPtr<nsHttpChannel> channel = do_QueryInterface(aRequest);
   mChannel = do_QueryInterface(aRequest);
   MOZ_DIAGNOSTIC_ASSERT(mChannel);
 
   // If this is a download, then redirect entirely within the parent.
   // TODO, see bug 1574372.
 
-  if (!CanSend()) {
+  if (!mDocumentChannelBridge) {
     return NS_ERROR_UNEXPECTED;
   }
 
   // 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) {
@@ -651,143 +653,140 @@ DocumentChannelParent::OnStartRequest(ns
   }
 
   TriggerRedirectToRealChannel();
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-DocumentChannelParent::OnStopRequest(nsIRequest* aRequest,
-                                     nsresult aStatusCode) {
+DocumentLoadListener::OnStopRequest(nsIRequest* aRequest,
+                                    nsresult aStatusCode) {
   mStopRequestValue = Some(aStatusCode);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-DocumentChannelParent::OnDataAvailable(nsIRequest* aRequest,
-                                       nsIInputStream* aInputStream,
-                                       uint64_t aOffset, uint32_t aCount) {
+DocumentLoadListener::OnDataAvailable(nsIRequest* aRequest,
+                                      nsIInputStream* aInputStream,
+                                      uint64_t aOffset, uint32_t aCount) {
   // This isn't supposed to happen, since we suspended the channel, but
   // sometimes Suspend just doesn't work. This can happen when we're routing
   // through nsUnknownDecoder to sniff the content type, and it doesn't handle
   // being suspended. Let's just store the data and manually forward it to our
   // redirected channel when it's ready.
   nsCString data;
   nsresult rv = NS_ReadInputStreamToString(aInputStream, data, aCount);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mPendingRequests.AppendElement(
       OnDataAvailableRequest({data, aOffset, aCount}));
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-DocumentChannelParent::SetParentListener(
+DocumentLoadListener::SetParentListener(
     mozilla::net::ParentChannelListener* listener) {
   // We don't need this (do we?)
   return NS_OK;
 }
 
 NS_IMETHODIMP
-DocumentChannelParent::GetInterface(const nsIID& aIID, void** result) {
+DocumentLoadListener::GetInterface(const nsIID& aIID, void** result) {
   // Only support nsILoadContext if child channel's callbacks did too
   if (aIID.Equals(NS_GET_IID(nsILoadContext)) && mLoadContext) {
     nsCOMPtr<nsILoadContext> copy = mLoadContext;
     copy.forget(result);
     return NS_OK;
   }
 
   return QueryInterface(aIID, result);
 }
 
 // Rather than forwarding all these nsIParentChannel functions to the child, we
 // cache a list of them, and then ask the 'real' channel to forward them for us
 // after it's created.
 NS_IMETHODIMP
-DocumentChannelParent::NotifyFlashPluginStateChanged(
+DocumentLoadListener::NotifyFlashPluginStateChanged(
     nsIHttpChannel::FlashPluginState aState) {
   mIParentChannelFunctions.AppendElement(
       IParentChannelFunction{VariantIndex<0>{}, aState});
   return NS_OK;
 }
 
 NS_IMETHODIMP
-DocumentChannelParent::SetClassifierMatchedInfo(const nsACString& aList,
-                                                const nsACString& aProvider,
-                                                const nsACString& aFullHash) {
+DocumentLoadListener::SetClassifierMatchedInfo(const nsACString& aList,
+                                               const nsACString& aProvider,
+                                               const nsACString& aFullHash) {
   ClassifierMatchedInfoParams params;
   params.mList = aList;
   params.mProvider = aProvider;
   params.mFullHash = aFullHash;
 
   mIParentChannelFunctions.AppendElement(
       IParentChannelFunction{VariantIndex<1>{}, std::move(params)});
   return NS_OK;
 }
 
 NS_IMETHODIMP
-DocumentChannelParent::SetClassifierMatchedTrackingInfo(
+DocumentLoadListener::SetClassifierMatchedTrackingInfo(
     const nsACString& aLists, const nsACString& aFullHash) {
   ClassifierMatchedTrackingInfoParams params;
   params.mLists = aLists;
   params.mFullHashes = aFullHash;
 
   mIParentChannelFunctions.AppendElement(
       IParentChannelFunction{VariantIndex<2>{}, std::move(params)});
   return NS_OK;
 }
 
 NS_IMETHODIMP
-DocumentChannelParent::NotifyClassificationFlags(uint32_t aClassificationFlags,
-                                                 bool aIsThirdParty) {
+DocumentLoadListener::NotifyClassificationFlags(uint32_t aClassificationFlags,
+                                                bool aIsThirdParty) {
   mIParentChannelFunctions.AppendElement(IParentChannelFunction{
       VariantIndex<3>{},
       ClassificationFlagsParams{aClassificationFlags, aIsThirdParty}});
   return NS_OK;
 }
 
 NS_IMETHODIMP
-DocumentChannelParent::NotifyChannelClassifierProtectionDisabled(
+DocumentLoadListener::NotifyChannelClassifierProtectionDisabled(
     uint32_t aAcceptedReason) {
   mIParentChannelFunctions.AppendElement(IParentChannelFunction{
       VariantIndex<4>{},
       NotifyChannelClassifierProtectionDisabledParams{aAcceptedReason}});
   return NS_OK;
 }
 
 NS_IMETHODIMP
-DocumentChannelParent::NotifyCookieAllowed() {
+DocumentLoadListener::NotifyCookieAllowed() {
   mIParentChannelFunctions.AppendElement(
       IParentChannelFunction{VariantIndex<5>{}, NotifyCookieAllowedParams()});
   return NS_OK;
 }
 
 NS_IMETHODIMP
-DocumentChannelParent::NotifyCookieBlocked(uint32_t aRejectedReason) {
+DocumentLoadListener::NotifyCookieBlocked(uint32_t aRejectedReason) {
   mIParentChannelFunctions.AppendElement(IParentChannelFunction{
       VariantIndex<6>{}, NotifyCookieBlockedParams{aRejectedReason}});
   return NS_OK;
 }
 
 NS_IMETHODIMP
-DocumentChannelParent::Delete() {
-  // TODO - not sure we need it, but should delete the child or call on the
-  // child to release the parent.
-  if (!CanSend()) {
-    return NS_ERROR_UNEXPECTED;
+DocumentLoadListener::Delete() {
+  if (mDocumentChannelBridge) {
+    mDocumentChannelBridge->Delete();
   }
-  Unused << SendDeleteSelf();
   return NS_OK;
 }
 
 NS_IMETHODIMP
-DocumentChannelParent::AsyncOnChannelRedirect(
+DocumentLoadListener::AsyncOnChannelRedirect(
     nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aFlags,
     nsIAsyncVerifyRedirectCallback* aCallback) {
   // We generally don't want to notify the content process about redirects,
   // so just update our channel and tell the callback that we're good to go.
   mChannel = aNewChannel;
 
   // We need the original URI of the current channel to use to open the real
   // channel in the content process. Unfortunately we overwrite the original
@@ -821,17 +820,17 @@ DocumentChannelParent::AsyncOnChannelRed
       nsAutoCString method;
       Unused << httpChannel->GetRequestMethod(method);
       isPost = method.EqualsLiteral("POST");
     }
     mRedirects.AppendElement(
         DocumentChannelRedirect{oldURI, aFlags, responseStatus, isPost});
   }
 
-  if (!CanSend()) {
+  if (!mDocumentChannelBridge) {
     return NS_BINDING_ABORTED;
   }
 
   // Currently the CSP code expects to run in the content
   // process so that it can send events. Send a message to
   // our content process to ask CSP if we should allow this
   // redirect, and wait for confirmation.
   nsCOMPtr<nsILoadInfo> loadInfo = aOldChannel->LoadInfo();
@@ -840,17 +839,17 @@ DocumentChannelParent::AsyncOnChannelRed
   MOZ_ASSERT(loadInfoArgs.isSome());
 
   nsCOMPtr<nsIURI> newUri;
   nsresult rv = aNewChannel->GetURI(getter_AddRefs(newUri));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIAsyncVerifyRedirectCallback> callback(aCallback);
   nsCOMPtr<nsIChannel> oldChannel(aOldChannel);
-  SendConfirmRedirect(*loadInfoArgs, newUri)
+  mDocumentChannelBridge->ConfirmRedirect(*loadInfoArgs, newUri)
       ->Then(
           GetCurrentThreadSerialEventTarget(), __func__,
           [callback,
            oldChannel](const Tuple<nsresult, Maybe<nsresult>>& aResult) {
             if (Get<1>(aResult)) {
               oldChannel->Cancel(*Get<1>(aResult));
             }
             callback->OnRedirectVerifyCallback(Get<0>(aResult));
@@ -864,41 +863,41 @@ DocumentChannelParent::AsyncOnChannelRed
   // channel would actually redirect and not have those values on the new one.
   // We expect the URI classifier to run on the redirected channel with
   // the new URI and set these again.
   mIParentChannelFunctions.Clear();
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
-// DocumentChannelParent::nsIProcessSwitchRequestor
+// DocumentLoadListener::nsIProcessSwitchRequestor
 //-----------------------------------------------------------------------------
 
-NS_IMETHODIMP DocumentChannelParent::GetChannel(nsIChannel** aChannel) {
+NS_IMETHODIMP DocumentLoadListener::GetChannel(nsIChannel** aChannel) {
   MOZ_ASSERT(mChannel);
   nsCOMPtr<nsIChannel> channel(mChannel);
   channel.forget(aChannel);
   return NS_OK;
 }
 
-NS_IMETHODIMP DocumentChannelParent::SwitchProcessTo(
+NS_IMETHODIMP DocumentLoadListener::SwitchProcessTo(
     dom::Promise* aContentProcessIdPromise, uint64_t aIdentifier) {
   MOZ_ASSERT(NS_IsMainThread());
   NS_ENSURE_ARG(aContentProcessIdPromise);
 
   mRedirectContentProcessIdPromise =
       ContentProcessIdPromise::FromDomPromise(aContentProcessIdPromise);
   mCrossProcessRedirectIdentifier = aIdentifier;
   return NS_OK;
 }
 
 // This method returns the cached result of running the Cross-Origin-Opener
 // policy compare algorithm by calling ComputeCrossOriginOpenerPolicyMismatch
 NS_IMETHODIMP
-DocumentChannelParent::HasCrossOriginOpenerPolicyMismatch(bool* aMismatch) {
+DocumentLoadListener::HasCrossOriginOpenerPolicyMismatch(bool* aMismatch) {
   MOZ_ASSERT(aMismatch);
 
   if (!aMismatch) {
     return NS_ERROR_INVALID_ARG;
   }
 
   // If we found a COOP mismatch on an earlier channel and then
   // redirected away from that, we should use that result.
@@ -913,17 +912,17 @@ DocumentChannelParent::HasCrossOriginOpe
     *aMismatch = false;
     return NS_OK;
   }
 
   return channel->HasCrossOriginOpenerPolicyMismatch(aMismatch);
 }
 
 NS_IMETHODIMP
-DocumentChannelParent::GetCrossOriginOpenerPolicy(
+DocumentLoadListener::GetCrossOriginOpenerPolicy(
     nsILoadInfo::CrossOriginOpenerPolicy* aPolicy) {
   MOZ_ASSERT(aPolicy);
   if (!aPolicy) {
     return NS_ERROR_INVALID_ARG;
   }
 
   nsCOMPtr<nsHttpChannel> channel = do_QueryInterface(mChannel);
   if (!channel) {
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/DocumentLoadListener.h
@@ -0,0 +1,335 @@
+/* vim: set sw=2 ts=8 et tw=80 : */
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_net_DocumentLoadListener_h
+#define mozilla_net_DocumentLoadListener_h
+
+#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 "nsIInterfaceRequestor.h"
+#include "nsIObserver.h"
+#include "nsIParentChannel.h"
+#include "nsIParentRedirectingChannel.h"
+#include "nsIProcessSwitchRequestor.h"
+#include "nsIRedirectResultListener.h"
+
+#define DOCUMENT_LOAD_LISTENER_IID                   \
+  {                                                  \
+    0x3b393c56, 0x9e01, 0x11e9, {                    \
+      0xa2, 0xa3, 0x2a, 0x2a, 0xe2, 0xdb, 0xcc, 0xe4 \
+    }                                                \
+  }
+
+namespace mozilla {
+namespace net {
+
+/**
+ * DocumentLoadListener represents a connecting document load for a
+ * CanonicalBrowsingContext (in the parent process).
+ *
+ * It creates a network channel for the document load and then waits for it to
+ * receive a response (after all redirects are resolved). It then decides where
+ * to handle that load (could be in a different process from the initiator),
+ * and then sets up a real network nsIChannel to deliver the data to the final
+ * destination docshell, maybe through an nsIParentChannel/nsIChildChannel IPDL
+ * layer.
+ *
+ * In the case where this was initiated from an nsDocShell, we also create an
+ * nsIChannel to act as a placeholder within the docshell while this process
+ * completes, and then notify the docshell of a 'redirect' when we replace this
+ * channel with the real one.
+ */
+
+// TODO: We currently don't implement nsIProgressEventSink and forward those
+// to the child. Should we? We get the interface requested.
+class DocumentLoadListener : public nsIInterfaceRequestor,
+                             public nsIAsyncVerifyRedirectReadyCallback,
+                             public nsIParentChannel,
+                             public nsIChannelEventSink,
+                             public HttpChannelSecurityWarningReporter,
+                             public nsIProcessSwitchRequestor {
+ public:
+  explicit DocumentLoadListener(const dom::PBrowserOrId& aIframeEmbedding,
+                                nsILoadContext* aLoadContext,
+                                PBOverrideStatus aOverrideStatus,
+                                ADocumentChannelBridge* aBridge);
+
+  // Creates the channel, and then calls AsyncOpen on it.
+  bool Open(nsDocShellLoadState* aLoadState, class LoadInfo* aLoadInfo,
+            const nsString* aInitiatorType, nsLoadFlags aLoadFlags,
+            uint32_t aLoadType, uint32_t aCacheKey, bool aIsActive,
+            bool aIsTopLevelDoc, bool aHasNonEmptySandboxingFlags,
+            const Maybe<ipc::URIParams>& aTopWindowURI,
+            const Maybe<ipc::PrincipalInfo>& aContentBlockingAllowListPrincipal,
+            const nsString& aCustomUserAgent, const uint64_t& aChannelId,
+            const TimeStamp& aAsyncOpenTime, nsresult* aRv);
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIREQUESTOBSERVER
+  NS_DECL_NSISTREAMLISTENER
+  NS_DECL_NSIPARENTCHANNEL
+  NS_DECL_NSIINTERFACEREQUESTOR
+  NS_DECL_NSIASYNCVERIFYREDIRECTREADYCALLBACK
+  NS_DECL_NSICHANNELEVENTSINK
+  NS_DECL_NSIPROCESSSWITCHREQUESTOR
+
+  // We suspend the underlying channel when replacing ourselves with
+  // the real listener channel.
+  // This helper resumes the underlying channel again, and manually
+  // forwards any nsIStreamListener messages that arrived while we
+  // were suspended (which might have failed).
+  void ResumeSuspendedChannel(nsIStreamListener* aListener);
+
+  NS_DECLARE_STATIC_IID_ACCESSOR(DOCUMENT_LOAD_LISTENER_IID)
+
+  void Cancel(const nsresult& status);
+  void Suspend();
+  void Resume();
+
+  nsresult ReportSecurityMessage(const nsAString& aMessageTag,
+                                 const nsAString& aMessageCategory) override {
+    ReportSecurityMessageParams params;
+    params.mMessageTag = aMessageTag;
+    params.mMessageCategory = aMessageCategory;
+    mSecurityWarningFunctions.AppendElement(
+        SecurityWarningFunction{VariantIndex<0>{}, std::move(params)});
+    return NS_OK;
+  }
+
+  nsresult LogBlockedCORSRequest(const nsAString& aMessage,
+                                 const nsACString& aCategory) override {
+    LogBlockedCORSRequestParams params;
+    params.mMessage = aMessage;
+    params.mCategory = aCategory;
+    mSecurityWarningFunctions.AppendElement(
+        SecurityWarningFunction{VariantIndex<1>{}, std::move(params)});
+    return NS_OK;
+  }
+
+  nsresult LogMimeTypeMismatch(const nsACString& aMessageName, bool aWarning,
+                               const nsAString& aURL,
+                               const nsAString& aContentType) override {
+    LogMimeTypeMismatchParams params;
+    params.mMessageName = aMessageName;
+    params.mWarning = aWarning;
+    params.mURL = aURL;
+    params.mContentType = aContentType;
+    mSecurityWarningFunctions.AppendElement(
+        SecurityWarningFunction{VariantIndex<2>{}, std::move(params)});
+    return NS_OK;
+  }
+
+  // Called by the bridge when it disconnects, so that we can drop
+  // our reference to it.
+  void DocumentChannelBridgeDisconnected();
+
+  base::ProcessId OtherPid() const {
+    if (mDocumentChannelBridge) {
+      return mDocumentChannelBridge->OtherPid();
+    }
+    return 0;
+  }
+
+  bool AttachStreamFilter(
+      ipc::Endpoint<mozilla::extensions::PStreamFilterParent>&& aEndpoint) {
+    if (mDocumentChannelBridge) {
+      return mDocumentChannelBridge->AttachStreamFilter(std::move(aEndpoint));
+    }
+    return false;
+  }
+
+  // Serializes all data needed to setup the new replacement channel
+  // in the content process into the RedirectToRealChannelArgs struct.
+  void SerializeRedirectData(RedirectToRealChannelArgs& aArgs,
+                             bool aIsCrossProcess, uint32_t aRedirectFlags,
+                             uint32_t aLoadFlags);
+
+ protected:
+  virtual ~DocumentLoadListener() = default;
+
+  // Initiates the switch from DocumentChannel to the real protocol-specific
+  // channel, and ensures that RedirectToRealChannelFinished is called when
+  // this is complete.
+  void TriggerRedirectToRealChannel(
+      const Maybe<uint64_t>& aDestinationProcess = Nothing());
+
+  // Called once the content-process side on setting up a replacement
+  // channel is complete. May wait for the new parent channel to
+  // finish, and then calls into FinishReplacementChannelSetup.
+  void RedirectToRealChannelFinished(nsresult aRv);
+
+  // Completes the replacement of the new channel.
+  // This redirects the ParentChannelListener to forward any future
+  // messages to the new channel, manually forwards any being held
+  // by us, and resumes the underlying source channel.
+  void FinishReplacementChannelSetup(bool aSucceeded);
+
+  // Called when we have a cross-process switch promise. Waits on the
+  // promise, and then call TriggerRedirectToRealChannel with the
+  // provided content process id.
+  void TriggerCrossProcessSwitch();
+
+  // A helper for TriggerRedirectToRealChannel that abstracts over
+  // the same-process and cross-process switch cases and returns
+  // a single promise to wait on.
+  RefPtr<PDocumentChannelParent::RedirectToRealChannelPromise>
+  RedirectToRealChannel(uint32_t aRedirectFlags, uint32_t aLoadFlags,
+                        const Maybe<uint64_t>& aDestinationProcess);
+
+  // 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&
+  // aProvider, const nsACString& aFullHash) = 0;
+  // SetClassifierMatchedTrackingInfo(const nsACString& aLists, const
+  // nsACString& aFullHashes) = 0; NotifyClassificationFlags(uint32_t
+  // aClassificationFlags, bool aIsThirdParty) = 0;
+  struct ClassifierMatchedInfoParams {
+    nsCString mList;
+    nsCString mProvider;
+    nsCString mFullHash;
+  };
+
+  struct ClassifierMatchedTrackingInfoParams {
+    nsCString mLists;
+    nsCString mFullHashes;
+  };
+
+  struct ClassificationFlagsParams {
+    uint32_t mClassificationFlags;
+    bool mIsThirdParty;
+  };
+
+  struct NotifyChannelClassifierProtectionDisabledParams {
+    uint32_t mAcceptedReason;
+  };
+
+  struct NotifyCookieAllowedParams {};
+
+  struct NotifyCookieBlockedParams {
+    uint32_t mRejectedReason;
+  };
+
+  typedef mozilla::Variant<
+      nsIHttpChannel::FlashPluginState, ClassifierMatchedInfoParams,
+      ClassifierMatchedTrackingInfoParams, ClassificationFlagsParams,
+      NotifyChannelClassifierProtectionDisabledParams,
+      NotifyCookieAllowedParams, NotifyCookieBlockedParams>
+      IParentChannelFunction;
+
+  // Store a list of all the attribute setters that have been called on this
+  // channel, so that we can repeat them on the real channel that we redirect
+  // to.
+  nsTArray<IParentChannelFunction> mIParentChannelFunctions;
+
+  // This defines a variant this describes all the functions
+  // from HttpChannelSecurityWarningReporter so that we can forward
+  // them on to the real channel.
+
+  struct ReportSecurityMessageParams {
+    nsString mMessageTag;
+    nsString mMessageCategory;
+  };
+
+  struct LogBlockedCORSRequestParams {
+    nsString mMessage;
+    nsCString mCategory;
+  };
+
+  struct LogMimeTypeMismatchParams {
+    nsCString mMessageName;
+    bool mWarning;
+    nsString mURL;
+    nsString mContentType;
+  };
+
+  typedef mozilla::Variant<ReportSecurityMessageParams,
+                           LogBlockedCORSRequestParams,
+                           LogMimeTypeMismatchParams>
+      SecurityWarningFunction;
+  nsTArray<SecurityWarningFunction> mSecurityWarningFunctions;
+
+  struct OnDataAvailableRequest {
+    nsCString data;
+    uint64_t offset;
+    uint32_t count;
+  };
+  // TODO Backtrack this.
+  nsTArray<OnDataAvailableRequest> mPendingRequests;
+  Maybe<nsresult> mStopRequestValue;
+
+  nsCOMPtr<nsIChannel> mChannel;
+
+  // An instance of ParentChannelListener that we use as a listener
+  // between mChannel (and any future redirected mChannels) and us.
+  // This handles service worker interception, and retargetting
+  // OnDataAvailable/OnStopRequest messages onto the listener that
+  // replaces us.
+  RefPtr<ParentChannelListener> mParentChannelListener;
+
+  // The bridge to the nsIChannel in the originating docshell.
+  // This reference forms a cycle with the bridge, and we expect
+  // the bridge to call DisonnectDocumentChannelBridge when it
+  // shuts down to break this.
+  RefPtr<ADocumentChannelBridge> mDocumentChannelBridge;
+
+  nsCOMPtr<nsILoadContext> mLoadContext;
+
+  PBOverrideStatus mPBOverride;
+
+  // The original URI of the current channel. If there are redirects,
+  // then the value on the channel gets overwritten with the original
+  // URI of the first channel in the redirect chain, so we cache the
+  // value we need here.
+  nsCOMPtr<nsIURI> mChannelCreationURI;
+
+  nsTArray<DocumentChannelRedirect> mRedirects;
+
+  // Corresponding redirect channel registrar Id for the final channel that
+  // we want to use when redirecting the child, or doing a process switch.
+  // 0 means redirection is not started.
+  uint32_t mRedirectChannelId = 0;
+  // Set to true if we called Suspend on mChannel to initiate our redirect.
+  // This isn't currently set when we do a process swap, since that gets
+  // initiated in nsHttpChannel.
+  bool mSuspendedChannel = false;
+  // Set to true if we're currently in the middle of replacing this with
+  // a new channel connected a different process.
+  bool mDoingProcessSwitch = false;
+  // The value of GetApplyConversion on mChannel when OnStartRequest
+  // was called. We override it to false to prevent a conversion
+  // helper from being installed, but we need to restore the value
+  // later.
+  bool mOldApplyConversion = false;
+  // Set to true if any previous channel that we redirected away
+  // from had a COOP mismatch.
+  bool mHasCrossOriginOpenerPolicyMismatch = false;
+
+  typedef MozPromise<uint64_t, nsresult, true /* exclusive */>
+      ContentProcessIdPromise;
+  // This promise is set following a on-may-change-process observer
+  // notification when the associated channel is getting relocated to another
+  // process. It will be resolved when that process is set up.
+  RefPtr<ContentProcessIdPromise> mRedirectContentProcessIdPromise;
+  // This identifier is set at the same time as the
+  // mRedirectContentProcessIdPromise.
+  // This identifier is later passed to the childChannel in order to identify it
+  // once the promise is resolved.
+  uint64_t mCrossProcessRedirectIdentifier = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(DocumentLoadListener, DOCUMENT_LOAD_LISTENER_IID)
+
+}  // namespace net
+}  // namespace mozilla
+
+#endif  // mozilla_net_DocumentChannelParent_h
--- a/netwerk/ipc/moz.build
+++ b/netwerk/ipc/moz.build
@@ -1,18 +1,20 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXPORTS.mozilla.net += [
+    'ADocumentChannelBridge.h',
     'ChannelEventQueue.h',
     'DocumentChannelChild.h',
     'DocumentChannelParent.h',
+    'DocumentLoadListener.h',
     'NeckoChild.h',
     'NeckoCommon.h',
     'NeckoMessageUtils.h',
     'NeckoParent.h',
     'NeckoTargetHolder.h',
     'SocketProcessBridgeChild.h',
     'SocketProcessBridgeParent.h',
     'SocketProcessChild.h',
@@ -20,16 +22,17 @@ EXPORTS.mozilla.net += [
     'SocketProcessImpl.h',
     'SocketProcessParent.h',
 ]
 
 UNIFIED_SOURCES += [
     'ChannelEventQueue.cpp',
     'DocumentChannelChild.cpp',
     'DocumentChannelParent.cpp',
+    'DocumentLoadListener.cpp',
     'NeckoChild.cpp',
     'NeckoCommon.cpp',
     'NeckoParent.cpp',
     'NeckoTargetHolder.cpp',
     'SocketProcessBridgeChild.cpp',
     'SocketProcessBridgeParent.cpp',
     'SocketProcessChild.cpp',
     'SocketProcessHost.cpp',
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -2600,17 +2600,17 @@ void nsHttpChannel::AssertNotDocumentCha
     // why. Disable the assertion in non-fission non-debug configurations to
     // avoid crashing user's browsers until we're done dogfooding fission.
     return;
   }
 #endif
 
   nsCOMPtr<nsIParentChannel> parentChannel;
   NS_QueryNotificationCallbacks(this, parentChannel);
-  RefPtr<DocumentChannelParent> documentChannelParent =
+  RefPtr<DocumentLoadListener> documentChannelParent =
       do_QueryObject(parentChannel);
   if (documentChannelParent) {
     // The load is using document channel.
     return;
   }
 
   RefPtr<HttpChannelParent> httpParent = do_QueryObject(parentChannel);
   if (!httpParent) {
@@ -6897,33 +6897,33 @@ nsHttpChannel::SetChannelIsForDownload(b
 }
 
 base::ProcessId nsHttpChannel::ProcessId() {
   nsCOMPtr<nsIParentChannel> parentChannel;
   NS_QueryNotificationCallbacks(this, parentChannel);
   if (RefPtr<HttpChannelParent> httpParent = do_QueryObject(parentChannel)) {
     return httpParent->OtherPid();
   }
-  if (RefPtr<DocumentChannelParent> docParent = do_QueryObject(parentChannel)) {
+  if (RefPtr<DocumentLoadListener> docParent = do_QueryObject(parentChannel)) {
     return docParent->OtherPid();
   }
   return base::GetCurrentProcId();
 }
 
 bool nsHttpChannel::AttachStreamFilter(
     mozilla::ipc::Endpoint<extensions::PStreamFilterParent>&& aEndpoint)
 
 {
   nsCOMPtr<nsIParentChannel> parentChannel;
   NS_QueryNotificationCallbacks(this, parentChannel);
   if (RefPtr<HttpChannelParent> httpParent = do_QueryObject(parentChannel)) {
     return httpParent->SendAttachStreamFilter(std::move(aEndpoint));
   }
-  if (RefPtr<DocumentChannelParent> docParent = do_QueryObject(parentChannel)) {
-    return docParent->SendAttachStreamFilter(std::move(aEndpoint));
+  if (RefPtr<DocumentLoadListener> docParent = do_QueryObject(parentChannel)) {
+    return docParent->AttachStreamFilter(std::move(aEndpoint));
   }
 
   extensions::StreamFilterParent::Attach(this, std::move(aEndpoint));
   return true;
 }
 
 NS_IMETHODIMP
 nsHttpChannel::GetNavigationStartTimeStamp(TimeStamp* aTimeStamp) {
@@ -7295,17 +7295,17 @@ NS_IMETHODIMP nsHttpChannel::SwitchProce
   MOZ_ASSERT(NS_IsMainThread());
   NS_ENSURE_ARG(aContentProcessIdPromise);
 
   LOG(("nsHttpChannel::SwitchProcessTo [this=%p]", this));
   LogCallingScriptLocation(this);
 
   nsCOMPtr<nsIParentChannel> parentChannel;
   NS_QueryNotificationCallbacks(this, parentChannel);
-  RefPtr<DocumentChannelParent> documentChannelParent =
+  RefPtr<DocumentLoadListener> documentChannelParent =
       do_QueryObject(parentChannel);
   // This is a temporary change as the DocumentChannelParent currently must go
   // through the nsHttpChannel to perform a process switch via SessionStore.
   if (!documentChannelParent) {
     // We cannot do this after OnStartRequest of the listener has been called.
     NS_ENSURE_FALSE(mOnStartRequestCalled, NS_ERROR_NOT_AVAILABLE);
   }