Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/dom/base/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 130 kB image not shown  

Quelle  nsFrameLoader.cpp   Sprache: C

 
/* -*- 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/. */


/*
 * Class for managing loading of a subframe (creation of the docshell,
 * handling of loads in it, recursion-checking).
 */


#include "nsFrameLoader.h"

#include "base/basictypes.h"

#include "prenv.h"

#include "nsDocShell.h"
#include "nsIContentInlines.h"
#include "nsIDocumentViewer.h"
#include "nsIPrintSettings.h"
#include "nsIPrintSettingsService.h"
#include "mozilla/dom/Document.h"
#include "nsPIDOMWindow.h"
#include "nsIWebNavigation.h"
#include "nsIWebProgress.h"
#include "nsIDocShell.h"
#include "nsIDocShellTreeOwner.h"
#include "nsDocShellLoadState.h"
#include "nsIBaseWindow.h"
#include "nsIBrowser.h"
#include "nsContentUtils.h"
#include "nsUnicharUtils.h"
#include "nsIScriptGlobalObject.h"
#include "nsIScriptSecurityManager.h"
#include "nsFrameLoaderOwner.h"
#include "nsIFrame.h"
#include "nsSubDocumentFrame.h"
#include "nsError.h"
#include "nsIAppWindow.h"
#include "nsIScriptError.h"
#include "nsGlobalWindowInner.h"
#include "nsGlobalWindowOuter.h"
#include "nsHTMLDocument.h"
#include "nsPIWindowRoot.h"
#include "nsLayoutUtils.h"
#include "nsView.h"
#include "nsViewManager.h"
#include "nsBaseWidget.h"
#include "nsQueryObject.h"
#include "ReferrerInfo.h"
#include "nsIOpenWindowInfo.h"
#include "nsISHistory.h"
#include "nsIURI.h"
#include "nsIXULRuntime.h"
#include "nsNetUtil.h"
#include "nsFocusManager.h"
#include "nsIINIParser.h"
#include "nsAppRunner.h"
#include "nsDirectoryService.h"
#include "nsDirectoryServiceDefs.h"

#include "nsGkAtoms.h"
#include "nsNameSpaceManager.h"

#include "nsThreadUtils.h"

#include "InProcessBrowserChildMessageManager.h"

#include "ContentParent.h"
#include "BrowserParent.h"
#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/ExpandedPrincipal.h"
#include "mozilla/FlushType.h"
#include "mozilla/HTMLEditor.h"
#include "mozilla/NullPrincipal.h"
#include "mozilla/Preferences.h"
#include "mozilla/PresShell.h"
#include "mozilla/PresShellInlines.h"
#include "mozilla/ProcessPriorityManager.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/ScrollContainerFrame.h"
#include "mozilla/StaticPrefs_fission.h"
#include "mozilla/Unused.h"
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/ChromeMessageSender.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/FrameCrashedEvent.h"
#include "mozilla/dom/FrameLoaderBinding.h"
#include "mozilla/dom/InProcessChild.h"
#include "mozilla/dom/MozFrameLoaderOwnerBinding.h"
#include "mozilla/dom/PBrowser.h"
#include "mozilla/dom/SessionHistoryEntry.h"
#include "mozilla/dom/SessionStoreChild.h"
#include "mozilla/dom/SessionStoreParent.h"
#include "mozilla/dom/SessionStoreUtils.h"
#include "mozilla/dom/WindowGlobalParent.h"
#include "mozilla/dom/XULFrameElement.h"
#include "mozilla/gfx/CrossProcessPaint.h"
#include "mozilla/ProfilerLabels.h"
#include "nsGenericHTMLFrameElement.h"

#include "jsapi.h"
#include "mozilla/dom/HTMLIFrameElement.h"
#include "nsSandboxFlags.h"
#include "mozilla/layers/CompositorBridgeChild.h"
#include "mozilla/dom/CustomEvent.h"

#include "mozilla/dom/ipc/StructuredCloneData.h"
#include "mozilla/WebBrowserPersistLocalDocument.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/PromiseNativeHandler.h"
#include "mozilla/dom/ChildSHistory.h"
#include "mozilla/dom/CanonicalBrowsingContext.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/ContentProcessManager.h"
#include "mozilla/dom/BrowserBridgeChild.h"
#include "mozilla/dom/BrowserHost.h"
#include "mozilla/dom/BrowserBridgeHost.h"
#include "mozilla/dom/BrowsingContextGroup.h"

#include "mozilla/dom/SessionStorageManager.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/PBackgroundChild.h"
#include "mozilla/dom/PBackgroundSessionStorageCache.h"
#include "mozilla/ipc/BackgroundUtils.h"

#include "mozilla/dom/HTMLBodyElement.h"

#include "mozilla/ContentPrincipal.h"

#include "buildid_section.h"
#include "mozilla/toolkit/library/buildid_reader_ffi.h"
#include "nsXPCOMPrivate.h"  // for XUL_DLL

#include "nsXULPopupManager.h"

#ifdef NS_PRINTING
#  include "nsIWebBrowserPrint.h"
#endif

#if defined(MOZ_TELEMETRY_REPORTING)
#  include "mozilla/glean/DomMetrics.h"
#endif  // defined(MOZ_TELEMETRY_REPORTING)

using namespace mozilla;
using namespace mozilla::hal;
using namespace mozilla::dom;
using namespace mozilla::dom::ipc;
using namespace mozilla::ipc;
using namespace mozilla::layers;
using namespace mozilla::layout;
using ViewID = ScrollableLayerGuid::ViewID;

using PrintPreviewResolver = std::function<void(const PrintPreviewResultInfo&)>;

// Bug 8065: Limit content frame depth to some reasonable level. This
// does not count chrome frames when determining depth, nor does it
// prevent chrome recursion.  Number is fairly arbitrary, but meant to
// keep number of shells to a reasonable number on accidental recursion with a
// small (but not 1) branching factor.  With large branching factors the number
// of shells can rapidly become huge and run us out of memory.  To solve that,
// we'd need to re-institute a fixed version of bug 98158.
#define MAX_DEPTH_CONTENT_FRAMES 10

NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsFrameLoader, mPendingBrowsingContext,
                                      mMessageManager, mChildMessageManager,
                                      mRemoteBrowser, mSessionStoreChild)
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFrameLoader)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrameLoader)

NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFrameLoader)
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
  NS_INTERFACE_MAP_ENTRY_CONCRETE(nsFrameLoader)
  NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
  NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END

nsFrameLoader::nsFrameLoader(Element* aOwner, BrowsingContext* aBrowsingContext,
                             bool aIsRemoteFrame, bool aNetworkCreated)
    : mPendingBrowsingContext(aBrowsingContext),
      mOwnerContent(aOwner),
      mDetachedSubdocFrame(nullptr),
      mPendingSwitchID(0),
      mChildID(0),
      mRemoteType(NOT_REMOTE_TYPE),
      mInitialized(false),
      mDepthTooGreat(false),
      mIsTopLevelContent(false),
      mDestroyCalled(false),
      mNeedsAsyncDestroy(false),
      mInSwap(false),
      mInShow(false),
      mHideCalled(false),
      mNetworkCreated(aNetworkCreated),
      mLoadingOriginalSrc(false),
      mShouldCheckForRecursion(false),
      mRemoteBrowserShown(false),
      mRemoteBrowserSized(false),
      mIsRemoteFrame(aIsRemoteFrame),
      mWillChangeProcess(false),
      mObservingOwnerContent(false),
      mHadDetachedFrame(false),
      mTabProcessCrashFired(false) {
  nsCOMPtr<nsFrameLoaderOwner> owner = do_QueryInterface(aOwner);
  owner->AttachFrameLoader(this);
}

nsFrameLoader::~nsFrameLoader() {
  if (mMessageManager) {
    mMessageManager->Disconnect();
  }

  MOZ_ASSERT(!mOwnerContent);
  MOZ_RELEASE_ASSERT(mDestroyCalled);
}

static nsAtom* TypeAttrName(Element* aOwnerContent) {
  return aOwnerContent->IsXULElement() ? nsGkAtoms::type
                                       : nsGkAtoms::mozframetype;
}

static void GetFrameName(Element* aOwnerContent, nsAString& aFrameName) {
  int32_t namespaceID = aOwnerContent->GetNameSpaceID();
  if (namespaceID == kNameSpaceID_XHTML && !aOwnerContent->IsInHTMLDocument()) {
    aOwnerContent->GetAttr(nsGkAtoms::id, aFrameName);
  } else {
    aOwnerContent->GetAttr(nsGkAtoms::name, aFrameName);
    // XXX if no NAME then use ID, after a transition period this will be
    // changed so that XUL only uses ID too (bug 254284).
    if (aFrameName.IsEmpty() && namespaceID == kNameSpaceID_XUL) {
      aOwnerContent->GetAttr(nsGkAtoms::id, aFrameName);
    }
  }
}

// If this method returns true, the nsFrameLoader will act as a boundary, as is
// the case for <iframe mozbrowser> and <browser type="content"> elements.
//
// # Historical Notes  (10 April 2019)
//
// In the past, this boundary was defined by the "typeContent" and "typeChrome"
// nsIDocShellTreeItem types. There was only ever a single split in the tree,
// and it occurred at the boundary between these two types of docshells. When
// <iframe mozbrowser> was introduced, it was given special casing to make it
// act like a second boundary, without having to change the existing code.
//
// The about:addons page, which is loaded within a content browser, then added a
// remote <browser type="content" remote="true"> element. When remote, this
// would also act as a mechanism for creating a disjoint tree, due to the
// process keeping the embedder and embedee separate.
//
// However, when initial out-of-process iframe support was implemented, this
// codepath became a risk, as it could've caused the oop iframe remote
// WindowProxy code to be activated for the addons page. This was fixed by
// extendng the isolation logic previously reserved to <iframe mozbrowser> to
// also cover <browser> elements with the explicit `remote` property loaded in
// content.
//
// To keep these boundaries clear, and allow them to work in a cross-process
// manner, they are no longer handled by typeContent and typeChrome. Instead,
// the actual BrowsingContext tree is broken at these edges.
static bool IsTopContent(BrowsingContext* aParent, Element* aOwner) {
  if (XRE_IsContentProcess()) {
    return false;
  }

  if (aParent->IsContent()) {
    // If we're already in content, we may still want to create a new
    // BrowsingContext tree if our element is a xul browser element with a
    // `remote="true"` marker.
    return aOwner->IsXULElement() &&
           aOwner->AttrValueIs(kNameSpaceID_None, nsGkAtoms::remote,
                               nsGkAtoms::_true, eCaseMatters);
  }

  // If we're in a chrome context, we want to start a new tree if we are an
  // element with a `type="content"` marker.
  return aOwner->AttrValueIs(kNameSpaceID_None, TypeAttrName(aOwner),
                             nsGkAtoms::content, eIgnoreCase);
}

static already_AddRefed<BrowsingContext> CreateBrowsingContext(
    Element* aOwner, nsIOpenWindowInfo* aOpenWindowInfo,
    BrowsingContextGroup* aSpecificGroup, bool aNetworkCreated = false) {
  MOZ_ASSERT(!aOpenWindowInfo || !aSpecificGroup,
             "Only one of SpecificGroup and OpenWindowInfo may be provided!");

  // If we've got a pending BrowserParent from the content process, use the
  // BrowsingContext which was created for it.
  if (aOpenWindowInfo && aOpenWindowInfo->GetNextRemoteBrowser()) {
    MOZ_ASSERT(XRE_IsParentProcess());
    return do_AddRef(
        aOpenWindowInfo->GetNextRemoteBrowser()->GetBrowsingContext());
  }

  RefPtr<BrowsingContext> opener;
  if (aOpenWindowInfo && !aOpenWindowInfo->GetForceNoOpener()) {
    opener = aOpenWindowInfo->GetParent();
    if (opener) {
      // Must create BrowsingContext with opener in-process.
      MOZ_ASSERT(opener->IsInProcess());

      // This can only happen when the opener was closed from a nested event
      // loop in the window provider code, and only when the open was triggered
      // by a non-e10s tab, and the new tab is being opened in a new browser
      // window. Since it is a corner case among corner cases, and the opener
      // window will appear to be null to consumers after it is discarded
      // anyway, just drop the opener entirely.
      if (opener->IsDiscarded()) {
        NS_WARNING(
            "Opener was closed from a nested event loop in the parent process. "
            "Please fix this.");
        opener = nullptr;
      }
    }
  }

  RefPtr<nsGlobalWindowInner> parentInner =
      nsGlobalWindowInner::Cast(aOwner->OwnerDoc()->GetInnerWindow());
  if (NS_WARN_IF(!parentInner) || parentInner->IsDying()) {
    return nullptr;
  }

  BrowsingContext* parentBC = parentInner->GetBrowsingContext();
  if (NS_WARN_IF(!parentBC) || parentBC->IsDiscarded()) {
    return nullptr;
  }

  // Determine the frame name for the new browsing context.
  nsAutoString frameName;
  GetFrameName(aOwner, frameName);

  // Create our BrowsingContext without immediately attaching it. It's possible
  // that no DocShell or remote browser will ever be created for this
  // FrameLoader, particularly if the document that we were created for is not
  // currently active. And in that latter case, if we try to attach our BC now,
  // it will wind up attached as a child of the currently active inner window
  // for the BrowsingContext, and cause no end of trouble.
  if (IsTopContent(parentBC, aOwner)) {
    BrowsingContext::CreateDetachedOptions options;
    if (aOpenWindowInfo) {
      options.topLevelCreatedByWebContent =
          aOpenWindowInfo->GetIsTopLevelCreatedByWebContent();
    }
    options.windowless = parentBC->Windowless();

    // Create toplevel context without a parent & as Type::Content.
    return BrowsingContext::CreateDetached(
        nullptr, opener, aSpecificGroup, frameName,
        BrowsingContext::Type::Content, options);
  }

  MOZ_ASSERT(!aOpenWindowInfo,
             "Can't have openWindowInfo for non-toplevel context");

  MOZ_ASSERT(!aSpecificGroup,
             "Can't force BrowsingContextGroup for non-toplevel context");
  return BrowsingContext::CreateDetached(
      parentInner, nullptr, nullptr, frameName, parentBC->GetType(),
      {.createdDynamically = !aNetworkCreated,
       .windowless = parentBC->Windowless()});
}

static bool InitialLoadIsRemote(Element* aOwner) {
  // The initial load in an content process iframe should never be made remote.
  // Content process iframes always become remote due to navigation.
  if (XRE_IsContentProcess()) {
    return false;
  }

  // Otherwise, we're remote if we have "remote=true" and we're a XUL element.
  return (aOwner->GetNameSpaceID() == kNameSpaceID_XUL) &&
         aOwner->AttrValueIs(kNameSpaceID_None, nsGkAtoms::remote,
                             nsGkAtoms::_true, eCaseMatters);
}

static already_AddRefed<BrowsingContextGroup> InitialBrowsingContextGroup(
    Element* aOwner) {
  nsAutoString attrString;
  if (aOwner->GetNameSpaceID() != kNameSpaceID_XUL ||
      !aOwner->GetAttr(nsGkAtoms::initialBrowsingContextGroupId, attrString)) {
    return nullptr;
  }

  // It's OK to read the attribute using a signed 64-bit integer parse, as an ID
  // generated using `nsContentUtils::GenerateProcessSpecificId` (like BCG IDs)
  // will only ever use 53 bits of precision, so it can be round-tripped through
  // a JS number.
  nsresult rv = NS_OK;
  int64_t signedGroupId = attrString.ToInteger64(&rv, 10);
  if (NS_FAILED(rv) || signedGroupId <= 0) {
    MOZ_DIAGNOSTIC_ASSERT(
        false"we intended to have a particular id, but failed to parse it!");
    return nullptr;
  }

  return BrowsingContextGroup::GetOrCreate(uint64_t(signedGroupId));
}

already_AddRefed<nsFrameLoader> nsFrameLoader::Create(
    Element* aOwner, bool aNetworkCreated, nsIOpenWindowInfo* aOpenWindowInfo) {
  NS_ENSURE_TRUE(aOwner, nullptr);
  Document* doc = aOwner->OwnerDoc();

  // We never create nsFrameLoaders for elements in resource documents.
  //
  // We never create nsFrameLoaders for elements in data documents, unless the
  // document is a static document.
  // Static documents are an exception because any sub-documents need an
  // nsFrameLoader to keep the relevant docShell alive, even though the
  // nsFrameLoader isn't used to load anything (the sub-document is created by
  // the static clone process).
  //
  // We never create nsFrameLoaders for elements that are not
  // in-composed-document, unless the element belongs to a static document.
  // Static documents are an exception because this method is called at a point
  // in the static clone process before aOwner has been inserted into its
  // document.  For other types of documents this wouldn't be a problem since
  // we'd create the nsFrameLoader as necessary after aOwner is inserted into a
  // document, but the mechanisms that take care of that don't apply for static
  // documents so we need to create the nsFrameLoader now. (This isn't wasteful
  // since for a static document we know aOwner will end up in a document and
  // the nsFrameLoader will be used for its docShell.)
  //
  NS_ENSURE_TRUE(!doc->IsResourceDoc() &&
                     ((!doc->IsLoadedAsData() && aOwner->IsInComposedDoc()) ||
                      doc->IsStaticDocument()),
                 nullptr);

  RefPtr<BrowsingContextGroup> group = InitialBrowsingContextGroup(aOwner);
  RefPtr<BrowsingContext> context =
      CreateBrowsingContext(aOwner, aOpenWindowInfo, group, aNetworkCreated);
  NS_ENSURE_TRUE(context, nullptr);

  if (XRE_IsParentProcess() && aOpenWindowInfo) {
    MOZ_ASSERT(context->IsTopContent());
    if (RefPtr<BrowsingContext> crossGroupOpener =
            aOpenWindowInfo->GetParent()) {
      context->Canonical()->SetCrossGroupOpenerId(crossGroupOpener->Id());
    }
  }

  bool isRemoteFrame = InitialLoadIsRemote(aOwner);
  RefPtr<nsFrameLoader> fl =
      new nsFrameLoader(aOwner, context, isRemoteFrame, aNetworkCreated);
  fl->mOpenWindowInfo = aOpenWindowInfo;

  // If this is a toplevel initial remote frame, we're looking at a browser
  // loaded in the parent process. Pull the remote type attribute off of the
  // <browser> element to determine which remote type it should be loaded in, or
  // use `DEFAULT_REMOTE_TYPE` if we can't tell.
  if (isRemoteFrame) {
    MOZ_ASSERT(XRE_IsParentProcess());
    nsAutoString remoteType;
    if (aOwner->GetAttr(nsGkAtoms::RemoteType, remoteType) &&
        !remoteType.IsEmpty()) {
      CopyUTF16toUTF8(remoteType, fl->mRemoteType);
    } else {
      fl->mRemoteType = DEFAULT_REMOTE_TYPE;
    }
  }
  return fl.forget();
}

/* static */
already_AddRefed<nsFrameLoader> nsFrameLoader::Recreate(
    mozilla::dom::Element* aOwner, BrowsingContext* aContext,
    BrowsingContextGroup* aSpecificGroup,
    const NavigationIsolationOptions& aRemotenessOptions, bool aIsRemote,
    bool aNetworkCreated, bool aPreserveContext) {
  NS_ENSURE_TRUE(aOwner, nullptr);

#ifdef DEBUG
  // This version of Create is only called for Remoteness updates, so we can
  // assume we need a FrameLoader here and skip the check in the other Create.
  Document* doc = aOwner->OwnerDoc();
  MOZ_ASSERT(!doc->IsResourceDoc());
  MOZ_ASSERT((!doc->IsLoadedAsData() && aOwner->IsInComposedDoc()) ||
             doc->IsStaticDocument());
#endif

  RefPtr<BrowsingContext> context = aContext;
  if (!context || !aPreserveContext) {
    context = CreateBrowsingContext(aOwner, /* openWindowInfo */ nullptr,
                                    aSpecificGroup);
    if (aContext) {
      MOZ_ASSERT(
          XRE_IsParentProcess(),
          "Recreating browing contexts only supported in the parent process");
      aContext->Canonical()->SynchronizeLayoutHistoryState();
      aContext->Canonical()->ReplacedBy(context->Canonical(),
                                        aRemotenessOptions);
    }
  }
  NS_ENSURE_TRUE(context, nullptr);

  RefPtr<nsFrameLoader> fl =
      new nsFrameLoader(aOwner, context, aIsRemote, aNetworkCreated);
  return fl.forget();
}

void nsFrameLoader::LoadFrame(bool aOriginalSrc,
                              bool aShouldCheckForRecursion) {
  if (NS_WARN_IF(!mOwnerContent)) {
    return;
  }

  nsAutoString src;
  nsCOMPtr<nsIPrincipal> principal;
  nsCOMPtr<nsIContentSecurityPolicy> csp;

  bool isSrcdoc = mOwnerContent->IsHTMLElement(nsGkAtoms::iframe) &&
                  mOwnerContent->HasAttr(nsGkAtoms::srcdoc);
  if (isSrcdoc) {
    src.AssignLiteral("about:srcdoc");
    principal = mOwnerContent->NodePrincipal();
    csp = mOwnerContent->GetCsp();
  } else {
    GetURL(src, getter_AddRefs(principal), getter_AddRefs(csp));

    src.Trim(" \t\n\r");

    if (src.IsEmpty()) {
      // If the frame is a XUL element and has the attribute 'nodefaultsrc=true'
      // then we will not use 'about:blank' as fallback but return early without
      // starting a load if no 'src' attribute is given (or it's empty).
      if (mOwnerContent->IsXULElement() &&
          mOwnerContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::nodefaultsrc,
                                     nsGkAtoms::_true, eCaseMatters)) {
        return;
      }
      src.AssignLiteral("about:blank");
      principal = mOwnerContent->NodePrincipal();
      csp = mOwnerContent->GetCsp();
    }
  }

  Document* doc = mOwnerContent->OwnerDoc();
  if (doc->IsStaticDocument()) {
    return;
  }

  // If we are being loaded by a lazy loaded iframe, use its base URI first
  // instead of the current base URI.
  auto* lazyBaseURI = GetLazyLoadFrameResumptionState().mBaseURI.get();
  nsIURI* baseURI = lazyBaseURI ? lazyBaseURI : mOwnerContent->GetBaseURI();

  auto encoding = doc->GetDocumentCharacterSet();

  nsCOMPtr<nsIURI> uri;
  nsresult rv = NS_NewURI(getter_AddRefs(uri), src, encoding, baseURI);

  // If the URI was malformed, try to recover by loading about:blank.
  if (rv == NS_ERROR_MALFORMED_URI) {
    rv = NS_NewURI(getter_AddRefs(uri), u"about:blank"_ns, encoding, baseURI);
  }

  if (NS_SUCCEEDED(rv)) {
    rv = LoadURI(uri, principal, csp, aOriginalSrc, aShouldCheckForRecursion);
  }

  if (NS_FAILED(rv)) {
    FireErrorEvent();
  }
}

void nsFrameLoader::ConfigRemoteProcess(const nsACString& aRemoteType,
                                        ContentParent* aContentParent) {
  MOZ_DIAGNOSTIC_ASSERT(IsRemoteFrame(), "Must be a remote frame");
  MOZ_DIAGNOSTIC_ASSERT(!mRemoteBrowser, "Must not have a browser yet");
  MOZ_DIAGNOSTIC_ASSERT_IF(aContentParent,
                           aContentParent->GetRemoteType() == aRemoteType);

  mRemoteType = aRemoteType;
  mChildID = aContentParent ? aContentParent->ChildID() : 0;
}

void nsFrameLoader::FireErrorEvent() {
  if (!mOwnerContent) {
    return;
  }
  RefPtr<AsyncEventDispatcher> loadBlockingAsyncDispatcher =
      new LoadBlockingAsyncEventDispatcher(
          mOwnerContent, u"error"_ns, CanBubble::eNo, ChromeOnlyDispatch::eNo);
  loadBlockingAsyncDispatcher->PostDOMEvent();
}

nsresult nsFrameLoader::LoadURI(nsIURI* aURI,
                                nsIPrincipal* aTriggeringPrincipal,
                                nsIContentSecurityPolicy* aCsp,
                                bool aOriginalSrc,
                                bool aShouldCheckForRecursion) {
  if (!aURI) return NS_ERROR_INVALID_POINTER;
  NS_ENSURE_STATE(!mDestroyCalled && mOwnerContent);
  MOZ_ASSERT(
      aTriggeringPrincipal,
      "Must have an explicit triggeringPrincipal to nsFrameLoader::LoadURI.");

  mLoadingOriginalSrc = aOriginalSrc;
  mShouldCheckForRecursion = aShouldCheckForRecursion;

  nsCOMPtr<Document> doc = mOwnerContent->OwnerDoc();

  nsresult rv;
  rv = CheckURILoad(aURI, aTriggeringPrincipal);
  NS_ENSURE_SUCCESS(rv, rv);

  mURIToLoad = aURI;
  mTriggeringPrincipal = aTriggeringPrincipal;
  mCsp = aCsp;
  rv = doc->InitializeFrameLoader(this);
  if (NS_FAILED(rv)) {
    mURIToLoad = nullptr;
    mTriggeringPrincipal = nullptr;
    mCsp = nullptr;
  }
  return rv;
}

void nsFrameLoader::ResumeLoad(uint64_t aPendingSwitchID) {
  Document* doc = mOwnerContent->OwnerDoc();
  if (doc->IsStaticDocument()) {
    // Static doc shouldn't load sub-documents.
    return;
  }

  if (NS_WARN_IF(mDestroyCalled || !mOwnerContent)) {
    FireErrorEvent();
    return;
  }

  mLoadingOriginalSrc = false;
  mShouldCheckForRecursion = false;
  mURIToLoad = nullptr;
  mPendingSwitchID = aPendingSwitchID;
  mTriggeringPrincipal = mOwnerContent->NodePrincipal();
  mCsp = mOwnerContent->GetCsp();

  nsresult rv = doc->InitializeFrameLoader(this);
  if (NS_FAILED(rv)) {
    mPendingSwitchID = 0;
    mTriggeringPrincipal = nullptr;
    mCsp = nullptr;

    FireErrorEvent();
  }
}

nsresult nsFrameLoader::ReallyStartLoading() {
  nsresult rv = ReallyStartLoadingInternal();
  if (NS_FAILED(rv)) {
    FireErrorEvent();
  }

  return rv;
}

nsresult nsFrameLoader::ReallyStartLoadingInternal() {
  NS_ENSURE_STATE((mURIToLoad || mPendingSwitchID) && mOwnerContent &&
                  mOwnerContent->IsInComposedDoc());

  AUTO_PROFILER_LABEL("nsFrameLoader::ReallyStartLoadingInternal", OTHER);
  RefPtr<nsDocShellLoadState> loadState;
  if (!mPendingSwitchID) {
    loadState = new nsDocShellLoadState(mURIToLoad);
    loadState->SetOriginalFrameSrc(mLoadingOriginalSrc);
    loadState->SetShouldCheckForRecursion(mShouldCheckForRecursion);

    // The triggering principal could be null if the frame is loaded other
    // than the src attribute, for example, the frame is sandboxed. In that
    // case we use the principal of the owner content, which is needed to
    // prevent XSS attaches on documents loaded in subframes.
    if (mTriggeringPrincipal) {
      loadState->SetTriggeringPrincipal(mTriggeringPrincipal);
    } else {
      loadState->SetTriggeringPrincipal(mOwnerContent->NodePrincipal());
    }

    // If we have an explicit CSP, we set it. If not, we only query it from
    // the document in case there was no explicit triggeringPrincipal.
    // Otherwise it's possible that the original triggeringPrincipal did not
    // have a CSP which causes the CSP on the Principal and explicit CSP
    // to be out of sync.
    if (mCsp) {
      loadState->SetCsp(mCsp);
    } else if (!mTriggeringPrincipal) {
      nsCOMPtr<nsIContentSecurityPolicy> csp = mOwnerContent->GetCsp();
      loadState->SetCsp(csp);
    }

    nsAutoString srcdoc;
    bool isSrcdoc = mOwnerContent->IsHTMLElement(nsGkAtoms::iframe) &&
                    mOwnerContent->GetAttr(nsGkAtoms::srcdoc, srcdoc);

    if (isSrcdoc) {
      loadState->SetSrcdocData(srcdoc);
      loadState->SetBaseURI(mOwnerContent->GetBaseURI());
    }

    auto referrerInfo = MakeRefPtr<ReferrerInfo>(
        *mOwnerContent, GetLazyLoadFrameResumptionState().mReferrerPolicy);
    loadState->SetReferrerInfo(referrerInfo);

    loadState->SetIsFromProcessingFrameAttributes();

    // Default flags:
    int32_t flags = nsIWebNavigation::LOAD_FLAGS_NONE;
    loadState->SetLoadFlags(flags);

    loadState->SetFirstParty(false);

    Document* ownerDoc = mOwnerContent->OwnerDoc();
    if (ownerDoc) {
      loadState->SetTriggeringStorageAccess(ownerDoc->UsingStorageAccess());
      loadState->SetTriggeringWindowId(ownerDoc->InnerWindowID());
    }

    // If we're loading the default about:blank document in a <browser> element,
    // prevent the load from causing a process switch by explicitly overriding
    // remote type selection.
    if (mPendingBrowsingContext->IsTopContent() &&
        mOwnerContent->IsXULElement(nsGkAtoms::browser) &&
        NS_IsAboutBlank(mURIToLoad) &&
        loadState->TriggeringPrincipal()->IsSystemPrincipal()) {
      loadState->SetRemoteTypeOverride(mRemoteType);
    }
  }

  if (IsRemoteFrame()) {
    if (!EnsureRemoteBrowser()) {
      NS_WARNING("Couldn't create child process for iframe.");
      return NS_ERROR_FAILURE;
    }

    if (mPendingSwitchID) {
      mRemoteBrowser->ResumeLoad(mPendingSwitchID);
      mPendingSwitchID = 0;
    } else {
      mRemoteBrowser->LoadURL(loadState);
    }

    if (!mRemoteBrowserShown) {
      // This can fail if it's too early to show the frame, we will retry later.
      Unused << ShowRemoteFrame(
          /* aFrame = */ do_QueryFrame(GetPrimaryFrameOfOwningContent()));
    }

    return NS_OK;
  }

  nsresult rv = MaybeCreateDocShell();
  if (NS_FAILED(rv)) {
    return rv;
  }
  MOZ_ASSERT(GetDocShell(),
             "MaybeCreateDocShell succeeded with a null docShell");

  // If we have a pending switch, just resume our load.
  if (mPendingSwitchID) {
    bool tmpState = mNeedsAsyncDestroy;
    mNeedsAsyncDestroy = true;
    rv = GetDocShell()->ResumeRedirectedLoad(mPendingSwitchID, -1);
    mNeedsAsyncDestroy = tmpState;
    mPendingSwitchID = 0;
    return rv;
  }

  // Just to be safe, recheck uri.
  rv = CheckURILoad(mURIToLoad, mTriggeringPrincipal);
  NS_ENSURE_SUCCESS(rv, rv);

  mLoadingOriginalSrc = false;
  mShouldCheckForRecursion = false;

  // Kick off the load...
  bool tmpState = mNeedsAsyncDestroy;
  mNeedsAsyncDestroy = true;

  RefPtr<nsDocShell> docShell = GetDocShell();
  rv = docShell->LoadURI(loadState, false);
  mNeedsAsyncDestroy = tmpState;
  mURIToLoad = nullptr;
  NS_ENSURE_SUCCESS(rv, rv);

  return NS_OK;
}

nsresult nsFrameLoader::CheckURILoad(nsIURI* aURI,
                                     nsIPrincipal* aTriggeringPrincipal) {
  // Check for security.  The fun part is trying to figure out what principals
  // to use.  The way I figure it, if we're doing a LoadFrame() accidentally
  // (eg someone created a frame/iframe node, we're being parsed, XUL iframes
  // are being reframed, etc.) then we definitely want to use the node
  // principal of mOwnerContent for security checks.  If, on the other hand,
  // someone's setting the src on our owner content, or created it via script,
  // or whatever, then they can clearly access it... and we should still use
  // the principal of mOwnerContent.  I don't think that leads to privilege
  // escalation, and it's reasonably guaranteed to not lead to XSS issues
  // (since caller can already access mOwnerContent in this case).  So just use
  // the principal of mOwnerContent no matter what.  If script wants to run
  // things with its own permissions, which differ from those of mOwnerContent
  // (which means the script is privileged in some way) it should set
  // window.location instead.
  nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();

  // Get our principal
  nsIPrincipal* principal =
      (aTriggeringPrincipal ? aTriggeringPrincipal
                            : mOwnerContent->NodePrincipal());

  // Check if we are allowed to load absURL
  nsresult rv = secMan->CheckLoadURIWithPrincipal(
      principal, aURI, nsIScriptSecurityManager::STANDARD,
      mOwnerContent->OwnerDoc()->InnerWindowID());
  if (NS_FAILED(rv)) {
    return rv;  // We're not
  }

  // Bail out if this is an infinite recursion scenario
  if (IsRemoteFrame()) {
    return NS_OK;
  }
  return CheckForRecursiveLoad(aURI);
}

nsDocShell* nsFrameLoader::GetDocShell(ErrorResult& aRv) {
  if (IsRemoteFrame()) {
    return nullptr;
  }

  // If we have an owner, make sure we have a docshell and return
  // that. If not, we're most likely in the middle of being torn down,
  // then we just return null.
  if (mOwnerContent) {
    nsresult rv = MaybeCreateDocShell();
    if (NS_FAILED(rv)) {
      aRv.Throw(rv);
      return nullptr;
    }
    MOZ_ASSERT(GetDocShell(),
               "MaybeCreateDocShell succeeded, but null docShell");
  }

  return GetDocShell();
}

static void SetTreeOwnerAndChromeEventHandlerOnDocshellTree(
    nsIDocShellTreeItem* aItem, nsIDocShellTreeOwner* aOwner,
    EventTarget* aHandler) {
  MOZ_ASSERT(aItem, "Must have item");

  aItem->SetTreeOwner(aOwner);

  int32_t childCount = 0;
  aItem->GetInProcessChildCount(&childCount);
  for (int32_t i = 0; i < childCount; ++i) {
    nsCOMPtr<nsIDocShellTreeItem> item;
    aItem->GetInProcessChildAt(i, getter_AddRefs(item));
    if (aHandler) {
      nsCOMPtr<nsIDocShell> shell(do_QueryInterface(item));
      shell->SetChromeEventHandler(aHandler);
    }
    SetTreeOwnerAndChromeEventHandlerOnDocshellTree(item, aOwner, aHandler);
  }
}

#if defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
static bool CheckDocShellType(mozilla::dom::Element* aOwnerContent,
                              nsIDocShellTreeItem* aDocShell, nsAtom* aAtom) {
  bool isContent = aOwnerContent->AttrValueIs(kNameSpaceID_None, aAtom,
                                              nsGkAtoms::content, eIgnoreCase);

  if (isContent) {
    return aDocShell->ItemType() == nsIDocShellTreeItem::typeContent;
  }

  nsCOMPtr<nsIDocShellTreeItem> parent;
  aDocShell->GetInProcessParent(getter_AddRefs(parent));

  return parent && parent->ItemType() == aDocShell->ItemType();
}
#endif  // defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)

/**
 * Hook up a given TreeItem to its tree owner. aItem's type must have already
 * been set, and it should already be part of the DocShellTree.
 * @param aItem the treeitem we're working with
 * @param aTreeOwner the relevant treeowner; might be null
 */

void nsFrameLoader::AddTreeItemToTreeOwner(nsIDocShellTreeItem* aItem,
                                           nsIDocShellTreeOwner* aOwner) {
  MOZ_ASSERT(aItem, "Must have docshell treeitem");
  MOZ_ASSERT(mOwnerContent, "Must have owning content");

  MOZ_DIAGNOSTIC_ASSERT(
      CheckDocShellType(mOwnerContent, aItem, TypeAttrName(mOwnerContent)),
      "Correct ItemType should be set when creating BrowsingContext");

  if (mIsTopLevelContent) {
    bool is_primary = mOwnerContent->AttrValueIs(
        kNameSpaceID_None, nsGkAtoms::primary, nsGkAtoms::_true, eIgnoreCase);
    if (aOwner) {
      mOwnerContent->AddMutationObserver(this);
      mObservingOwnerContent = true;
      aOwner->ContentShellAdded(aItem, is_primary);
    }
  }
}

static bool AllDescendantsOfType(BrowsingContext* aParent,
                                 BrowsingContext::Type aType) {
  for (auto& child : aParent->Children()) {
    if (child->GetType() != aType || !AllDescendantsOfType(child, aType)) {
      return false;
    }
  }

  return true;
}

void nsFrameLoader::MaybeShowFrame() {
  nsIFrame* frame = GetPrimaryFrameOfOwningContent();
  if (frame) {
    nsSubDocumentFrame* subDocFrame = do_QueryFrame(frame);
    if (subDocFrame) {
      subDocFrame->MaybeShowViewer();
    }
  }
}

static ScrollbarPreference GetScrollbarPreference(const Element* aOwner) {
  if (!aOwner) {
    return ScrollbarPreference::Auto;
  }
  const nsAttrValue* attrValue = aOwner->GetParsedAttr(nsGkAtoms::scrolling);
  return nsGenericHTMLFrameElement::MapScrollingAttribute(attrValue);
}

static CSSIntSize GetMarginAttributes(const Element* aOwner) {
  CSSIntSize result(-1, -1);
  auto* content = nsGenericHTMLElement::FromNodeOrNull(aOwner);
  if (!content) {
    return result;
  }
  const nsAttrValue* attr = content->GetParsedAttr(nsGkAtoms::marginwidth);
  if (attr && attr->Type() == nsAttrValue::eInteger) {
    result.width = attr->GetIntegerValue();
  }
  attr = content->GetParsedAttr(nsGkAtoms::marginheight);
  if (attr && attr->Type() == nsAttrValue::eInteger) {
    result.height = attr->GetIntegerValue();
  }
  return result;
}

bool nsFrameLoader::Show(nsSubDocumentFrame* aFrame) {
  if (mInShow) {
    return false;
  }
  mInShow = true;

  auto resetInShow = mozilla::MakeScopeExit([&] { mInShow = false; });
  if (IsRemoteFrame()) {
    return ShowRemoteFrame(aFrame);
  }
  const LayoutDeviceIntSize size = aFrame->GetInitialSubdocumentSize();
  nsresult rv = MaybeCreateDocShell();
  if (NS_FAILED(rv)) {
    return false;
  }
  nsDocShell* ds = GetDocShell();
  MOZ_ASSERT(ds, "MaybeCreateDocShell succeeded, but null docShell");
  if (!ds) {
    return false;
  }

  ds->SetScrollbarPreference(GetScrollbarPreference(mOwnerContent));
  const bool marginsChanged =
      ds->UpdateFrameMargins(GetMarginAttributes(mOwnerContent));

  nsView* view = aFrame->EnsureInnerView();
  if (!view) {
    return false;
  }

  // If we already have a pres shell (which can happen with <object> / <embed>)
  // then hook it up in the view tree.
  if (PresShell* presShell = ds->GetPresShell()) {
    // Ensure root scroll frame is reflowed in case margins have changed.
    if (marginsChanged) {
      if (nsIFrame* rootScrollContainerFrame =
              presShell->GetRootScrollContainerFrame()) {
        presShell->FrameNeedsReflow(rootScrollContainerFrame,
                                    IntrinsicDirty::None, NS_FRAME_IS_DIRTY);
      }
    }
    nsView* childView = presShell->GetViewManager()->GetRootView();
    MOZ_DIAGNOSTIC_ASSERT(childView);
    if (childView->GetParent() == view) {
      // We were probably doing a docshell swap and succeeded before getting
      // here, hooray, nothing else to do.
      return true;
    }

    // We did layout before due to <object> or <embed> and now we need to fix
    // up our stuff and initialize our docshell below too.
    MOZ_DIAGNOSTIC_ASSERT(!view->GetFirstChild());
    MOZ_DIAGNOSTIC_ASSERT(!childView->GetParent(), "Stale view?");
    nsSubDocumentFrame::InsertViewsInReverseOrder(childView, view);
    nsSubDocumentFrame::EndSwapDocShellsForViews(view->GetFirstChild());
  }

  RefPtr<nsDocShell> baseWindow = GetDocShell();
  baseWindow->InitWindow(view->GetWidget(), 0, 0, size.width, size.height);
  baseWindow->SetVisibility(true);
  NS_ENSURE_TRUE(GetDocShell(), false);

  // Trigger editor re-initialization if midas is turned on in the sub-document.
  // This shouldn't be necessary, but given the way our editor works, it is.
  //
  // See https://bugzilla.mozilla.org/show_bug.cgi?id=284245
  //
  // FIXME(emilio): This is a massive hack, plus probably not the right place to
  // do it. Should probably be done in Document::CreatePresShell?
  if (RefPtr<PresShell> presShell = GetDocShell()->GetPresShell()) {
    Document* doc = presShell->GetDocument();
    nsHTMLDocument* htmlDoc =
        doc && doc->IsHTMLOrXHTML() ? doc->AsHTMLDocument() : nullptr;

    if (htmlDoc) {
      nsAutoString designMode;
      htmlDoc->GetDesignMode(designMode);

      if (designMode.EqualsLiteral("on")) {
        // Hold on to the editor object to let the document reattach to the
        // same editor object, instead of creating a new one.
        RefPtr<HTMLEditor> htmlEditor = GetDocShell()->GetHTMLEditor();
        Unused << htmlEditor;
        htmlDoc->SetDesignMode(u"off"_ns, Nothing(), IgnoreErrors());

        htmlDoc->SetDesignMode(u"on"_ns, Nothing(), IgnoreErrors());
      } else {
        // Re-initialize the presentation for contenteditable documents
        bool editable = false, hasEditingSession = false;
        GetDocShell()->GetEditable(&editable);
        GetDocShell()->GetHasEditingSession(&hasEditingSession);
        RefPtr<HTMLEditor> htmlEditor = GetDocShell()->GetHTMLEditor();
        if (editable && hasEditingSession && htmlEditor) {
          htmlEditor->PostCreate();
        }
      }
    }
  }

  mInShow = false;
  if (mHideCalled) {
    mHideCalled = false;
    Hide();
    return false;
  }
  return true;
}

void nsFrameLoader::MarginsChanged() {
  // We assume that the margins are always zero for remote frames.
  if (IsRemoteFrame()) {
    return;
  }

  nsDocShell* docShell = GetDocShell();
  // If there's no docshell, we're probably not up and running yet.
  // nsFrameLoader::Show() will take care of setting the right
  // margins.
  if (!docShell) {
    return;
  }

  if (!docShell->UpdateFrameMargins(GetMarginAttributes(mOwnerContent))) {
    return;
  }

  // There's a cached property declaration block
  // that needs to be updated
  if (Document* doc = docShell->GetDocument()) {
    for (nsINode* cur = doc; cur; cur = cur->GetNextNode()) {
      if (auto* body = HTMLBodyElement::FromNode(cur)) {
        body->FrameMarginsChanged();
      }
    }
  }
}

bool nsFrameLoader::ShowRemoteFrame(nsSubDocumentFrame* aFrame) {
  AUTO_PROFILER_LABEL("nsFrameLoader::ShowRemoteFrame", OTHER);
  NS_ASSERTION(IsRemoteFrame(),
               "ShowRemote only makes sense on remote frames.");

  if (!EnsureRemoteBrowser()) {
    NS_ERROR("Couldn't create child process.");
    return false;
  }

  const bool hasSize =
      aFrame && !aFrame->HasAnyStateBits(NS_FRAME_FIRST_REFLOW);

  // FIXME/bug 589337: Show()/Hide() is pretty expensive for
  // cross-process layers; need to figure out what behavior we really
  // want here.  For now, hack.
  if (!mRemoteBrowserShown) {
    if (!mOwnerContent || !mOwnerContent->GetComposedDoc()) {
      return false;
    }

    // We never want to host remote frameloaders in simple popups, like menus.
    nsIWidget* widget = nsContentUtils::WidgetForContent(mOwnerContent);
    if (!widget || static_cast<nsBaseWidget*>(widget)->IsSmallPopup()) {
      return false;
    }

    if (BrowserHost* bh = mRemoteBrowser->AsBrowserHost()) {
      RefPtr<BrowsingContext> bc = bh->GetBrowsingContext()->Top();

      // Set to the current activation of the window.
      bc->SetIsActiveBrowserWindow(bc->GetIsActiveBrowserWindow());
    }

    nsCOMPtr<nsISupports> container = mOwnerContent->OwnerDoc()->GetContainer();
    nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(container);
    nsCOMPtr<nsIWidget> mainWidget;
    baseWindow->GetMainWidget(getter_AddRefs(mainWidget));
    nsSizeMode sizeMode =
        mainWidget ? mainWidget->SizeMode() : nsSizeMode_Normal;
    const auto size =
        hasSize ? aFrame->GetSubdocumentSize() : LayoutDeviceIntSize();
    OwnerShowInfo info(size, GetScrollbarPreference(mOwnerContent), sizeMode);
    if (!mRemoteBrowser->Show(info)) {
      return false;
    }
    mRemoteBrowserShown = true;
    mRemoteBrowserSized = hasSize;

    // This notification doesn't apply to fission, apparently.
    if (!GetBrowserBridgeChild()) {
      if (nsCOMPtr<nsIObserverService> os = services::GetObserverService()) {
        os->NotifyObservers(ToSupports(this), "remote-browser-shown", nullptr);
      }
    }
  } else if (hasSize) {
    NS_ENSURE_SUCCESS(UpdatePositionAndSize(aFrame), false);
  }

  return true;
}

void nsFrameLoader::Hide() {
  if (mHideCalled) {
    return;
  }
  if (mInShow) {
    mHideCalled = true;
    return;
  }

  if (mRemoteBrowser) {
    mRemoteBrowser->UpdateEffects(EffectsInfo::FullyHidden());
  }

  if (!GetDocShell()) {
    return;
  }

  nsCOMPtr<nsIDocumentViewer> viewer;
  GetDocShell()->GetDocViewer(getter_AddRefs(viewer));
  if (viewer) viewer->SetSticky(false);

  RefPtr<nsDocShell> baseWin = GetDocShell();
  baseWin->SetVisibility(false);
  baseWin->SetParentWidget(nullptr);
}

void nsFrameLoader::ForceLayoutIfNecessary() {
  nsIFrame* frame = GetPrimaryFrameOfOwningContent();
  if (!frame) {
    return;
  }

  nsPresContext* presContext = frame->PresContext();
  if (!presContext) {
    return;
  }

  // Only force the layout flush if the frameloader hasn't ever been
  // run through layout.
  if (frame->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
    if (RefPtr<PresShell> presShell = presContext->GetPresShell()) {
      presShell->FlushPendingNotifications(FlushType::Layout);
    }
  }
}

nsresult nsFrameLoader::SwapWithOtherRemoteLoader(
    nsFrameLoader* aOther, nsFrameLoaderOwner* aThisOwner,
    nsFrameLoaderOwner* aOtherOwner) {
  MOZ_ASSERT(NS_IsMainThread());

#ifdef DEBUG
  RefPtr<nsFrameLoader> first = aThisOwner->GetFrameLoader();
  RefPtr<nsFrameLoader> second = aOtherOwner->GetFrameLoader();
  MOZ_ASSERT(first == this"aThisOwner must own this");
  MOZ_ASSERT(second == aOther, "aOtherOwner must own aOther");
#endif

  Element* ourContent = mOwnerContent;
  Element* otherContent = aOther->mOwnerContent;

  if (!ourContent || !otherContent) {
    // Can't handle this
    return NS_ERROR_NOT_IMPLEMENTED;
  }

  // Make sure there are no same-origin issues
  bool equal;
  nsresult rv = ourContent->NodePrincipal()->Equals(
      otherContent->NodePrincipal(), &equal);
  if (NS_FAILED(rv) || !equal) {
    // Security problems loom.  Just bail on it all
    return NS_ERROR_DOM_SECURITY_ERR;
  }

  Document* ourDoc = ourContent->GetComposedDoc();
  Document* otherDoc = otherContent->GetComposedDoc();
  if (!ourDoc || !otherDoc) {
    // Again, how odd, given that we had docshells
    return NS_ERROR_NOT_IMPLEMENTED;
  }

  PresShell* ourPresShell = ourDoc->GetPresShell();
  PresShell* otherPresShell = otherDoc->GetPresShell();
  if (!ourPresShell || !otherPresShell) {
    return NS_ERROR_NOT_IMPLEMENTED;
  }

  auto* browserParent = GetBrowserParent();
  auto* otherBrowserParent = aOther->GetBrowserParent();

  if (!browserParent || !otherBrowserParent) {
    return NS_ERROR_NOT_IMPLEMENTED;
  }

  RefPtr<BrowsingContext> ourBc = browserParent->GetBrowsingContext();
  RefPtr<BrowsingContext> otherBc = otherBrowserParent->GetBrowsingContext();

  // When we swap docShells, maybe we have to deal with a new page created just
  // for this operation. In this case, the browser code should already have set
  // the correct userContextId attribute value in the owning element, but our
  // docShell, that has been created way before) doesn't know that that
  // happened.
  // This is the reason why now we must retrieve the correct value from the
  // usercontextid attribute before comparing our originAttributes with the
  // other one.
  OriginAttributes ourOriginAttributes = ourBc->OriginAttributesRef();
  rv = PopulateOriginContextIdsFromAttributes(ourOriginAttributes);
  NS_ENSURE_SUCCESS(rv, rv);

  OriginAttributes otherOriginAttributes = otherBc->OriginAttributesRef();
  rv = aOther->PopulateOriginContextIdsFromAttributes(otherOriginAttributes);
  NS_ENSURE_SUCCESS(rv, rv);

  if (!ourOriginAttributes.EqualsIgnoringFPD(otherOriginAttributes)) {
    return NS_ERROR_NOT_IMPLEMENTED;
  }

  bool ourHasHistory = mIsTopLevelContent &&
                       ourContent->IsXULElement(nsGkAtoms::browser) &&
                       !ourContent->HasAttr(nsGkAtoms::disablehistory);
  bool otherHasHistory = aOther->mIsTopLevelContent &&
                         otherContent->IsXULElement(nsGkAtoms::browser) &&
                         !otherContent->HasAttr(nsGkAtoms::disablehistory);
  if (ourHasHistory != otherHasHistory) {
    return NS_ERROR_NOT_IMPLEMENTED;
  }

  if (mInSwap || aOther->mInSwap) {
    return NS_ERROR_NOT_IMPLEMENTED;
  }
  mInSwap = aOther->mInSwap = true;

  // NOTE(emilio): This doesn't have to flush because the caller does already.
  nsIFrame* ourFrame = ourContent->GetPrimaryFrame();
  nsIFrame* otherFrame = otherContent->GetPrimaryFrame();
  if (!ourFrame || !otherFrame) {
    mInSwap = aOther->mInSwap = false;
    return NS_ERROR_NOT_IMPLEMENTED;
  }

  nsSubDocumentFrame* ourFrameFrame = do_QueryFrame(ourFrame);
  if (!ourFrameFrame) {
    mInSwap = aOther->mInSwap = false;
    return NS_ERROR_NOT_IMPLEMENTED;
  }

  rv = ourFrameFrame->BeginSwapDocShells(otherFrame);
  if (NS_FAILED(rv)) {
    mInSwap = aOther->mInSwap = false;
    return rv;
  }

  nsCOMPtr<nsIBrowserDOMWindow> otherBrowserDOMWindow =
      otherBrowserParent->GetBrowserDOMWindow();
  nsCOMPtr<nsIBrowserDOMWindow> browserDOMWindow =
      browserParent->GetBrowserDOMWindow();

  if (!!otherBrowserDOMWindow != !!browserDOMWindow) {
    return NS_ERROR_NOT_IMPLEMENTED;
  }

  otherBrowserParent->SetBrowserDOMWindow(browserDOMWindow);
  browserParent->SetBrowserDOMWindow(otherBrowserDOMWindow);

  MaybeUpdatePrimaryBrowserParent(eBrowserParentRemoved);
  aOther->MaybeUpdatePrimaryBrowserParent(eBrowserParentRemoved);

  if (mozilla::BFCacheInParent() && XRE_IsParentProcess()) {
    // nsFrameLoaders in session history can't be moved to another owner since
    // there are no corresponging message managers on which swap can be done.
    // See the line mMessageManager.swap(aOther->mMessageManager); below.
    auto evict = [](nsFrameLoader* aFrameLoader) {
      if (BrowsingContext* bc =
              aFrameLoader->GetMaybePendingBrowsingContext()) {
        nsCOMPtr<nsISHistory> shistory = bc->Canonical()->GetSessionHistory();
        if (shistory) {
          shistory->EvictAllDocumentViewers();
        }
      }
    };
    evict(this);
    evict(aOther);
  }

  SetOwnerContent(otherContent);
  aOther->SetOwnerContent(ourContent);

  browserParent->SetOwnerElement(otherContent);
  otherBrowserParent->SetOwnerElement(ourContent);

  // Update window activation state for the swapped owner content.
  bool ourActive = otherBc->GetIsActiveBrowserWindow();
  bool otherActive = ourBc->GetIsActiveBrowserWindow();
  if (ourBc->IsTop()) {
    ourBc->SetIsActiveBrowserWindow(otherActive);
  }
  if (otherBc->IsTop()) {
    otherBc->SetIsActiveBrowserWindow(ourActive);
  }

  MaybeUpdatePrimaryBrowserParent(eBrowserParentChanged);
  aOther->MaybeUpdatePrimaryBrowserParent(eBrowserParentChanged);

  RefPtr<nsFrameMessageManager> ourMessageManager = mMessageManager;
  RefPtr<nsFrameMessageManager> otherMessageManager = aOther->mMessageManager;
  // Swap and setup things in parent message managers.
  if (ourMessageManager) {
    ourMessageManager->SetCallback(aOther);
  }
  if (otherMessageManager) {
    otherMessageManager->SetCallback(this);
  }
  mMessageManager.swap(aOther->mMessageManager);

  // XXXsmaug what should be done to JSWindowActorParent objects when swapping
  // frameloaders? Currently they leak very easily, bug 1697918.

  // Perform the actual swap of the internal refptrs. We keep a strong reference
  // to ourselves to make sure we don't die while we overwrite our reference to
  // ourself.
  RefPtr<nsFrameLoader> kungFuDeathGrip(this);
  aThisOwner->SetFrameLoader(aOther);
  aOtherOwner->SetFrameLoader(kungFuDeathGrip);

  ourFrameFrame->EndSwapDocShells(otherFrame);

  ourPresShell->BackingScaleFactorChanged();
  otherPresShell->BackingScaleFactorChanged();

  mInSwap = aOther->mInSwap = false;

  // Send an updated tab context since owner content type may have changed.
  MutableTabContext ourContext;
  rv = GetNewTabContext(&ourContext);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }
  MutableTabContext otherContext;
  rv = aOther->GetNewTabContext(&otherContext);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  Unused << browserParent->SendSwappedWithOtherRemoteLoader(
      ourContext.AsIPCTabContext());
  Unused << otherBrowserParent->SendSwappedWithOtherRemoteLoader(
      otherContext.AsIPCTabContext());
  // These might have moved to a new window, so make sure they have
  // the appropriate priority (bug 1896172)
  browserParent->RecomputeProcessPriority();
  otherBrowserParent->RecomputeProcessPriority();
  return NS_OK;
}

class MOZ_RAII AutoResetInFrameSwap final {
 public:
  AutoResetInFrameSwap(nsFrameLoader* aThisFrameLoader,
                       nsFrameLoader* aOtherFrameLoader,
                       nsDocShell* aThisDocShell, nsDocShell* aOtherDocShell,
                       EventTarget* aThisEventTarget,
                       EventTarget* aOtherEventTarget)
      : mThisFrameLoader(aThisFrameLoader),
        mOtherFrameLoader(aOtherFrameLoader),
        mThisDocShell(aThisDocShell),
        mOtherDocShell(aOtherDocShell),
        mThisEventTarget(aThisEventTarget),
        mOtherEventTarget(aOtherEventTarget) {
    mThisFrameLoader->mInSwap = true;
    mOtherFrameLoader->mInSwap = true;
    mThisDocShell->SetInFrameSwap(true);
    mOtherDocShell->SetInFrameSwap(true);

    // Fire pageshow events on still-loading pages, and then fire pagehide
    // events.  Note that we do NOT fire these in the normal way, but just fire
    // them on the chrome event handlers.
    nsContentUtils::FirePageShowEventForFrameLoaderSwap(
        mThisDocShell, mThisEventTarget, false);
    nsContentUtils::FirePageShowEventForFrameLoaderSwap(
        mOtherDocShell, mOtherEventTarget, false);
    nsContentUtils::FirePageHideEventForFrameLoaderSwap(mThisDocShell,
                                                        mThisEventTarget);
    nsContentUtils::FirePageHideEventForFrameLoaderSwap(mOtherDocShell,
                                                        mOtherEventTarget);
  }

  ~AutoResetInFrameSwap() {
    nsContentUtils::FirePageShowEventForFrameLoaderSwap(mThisDocShell,
                                                        mThisEventTarget, true);
    nsContentUtils::FirePageShowEventForFrameLoaderSwap(
        mOtherDocShell, mOtherEventTarget, true);

    mThisFrameLoader->mInSwap = false;
    mOtherFrameLoader->mInSwap = false;
    mThisDocShell->SetInFrameSwap(false);
    mOtherDocShell->SetInFrameSwap(false);

    // This is needed to get visibility state right in cases when we swapped a
    // visible tab (foreground in visible window) with a non-visible tab.
    if (RefPtr<Document> doc = mThisDocShell->GetDocument()) {
      doc->UpdateVisibilityState();
    }
    if (RefPtr<Document> doc = mOtherDocShell->GetDocument()) {
      doc->UpdateVisibilityState();
    }
  }

 private:
  RefPtr<nsFrameLoader> mThisFrameLoader;
  RefPtr<nsFrameLoader> mOtherFrameLoader;
  RefPtr<nsDocShell> mThisDocShell;
  RefPtr<nsDocShell> mOtherDocShell;
  nsCOMPtr<EventTarget> mThisEventTarget;
  nsCOMPtr<EventTarget> mOtherEventTarget;
};

nsresult nsFrameLoader::SwapWithOtherLoader(nsFrameLoader* aOther,
                                            nsFrameLoaderOwner* aThisOwner,
                                            nsFrameLoaderOwner* aOtherOwner) {
#ifdef DEBUG
  RefPtr<nsFrameLoader> first = aThisOwner->GetFrameLoader();
  RefPtr<nsFrameLoader> second = aOtherOwner->GetFrameLoader();
  MOZ_ASSERT(first == this"aThisOwner must own this");
  MOZ_ASSERT(second == aOther, "aOtherOwner must own aOther");
#endif

  NS_ENSURE_STATE(!mInShow && !aOther->mInShow);

  if (IsRemoteFrame() != aOther->IsRemoteFrame()) {
    NS_WARNING(
        "Swapping remote and non-remote frames is not currently supported");
    return NS_ERROR_NOT_IMPLEMENTED;
  }

  RefPtr<Element> ourContent = mOwnerContent;
  RefPtr<Element> otherContent = aOther->mOwnerContent;
  if (!ourContent || !otherContent) {
    // Can't handle this
    return NS_ERROR_NOT_IMPLEMENTED;
  }

  nsIFrame* ourFrame = ourContent->GetPrimaryFrame(FlushType::Frames);
  nsIFrame* otherFrame = otherContent->GetPrimaryFrame(FlushType::Frames);
  if (!ourFrame || !otherFrame) {
    return NS_ERROR_NOT_IMPLEMENTED;
  }

  // Ensure the flushes above haven't changed all the world.
  if (ourContent != mOwnerContent || otherContent != aOther->mOwnerContent) {
    return NS_ERROR_NOT_IMPLEMENTED;
  }

  bool ourHasSrcdoc = ourContent->IsHTMLElement(nsGkAtoms::iframe) &&
                      ourContent->HasAttr(nsGkAtoms::srcdoc);
  bool otherHasSrcdoc = otherContent->IsHTMLElement(nsGkAtoms::iframe) &&
                        otherContent->HasAttr(nsGkAtoms::srcdoc);
  if (ourHasSrcdoc || otherHasSrcdoc) {
    // Ignore this case entirely for now, since we support XUL <-> HTML swapping
    return NS_ERROR_NOT_IMPLEMENTED;
  }

  bool ourFullscreenAllowed = ourContent->IsXULElement();
  bool otherFullscreenAllowed = otherContent->IsXULElement();
  if (ourFullscreenAllowed != otherFullscreenAllowed) {
    return NS_ERROR_NOT_IMPLEMENTED;
  }

  nsILoadContext* ourLoadContext = ourContent->OwnerDoc()->GetLoadContext();
  nsILoadContext* otherLoadContext = otherContent->OwnerDoc()->GetLoadContext();
  MOZ_ASSERT(ourLoadContext && otherLoadContext,
             "Swapping frames within dead documents?");
  if (ourLoadContext->UseRemoteTabs() != otherLoadContext->UseRemoteTabs()) {
    NS_WARNING("Can't swap between e10s and non-e10s windows");
    return NS_ERROR_NOT_IMPLEMENTED;
  }
  if (ourLoadContext->UseRemoteSubframes() !=
      otherLoadContext->UseRemoteSubframes()) {
    NS_WARNING("Can't swap between fission and non-fission windows");
    return NS_ERROR_NOT_IMPLEMENTED;
  }

  // Divert to a separate path for the remaining steps in the remote case
  if (IsRemoteFrame()) {
    MOZ_ASSERT(aOther->IsRemoteFrame());
    return SwapWithOtherRemoteLoader(aOther, aThisOwner, aOtherOwner);
  }

  // Make sure there are no same-origin issues
  bool equal;
  nsresult rv = ourContent->NodePrincipal()->Equals(
      otherContent->NodePrincipal(), &equal);
  if (NS_FAILED(rv) || !equal) {
    // Security problems loom.  Just bail on it all
    return NS_ERROR_DOM_SECURITY_ERR;
  }

  RefPtr<nsDocShell> ourDocshell = GetExistingDocShell();
  RefPtr<nsDocShell> otherDocshell = aOther->GetExistingDocShell();
  if (!ourDocshell || !otherDocshell) {
    // How odd
    return NS_ERROR_NOT_IMPLEMENTED;
  }

  // To avoid having to mess with session history, avoid swapping
  // frameloaders that don't correspond to root same-type docshells,
  // unless both roots have session history disabled.
  nsCOMPtr<nsIDocShellTreeItem> ourRootTreeItem, otherRootTreeItem;
  ourDocshell->GetInProcessSameTypeRootTreeItem(
      getter_AddRefs(ourRootTreeItem));
  otherDocshell->GetInProcessSameTypeRootTreeItem(
      getter_AddRefs(otherRootTreeItem));
  nsCOMPtr<nsIWebNavigation> ourRootWebnav = do_QueryInterface(ourRootTreeItem);
  nsCOMPtr<nsIWebNavigation> otherRootWebnav =
      do_QueryInterface(otherRootTreeItem);

  if (!ourRootWebnav || !otherRootWebnav) {
    return NS_ERROR_NOT_IMPLEMENTED;
  }

  RefPtr<ChildSHistory> ourHistory = ourRootWebnav->GetSessionHistory();
  RefPtr<ChildSHistory> otherHistory = otherRootWebnav->GetSessionHistory();

  if ((ourRootTreeItem != ourDocshell || otherRootTreeItem != otherDocshell) &&
      (ourHistory || otherHistory)) {
    return NS_ERROR_NOT_IMPLEMENTED;
  }

  RefPtr<BrowsingContext> ourBc = ourDocshell->GetBrowsingContext();
  RefPtr<BrowsingContext> otherBc = otherDocshell->GetBrowsingContext();

  // Also make sure that the two BrowsingContexts are the same type. Otherwise
  // swapping is certainly not safe. If this needs to be changed then
  // the code below needs to be audited as it assumes identical types.
  if (ourBc->GetType() != otherBc->GetType()) {
    return NS_ERROR_NOT_IMPLEMENTED;
  }

  // We ensure that BCs are either both top frames or both subframes.
  if (ourBc->IsTop() != otherBc->IsTop()) {
    return NS_ERROR_NOT_IMPLEMENTED;
  }

  // One more twist here.  Setting up the right treeowners in a heterogeneous
  // tree is a bit of a pain.  So make sure that if `ourBc->GetType()` is not
  // nsIDocShellTreeItem::typeContent then all of our descendants are the same
  // type as us.
  if (!ourBc->IsContent() &&
      (!AllDescendantsOfType(ourBc, ourBc->GetType()) ||
       !AllDescendantsOfType(otherBc, otherBc->GetType()))) {
    return NS_ERROR_NOT_IMPLEMENTED;
  }

  // Save off the tree owners, frame elements, chrome event handlers, and
  // docshell and document parents before doing anything else.
  nsCOMPtr<nsIDocShellTreeOwner> ourOwner, otherOwner;
  ourDocshell->GetTreeOwner(getter_AddRefs(ourOwner));
  otherDocshell->GetTreeOwner(getter_AddRefs(otherOwner));
  // Note: it's OK to have null treeowners.

  nsCOMPtr<nsIDocShellTreeItem> ourParentItem, otherParentItem;
  ourDocshell->GetInProcessParent(getter_AddRefs(ourParentItem));
  otherDocshell->GetInProcessParent(getter_AddRefs(otherParentItem));
  if (!ourParentItem || !otherParentItem) {
    return NS_ERROR_NOT_IMPLEMENTED;
  }

  nsCOMPtr<nsPIDOMWindowOuter> ourWindow = ourDocshell->GetWindow();
  nsCOMPtr<nsPIDOMWindowOuter> otherWindow = otherDocshell->GetWindow();

  nsCOMPtr<Element> ourFrameElement = ourWindow->GetFrameElementInternal();
  nsCOMPtr<Element> otherFrameElement = otherWindow->GetFrameElementInternal();

  nsCOMPtr<EventTarget> ourChromeEventHandler =
      ourWindow->GetChromeEventHandler();
  nsCOMPtr<EventTarget> otherChromeEventHandler =
      otherWindow->GetChromeEventHandler();

  nsCOMPtr<EventTarget> ourEventTarget = ourWindow->GetParentTarget();
  nsCOMPtr<EventTarget> otherEventTarget = otherWindow->GetParentTarget();

  NS_ASSERTION(SameCOMIdentity(ourFrameElement, ourContent) &&
                   SameCOMIdentity(otherFrameElement, otherContent) &&
                   SameCOMIdentity(ourChromeEventHandler, ourContent) &&
                   SameCOMIdentity(otherChromeEventHandler, otherContent),
               "How did that happen, exactly?");

  nsCOMPtr<Document> ourChildDocument = ourWindow->GetExtantDoc();
  nsCOMPtr<Document> otherChildDocument = otherWindow->GetExtantDoc();
  if (!ourChildDocument || !otherChildDocument) {
    // This shouldn't be happening
    return NS_ERROR_NOT_IMPLEMENTED;
  }

  nsCOMPtr<Document> ourParentDocument =
      ourChildDocument->GetInProcessParentDocument();
  nsCOMPtr<Document> otherParentDocument =
      otherChildDocument->GetInProcessParentDocument();

  // Make sure to swap docshells between the two frames.
  Document* ourDoc = ourContent->GetComposedDoc();
  Document* otherDoc = otherContent->GetComposedDoc();
  if (!ourDoc || !otherDoc) {
    // Again, how odd, given that we had docshells
    return NS_ERROR_NOT_IMPLEMENTED;
  }

  NS_ASSERTION(ourDoc == ourParentDocument, "Unexpected parent document");
  NS_ASSERTION(otherDoc == otherParentDocument, "Unexpected parent document");

  PresShell* ourPresShell = ourDoc->GetPresShell();
  PresShell* otherPresShell = otherDoc->GetPresShell();
  if (!ourPresShell || !otherPresShell) {
    return NS_ERROR_NOT_IMPLEMENTED;
  }

  // When we swap docShells, maybe we have to deal with a new page created just
  // for this operation. In this case, the browser code should already have set
  // the correct userContextId attribute value in the owning element, but our
  // docShell, that has been created way before) doesn't know that that
  // happened.
  // This is the reason why now we must retrieve the correct value from the
  // usercontextid attribute before comparing our originAttributes with the
  // other one.
  OriginAttributes ourOriginAttributes = ourDocshell->GetOriginAttributes();
  rv = PopulateOriginContextIdsFromAttributes(ourOriginAttributes);
  NS_ENSURE_SUCCESS(rv, rv);

  OriginAttributes otherOriginAttributes = otherDocshell->GetOriginAttributes();
  rv = aOther->PopulateOriginContextIdsFromAttributes(otherOriginAttributes);
  NS_ENSURE_SUCCESS(rv, rv);

  if (ourOriginAttributes != otherOriginAttributes) {
    return NS_ERROR_NOT_IMPLEMENTED;
  }

  if (mInSwap || aOther->mInSwap) {
    return NS_ERROR_NOT_IMPLEMENTED;
  }
  AutoResetInFrameSwap autoFrameSwap(this, aOther, ourDocshell, otherDocshell,
                                     ourEventTarget, otherEventTarget);

  nsSubDocumentFrame* ourFrameFrame = do_QueryFrame(ourFrame);
  if (!ourFrameFrame) {
    return NS_ERROR_NOT_IMPLEMENTED;
  }

  // OK.  First begin to swap the docshells in the two nsIFrames
  rv = ourFrameFrame->BeginSwapDocShells(otherFrame);
  if (NS_FAILED(rv)) {
    return rv;
  }

  // Now move the docshells to the right docshell trees.  Note that this
  // resets their treeowners to null.
  ourParentItem->RemoveChild(ourDocshell);
  otherParentItem->RemoveChild(otherDocshell);
  if (ourBc->IsContent()) {
    ourOwner->ContentShellRemoved(ourDocshell);
    otherOwner->ContentShellRemoved(otherDocshell);
  }

  ourParentItem->AddChild(otherDocshell);
  otherParentItem->AddChild(ourDocshell);

  // Restore the correct chrome event handlers.
  ourDocshell->SetChromeEventHandler(otherChromeEventHandler);
  otherDocshell->SetChromeEventHandler(ourChromeEventHandler);
  // Restore the correct treeowners
  // (and also chrome event handlers for content frames only).
  SetTreeOwnerAndChromeEventHandlerOnDocshellTree(
      ourDocshell, otherOwner,
      ourBc->IsContent() ? otherChromeEventHandler.get() : nullptr);
  SetTreeOwnerAndChromeEventHandlerOnDocshellTree(
      otherDocshell, ourOwner,
      ourBc->IsContent() ? ourChromeEventHandler.get() : nullptr);

  // Switch the owner content before we start calling AddTreeItemToTreeOwner.
  // Note that we rely on this to deal with setting mObservingOwnerContent to
  // false and calling RemoveMutationObserver as needed.
  SetOwnerContent(otherContent);
  aOther->SetOwnerContent(ourContent);

  AddTreeItemToTreeOwner(ourDocshell, otherOwner);
  aOther->AddTreeItemToTreeOwner(otherDocshell, ourOwner);

  // SetSubDocumentFor nulls out parent documents on the old child doc if a
  // new non-null document is passed in, so just go ahead and remove both
  // kids before reinserting in the parent subdoc maps, to avoid
  // complications.
  ourParentDocument->SetSubDocumentFor(ourContent, nullptr);
  otherParentDocument->SetSubDocumentFor(otherContent, nullptr);
  ourParentDocument->SetSubDocumentFor(ourContent, otherChildDocument);
  otherParentDocument->SetSubDocumentFor(otherContent, ourChildDocument);

  ourWindow->SetFrameElementInternal(otherFrameElement);
  otherWindow->SetFrameElementInternal(ourFrameElement);

  RefPtr<nsFrameMessageManager> ourMessageManager = mMessageManager;
  RefPtr<nsFrameMessageManager> otherMessageManager = aOther->mMessageManager;
  // Swap pointers in child message managers.
  if (mChildMessageManager) {
    InProcessBrowserChildMessageManager* browserChild = mChildMessageManager;
    browserChild->SetOwner(otherContent);
    browserChild->SetChromeMessageManager(otherMessageManager);
  }
  if (aOther->mChildMessageManager) {
    InProcessBrowserChildMessageManager* otherBrowserChild =
        aOther->mChildMessageManager;
    otherBrowserChild->SetOwner(ourContent);
    otherBrowserChild->SetChromeMessageManager(ourMessageManager);
  }
  // Swap and setup things in parent message managers.
  if (mMessageManager) {
    mMessageManager->SetCallback(aOther);
  }
  if (aOther->mMessageManager) {
    aOther->mMessageManager->SetCallback(this);
  }
  mMessageManager.swap(aOther->mMessageManager);

  // Perform the actual swap of the internal refptrs. We keep a strong reference
  // to ourselves to make sure we don't die while we overwrite our reference to
  // ourself.
  RefPtr<nsFrameLoader> kungFuDeathGrip(this);
  aThisOwner->SetFrameLoader(aOther);
  aOtherOwner->SetFrameLoader(kungFuDeathGrip);

  // Drop any cached content viewers in the two session histories.
  if (ourHistory) {
--> --------------------

--> maximum size reached

--> --------------------

99%


¤ Dauer der Verarbeitung: 0.26 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.