Backed out 7 changesets (bug 1576714) for fission permafailures on test_bug590812.html. a=backout
authorCsoregi Natalia <ncsoregi@mozilla.com>
Sat, 05 Oct 2019 00:08:33 +0300
changeset 496407 e966603209bba385cac292c937cd74d9426a3412
parent 496368 8d68e16d3d775740b85377b775eecb2173c6a03c
child 496408 74c62117e3e5215f69a07e5ba0adddae33773060
push id114143
push userrgurzau@mozilla.com
push dateMon, 07 Oct 2019 09:35:08 +0000
treeherdermozilla-inbound@3955e0a93047 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbackout
bugs1576714, 590812
milestone71.0a1
backs outd0c49f00eb917de66bc2c4ffa445048a1b5f0aaa
faecc9f35b49458cba661f961a70a21292360d77
2e156655c31ede8e8b39362a1682efba907ac7d6
eece722082c79c0750544d4933e9b43dd419e0f6
ebda40f9688438654a50190c8b91ffa1091ce476
7dce423417d8fdf37955b6facdfe900b2aac0472
9a5072019168f59cc503570c39d698b2082d4c30
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out 7 changesets (bug 1576714) for fission permafailures on test_bug590812.html. a=backout Backed out changeset d0c49f00eb91 (bug 1576714) Backed out changeset faecc9f35b49 (bug 1576714) Backed out changeset 2e156655c31e (bug 1576714) Backed out changeset eece722082c7 (bug 1576714) Backed out changeset ebda40f96884 (bug 1576714) Backed out changeset 7dce423417d8 (bug 1576714) Backed out changeset 9a5072019168 (bug 1576714)
browser/components/sessionstore/SessionStore.jsm
browser/components/sessionstore/test/browser.ini
docshell/base/CanonicalBrowsingContext.cpp
docshell/base/CanonicalBrowsingContext.h
docshell/base/nsDocShell.cpp
dom/base/nsFrameLoader.h
dom/base/nsFrameLoaderOwner.cpp
dom/base/nsFrameLoaderOwner.h
dom/chrome-webidl/BrowsingContext.webidl
dom/chrome-webidl/WindowGlobalActors.webidl
dom/ipc/BrowserBridgeChild.cpp
dom/ipc/BrowserBridgeChild.h
dom/ipc/BrowserBridgeParent.cpp
dom/ipc/BrowserBridgeParent.h
dom/ipc/ContentChild.cpp
dom/ipc/PWindowGlobal.ipdl
dom/ipc/WindowGlobalChild.cpp
dom/ipc/WindowGlobalChild.h
dom/ipc/WindowGlobalParent.cpp
dom/ipc/WindowGlobalParent.h
netwerk/ipc/DocumentChannelParent.cpp
netwerk/ipc/DocumentChannelParent.h
netwerk/protocol/http/HttpChannelParent.cpp
netwerk/protocol/http/HttpChannelParent.h
netwerk/test/browser/browser.ini
netwerk/test/browser/browser_cross_process_redirect.js
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -2596,17 +2596,18 @@ var SessionStoreInternal = {
         aBrowsingContext.embedderElement,
         aRemoteType,
         aChannel,
         aSwitchId,
         aReplaceBrowsingContext
       );
     }
 
-    return aBrowsingContext.changeFrameRemoteness(aRemoteType, aSwitchId);
+    let wg = aBrowsingContext.embedderWindowGlobal;
+    return wg.changeFrameRemoteness(aBrowsingContext, aRemoteType, aSwitchId);
   },
 
   // Examine the channel response to see if we should change the process
   // performing the given load. aRequestor implements nsIProcessSwitchRequestor
   onMayChangeProcess(aRequestor) {
     if (
       !E10SUtils.useHttpResponseProcessSelection() &&
       !E10SUtils.useCrossOriginOpenerPolicy()
--- a/browser/components/sessionstore/test/browser.ini
+++ b/browser/components/sessionstore/test/browser.ini
@@ -191,16 +191,17 @@ skip-if = (os == 'linux' && e10s && (deb
 [browser_586147.js]
 [browser_586068-apptabs.js]
 [browser_586068-apptabs_ondemand.js]
 skip-if = (verify && (os == 'mac' || os == 'win'))
 [browser_586068-browser_state_interrupted.js]
 [browser_586068-cascade.js]
 [browser_586068-multi_window.js]
 [browser_586068-reload.js]
+fail-if = fission
 skip-if = fission && debug # Crashes intermittently: @ mozilla::net::HttpChannelChild::DoOnStartRequest(nsIRequest*, nsISupports*)
 [browser_586068-select.js]
 [browser_586068-window_state.js]
 [browser_586068-window_state_override.js]
 [browser_588426.js]
 [browser_590268.js]
 [browser_590563.js]
 [browser_595601-restore_hidden.js]
--- a/docshell/base/CanonicalBrowsingContext.cpp
+++ b/docshell/base/CanonicalBrowsingContext.cpp
@@ -4,20 +4,16 @@
  * 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 "mozilla/dom/CanonicalBrowsingContext.h"
 
 #include "mozilla/dom/BrowsingContextGroup.h"
 #include "mozilla/dom/WindowGlobalParent.h"
 #include "mozilla/dom/ContentProcessManager.h"
-#include "mozilla/ipc/ProtocolUtils.h"
-#include "mozilla/NullPrincipal.h"
-
-using namespace mozilla::ipc;
 
 extern mozilla::LazyLogModule gAutoplayPermissionLog;
 
 #define AUTOPLAY_LOG(msg, ...) \
   MOZ_LOG(gAutoplayPermissionLog, LogLevel::Debug, (msg, ##__VA_ARGS__))
 
 namespace mozilla {
 namespace dom {
@@ -193,262 +189,10 @@ void CanonicalBrowsingContext::UpdateMed
   if (window) {
     window->UpdateMediaAction(aAction);
   }
   Group()->EachParent([&](ContentParent* aParent) {
     Unused << aParent->SendUpdateMediaAction(this, aAction);
   });
 }
 
-namespace {
-
-using NewOrUsedPromise = MozPromise<RefPtr<ContentParent>, nsresult, false>;
-
-// NOTE: This method is currently a dummy, and always actually spawns sync. It
-// mostly exists so I can test out the async API right now.
-RefPtr<NewOrUsedPromise> GetNewOrUsedBrowserProcessAsync(
-    const nsAString& aRemoteType) {
-  RefPtr<ContentParent> contentParent =
-      ContentParent::GetNewOrUsedBrowserProcess(
-          nullptr, aRemoteType, hal::PROCESS_PRIORITY_FOREGROUND, nullptr,
-          false);
-  if (!contentParent) {
-    return NewOrUsedPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
-  }
-  return NewOrUsedPromise::CreateAndResolve(contentParent, __func__);
-}
-
-}  // anonymous namespace
-
-void CanonicalBrowsingContext::PendingRemotenessChange::Complete(
-    ContentParent* aContentParent) {
-  if (!mPromise) {
-    return;
-  }
-
-  RefPtr<CanonicalBrowsingContext> target(mTarget);
-  RefPtr<WindowGlobalParent> embedderWindow = target->GetEmbedderWindowGlobal();
-  if (NS_WARN_IF(!embedderWindow) || NS_WARN_IF(!embedderWindow->CanSend())) {
-    Cancel(NS_ERROR_FAILURE);
-    return;
-  }
-
-  RefPtr<BrowserParent> embedderBrowser = embedderWindow->GetBrowserParent();
-  if (NS_WARN_IF(!embedderBrowser)) {
-    Cancel(NS_ERROR_FAILURE);
-    return;
-  }
-
-  // Pull load flags from our embedder browser.
-  nsCOMPtr<nsILoadContext> loadContext = embedderBrowser->GetLoadContext();
-  MOZ_DIAGNOSTIC_ASSERT(
-      loadContext->UseRemoteTabs() && loadContext->UseRemoteSubframes(),
-      "Not supported without fission");
-
-  // NOTE: These are the only flags we actually care about
-  uint32_t chromeFlags = nsIWebBrowserChrome::CHROME_REMOTE_WINDOW |
-                         nsIWebBrowserChrome::CHROME_FISSION_WINDOW;
-  if (loadContext->UsePrivateBrowsing()) {
-    chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW;
-  }
-
-  TabId tabId(nsContentUtils::GenerateTabId());
-  RefPtr<BrowserBridgeParent> bridge = new BrowserBridgeParent();
-  ManagedEndpoint<PBrowserBridgeChild> endpoint =
-      embedderBrowser->OpenPBrowserBridgeEndpoint(bridge);
-  if (NS_WARN_IF(!endpoint.IsValid())) {
-    Cancel(NS_ERROR_UNEXPECTED);
-    return;
-  }
-
-  RefPtr<WindowGlobalParent> oldWindow = target->mCurrentWindowGlobal;
-  RefPtr<BrowserParent> oldBrowser =
-      oldWindow ? oldWindow->GetBrowserParent() : nullptr;
-  bool wasRemote = oldWindow && oldWindow->IsProcessRoot();
-
-  // Update which process is considered the current owner
-  uint64_t inFlightProcessId = target->OwnerProcessId();
-  target->SetInFlightProcessId(inFlightProcessId);
-  target->SetOwnerProcessId(aContentParent->ChildID());
-
-  auto resetInFlightId = [target, inFlightProcessId] {
-    MOZ_DIAGNOSTIC_ASSERT(target->GetInFlightProcessId() == inFlightProcessId);
-    target->SetInFlightProcessId(0);
-  };
-
-  // If we were in a remote frame, trigger unloading of the remote window. When
-  // the original remote window acknowledges, we can clear the in-flight ID.
-  if (wasRemote) {
-    MOZ_DIAGNOSTIC_ASSERT(oldBrowser);
-    MOZ_DIAGNOSTIC_ASSERT(oldBrowser != embedderBrowser);
-    MOZ_DIAGNOSTIC_ASSERT(oldBrowser->GetBrowserBridgeParent());
-
-    oldBrowser->SendSkipBrowsingContextDetach(
-        [resetInFlightId](bool aSuccess) { resetInFlightId(); },
-        [resetInFlightId](mozilla::ipc::ResponseRejectReason aReason) {
-          resetInFlightId();
-        });
-    oldBrowser->Destroy();
-  }
-
-  // Tell the embedder process a remoteness change is in-process. When this is
-  // acknowledged, reset the in-flight ID if it used to be an in-process load.
-  embedderWindow->SendMakeFrameRemote(
-      target, std::move(endpoint), tabId,
-      [wasRemote, resetInFlightId](bool aSuccess) {
-        if (!wasRemote) {
-          resetInFlightId();
-        }
-      },
-      [wasRemote, resetInFlightId](mozilla::ipc::ResponseRejectReason aReason) {
-        if (!wasRemote) {
-          resetInFlightId();
-        }
-      });
-
-  // FIXME: We should get the correct principal for the to-be-created window so
-  // we can avoid creating unnecessary extra windows in the new process.
-  nsCOMPtr<nsIPrincipal> initialPrincipal =
-      NullPrincipal::CreateWithInheritedAttributes(
-          embedderBrowser->OriginAttributesRef(),
-          /* isFirstParty */ false);
-  WindowGlobalInit windowInit =
-      WindowGlobalActor::AboutBlankInitializer(target, initialPrincipal);
-
-  // Actually create the new BrowserParent actor and finish initialization of
-  // our new BrowserBridgeParent.
-  nsresult rv = bridge->InitWithProcess(aContentParent, EmptyString(),
-                                        windowInit, chromeFlags, tabId);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    Cancel(rv);
-    return;
-  }
-
-  RefPtr<BrowserParent> newBrowser = bridge->GetBrowserParent();
-  newBrowser->ResumeLoad(mPendingSwitchId);
-
-  // We did it! The process switch is complete.
-  mPromise->Resolve(newBrowser, __func__);
-  Clear();
-}
-
-void CanonicalBrowsingContext::PendingRemotenessChange::Cancel(nsresult aRv) {
-  if (!mPromise) {
-    return;
-  }
-
-  mPromise->Reject(aRv, __func__);
-  Clear();
-}
-
-void CanonicalBrowsingContext::PendingRemotenessChange::Clear() {
-  // Make sure we don't die while we're doing cleanup.
-  RefPtr<PendingRemotenessChange> kungFuDeathGrip(this);
-  if (mTarget) {
-    MOZ_DIAGNOSTIC_ASSERT(mTarget->mPendingRemotenessChange == this);
-    mTarget->mPendingRemotenessChange = nullptr;
-  }
-
-  mPromise = nullptr;
-  mTarget = nullptr;
-}
-
-CanonicalBrowsingContext::PendingRemotenessChange::~PendingRemotenessChange() {
-  MOZ_ASSERT(!mPromise && !mTarget,
-             "should've already been Cancel() or Complete()-ed");
-}
-
-RefPtr<CanonicalBrowsingContext::RemotenessPromise>
-CanonicalBrowsingContext::ChangeFrameRemoteness(const nsAString& aRemoteType,
-                                                uint64_t aPendingSwitchId) {
-  // Ensure our embedder hasn't been destroyed already.
-  RefPtr<WindowGlobalParent> embedderWindowGlobal = GetEmbedderWindowGlobal();
-  if (!embedderWindowGlobal) {
-    NS_WARNING("Non-embedded BrowsingContext");
-    return RemotenessPromise::CreateAndReject(NS_ERROR_UNEXPECTED, __func__);
-  }
-
-  if (!embedderWindowGlobal->CanSend()) {
-    NS_WARNING("Embedder already been destroyed.");
-    return RemotenessPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__);
-  }
-
-  RefPtr<ContentParent> oldContent = GetContentParent();
-  if (!oldContent || aRemoteType.IsEmpty()) {
-    NS_WARNING("Cannot switch to or from non-remote frame");
-    return RemotenessPromise::CreateAndReject(NS_ERROR_NOT_IMPLEMENTED,
-                                              __func__);
-  }
-
-  if (aRemoteType.Equals(oldContent->GetRemoteType())) {
-    NS_WARNING("Already in the correct process");
-    return RemotenessPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
-  }
-
-  // Cancel ongoing remoteness changes.
-  if (mPendingRemotenessChange) {
-    mPendingRemotenessChange->Cancel(NS_ERROR_ABORT);
-    MOZ_ASSERT(!mPendingRemotenessChange, "Should have cleared");
-  }
-
-  RefPtr<BrowserParent> embedderBrowser =
-      embedderWindowGlobal->GetBrowserParent();
-  MOZ_ASSERT(embedderBrowser);
-
-  // Switching to local. No new process, so perform switch sync.
-  if (aRemoteType.Equals(embedderBrowser->Manager()->GetRemoteType())) {
-    if (mCurrentWindowGlobal) {
-      MOZ_DIAGNOSTIC_ASSERT(mCurrentWindowGlobal->IsProcessRoot());
-      RefPtr<BrowserParent> oldBrowser =
-          mCurrentWindowGlobal->GetBrowserParent();
-
-      RefPtr<CanonicalBrowsingContext> target(this);
-      SetInFlightProcessId(OwnerProcessId());
-      oldBrowser->SendSkipBrowsingContextDetach(
-          [target](bool aSuccess) { target->SetInFlightProcessId(0); },
-          [target](mozilla::ipc::ResponseRejectReason aReason) {
-            target->SetInFlightProcessId(0);
-          });
-      oldBrowser->Destroy();
-    }
-
-    SetOwnerProcessId(embedderBrowser->Manager()->ChildID());
-    Unused << embedderWindowGlobal->SendMakeFrameLocal(this, aPendingSwitchId);
-    return RemotenessPromise::CreateAndResolve(embedderBrowser, __func__);
-  }
-
-  // Switching to remote. Wait for new process to launch before switch.
-  auto promise = MakeRefPtr<RemotenessPromise::Private>(__func__);
-  RefPtr<PendingRemotenessChange> change =
-      new PendingRemotenessChange(this, promise, aPendingSwitchId);
-  mPendingRemotenessChange = change;
-
-  GetNewOrUsedBrowserProcessAsync(aRemoteType)
-      ->Then(
-          GetMainThreadSerialEventTarget(), __func__,
-          [change](ContentParent* aContentParent) {
-            change->Complete(aContentParent);
-          },
-          [change](nsresult aRv) { change->Cancel(aRv); });
-  return promise.forget();
-}
-
-already_AddRefed<Promise> CanonicalBrowsingContext::ChangeFrameRemoteness(
-    const nsAString& aRemoteType, uint64_t aPendingSwitchId, ErrorResult& aRv) {
-  nsIGlobalObject* global = xpc::NativeGlobal(xpc::PrivilegedJunkScope());
-
-  RefPtr<Promise> promise = Promise::Create(global, aRv);
-  if (aRv.Failed()) {
-    return nullptr;
-  }
-
-  ChangeFrameRemoteness(aRemoteType, aPendingSwitchId)
-      ->Then(
-          GetMainThreadSerialEventTarget(), __func__,
-          [promise](BrowserParent* aBrowserParent) {
-            promise->MaybeResolve(aBrowserParent->Manager()->ChildID());
-          },
-          [promise](nsresult aRv) { promise->MaybeReject(aRv); });
-  return promise.forget();
-}
-
 }  // namespace dom
 }  // namespace mozilla
--- a/docshell/base/CanonicalBrowsingContext.h
+++ b/docshell/base/CanonicalBrowsingContext.h
@@ -5,29 +5,27 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_CanonicalBrowsingContext_h
 #define mozilla_dom_CanonicalBrowsingContext_h
 
 #include "mozilla/dom/BrowsingContext.h"
 #include "mozilla/dom/MediaController.h"
 #include "mozilla/RefPtr.h"
-#include "mozilla/MozPromise.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsWrapperCache.h"
 #include "nsTHashtable.h"
 #include "nsHashKeys.h"
 
 class nsIDocShell;
 
 namespace mozilla {
 namespace dom {
 
 class WindowGlobalParent;
-class BrowserParent;
 
 // CanonicalBrowsingContext is a BrowsingContext living in the parent
 // process, with whatever extra data that a BrowsingContext in the
 // parent needs.
 class CanonicalBrowsingContext final : public BrowsingContext {
  public:
   static already_AddRefed<CanonicalBrowsingContext> Get(uint64_t aId);
   static CanonicalBrowsingContext* Cast(BrowsingContext* aContext);
@@ -73,75 +71,38 @@ class CanonicalBrowsingContext final : p
   // set the media mute property for the top level window and propagate it to
   // other top level windows in other processes.
   void NotifyMediaMutedChanged(bool aMuted);
 
   // This function would update the media action for the current outer window
   // and propogate the action to other browsing contexts in content processes.
   void UpdateMediaAction(MediaControlActions aAction);
 
-  using RemotenessPromise = MozPromise<RefPtr<BrowserParent>, nsresult, false>;
-  RefPtr<RemotenessPromise> ChangeFrameRemoteness(const nsAString& aRemoteType,
-                                                  uint64_t aPendingSwitchId);
-
-  // Helper version for WebIDL - resolves to the PID where the load is being
-  // resumed.
-  already_AddRefed<Promise> ChangeFrameRemoteness(const nsAString& aRemoteType,
-                                                  uint64_t aPendingSwitchId,
-                                                  ErrorResult& aRv);
-
  protected:
   void Traverse(nsCycleCollectionTraversalCallback& cb);
   void Unlink();
 
   using Type = BrowsingContext::Type;
   CanonicalBrowsingContext(BrowsingContext* aParent,
                            BrowsingContextGroup* aGroup,
                            uint64_t aBrowsingContextId, uint64_t aProcessId,
                            Type aType);
 
  private:
   friend class BrowsingContext;
 
-  class PendingRemotenessChange {
-   public:
-    NS_INLINE_DECL_REFCOUNTING(PendingRemotenessChange)
-
-    PendingRemotenessChange(CanonicalBrowsingContext* aTarget,
-                            RemotenessPromise::Private* aPromise,
-                            uint64_t aPendingSwitchId)
-        : mTarget(aTarget),
-          mPromise(aPromise),
-          mPendingSwitchId(aPendingSwitchId) {}
-
-    void Cancel(nsresult aRv);
-    void Complete(ContentParent* aContentParent);
-
-   private:
-    ~PendingRemotenessChange();
-    void Clear();
-
-    RefPtr<CanonicalBrowsingContext> mTarget;
-    RefPtr<RemotenessPromise::Private> mPromise;
-
-    uint64_t mPendingSwitchId;
-  };
-
   // XXX(farre): Store a ContentParent pointer here rather than mProcessId?
   // Indicates which process owns the docshell.
   uint64_t mProcessId;
 
   // The ID of the former owner process during an ownership change, which may
   // have in-flight messages that assume it is still the owner.
   uint64_t mInFlightProcessId = 0;
 
   // All live window globals within this browsing context.
   nsTHashtable<nsRefPtrHashKey<WindowGlobalParent>> mWindowGlobals;
   RefPtr<WindowGlobalParent> mCurrentWindowGlobal;
-
-  // The current remoteness change which is in a pending state.
-  RefPtr<PendingRemotenessChange> mPendingRemotenessChange;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // !defined(mozilla_dom_CanonicalBrowsingContext_h)
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -7718,21 +7718,19 @@ nsresult nsDocShell::RestoreFromHistory(
     // We don't plan to save a viewer in mOSHE; tell it to drop
     // any other state it's holding.
     mOSHE->SyncPresentationState();
   }
 
   // Order the mContentViewer setup just like Embed does.
   mContentViewer = nullptr;
 
-  if (!mSkipBrowsingContextDetachOnDestroy) {
-    // Move the browsing ontext's children to the cache. If we're
-    // detaching them, we'll detach them from there.
-    mBrowsingContext->CacheChildren();
-  }
+  // Move the browsing ontext's children to the cache. If we're
+  // detaching them, we'll detach them from there.
+  mBrowsingContext->CacheChildren();
 
   // Now that we're about to switch documents, forget all of our children.
   // Note that we cached them as needed up in CaptureState above.
   DestroyChildren();
 
   mContentViewer.swap(viewer);
 
   // Grab all of the related presentation from the SHEntry now.
--- a/dom/base/nsFrameLoader.h
+++ b/dom/base/nsFrameLoader.h
@@ -88,17 +88,16 @@ typedef struct _GtkWidget GtkWidget;
     }                                                \
   }
 
 class nsFrameLoader final : public nsStubMutationObserver,
                             public mozilla::dom::ipc::MessageManagerCallback,
                             public nsWrapperCache {
   friend class AutoResetInShow;
   friend class AutoResetInFrameSwap;
-  friend class nsFrameLoaderOwner;
   typedef mozilla::dom::Document Document;
   typedef mozilla::dom::Element Element;
   typedef mozilla::dom::BrowserParent BrowserParent;
   typedef mozilla::dom::BrowserBridgeChild BrowserBridgeChild;
   typedef mozilla::dom::BrowsingContext BrowsingContext;
 
  public:
   // Called by Frame Elements to create a new FrameLoader.
--- a/dom/base/nsFrameLoaderOwner.cpp
+++ b/dom/base/nsFrameLoaderOwner.cpp
@@ -10,20 +10,16 @@
 #include "nsSubDocumentFrame.h"
 #include "nsQueryObject.h"
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/dom/BrowsingContext.h"
 #include "mozilla/dom/FrameLoaderBinding.h"
 #include "mozilla/dom/HTMLIFrameElement.h"
 #include "mozilla/dom/MozFrameLoaderOwnerBinding.h"
 #include "mozilla/ScopeExit.h"
-#include "mozilla/dom/BrowserBridgeChild.h"
-#include "mozilla/dom/ContentParent.h"
-#include "mozilla/dom/BrowserBridgeHost.h"
-#include "mozilla/dom/BrowserHost.h"
 #include "mozilla/StaticPrefs_fission.h"
 #include "mozilla/EventStateManager.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 already_AddRefed<nsFrameLoader> nsFrameLoaderOwner::GetFrameLoader() {
   return do_AddRef(mFrameLoader);
@@ -64,65 +60,79 @@ bool nsFrameLoaderOwner::ShouldPreserveB
   }
 
   // We will preserve our browsing context if either fission is enabled, or the
   // `preserve_browsing_contexts` pref is active.
   return UseRemoteSubframes() ||
          StaticPrefs::fission_preserve_browsing_contexts();
 }
 
-void nsFrameLoaderOwner::ChangeRemotenessCommon(
-    bool aPreserveContext, const nsAString& aRemoteType,
-    std::function<void()>& aFrameLoaderInit, mozilla::ErrorResult& aRv) {
+void nsFrameLoaderOwner::ChangeRemoteness(
+    const mozilla::dom::RemotenessOptions& aOptions, mozilla::ErrorResult& rv) {
   RefPtr<mozilla::dom::BrowsingContext> bc;
   bool networkCreated = false;
 
   // In this case, we're not reparenting a frameloader, we're just destroying
   // our current one and creating a new one, so we can use ourselves as the
   // owner.
   RefPtr<Element> owner = do_QueryObject(this);
   MOZ_ASSERT(owner);
 
   // When we destroy the original frameloader, it will stop blocking the parent
   // document's load event, and immediately trigger the load event if there are
   // no other blockers. Since we're going to be adding a new blocker as soon as
   // we recreate the frame loader, this is not what we want, so add our own
   // blocker until the process is complete.
   Document* doc = owner->OwnerDoc();
   doc->BlockOnload();
-  auto cleanup = MakeScopeExit([&]() { doc->UnblockOnload(false); });
+  auto cleanup = MakeScopeExit([&]() {
+    doc->UnblockOnload(false);
+  });
 
   // If we already have a Frameloader, destroy it, possibly preserving its
   // browsing context.
   if (mFrameLoader) {
-    if (aPreserveContext) {
+    if (ShouldPreserveBrowsingContext(aOptions)) {
       bc = mFrameLoader->GetBrowsingContext();
       mFrameLoader->SkipBrowsingContextDetach();
     }
 
     // Preserve the networkCreated status, as nsDocShells created after a
     // process swap may shouldn't change their dynamically-created status.
     networkCreated = mFrameLoader->IsNetworkCreated();
     mFrameLoader->Destroy();
     mFrameLoader = nullptr;
   }
 
   mFrameLoader =
-      nsFrameLoader::Recreate(owner, bc, aRemoteType, networkCreated);
+      nsFrameLoader::Recreate(owner, bc, aOptions.mRemoteType, networkCreated);
+
   if (NS_WARN_IF(!mFrameLoader)) {
-    aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
-  // Invoke the frame loader initialization callback to perform setup on our new
-  // nsFrameLoader. This may cause our ErrorResult to become errored, so
-  // double-check after calling.
-  aFrameLoaderInit();
-  if (NS_WARN_IF(aRv.Failed())) {
-    return;
+  if (aOptions.mError.WasPassed()) {
+    nsCOMPtr<nsIURI> uri;
+    rv = NS_NewURI(getter_AddRefs(uri), "about:blank");
+    if (NS_WARN_IF(rv.Failed())) {
+      return;
+    }
+
+    nsDocShell* docShell = mFrameLoader->GetDocShell(rv);
+    if (NS_WARN_IF(rv.Failed())) {
+      return;
+    }
+    bool displayed = false;
+    docShell->DisplayLoadError(static_cast<nsresult>(aOptions.mError.Value()),
+                               uri, u"about:blank", nullptr, &displayed);
+
+  } else if (aOptions.mPendingSwitchID.WasPassed()) {
+    mFrameLoader->ResumeLoad(aOptions.mPendingSwitchID.Value());
+  } else {
+    mFrameLoader->LoadFrame(false);
   }
 
   // Now that we've got a new FrameLoader, we need to reset our
   // nsSubDocumentFrame to use the new FrameLoader.
   if (nsSubDocumentFrame* ourFrame = do_QueryFrame(owner->GetPrimaryFrame())) {
     ourFrame->ResetFrameLoader();
   }
 
@@ -135,87 +145,17 @@ void nsFrameLoaderOwner::ChangeRemotenes
   }
 
   if (owner->GetPrimaryFrame()) {
     EventStateManager* eventManager =
         owner->GetPrimaryFrame()->PresContext()->EventStateManager();
     eventManager->RecomputeMouseEnterStateForRemoteFrame(*owner);
   }
 
-  if (owner->IsXULElement()) {
-    // Assuming this element is a XULFrameElement, once we've reset our
-    // FrameLoader, fire an event to act like we've recreated ourselves, similar
-    // to what XULFrameElement does after rebinding to the tree.
-    // ChromeOnlyDispatch is turns on to make sure this isn't fired into
-    // content.
-    (new mozilla::AsyncEventDispatcher(
-         owner, NS_LITERAL_STRING("XULFrameLoaderCreated"),
-         mozilla::CanBubble::eYes, mozilla::ChromeOnlyDispatch::eYes))
-        ->RunDOMEventWhenSafe();
-  }
+  // Assuming this element is a XULFrameElement, once we've reset our
+  // FrameLoader, fire an event to act like we've recreated ourselves, similar
+  // to what XULFrameElement does after rebinding to the tree.
+  // ChromeOnlyDispatch is turns on to make sure this isn't fired into content.
+  (new mozilla::AsyncEventDispatcher(
+       owner, NS_LITERAL_STRING("XULFrameLoaderCreated"),
+       mozilla::CanBubble::eYes, mozilla::ChromeOnlyDispatch::eYes))
+      ->RunDOMEventWhenSafe();
 }
-
-void nsFrameLoaderOwner::ChangeRemoteness(
-    const mozilla::dom::RemotenessOptions& aOptions, mozilla::ErrorResult& rv) {
-  std::function<void()> frameLoaderInit = [&] {
-    if (aOptions.mError.WasPassed()) {
-      nsCOMPtr<nsIURI> uri;
-      rv = NS_NewURI(getter_AddRefs(uri), "about:blank");
-      if (NS_WARN_IF(rv.Failed())) {
-        return;
-      }
-
-      nsDocShell* docShell = mFrameLoader->GetDocShell(rv);
-      if (NS_WARN_IF(rv.Failed())) {
-        return;
-      }
-      bool displayed = false;
-      docShell->DisplayLoadError(static_cast<nsresult>(aOptions.mError.Value()),
-                                 uri, u"about:blank", nullptr, &displayed);
-
-    } else if (aOptions.mPendingSwitchID.WasPassed()) {
-      mFrameLoader->ResumeLoad(aOptions.mPendingSwitchID.Value());
-    } else {
-      mFrameLoader->LoadFrame(false);
-    }
-  };
-
-  ChangeRemotenessCommon(ShouldPreserveBrowsingContext(aOptions),
-                         aOptions.mRemoteType, frameLoaderInit, rv);
-}
-
-void nsFrameLoaderOwner::ChangeRemotenessWithBridge(
-    mozilla::ipc::ManagedEndpoint<mozilla::dom::PBrowserBridgeChild> aEndpoint,
-    uint64_t aTabId, mozilla::ErrorResult& rv) {
-  MOZ_ASSERT(XRE_IsContentProcess());
-  if (NS_WARN_IF(!mFrameLoader)) {
-    rv.Throw(NS_ERROR_UNEXPECTED);
-    return;
-  }
-
-  std::function<void()> frameLoaderInit = [&] {
-    RefPtr<BrowsingContext> browsingContext = mFrameLoader->mBrowsingContext;
-    RefPtr<BrowserBridgeChild> bridge =
-        new BrowserBridgeChild(mFrameLoader, browsingContext, TabId(aTabId));
-    Document* ownerDoc = mFrameLoader->GetOwnerDoc();
-    if (NS_WARN_IF(!ownerDoc)) {
-      rv.Throw(NS_ERROR_UNEXPECTED);
-      return;
-    }
-
-    RefPtr<BrowserChild> browser =
-        BrowserChild::GetFrom(ownerDoc->GetDocShell());
-    if (!browser->BindPBrowserBridgeEndpoint(std::move(aEndpoint), bridge)) {
-      rv.Throw(NS_ERROR_UNEXPECTED);
-      return;
-    }
-
-    RefPtr<BrowserBridgeHost> host = bridge->FinishInit();
-    browsingContext->SetEmbedderElement(mFrameLoader->GetOwnerContent());
-    mFrameLoader->mRemoteBrowser = host;
-  };
-
-  // NOTE: We always use the DEFAULT_REMOTE_TYPE here, because we don't actually
-  // know the real remote type, and don't need to, as we're a content process.
-  ChangeRemotenessCommon(
-      /* preserve */ true, NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE),
-      frameLoaderInit, rv);
-}
--- a/dom/base/nsFrameLoaderOwner.h
+++ b/dom/base/nsFrameLoaderOwner.h
@@ -9,23 +9,18 @@
 
 #include "nsISupports.h"
 
 class nsFrameLoader;
 namespace mozilla {
 class ErrorResult;
 namespace dom {
 class BrowsingContext;
-class PBrowserBridgeChild;
 struct RemotenessOptions;
 }  // namespace dom
-namespace ipc {
-template <typename T>
-class ManagedEndpoint;
-}  // namespace ipc
 }  // namespace mozilla
 
 // IID for the FrameLoaderOwner interface
 #define NS_FRAMELOADEROWNER_IID                      \
   {                                                  \
     0x1b4fd25c, 0x2e57, 0x11e9, {                    \
       0x9e, 0x5a, 0x5b, 0x86, 0xe9, 0x89, 0xa5, 0xc0 \
     }                                                \
@@ -52,29 +47,20 @@ class nsFrameLoaderOwner : public nsISup
   // remoteness requirements. This should follow the same path as
   // tabbrowser.js's updateBrowserRemoteness, including running the same logic
   // and firing the same events as unbinding a XULBrowserElement from the tree.
   // However, this method is available from backend and does not manipulate the
   // DOM.
   void ChangeRemoteness(const mozilla::dom::RemotenessOptions& aOptions,
                         mozilla::ErrorResult& rv);
 
-  void ChangeRemotenessWithBridge(
-      mozilla::ipc::ManagedEndpoint<mozilla::dom::PBrowserBridgeChild>
-          aEndpoint,
-      uint64_t aTabId, mozilla::ErrorResult& rv);
-
  private:
   bool UseRemoteSubframes();
   bool ShouldPreserveBrowsingContext(
       const mozilla::dom::RemotenessOptions& aOptions);
-  void ChangeRemotenessCommon(bool aPreserveContext,
-                              const nsAString& aRemoteType,
-                              std::function<void()>& aFrameLoaderInit,
-                              mozilla::ErrorResult& aRv);
 
  protected:
   virtual ~nsFrameLoaderOwner() = default;
   RefPtr<nsFrameLoader> mFrameLoader;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsFrameLoaderOwner, NS_FRAMELOADEROWNER_IID)
 
--- a/dom/chrome-webidl/BrowsingContext.webidl
+++ b/dom/chrome-webidl/BrowsingContext.webidl
@@ -44,18 +44,14 @@ interface CanonicalBrowsingContext : Bro
   // content process has died.
   [Throws]
   readonly attribute DOMString? currentRemoteType;
 
   readonly attribute WindowGlobalParent? embedderWindowGlobal;
 
   void notifyStartDelayedAutoplayMedia();
   void notifyMediaMutedChanged(boolean muted);
-
-  [Throws]
-  Promise<unsigned long long> changeFrameRemoteness(
-      DOMString remoteType, unsigned long long pendingSwitchId);
 };
 
 [Exposed=Window, ChromeOnly]
 interface BrowsingContextGroup {
   sequence<BrowsingContext> getToplevels();
 };
--- a/dom/chrome-webidl/WindowGlobalActors.webidl
+++ b/dom/chrome-webidl/WindowGlobalActors.webidl
@@ -49,16 +49,21 @@ interface WindowGlobalParent {
    * Get or create the JSWindowActor with the given name.
    *
    * See WindowActorOptions from JSWindowActor.webidl for details on how to
    * customize actor creation.
    */
   [Throws]
   JSWindowActorParent getActor(DOMString name);
 
+  [Throws]
+  Promise<unsigned long long> changeFrameRemoteness(
+    BrowsingContext? bc, DOMString remoteType,
+    unsigned long long pendingSwitchId);
+
   /**
    * Renders a region of the frame into an image bitmap.
    *
    * @param rect Specify the area of the window to render, in CSS pixels. This
    * is relative to the current scroll position. If null, the entire viewport
    * is rendered.
    * @param scale The scale to render the window at. Use devicePixelRatio
    * to have comparable rendering to the OS.
--- a/dom/ipc/BrowserBridgeChild.cpp
+++ b/dom/ipc/BrowserBridgeChild.cpp
@@ -1,25 +1,19 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 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/. */
 
-#ifdef ACCESSIBILITY
-#  ifdef XP_WIN
-#    include "mozilla/a11y/ProxyAccessible.h"
-#    include "mozilla/a11y/ProxyWrappers.h"
-#  endif
-#  include "mozilla/a11y/DocAccessible.h"
-#  include "mozilla/a11y/DocManager.h"
-#  include "mozilla/a11y/OuterDocAccessible.h"
+#if defined(ACCESSIBILITY) && defined(XP_WIN)
+#  include "mozilla/a11y/ProxyAccessible.h"
+#  include "mozilla/a11y/ProxyWrappers.h"
 #endif
 #include "mozilla/dom/BrowserBridgeChild.h"
-#include "mozilla/dom/BrowserBridgeHost.h"
 #include "mozilla/dom/BrowsingContext.h"
 #include "mozilla/dom/MozFrameLoaderOwnerBinding.h"
 #include "nsFocusManager.h"
 #include "nsFrameLoader.h"
 #include "nsFrameLoaderOwner.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsQueryObject.h"
 #include "nsSubDocumentFrame.h"
@@ -30,48 +24,28 @@ using namespace mozilla::ipc;
 namespace mozilla {
 namespace dom {
 
 BrowserBridgeChild::BrowserBridgeChild(nsFrameLoader* aFrameLoader,
                                        BrowsingContext* aBrowsingContext,
                                        TabId aId)
     : mId{aId},
       mLayersId{0},
+      mIPCOpen(true),
       mFrameLoader(aFrameLoader),
       mBrowsingContext(aBrowsingContext) {}
 
 BrowserBridgeChild::~BrowserBridgeChild() {
 #if defined(ACCESSIBILITY) && defined(XP_WIN)
   if (mEmbeddedDocAccessible) {
     mEmbeddedDocAccessible->Shutdown();
   }
 #endif
 }
 
-already_AddRefed<BrowserBridgeHost> BrowserBridgeChild::FinishInit() {
-  RefPtr<Element> owner = mFrameLoader->GetOwnerContent();
-  nsCOMPtr<nsIDocShell> docShell = do_GetInterface(owner->GetOwnerGlobal());
-  MOZ_DIAGNOSTIC_ASSERT(docShell);
-
-  nsDocShell::Cast(docShell)->OOPChildLoadStarted(this);
-
-#if defined(ACCESSIBILITY)
-  if (a11y::DocAccessible* docAcc =
-          a11y::GetExistingDocAccessible(owner->OwnerDoc())) {
-    if (a11y::Accessible* ownerAcc = docAcc->GetAccessible(owner)) {
-      if (a11y::OuterDocAccessible* outerAcc = ownerAcc->AsOuterDoc()) {
-        outerAcc->SendEmbedderAccessible(this);
-      }
-    }
-  }
-#endif  // defined(ACCESSIBILITY)
-
-  return MakeAndAddRef<BrowserBridgeHost>(this);
-}
-
 void BrowserBridgeChild::NavigateByKey(bool aForward,
                                        bool aForDocumentNavigation) {
   Unused << SendNavigateByKey(aForward, aForDocumentNavigation);
 }
 
 void BrowserBridgeChild::Activate() { Unused << SendActivate(); }
 
 void BrowserBridgeChild::Deactivate(bool aWindowLowering) {
@@ -237,16 +211,18 @@ mozilla::ipc::IPCResult BrowserBridgeChi
       return IPC_FAIL(this, "Remoteness change failed");
     }
   }
 
   return IPC_OK();
 }
 
 void BrowserBridgeChild::ActorDestroy(ActorDestroyReason aWhy) {
+  mIPCOpen = false;
+
   // Ensure we unblock our document's 'load' event (in case the OOP-iframe has
   // been removed before it finished loading, or its subprocess crashed):
   UnblockOwnerDocsLoadEvent();
 }
 
 void BrowserBridgeChild::UnblockOwnerDocsLoadEvent() {
   if (!mHadInitialLoad) {
     mHadInitialLoad = true;
--- a/dom/ipc/BrowserBridgeChild.h
+++ b/dom/ipc/BrowserBridgeChild.h
@@ -15,30 +15,29 @@ namespace mozilla {
 
 namespace a11y {
 class RemoteIframeDocProxyAccessibleWrap;
 }
 
 namespace dom {
 class BrowsingContext;
 class ContentChild;
-class BrowserBridgeHost;
 
 /**
  * BrowserBridgeChild implements the child actor part of the PBrowserBridge
  * protocol. See PBrowserBridge for more information.
  */
 class BrowserBridgeChild : public PBrowserBridgeChild {
  public:
   typedef mozilla::layers::LayersId LayersId;
 
   NS_INLINE_DECL_REFCOUNTING(BrowserBridgeChild, final);
 
   BrowserChild* Manager() {
-    MOZ_ASSERT(CanSend());
+    MOZ_ASSERT(mIPCOpen);
     return static_cast<BrowserChild*>(PBrowserBridgeChild::Manager());
   }
 
   TabId GetTabId() { return mId; }
 
   LayersId GetLayersId() { return mLayersId; }
 
   nsFrameLoader* GetFrameLoader() const { return mFrameLoader; }
@@ -51,35 +50,33 @@ class BrowserBridgeChild : public PBrows
   void NavigateByKey(bool aForward, bool aForDocumentNavigation);
 
   void Activate();
 
   void Deactivate(bool aWindowLowering);
 
   void SetIsUnderHiddenEmbedderElement(bool aIsUnderHiddenEmbedderElement);
 
-  already_AddRefed<BrowserBridgeHost> FinishInit();
-
 #if defined(ACCESSIBILITY) && defined(XP_WIN)
   a11y::RemoteIframeDocProxyAccessibleWrap* GetEmbeddedDocAccessible() {
     return mEmbeddedDocAccessible;
   }
 #endif
 
   static BrowserBridgeChild* GetFrom(nsFrameLoader* aFrameLoader);
 
   static BrowserBridgeChild* GetFrom(nsIContent* aContent);
 
-  BrowserBridgeChild(nsFrameLoader* aFrameLoader,
-                     BrowsingContext* aBrowsingContext, TabId aId);
-
  protected:
   friend class ContentChild;
   friend class PBrowserBridgeChild;
 
+  BrowserBridgeChild(nsFrameLoader* aFrameLoader,
+                     BrowsingContext* aBrowsingContext, TabId aId);
+
   mozilla::ipc::IPCResult RecvSetLayersId(
       const mozilla::layers::LayersId& aLayersId);
 
   mozilla::ipc::IPCResult RecvRequestFocus(const bool& aCanRaise);
 
   mozilla::ipc::IPCResult RecvMoveFocus(const bool& aForward,
                                         const bool& aForDocumentNavigation);
 
@@ -101,16 +98,17 @@ class BrowserBridgeChild : public PBrows
 
  private:
   ~BrowserBridgeChild();
 
   void UnblockOwnerDocsLoadEvent();
 
   TabId mId;
   LayersId mLayersId;
+  bool mIPCOpen;
   bool mHadInitialLoad = false;
   RefPtr<nsFrameLoader> mFrameLoader;
   RefPtr<BrowsingContext> mBrowsingContext;
 #if defined(ACCESSIBILITY) && defined(XP_WIN)
   RefPtr<a11y::RemoteIframeDocProxyAccessibleWrap> mEmbeddedDocAccessible;
 #endif
 };
 
--- a/dom/ipc/BrowserBridgeParent.cpp
+++ b/dom/ipc/BrowserBridgeParent.cpp
@@ -18,47 +18,67 @@
 
 using namespace mozilla::ipc;
 using namespace mozilla::layout;
 using namespace mozilla::hal;
 
 namespace mozilla {
 namespace dom {
 
-BrowserBridgeParent::BrowserBridgeParent() {}
+BrowserBridgeParent::BrowserBridgeParent()
+    :
+#ifdef ACCESSIBILITY
+      mEmbedderAccessibleID(0),
+#endif
+      mIPCOpen(false) {
+}
 
 BrowserBridgeParent::~BrowserBridgeParent() { Destroy(); }
 
-nsresult BrowserBridgeParent::InitWithProcess(
-    ContentParent* aContentParent, const nsString& aPresentationURL,
-    const WindowGlobalInit& aWindowInit, uint32_t aChromeFlags, TabId aTabId) {
+nsresult BrowserBridgeParent::Init(const nsString& aPresentationURL,
+                                   const nsString& aRemoteType,
+                                   const WindowGlobalInit& aWindowInit,
+                                   const uint32_t& aChromeFlags, TabId aTabId) {
+  mIPCOpen = true;
+
   RefPtr<CanonicalBrowsingContext> browsingContext =
       aWindowInit.browsingContext()->Canonical();
 
   // We can inherit most TabContext fields for the new BrowserParent actor from
   // our Manager BrowserParent.
   MutableTabContext tabContext;
   tabContext.SetTabContext(false, Manager()->ChromeOuterWindowID(),
                            Manager()->ShowFocusRings(),
                            Manager()->OriginAttributesRef(), aPresentationURL,
                            Manager()->GetMaxTouchPoints());
 
+  ProcessPriority initialPriority = PROCESS_PRIORITY_FOREGROUND;
+
+  // Get our ConstructorSender object.
+  RefPtr<ContentParent> constructorSender =
+      ContentParent::GetNewOrUsedBrowserProcess(
+          nullptr, aRemoteType, initialPriority, nullptr, false);
+  if (NS_WARN_IF(!constructorSender)) {
+    MOZ_ASSERT(false, "Unable to allocate content process!");
+    return NS_ERROR_FAILURE;
+  }
+
   // Ensure that our content process is subscribed to our newly created
   // BrowsingContextGroup.
-  browsingContext->Group()->EnsureSubscribed(aContentParent);
-  browsingContext->SetOwnerProcessId(aContentParent->ChildID());
+  browsingContext->Group()->EnsureSubscribed(constructorSender);
+  browsingContext->SetOwnerProcessId(constructorSender->ChildID());
 
   // Construct the BrowserParent object for our subframe.
   auto browserParent = MakeRefPtr<BrowserParent>(
-      aContentParent, aTabId, tabContext, browsingContext, aChromeFlags);
+      constructorSender, aTabId, tabContext, browsingContext, aChromeFlags);
   browserParent->SetBrowserBridgeParent(this);
 
   // Open a remote endpoint for our PBrowser actor.
   ManagedEndpoint<PBrowserChild> childEp =
-      aContentParent->OpenPBrowserEndpoint(browserParent);
+      constructorSender->OpenPBrowserEndpoint(browserParent);
   if (NS_WARN_IF(!childEp.IsValid())) {
     MOZ_ASSERT(false, "Browser Open Endpoint Failed");
     return NS_ERROR_FAILURE;
   }
 
   ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
   cpm->RegisterRemoteFrame(browserParent);
 
@@ -68,20 +88,20 @@ nsresult BrowserBridgeParent::InitWithPr
   ManagedEndpoint<PWindowGlobalChild> windowChildEp =
       browserParent->OpenPWindowGlobalEndpoint(windowParent);
   if (NS_WARN_IF(!windowChildEp.IsValid())) {
     MOZ_ASSERT(false, "WindowGlobal Open Endpoint Failed");
     return NS_ERROR_FAILURE;
   }
 
   // Tell the content process to set up its PBrowserChild.
-  bool ok = aContentParent->SendConstructBrowser(
+  bool ok = constructorSender->SendConstructBrowser(
       std::move(childEp), std::move(windowChildEp), aTabId, TabId(0),
       tabContext.AsIPCTabContext(), aWindowInit, aChromeFlags,
-      aContentParent->ChildID(), aContentParent->IsForBrowser(),
+      constructorSender->ChildID(), constructorSender->IsForBrowser(),
       /* aIsTopLevel */ false);
   if (NS_WARN_IF(!ok)) {
     MOZ_ASSERT(false, "Browser Constructor Failed");
     return NS_ERROR_FAILURE;
   }
 
   // Set our BrowserParent object to the newly created browser.
   mBrowserParent = browserParent.forget();
@@ -90,39 +110,22 @@ nsresult BrowserBridgeParent::InitWithPr
 
   windowParent->Init(aWindowInit);
 
   // Send the newly created layers ID back into content.
   Unused << SendSetLayersId(mBrowserParent->GetLayersId());
   return NS_OK;
 }
 
-nsresult BrowserBridgeParent::Init(const nsString& aPresentationURL,
-                                   const nsString& aRemoteType,
-                                   const WindowGlobalInit& aWindowInit,
-                                   uint32_t aChromeFlags, TabId aTabId) {
-  // Get our ConstructorSender object.
-  RefPtr<ContentParent> constructorSender =
-      ContentParent::GetNewOrUsedBrowserProcess(
-          nullptr, aRemoteType, PROCESS_PRIORITY_FOREGROUND, nullptr, false);
-  if (NS_WARN_IF(!constructorSender)) {
-    MOZ_ASSERT(false, "Unable to allocate content process!");
-    return NS_ERROR_FAILURE;
-  }
-
-  return InitWithProcess(constructorSender, aPresentationURL, aWindowInit,
-                         aChromeFlags, aTabId);
-}
-
 CanonicalBrowsingContext* BrowserBridgeParent::GetBrowsingContext() {
   return mBrowserParent->GetBrowsingContext();
 }
 
 BrowserParent* BrowserBridgeParent::Manager() {
-  MOZ_ASSERT(CanSend());
+  MOZ_ASSERT(mIPCOpen);
   return static_cast<BrowserParent*>(PBrowserBridgeParent::Manager());
 }
 
 void BrowserBridgeParent::Destroy() {
   if (mBrowserParent) {
     mBrowserParent->Destroy();
     mBrowserParent->SetBrowserBridgeParent(nullptr);
     mBrowserParent = nullptr;
@@ -223,12 +226,15 @@ IPCResult BrowserBridgeParent::RecvSetEm
     PDocAccessibleParent* aDoc, uint64_t aID) {
 #ifdef ACCESSIBILITY
   mEmbedderAccessibleDoc = static_cast<a11y::DocAccessibleParent*>(aDoc);
   mEmbedderAccessibleID = aID;
 #endif
   return IPC_OK();
 }
 
-void BrowserBridgeParent::ActorDestroy(ActorDestroyReason aWhy) { Destroy(); }
+void BrowserBridgeParent::ActorDestroy(ActorDestroyReason aWhy) {
+  mIPCOpen = false;
+  Destroy();
+}
 
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/ipc/BrowserBridgeParent.h
+++ b/dom/ipc/BrowserBridgeParent.h
@@ -28,23 +28,18 @@ class BrowserParent;
 class BrowserBridgeParent : public PBrowserBridgeParent {
  public:
   NS_INLINE_DECL_REFCOUNTING(BrowserBridgeParent, final);
 
   BrowserBridgeParent();
 
   // Initialize this actor after performing startup.
   nsresult Init(const nsString& aPresentationURL, const nsString& aRemoteType,
-                const WindowGlobalInit& aWindowInit, uint32_t aChromeFlags,
-                TabId aTabId);
-
-  nsresult InitWithProcess(ContentParent* aContentParent,
-                           const nsString& aPresentationURL,
-                           const WindowGlobalInit& aWindowInit,
-                           uint32_t aChromeFlags, TabId aTabId);
+                const WindowGlobalInit& aWindowInit,
+                const uint32_t& aChromeFlags, TabId aTabId);
 
   BrowserParent* GetBrowserParent() { return mBrowserParent; }
 
   CanonicalBrowsingContext* GetBrowsingContext();
 
   // Get our manager actor.
   BrowserParent* Manager();
 
@@ -98,16 +93,17 @@ class BrowserBridgeParent : public PBrow
   void ActorDestroy(ActorDestroyReason aWhy) override;
 
  private:
   ~BrowserBridgeParent();
 
   RefPtr<BrowserParent> mBrowserParent;
 #if defined(ACCESSIBILITY)
   RefPtr<a11y::DocAccessibleParent> mEmbedderAccessibleDoc;
-  uint64_t mEmbedderAccessibleID = 0;
+  uint64_t mEmbedderAccessibleID;
 #endif  // defined(ACCESSIBILITY)
+  bool mIPCOpen;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // !defined(mozilla_dom_BrowserBridgeParent_h)
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -2117,21 +2117,40 @@ already_AddRefed<RemoteBrowser> ContentC
   if (docShell->GetAffectPrivateSessionLifetime()) {
     chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_LIFETIME;
   }
 
   TabId tabId(nsContentUtils::GenerateTabId());
   RefPtr<BrowserBridgeChild> browserBridge =
       new BrowserBridgeChild(aFrameLoader, aBrowsingContext, tabId);
 
+  nsDocShell::Cast(docShell)->OOPChildLoadStarted(browserBridge);
+
   browserChild->SendPBrowserBridgeConstructor(
       browserBridge, PromiseFlatString(aContext.PresentationURL()), aRemoteType,
       aBrowsingContext, chromeFlags, tabId);
-
-  return browserBridge->FinishInit();
+  browserBridge->mIPCOpen = true;
+
+#if defined(ACCESSIBILITY)
+  a11y::DocAccessible* docAcc =
+      a11y::GetExistingDocAccessible(owner->OwnerDoc());
+  if (docAcc) {
+    a11y::Accessible* ownerAcc = docAcc->GetAccessible(owner);
+    if (ownerAcc) {
+      a11y::OuterDocAccessible* outerAcc = ownerAcc->AsOuterDoc();
+      if (outerAcc) {
+        outerAcc->SendEmbedderAccessible(browserBridge);
+      }
+    }
+  }
+#endif  // defined(ACCESSIBILITY)
+
+  RefPtr<BrowserBridgeHost> browserBridgeHost =
+      new BrowserBridgeHost(browserBridge);
+  return browserBridgeHost.forget();
 }
 
 PScriptCacheChild* ContentChild::AllocPScriptCacheChild(
     const FileDescOrError& cacheFile, const bool& wantCacheData) {
   return new loader::ScriptCacheChild();
 }
 
 bool ContentChild::DeallocPScriptCacheChild(PScriptCacheChild* cache) {
--- a/dom/ipc/PWindowGlobal.ipdl
+++ b/dom/ipc/PWindowGlobal.ipdl
@@ -12,17 +12,16 @@ include protocol PBrowserBridge;
 
 include DOMTypes;
 
 using JSWindowActorMessageKind from "mozilla/dom/JSWindowActor.h";
 using mozilla::gfx::IntRect from "mozilla/gfx/Rect.h";
 using moveonly mozilla::gfx::PaintFragment from "mozilla/gfx/CrossProcessPaint.h";
 using nscolor from "nsColor.h";
 using refcounted class nsDocShellLoadState from "nsDocShellLoadState.h";
-using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h";
 
 namespace mozilla {
 namespace dom {
 
 struct JSWindowActorMessageMeta {
   nsString actorName;
   nsString messageName;
   uint64_t queryId;
@@ -37,20 +36,20 @@ struct JSWindowActorMessageMeta {
  */
 async refcounted protocol PWindowGlobal
 {
   manager PBrowser or PInProcess;
 
 child:
   async __delete__();
 
-  async MakeFrameLocal(BrowsingContext aFrameContext, uint64_t aSwitchId);
-  async MakeFrameRemote(BrowsingContext aFrameContext,
-                        ManagedEndpoint<PBrowserBridgeChild> aEndpoint,
-                        TabId aTabId) returns (bool success);
+  async ChangeFrameRemoteness(BrowsingContext aFrameContext,
+                              nsString aRemoteType,
+                              uint64_t aSwitchId)
+      returns (nsresult rv, nullable PBrowserBridge bridge);
 
   async DrawSnapshot(IntRect? aRect, float aScale, nscolor aBackgroundColor, uint32_t aFlags) returns (PaintFragment retval);
 
   /**
    * Returns the serialized security info associated with this window.
    */
   async GetSecurityInfo() returns(nsCString? serializedSecInfo);
 
--- a/dom/ipc/WindowGlobalChild.cpp
+++ b/dom/ipc/WindowGlobalChild.cpp
@@ -8,17 +8,17 @@
 
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/dom/WindowGlobalParent.h"
 #include "mozilla/dom/BrowsingContext.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/MozFrameLoaderOwnerBinding.h"
 #include "mozilla/dom/BrowserChild.h"
 #include "mozilla/dom/BrowserBridgeChild.h"
-#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/WindowGlobalActorsBinding.h"
 #include "mozilla/dom/WindowGlobalParent.h"
 #include "mozilla/ipc/InProcessChild.h"
 #include "mozilla/ipc/InProcessParent.h"
 #include "nsContentUtils.h"
 #include "nsDocShell.h"
 #include "nsFrameLoaderOwner.h"
 #include "nsGlobalWindowInner.h"
@@ -234,69 +234,95 @@ void WindowGlobalChild::Destroy() {
 }
 
 mozilla::ipc::IPCResult WindowGlobalChild::RecvLoadURIInChild(
     nsDocShellLoadState* aLoadState) {
   mWindowGlobal->GetDocShell()->LoadURI(aLoadState);
   return IPC_OK();
 }
 
-mozilla::ipc::IPCResult WindowGlobalChild::RecvMakeFrameLocal(
-    dom::BrowsingContext* aFrameContext, uint64_t aPendingSwitchId) {
-  MOZ_DIAGNOSTIC_ASSERT(XRE_IsContentProcess());
+static nsresult ChangeFrameRemoteness(WindowGlobalChild* aWgc,
+                                      BrowsingContext* aBc,
+                                      const nsString& aRemoteType,
+                                      uint64_t aPendingSwitchId,
+                                      BrowserBridgeChild** aBridge) {
+  MOZ_ASSERT(XRE_IsContentProcess(), "This doesn't make sense in the parent");
 
-  MOZ_LOG(aFrameContext->GetLog(), LogLevel::Debug,
-          ("RecvMakeFrameLocal ID=%" PRIx64, aFrameContext->Id()));
-
-  RefPtr<Element> embedderElt = aFrameContext->GetEmbedderElement();
-  if (NS_WARN_IF(!embedderElt)) {
-    return IPC_FAIL(this, "No embedder element in this process");
+  // Get the target embedder's FrameLoaderOwner, and make sure we're in the
+  // right place.
+  RefPtr<Element> embedderElt = aBc->GetEmbedderElement();
+  if (!embedderElt) {
+    return NS_ERROR_NOT_AVAILABLE;
   }
 
-  if (NS_WARN_IF(embedderElt->GetOwnerGlobal() != WindowGlobal())) {
-    return IPC_FAIL(this, "Wrong actor");
+  if (NS_WARN_IF(embedderElt->GetOwnerGlobal() != aWgc->WindowGlobal())) {
+    return NS_ERROR_UNEXPECTED;
   }
 
   RefPtr<nsFrameLoaderOwner> flo = do_QueryObject(embedderElt);
-  MOZ_DIAGNOSTIC_ASSERT(flo, "Embedder must be a nsFrameLoaderOwner");
+  MOZ_ASSERT(flo, "Embedder must be a nsFrameLoaderOwner!");
 
-  // Trigger a process switch into the current process.
+  MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
+
+  // Actually perform the remoteness swap.
   RemotenessOptions options;
-  options.mRemoteType.Assign(VoidString());
   options.mPendingSwitchID.Construct(aPendingSwitchId);
-  flo->ChangeRemoteness(options, IgnoreErrors());
-  return IPC_OK();
+  options.mRemoteType.Assign(aRemoteType);
+
+  // Clear mRemoteType to VoidString() (for non-remote) if it matches the
+  // current process' remote type.
+  if (ContentChild::GetSingleton()->GetRemoteType().Equals(aRemoteType)) {
+    options.mRemoteType.Assign(VoidString());
+  }
+
+  ErrorResult error;
+  flo->ChangeRemoteness(options, error);
+  if (NS_WARN_IF(error.Failed())) {
+    return error.StealNSResult();
+  }
+
+  // Make sure we successfully created either an in-process nsDocShell or a
+  // cross-process BrowserBridgeChild. If we didn't, produce an error.
+  RefPtr<nsFrameLoader> frameLoader = flo->GetFrameLoader();
+  if (NS_WARN_IF(!frameLoader)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  RefPtr<BrowserBridgeChild> bbc;
+  if (frameLoader->IsRemoteFrame()) {
+    bbc = frameLoader->GetBrowserBridgeChild();
+    if (NS_WARN_IF(!bbc)) {
+      return NS_ERROR_FAILURE;
+    }
+  } else {
+    nsDocShell* ds = frameLoader->GetDocShell(error);
+    if (NS_WARN_IF(error.Failed())) {
+      return error.StealNSResult();
+    }
+
+    if (NS_WARN_IF(!ds)) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+
+  bbc.forget(aBridge);
+  return NS_OK;
 }
 
-mozilla::ipc::IPCResult WindowGlobalChild::RecvMakeFrameRemote(
-    dom::BrowsingContext* aFrameContext,
-    ManagedEndpoint<PBrowserBridgeChild>&& aEndpoint, const TabId& aTabId,
-    MakeFrameRemoteResolver&& aResolve) {
-  MOZ_DIAGNOSTIC_ASSERT(XRE_IsContentProcess());
-
-  MOZ_LOG(aFrameContext->GetLog(), LogLevel::Debug,
-          ("RecvMakeFrameRemote ID=%" PRIx64, aFrameContext->Id()));
-
-  // Immediately resolve the promise, acknowledging the request.
-  aResolve(true);
+IPCResult WindowGlobalChild::RecvChangeFrameRemoteness(
+    dom::BrowsingContext* aBc, const nsString& aRemoteType,
+    uint64_t aPendingSwitchId, ChangeFrameRemotenessResolver&& aResolver) {
+  MOZ_ASSERT(XRE_IsContentProcess(), "This doesn't make sense in the parent");
 
-  RefPtr<Element> embedderElt = aFrameContext->GetEmbedderElement();
-  if (NS_WARN_IF(!embedderElt)) {
-    return IPC_FAIL(this, "No embedder element in this process");
-  }
+  RefPtr<BrowserBridgeChild> bbc;
+  nsresult rv = ChangeFrameRemoteness(this, aBc, aRemoteType, aPendingSwitchId,
+                                      getter_AddRefs(bbc));
 
-  if (NS_WARN_IF(embedderElt->GetOwnerGlobal() != WindowGlobal())) {
-    return IPC_FAIL(this, "Wrong actor");
-  }
-
-  RefPtr<nsFrameLoaderOwner> flo = do_QueryObject(embedderElt);
-  MOZ_DIAGNOSTIC_ASSERT(flo, "Embedder must be a nsFrameLoaderOwner");
-
-  // Trgger a process switch into the specified process.
-  flo->ChangeRemotenessWithBridge(std::move(aEndpoint), aTabId, IgnoreErrors());
+  // To make the type system happy, we've gotta do some gymnastics.
+  aResolver(Tuple<const nsresult&, PBrowserBridgeChild*>(rv, bbc));
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult WindowGlobalChild::RecvDrawSnapshot(
     const Maybe<IntRect>& aRect, const float& aScale,
     const nscolor& aBackgroundColor, const uint32_t& aFlags,
     DrawSnapshotResolver&& aResolve) {
   nsCOMPtr<nsIDocShell> docShell = BrowsingContext()->GetDocShell();
--- a/dom/ipc/WindowGlobalChild.h
+++ b/dom/ipc/WindowGlobalChild.h
@@ -111,23 +111,19 @@ class WindowGlobalChild final : public W
   JSWindowActor::Type GetSide() override { return JSWindowActor::Type::Child; }
 
   // IPC messages
   mozilla::ipc::IPCResult RecvRawMessage(const JSWindowActorMessageMeta& aMeta,
                                          const ClonedMessageData& aData);
 
   mozilla::ipc::IPCResult RecvLoadURIInChild(nsDocShellLoadState* aLoadState);
 
-  mozilla::ipc::IPCResult RecvMakeFrameLocal(
-      dom::BrowsingContext* aFrameContext, uint64_t aPendingSwitchId);
-
-  mozilla::ipc::IPCResult RecvMakeFrameRemote(
-      dom::BrowsingContext* aFrameContext,
-      ManagedEndpoint<PBrowserBridgeChild>&& aEndpoint, const TabId& aTabId,
-      MakeFrameRemoteResolver&& aResolve);
+  mozilla::ipc::IPCResult RecvChangeFrameRemoteness(
+      dom::BrowsingContext* aBc, const nsString& aRemoteType,
+      uint64_t aPendingSwitchId, ChangeFrameRemotenessResolver&& aResolver);
 
   mozilla::ipc::IPCResult RecvDrawSnapshot(const Maybe<IntRect>& aRect,
                                            const float& aScale,
                                            const nscolor& aBackgroundColor,
                                            const uint32_t& aFlags,
                                            DrawSnapshotResolver&& aResolve);
 
   mozilla::ipc::IPCResult RecvGetSecurityInfo(
--- a/dom/ipc/WindowGlobalParent.cpp
+++ b/dom/ipc/WindowGlobalParent.cpp
@@ -297,16 +297,83 @@ already_AddRefed<JSWindowActorParent> Wi
   mWindowActors.Put(aName, actor);
   return actor.forget();
 }
 
 bool WindowGlobalParent::IsCurrentGlobal() {
   return CanSend() && mBrowsingContext->GetCurrentWindowGlobal() == this;
 }
 
+already_AddRefed<Promise> WindowGlobalParent::ChangeFrameRemoteness(
+    dom::BrowsingContext* aBc, const nsAString& aRemoteType,
+    uint64_t aPendingSwitchId, ErrorResult& aRv) {
+  RefPtr<BrowserParent> embedderBrowserParent = GetBrowserParent();
+  if (NS_WARN_IF(!embedderBrowserParent)) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  nsIGlobalObject* global = GetParentObject();
+  RefPtr<Promise> promise = Promise::Create(global, aRv);
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  RefPtr<CanonicalBrowsingContext> browsingContext =
+      CanonicalBrowsingContext::Cast(aBc);
+
+  // When the reply comes back from content, either resolve or reject.
+  auto resolve =
+      [=](mozilla::Tuple<nsresult, PBrowserBridgeParent*>&& aResult) {
+        nsresult rv = Get<0>(aResult);
+        RefPtr<BrowserBridgeParent> bridge =
+            static_cast<BrowserBridgeParent*>(Get<1>(aResult));
+        if (NS_FAILED(rv)) {
+          promise->MaybeReject(rv);
+          return;
+        }
+
+        // If we got a `BrowserBridgeParent`, the frame is out-of-process, so we
+        // can get the target off of it. Otherwise, it's an in-process frame, so
+        // we can use the embedder `BrowserParent`.
+        RefPtr<BrowserParent> browserParent;
+        if (bridge) {
+          browserParent = bridge->GetBrowserParent();
+        } else {
+          browserParent = embedderBrowserParent;
+        }
+        MOZ_ASSERT(browserParent);
+
+        if (!browserParent || !browserParent->CanSend()) {
+          promise->MaybeReject(NS_ERROR_FAILURE);
+          return;
+        }
+
+        // Update our BrowsingContext to its new owner, if it hasn't been
+        // updated yet. This can happen when switching from a out-of-process to
+        // in-process frame. For remote frames, the BrowserBridgeParent::Init
+        // method should've already set up the OwnerProcessId.
+        uint64_t childId = browserParent->Manager()->ChildID();
+        MOZ_ASSERT_IF(bridge,
+                      browsingContext == browserParent->GetBrowsingContext());
+        MOZ_ASSERT_IF(bridge, browsingContext->IsOwnedByProcess(childId));
+        browsingContext->SetOwnerProcessId(childId);
+
+        promise->MaybeResolve(childId);
+      };
+
+  auto reject = [=](ResponseRejectReason aReason) {
+    promise->MaybeReject(NS_ERROR_FAILURE);
+  };
+
+  SendChangeFrameRemoteness(aBc, PromiseFlatString(aRemoteType),
+                            aPendingSwitchId, resolve, reject);
+  return promise.forget();
+}
+
 already_AddRefed<mozilla::dom::Promise> WindowGlobalParent::DrawSnapshot(
     const DOMRect* aRect, double aScale, const nsAString& aBackgroundColor,
     mozilla::ErrorResult& aRv) {
   nsIGlobalObject* global = GetParentObject();
   RefPtr<Promise> promise = Promise::Create(global, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
--- a/dom/ipc/WindowGlobalParent.h
+++ b/dom/ipc/WindowGlobalParent.h
@@ -106,16 +106,21 @@ class WindowGlobalParent final : public 
   bool IsCurrentGlobal();
 
   bool IsProcessRoot();
 
   bool IsInitialDocument() { return mIsInitialDocument; }
 
   bool HasBeforeUnload() { return mHasBeforeUnload; }
 
+  already_AddRefed<Promise> ChangeFrameRemoteness(dom::BrowsingContext* aBc,
+                                                  const nsAString& aRemoteType,
+                                                  uint64_t aPendingSwitchId,
+                                                  ErrorResult& aRv);
+
   already_AddRefed<mozilla::dom::Promise> DrawSnapshot(
       const DOMRect* aRect, double aScale, const nsAString& aBackgroundColor,
       mozilla::ErrorResult& aRv);
 
   already_AddRefed<Promise> GetSecurityInfo(ErrorResult& aRv);
 
   // Create a WindowGlobalParent from over IPC. This method should not be called
   // from outside of the IPC constructors.
--- a/netwerk/ipc/DocumentChannelParent.cpp
+++ b/netwerk/ipc/DocumentChannelParent.cpp
@@ -139,32 +139,46 @@ void DocumentChannelParent::ActorDestroy
   // 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);
   }
 }
 
+void DocumentChannelParent::CancelChildForProcessSwitch() {
+  MOZ_ASSERT(!mDoingProcessSwitch, "Already in the middle of switching?");
+  MOZ_ASSERT(NS_IsMainThread());
+
+  mDoingProcessSwitch = true;
+  if (CanSend()) {
+    Unused << SendCancelForProcessSwitch();
+  }
+}
+
 bool DocumentChannelParent::RecvCancel(const nsresult& aStatusCode) {
-  if (mChannel && !mDoingProcessSwitch) {
+  if (mDoingProcessSwitch) {
+    return IPC_OK();
+  }
+
+  if (mChannel) {
     mChannel->Cancel(aStatusCode);
   }
   return true;
 }
 
 bool DocumentChannelParent::RecvSuspend() {
-  if (mChannel && !mDoingProcessSwitch) {
+  if (mChannel) {
     mChannel->Suspend();
   }
   return true;
 }
 
 bool DocumentChannelParent::RecvResume() {
-  if (mChannel && !mDoingProcessSwitch) {
+  if (mChannel) {
     mChannel->Resume();
   }
   return true;
 }
 
 void DocumentChannelParent::RedirectToRealChannelFinished(nsresult aRv) {
   if (NS_FAILED(aRv)) {
     FinishReplacementChannelSetup(false);
@@ -202,20 +216,16 @@ NS_IMETHODIMP
 DocumentChannelParent::ReadyToVerify(nsresult aResultCode) {
   FinishReplacementChannelSetup(NS_SUCCEEDED(aResultCode));
   return NS_OK;
 }
 
 void DocumentChannelParent::FinishReplacementChannelSetup(bool aSucceeded) {
   nsresult rv;
 
-  if (mDoingProcessSwitch && CanSend()) {
-    Unused << SendCancelForProcessSwitch();
-  }
-
   nsCOMPtr<nsIParentChannel> redirectChannel;
   if (mRedirectChannelId) {
     nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
         RedirectChannelRegistrar::GetOrCreate();
     MOZ_ASSERT(registrar);
 
     rv = registrar->GetParentChannel(mRedirectChannelId,
                                      getter_AddRefs(redirectChannel));
@@ -345,21 +355,17 @@ void DocumentChannelParent::FinishReplac
     }
 
     mChannel->Resume();
   }
 }
 
 void DocumentChannelParent::TriggerCrossProcessSwitch() {
   MOZ_ASSERT(mRedirectContentProcessIdPromise);
-  MOZ_ASSERT(!mDoingProcessSwitch, "Already in the middle of switching?");
-  MOZ_ASSERT(NS_IsMainThread());
-
-  mDoingProcessSwitch = true;
-
+  CancelChildForProcessSwitch();
   RefPtr<DocumentChannelParent> self = this;
   mRedirectContentProcessIdPromise->Then(
       GetMainThreadSerialEventTarget(), __func__,
       [self, this](uint64_t aCpId) {
         MOZ_ASSERT(mChannel, "Something went wrong, channel got cancelled");
         TriggerRedirectToRealChannel(mChannel, Some(aCpId),
                                      mCrossProcessRedirectIdentifier);
       },
--- a/netwerk/ipc/DocumentChannelParent.h
+++ b/netwerk/ipc/DocumentChannelParent.h
@@ -89,16 +89,21 @@ class DocumentChannelParent : public nsI
     params.mContentType = aContentType;
     mSecurityWarningFunctions.AppendElement(
         SecurityWarningFunction{VariantIndex<2>{}, std::move(params)});
     return NS_OK;
   }
 
   virtual void ActorDestroy(ActorDestroyReason why) override;
 
+  // Notify the DocumentChannelChild that we're switching
+  // to a different process and that it can notify listeners
+  // that it's finished.
+  void CancelChildForProcessSwitch();
+
  private:
   virtual ~DocumentChannelParent() = default;
 
   void TriggerRedirectToRealChannel(
       nsIChannel* aChannel,
       const Maybe<uint64_t>& aDestinationProcess = Nothing(),
       uint64_t aIdentifier = 0);
 
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -2109,16 +2109,26 @@ HttpChannelParent::StartRedirect(nsIChan
 
   // Result is handled in RecvRedirect2Verify above
 
   mRedirectChannel = newChannel;
   mRedirectCallback = callback;
   return NS_OK;
 }
 
+void HttpChannelParent::CancelChildCrossProcessRedirect() {
+  MOZ_ASSERT(!mDoingCrossProcessRedirect, "Already redirected");
+  MOZ_ASSERT(NS_IsMainThread());
+
+  mDoingCrossProcessRedirect = true;
+  if (!mIPCClosed) {
+    Unused << SendCancelRedirected();
+  }
+}
+
 NS_IMETHODIMP
 HttpChannelParent::CompleteRedirect(bool succeeded) {
   LOG(("HttpChannelParent::CompleteRedirect [this=%p succeeded=%d]\n", this,
        succeeded));
 
   if (mDoingCrossProcessRedirect) {
     LOG(("Child was cancelled for cross-process redirect. Bail."));
     return NS_OK;
@@ -2627,23 +2637,17 @@ HttpChannelParent::OnRedirectResult(bool
     redirectChannel->Delete();
   }
 
   return NS_OK;
 }
 
 nsresult HttpChannelParent::TriggerCrossProcessSwitch(nsIHttpChannel* aChannel,
                                                       uint64_t aIdentifier) {
-  MOZ_ASSERT(NS_IsMainThread());
-
-  // Mark ourselves as performing a cross-process redirect. This will prevent
-  // messages being communicated to the underlying channel, allowing us to keep
-  // it open.
-  MOZ_ASSERT(!mDoingCrossProcessRedirect, "Already redirected");
-  mDoingCrossProcessRedirect = true;
+  CancelChildCrossProcessRedirect();
 
   nsCOMPtr<nsIChannel> channel = aChannel;
   RefPtr<nsHttpChannel> httpChannel = do_QueryObject(channel);
   MOZ_DIAGNOSTIC_ASSERT(httpChannel,
                         "Must be called with nsHttpChannel object");
 
   RefPtr<nsHttpChannel::ContentProcessIdPromise> p =
       httpChannel->TakeRedirectContentProcessIdPromise();
@@ -2664,22 +2668,16 @@ nsresult HttpChannelParent::TriggerCross
   loadInfo->SetResultPrincipalURI(uri);
 
   RefPtr<HttpChannelParent> self = this;
   p->Then(
       GetMainThreadSerialEventTarget(), __func__,
       [=](uint64_t cpId) {
         nsresult rv;
 
-        // Cancel the channel in the original process, as the switch is
-        // happening in earnest.
-        if (!self->mIPCClosed) {
-          Unused << self->SendCancelRedirected();
-        }
-
         // Register the new channel and obtain id for it
         nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
             RedirectChannelRegistrar::GetOrCreate();
         MOZ_ASSERT(registrar);
         rv = registrar->RegisterChannel(channel, &self->mRedirectChannelId);
         NS_ENSURE_SUCCESS_VOID(rv);
 
         LOG(("Registered %p channel under id=%d", channel.get(),
@@ -2739,24 +2737,18 @@ nsresult HttpChannelParent::TriggerCross
                   MOZ_ASSERT(newParent);
                   newParent->CrossProcessRedirectDone(Get<0>(aResponse),
                                                       Get<1>(aResponse));
                 },
                 [self](const mozilla::ipc::ResponseRejectReason) {
                   self->CrossProcessRedirectDone(NS_ERROR_FAILURE, Nothing());
                 });
       },
-      [=](nsresult aStatus) {
+      [httpChannel](nsresult aStatus) {
         MOZ_ASSERT(NS_FAILED(aStatus), "Status should be error");
-
-        // We failed to do a process switch. Make sure the content process has
-        // canceled the channel, and then resume the load process with an error.
-        if (!self->mIPCClosed) {
-          Unused << self->SendCancelRedirected();
-        }
         httpChannel->OnRedirectVerifyCallback(aStatus);
       });
 
   return NS_OK;
 }
 
 }  // namespace net
 }  // namespace mozilla
--- a/netwerk/protocol/http/HttpChannelParent.h
+++ b/netwerk/protocol/http/HttpChannelParent.h
@@ -131,16 +131,22 @@ class HttpChannelParent final : public n
 
   // Called by nsHttpChannel when a process switch is about to start.
   // aChannel: nsIHttpChannel caller.
   // aIdentifier: identifier from SessionStore to be passed to the childChannel
   //              in order to identify it.
   nsresult TriggerCrossProcessSwitch(nsIHttpChannel* aChannel,
                                      uint64_t aIdentifier);
 
+  // Calling this method will cancel the HttpChannelChild because the consumer
+  // needs to be relocated to another process.
+  // Any OnStart/Stop/DataAvailable calls that follow will not be sent to the
+  // child channel.
+  void CancelChildCrossProcessRedirect();
+
  protected:
   // used to connect redirected-to channel in parent with just created
   // ChildChannel.  Used during redirects.
   MOZ_MUST_USE bool ConnectChannel(const uint32_t& channelId,
                                    const bool& shouldIntercept);
 
   MOZ_MUST_USE bool DoAsyncOpen(
       const URIParams& uri, const Maybe<URIParams>& originalUri,
--- a/netwerk/test/browser/browser.ini
+++ b/netwerk/test/browser/browser.ini
@@ -10,14 +10,16 @@ support-files =
 [browser_child_resource.js]
 skip-if = !crashreporter || (e10s && debug && os == "linux" && bits == 64) || debug # Bug 1370783
 [browser_post_file.js]
 [browser_nsIFormPOSTActionChannel.js]
 skip-if = e10s # protocol handler and channel does not work in content process
 [browser_resource_navigation.js]
 [browser_test_io_activity.js]
 [browser_cookie_sync_across_tabs.js]
+[browser_cross_process_redirect.js]
+fail-if = fission
 [browser_test_favicon.js]
 skip-if = (verify && (os == 'linux' || os == 'mac'))
 support-files =
   damonbowling.jpg
   damonbowling.jpg^headers^
   file_favicon.html
new file mode 100644
--- /dev/null
+++ b/netwerk/test/browser/browser_cross_process_redirect.js
@@ -0,0 +1,292 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+"use strict";
+
+const gRegistrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+
+let gLoadedInProcess2Promise = null;
+
+function _createProcessChooser(remoteTab, from, to, rejectPromise = false) {
+  let processChooser = new ProcessChooser(
+    remoteTab,
+    "example.com",
+    "example.org",
+    rejectPromise
+  );
+  registerCleanupFunction(function() {
+    processChooser.unregister();
+  });
+}
+
+function ProcessChooser(remoteTab, from, to, rejectPromise = false) {
+  this.remoteTab = remoteTab;
+  this.fromDomain = from;
+  this.toDomain = to;
+  this.rejectPromise = rejectPromise;
+
+  this.registered = true;
+  Services.obs.addObserver(this, "http-on-may-change-process");
+}
+
+ProcessChooser.prototype = {
+  unregister() {
+    if (!this.registered) {
+      return;
+    }
+    this.registered = false;
+    Services.obs.removeObserver(this, "http-on-may-change-process");
+  },
+
+  examine(aRequestor) {
+    const channel = aRequestor.channel;
+
+    if (this.channel && this.channel != channel) {
+      // Hack: this is just so we don't get redirected multiple times.
+      info("same channel. give null");
+      return;
+    }
+
+    if (channel.URI.host != this.toDomain) {
+      info("wrong host for channel " + channel.URI.host);
+      return;
+    }
+
+    let redirects = channel.loadInfo.redirectChain;
+    if (redirects[redirects.length - 1].principal.URI.host != this.fromDomain) {
+      info("didn't find redirect");
+      return;
+    }
+
+    info("setting channel");
+    this.channel = channel;
+    let self = this;
+
+    info("unregistering");
+    this.unregister();
+
+    let identifier = 42;
+    let tabPromise = new Promise((resolve, reject) => {
+      if (self.rejectPromise) {
+        info("rejecting");
+        reject(Cr.NS_ERROR_NOT_AVAILABLE);
+        return;
+      }
+      // Can asyncly create a tab, or can resolve with a tab that was
+      // previously created.
+      info("resolving");
+      resolve(self.remoteTab.contentProcessId);
+    });
+
+    info("calling switchprocessto");
+    aRequestor.switchProcessTo(tabPromise, identifier);
+  },
+
+  observe(aSubject, aTopic, aData) {
+    switch (aTopic) {
+      case "http-on-may-change-process":
+        this.examine(aSubject.QueryInterface(Ci.nsIProcessSwitchRequestor));
+        break;
+      default:
+        ok(false, "Unexpected topic observed!");
+        break;
+    }
+  },
+
+  // nsISupports
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]),
+};
+
+add_task(async function() {
+  info(
+    "Check that a redirect in process A may be correctly handled in process B"
+  );
+
+  const kRoot1 = getRootDirectory(gTestPath).replace(
+    "chrome://mochitests/content/",
+    "https://example.com/"
+  );
+  const kRoot2 = getRootDirectory(gTestPath).replace(
+    "chrome://mochitests/content/",
+    "https://example.org/"
+  );
+  const kRoot3 = getRootDirectory(gTestPath);
+
+  // This process will attempt to load the page that redirects to a different origin
+  let tab1 = await BrowserTestUtils.openNewForegroundTab({
+    gBrowser,
+    url: kRoot1 + "dummy.html",
+    forceNewProcess: true,
+  });
+  // This process will eventually receive the redirected channel.
+  let tab2 = await BrowserTestUtils.openNewForegroundTab({
+    gBrowser,
+    url: kRoot2 + "dummy.html",
+    forceNewProcess: true,
+  });
+
+  let browser1 = gBrowser.getBrowserForTab(tab1);
+  let browser2 = gBrowser.getBrowserForTab(tab2);
+
+  // This is for testing purposes only.
+  // This "process chooser" will direct the channel to be opened in the second
+  // tab, and thus in the second parent.
+  let processChooser = _createProcessChooser(
+    browser2.frameLoader.remoteTab,
+    "example.com",
+    "example.org"
+  );
+
+  info("Loading redirected URL");
+  // Open the URL in the first process. We expect it to wind up in the second
+  // process.
+
+  // Define the child listener in the new channel.
+  await ContentTask.spawn(browser2, null, async function(arg) {
+    function ChannelListener(childListener) {
+      this.childListener = childListener;
+    }
+    ChannelListener.prototype = {
+      onStartRequest(aRequest) {
+        info("onStartRequest");
+        let channel = aRequest.QueryInterface(Ci.nsIChannel);
+        Assert.equal(
+          channel.URI.spec,
+          this.childListener.URI,
+          "Make sure the channel has the proper URI"
+        );
+        Assert.equal(
+          channel.originalURI.spec,
+          this.childListener.originalURI,
+          "Make sure the originalURI is correct"
+        );
+      },
+      onStopRequest(aRequest, aStatusCode) {
+        info("onStopRequest");
+        Assert.equal(aStatusCode, Cr.NS_OK, "Check the status code");
+        Assert.equal(
+          this.gotData,
+          true,
+          "Check that the channel received data"
+        );
+        if (this.childListener.onComplete) {
+          this.childListener.onComplete();
+        }
+        this.childListener.resolve();
+      },
+      onDataAvailable(aRequest, aContext, aInputStream, aOffset, aCount) {
+        this.gotData = true;
+        info("onDataAvailable");
+      },
+      QueryInterface: ChromeUtils.generateQI([
+        Ci.nsIStreamListener,
+        Ci.nsIRequestObserver,
+      ]),
+    };
+
+    function ChildListener(uri, originalURI, resolve) {
+      this.URI = uri;
+      this.originalURI = originalURI;
+      this.resolve = resolve;
+    }
+    ChildListener.prototype = {
+      // nsIChildProcessChannelListener
+      onChannelReady(aChildChannel, aIdentifier) {
+        Assert.equal(aIdentifier, 42, "Check the status code");
+        info("onChannelReady");
+        aChildChannel.completeRedirectSetup(new ChannelListener(this), null);
+      },
+      // nsIFactory
+      createInstance(aOuter, aIID) {
+        if (aOuter) {
+          throw Cr.NS_ERROR_NO_AGGREGATION;
+        }
+        return this.QueryInterface(aIID);
+      },
+      lockFactory() {},
+      // nsISupports
+      QueryInterface: ChromeUtils.generateQI([
+        Ci.nsIChildProcessChannelListener,
+        Ci.nsIFactory,
+      ]),
+      classID: Components.ID("{a6c142a9-eb38-4a09-a940-b71cdad479e1}"),
+    };
+
+    content.window.ChildListener = ChildListener;
+  });
+
+  // This promise instantiates a ChildListener and is resolved when the redirected
+  // channel is completed.
+  let loadedInProcess2Promise = ContentTask.spawn(
+    browser2,
+    {
+      URI: kRoot2 + "dummy.html",
+      originalURI: kRoot1 + "redirect.sjs?" + kRoot2 + "dummy.html",
+    },
+    async function(arg) {
+      // We register the listener in process no. 2
+      return new Promise(resolve => {
+        var childListener = new content.window.ChildListener(
+          arg.URI,
+          arg.originalURI,
+          resolve
+        );
+        var registrar = Components.manager.QueryInterface(
+          Ci.nsIComponentRegistrar
+        );
+        childListener.onComplete = () => {
+          registrar.unregisterFactory(childListener.classID, childListener);
+        };
+        registrar.registerFactory(
+          childListener.classID,
+          "",
+          "@mozilla.org/network/childProcessChannelListener;1",
+          childListener
+        );
+      });
+    }
+  );
+
+  let browser1LoadHasStopped = BrowserTestUtils.browserStopped(
+    browser1,
+    undefined,
+    true
+  );
+
+  await BrowserTestUtils.loadURI(
+    browser1,
+    kRoot1 + "redirect.sjs?" + kRoot2 + "dummy.html"
+  );
+
+  // Check that the channel was delivered to process no. 2
+  await loadedInProcess2Promise;
+  info("channel has loaded in second process");
+  // This is to check that the old channel was cancelled.
+  await browser1LoadHasStopped;
+
+  // check that a rejected promise also works.
+  processChooser = _createProcessChooser(
+    browser2.frameLoader.remoteTab,
+    "example.com",
+    "example.org",
+    true
+  );
+  let browser1LoadHasStoppedAgain = BrowserTestUtils.browserStopped(
+    browser1,
+    undefined,
+    true
+  );
+  await BrowserTestUtils.loadURI(
+    browser1,
+    kRoot1 + "redirect.sjs?" + kRoot2 + "dummy.html"
+  );
+  await browser1LoadHasStoppedAgain;
+  info("this is done now");
+
+  BrowserTestUtils.removeTab(tab1);
+  BrowserTestUtils.removeTab(tab2);
+
+  ok(true, "Got to the end of the test!");
+});