tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsFrameLoader.cpp (135875B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 /*
      8 * Class for managing loading of a subframe (creation of the docshell,
      9 * handling of loads in it, recursion-checking).
     10 */
     11 
     12 #include "nsFrameLoader.h"
     13 
     14 #include "BrowserParent.h"
     15 #include "ContentParent.h"
     16 #include "InProcessBrowserChildMessageManager.h"
     17 #include "ReferrerInfo.h"
     18 #include "base/basictypes.h"
     19 #include "buildid_section.h"
     20 #include "jsapi.h"
     21 #include "mozilla/AsyncEventDispatcher.h"
     22 #include "mozilla/BasePrincipal.h"
     23 #include "mozilla/ContentPrincipal.h"
     24 #include "mozilla/ExpandedPrincipal.h"
     25 #include "mozilla/FlushType.h"
     26 #include "mozilla/HTMLEditor.h"
     27 #include "mozilla/NullPrincipal.h"
     28 #include "mozilla/Preferences.h"
     29 #include "mozilla/PresShell.h"
     30 #include "mozilla/PresShellInlines.h"
     31 #include "mozilla/ProcessPriorityManager.h"
     32 #include "mozilla/ProfilerLabels.h"
     33 #include "mozilla/ScopeExit.h"
     34 #include "mozilla/ScrollContainerFrame.h"
     35 #include "mozilla/StaticPrefs_fission.h"
     36 #include "mozilla/WebBrowserPersistLocalDocument.h"
     37 #include "mozilla/dom/BrowserBridgeChild.h"
     38 #include "mozilla/dom/BrowserBridgeHost.h"
     39 #include "mozilla/dom/BrowserHost.h"
     40 #include "mozilla/dom/BrowsingContext.h"
     41 #include "mozilla/dom/BrowsingContextGroup.h"
     42 #include "mozilla/dom/CanonicalBrowsingContext.h"
     43 #include "mozilla/dom/ChildSHistory.h"
     44 #include "mozilla/dom/ChromeMessageSender.h"
     45 #include "mozilla/dom/ContentChild.h"
     46 #include "mozilla/dom/ContentProcessManager.h"
     47 #include "mozilla/dom/CustomEvent.h"
     48 #include "mozilla/dom/Document.h"
     49 #include "mozilla/dom/Element.h"
     50 #include "mozilla/dom/FrameCrashedEvent.h"
     51 #include "mozilla/dom/FrameLoaderBinding.h"
     52 #include "mozilla/dom/HTMLBodyElement.h"
     53 #include "mozilla/dom/HTMLIFrameElement.h"
     54 #include "mozilla/dom/InProcessChild.h"
     55 #include "mozilla/dom/MozFrameLoaderOwnerBinding.h"
     56 #include "mozilla/dom/PBackgroundSessionStorageCache.h"
     57 #include "mozilla/dom/PBrowser.h"
     58 #include "mozilla/dom/PolicyContainer.h"
     59 #include "mozilla/dom/Promise.h"
     60 #include "mozilla/dom/PromiseNativeHandler.h"
     61 #include "mozilla/dom/SessionHistoryEntry.h"
     62 #include "mozilla/dom/SessionStorageManager.h"
     63 #include "mozilla/dom/SessionStoreChild.h"
     64 #include "mozilla/dom/SessionStoreParent.h"
     65 #include "mozilla/dom/SessionStoreUtils.h"
     66 #include "mozilla/dom/WindowGlobalParent.h"
     67 #include "mozilla/dom/XULFrameElement.h"
     68 #include "mozilla/dom/ipc/StructuredCloneData.h"
     69 #include "mozilla/gfx/CrossProcessPaint.h"
     70 #include "mozilla/ipc/BackgroundChild.h"
     71 #include "mozilla/ipc/BackgroundUtils.h"
     72 #include "mozilla/ipc/PBackgroundChild.h"
     73 #include "mozilla/layers/CompositorBridgeChild.h"
     74 #include "mozilla/toolkit/library/buildid_reader_ffi.h"
     75 #include "nsAppRunner.h"
     76 #include "nsContentUtils.h"
     77 #include "nsDirectoryService.h"
     78 #include "nsDirectoryServiceDefs.h"
     79 #include "nsDocShell.h"
     80 #include "nsDocShellLoadState.h"
     81 #include "nsError.h"
     82 #include "nsFocusManager.h"
     83 #include "nsFrameLoaderOwner.h"
     84 #include "nsGenericHTMLFrameElement.h"
     85 #include "nsGkAtoms.h"
     86 #include "nsGlobalWindowInner.h"
     87 #include "nsGlobalWindowOuter.h"
     88 #include "nsHTMLDocument.h"
     89 #include "nsIAppWindow.h"
     90 #include "nsIBaseWindow.h"
     91 #include "nsIBrowser.h"
     92 #include "nsIContentInlines.h"
     93 #include "nsIDocShell.h"
     94 #include "nsIDocShellTreeOwner.h"
     95 #include "nsIDocumentViewer.h"
     96 #include "nsIFrame.h"
     97 #include "nsIINIParser.h"
     98 #include "nsIOpenWindowInfo.h"
     99 #include "nsIPrintSettings.h"
    100 #include "nsIPrintSettingsService.h"
    101 #include "nsISHistory.h"
    102 #include "nsIScriptError.h"
    103 #include "nsIScriptGlobalObject.h"
    104 #include "nsIScriptSecurityManager.h"
    105 #include "nsIURI.h"
    106 #include "nsIWebNavigation.h"
    107 #include "nsIWebProgress.h"
    108 #include "nsIWidget.h"
    109 #include "nsIXULRuntime.h"
    110 #include "nsLayoutUtils.h"
    111 #include "nsNameSpaceManager.h"
    112 #include "nsNetUtil.h"
    113 #include "nsOpenWindowInfo.h"
    114 #include "nsPIDOMWindow.h"
    115 #include "nsPIWindowRoot.h"
    116 #include "nsQueryObject.h"
    117 #include "nsSandboxFlags.h"
    118 #include "nsSubDocumentFrame.h"
    119 #include "nsThreadUtils.h"
    120 #include "nsUnicharUtils.h"
    121 #include "nsXPCOMPrivate.h"  // for XUL_DLL
    122 #include "nsXULPopupManager.h"
    123 #include "prenv.h"
    124 
    125 #ifdef NS_PRINTING
    126 #  include "nsIWebBrowserPrint.h"
    127 #endif
    128 
    129 #if defined(MOZ_TELEMETRY_REPORTING)
    130 #  include "mozilla/glean/DomMetrics.h"
    131 #endif  // defined(MOZ_TELEMETRY_REPORTING)
    132 
    133 using namespace mozilla;
    134 using namespace mozilla::hal;
    135 using namespace mozilla::dom;
    136 using namespace mozilla::dom::ipc;
    137 using namespace mozilla::ipc;
    138 using namespace mozilla::layers;
    139 using namespace mozilla::layout;
    140 using ViewID = ScrollableLayerGuid::ViewID;
    141 
    142 using PrintPreviewResolver = std::function<void(const PrintPreviewResultInfo&)>;
    143 
    144 // Bug 8065: Limit content frame depth to some reasonable level. This
    145 // does not count chrome frames when determining depth, nor does it
    146 // prevent chrome recursion.  Number is fairly arbitrary, but meant to
    147 // keep number of shells to a reasonable number on accidental recursion with a
    148 // small (but not 1) branching factor.  With large branching factors the number
    149 // of shells can rapidly become huge and run us out of memory.  To solve that,
    150 // we'd need to re-institute a fixed version of bug 98158.
    151 #define MAX_DEPTH_CONTENT_FRAMES 10
    152 
    153 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsFrameLoader, mPendingBrowsingContext,
    154                                      mMessageManager, mChildMessageManager,
    155                                      mRemoteBrowser, mSessionStoreChild)
    156 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFrameLoader)
    157 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrameLoader)
    158 
    159 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFrameLoader)
    160  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
    161  NS_INTERFACE_MAP_ENTRY_CONCRETE(nsFrameLoader)
    162  NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
    163  NS_INTERFACE_MAP_ENTRY(nsISupports)
    164 NS_INTERFACE_MAP_END
    165 
    166 nsFrameLoader::nsFrameLoader(Element* aOwner, BrowsingContext* aBrowsingContext,
    167                             bool aIsRemoteFrame, bool aNetworkCreated)
    168    : mPendingBrowsingContext(aBrowsingContext),
    169      mOwnerContent(aOwner),
    170      mPendingSwitchID(0),
    171      mChildID(0),
    172      mRemoteType(NOT_REMOTE_TYPE),
    173      mInitialized(false),
    174      mDepthTooGreat(false),
    175      mIsTopLevelContent(false),
    176      mDestroyCalled(false),
    177      mNeedsAsyncDestroy(false),
    178      mInSwap(false),
    179      mInShow(false),
    180      mHideCalled(false),
    181      mNetworkCreated(aNetworkCreated),
    182      mLoadingOriginalSrc(false),
    183      mShouldCheckForRecursion(false),
    184      mRemoteBrowserShown(false),
    185      mRemoteBrowserSized(false),
    186      mIsRemoteFrame(aIsRemoteFrame),
    187      mWillChangeProcess(false),
    188      mObservingOwnerContent(false),
    189      mHadDetachedFrame(false),
    190      mTabProcessCrashFired(false) {
    191  nsCOMPtr<nsFrameLoaderOwner> owner = do_QueryInterface(aOwner);
    192  owner->AttachFrameLoader(this);
    193 }
    194 
    195 nsFrameLoader::~nsFrameLoader() {
    196  if (mMessageManager) {
    197    mMessageManager->Disconnect();
    198  }
    199 
    200  MOZ_ASSERT(!mOwnerContent);
    201  MOZ_RELEASE_ASSERT(mDestroyCalled);
    202 }
    203 
    204 static nsAtom* TypeAttrName(Element* aOwnerContent) {
    205  return aOwnerContent->IsXULElement() ? nsGkAtoms::type
    206                                       : nsGkAtoms::mozframetype;
    207 }
    208 
    209 static void GetFrameName(Element* aOwnerContent, nsAString& aFrameName) {
    210  int32_t namespaceID = aOwnerContent->GetNameSpaceID();
    211  if (namespaceID == kNameSpaceID_XHTML && !aOwnerContent->IsInHTMLDocument()) {
    212    aOwnerContent->GetAttr(nsGkAtoms::id, aFrameName);
    213  } else {
    214    aOwnerContent->GetAttr(nsGkAtoms::name, aFrameName);
    215    // XXX if no NAME then use ID, after a transition period this will be
    216    // changed so that XUL only uses ID too (bug 254284).
    217    if (aFrameName.IsEmpty() && namespaceID == kNameSpaceID_XUL) {
    218      aOwnerContent->GetAttr(nsGkAtoms::id, aFrameName);
    219    }
    220  }
    221 }
    222 
    223 // If this method returns true, the nsFrameLoader will act as a boundary, as is
    224 // the case for <iframe mozbrowser> and <browser type="content"> elements.
    225 //
    226 // # Historical Notes  (10 April 2019)
    227 //
    228 // In the past, this boundary was defined by the "typeContent" and "typeChrome"
    229 // nsIDocShellTreeItem types. There was only ever a single split in the tree,
    230 // and it occurred at the boundary between these two types of docshells. When
    231 // <iframe mozbrowser> was introduced, it was given special casing to make it
    232 // act like a second boundary, without having to change the existing code.
    233 //
    234 // The about:addons page, which is loaded within a content browser, then added a
    235 // remote <browser type="content" remote="true"> element. When remote, this
    236 // would also act as a mechanism for creating a disjoint tree, due to the
    237 // process keeping the embedder and embedee separate.
    238 //
    239 // However, when initial out-of-process iframe support was implemented, this
    240 // codepath became a risk, as it could've caused the oop iframe remote
    241 // WindowProxy code to be activated for the addons page. This was fixed by
    242 // extendng the isolation logic previously reserved to <iframe mozbrowser> to
    243 // also cover <browser> elements with the explicit `remote` property loaded in
    244 // content.
    245 //
    246 // To keep these boundaries clear, and allow them to work in a cross-process
    247 // manner, they are no longer handled by typeContent and typeChrome. Instead,
    248 // the actual BrowsingContext tree is broken at these edges.
    249 static bool IsTopContent(BrowsingContext* aParent, Element* aOwner) {
    250  if (XRE_IsContentProcess()) {
    251    return false;
    252  }
    253 
    254  if (aParent->IsContent()) {
    255    // If we're already in content, we may still want to create a new
    256    // BrowsingContext tree if our element is a xul browser element with a
    257    // `remote="true"` marker.
    258    return aOwner->IsXULElement() &&
    259           aOwner->AttrValueIs(kNameSpaceID_None, nsGkAtoms::remote,
    260                               nsGkAtoms::_true, eCaseMatters);
    261  }
    262 
    263  // If we're in a chrome context, we want to start a new tree if we are an
    264  // element with a `type="content"` marker.
    265  return aOwner->AttrValueIs(kNameSpaceID_None, TypeAttrName(aOwner),
    266                             nsGkAtoms::content, eIgnoreCase);
    267 }
    268 
    269 static already_AddRefed<BrowsingContext> CreateBrowsingContext(
    270    Element* aOwner, nsIOpenWindowInfo* aOpenWindowInfo,
    271    BrowsingContextGroup* aSpecificGroup, bool aNetworkCreated = false) {
    272  MOZ_ASSERT(!aOpenWindowInfo || !aSpecificGroup,
    273             "Only one of SpecificGroup and OpenWindowInfo may be provided!");
    274 
    275  // If we've got a pending BrowserParent from the content process, use the
    276  // BrowsingContext which was created for it.
    277  if (aOpenWindowInfo && aOpenWindowInfo->GetNextRemoteBrowser()) {
    278    MOZ_ASSERT(XRE_IsParentProcess());
    279    return do_AddRef(
    280        aOpenWindowInfo->GetNextRemoteBrowser()->GetBrowsingContext());
    281  }
    282 
    283  RefPtr<BrowsingContext> opener;
    284  if (aOpenWindowInfo && !aOpenWindowInfo->GetForceNoOpener()) {
    285    opener = aOpenWindowInfo->GetParent();
    286    if (opener) {
    287      // Must create BrowsingContext with opener in-process.
    288      MOZ_ASSERT(opener->IsInProcess());
    289 
    290      // This can only happen when the opener was closed from a nested event
    291      // loop in the window provider code, and only when the open was triggered
    292      // by a non-e10s tab, and the new tab is being opened in a new browser
    293      // window. Since it is a corner case among corner cases, and the opener
    294      // window will appear to be null to consumers after it is discarded
    295      // anyway, just drop the opener entirely.
    296      if (opener->IsDiscarded()) {
    297        NS_WARNING(
    298            "Opener was closed from a nested event loop in the parent process. "
    299            "Please fix this.");
    300        opener = nullptr;
    301      }
    302    }
    303  }
    304 
    305  RefPtr<nsGlobalWindowInner> parentInner =
    306      nsGlobalWindowInner::Cast(aOwner->OwnerDoc()->GetInnerWindow());
    307  if (NS_WARN_IF(!parentInner) || parentInner->IsDying()) {
    308    return nullptr;
    309  }
    310 
    311  BrowsingContext* parentBC = parentInner->GetBrowsingContext();
    312  if (NS_WARN_IF(!parentBC) || parentBC->IsDiscarded()) {
    313    return nullptr;
    314  }
    315 
    316  // Determine the frame name for the new browsing context.
    317  nsAutoString frameName;
    318  GetFrameName(aOwner, frameName);
    319 
    320  // Create our BrowsingContext without immediately attaching it. It's possible
    321  // that no DocShell or remote browser will ever be created for this
    322  // FrameLoader, particularly if the document that we were created for is not
    323  // currently active. And in that latter case, if we try to attach our BC now,
    324  // it will wind up attached as a child of the currently active inner window
    325  // for the BrowsingContext, and cause no end of trouble.
    326  if (IsTopContent(parentBC, aOwner)) {
    327    BrowsingContext::CreateDetachedOptions options;
    328    if (aOpenWindowInfo) {
    329      options.topLevelCreatedByWebContent =
    330          aOpenWindowInfo->GetIsTopLevelCreatedByWebContent();
    331    }
    332    options.windowless = parentBC->Windowless();
    333 
    334    // Create toplevel context without a parent & as Type::Content.
    335    return BrowsingContext::CreateDetached(
    336        nullptr, opener, aSpecificGroup, frameName,
    337        BrowsingContext::Type::Content, options);
    338  }
    339 
    340  MOZ_ASSERT(!aOpenWindowInfo,
    341             "Can't have openWindowInfo for non-toplevel context");
    342 
    343  MOZ_ASSERT(!aSpecificGroup,
    344             "Can't force BrowsingContextGroup for non-toplevel context");
    345  return BrowsingContext::CreateDetached(
    346      parentInner, nullptr, nullptr, frameName, parentBC->GetType(),
    347      {.createdDynamically = !aNetworkCreated,
    348       .windowless = parentBC->Windowless()});
    349 }
    350 
    351 static bool InitialLoadIsRemote(Element* aOwner) {
    352  // The initial load in an content process iframe should never be made remote.
    353  // Content process iframes always become remote due to navigation.
    354  if (XRE_IsContentProcess()) {
    355    return false;
    356  }
    357 
    358  // Otherwise, we're remote if we have "remote=true" and we're a XUL element.
    359  return (aOwner->GetNameSpaceID() == kNameSpaceID_XUL) &&
    360         aOwner->AttrValueIs(kNameSpaceID_None, nsGkAtoms::remote,
    361                             nsGkAtoms::_true, eCaseMatters);
    362 }
    363 
    364 static already_AddRefed<BrowsingContextGroup> InitialBrowsingContextGroup(
    365    Element* aOwner) {
    366  nsAutoString attrString;
    367  if (aOwner->GetNameSpaceID() != kNameSpaceID_XUL ||
    368      !aOwner->GetAttr(nsGkAtoms::initialBrowsingContextGroupId, attrString)) {
    369    return nullptr;
    370  }
    371 
    372  // It's OK to read the attribute using a signed 64-bit integer parse, as an ID
    373  // generated using `nsContentUtils::GenerateProcessSpecificId` (like BCG IDs)
    374  // will only ever use 53 bits of precision, so it can be round-tripped through
    375  // a JS number.
    376  nsresult rv = NS_OK;
    377  int64_t signedGroupId = attrString.ToInteger64(&rv, 10);
    378  if (NS_FAILED(rv) || signedGroupId <= 0) {
    379    MOZ_DIAGNOSTIC_ASSERT(
    380        false, "we intended to have a particular id, but failed to parse it!");
    381    return nullptr;
    382  }
    383 
    384  return BrowsingContextGroup::GetOrCreate(uint64_t(signedGroupId));
    385 }
    386 
    387 already_AddRefed<nsFrameLoader> nsFrameLoader::Create(
    388    Element* aOwner, bool aNetworkCreated, nsIOpenWindowInfo* aOpenWindowInfo) {
    389  NS_ENSURE_TRUE(aOwner, nullptr);
    390  Document* doc = aOwner->OwnerDoc();
    391 
    392  // We never create nsFrameLoaders for elements in resource documents.
    393  //
    394  // We never create nsFrameLoaders for elements in data documents, unless the
    395  // document is a static document.
    396  // Static documents are an exception because any sub-documents need an
    397  // nsFrameLoader to keep the relevant docShell alive, even though the
    398  // nsFrameLoader isn't used to load anything (the sub-document is created by
    399  // the static clone process).
    400  //
    401  // We never create nsFrameLoaders for elements that are not
    402  // in-composed-document, unless the element belongs to a static document.
    403  // Static documents are an exception because this method is called at a point
    404  // in the static clone process before aOwner has been inserted into its
    405  // document.  For other types of documents this wouldn't be a problem since
    406  // we'd create the nsFrameLoader as necessary after aOwner is inserted into a
    407  // document, but the mechanisms that take care of that don't apply for static
    408  // documents so we need to create the nsFrameLoader now. (This isn't wasteful
    409  // since for a static document we know aOwner will end up in a document and
    410  // the nsFrameLoader will be used for its docShell.)
    411  //
    412  NS_ENSURE_TRUE(!doc->IsResourceDoc() &&
    413                     ((!doc->IsLoadedAsData() && aOwner->IsInComposedDoc()) ||
    414                      doc->IsStaticDocument()),
    415                 nullptr);
    416 
    417  RefPtr<BrowsingContextGroup> group = InitialBrowsingContextGroup(aOwner);
    418  RefPtr<BrowsingContext> context =
    419      CreateBrowsingContext(aOwner, aOpenWindowInfo, group, aNetworkCreated);
    420  NS_ENSURE_TRUE(context, nullptr);
    421 
    422  if (XRE_IsParentProcess() && aOpenWindowInfo) {
    423    MOZ_ASSERT(context->IsTopContent());
    424    if (RefPtr<BrowsingContext> crossGroupOpener =
    425            aOpenWindowInfo->GetParent()) {
    426      context->Canonical()->SetCrossGroupOpenerId(crossGroupOpener->Id());
    427    }
    428  }
    429 
    430  bool isRemoteFrame = InitialLoadIsRemote(aOwner);
    431  RefPtr<nsFrameLoader> fl =
    432      new nsFrameLoader(aOwner, context, isRemoteFrame, aNetworkCreated);
    433  fl->mOpenWindowInfo = aOpenWindowInfo;
    434 
    435  // If this is a toplevel initial remote frame, we're looking at a browser
    436  // loaded in the parent process. Pull the remote type attribute off of the
    437  // <browser> element to determine which remote type it should be loaded in, or
    438  // use `DEFAULT_REMOTE_TYPE` if we can't tell.
    439  if (isRemoteFrame) {
    440    MOZ_ASSERT(XRE_IsParentProcess());
    441    nsAutoString remoteType;
    442    if (aOwner->GetAttr(nsGkAtoms::RemoteType, remoteType) &&
    443        !remoteType.IsEmpty()) {
    444      CopyUTF16toUTF8(remoteType, fl->mRemoteType);
    445    } else {
    446      fl->mRemoteType = DEFAULT_REMOTE_TYPE;
    447    }
    448  }
    449  return fl.forget();
    450 }
    451 
    452 /* static */
    453 already_AddRefed<nsFrameLoader> nsFrameLoader::Recreate(
    454    mozilla::dom::Element* aOwner, BrowsingContext* aContext,
    455    BrowsingContextGroup* aSpecificGroup,
    456    const NavigationIsolationOptions& aRemotenessOptions, bool aIsRemote,
    457    bool aNetworkCreated, bool aPreserveContext) {
    458  NS_ENSURE_TRUE(aOwner, nullptr);
    459 
    460 #ifdef DEBUG
    461  // This version of Create is only called for Remoteness updates, so we can
    462  // assume we need a FrameLoader here and skip the check in the other Create.
    463  Document* doc = aOwner->OwnerDoc();
    464  MOZ_ASSERT(!doc->IsResourceDoc());
    465  MOZ_ASSERT((!doc->IsLoadedAsData() && aOwner->IsInComposedDoc()) ||
    466             doc->IsStaticDocument());
    467 #endif
    468 
    469  RefPtr<BrowsingContext> context = aContext;
    470  if (!context || !aPreserveContext) {
    471    context = CreateBrowsingContext(aOwner, /* openWindowInfo */ nullptr,
    472                                    aSpecificGroup);
    473    if (aContext) {
    474      MOZ_ASSERT(
    475          XRE_IsParentProcess(),
    476          "Recreating browing contexts only supported in the parent process");
    477      aContext->Canonical()->SynchronizeLayoutHistoryState();
    478      aContext->Canonical()->ReplacedBy(context->Canonical(),
    479                                        aRemotenessOptions);
    480    }
    481  }
    482  NS_ENSURE_TRUE(context, nullptr);
    483 
    484  RefPtr<nsFrameLoader> fl =
    485      new nsFrameLoader(aOwner, context, aIsRemote, aNetworkCreated);
    486  return fl.forget();
    487 }
    488 
    489 void nsFrameLoader::LoadFrame(bool aOriginalSrc,
    490                              bool aShouldCheckForRecursion) {
    491  if (NS_WARN_IF(!mOwnerContent)) {
    492    return;
    493  }
    494 
    495  nsAutoString src;
    496  nsCOMPtr<nsIPrincipal> principal;
    497  nsCOMPtr<nsIPolicyContainer> policyContainer;
    498 
    499  bool isSrcdoc = mOwnerContent->IsHTMLElement(nsGkAtoms::iframe) &&
    500                  mOwnerContent->HasAttr(nsGkAtoms::srcdoc);
    501  if (isSrcdoc) {
    502    src.AssignLiteral("about:srcdoc");
    503    principal = mOwnerContent->NodePrincipal();
    504    policyContainer = mOwnerContent->GetPolicyContainer();
    505  } else {
    506    GetURL(src, getter_AddRefs(principal), getter_AddRefs(policyContainer));
    507 
    508    src.Trim(" \t\n\r");
    509 
    510    if (src.IsEmpty()) {
    511      // If the frame is a XUL element and has the attribute 'nodefaultsrc=true'
    512      // then we will not use 'about:blank' as fallback but return early without
    513      // starting a load if no 'src' attribute is given (or it's empty).
    514      if (mOwnerContent->IsXULElement() &&
    515          mOwnerContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::nodefaultsrc,
    516                                     nsGkAtoms::_true, eCaseMatters)) {
    517        return;
    518      }
    519      src.AssignLiteral("about:blank");
    520      principal = mOwnerContent->NodePrincipal();
    521      policyContainer = mOwnerContent->GetPolicyContainer();
    522    }
    523  }
    524 
    525  Document* doc = mOwnerContent->OwnerDoc();
    526  if (doc->IsStaticDocument()) {
    527    return;
    528  }
    529 
    530  // If we are being loaded by a lazy loaded iframe, use its base URI first
    531  // instead of the current base URI.
    532  auto* lazyBaseURI = GetLazyLoadFrameResumptionState().mBaseURI.get();
    533  nsIURI* baseURI = lazyBaseURI ? lazyBaseURI : mOwnerContent->GetBaseURI();
    534 
    535  auto encoding = doc->GetDocumentCharacterSet();
    536 
    537  nsCOMPtr<nsIURI> uri;
    538  nsresult rv = NS_NewURI(getter_AddRefs(uri), src, encoding, baseURI);
    539 
    540  // If the URI was malformed, try to recover by loading about:blank.
    541  if (rv == NS_ERROR_MALFORMED_URI) {
    542    rv = NS_NewURI(getter_AddRefs(uri), u"about:blank"_ns, encoding, baseURI);
    543  }
    544 
    545  if (NS_SUCCEEDED(rv)) {
    546    rv = LoadURI(uri, principal, policyContainer, aOriginalSrc,
    547                 aShouldCheckForRecursion);
    548  }
    549 
    550  if (NS_FAILED(rv)) {
    551    FireErrorEvent();
    552  }
    553 }
    554 
    555 void nsFrameLoader::ConfigRemoteProcess(const nsACString& aRemoteType,
    556                                        ContentParent* aContentParent) {
    557  MOZ_DIAGNOSTIC_ASSERT(IsRemoteFrame(), "Must be a remote frame");
    558  MOZ_DIAGNOSTIC_ASSERT(!mRemoteBrowser, "Must not have a browser yet");
    559  MOZ_DIAGNOSTIC_ASSERT_IF(aContentParent,
    560                           aContentParent->GetRemoteType() == aRemoteType);
    561 
    562  mRemoteType = aRemoteType;
    563  mChildID = aContentParent ? aContentParent->ChildID() : 0;
    564 }
    565 
    566 void nsFrameLoader::FireErrorEvent() {
    567  if (!mOwnerContent) {
    568    return;
    569  }
    570  RefPtr<AsyncEventDispatcher> loadBlockingAsyncDispatcher =
    571      new LoadBlockingAsyncEventDispatcher(
    572          mOwnerContent, u"error"_ns, CanBubble::eNo, ChromeOnlyDispatch::eNo);
    573  loadBlockingAsyncDispatcher->PostDOMEvent();
    574 }
    575 
    576 nsresult nsFrameLoader::LoadURI(nsIURI* aURI,
    577                                nsIPrincipal* aTriggeringPrincipal,
    578                                nsIPolicyContainer* aPolicyContainer,
    579                                bool aOriginalSrc,
    580                                bool aShouldCheckForRecursion) {
    581  if (!aURI) return NS_ERROR_INVALID_POINTER;
    582  NS_ENSURE_STATE(!mDestroyCalled && mOwnerContent);
    583  MOZ_ASSERT(
    584      aTriggeringPrincipal,
    585      "Must have an explicit triggeringPrincipal to nsFrameLoader::LoadURI.");
    586 
    587  mLoadingOriginalSrc = aOriginalSrc;
    588  mShouldCheckForRecursion = aShouldCheckForRecursion;
    589 
    590  nsCOMPtr<Document> doc = mOwnerContent->OwnerDoc();
    591 
    592  nsresult rv;
    593  rv = CheckURILoad(aURI, aTriggeringPrincipal);
    594  NS_ENSURE_SUCCESS(rv, rv);
    595 
    596  mURIToLoad = aURI;
    597  mTriggeringPrincipal = aTriggeringPrincipal;
    598  mPolicyContainer = aPolicyContainer;
    599  rv = doc->InitializeFrameLoader(this);
    600  if (NS_FAILED(rv)) {
    601    mURIToLoad = nullptr;
    602    mTriggeringPrincipal = nullptr;
    603    mPolicyContainer = nullptr;
    604  }
    605  return rv;
    606 }
    607 
    608 void nsFrameLoader::ResumeLoad(uint64_t aPendingSwitchID) {
    609  Document* doc = mOwnerContent->OwnerDoc();
    610  if (doc->IsStaticDocument()) {
    611    // Static doc shouldn't load sub-documents.
    612    return;
    613  }
    614 
    615  if (NS_WARN_IF(mDestroyCalled || !mOwnerContent)) {
    616    FireErrorEvent();
    617    return;
    618  }
    619 
    620  mLoadingOriginalSrc = false;
    621  mShouldCheckForRecursion = false;
    622  mURIToLoad = nullptr;
    623  mPendingSwitchID = aPendingSwitchID;
    624  mTriggeringPrincipal = mOwnerContent->NodePrincipal();
    625  mPolicyContainer = mOwnerContent->GetPolicyContainer();
    626 
    627  nsresult rv = doc->InitializeFrameLoader(this);
    628  if (NS_FAILED(rv)) {
    629    mPendingSwitchID = 0;
    630    mTriggeringPrincipal = nullptr;
    631    mPolicyContainer = nullptr;
    632 
    633    FireErrorEvent();
    634  }
    635 }
    636 
    637 nsresult nsFrameLoader::ReallyStartLoading() {
    638  nsresult rv = ReallyStartLoadingInternal();
    639  if (NS_FAILED(rv)) {
    640    FireErrorEvent();
    641  }
    642 
    643  return rv;
    644 }
    645 
    646 nsresult nsFrameLoader::ReallyStartLoadingInternal() {
    647  NS_ENSURE_STATE((mURIToLoad || mPendingSwitchID) && mOwnerContent &&
    648                  mOwnerContent->IsInComposedDoc());
    649 
    650  AUTO_PROFILER_LABEL("nsFrameLoader::ReallyStartLoadingInternal", OTHER);
    651  RefPtr<nsDocShellLoadState> loadState;
    652  if (!mPendingSwitchID) {
    653    loadState = new nsDocShellLoadState(mURIToLoad);
    654    loadState->SetOriginalFrameSrc(mLoadingOriginalSrc);
    655    loadState->SetShouldCheckForRecursion(mShouldCheckForRecursion);
    656 
    657    // The triggering principal could be null if the frame is loaded other
    658    // than the src attribute, for example, the frame is sandboxed. In that
    659    // case we use the principal of the owner content, which is needed to
    660    // prevent XSS attaches on documents loaded in subframes.
    661    if (mTriggeringPrincipal) {
    662      loadState->SetTriggeringPrincipal(mTriggeringPrincipal);
    663    } else {
    664      loadState->SetTriggeringPrincipal(mOwnerContent->NodePrincipal());
    665    }
    666 
    667    // If we have an explicit policyContainer, we set it. If not, we only query
    668    // it from the document in case there was no explicit triggeringPrincipal.
    669    // Otherwise it's possible that the original triggeringPrincipal did not
    670    // have a policyContainer which causes the policyContainer on the Principal
    671    // and explicit policyContainer to be out of sync.
    672    if (mPolicyContainer) {
    673      loadState->SetPolicyContainer(mPolicyContainer);
    674    } else if (!mTriggeringPrincipal) {
    675      nsCOMPtr<nsIPolicyContainer> policyContainer =
    676          mOwnerContent->GetPolicyContainer();
    677      loadState->SetPolicyContainer(policyContainer);
    678    }
    679 
    680    nsAutoString srcdoc;
    681    bool isSrcdoc = mOwnerContent->IsHTMLElement(nsGkAtoms::iframe) &&
    682                    mOwnerContent->GetAttr(nsGkAtoms::srcdoc, srcdoc);
    683 
    684    if (isSrcdoc) {
    685      loadState->SetSrcdocData(srcdoc);
    686      loadState->SetBaseURI(mOwnerContent->GetBaseURI());
    687    }
    688 
    689    auto referrerInfo = MakeRefPtr<ReferrerInfo>(
    690        *mOwnerContent, GetLazyLoadFrameResumptionState().mReferrerPolicy);
    691    loadState->SetReferrerInfo(referrerInfo);
    692 
    693    loadState->SetIsFromProcessingFrameAttributes();
    694 
    695    // Default flags:
    696    int32_t flags = nsIWebNavigation::LOAD_FLAGS_NONE;
    697    loadState->SetLoadFlags(flags);
    698 
    699    loadState->SetFirstParty(false);
    700 
    701    Document* ownerDoc = mOwnerContent->OwnerDoc();
    702    if (ownerDoc) {
    703      loadState->SetTriggeringStorageAccess(ownerDoc->UsingStorageAccess());
    704      loadState->SetTriggeringWindowId(ownerDoc->InnerWindowID());
    705      loadState->SetTriggeringClassificationFlags(
    706          ownerDoc->GetScriptTrackingFlags());
    707    }
    708 
    709    // If we're loading the default about:blank document in a <browser> element,
    710    // prevent the load from causing a process switch by explicitly overriding
    711    // remote type selection.
    712    if (mPendingBrowsingContext->IsTopContent() &&
    713        mOwnerContent->IsXULElement(nsGkAtoms::browser) &&
    714        NS_IsAboutBlank(mURIToLoad) &&
    715        loadState->TriggeringPrincipal()->IsSystemPrincipal()) {
    716      loadState->SetRemoteTypeOverride(mRemoteType);
    717    }
    718  }
    719 
    720  if (IsRemoteFrame()) {
    721    if (!EnsureRemoteBrowser()) {
    722      NS_WARNING("Couldn't create child process for iframe.");
    723      return NS_ERROR_FAILURE;
    724    }
    725 
    726    if (mPendingSwitchID) {
    727      mRemoteBrowser->ResumeLoad(mPendingSwitchID);
    728      mPendingSwitchID = 0;
    729    } else {
    730      mRemoteBrowser->LoadURL(loadState);
    731    }
    732 
    733    if (!mRemoteBrowserShown) {
    734      // This can fail if it's too early to show the frame, we will retry later.
    735      (void)ShowRemoteFrame(
    736          /* aFrame = */ do_QueryFrame(GetPrimaryFrameOfOwningContent()));
    737    }
    738 
    739    return NS_OK;
    740  }
    741 
    742  nsresult rv = MaybeCreateDocShell();
    743  if (NS_FAILED(rv)) {
    744    return rv;
    745  }
    746  MOZ_ASSERT(GetDocShell(),
    747             "MaybeCreateDocShell succeeded with a null docShell");
    748 
    749  // If we have a pending switch, just resume our load.
    750  if (mPendingSwitchID) {
    751    bool tmpState = mNeedsAsyncDestroy;
    752    mNeedsAsyncDestroy = true;
    753    rv = GetDocShell()->ResumeRedirectedLoad(mPendingSwitchID, -1);
    754    mNeedsAsyncDestroy = tmpState;
    755    mPendingSwitchID = 0;
    756    return rv;
    757  }
    758 
    759  // Just to be safe, recheck uri.
    760  rv = CheckURILoad(mURIToLoad, mTriggeringPrincipal);
    761  NS_ENSURE_SUCCESS(rv, rv);
    762 
    763  mLoadingOriginalSrc = false;
    764  mShouldCheckForRecursion = false;
    765 
    766  // Kick off the load...
    767  bool tmpState = mNeedsAsyncDestroy;
    768  // Sync destroy should be possible from the sync about:blank load event
    769  mNeedsAsyncDestroy = !NS_IsAboutBlankAllowQueryAndFragment(mURIToLoad);
    770 
    771  RefPtr<nsDocShell> docShell = GetDocShell();
    772  rv = docShell->LoadURI(loadState, false);
    773  mNeedsAsyncDestroy = tmpState;
    774  mURIToLoad = nullptr;
    775  NS_ENSURE_SUCCESS(rv, rv);
    776 
    777  return NS_OK;
    778 }
    779 
    780 nsresult nsFrameLoader::CheckURILoad(nsIURI* aURI,
    781                                     nsIPrincipal* aTriggeringPrincipal) {
    782  // Check for security.  The fun part is trying to figure out what principals
    783  // to use.  The way I figure it, if we're doing a LoadFrame() accidentally
    784  // (eg someone created a frame/iframe node, we're being parsed, XUL iframes
    785  // are being reframed, etc.) then we definitely want to use the node
    786  // principal of mOwnerContent for security checks.  If, on the other hand,
    787  // someone's setting the src on our owner content, or created it via script,
    788  // or whatever, then they can clearly access it... and we should still use
    789  // the principal of mOwnerContent.  I don't think that leads to privilege
    790  // escalation, and it's reasonably guaranteed to not lead to XSS issues
    791  // (since caller can already access mOwnerContent in this case).  So just use
    792  // the principal of mOwnerContent no matter what.  If script wants to run
    793  // things with its own permissions, which differ from those of mOwnerContent
    794  // (which means the script is privileged in some way) it should set
    795  // window.location instead.
    796  nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
    797 
    798  // Get our principal
    799  nsIPrincipal* principal =
    800      (aTriggeringPrincipal ? aTriggeringPrincipal
    801                            : mOwnerContent->NodePrincipal());
    802 
    803  // Check if we are allowed to load absURL
    804  nsresult rv = secMan->CheckLoadURIWithPrincipal(
    805      principal, aURI, nsIScriptSecurityManager::STANDARD,
    806      mOwnerContent->OwnerDoc()->InnerWindowID());
    807  if (NS_FAILED(rv)) {
    808    return rv;  // We're not
    809  }
    810 
    811  // Bail out if this is an infinite recursion scenario
    812  if (IsRemoteFrame()) {
    813    return NS_OK;
    814  }
    815  return CheckForRecursiveLoad(aURI);
    816 }
    817 
    818 nsDocShell* nsFrameLoader::GetDocShell(ErrorResult& aRv) {
    819  if (IsRemoteFrame()) {
    820    return nullptr;
    821  }
    822 
    823  // If we have an owner, make sure we have a docshell and return
    824  // that. If not, we're most likely in the middle of being torn down,
    825  // then we just return null.
    826  if (mOwnerContent) {
    827    nsresult rv = MaybeCreateDocShell();
    828    if (NS_FAILED(rv)) {
    829      aRv.Throw(rv);
    830      return nullptr;
    831    }
    832    MOZ_ASSERT(GetDocShell(),
    833               "MaybeCreateDocShell succeeded, but null docShell");
    834  }
    835 
    836  return GetDocShell();
    837 }
    838 
    839 static void SetTreeOwnerAndChromeEventHandlerOnDocshellTree(
    840    nsIDocShellTreeItem* aItem, nsIDocShellTreeOwner* aOwner,
    841    EventTarget* aHandler) {
    842  MOZ_ASSERT(aItem, "Must have item");
    843 
    844  aItem->SetTreeOwner(aOwner);
    845 
    846  int32_t childCount = 0;
    847  aItem->GetInProcessChildCount(&childCount);
    848  for (int32_t i = 0; i < childCount; ++i) {
    849    nsCOMPtr<nsIDocShellTreeItem> item;
    850    aItem->GetInProcessChildAt(i, getter_AddRefs(item));
    851    if (aHandler) {
    852      nsCOMPtr<nsIDocShell> shell(do_QueryInterface(item));
    853      shell->SetChromeEventHandler(aHandler);
    854    }
    855    SetTreeOwnerAndChromeEventHandlerOnDocshellTree(item, aOwner, aHandler);
    856  }
    857 }
    858 
    859 #if defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
    860 static bool CheckDocShellType(mozilla::dom::Element* aOwnerContent,
    861                              nsIDocShellTreeItem* aDocShell, nsAtom* aAtom) {
    862  bool isContent = aOwnerContent->AttrValueIs(kNameSpaceID_None, aAtom,
    863                                              nsGkAtoms::content, eIgnoreCase);
    864 
    865  if (isContent) {
    866    return aDocShell->ItemType() == nsIDocShellTreeItem::typeContent;
    867  }
    868 
    869  nsCOMPtr<nsIDocShellTreeItem> parent;
    870  aDocShell->GetInProcessParent(getter_AddRefs(parent));
    871 
    872  return parent && parent->ItemType() == aDocShell->ItemType();
    873 }
    874 #endif  // defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
    875 
    876 /**
    877 * Hook up a given TreeItem to its tree owner. aItem's type must have already
    878 * been set, and it should already be part of the DocShellTree.
    879 * @param aItem the treeitem we're working with
    880 * @param aTreeOwner the relevant treeowner; might be null
    881 */
    882 void nsFrameLoader::AddTreeItemToTreeOwner(nsIDocShellTreeItem* aItem,
    883                                           nsIDocShellTreeOwner* aOwner) {
    884  MOZ_ASSERT(aItem, "Must have docshell treeitem");
    885  MOZ_ASSERT(mOwnerContent, "Must have owning content");
    886 
    887  MOZ_DIAGNOSTIC_ASSERT(
    888      CheckDocShellType(mOwnerContent, aItem, TypeAttrName(mOwnerContent)),
    889      "Correct ItemType should be set when creating BrowsingContext");
    890 
    891  if (mIsTopLevelContent) {
    892    bool is_primary = mOwnerContent->AttrValueIs(
    893        kNameSpaceID_None, nsGkAtoms::primary, nsGkAtoms::_true, eIgnoreCase);
    894    if (aOwner) {
    895      mOwnerContent->AddMutationObserver(this);
    896      mObservingOwnerContent = true;
    897      aOwner->ContentShellAdded(aItem, is_primary);
    898    }
    899  }
    900 }
    901 
    902 static bool AllDescendantsOfType(BrowsingContext* aParent,
    903                                 BrowsingContext::Type aType) {
    904  for (auto& child : aParent->Children()) {
    905    if (child->GetType() != aType || !AllDescendantsOfType(child, aType)) {
    906      return false;
    907    }
    908  }
    909 
    910  return true;
    911 }
    912 
    913 void nsFrameLoader::MaybeShowFrame() {
    914  nsIFrame* frame = GetPrimaryFrameOfOwningContent();
    915  if (frame) {
    916    nsSubDocumentFrame* subDocFrame = do_QueryFrame(frame);
    917    if (subDocFrame) {
    918      subDocFrame->MaybeShowViewer();
    919    }
    920  }
    921 }
    922 
    923 static ScrollbarPreference GetScrollbarPreference(const Element* aOwner) {
    924  if (!aOwner) {
    925    return ScrollbarPreference::Auto;
    926  }
    927  const nsAttrValue* attrValue = aOwner->GetParsedAttr(nsGkAtoms::scrolling);
    928  return nsGenericHTMLFrameElement::MapScrollingAttribute(attrValue);
    929 }
    930 
    931 static CSSIntSize GetMarginAttributes(const Element* aOwner) {
    932  CSSIntSize result(-1, -1);
    933  auto* content = nsGenericHTMLElement::FromNodeOrNull(aOwner);
    934  if (!content) {
    935    return result;
    936  }
    937  const nsAttrValue* attr = content->GetParsedAttr(nsGkAtoms::marginwidth);
    938  if (attr && attr->Type() == nsAttrValue::eInteger) {
    939    result.width = attr->GetIntegerValue();
    940  }
    941  attr = content->GetParsedAttr(nsGkAtoms::marginheight);
    942  if (attr && attr->Type() == nsAttrValue::eInteger) {
    943    result.height = attr->GetIntegerValue();
    944  }
    945  return result;
    946 }
    947 
    948 bool nsFrameLoader::Show(nsSubDocumentFrame* aFrame) {
    949  if (mInShow) {
    950    return false;
    951  }
    952  mInShow = true;
    953 
    954  auto resetInShow = mozilla::MakeScopeExit([&] { mInShow = false; });
    955  if (IsRemoteFrame()) {
    956    return ShowRemoteFrame(aFrame);
    957  }
    958  const LayoutDeviceIntSize size = aFrame->GetInitialSubdocumentSize();
    959 
    960  nsresult rv = MaybeCreateDocShell();
    961  if (NS_FAILED(rv)) {
    962    return false;
    963  }
    964  nsDocShell* ds = GetDocShell();
    965  MOZ_ASSERT(ds, "MaybeCreateDocShell succeeded, but null docShell");
    966  if (!ds) {
    967    return false;
    968  }
    969 
    970  ds->SetScrollbarPreference(GetScrollbarPreference(mOwnerContent));
    971  const bool marginsChanged =
    972      ds->UpdateFrameMargins(GetMarginAttributes(mOwnerContent));
    973 
    974  // If we already have a pres shell (which can happen with <object> / <embed>)
    975  // then hook it up to the frame.
    976  if (PresShell* presShell = ds->GetPresShell()) {
    977    // Ensure root scroll frame is reflowed in case margins have changed.
    978    if (marginsChanged) {
    979      if (nsIFrame* rootScrollContainerFrame =
    980              presShell->GetRootScrollContainerFrame()) {
    981        presShell->FrameNeedsReflow(rootScrollContainerFrame,
    982                                    IntrinsicDirty::None, NS_FRAME_IS_DIRTY);
    983      }
    984    }
    985    aFrame->EnsureEmbeddingPresShell(presShell);
    986  }
    987 
    988  RefPtr<nsDocShell> baseWindow = GetDocShell();
    989  MOZ_ASSERT(ds == baseWindow, "How did the docshell change?");
    990  baseWindow->InitWindow(nullptr, 0, 0, size.width, size.height, nullptr,
    991                         nullptr);
    992  baseWindow->SetVisibility(true);
    993  NS_ENSURE_TRUE(GetDocShell(), false);
    994 
    995  // Trigger editor re-initialization if midas is turned on in the sub-document.
    996  // This shouldn't be necessary, but given the way our editor works, it is.
    997  //
    998  // See https://bugzilla.mozilla.org/show_bug.cgi?id=284245
    999  //
   1000  // FIXME(emilio): This is a massive hack, plus probably not the right place to
   1001  // do it. Should probably be done in Document::CreatePresShell?
   1002  if (RefPtr<PresShell> presShell = GetDocShell()->GetPresShell()) {
   1003    Document* doc = presShell->GetDocument();
   1004    nsHTMLDocument* htmlDoc =
   1005        doc && doc->IsHTMLOrXHTML() ? doc->AsHTMLDocument() : nullptr;
   1006 
   1007    if (htmlDoc) {
   1008      nsAutoString designMode;
   1009      htmlDoc->GetDesignMode(designMode);
   1010 
   1011      if (designMode.EqualsLiteral("on")) {
   1012        // Hold on to the editor object to let the document reattach to the
   1013        // same editor object, instead of creating a new one.
   1014        RefPtr<HTMLEditor> htmlEditor = GetDocShell()->GetHTMLEditor();
   1015        (void)htmlEditor;
   1016        htmlDoc->SetDesignMode(u"off"_ns, Nothing(), IgnoreErrors());
   1017 
   1018        htmlDoc->SetDesignMode(u"on"_ns, Nothing(), IgnoreErrors());
   1019      } else {
   1020        // Re-initialize the presentation for contenteditable documents
   1021        bool editable = false, hasEditingSession = false;
   1022        GetDocShell()->GetEditable(&editable);
   1023        GetDocShell()->GetHasEditingSession(&hasEditingSession);
   1024        RefPtr<HTMLEditor> htmlEditor = GetDocShell()->GetHTMLEditor();
   1025        if (editable && hasEditingSession && htmlEditor) {
   1026          htmlEditor->PostCreate();
   1027        }
   1028      }
   1029    }
   1030  }
   1031 
   1032  mInShow = false;
   1033  if (mHideCalled) {
   1034    mHideCalled = false;
   1035    Hide();
   1036    return false;
   1037  }
   1038  return true;
   1039 }
   1040 
   1041 void nsFrameLoader::MarginsChanged() {
   1042  // We assume that the margins are always zero for remote frames.
   1043  if (IsRemoteFrame()) {
   1044    return;
   1045  }
   1046 
   1047  nsDocShell* docShell = GetDocShell();
   1048  // If there's no docshell, we're probably not up and running yet.
   1049  // nsFrameLoader::Show() will take care of setting the right
   1050  // margins.
   1051  if (!docShell) {
   1052    return;
   1053  }
   1054 
   1055  if (!docShell->UpdateFrameMargins(GetMarginAttributes(mOwnerContent))) {
   1056    return;
   1057  }
   1058 
   1059  // There's a cached property declaration block
   1060  // that needs to be updated
   1061  if (Document* doc = docShell->GetDocument()) {
   1062    for (nsINode* cur = doc; cur; cur = cur->GetNextNode()) {
   1063      if (auto* body = HTMLBodyElement::FromNode(cur)) {
   1064        body->FrameMarginsChanged();
   1065      }
   1066    }
   1067  }
   1068 }
   1069 
   1070 bool nsFrameLoader::ShowRemoteFrame(nsSubDocumentFrame* aFrame) {
   1071  AUTO_PROFILER_LABEL("nsFrameLoader::ShowRemoteFrame", OTHER);
   1072  NS_ASSERTION(IsRemoteFrame(),
   1073               "ShowRemote only makes sense on remote frames.");
   1074 
   1075  if (!EnsureRemoteBrowser()) {
   1076    NS_ERROR("Couldn't create child process.");
   1077    return false;
   1078  }
   1079 
   1080  const bool hasSize =
   1081      aFrame && !aFrame->HasAnyStateBits(NS_FRAME_FIRST_REFLOW);
   1082 
   1083  // FIXME/bug 589337: Show()/Hide() is pretty expensive for
   1084  // cross-process layers; need to figure out what behavior we really
   1085  // want here.  For now, hack.
   1086  if (!mRemoteBrowserShown) {
   1087    if (!mOwnerContent || !mOwnerContent->GetComposedDoc()) {
   1088      return false;
   1089    }
   1090 
   1091    // We never want to host remote frameloaders in simple popups, like menus.
   1092    nsIWidget* widget = nsContentUtils::WidgetForContent(mOwnerContent);
   1093    if (!widget || widget->IsSmallPopup()) {
   1094      return false;
   1095    }
   1096 
   1097    if (BrowserHost* bh = mRemoteBrowser->AsBrowserHost()) {
   1098      RefPtr<BrowsingContext> bc = bh->GetBrowsingContext()->Top();
   1099 
   1100      // Set to the current activation of the window.
   1101      bc->SetIsActiveBrowserWindow(bc->GetIsActiveBrowserWindow());
   1102    }
   1103 
   1104    nsCOMPtr<nsISupports> container = mOwnerContent->OwnerDoc()->GetContainer();
   1105    nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(container);
   1106    nsCOMPtr<nsIWidget> mainWidget;
   1107    baseWindow->GetMainWidget(getter_AddRefs(mainWidget));
   1108    nsSizeMode sizeMode =
   1109        mainWidget ? mainWidget->SizeMode() : nsSizeMode_Normal;
   1110    const auto size =
   1111        hasSize ? aFrame->GetSubdocumentSize() : LayoutDeviceIntSize();
   1112    OwnerShowInfo info(size, GetScrollbarPreference(mOwnerContent), sizeMode);
   1113    if (!mRemoteBrowser->Show(info)) {
   1114      return false;
   1115    }
   1116    mRemoteBrowserShown = true;
   1117    mRemoteBrowserSized = hasSize;
   1118 
   1119    // This notification doesn't apply to fission, apparently.
   1120    if (!GetBrowserBridgeChild()) {
   1121      if (nsCOMPtr<nsIObserverService> os = services::GetObserverService()) {
   1122        os->NotifyObservers(ToSupports(this), "remote-browser-shown", nullptr);
   1123      }
   1124    }
   1125  } else if (hasSize) {
   1126    NS_ENSURE_SUCCESS(UpdatePositionAndSize(aFrame), false);
   1127  }
   1128 
   1129  return true;
   1130 }
   1131 
   1132 void nsFrameLoader::Hide() {
   1133  if (mHideCalled) {
   1134    return;
   1135  }
   1136  if (mInShow) {
   1137    mHideCalled = true;
   1138    return;
   1139  }
   1140 
   1141  if (mRemoteBrowser) {
   1142    mRemoteBrowser->UpdateEffects(EffectsInfo::FullyHidden());
   1143  }
   1144 
   1145  if (!GetDocShell()) {
   1146    return;
   1147  }
   1148 
   1149  nsCOMPtr<nsIDocumentViewer> viewer;
   1150  GetDocShell()->GetDocViewer(getter_AddRefs(viewer));
   1151  if (viewer) viewer->SetSticky(false);
   1152 
   1153  RefPtr<nsDocShell> baseWin = GetDocShell();
   1154  baseWin->SetVisibility(false);
   1155  baseWin->SetParentWidget(nullptr);
   1156 }
   1157 
   1158 void nsFrameLoader::ForceLayoutIfNecessary() {
   1159  nsIFrame* frame = GetPrimaryFrameOfOwningContent();
   1160  if (!frame) {
   1161    return;
   1162  }
   1163 
   1164  nsPresContext* presContext = frame->PresContext();
   1165  if (!presContext) {
   1166    return;
   1167  }
   1168 
   1169  // Only force the layout flush if the frameloader hasn't ever been
   1170  // run through layout.
   1171  if (frame->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
   1172    if (RefPtr<PresShell> presShell = presContext->GetPresShell()) {
   1173      presShell->FlushPendingNotifications(FlushType::Layout);
   1174    }
   1175  }
   1176 }
   1177 
   1178 nsresult nsFrameLoader::SwapWithOtherRemoteLoader(
   1179    nsFrameLoader* aOther, nsFrameLoaderOwner* aThisOwner,
   1180    nsFrameLoaderOwner* aOtherOwner) {
   1181  MOZ_ASSERT(NS_IsMainThread());
   1182 
   1183 #ifdef DEBUG
   1184  RefPtr<nsFrameLoader> first = aThisOwner->GetFrameLoader();
   1185  RefPtr<nsFrameLoader> second = aOtherOwner->GetFrameLoader();
   1186  MOZ_ASSERT(first == this, "aThisOwner must own this");
   1187  MOZ_ASSERT(second == aOther, "aOtherOwner must own aOther");
   1188 #endif
   1189 
   1190  Element* ourContent = mOwnerContent;
   1191  Element* otherContent = aOther->mOwnerContent;
   1192 
   1193  if (!ourContent || !otherContent) {
   1194    // Can't handle this
   1195    return NS_ERROR_NOT_IMPLEMENTED;
   1196  }
   1197 
   1198  // Make sure there are no same-origin issues
   1199  bool equal;
   1200  nsresult rv = ourContent->NodePrincipal()->Equals(
   1201      otherContent->NodePrincipal(), &equal);
   1202  if (NS_FAILED(rv) || !equal) {
   1203    // Security problems loom.  Just bail on it all
   1204    return NS_ERROR_DOM_SECURITY_ERR;
   1205  }
   1206 
   1207  Document* ourDoc = ourContent->GetComposedDoc();
   1208  Document* otherDoc = otherContent->GetComposedDoc();
   1209  if (!ourDoc || !otherDoc) {
   1210    // Again, how odd, given that we had docshells
   1211    return NS_ERROR_NOT_IMPLEMENTED;
   1212  }
   1213 
   1214  PresShell* ourPresShell = ourDoc->GetPresShell();
   1215  PresShell* otherPresShell = otherDoc->GetPresShell();
   1216  if (!ourPresShell || !otherPresShell) {
   1217    return NS_ERROR_NOT_IMPLEMENTED;
   1218  }
   1219 
   1220  auto* browserParent = GetBrowserParent();
   1221  auto* otherBrowserParent = aOther->GetBrowserParent();
   1222 
   1223  if (!browserParent || !otherBrowserParent) {
   1224    return NS_ERROR_NOT_IMPLEMENTED;
   1225  }
   1226 
   1227  RefPtr<BrowsingContext> ourBc = browserParent->GetBrowsingContext();
   1228  RefPtr<BrowsingContext> otherBc = otherBrowserParent->GetBrowsingContext();
   1229 
   1230  // When we swap docShells, maybe we have to deal with a new page created just
   1231  // for this operation. In this case, the browser code should already have set
   1232  // the correct userContextId attribute value in the owning element, but our
   1233  // docShell, that has been created way before) doesn't know that that
   1234  // happened.
   1235  // This is the reason why now we must retrieve the correct value from the
   1236  // usercontextid attribute before comparing our originAttributes with the
   1237  // other one.
   1238  OriginAttributes ourOriginAttributes = ourBc->OriginAttributesRef();
   1239  rv = PopulateOriginContextIdsFromAttributes(ourOriginAttributes);
   1240  NS_ENSURE_SUCCESS(rv, rv);
   1241 
   1242  OriginAttributes otherOriginAttributes = otherBc->OriginAttributesRef();
   1243  rv = aOther->PopulateOriginContextIdsFromAttributes(otherOriginAttributes);
   1244  NS_ENSURE_SUCCESS(rv, rv);
   1245 
   1246  if (!ourOriginAttributes.EqualsIgnoringFPD(otherOriginAttributes)) {
   1247    return NS_ERROR_NOT_IMPLEMENTED;
   1248  }
   1249 
   1250  bool ourHasHistory = mIsTopLevelContent &&
   1251                       ourContent->IsXULElement(nsGkAtoms::browser) &&
   1252                       !ourContent->HasAttr(nsGkAtoms::disablehistory);
   1253  bool otherHasHistory = aOther->mIsTopLevelContent &&
   1254                         otherContent->IsXULElement(nsGkAtoms::browser) &&
   1255                         !otherContent->HasAttr(nsGkAtoms::disablehistory);
   1256  if (ourHasHistory != otherHasHistory) {
   1257    return NS_ERROR_NOT_IMPLEMENTED;
   1258  }
   1259 
   1260  if (mInSwap || aOther->mInSwap) {
   1261    return NS_ERROR_NOT_IMPLEMENTED;
   1262  }
   1263  mInSwap = aOther->mInSwap = true;
   1264 
   1265  // NOTE(emilio): This doesn't have to flush because the caller does already.
   1266  nsIFrame* ourFrame = ourContent->GetPrimaryFrame();
   1267  nsIFrame* otherFrame = otherContent->GetPrimaryFrame();
   1268  if (!ourFrame || !otherFrame) {
   1269    mInSwap = aOther->mInSwap = false;
   1270    return NS_ERROR_NOT_IMPLEMENTED;
   1271  }
   1272 
   1273  nsSubDocumentFrame* ourFrameFrame = do_QueryFrame(ourFrame);
   1274  if (!ourFrameFrame) {
   1275    mInSwap = aOther->mInSwap = false;
   1276    return NS_ERROR_NOT_IMPLEMENTED;
   1277  }
   1278 
   1279  rv = ourFrameFrame->BeginSwapDocShells(otherFrame);
   1280  if (NS_FAILED(rv)) {
   1281    mInSwap = aOther->mInSwap = false;
   1282    return rv;
   1283  }
   1284 
   1285  nsCOMPtr<nsIBrowserDOMWindow> otherBrowserDOMWindow =
   1286      otherBrowserParent->GetBrowserDOMWindow();
   1287  nsCOMPtr<nsIBrowserDOMWindow> browserDOMWindow =
   1288      browserParent->GetBrowserDOMWindow();
   1289 
   1290  if (!!otherBrowserDOMWindow != !!browserDOMWindow) {
   1291    return NS_ERROR_NOT_IMPLEMENTED;
   1292  }
   1293 
   1294  otherBrowserParent->SetBrowserDOMWindow(browserDOMWindow);
   1295  browserParent->SetBrowserDOMWindow(otherBrowserDOMWindow);
   1296 
   1297  MaybeUpdatePrimaryBrowserParent(eBrowserParentRemoved);
   1298  aOther->MaybeUpdatePrimaryBrowserParent(eBrowserParentRemoved);
   1299 
   1300  if (mozilla::BFCacheInParent() && XRE_IsParentProcess()) {
   1301    // nsFrameLoaders in session history can't be moved to another owner since
   1302    // there are no corresponging message managers on which swap can be done.
   1303    // See the line mMessageManager.swap(aOther->mMessageManager); below.
   1304    auto evict = [](nsFrameLoader* aFrameLoader) {
   1305      if (BrowsingContext* bc =
   1306              aFrameLoader->GetMaybePendingBrowsingContext()) {
   1307        nsCOMPtr<nsISHistory> shistory = bc->Canonical()->GetSessionHistory();
   1308        if (shistory) {
   1309          shistory->EvictAllDocumentViewers();
   1310        }
   1311      }
   1312    };
   1313    evict(this);
   1314    evict(aOther);
   1315  }
   1316 
   1317  SetOwnerContent(otherContent);
   1318  aOther->SetOwnerContent(ourContent);
   1319 
   1320  browserParent->SetOwnerElement(otherContent);
   1321  otherBrowserParent->SetOwnerElement(ourContent);
   1322 
   1323  // Update window activation state for the swapped owner content.
   1324  bool ourActive = otherBc->GetIsActiveBrowserWindow();
   1325  bool otherActive = ourBc->GetIsActiveBrowserWindow();
   1326  if (ourBc->IsTop()) {
   1327    ourBc->SetIsActiveBrowserWindow(otherActive);
   1328  }
   1329  if (otherBc->IsTop()) {
   1330    otherBc->SetIsActiveBrowserWindow(ourActive);
   1331  }
   1332 
   1333  MaybeUpdatePrimaryBrowserParent(eBrowserParentChanged);
   1334  aOther->MaybeUpdatePrimaryBrowserParent(eBrowserParentChanged);
   1335 
   1336  RefPtr<nsFrameMessageManager> ourMessageManager = mMessageManager;
   1337  RefPtr<nsFrameMessageManager> otherMessageManager = aOther->mMessageManager;
   1338  // Swap and setup things in parent message managers.
   1339  if (ourMessageManager) {
   1340    ourMessageManager->SetCallback(aOther);
   1341  }
   1342  if (otherMessageManager) {
   1343    otherMessageManager->SetCallback(this);
   1344  }
   1345  mMessageManager.swap(aOther->mMessageManager);
   1346 
   1347  // XXXsmaug what should be done to JSWindowActorParent objects when swapping
   1348  // frameloaders? Currently they leak very easily, bug 1697918.
   1349 
   1350  // Perform the actual swap of the internal refptrs. We keep a strong reference
   1351  // to ourselves to make sure we don't die while we overwrite our reference to
   1352  // ourself.
   1353  RefPtr<nsFrameLoader> kungFuDeathGrip(this);
   1354  aThisOwner->SetFrameLoader(aOther);
   1355  aOtherOwner->SetFrameLoader(kungFuDeathGrip);
   1356 
   1357  ourFrameFrame->EndSwapDocShells(otherFrame);
   1358 
   1359  ourPresShell->BackingScaleFactorChanged();
   1360  otherPresShell->BackingScaleFactorChanged();
   1361 
   1362  mInSwap = aOther->mInSwap = false;
   1363 
   1364  // Send an updated tab context since owner content type may have changed.
   1365  MutableTabContext ourContext;
   1366  rv = GetNewTabContext(&ourContext);
   1367  if (NS_WARN_IF(NS_FAILED(rv))) {
   1368    return rv;
   1369  }
   1370  MutableTabContext otherContext;
   1371  rv = aOther->GetNewTabContext(&otherContext);
   1372  if (NS_WARN_IF(NS_FAILED(rv))) {
   1373    return rv;
   1374  }
   1375 
   1376  (void)browserParent->SendSwappedWithOtherRemoteLoader(
   1377      ourContext.AsIPCTabContext());
   1378  (void)otherBrowserParent->SendSwappedWithOtherRemoteLoader(
   1379      otherContext.AsIPCTabContext());
   1380  // These might have moved to a new window, so make sure they have
   1381  // the appropriate priority (bug 1896172)
   1382  browserParent->RecomputeProcessPriority();
   1383  otherBrowserParent->RecomputeProcessPriority();
   1384  return NS_OK;
   1385 }
   1386 
   1387 class MOZ_RAII AutoResetInFrameSwap final {
   1388 public:
   1389  AutoResetInFrameSwap(nsFrameLoader* aThisFrameLoader,
   1390                       nsFrameLoader* aOtherFrameLoader,
   1391                       nsDocShell* aThisDocShell, nsDocShell* aOtherDocShell,
   1392                       EventTarget* aThisEventTarget,
   1393                       EventTarget* aOtherEventTarget)
   1394      : mThisFrameLoader(aThisFrameLoader),
   1395        mOtherFrameLoader(aOtherFrameLoader),
   1396        mThisDocShell(aThisDocShell),
   1397        mOtherDocShell(aOtherDocShell),
   1398        mThisEventTarget(aThisEventTarget),
   1399        mOtherEventTarget(aOtherEventTarget) {
   1400    mThisFrameLoader->mInSwap = true;
   1401    mOtherFrameLoader->mInSwap = true;
   1402    mThisDocShell->SetInFrameSwap(true);
   1403    mOtherDocShell->SetInFrameSwap(true);
   1404 
   1405    // Fire pageshow events on still-loading pages, and then fire pagehide
   1406    // events.  Note that we do NOT fire these in the normal way, but just fire
   1407    // them on the chrome event handlers.
   1408    nsContentUtils::FirePageShowEventForFrameLoaderSwap(
   1409        mThisDocShell, mThisEventTarget, false);
   1410    nsContentUtils::FirePageShowEventForFrameLoaderSwap(
   1411        mOtherDocShell, mOtherEventTarget, false);
   1412    nsContentUtils::FirePageHideEventForFrameLoaderSwap(mThisDocShell,
   1413                                                        mThisEventTarget);
   1414    nsContentUtils::FirePageHideEventForFrameLoaderSwap(mOtherDocShell,
   1415                                                        mOtherEventTarget);
   1416  }
   1417 
   1418  ~AutoResetInFrameSwap() {
   1419    nsContentUtils::FirePageShowEventForFrameLoaderSwap(mThisDocShell,
   1420                                                        mThisEventTarget, true);
   1421    nsContentUtils::FirePageShowEventForFrameLoaderSwap(
   1422        mOtherDocShell, mOtherEventTarget, true);
   1423 
   1424    mThisFrameLoader->mInSwap = false;
   1425    mOtherFrameLoader->mInSwap = false;
   1426    mThisDocShell->SetInFrameSwap(false);
   1427    mOtherDocShell->SetInFrameSwap(false);
   1428 
   1429    // This is needed to get visibility state right in cases when we swapped a
   1430    // visible tab (foreground in visible window) with a non-visible tab.
   1431    if (RefPtr<Document> doc = mThisDocShell->GetDocument()) {
   1432      doc->UpdateVisibilityState();
   1433    }
   1434    if (RefPtr<Document> doc = mOtherDocShell->GetDocument()) {
   1435      doc->UpdateVisibilityState();
   1436    }
   1437  }
   1438 
   1439 private:
   1440  RefPtr<nsFrameLoader> mThisFrameLoader;
   1441  RefPtr<nsFrameLoader> mOtherFrameLoader;
   1442  RefPtr<nsDocShell> mThisDocShell;
   1443  RefPtr<nsDocShell> mOtherDocShell;
   1444  nsCOMPtr<EventTarget> mThisEventTarget;
   1445  nsCOMPtr<EventTarget> mOtherEventTarget;
   1446 };
   1447 
   1448 nsresult nsFrameLoader::SwapWithOtherLoader(nsFrameLoader* aOther,
   1449                                            nsFrameLoaderOwner* aThisOwner,
   1450                                            nsFrameLoaderOwner* aOtherOwner) {
   1451 #ifdef DEBUG
   1452  RefPtr<nsFrameLoader> first = aThisOwner->GetFrameLoader();
   1453  RefPtr<nsFrameLoader> second = aOtherOwner->GetFrameLoader();
   1454  MOZ_ASSERT(first == this, "aThisOwner must own this");
   1455  MOZ_ASSERT(second == aOther, "aOtherOwner must own aOther");
   1456 #endif
   1457 
   1458  NS_ENSURE_STATE(!mInShow && !aOther->mInShow);
   1459 
   1460  if (IsRemoteFrame() != aOther->IsRemoteFrame()) {
   1461    NS_WARNING(
   1462        "Swapping remote and non-remote frames is not currently supported");
   1463    return NS_ERROR_NOT_IMPLEMENTED;
   1464  }
   1465 
   1466  RefPtr<Element> ourContent = mOwnerContent;
   1467  RefPtr<Element> otherContent = aOther->mOwnerContent;
   1468  if (!ourContent || !otherContent) {
   1469    // Can't handle this
   1470    return NS_ERROR_NOT_IMPLEMENTED;
   1471  }
   1472 
   1473  nsIFrame* ourFrame = ourContent->GetPrimaryFrame(FlushType::Frames);
   1474  nsIFrame* otherFrame = otherContent->GetPrimaryFrame(FlushType::Frames);
   1475  if (!ourFrame || !otherFrame) {
   1476    return NS_ERROR_NOT_IMPLEMENTED;
   1477  }
   1478 
   1479  // Ensure the flushes above haven't changed all the world.
   1480  if (ourContent != mOwnerContent || otherContent != aOther->mOwnerContent) {
   1481    return NS_ERROR_NOT_IMPLEMENTED;
   1482  }
   1483 
   1484  bool ourHasSrcdoc = ourContent->IsHTMLElement(nsGkAtoms::iframe) &&
   1485                      ourContent->HasAttr(nsGkAtoms::srcdoc);
   1486  bool otherHasSrcdoc = otherContent->IsHTMLElement(nsGkAtoms::iframe) &&
   1487                        otherContent->HasAttr(nsGkAtoms::srcdoc);
   1488  if (ourHasSrcdoc || otherHasSrcdoc) {
   1489    // Ignore this case entirely for now, since we support XUL <-> HTML swapping
   1490    return NS_ERROR_NOT_IMPLEMENTED;
   1491  }
   1492 
   1493  bool ourFullscreenAllowed = ourContent->IsXULElement();
   1494  bool otherFullscreenAllowed = otherContent->IsXULElement();
   1495  if (ourFullscreenAllowed != otherFullscreenAllowed) {
   1496    return NS_ERROR_NOT_IMPLEMENTED;
   1497  }
   1498 
   1499  nsILoadContext* ourLoadContext = ourContent->OwnerDoc()->GetLoadContext();
   1500  nsILoadContext* otherLoadContext = otherContent->OwnerDoc()->GetLoadContext();
   1501  MOZ_ASSERT(ourLoadContext && otherLoadContext,
   1502             "Swapping frames within dead documents?");
   1503  if (ourLoadContext->UseRemoteTabs() != otherLoadContext->UseRemoteTabs()) {
   1504    NS_WARNING("Can't swap between e10s and non-e10s windows");
   1505    return NS_ERROR_NOT_IMPLEMENTED;
   1506  }
   1507  if (ourLoadContext->UseRemoteSubframes() !=
   1508      otherLoadContext->UseRemoteSubframes()) {
   1509    NS_WARNING("Can't swap between fission and non-fission windows");
   1510    return NS_ERROR_NOT_IMPLEMENTED;
   1511  }
   1512 
   1513  // Divert to a separate path for the remaining steps in the remote case
   1514  if (IsRemoteFrame()) {
   1515    MOZ_ASSERT(aOther->IsRemoteFrame());
   1516    return SwapWithOtherRemoteLoader(aOther, aThisOwner, aOtherOwner);
   1517  }
   1518 
   1519  // Make sure there are no same-origin issues
   1520  bool equal;
   1521  nsresult rv = ourContent->NodePrincipal()->Equals(
   1522      otherContent->NodePrincipal(), &equal);
   1523  if (NS_FAILED(rv) || !equal) {
   1524    // Security problems loom.  Just bail on it all
   1525    return NS_ERROR_DOM_SECURITY_ERR;
   1526  }
   1527 
   1528  RefPtr<nsDocShell> ourDocshell = GetExistingDocShell();
   1529  RefPtr<nsDocShell> otherDocshell = aOther->GetExistingDocShell();
   1530  if (!ourDocshell || !otherDocshell) {
   1531    // How odd
   1532    return NS_ERROR_NOT_IMPLEMENTED;
   1533  }
   1534 
   1535  // To avoid having to mess with session history, avoid swapping
   1536  // frameloaders that don't correspond to root same-type docshells,
   1537  // unless both roots have session history disabled.
   1538  nsCOMPtr<nsIDocShellTreeItem> ourRootTreeItem, otherRootTreeItem;
   1539  ourDocshell->GetInProcessSameTypeRootTreeItem(
   1540      getter_AddRefs(ourRootTreeItem));
   1541  otherDocshell->GetInProcessSameTypeRootTreeItem(
   1542      getter_AddRefs(otherRootTreeItem));
   1543  nsCOMPtr<nsIWebNavigation> ourRootWebnav = do_QueryInterface(ourRootTreeItem);
   1544  nsCOMPtr<nsIWebNavigation> otherRootWebnav =
   1545      do_QueryInterface(otherRootTreeItem);
   1546 
   1547  if (!ourRootWebnav || !otherRootWebnav) {
   1548    return NS_ERROR_NOT_IMPLEMENTED;
   1549  }
   1550 
   1551  RefPtr<ChildSHistory> ourHistory = ourRootWebnav->GetSessionHistory();
   1552  RefPtr<ChildSHistory> otherHistory = otherRootWebnav->GetSessionHistory();
   1553 
   1554  if ((ourRootTreeItem != ourDocshell || otherRootTreeItem != otherDocshell) &&
   1555      (ourHistory || otherHistory)) {
   1556    return NS_ERROR_NOT_IMPLEMENTED;
   1557  }
   1558 
   1559  RefPtr<BrowsingContext> ourBc = ourDocshell->GetBrowsingContext();
   1560  RefPtr<BrowsingContext> otherBc = otherDocshell->GetBrowsingContext();
   1561 
   1562  // Also make sure that the two BrowsingContexts are the same type. Otherwise
   1563  // swapping is certainly not safe. If this needs to be changed then
   1564  // the code below needs to be audited as it assumes identical types.
   1565  if (ourBc->GetType() != otherBc->GetType()) {
   1566    return NS_ERROR_NOT_IMPLEMENTED;
   1567  }
   1568 
   1569  // We ensure that BCs are either both top frames or both subframes.
   1570  if (ourBc->IsTop() != otherBc->IsTop()) {
   1571    return NS_ERROR_NOT_IMPLEMENTED;
   1572  }
   1573 
   1574  // One more twist here.  Setting up the right treeowners in a heterogeneous
   1575  // tree is a bit of a pain.  So make sure that if `ourBc->GetType()` is not
   1576  // nsIDocShellTreeItem::typeContent then all of our descendants are the same
   1577  // type as us.
   1578  if (!ourBc->IsContent() &&
   1579      (!AllDescendantsOfType(ourBc, ourBc->GetType()) ||
   1580       !AllDescendantsOfType(otherBc, otherBc->GetType()))) {
   1581    return NS_ERROR_NOT_IMPLEMENTED;
   1582  }
   1583 
   1584  // Save off the tree owners, frame elements, chrome event handlers, and
   1585  // docshell and document parents before doing anything else.
   1586  nsCOMPtr<nsIDocShellTreeOwner> ourOwner, otherOwner;
   1587  ourDocshell->GetTreeOwner(getter_AddRefs(ourOwner));
   1588  otherDocshell->GetTreeOwner(getter_AddRefs(otherOwner));
   1589  // Note: it's OK to have null treeowners.
   1590 
   1591  nsCOMPtr<nsIDocShellTreeItem> ourParentItem, otherParentItem;
   1592  ourDocshell->GetInProcessParent(getter_AddRefs(ourParentItem));
   1593  otherDocshell->GetInProcessParent(getter_AddRefs(otherParentItem));
   1594  if (!ourParentItem || !otherParentItem) {
   1595    return NS_ERROR_NOT_IMPLEMENTED;
   1596  }
   1597 
   1598  nsCOMPtr<nsPIDOMWindowOuter> ourWindow = ourDocshell->GetWindow();
   1599  nsCOMPtr<nsPIDOMWindowOuter> otherWindow = otherDocshell->GetWindow();
   1600 
   1601  nsCOMPtr<Element> ourFrameElement = ourWindow->GetFrameElementInternal();
   1602  nsCOMPtr<Element> otherFrameElement = otherWindow->GetFrameElementInternal();
   1603 
   1604  nsCOMPtr<EventTarget> ourChromeEventHandler =
   1605      ourWindow->GetChromeEventHandler();
   1606  nsCOMPtr<EventTarget> otherChromeEventHandler =
   1607      otherWindow->GetChromeEventHandler();
   1608 
   1609  nsCOMPtr<EventTarget> ourEventTarget = ourWindow->GetParentTarget();
   1610  nsCOMPtr<EventTarget> otherEventTarget = otherWindow->GetParentTarget();
   1611 
   1612  NS_ASSERTION(SameCOMIdentity(ourFrameElement, ourContent) &&
   1613                   SameCOMIdentity(otherFrameElement, otherContent) &&
   1614                   SameCOMIdentity(ourChromeEventHandler, ourContent) &&
   1615                   SameCOMIdentity(otherChromeEventHandler, otherContent),
   1616               "How did that happen, exactly?");
   1617 
   1618  nsCOMPtr<Document> ourChildDocument = ourWindow->GetExtantDoc();
   1619  nsCOMPtr<Document> otherChildDocument = otherWindow->GetExtantDoc();
   1620  if (!ourChildDocument || !otherChildDocument) {
   1621    // This shouldn't be happening
   1622    return NS_ERROR_NOT_IMPLEMENTED;
   1623  }
   1624 
   1625  nsCOMPtr<Document> ourParentDocument =
   1626      ourChildDocument->GetInProcessParentDocument();
   1627  nsCOMPtr<Document> otherParentDocument =
   1628      otherChildDocument->GetInProcessParentDocument();
   1629 
   1630  // Make sure to swap docshells between the two frames.
   1631  Document* ourDoc = ourContent->GetComposedDoc();
   1632  Document* otherDoc = otherContent->GetComposedDoc();
   1633  if (!ourDoc || !otherDoc) {
   1634    // Again, how odd, given that we had docshells
   1635    return NS_ERROR_NOT_IMPLEMENTED;
   1636  }
   1637 
   1638  NS_ASSERTION(ourDoc == ourParentDocument, "Unexpected parent document");
   1639  NS_ASSERTION(otherDoc == otherParentDocument, "Unexpected parent document");
   1640 
   1641  PresShell* ourPresShell = ourDoc->GetPresShell();
   1642  PresShell* otherPresShell = otherDoc->GetPresShell();
   1643  if (!ourPresShell || !otherPresShell) {
   1644    return NS_ERROR_NOT_IMPLEMENTED;
   1645  }
   1646 
   1647  // When we swap docShells, maybe we have to deal with a new page created just
   1648  // for this operation. In this case, the browser code should already have set
   1649  // the correct userContextId attribute value in the owning element, but our
   1650  // docShell, that has been created way before) doesn't know that that
   1651  // happened.
   1652  // This is the reason why now we must retrieve the correct value from the
   1653  // usercontextid attribute before comparing our originAttributes with the
   1654  // other one.
   1655  OriginAttributes ourOriginAttributes = ourDocshell->GetOriginAttributes();
   1656  rv = PopulateOriginContextIdsFromAttributes(ourOriginAttributes);
   1657  NS_ENSURE_SUCCESS(rv, rv);
   1658 
   1659  OriginAttributes otherOriginAttributes = otherDocshell->GetOriginAttributes();
   1660  rv = aOther->PopulateOriginContextIdsFromAttributes(otherOriginAttributes);
   1661  NS_ENSURE_SUCCESS(rv, rv);
   1662 
   1663  if (ourOriginAttributes != otherOriginAttributes) {
   1664    return NS_ERROR_NOT_IMPLEMENTED;
   1665  }
   1666 
   1667  if (mInSwap || aOther->mInSwap) {
   1668    return NS_ERROR_NOT_IMPLEMENTED;
   1669  }
   1670  AutoResetInFrameSwap autoFrameSwap(this, aOther, ourDocshell, otherDocshell,
   1671                                     ourEventTarget, otherEventTarget);
   1672 
   1673  nsSubDocumentFrame* ourFrameFrame = do_QueryFrame(ourFrame);
   1674  if (!ourFrameFrame) {
   1675    return NS_ERROR_NOT_IMPLEMENTED;
   1676  }
   1677 
   1678  // OK.  First begin to swap the docshells in the two nsIFrames
   1679  rv = ourFrameFrame->BeginSwapDocShells(otherFrame);
   1680  if (NS_FAILED(rv)) {
   1681    return rv;
   1682  }
   1683 
   1684  // Now move the docshells to the right docshell trees.  Note that this
   1685  // resets their treeowners to null.
   1686  ourParentItem->RemoveChild(ourDocshell);
   1687  otherParentItem->RemoveChild(otherDocshell);
   1688  if (ourBc->IsContent()) {
   1689    ourOwner->ContentShellRemoved(ourDocshell);
   1690    otherOwner->ContentShellRemoved(otherDocshell);
   1691  }
   1692 
   1693  ourParentItem->AddChild(otherDocshell);
   1694  otherParentItem->AddChild(ourDocshell);
   1695 
   1696  // Restore the correct chrome event handlers.
   1697  ourDocshell->SetChromeEventHandler(otherChromeEventHandler);
   1698  otherDocshell->SetChromeEventHandler(ourChromeEventHandler);
   1699  // Restore the correct treeowners
   1700  // (and also chrome event handlers for content frames only).
   1701  SetTreeOwnerAndChromeEventHandlerOnDocshellTree(
   1702      ourDocshell, otherOwner,
   1703      ourBc->IsContent() ? otherChromeEventHandler.get() : nullptr);
   1704  SetTreeOwnerAndChromeEventHandlerOnDocshellTree(
   1705      otherDocshell, ourOwner,
   1706      ourBc->IsContent() ? ourChromeEventHandler.get() : nullptr);
   1707 
   1708  // Switch the owner content before we start calling AddTreeItemToTreeOwner.
   1709  // Note that we rely on this to deal with setting mObservingOwnerContent to
   1710  // false and calling RemoveMutationObserver as needed.
   1711  SetOwnerContent(otherContent);
   1712  aOther->SetOwnerContent(ourContent);
   1713 
   1714  AddTreeItemToTreeOwner(ourDocshell, otherOwner);
   1715  aOther->AddTreeItemToTreeOwner(otherDocshell, ourOwner);
   1716 
   1717  // SetSubDocumentFor nulls out parent documents on the old child doc if a
   1718  // new non-null document is passed in, so just go ahead and remove both
   1719  // kids before reinserting in the parent subdoc maps, to avoid
   1720  // complications.
   1721  ourParentDocument->SetSubDocumentFor(ourContent, nullptr);
   1722  otherParentDocument->SetSubDocumentFor(otherContent, nullptr);
   1723  ourParentDocument->SetSubDocumentFor(ourContent, otherChildDocument);
   1724  otherParentDocument->SetSubDocumentFor(otherContent, ourChildDocument);
   1725 
   1726  ourWindow->SetFrameElementInternal(otherFrameElement);
   1727  otherWindow->SetFrameElementInternal(ourFrameElement);
   1728 
   1729  RefPtr<nsFrameMessageManager> ourMessageManager = mMessageManager;
   1730  RefPtr<nsFrameMessageManager> otherMessageManager = aOther->mMessageManager;
   1731  // Swap pointers in child message managers.
   1732  if (mChildMessageManager) {
   1733    InProcessBrowserChildMessageManager* browserChild = mChildMessageManager;
   1734    browserChild->SetOwner(otherContent);
   1735    browserChild->SetChromeMessageManager(otherMessageManager);
   1736  }
   1737  if (aOther->mChildMessageManager) {
   1738    InProcessBrowserChildMessageManager* otherBrowserChild =
   1739        aOther->mChildMessageManager;
   1740    otherBrowserChild->SetOwner(ourContent);
   1741    otherBrowserChild->SetChromeMessageManager(ourMessageManager);
   1742  }
   1743  // Swap and setup things in parent message managers.
   1744  if (mMessageManager) {
   1745    mMessageManager->SetCallback(aOther);
   1746  }
   1747  if (aOther->mMessageManager) {
   1748    aOther->mMessageManager->SetCallback(this);
   1749  }
   1750  mMessageManager.swap(aOther->mMessageManager);
   1751 
   1752  // Perform the actual swap of the internal refptrs. We keep a strong reference
   1753  // to ourselves to make sure we don't die while we overwrite our reference to
   1754  // ourself.
   1755  RefPtr<nsFrameLoader> kungFuDeathGrip(this);
   1756  aThisOwner->SetFrameLoader(aOther);
   1757  aOtherOwner->SetFrameLoader(kungFuDeathGrip);
   1758 
   1759  // Drop any cached content viewers in the two session histories.
   1760  if (ourHistory) {
   1761    ourHistory->EvictLocalDocumentViewers();
   1762  }
   1763  if (otherHistory) {
   1764    otherHistory->EvictLocalDocumentViewers();
   1765  }
   1766 
   1767  NS_ASSERTION(ourFrame == ourContent->GetPrimaryFrame() &&
   1768                   otherFrame == otherContent->GetPrimaryFrame(),
   1769               "changed primary frame");
   1770 
   1771  ourFrameFrame->EndSwapDocShells(otherFrame);
   1772 
   1773  // If the content being swapped came from windows on two screens with
   1774  // incompatible backing resolution (e.g. dragging a tab between windows on
   1775  // hi-dpi and low-dpi screens), it will have style data that is based on
   1776  // the wrong appUnitsPerDevPixel value. So we tell the PresShells that their
   1777  // backing scale factor may have changed. (Bug 822266)
   1778  ourFrame->PresShell()->BackingScaleFactorChanged();
   1779  otherFrame->PresShell()->BackingScaleFactorChanged();
   1780 
   1781  return NS_OK;
   1782 }
   1783 
   1784 void nsFrameLoader::Destroy(bool aForProcessSwitch) {
   1785  StartDestroy(aForProcessSwitch);
   1786 }
   1787 
   1788 class nsFrameLoaderDestroyRunnable : public Runnable {
   1789  enum DestroyPhase {
   1790    // See the implementation of Run for an explanation of these phases.
   1791    eDestroyDocShell,
   1792    eWaitForUnloadMessage,
   1793    eDestroyComplete
   1794  };
   1795 
   1796  RefPtr<nsFrameLoader> mFrameLoader;
   1797  DestroyPhase mPhase;
   1798 
   1799 public:
   1800  explicit nsFrameLoaderDestroyRunnable(nsFrameLoader* aFrameLoader)
   1801      : mozilla::Runnable("nsFrameLoaderDestroyRunnable"),
   1802        mFrameLoader(aFrameLoader),
   1803        mPhase(eDestroyDocShell) {}
   1804 
   1805  NS_IMETHOD Run() override;
   1806 };
   1807 
   1808 void nsFrameLoader::StartDestroy(bool aForProcessSwitch) {
   1809  // nsFrameLoader::StartDestroy is called just before the frameloader is
   1810  // detached from the <browser> element. Destruction continues in phases via
   1811  // the nsFrameLoaderDestroyRunnable.
   1812 
   1813  if (mDestroyCalled) {
   1814    return;
   1815  }
   1816  mDestroyCalled = true;
   1817 
   1818  // Request a full tab state flush if the tab is closing.
   1819  //
   1820  // XXX If we find that we need to do Session Store cleanup for the frameloader
   1821  // that's going away, we should unconditionally do the flush here, but include
   1822  // the |aForProcessSwitch| flag in the completion notification.
   1823  if (!aForProcessSwitch) {
   1824    RequestFinalTabStateFlush();
   1825  }
   1826 
   1827  // After this point, we return an error when trying to send a message using
   1828  // the message manager on the frame.
   1829  if (mMessageManager) {
   1830    mMessageManager->Close();
   1831  }
   1832 
   1833  // Retain references to the <browser> element and the frameloader in case we
   1834  // receive any messages from the message manager on the frame. These
   1835  // references are dropped in DestroyComplete.
   1836  if (mChildMessageManager || mRemoteBrowser) {
   1837    mOwnerContentStrong = mOwnerContent;
   1838    if (auto* browserParent = GetBrowserParent()) {
   1839      browserParent->CacheFrameLoader(this);
   1840    }
   1841    if (mChildMessageManager) {
   1842      mChildMessageManager->CacheFrameLoader(this);
   1843    }
   1844  }
   1845 
   1846  // If the BrowserParent has installed any event listeners on the window, this
   1847  // is its last chance to remove them while we're still in the document.
   1848  if (auto* browserParent = GetBrowserParent()) {
   1849    browserParent->RemoveWindowListeners();
   1850  }
   1851 
   1852  // Hide the content viewer before nulling out the embedder element and so.
   1853  Hide();
   1854 
   1855  nsCOMPtr<Document> doc;
   1856  bool dynamicSubframeRemoval = false;
   1857  if (mOwnerContent) {
   1858    doc = mOwnerContent->OwnerDoc();
   1859    dynamicSubframeRemoval = !aForProcessSwitch &&
   1860                             mPendingBrowsingContext->IsSubframe() &&
   1861                             !doc->InUnlinkOrDeletion();
   1862    doc->SetSubDocumentFor(mOwnerContent, nullptr);
   1863    MaybeUpdatePrimaryBrowserParent(eBrowserParentRemoved);
   1864 
   1865    nsCOMPtr<nsFrameLoaderOwner> owner = do_QueryInterface(mOwnerContent);
   1866    owner->FrameLoaderDestroying(this, !aForProcessSwitch);
   1867    SetOwnerContent(nullptr);
   1868  }
   1869 
   1870  // Seems like this is a dynamic frame removal.
   1871  if (dynamicSubframeRemoval) {
   1872    BrowsingContext* browsingContext = GetExtantBrowsingContext();
   1873    if (browsingContext) {
   1874      RefPtr<ChildSHistory> childSHistory =
   1875          browsingContext->Top()->GetChildSessionHistory();
   1876      if (childSHistory) {
   1877        if (mozilla::SessionHistoryInParent()) {
   1878          uint32_t addedEntries = 0;
   1879          browsingContext->PreOrderWalk([&addedEntries](BrowsingContext* aBC) {
   1880            const uint32_t len = aBC->GetHistoryEntryCount();
   1881            // There might not be a SH entry yet, which is fine.
   1882            // The first entry doesn't increase history length, as it's added to
   1883            // it's parent entry
   1884            addedEntries += len > 0 ? len - 1 : 0;
   1885          });
   1886 
   1887          nsID changeID = {};
   1888          if (addedEntries > 0) {
   1889            ChildSHistory* shistory =
   1890                browsingContext->Top()->GetChildSessionHistory();
   1891            if (shistory) {
   1892              changeID = shistory->AddPendingHistoryChange(0, -addedEntries);
   1893            }
   1894          }
   1895          browsingContext->RemoveFromSessionHistory(changeID);
   1896        } else {
   1897          AutoTArray<nsID, 16> ids({browsingContext->GetHistoryID()});
   1898          childSHistory->LegacySHistory()->RemoveEntries(
   1899              ids, childSHistory->Index());
   1900        }
   1901      }
   1902    }
   1903  }
   1904 
   1905  // Let the tree owner know we're gone.
   1906  if (nsCOMPtr<nsIDocShell> ds = GetDocShell()) {
   1907    if (mIsTopLevelContent) {
   1908      nsCOMPtr<nsIDocShellTreeItem> parentItem;
   1909      ds->GetInProcessParent(getter_AddRefs(parentItem));
   1910      if (nsCOMPtr<nsIDocShellTreeOwner> owner = do_GetInterface(parentItem)) {
   1911        owner->ContentShellRemoved(ds);
   1912      }
   1913    }
   1914    if (nsCOMPtr<nsPIDOMWindowOuter> win = ds->GetWindow()) {
   1915      win->SetFrameElementInternal(nullptr);
   1916    }
   1917  }
   1918 
   1919  nsCOMPtr<nsIRunnable> destroyRunnable =
   1920      new nsFrameLoaderDestroyRunnable(this);
   1921  if (mNeedsAsyncDestroy || !doc ||
   1922      NS_FAILED(doc->FinalizeFrameLoader(this, destroyRunnable))) {
   1923    NS_DispatchToCurrentThread(destroyRunnable);
   1924  }
   1925 }
   1926 
   1927 nsresult nsFrameLoaderDestroyRunnable::Run() {
   1928  switch (mPhase) {
   1929    case eDestroyDocShell:
   1930      mFrameLoader->DestroyDocShell();
   1931 
   1932      // In the out-of-process case, BrowserParent will eventually call
   1933      // DestroyComplete once it receives a __delete__ message from the child.
   1934      // In the in-process case, or if the BrowserParent was already destroyed,
   1935      // we dispatch a series of runnables to ensure that DestroyComplete gets
   1936      // called at the right time. The frame loader is kept alive by
   1937      // mFrameLoader during this time.
   1938      if (!mFrameLoader->GetRemoteBrowser() ||
   1939          !mFrameLoader->GetRemoteBrowser()->CanRecv()) {
   1940        // When the docshell is destroyed, NotifyWindowIDDestroyed is called to
   1941        // asynchronously notify {outer,inner}-window-destroyed via a runnable.
   1942        // We don't want DestroyComplete to run until after those runnables have
   1943        // run. Since we're enqueueing ourselves after the window-destroyed
   1944        // runnables are enqueued, we're guaranteed to run after.
   1945        mPhase = eWaitForUnloadMessage;
   1946        NS_DispatchToCurrentThread(this);
   1947      }
   1948      break;
   1949 
   1950    case eWaitForUnloadMessage:
   1951      // The *-window-destroyed observers have finished running at this
   1952      // point. However, it's possible that a *-window-destroyed observer might
   1953      // have sent a message using the message manager. These messages might not
   1954      // have been processed yet. So we enqueue ourselves again to ensure that
   1955      // DestroyComplete runs after all messages sent by *-window-destroyed
   1956      // observers have been processed.
   1957      mPhase = eDestroyComplete;
   1958      NS_DispatchToCurrentThread(this);
   1959      break;
   1960 
   1961    case eDestroyComplete:
   1962      // Now that all messages sent by unload listeners and window destroyed
   1963      // observers have been processed, we disconnect the message manager and
   1964      // finish destruction.
   1965      mFrameLoader->DestroyComplete();
   1966      break;
   1967  }
   1968 
   1969  return NS_OK;
   1970 }
   1971 
   1972 void nsFrameLoader::DestroyDocShell() {
   1973  // This code runs after the frameloader has been detached from the <browser>
   1974  // element. We postpone this work because we may not be allowed to run
   1975  // script at that time.
   1976 
   1977  // Ask the BrowserChild to fire the frame script "unload" event, destroy its
   1978  // docshell, and finally destroy the PBrowser actor. This eventually leads to
   1979  // nsFrameLoader::DestroyComplete being called.
   1980  if (mRemoteBrowser) {
   1981    mRemoteBrowser->DestroyStart();
   1982  }
   1983 
   1984  // Fire the "unload" event if we're in-process.
   1985  if (mChildMessageManager) {
   1986    mChildMessageManager->FireUnloadEvent();
   1987  }
   1988 
   1989  if (mSessionStoreChild) {
   1990    mSessionStoreChild->Stop();
   1991    mSessionStoreChild = nullptr;
   1992  }
   1993 
   1994  // Destroy the docshell.
   1995  if (GetDocShell()) {
   1996    GetDocShell()->Destroy();
   1997  }
   1998 
   1999  if (!mWillChangeProcess && mPendingBrowsingContext &&
   2000      mPendingBrowsingContext->EverAttached()) {
   2001    mPendingBrowsingContext->Detach();
   2002  }
   2003 
   2004  mPendingBrowsingContext = nullptr;
   2005  mDocShell = nullptr;
   2006 
   2007  if (mChildMessageManager) {
   2008    // Stop handling events in the in-process frame script.
   2009    mChildMessageManager->DisconnectEventListeners();
   2010  }
   2011 }
   2012 
   2013 void nsFrameLoader::DestroyComplete() {
   2014  // We get here, as part of StartDestroy, after the docshell has been destroyed
   2015  // and all message manager messages sent during docshell destruction have been
   2016  // dispatched.  We also get here if the child process crashes. In the latter
   2017  // case, StartDestroy might not have been called.
   2018 
   2019  // Drop the strong references created in StartDestroy.
   2020  if (mChildMessageManager || mRemoteBrowser) {
   2021    mOwnerContentStrong = nullptr;
   2022    if (auto* browserParent = GetBrowserParent()) {
   2023      browserParent->CacheFrameLoader(nullptr);
   2024    }
   2025    if (mChildMessageManager) {
   2026      mChildMessageManager->CacheFrameLoader(nullptr);
   2027    }
   2028  }
   2029 
   2030  // Call BrowserParent::Destroy if we haven't already (in case of a crash).
   2031  if (mRemoteBrowser) {
   2032    mRemoteBrowser->DestroyComplete();
   2033    mRemoteBrowser = nullptr;
   2034  }
   2035 
   2036  if (mMessageManager) {
   2037    mMessageManager->Disconnect();
   2038  }
   2039 
   2040  if (mChildMessageManager) {
   2041    mChildMessageManager->Disconnect();
   2042  }
   2043 
   2044  mMessageManager = nullptr;
   2045  mChildMessageManager = nullptr;
   2046 }
   2047 
   2048 void nsFrameLoader::SetOwnerContent(Element* aContent) {
   2049  if (mObservingOwnerContent) {
   2050    mObservingOwnerContent = false;
   2051    mOwnerContent->RemoveMutationObserver(this);
   2052  }
   2053 
   2054  // XXXBFCache Need to update also all the non-current FrameLoaders in the
   2055  // owner when moving a FrameLoader.
   2056  // This temporary setup doesn't crash, but behaves badly with bfcached docs.
   2057  if (RefPtr<nsFrameLoaderOwner> owner = do_QueryObject(mOwnerContent)) {
   2058    owner->DetachFrameLoader(this);
   2059  }
   2060 
   2061  mOwnerContent = aContent;
   2062 
   2063  if (RefPtr<nsFrameLoaderOwner> owner = do_QueryObject(mOwnerContent)) {
   2064    owner->AttachFrameLoader(this);
   2065 
   2066 #ifdef NIGHTLY_BUILD
   2067    if (mozilla::BFCacheInParent() && XRE_IsParentProcess()) {
   2068      if (BrowsingContext* bc = GetMaybePendingBrowsingContext()) {
   2069        nsISHistory* shistory = bc->Canonical()->GetSessionHistory();
   2070        if (shistory) {
   2071          uint32_t count = shistory->GetCount();
   2072          for (uint32_t i = 0; i < count; ++i) {
   2073            nsCOMPtr<nsISHEntry> entry;
   2074            shistory->GetEntryAtIndex(i, getter_AddRefs(entry));
   2075            nsCOMPtr<SessionHistoryEntry> she = do_QueryInterface(entry);
   2076            MOZ_RELEASE_ASSERT(!she || !she->GetFrameLoader());
   2077          }
   2078        }
   2079      }
   2080    }
   2081 #endif
   2082  }
   2083 
   2084  if (mSessionStoreChild && mOwnerContent) {
   2085    // mOwnerContent will only be null when the frame loader is being destroyed,
   2086    // so the session store listener will be destroyed along with it.
   2087    // XXX(farre): This probably needs to update the cache. See bug 1698497.
   2088    mSessionStoreChild->SetOwnerContent(mOwnerContent);
   2089  }
   2090 
   2091  if (RefPtr<BrowsingContext> browsingContext = GetExtantBrowsingContext()) {
   2092    browsingContext->SetEmbedderElement(mOwnerContent);
   2093  }
   2094 
   2095  if (mSessionStoreChild) {
   2096    // UpdateEventTargets will requery its browser contexts for event
   2097    // targets, so this call needs to happen after the call to
   2098    // SetEmbedderElement above.
   2099    mSessionStoreChild->UpdateEventTargets();
   2100  }
   2101 
   2102  AutoJSAPI jsapi;
   2103  jsapi.Init();
   2104 
   2105  JS::Rooted<JSObject*> wrapper(jsapi.cx(), GetWrapper());
   2106  if (wrapper) {
   2107    JSAutoRealm ar(jsapi.cx(), wrapper);
   2108    IgnoredErrorResult rv;
   2109    UpdateReflectorGlobal(jsapi.cx(), wrapper, rv);
   2110    (void)NS_WARN_IF(rv.Failed());
   2111  }
   2112 }
   2113 
   2114 nsIContent* nsFrameLoader::GetParentObject() const { return mOwnerContent; }
   2115 
   2116 void nsFrameLoader::AssertSafeToInit() {
   2117  MOZ_DIAGNOSTIC_ASSERT(nsContentUtils::IsSafeToRunScript() ||
   2118                            mOwnerContent->OwnerDoc()->IsStaticDocument(),
   2119                        "FrameLoader should never be initialized during "
   2120                        "document update or reflow!");
   2121 }
   2122 
   2123 nsresult nsFrameLoader::MaybeCreateDocShell() {
   2124  if (GetDocShell()) {
   2125    return NS_OK;
   2126  }
   2127  if (IsRemoteFrame()) {
   2128    return NS_OK;
   2129  }
   2130  NS_ENSURE_STATE(!mDestroyCalled);
   2131 
   2132  AssertSafeToInit();
   2133 
   2134  // Get our parent docshell off the document of mOwnerContent
   2135  // XXXbz this is such a total hack.... We really need to have a
   2136  // better setup for doing this.
   2137  Document* doc = mOwnerContent->OwnerDoc();
   2138 
   2139  MOZ_RELEASE_ASSERT(!doc->IsResourceDoc(), "We shouldn't even exist");
   2140 
   2141  // If we've already tried to initialize and failed, don't try again.
   2142  if (mInitialized) {
   2143    return NS_ERROR_NOT_AVAILABLE;
   2144  }
   2145  mInitialized = true;
   2146 
   2147  // Check if the document still has a window since it is possible for an
   2148  // iframe to be inserted and cause the creation of the docshell in a
   2149  // partially unloaded document (see Bug 1305237 comment 127).
   2150  if (!doc->IsStaticDocument() &&
   2151      (!doc->GetWindow() || !mOwnerContent->IsInComposedDoc())) {
   2152    return NS_ERROR_UNEXPECTED;
   2153  }
   2154 
   2155  if (!doc->IsActive()) {
   2156    // Don't allow subframe loads in non-active documents.
   2157    // (See bug 610571 comment 5.)
   2158    return NS_ERROR_NOT_AVAILABLE;
   2159  }
   2160 
   2161  RefPtr<nsDocShell> parentDocShell = nsDocShell::Cast(doc->GetDocShell());
   2162  if (NS_WARN_IF(!parentDocShell)) {
   2163    return NS_ERROR_UNEXPECTED;
   2164  }
   2165 
   2166  if (doc->GetWindowContext()->IsDiscarded() ||
   2167      parentDocShell->GetBrowsingContext()->IsDiscarded()) {
   2168    // Don't allow subframe loads in discarded contexts.
   2169    // (see bug 1652085, bug 1656854)
   2170    return NS_ERROR_NOT_AVAILABLE;
   2171  }
   2172 
   2173  if (!EnsureBrowsingContextAttached()) {
   2174    return NS_ERROR_FAILURE;
   2175  }
   2176 
   2177  mPendingBrowsingContext->SetEmbedderElement(mOwnerContent);
   2178 
   2179  // nsDocShell::Create will attach itself to the passed browsing
   2180  // context inside of nsDocShell::Create
   2181  RefPtr<nsDocShell> docShell = nsDocShell::Create(mPendingBrowsingContext);
   2182  NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
   2183  mDocShell = docShell;
   2184 
   2185  mPendingBrowsingContext->Embed();
   2186 
   2187  InvokeBrowsingContextReadyCallback();
   2188 
   2189  mIsTopLevelContent = mPendingBrowsingContext->IsTopContent();
   2190 
   2191  if (mIsTopLevelContent) {
   2192    // Manually add ourselves to our parent's docshell, as BrowsingContext won't
   2193    // have done this for us.
   2194    //
   2195    // XXX(nika): Consider removing the DocShellTree in the future, for
   2196    // consistency between local and remote frames..
   2197    parentDocShell->AddChild(docShell);
   2198  }
   2199 
   2200  // Now that we are part of the DocShellTree, attach our DocShell to our
   2201  // parent's TreeOwner.
   2202  nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner;
   2203  parentDocShell->GetTreeOwner(getter_AddRefs(parentTreeOwner));
   2204  AddTreeItemToTreeOwner(docShell, parentTreeOwner);
   2205 
   2206  // Make sure all nsDocShells have links back to the content element in the
   2207  // nearest enclosing chrome shell.
   2208  RefPtr<EventTarget> chromeEventHandler;
   2209  bool parentIsContent = parentDocShell->GetBrowsingContext()->IsContent();
   2210  if (parentIsContent) {
   2211    // Our parent shell is a content shell. Get the chrome event handler from it
   2212    // and use that for our shell as well.
   2213    parentDocShell->GetChromeEventHandler(getter_AddRefs(chromeEventHandler));
   2214  } else {
   2215    // Our parent shell is a chrome shell. It is therefore our nearest enclosing
   2216    // chrome shell.
   2217    chromeEventHandler = mOwnerContent;
   2218  }
   2219 
   2220  docShell->SetChromeEventHandler(chromeEventHandler);
   2221 
   2222  // This is nasty, this code (the docShell->GetWindow() below)
   2223  // *must* come *after* the above call to
   2224  // docShell->SetChromeEventHandler() for the global window to get
   2225  // the right chrome event handler.
   2226 
   2227  // Tell the window about the frame that hosts it.
   2228  nsCOMPtr<nsPIDOMWindowOuter> newWindow = docShell->GetWindow();
   2229  if (NS_WARN_IF(!newWindow)) {
   2230    // Do not call Destroy() here. See bug 472312.
   2231    NS_WARNING("Something wrong when creating the docshell for a frameloader!");
   2232    // The docshell isn't completely initialized. Clear it so that it isn't
   2233    // reachable. Future calls to MaybeCreateDocShell will fail due to
   2234    // mInitialized.
   2235    mDocShell = nullptr;
   2236    return NS_ERROR_FAILURE;
   2237  }
   2238 
   2239  newWindow->SetFrameElementInternal(mOwnerContent);
   2240 
   2241  // Allow scripts to close the docshell if specified.
   2242  if (mOwnerContent->IsXULElement(nsGkAtoms::browser) &&
   2243      mOwnerContent->AttrValueIs(kNameSpaceID_None,
   2244                                 nsGkAtoms::allowscriptstoclose,
   2245                                 nsGkAtoms::_true, eCaseMatters)) {
   2246    nsGlobalWindowOuter::Cast(newWindow)->AllowScriptsToClose();
   2247  }
   2248 
   2249  NS_ENSURE_STATE(mOwnerContent);
   2250 
   2251  // If we are an in-process browser, we want to set up our session history.
   2252  if (mIsTopLevelContent && mOwnerContent->IsXULElement(nsGkAtoms::browser) &&
   2253      !mOwnerContent->HasAttr(nsGkAtoms::disablehistory)) {
   2254    // XXX(nika): Set this up more explicitly?
   2255    mPendingBrowsingContext->InitSessionHistory();
   2256  }
   2257 
   2258  // Apply sandbox flags even if our owner is not an iframe, as this copies
   2259  // flags from our owning content's owning document.
   2260  // Note: ApplySandboxFlags should be called after docShell->SetIsFrame
   2261  // because we need to get the correct presentation URL in ApplySandboxFlags.
   2262  uint32_t sandboxFlags = 0;
   2263  HTMLIFrameElement* iframe = HTMLIFrameElement::FromNode(mOwnerContent);
   2264  if (iframe) {
   2265    sandboxFlags = iframe->GetSandboxFlags();
   2266  }
   2267  ApplySandboxFlags(sandboxFlags);
   2268  MOZ_ALWAYS_SUCCEEDS(mPendingBrowsingContext->SetInitialSandboxFlags(
   2269      mPendingBrowsingContext->GetSandboxFlags()));
   2270 
   2271  // Gather things to inherit into the initial about:blank
   2272 
   2273  // For HTML [i]frames and objects, perform the inheritance here. (It would
   2274  // probably be more proper to hoist this to each call site of
   2275  // nsFrameLoader::Create.)
   2276  nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
   2277  nsCOMPtr<nsIPrincipal> partitionedPrincipal = doc->PartitionedPrincipal();
   2278 
   2279  // We use mOpenWindowInfo so that JS can force a principal onto us
   2280  if (mOpenWindowInfo && mOpenWindowInfo->PrincipalToInheritForAboutBlank()) {
   2281    principal = mOpenWindowInfo->PrincipalToInheritForAboutBlank();
   2282    partitionedPrincipal =
   2283        mOpenWindowInfo->PartitionedPrincipalToInheritForAboutBlank();
   2284  }
   2285 
   2286  if ((mPendingBrowsingContext->IsContent() || XRE_IsContentProcess()) &&
   2287      (!principal || principal->IsSystemPrincipal())) {
   2288    // Never inherit system principal to a content HTML [i]frame.
   2289    principal = NullPrincipal::Create(
   2290        mPendingBrowsingContext->OriginAttributesRef(), nullptr);
   2291    partitionedPrincipal = principal;
   2292  }
   2293 
   2294  RefPtr<nsOpenWindowInfo> openWindowInfo = new nsOpenWindowInfo();
   2295  openWindowInfo->mPrincipalToInheritForAboutBlank = principal.forget();
   2296  openWindowInfo->mPartitionedPrincipalToInheritForAboutBlank =
   2297      partitionedPrincipal.forget();
   2298  openWindowInfo->mPolicyContainerToInheritForAboutBlank =
   2299      doc->GetPolicyContainer();
   2300  openWindowInfo->mCoepToInheritForAboutBlank = doc->GetEmbedderPolicy();
   2301  openWindowInfo->mBaseUriToInheritForAboutBlank = mOwnerContent->GetBaseURI();
   2302  if (!docShell->Initialize(openWindowInfo, nullptr)) {
   2303    // Do not call Destroy() here. See bug 472312.
   2304    NS_WARNING("Something wrong when creating the docshell for a frameloader!");
   2305    return NS_ERROR_FAILURE;
   2306  }
   2307 
   2308  ReallyLoadFrameScripts();
   2309 
   2310  // Previously, the lazy about:blank creation had the effect of running
   2311  // nsGlobalWindowOuter::DispatchDOMWindowCreated, which sets up the message
   2312  // manager, after ReallyLoadFrameScripts(). We can't achieve the same by using
   2313  // a script blocker while calling `docShell->Initialize()`, because the
   2314  // initialization expects to be able to assert that scripts are allowed to
   2315  // run. Therefore, let's fix up the message manager setup here.
   2316  if (Document* doc = docShell->GetDocument()) {
   2317    if (nsPIDOMWindowOuter* window = doc->GetWindow()) {
   2318      window->UpdateParentTarget();
   2319    }
   2320  }
   2321 
   2322  return NS_OK;
   2323 }
   2324 
   2325 void nsFrameLoader::GetURL(nsString& aURL, nsIPrincipal** aTriggeringPrincipal,
   2326                           nsIPolicyContainer** aPolicyContainer) {
   2327  aURL.Truncate();
   2328  // Within this function we default to using the NodePrincipal as the
   2329  // triggeringPrincipal and the CSP of the document.
   2330  // Expanded Principals however override the CSP of the document, hence
   2331  // if frame->GetSrcTriggeringPrincipal() returns a valid principal, we
   2332  // have to query the CSP from that Principal.
   2333  nsCOMPtr<nsIPrincipal> triggeringPrincipal = mOwnerContent->NodePrincipal();
   2334  nsCOMPtr<nsIPolicyContainer> policyContainer =
   2335      mOwnerContent->GetPolicyContainer();
   2336 
   2337  if (mOwnerContent->IsHTMLElement(nsGkAtoms::object)) {
   2338    mOwnerContent->GetAttr(nsGkAtoms::data, aURL);
   2339  } else {
   2340    mOwnerContent->GetAttr(nsGkAtoms::src, aURL);
   2341    if (RefPtr<nsGenericHTMLFrameElement> frame =
   2342            do_QueryObject(mOwnerContent)) {
   2343      nsCOMPtr<nsIPrincipal> srcPrincipal = frame->GetSrcTriggeringPrincipal();
   2344      if (srcPrincipal) {
   2345        triggeringPrincipal = srcPrincipal;
   2346        nsCOMPtr<nsIExpandedPrincipal> ep =
   2347            do_QueryInterface(triggeringPrincipal);
   2348        if (ep) {
   2349          // Bug 1548468: Move CSP off ExpandedPrincipal
   2350          RefPtr<PolicyContainer> addonPolicyContainer;
   2351          if (nsCOMPtr<nsIContentSecurityPolicy> addonCSP = ep->GetCsp()) {
   2352            addonPolicyContainer = new PolicyContainer();
   2353            addonPolicyContainer->SetCSP(addonCSP);
   2354          }
   2355          policyContainer = addonPolicyContainer.forget();
   2356        }
   2357      }
   2358    }
   2359  }
   2360  triggeringPrincipal.forget(aTriggeringPrincipal);
   2361  policyContainer.forget(aPolicyContainer);
   2362 }
   2363 
   2364 nsresult nsFrameLoader::CheckForRecursiveLoad(nsIURI* aURI) {
   2365  MOZ_ASSERT(!IsRemoteFrame(),
   2366             "Shouldn't call CheckForRecursiveLoad on remote frames.");
   2367 
   2368  mDepthTooGreat = false;
   2369  RefPtr<BrowsingContext> parentBC(
   2370      mOwnerContent->OwnerDoc()->GetBrowsingContext());
   2371  NS_ENSURE_STATE(parentBC);
   2372 
   2373  if (!parentBC->IsContent()) {
   2374    return NS_OK;
   2375  }
   2376 
   2377  // Bug 8065: Don't exceed some maximum depth in content frames
   2378  // (MAX_DEPTH_CONTENT_FRAMES)
   2379  int32_t depth = 0;
   2380  for (BrowsingContext* bc = parentBC; bc; bc = bc->GetParent()) {
   2381    ++depth;
   2382    if (depth >= MAX_DEPTH_CONTENT_FRAMES) {
   2383      mDepthTooGreat = true;
   2384      NS_WARNING("Too many nested content frames so giving up");
   2385 
   2386      return NS_ERROR_UNEXPECTED;  // Too deep, give up!  (silently?)
   2387    }
   2388  }
   2389 
   2390  return NS_OK;
   2391 }
   2392 
   2393 nsresult nsFrameLoader::GetWindowDimensions(LayoutDeviceIntRect& aRect) {
   2394  if (!mOwnerContent) {
   2395    return NS_ERROR_FAILURE;
   2396  }
   2397 
   2398  // Need to get outer window position here
   2399  Document* doc = mOwnerContent->GetComposedDoc();
   2400  if (!doc) {
   2401    return NS_ERROR_FAILURE;
   2402  }
   2403 
   2404  MOZ_RELEASE_ASSERT(!doc->IsResourceDoc(), "We shouldn't even exist");
   2405 
   2406  nsCOMPtr<nsPIDOMWindowOuter> win = doc->GetWindow();
   2407  if (!win) {
   2408    return NS_ERROR_FAILURE;
   2409  }
   2410 
   2411  nsCOMPtr<nsIDocShellTreeItem> parentAsItem(win->GetDocShell());
   2412  if (!parentAsItem) {
   2413    return NS_ERROR_FAILURE;
   2414  }
   2415 
   2416  nsCOMPtr<nsIDocShellTreeOwner> parentOwner;
   2417  if (NS_FAILED(parentAsItem->GetTreeOwner(getter_AddRefs(parentOwner))) ||
   2418      !parentOwner) {
   2419    return NS_ERROR_FAILURE;
   2420  }
   2421 
   2422  nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_GetInterface(parentOwner));
   2423  aRect.MoveTo(treeOwnerAsWin->GetPosition());
   2424  aRect.SizeTo(treeOwnerAsWin->GetSize());
   2425  return NS_OK;
   2426 }
   2427 
   2428 nsresult nsFrameLoader::UpdatePositionAndSize(nsSubDocumentFrame* aFrame) {
   2429  const auto size = aFrame->GetSubdocumentSize();
   2430  mLazySize = size;
   2431 
   2432  if (IsRemoteFrame()) {
   2433    if (mRemoteBrowser) {
   2434      // If we were not able to show remote frame before, we should probably
   2435      // retry now to send correct showInfo.
   2436      if (!mRemoteBrowserShown) {
   2437        ShowRemoteFrame(aFrame);
   2438      }
   2439      LayoutDeviceIntRect dimensions;
   2440      MOZ_TRY(GetWindowDimensions(dimensions));
   2441      mRemoteBrowser->UpdateDimensions(dimensions, size);
   2442      mRemoteBrowserSized = true;
   2443    }
   2444    return NS_OK;
   2445  }
   2446  nsCOMPtr<nsIBaseWindow> baseWindow = GetDocShell(IgnoreErrors());
   2447  if (!baseWindow) {
   2448    return NS_OK;
   2449  }
   2450  int32_t x = 0;
   2451  int32_t y = 0;
   2452 
   2453  AutoWeakFrame weakFrame(aFrame);
   2454  baseWindow->GetPosition(&x, &y);
   2455 
   2456  if (!weakFrame.IsAlive()) {
   2457    // GetPosition() killed us
   2458    return NS_OK;
   2459  }
   2460  baseWindow->SetPositionAndSize(x, y, size.width, size.height,
   2461                                 nsIBaseWindow::eDelayResize);
   2462  return NS_OK;
   2463 }
   2464 
   2465 void nsFrameLoader::PropagateIsUnderHiddenEmbedderElement(
   2466    bool aIsUnderHiddenEmbedderElement) {
   2467  bool isUnderHiddenEmbedderElement = true;
   2468  if (Document* ownerDoc = GetOwnerDoc()) {
   2469    if (PresShell* presShell = ownerDoc->GetPresShell()) {
   2470      isUnderHiddenEmbedderElement = presShell->IsUnderHiddenEmbedderElement();
   2471    }
   2472  }
   2473 
   2474  isUnderHiddenEmbedderElement |= aIsUnderHiddenEmbedderElement;
   2475 
   2476  BrowsingContext* browsingContext = GetExtantBrowsingContext();
   2477  if (browsingContext && browsingContext->IsUnderHiddenEmbedderElement() !=
   2478                             isUnderHiddenEmbedderElement) {
   2479    (void)browsingContext->SetIsUnderHiddenEmbedderElement(
   2480        isUnderHiddenEmbedderElement);
   2481  }
   2482 }
   2483 
   2484 void nsFrameLoader::UpdateRemoteStyle(
   2485    mozilla::StyleImageRendering aImageRendering) {
   2486  MOZ_DIAGNOSTIC_ASSERT(IsRemoteFrame());
   2487 
   2488  if (auto* browserBridgeChild = GetBrowserBridgeChild()) {
   2489    browserBridgeChild->SendUpdateRemoteStyle(aImageRendering);
   2490  }
   2491 }
   2492 
   2493 uint32_t nsFrameLoader::LazyWidth() const {
   2494  uint32_t lazyWidth = mLazySize.width;
   2495  if (nsIFrame* frame = GetPrimaryFrameOfOwningContent()) {
   2496    lazyWidth = frame->PresContext()->DevPixelsToIntCSSPixels(lazyWidth);
   2497  }
   2498  return lazyWidth;
   2499 }
   2500 
   2501 uint32_t nsFrameLoader::LazyHeight() const {
   2502  uint32_t lazyHeight = mLazySize.height;
   2503  if (nsIFrame* frame = GetPrimaryFrameOfOwningContent()) {
   2504    lazyHeight = frame->PresContext()->DevPixelsToIntCSSPixels(lazyHeight);
   2505  }
   2506  return lazyHeight;
   2507 }
   2508 
   2509 bool nsFrameLoader::EnsureRemoteBrowser() {
   2510  MOZ_ASSERT(IsRemoteFrame());
   2511  return mRemoteBrowser || TryRemoteBrowser();
   2512 }
   2513 
   2514 bool nsFrameLoader::TryRemoteBrowserInternal() {
   2515  NS_ASSERTION(!mRemoteBrowser,
   2516               "TryRemoteBrowser called with a remote browser already?");
   2517  MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess(),
   2518                        "Remote subframes should only be created using the "
   2519                        "`CanonicalBrowsingContext::ChangeRemoteness` API");
   2520 
   2521  AssertSafeToInit();
   2522 
   2523  if (!mOwnerContent) {
   2524    return false;
   2525  }
   2526 
   2527  // XXXsmaug Per spec (2014/08/21) frameloader should not work in case the
   2528  //         element isn't in document, only in shadow dom, but that will change
   2529  //         https://www.w3.org/Bugs/Public/show_bug.cgi?id=26365#c0
   2530  RefPtr<Document> doc = mOwnerContent->GetComposedDoc();
   2531  if (!doc) {
   2532    return false;
   2533  }
   2534 
   2535  MOZ_RELEASE_ASSERT(!doc->IsResourceDoc(), "We shouldn't even exist");
   2536 
   2537  // Graphics initialization code relies on having a frame for the
   2538  // remote browser case, as we can be inside a popup, which is a different
   2539  // widget.
   2540 
   2541  if (!mOwnerContent->GetPrimaryFrame()) {
   2542    doc->FlushPendingNotifications(FlushType::Frames);
   2543  }
   2544 
   2545  // The flush could have initialized us.
   2546  if (mRemoteBrowser) {
   2547    return true;
   2548  }
   2549 
   2550  // If we've already tried to initialize and failed, don't try again.
   2551  if (mInitialized) {
   2552    return false;
   2553  }
   2554  mInitialized = true;
   2555 
   2556  // Ensure the world hasn't changed that much as a result of that.
   2557  if (!mOwnerContent || mOwnerContent->OwnerDoc() != doc ||
   2558      !mOwnerContent->IsInComposedDoc()) {
   2559    return false;
   2560  }
   2561 
   2562  if (RefPtr<nsFrameLoaderOwner> flo = do_QueryObject(mOwnerContent)) {
   2563    RefPtr<nsFrameLoader> fl = flo->GetFrameLoader();
   2564    if (fl != this) {
   2565      MOZ_ASSERT_UNREACHABLE(
   2566          "Got TryRemoteBrowserInternal but mOwnerContent already has a "
   2567          "different frameloader?");
   2568      return false;
   2569    }
   2570  }
   2571 
   2572  if (!doc->IsActive()) {
   2573    // Don't allow subframe loads in non-active documents.
   2574    // (See bug 610571 comment 5.)
   2575    return false;
   2576  }
   2577 
   2578  nsCOMPtr<nsPIDOMWindowOuter> parentWin = doc->GetWindow();
   2579  if (!parentWin) {
   2580    return false;
   2581  }
   2582 
   2583  nsCOMPtr<nsIDocShell> parentDocShell = parentWin->GetDocShell();
   2584  if (!parentDocShell) {
   2585    return false;
   2586  }
   2587 
   2588  if (!EnsureBrowsingContextAttached()) {
   2589    return false;
   2590  }
   2591 
   2592  if (mPendingBrowsingContext->IsTop()) {
   2593    mPendingBrowsingContext->InitSessionHistory();
   2594  }
   2595 
   2596  // iframes for JS plugins also get to skip these checks. We control the URL
   2597  // that gets loaded, but the load is triggered from the document containing
   2598  // the plugin.
   2599  // out of process iframes also get to skip this check.
   2600  if (!XRE_IsContentProcess()) {
   2601    if (parentDocShell->ItemType() != nsIDocShellTreeItem::typeChrome) {
   2602      // Allow three exceptions to this rule :
   2603      // - about:addons so it can load remote extension options pages
   2604      // - about:preferences (in Thunderbird only) so it can load remote
   2605      //     extension options pages for FileLink providers
   2606      // - DevTools webext panels if DevTools is loaded in a content frame
   2607      // - DevTools Network Monitor, which uses content frame for HTML request
   2608      // previews
   2609      // - Chrome mochitests can also do this.
   2610      //
   2611      // Note that the new frame's message manager will not be a child of the
   2612      // chrome window message manager, and, the values of window.top and
   2613      // window.parent will be different than they would be for a non-remote
   2614      // frame.
   2615      nsIURI* parentURI = parentWin->GetDocumentURI();
   2616      if (!parentURI) {
   2617        return false;
   2618      }
   2619 
   2620      nsAutoCString specIgnoringRef;
   2621      if (NS_FAILED(parentURI->GetSpecIgnoringRef(specIgnoringRef))) {
   2622        return false;
   2623      }
   2624 
   2625      const bool allowed = [&] {
   2626        const nsLiteralCString kAllowedURIs[] = {
   2627            "about:addons"_ns,
   2628            "chrome://mozapps/content/extensions/aboutaddons.html"_ns,
   2629 #ifdef MOZ_THUNDERBIRD
   2630            "about:3pane"_ns,
   2631            "about:message"_ns,
   2632            "about:preferences"_ns,
   2633 #endif
   2634            "chrome://browser/content/webext-panels.xhtml"_ns,
   2635            "chrome://devtools/content/netmonitor/index.html"_ns,
   2636            "chrome://devtools/content/webconsole/index.html"_ns,
   2637        };
   2638 
   2639        for (const auto& allowedURI : kAllowedURIs) {
   2640          if (specIgnoringRef.Equals(allowedURI)) {
   2641            return true;
   2642          }
   2643        }
   2644 
   2645        if (xpc::IsInAutomation() &&
   2646            StringBeginsWith(specIgnoringRef,
   2647                             "chrome://mochitests/content/chrome/"_ns)) {
   2648          return true;
   2649        }
   2650        return false;
   2651      }();
   2652 
   2653      if (!allowed) {
   2654        NS_WARNING(
   2655            nsPrintfCString("Forbidden remote frame from content docshell %s",
   2656                            specIgnoringRef.get())
   2657                .get());
   2658        return false;
   2659      }
   2660    }
   2661 
   2662    if (!mOwnerContent->IsXULElement()) {
   2663      return false;
   2664    }
   2665 
   2666    if (!mOwnerContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
   2667                                    nsGkAtoms::content, eIgnoreCase)) {
   2668      return false;
   2669    }
   2670  }
   2671 
   2672  uint32_t chromeFlags = 0;
   2673  nsCOMPtr<nsIDocShellTreeOwner> parentOwner;
   2674  if (NS_FAILED(parentDocShell->GetTreeOwner(getter_AddRefs(parentOwner))) ||
   2675      !parentOwner) {
   2676    return false;
   2677  }
   2678  nsCOMPtr<nsIAppWindow> window(do_GetInterface(parentOwner));
   2679  if (window && NS_FAILED(window->GetChromeFlags(&chromeFlags))) {
   2680    return false;
   2681  }
   2682 
   2683  AUTO_PROFILER_LABEL("nsFrameLoader::TryRemoteBrowser:Create", OTHER);
   2684 
   2685  MutableTabContext context;
   2686  nsresult rv = GetNewTabContext(&context);
   2687  NS_ENSURE_SUCCESS(rv, false);
   2688 
   2689  RefPtr<Element> ownerElement = mOwnerContent;
   2690 
   2691  RefPtr<BrowserParent> nextRemoteBrowser =
   2692      mOpenWindowInfo ? mOpenWindowInfo->GetNextRemoteBrowser() : nullptr;
   2693  if (nextRemoteBrowser) {
   2694    mRemoteBrowser = new BrowserHost(nextRemoteBrowser);
   2695    if (nextRemoteBrowser->GetOwnerElement()) {
   2696      MOZ_ASSERT_UNREACHABLE("Shouldn't have an owner element before");
   2697      return false;
   2698    }
   2699    nextRemoteBrowser->SetOwnerElement(ownerElement);
   2700  } else {
   2701    RefPtr<ContentParent> contentParent;
   2702    if (mChildID != 0) {
   2703      ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
   2704      if (!cpm) {
   2705        return false;
   2706      }
   2707      contentParent = cpm->GetContentProcessById(ContentParentId(mChildID));
   2708    }
   2709    mRemoteBrowser =
   2710        ContentParent::CreateBrowser(context, ownerElement, mRemoteType,
   2711                                     mPendingBrowsingContext, contentParent);
   2712  }
   2713  if (!mRemoteBrowser) {
   2714    return false;
   2715  }
   2716 
   2717  MOZ_DIAGNOSTIC_ASSERT(mPendingBrowsingContext ==
   2718                        mRemoteBrowser->GetBrowsingContext());
   2719 
   2720  mRemoteBrowser->GetBrowsingContext()->Embed();
   2721  InvokeBrowsingContextReadyCallback();
   2722 
   2723  // Grab the reference to the actor
   2724  RefPtr<BrowserParent> browserParent = GetBrowserParent();
   2725 
   2726  MOZ_ASSERT(browserParent->CanSend(), "BrowserParent cannot send?");
   2727 
   2728  // We no longer need the remoteType attribute on the frame element.
   2729  // The remoteType can be queried by asking the message manager instead.
   2730  ownerElement->UnsetAttr(kNameSpaceID_None, nsGkAtoms::RemoteType, false);
   2731 
   2732  // Now that browserParent is set, we can initialize graphics
   2733  browserParent->InitRendering();
   2734 
   2735  MaybeUpdatePrimaryBrowserParent(eBrowserParentChanged);
   2736 
   2737  mChildID = browserParent->Manager()->ChildID();
   2738 
   2739  nsCOMPtr<nsIDocShellTreeItem> rootItem;
   2740  parentDocShell->GetInProcessRootTreeItem(getter_AddRefs(rootItem));
   2741  RefPtr<nsGlobalWindowOuter> rootWin =
   2742      nsGlobalWindowOuter::Cast(rootItem->GetWindow());
   2743 
   2744  if (rootWin && rootWin->IsChromeWindow()) {
   2745    browserParent->SetBrowserDOMWindow(rootWin->GetBrowserDOMWindow());
   2746  }
   2747 
   2748  // For xul:browsers, update some settings based on attributes:
   2749  if (mOwnerContent->IsXULElement()) {
   2750    // Send down the name of the browser through browserParent if it is set.
   2751    nsAutoString frameName;
   2752    mOwnerContent->GetAttr(nsGkAtoms::name, frameName);
   2753    if (nsContentUtils::IsOverridingWindowName(frameName)) {
   2754      MOZ_ALWAYS_SUCCEEDS(mPendingBrowsingContext->SetName(frameName));
   2755    }
   2756    // Allow scripts to close the window if the browser specified so:
   2757    if (mOwnerContent->AttrValueIs(kNameSpaceID_None,
   2758                                   nsGkAtoms::allowscriptstoclose,
   2759                                   nsGkAtoms::_true, eCaseMatters)) {
   2760      (void)browserParent->SendAllowScriptsToClose();
   2761    }
   2762  }
   2763 
   2764  ReallyLoadFrameScripts();
   2765 
   2766  return true;
   2767 }
   2768 
   2769 bool nsFrameLoader::TryRemoteBrowser() {
   2770  // NOTE: Do not add new checks to this function, it exists to ensure we always
   2771  // MaybeNotifyCrashed for any errors within TryRemoteBrowserInternal. If new
   2772  // checks are added, they should be added into that function, not here.
   2773 
   2774  // Try to create the internal remote browser.
   2775  if (TryRemoteBrowserInternal()) {
   2776    return true;
   2777  }
   2778 
   2779  // We shouldn't TryRemoteBrowser again, even if a check failed before we
   2780  // initialize mInitialized within TryRemoteBrowserInternal.
   2781  mInitialized = true;
   2782 
   2783  // Check if we should report a browser-crashed error because the browser
   2784  // failed to start.
   2785  if (XRE_IsParentProcess() && mOwnerContent && mOwnerContent->IsXULElement()) {
   2786    MaybeNotifyCrashed(nullptr, ContentParentId(), nullptr);
   2787  }
   2788 
   2789  return false;
   2790 }
   2791 
   2792 nsIFrame* nsFrameLoader::GetPrimaryFrameOfOwningContent() const {
   2793  return mOwnerContent ? mOwnerContent->GetPrimaryFrame() : nullptr;
   2794 }
   2795 
   2796 Document* nsFrameLoader::GetOwnerDoc() const {
   2797  return mOwnerContent ? mOwnerContent->OwnerDoc() : nullptr;
   2798 }
   2799 
   2800 BrowserParent* nsFrameLoader::GetBrowserParent() const {
   2801  if (!mRemoteBrowser) {
   2802    return nullptr;
   2803  }
   2804  RefPtr<BrowserHost> browserHost = mRemoteBrowser->AsBrowserHost();
   2805  if (!browserHost) {
   2806    return nullptr;
   2807  }
   2808  return browserHost->GetActor();
   2809 }
   2810 
   2811 BrowserBridgeChild* nsFrameLoader::GetBrowserBridgeChild() const {
   2812  if (!mRemoteBrowser) {
   2813    return nullptr;
   2814  }
   2815  RefPtr<BrowserBridgeHost> browserBridgeHost =
   2816      mRemoteBrowser->AsBrowserBridgeHost();
   2817  if (!browserBridgeHost) {
   2818    return nullptr;
   2819  }
   2820  return browserBridgeHost->GetActor();
   2821 }
   2822 
   2823 mozilla::layers::LayersId nsFrameLoader::GetLayersId() const {
   2824  MOZ_ASSERT(mIsRemoteFrame);
   2825  return mRemoteBrowser->GetLayersId();
   2826 }
   2827 
   2828 nsresult nsFrameLoader::DoRemoteStaticClone(nsFrameLoader* aStaticCloneOf,
   2829                                            nsIPrintSettings* aPrintSettings) {
   2830  MOZ_ASSERT(aStaticCloneOf->IsRemoteFrame());
   2831  MOZ_ASSERT(aPrintSettings);
   2832  auto* cc = ContentChild::GetSingleton();
   2833  if (!cc) {
   2834    MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
   2835    // TODO: Could possibly be implemented without too much effort.
   2836    return NS_ERROR_NOT_IMPLEMENTED;
   2837  }
   2838  BrowsingContext* bcToClone = aStaticCloneOf->GetBrowsingContext();
   2839  if (NS_WARN_IF(!bcToClone)) {
   2840    return NS_ERROR_UNEXPECTED;
   2841  }
   2842  BrowsingContext* bc = GetBrowsingContext();
   2843  MOZ_DIAGNOSTIC_ASSERT(bc);
   2844  nsCOMPtr<nsIPrintSettingsService> printSettingsSvc =
   2845      do_GetService("@mozilla.org/gfx/printsettings-service;1");
   2846  if (NS_WARN_IF(!printSettingsSvc)) {
   2847    return NS_ERROR_UNEXPECTED;
   2848  }
   2849  embedding::PrintData printData;
   2850  nsresult rv =
   2851      printSettingsSvc->SerializeToPrintData(aPrintSettings, &printData);
   2852  if (NS_WARN_IF(NS_FAILED(rv))) {
   2853    return rv;
   2854  }
   2855 
   2856  cc->SendCloneDocumentTreeInto(bcToClone, bc, printData);
   2857  return NS_OK;
   2858 }
   2859 
   2860 nsresult nsFrameLoader::FinishStaticClone(
   2861    nsFrameLoader* aStaticCloneOf, nsIPrintSettings* aPrintSettings,
   2862    bool* aOutHasInProcessPrintCallbacks) {
   2863  MOZ_DIAGNOSTIC_ASSERT(
   2864      !nsContentUtils::IsSafeToRunScript(),
   2865      "A script blocker should be on the stack while FinishStaticClone is run");
   2866 
   2867  // NOTE: We don't check `aStaticCloneOf->IsDead()` here, as the nsFrameLoader
   2868  // which we're a static clone of may be in the process of being destroyed. It
   2869  // won't be fully destroyed when `FinishStaticClone` is called, as a script
   2870  // blocker on our caller's stack is preventing it from becoming finalized.
   2871  //
   2872  // This is quite fragile, but is quite difficult to work around without
   2873  // getting print-preview to stop re-cloning and replacing the previewed
   2874  // document when changing layout.
   2875  if (NS_WARN_IF(IsDead())) {
   2876    return NS_ERROR_UNEXPECTED;
   2877  }
   2878 
   2879  if (aStaticCloneOf->IsRemoteFrame()) {
   2880    return DoRemoteStaticClone(aStaticCloneOf, aPrintSettings);
   2881  }
   2882 
   2883  nsIDocShell* origDocShell = aStaticCloneOf->GetDocShell();
   2884  NS_ENSURE_STATE(origDocShell);
   2885 
   2886  nsCOMPtr<Document> doc = origDocShell->GetDocument();
   2887  NS_ENSURE_STATE(doc);
   2888 
   2889  MaybeCreateDocShell();
   2890  RefPtr<nsDocShell> docShell = GetDocShell();
   2891  NS_ENSURE_STATE(docShell);
   2892 
   2893  nsCOMPtr<Document> kungFuDeathGrip = docShell->GetDocument();
   2894  (void)kungFuDeathGrip;
   2895 
   2896  nsCOMPtr<nsIDocumentViewer> viewer;
   2897  docShell->GetDocViewer(getter_AddRefs(viewer));
   2898  NS_ENSURE_STATE(viewer);
   2899 
   2900  nsCOMPtr<Document> clonedDoc = doc->CreateStaticClone(
   2901      docShell, viewer, aPrintSettings, aOutHasInProcessPrintCallbacks);
   2902 
   2903  return NS_OK;
   2904 }
   2905 
   2906 bool nsFrameLoader::DoLoadMessageManagerScript(const nsAString& aURL,
   2907                                               bool aRunInGlobalScope) {
   2908  if (auto* browserParent = GetBrowserParent()) {
   2909    return browserParent->SendLoadRemoteScript(aURL, aRunInGlobalScope);
   2910  }
   2911  RefPtr<InProcessBrowserChildMessageManager> browserChild =
   2912      GetBrowserChildMessageManager();
   2913  if (browserChild) {
   2914    browserChild->LoadFrameScript(aURL, aRunInGlobalScope);
   2915  }
   2916  return true;
   2917 }
   2918 
   2919 class nsAsyncMessageToChild : public nsSameProcessAsyncMessageBase,
   2920                              public Runnable {
   2921 public:
   2922  explicit nsAsyncMessageToChild(nsFrameLoader* aFrameLoader)
   2923      : mozilla::Runnable("nsAsyncMessageToChild"),
   2924        mFrameLoader(aFrameLoader) {}
   2925 
   2926  NS_IMETHOD Run() override {
   2927    InProcessBrowserChildMessageManager* browserChild =
   2928        mFrameLoader->mChildMessageManager;
   2929    // Since bug 1126089, messages can arrive even when the docShell is
   2930    // destroyed. Here we make sure that those messages are not delivered.
   2931    if (browserChild && browserChild->GetInnerManager() &&
   2932        mFrameLoader->GetExistingDocShell()) {
   2933      JS::Rooted<JSObject*> kungFuDeathGrip(dom::RootingCx(),
   2934                                            browserChild->GetWrapper());
   2935      ReceiveMessage(static_cast<EventTarget*>(browserChild), mFrameLoader,
   2936                     browserChild->GetInnerManager());
   2937    }
   2938    return NS_OK;
   2939  }
   2940  RefPtr<nsFrameLoader> mFrameLoader;
   2941 };
   2942 
   2943 nsresult nsFrameLoader::DoSendAsyncMessage(const nsAString& aMessage,
   2944                                           StructuredCloneData& aData) {
   2945  auto* browserParent = GetBrowserParent();
   2946  if (browserParent) {
   2947    ClonedMessageData data;
   2948    if (!BuildClonedMessageData(aData, data)) {
   2949      MOZ_CRASH();
   2950      return NS_ERROR_DOM_DATA_CLONE_ERR;
   2951    }
   2952    if (browserParent->SendAsyncMessage(aMessage, data)) {
   2953      return NS_OK;
   2954    } else {
   2955      return NS_ERROR_UNEXPECTED;
   2956    }
   2957  }
   2958 
   2959  if (mChildMessageManager) {
   2960    RefPtr<nsAsyncMessageToChild> ev = new nsAsyncMessageToChild(this);
   2961    nsresult rv = ev->Init(aMessage, aData);
   2962    if (NS_FAILED(rv)) {
   2963      return rv;
   2964    }
   2965    rv = NS_DispatchToCurrentThread(ev);
   2966    if (NS_FAILED(rv)) {
   2967      return rv;
   2968    }
   2969    return rv;
   2970  }
   2971 
   2972  // We don't have any targets to send our asynchronous message to.
   2973  return NS_ERROR_UNEXPECTED;
   2974 }
   2975 
   2976 already_AddRefed<MessageSender> nsFrameLoader::GetMessageManager() {
   2977  EnsureMessageManager();
   2978  return do_AddRef(mMessageManager);
   2979 }
   2980 
   2981 nsresult nsFrameLoader::EnsureMessageManager() {
   2982  NS_ENSURE_STATE(mOwnerContent);
   2983 
   2984  if (mMessageManager) {
   2985    return NS_OK;
   2986  }
   2987 
   2988  if (!mIsTopLevelContent && !IsRemoteFrame() &&
   2989      !(mOwnerContent->IsXULElement() &&
   2990        mOwnerContent->AttrValueIs(kNameSpaceID_None,
   2991                                   nsGkAtoms::forcemessagemanager,
   2992                                   nsGkAtoms::_true, eCaseMatters))) {
   2993    return NS_OK;
   2994  }
   2995 
   2996  RefPtr<nsGlobalWindowOuter> window =
   2997      nsGlobalWindowOuter::Cast(GetOwnerDoc()->GetWindow());
   2998  RefPtr<ChromeMessageBroadcaster> parentManager;
   2999 
   3000  if (window && window->IsChromeWindow()) {
   3001    nsAutoString messagemanagergroup;
   3002    if (mOwnerContent->IsXULElement() &&
   3003        mOwnerContent->GetAttr(nsGkAtoms::messagemanagergroup,
   3004                               messagemanagergroup)) {
   3005      parentManager = window->GetGroupMessageManager(messagemanagergroup);
   3006    }
   3007 
   3008    if (!parentManager) {
   3009      parentManager = window->GetMessageManager();
   3010    }
   3011  } else {
   3012    parentManager = nsFrameMessageManager::GetGlobalMessageManager();
   3013  }
   3014 
   3015  mMessageManager = new ChromeMessageSender(parentManager);
   3016  if (!IsRemoteFrame()) {
   3017    nsresult rv = MaybeCreateDocShell();
   3018    if (NS_FAILED(rv)) {
   3019      return rv;
   3020    }
   3021    MOZ_ASSERT(GetDocShell(),
   3022               "MaybeCreateDocShell succeeded, but null docShell");
   3023    if (!GetDocShell()) {
   3024      return NS_ERROR_FAILURE;
   3025    }
   3026    mChildMessageManager = InProcessBrowserChildMessageManager::Create(
   3027        GetDocShell(), mOwnerContent, mMessageManager);
   3028    NS_ENSURE_TRUE(mChildMessageManager, NS_ERROR_UNEXPECTED);
   3029 
   3030    // Set up session store
   3031    if (SessionStorePlatformCollection()) {
   3032      if (XRE_IsParentProcess() && mIsTopLevelContent) {
   3033        mSessionStoreChild = SessionStoreChild::GetOrCreate(
   3034            GetExtantBrowsingContext(), mOwnerContent);
   3035      }
   3036    }
   3037  }
   3038  return NS_OK;
   3039 }
   3040 
   3041 nsresult nsFrameLoader::ReallyLoadFrameScripts() {
   3042  nsresult rv = EnsureMessageManager();
   3043  if (NS_WARN_IF(NS_FAILED(rv))) {
   3044    return rv;
   3045  }
   3046  if (mMessageManager) {
   3047    mMessageManager->InitWithCallback(this);
   3048  }
   3049  return NS_OK;
   3050 }
   3051 
   3052 already_AddRefed<Element> nsFrameLoader::GetOwnerElement() {
   3053  return do_AddRef(mOwnerContent);
   3054 }
   3055 
   3056 const LazyLoadFrameResumptionState&
   3057 nsFrameLoader::GetLazyLoadFrameResumptionState() {
   3058  static const LazyLoadFrameResumptionState sEmpty;
   3059  if (auto* iframe = HTMLIFrameElement::FromNode(*mOwnerContent)) {
   3060    return iframe->GetLazyLoadFrameResumptionState();
   3061  }
   3062  return sEmpty;
   3063 }
   3064 
   3065 void nsFrameLoader::SetDetachedSubdocs(WeakPresShellArray&& aDocs) {
   3066  mDetachedSubdocs = std::move(aDocs);
   3067 }
   3068 
   3069 auto nsFrameLoader::TakeDetachedSubdocs() -> WeakPresShellArray {
   3070  return std::move(mDetachedSubdocs);
   3071 }
   3072 
   3073 void nsFrameLoader::ApplySandboxFlags(uint32_t sandboxFlags) {
   3074  // If our BrowsingContext doesn't exist yet, it means we haven't been
   3075  // initialized yet. This method will be called again once we're initialized
   3076  // from MaybeCreateDocShell. <iframe> BrowsingContexts are never created as
   3077  // initially remote, so we don't need to worry about updating sandbox flags
   3078  // for an uninitialized initially-remote iframe.
   3079  BrowsingContext* context = GetExtantBrowsingContext();
   3080  if (!context) {
   3081    MOZ_ASSERT(!IsRemoteFrame(),
   3082               "cannot apply sandbox flags to an uninitialized "
   3083               "initially-remote frame");
   3084    return;
   3085  }
   3086 
   3087  uint32_t parentSandboxFlags = mOwnerContent->OwnerDoc()->GetSandboxFlags();
   3088 
   3089  // The child can only add restrictions, never remove them.
   3090  sandboxFlags |= parentSandboxFlags;
   3091 
   3092  MOZ_ALWAYS_SUCCEEDS(context->SetSandboxFlags(sandboxFlags));
   3093 }
   3094 
   3095 /* virtual */
   3096 void nsFrameLoader::AttributeChanged(mozilla::dom::Element* aElement,
   3097                                     int32_t aNameSpaceID, nsAtom* aAttribute,
   3098                                     AttrModType,
   3099                                     const nsAttrValue* aOldValue) {
   3100  MOZ_ASSERT(mObservingOwnerContent);
   3101 
   3102  if (aElement != mOwnerContent) {
   3103    return;
   3104  }
   3105 
   3106  if (aNameSpaceID != kNameSpaceID_None ||
   3107      (aAttribute != TypeAttrName(aElement) &&
   3108       aAttribute != nsGkAtoms::primary)) {
   3109    return;
   3110  }
   3111 
   3112  // Note: This logic duplicates a lot of logic in
   3113  // MaybeCreateDocshell.  We should fix that.
   3114 
   3115  // Notify our enclosing chrome that our type has changed.  We only do this
   3116  // if our parent is chrome, since in all other cases we're random content
   3117  // subframes and the treeowner shouldn't worry about us.
   3118  if (!GetDocShell()) {
   3119    MaybeUpdatePrimaryBrowserParent(eBrowserParentChanged);
   3120    return;
   3121  }
   3122 
   3123  nsCOMPtr<nsIDocShellTreeItem> parentItem;
   3124  GetDocShell()->GetInProcessParent(getter_AddRefs(parentItem));
   3125  if (!parentItem) {
   3126    return;
   3127  }
   3128 
   3129  if (parentItem->ItemType() != nsIDocShellTreeItem::typeChrome) {
   3130    return;
   3131  }
   3132 
   3133  nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner;
   3134  parentItem->GetTreeOwner(getter_AddRefs(parentTreeOwner));
   3135  if (!parentTreeOwner) {
   3136    return;
   3137  }
   3138 
   3139  bool is_primary = aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::primary,
   3140                                          nsGkAtoms::_true, eIgnoreCase);
   3141 
   3142  // when a content panel is no longer primary, hide any open popups it may have
   3143  if (!is_primary) {
   3144    nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
   3145    if (pm) {
   3146      pm->HidePopupsInDocShell(GetDocShell());
   3147    }
   3148  }
   3149 
   3150  parentTreeOwner->ContentShellRemoved(GetDocShell());
   3151  if (aElement->AttrValueIs(kNameSpaceID_None, TypeAttrName(aElement),
   3152                            nsGkAtoms::content, eIgnoreCase)) {
   3153    parentTreeOwner->ContentShellAdded(GetDocShell(), is_primary);
   3154  }
   3155 }
   3156 
   3157 void nsFrameLoader::RequestUpdatePosition(ErrorResult& aRv) {
   3158  if (auto* browserParent = GetBrowserParent()) {
   3159    nsresult rv = browserParent->UpdatePosition();
   3160 
   3161    if (NS_FAILED(rv)) {
   3162      aRv.Throw(rv);
   3163    }
   3164  }
   3165 }
   3166 
   3167 SessionStoreParent* nsFrameLoader::GetSessionStoreParent() {
   3168  MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
   3169  if (mSessionStoreChild) {
   3170    return static_cast<SessionStoreParent*>(
   3171        InProcessChild::ParentActorFor(mSessionStoreChild));
   3172  }
   3173 
   3174  if (BrowserParent* browserParent = GetBrowserParent()) {
   3175    return static_cast<SessionStoreParent*>(
   3176        SingleManagedOrNull(browserParent->ManagedPSessionStoreParent()));
   3177  }
   3178 
   3179  return nullptr;
   3180 }
   3181 
   3182 already_AddRefed<Promise> nsFrameLoader::RequestTabStateFlush(
   3183    ErrorResult& aRv) {
   3184  MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
   3185  Document* ownerDoc = GetOwnerDoc();
   3186  if (!ownerDoc) {
   3187    aRv.ThrowNotSupportedError("No owner document");
   3188    return nullptr;
   3189  }
   3190 
   3191  RefPtr<Promise> promise = Promise::Create(ownerDoc->GetOwnerGlobal(), aRv);
   3192  if (aRv.Failed()) {
   3193    return nullptr;
   3194  }
   3195 
   3196  BrowsingContext* browsingContext = GetExtantBrowsingContext();
   3197  if (!browsingContext) {
   3198    promise->MaybeResolveWithUndefined();
   3199    return promise.forget();
   3200  }
   3201 
   3202  SessionStoreParent* sessionStoreParent = GetSessionStoreParent();
   3203  if (!sessionStoreParent) {
   3204    promise->MaybeResolveWithUndefined();
   3205    return promise.forget();
   3206  }
   3207 
   3208  sessionStoreParent->FlushAllSessionStoreChildren(
   3209      [promise]() { promise->MaybeResolveWithUndefined(); });
   3210 
   3211  return promise.forget();
   3212 }
   3213 
   3214 void nsFrameLoader::RequestFinalTabStateFlush() {
   3215  BrowsingContext* context = GetExtantBrowsingContext();
   3216  if (!context || !context->IsTop() || context->Canonical()->IsReplaced()) {
   3217    return;
   3218  }
   3219 
   3220  RefPtr<CanonicalBrowsingContext> canonical = context->Canonical();
   3221  RefPtr<WindowGlobalParent> wgp = canonical->GetCurrentWindowGlobal();
   3222  RefPtr<Element> embedder = context->GetEmbedderElement();
   3223 
   3224  RefPtr<SessionStoreParent> sessionStoreParent = GetSessionStoreParent();
   3225  if (!sessionStoreParent) {
   3226    canonical->ClearPermanentKey();
   3227    if (wgp) {
   3228      wgp->NotifySessionStoreUpdatesComplete(embedder);
   3229    }
   3230 
   3231    return;
   3232  }
   3233 
   3234  sessionStoreParent->FinalFlushAllSessionStoreChildren(
   3235      [canonical, wgp, embedder]() {
   3236        if (canonical) {
   3237          canonical->ClearPermanentKey();
   3238        }
   3239        if (wgp) {
   3240          wgp->NotifySessionStoreUpdatesComplete(embedder);
   3241        }
   3242      });
   3243 }
   3244 
   3245 void nsFrameLoader::RequestEpochUpdate(uint32_t aEpoch) {
   3246  BrowsingContext* context = GetExtantBrowsingContext();
   3247  if (context) {
   3248    BrowsingContext* top = context->Top();
   3249    (void)top->SetSessionStoreEpoch(aEpoch);
   3250  }
   3251 }
   3252 
   3253 void nsFrameLoader::RequestSHistoryUpdate() {
   3254  if (mSessionStoreChild) {
   3255    mSessionStoreChild->UpdateSHistoryChanges();
   3256    return;
   3257  }
   3258 
   3259  // If remote browsing (e10s), handle this with the BrowserParent.
   3260  if (auto* browserParent = GetBrowserParent()) {
   3261    (void)browserParent->SendUpdateSHistory();
   3262  }
   3263 }
   3264 
   3265 already_AddRefed<Promise> nsFrameLoader::PrintPreview(
   3266    nsIPrintSettings* aPrintSettings, BrowsingContext* aSourceBrowsingContext,
   3267    ErrorResult& aRv) {
   3268  auto* ownerDoc = GetOwnerDoc();
   3269  if (!ownerDoc) {
   3270    aRv.ThrowNotSupportedError("No owner document");
   3271    return nullptr;
   3272  }
   3273  RefPtr<Promise> promise = Promise::Create(ownerDoc->GetOwnerGlobal(), aRv);
   3274  if (!promise) {
   3275    return nullptr;
   3276  }
   3277 
   3278 #ifndef NS_PRINTING
   3279  promise->MaybeRejectWithNotSupportedError("Build does not support printing");
   3280  return promise.forget();
   3281 #else
   3282  auto resolve = [promise](PrintPreviewResultInfo aInfo) {
   3283    using Orientation = dom::PrintPreviewOrientation;
   3284    if (aInfo.sheetCount() > 0) {
   3285      PrintPreviewSuccessInfo info;
   3286      info.mSheetCount = aInfo.sheetCount();
   3287      info.mTotalPageCount = aInfo.totalPageCount();
   3288      info.mHasSelection = aInfo.hasSelection();
   3289      info.mHasSelfSelection = aInfo.hasSelfSelection();
   3290      info.mIsEmpty = aInfo.isEmpty();
   3291      if (aInfo.printLandscape()) {
   3292        info.mOrientation = aInfo.printLandscape().value()
   3293                                ? Orientation::Landscape
   3294                                : Orientation::Portrait;
   3295      } else {
   3296        MOZ_ASSERT(info.mOrientation == Orientation::Unspecified);
   3297      }
   3298      if (aInfo.pageWidth()) {
   3299        info.mPageWidth = aInfo.pageWidth().value();
   3300      }
   3301      if (aInfo.pageHeight()) {
   3302        info.mPageHeight = aInfo.pageHeight().value();
   3303      }
   3304 
   3305      promise->MaybeResolve(info);
   3306    } else {
   3307      promise->MaybeRejectWithUnknownError("Print preview failed");
   3308    }
   3309  };
   3310 
   3311  if (auto* browserParent = GetBrowserParent()) {
   3312    nsCOMPtr<nsIPrintSettingsService> printSettingsSvc =
   3313        do_GetService("@mozilla.org/gfx/printsettings-service;1");
   3314    if (!printSettingsSvc) {
   3315      promise->MaybeRejectWithNotSupportedError("No nsIPrintSettingsService");
   3316      return promise.forget();
   3317    }
   3318 
   3319    embedding::PrintData printData;
   3320    nsresult rv =
   3321        printSettingsSvc->SerializeToPrintData(aPrintSettings, &printData);
   3322    if (NS_WARN_IF(NS_FAILED(rv))) {
   3323      promise->MaybeReject(ErrorResult(rv));
   3324      return promise.forget();
   3325    }
   3326 
   3327    browserParent->SendPrintPreview(printData, aSourceBrowsingContext)
   3328        ->Then(
   3329            GetMainThreadSerialEventTarget(), __func__, std::move(resolve),
   3330            [promise](const mozilla::ipc::ResponseRejectReason) {
   3331              promise->MaybeRejectWithUnknownError("Print preview IPC failed");
   3332            });
   3333 
   3334    return promise.forget();
   3335  }
   3336 
   3337  RefPtr<nsGlobalWindowOuter> sourceWindow;
   3338  if (aSourceBrowsingContext) {
   3339    sourceWindow =
   3340        nsGlobalWindowOuter::Cast(aSourceBrowsingContext->GetDOMWindow());
   3341  } else {
   3342    nsDocShell* ourDocshell = GetExistingDocShell();
   3343    if (NS_WARN_IF(!ourDocshell)) {
   3344      promise->MaybeRejectWithNotSupportedError("No print preview docShell");
   3345      return promise.forget();
   3346    }
   3347    sourceWindow = nsGlobalWindowOuter::Cast(ourDocshell->GetWindow());
   3348  }
   3349  if (NS_WARN_IF(!sourceWindow)) {
   3350    promise->MaybeRejectWithNotSupportedError("No print preview source window");
   3351    return promise.forget();
   3352  }
   3353 
   3354  nsCOMPtr<nsIDocShell> docShellToCloneInto = nullptr;
   3355  if (aSourceBrowsingContext) {
   3356    // We're going to call `Print()` below on a window that is not our own,
   3357    // which happens when we are creating a new print preview document instead
   3358    // of just applying a settings change to the existing PP document.  In this
   3359    // case we need to explicitly pass our docShell as the docShell to clone
   3360    // into.
   3361    docShellToCloneInto = GetExistingDocShell();
   3362    if (NS_WARN_IF(!docShellToCloneInto)) {
   3363      promise->MaybeRejectWithNotSupportedError("No print preview docShell");
   3364      return promise.forget();
   3365    }
   3366 
   3367    // We need to make sure we're displayed so that the view tree ends up right.
   3368    RefPtr<BrowsingContext> bc = docShellToCloneInto->GetBrowsingContext();
   3369    if (NS_WARN_IF(!bc)) {
   3370      promise->MaybeRejectWithNotSupportedError(
   3371          "No print preview browsing context");
   3372      return promise.forget();
   3373    }
   3374 
   3375    RefPtr<Element> embedder = bc->GetEmbedderElement();
   3376    if (NS_WARN_IF(!embedder)) {
   3377      promise->MaybeRejectWithNotSupportedError(
   3378          "Trying to clone into a frameloader with no element?");
   3379      return promise.forget();
   3380    }
   3381 
   3382    nsIFrame* frame = embedder->GetPrimaryFrame(FlushType::Frames);
   3383    if (NS_WARN_IF(!frame)) {
   3384      promise->MaybeRejectWithNotSupportedError("Frame is not being displayed");
   3385      return promise.forget();
   3386    }
   3387  }
   3388 
   3389  // Unfortunately we can't pass `resolve` directly here because IPDL, for now,
   3390  // unfortunately generates slightly different parameter types for functions
   3391  // taking PrintPreviewResultInfo in PBrowserParent vs. PBrowserChild.
   3392  ErrorResult rv;
   3393  sourceWindow->Print(
   3394      aPrintSettings,
   3395      /* aRemotePrintJob = */ nullptr,
   3396      /* aListener = */ nullptr, docShellToCloneInto,
   3397      nsGlobalWindowOuter::IsPreview::Yes,
   3398      nsGlobalWindowOuter::IsForWindowDotPrint::No,
   3399      [resolve](const PrintPreviewResultInfo& aInfo) { resolve(aInfo); },
   3400      nullptr, rv);
   3401  if (NS_WARN_IF(rv.Failed())) {
   3402    promise->MaybeReject(std::move(rv));
   3403  }
   3404 
   3405  return promise.forget();
   3406 #endif
   3407 }
   3408 
   3409 void nsFrameLoader::ExitPrintPreview() {
   3410 #ifdef NS_PRINTING
   3411  if (auto* browserParent = GetBrowserParent()) {
   3412    (void)browserParent->SendExitPrintPreview();
   3413    return;
   3414  }
   3415  if (NS_WARN_IF(!GetExistingDocShell())) {
   3416    return;
   3417  }
   3418  nsCOMPtr<nsIWebBrowserPrint> webBrowserPrint =
   3419      do_GetInterface(ToSupports(GetExistingDocShell()->GetWindow()));
   3420  if (NS_WARN_IF(!webBrowserPrint)) {
   3421    return;
   3422  }
   3423  webBrowserPrint->ExitPrintPreview();
   3424 #endif
   3425 }
   3426 
   3427 already_AddRefed<nsIRemoteTab> nsFrameLoader::GetRemoteTab() {
   3428  if (!mRemoteBrowser) {
   3429    return nullptr;
   3430  }
   3431  if (auto* browserHost = mRemoteBrowser->AsBrowserHost()) {
   3432    return do_AddRef(browserHost);
   3433  }
   3434  return nullptr;
   3435 }
   3436 
   3437 already_AddRefed<nsILoadContext> nsFrameLoader::GetLoadContext() {
   3438  return do_AddRef(GetBrowsingContext());
   3439 }
   3440 
   3441 BrowsingContext* nsFrameLoader::GetBrowsingContext() {
   3442  if (!mInitialized) {
   3443    if (IsRemoteFrame()) {
   3444      (void)EnsureRemoteBrowser();
   3445    } else if (mOwnerContent) {
   3446      (void)MaybeCreateDocShell();
   3447    }
   3448  }
   3449  MOZ_ASSERT(mInitialized || mDestroyCalled);
   3450  return GetExtantBrowsingContext();
   3451 }
   3452 
   3453 BrowsingContext* nsFrameLoader::GetExtantBrowsingContext() {
   3454  if (!mPendingBrowsingContext) {
   3455    // If mPendingBrowsingContext is null then the frame loader is being
   3456    // destroyed (nsFrameLoader::DestroyDocShell was called), so return null
   3457    // here in that case.
   3458    return nullptr;
   3459  }
   3460 
   3461  if (!mInitialized || !mPendingBrowsingContext->EverAttached()) {
   3462    // Don't return the pending BrowsingContext until this nsFrameLoader has
   3463    // been initialized, and the BC was attached.
   3464    return nullptr;
   3465  }
   3466 
   3467  return mPendingBrowsingContext;
   3468 }
   3469 
   3470 void nsFrameLoader::StartPersistence(
   3471    BrowsingContext* aContext, nsIWebBrowserPersistDocumentReceiver* aRecv,
   3472    ErrorResult& aRv) {
   3473  MOZ_ASSERT(aRecv);
   3474  RefPtr<BrowsingContext> context = aContext ? aContext : GetBrowsingContext();
   3475 
   3476  if (!context || !context->IsInSubtreeOf(GetBrowsingContext())) {
   3477    aRecv->OnError(NS_ERROR_NO_CONTENT);
   3478    return;
   3479  }
   3480 
   3481  if (!context->GetDocShell() && XRE_IsParentProcess()) {
   3482    CanonicalBrowsingContext* canonical =
   3483        CanonicalBrowsingContext::Cast(context);
   3484    if (!canonical->GetCurrentWindowGlobal()) {
   3485      aRecv->OnError(NS_ERROR_NO_CONTENT);
   3486      return;
   3487    }
   3488    RefPtr<BrowserParent> browserParent =
   3489        canonical->GetCurrentWindowGlobal()->GetBrowserParent();
   3490    browserParent->StartPersistence(canonical, aRecv, aRv);
   3491    return;
   3492  }
   3493 
   3494  nsCOMPtr<Document> foundDoc = context->GetDocument();
   3495 
   3496  if (!foundDoc) {
   3497    aRecv->OnError(NS_ERROR_NO_CONTENT);
   3498  } else {
   3499    nsCOMPtr<nsIWebBrowserPersistDocument> pdoc =
   3500        new mozilla::WebBrowserPersistLocalDocument(foundDoc);
   3501    aRecv->OnDocumentReady(pdoc);
   3502  }
   3503 }
   3504 
   3505 void nsFrameLoader::MaybeUpdatePrimaryBrowserParent(
   3506    BrowserParentChange aChange) {
   3507  if (!mOwnerContent || !mRemoteBrowser) {
   3508    return;
   3509  }
   3510 
   3511  RefPtr<BrowserHost> browserHost = mRemoteBrowser->AsBrowserHost();
   3512  if (!browserHost) {
   3513    return;
   3514  }
   3515 
   3516  nsCOMPtr<nsIDocShell> docShell = mOwnerContent->OwnerDoc()->GetDocShell();
   3517  if (!docShell) {
   3518    return;
   3519  }
   3520 
   3521  BrowsingContext* browsingContext = docShell->GetBrowsingContext();
   3522  if (!browsingContext->IsChrome()) {
   3523    return;
   3524  }
   3525 
   3526  nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner;
   3527  docShell->GetTreeOwner(getter_AddRefs(parentTreeOwner));
   3528  if (!parentTreeOwner) {
   3529    return;
   3530  }
   3531 
   3532  if (!mObservingOwnerContent) {
   3533    mOwnerContent->AddMutationObserver(this);
   3534    mObservingOwnerContent = true;
   3535  }
   3536 
   3537  parentTreeOwner->RemoteTabRemoved(browserHost);
   3538  if (aChange == eBrowserParentChanged) {
   3539    bool isPrimary = mOwnerContent->AttrValueIs(
   3540        kNameSpaceID_None, nsGkAtoms::primary, nsGkAtoms::_true, eIgnoreCase);
   3541    parentTreeOwner->RemoteTabAdded(browserHost, isPrimary);
   3542  }
   3543 }
   3544 
   3545 nsresult nsFrameLoader::GetNewTabContext(MutableTabContext* aTabContext,
   3546                                         nsIURI* aURI) {
   3547  nsCOMPtr<nsIDocShell> docShell = mOwnerContent->OwnerDoc()->GetDocShell();
   3548  nsCOMPtr<nsILoadContext> parentContext = do_QueryInterface(docShell);
   3549  NS_ENSURE_STATE(parentContext);
   3550 
   3551  MOZ_ASSERT(mPendingBrowsingContext->EverAttached());
   3552 
   3553  uint64_t chromeOuterWindowID = 0;
   3554 
   3555  nsCOMPtr<nsPIWindowRoot> root =
   3556      nsContentUtils::GetWindowRoot(mOwnerContent->OwnerDoc());
   3557  if (root) {
   3558    nsPIDOMWindowOuter* outerWin = root->GetWindow();
   3559    if (outerWin) {
   3560      chromeOuterWindowID = outerWin->WindowID();
   3561    }
   3562  }
   3563 
   3564  uint32_t maxTouchPoints = BrowserParent::GetMaxTouchPoints(mOwnerContent);
   3565 
   3566  bool tabContextUpdated =
   3567      aTabContext->SetTabContext(chromeOuterWindowID, maxTouchPoints);
   3568  NS_ENSURE_STATE(tabContextUpdated);
   3569 
   3570  return NS_OK;
   3571 }
   3572 
   3573 nsresult nsFrameLoader::PopulateOriginContextIdsFromAttributes(
   3574    OriginAttributes& aAttr) {
   3575  // Only XUL are allowed to set context IDs
   3576  uint32_t namespaceID = mOwnerContent->GetNameSpaceID();
   3577  if (namespaceID != kNameSpaceID_XUL) {
   3578    return NS_OK;
   3579  }
   3580 
   3581  nsAutoString attributeValue;
   3582  if (aAttr.mUserContextId ==
   3583          nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID &&
   3584      mOwnerContent->GetAttr(nsGkAtoms::usercontextid, attributeValue) &&
   3585      !attributeValue.IsEmpty()) {
   3586    nsresult rv;
   3587    aAttr.mUserContextId = attributeValue.ToInteger(&rv);
   3588    NS_ENSURE_SUCCESS(rv, rv);
   3589  }
   3590 
   3591  if (aAttr.mGeckoViewSessionContextId.IsEmpty() &&
   3592      mOwnerContent->GetAttr(nsGkAtoms::geckoViewSessionContextId,
   3593                             attributeValue) &&
   3594      !attributeValue.IsEmpty()) {
   3595    // XXX: Should we check the format from `GeckoViewNavigation.sys.mjs` here?
   3596    aAttr.mGeckoViewSessionContextId = attributeValue;
   3597  }
   3598 
   3599  return NS_OK;
   3600 }
   3601 
   3602 ProcessMessageManager* nsFrameLoader::GetProcessMessageManager() const {
   3603  if (auto* browserParent = GetBrowserParent()) {
   3604    return browserParent->Manager()->GetMessageManager();
   3605  }
   3606  return nullptr;
   3607 };
   3608 
   3609 JSObject* nsFrameLoader::WrapObject(JSContext* cx,
   3610                                    JS::Handle<JSObject*> aGivenProto) {
   3611  JS::Rooted<JSObject*> result(cx);
   3612  FrameLoader_Binding::Wrap(cx, this, this, aGivenProto, &result);
   3613  return result;
   3614 }
   3615 
   3616 void nsFrameLoader::SetWillChangeProcess() {
   3617  mWillChangeProcess = true;
   3618 
   3619  if (IsRemoteFrame()) {
   3620    if (auto* browserParent = GetBrowserParent()) {
   3621      if (auto* bc = CanonicalBrowsingContext::Cast(mPendingBrowsingContext);
   3622          bc && bc->EverAttached()) {
   3623        bc->StartUnloadingHost(browserParent->Manager()->ChildID());
   3624        bc->SetCurrentBrowserParent(nullptr);
   3625      }
   3626      // OOP Browser - Go directly over Browser Parent
   3627      (void)browserParent->SendWillChangeProcess();
   3628    } else if (auto* browserBridgeChild = GetBrowserBridgeChild()) {
   3629      // OOP IFrame - Through Browser Bridge Parent, set on browser child
   3630      (void)browserBridgeChild->SendWillChangeProcess();
   3631    }
   3632    return;
   3633  }
   3634 
   3635  // In process
   3636  RefPtr<nsDocShell> docshell = GetDocShell();
   3637  MOZ_ASSERT(docshell);
   3638  docshell->SetWillChangeProcess();
   3639 }
   3640 
   3641 static mozilla::Result<bool, nsresult> BuildIDMismatchMemoryAndDisk() {
   3642  if (const char* forceMismatch = PR_GetEnv("MOZ_FORCE_BUILDID_MISMATCH")) {
   3643    if (forceMismatch[0] == '1') {
   3644      NS_WARNING("Forcing a buildid mismatch");
   3645      return true;
   3646    }
   3647  }
   3648 
   3649 #if defined(ANDROID)
   3650  // Android packages on installation will stop existing instance, so we
   3651  // cannot run into this problem.
   3652  return false;
   3653 #else
   3654 
   3655 #  if defined(XP_WIN)
   3656  {
   3657    // Windows Store packages cannot run into this problem.
   3658    nsCOMPtr<nsIPropertyBag2> infoService =
   3659        do_GetService("@mozilla.org/system-info;1");
   3660    MOZ_ASSERT(infoService, "Could not find a system info service");
   3661    bool isMSIX;
   3662    nsresult rv = infoService->GetPropertyAsBool(u"isPackagedApp"_ns, &isMSIX);
   3663    if (NS_SUCCEEDED(rv)) {
   3664      if (isMSIX) {
   3665        return false;
   3666      }
   3667    }
   3668  }
   3669 #  endif  // XP_WIN
   3670 
   3671  nsresult rv;
   3672  nsCOMPtr<nsIFile> file;
   3673 
   3674  rv = NS_GetSpecialDirectory(NS_GRE_BIN_DIR, getter_AddRefs(file));
   3675  MOZ_TRY(rv);
   3676 
   3677  rv = file->Append(XUL_DLL u""_ns);
   3678  MOZ_TRY(rv);
   3679 
   3680  nsAutoString xul;
   3681  rv = file->GetPath(xul);
   3682  MOZ_TRY(rv);
   3683 
   3684  nsCString installedBuildID;
   3685  nsCString section_name(MOZ_BUILDID_SECTION_NAME);
   3686  rv = read_toolkit_buildid_from_file(&xul, &section_name, &installedBuildID);
   3687  MOZ_TRY(rv);
   3688 
   3689  return (installedBuildID != PlatformBuildID());
   3690 #endif    // !ANDROID
   3691 }
   3692 
   3693 void nsFrameLoader::MaybeNotifyCrashed(BrowsingContext* aBrowsingContext,
   3694                                       ContentParentId aChildID,
   3695                                       mozilla::ipc::MessageChannel* aChannel) {
   3696  if (mTabProcessCrashFired) {
   3697    return;
   3698  }
   3699 
   3700  if (mPendingBrowsingContext == aBrowsingContext) {
   3701    mTabProcessCrashFired = true;
   3702  }
   3703 
   3704  // Fire the crashed observer notification.
   3705  nsCOMPtr<nsIObserverService> os = services::GetObserverService();
   3706  if (!os) {
   3707    return;
   3708  }
   3709 
   3710  os->NotifyObservers(ToSupports(this), "oop-frameloader-crashed", nullptr);
   3711 
   3712  // Check our owner element still references us. If it's moved, on, events
   3713  // don't need to be fired.
   3714  RefPtr<nsFrameLoaderOwner> owner = do_QueryObject(mOwnerContent);
   3715  if (!owner) {
   3716    return;
   3717  }
   3718 
   3719  RefPtr<nsFrameLoader> currentFrameLoader = owner->GetFrameLoader();
   3720  if (currentFrameLoader != this) {
   3721    return;
   3722  }
   3723 
   3724 #if defined(MOZ_TELEMETRY_REPORTING)
   3725  bool sendTelemetryFalsePositive = false, sendTelemetryTrueMismatch = false;
   3726 
   3727  static bool haveSentTelemetryFalsePositive = false,
   3728              haveSentTelemetryTrueMismatch = false;
   3729 #endif  // defined(MOZ_TELEMETRY_REPORTING)
   3730 
   3731  // Fire the actual crashed event.
   3732  nsString eventName;
   3733  if (aChannel && !aChannel->DoBuildIDsMatch()) {
   3734    auto changedOrError = BuildIDMismatchMemoryAndDisk();
   3735    if (changedOrError.isErr()) {
   3736      NS_WARNING("Error while checking buildid mismatch");
   3737      eventName = u"oop-browser-crashed"_ns;
   3738    } else {
   3739      bool aChanged = changedOrError.unwrap();
   3740      if (aChanged) {
   3741        NS_WARNING("True build ID mismatch");
   3742        eventName = u"oop-browser-buildid-mismatch"_ns;
   3743 #if defined(MOZ_TELEMETRY_REPORTING)
   3744        sendTelemetryTrueMismatch = true;
   3745 #endif  // defined(MOZ_TELEMETRY_REPORTING)
   3746      } else {
   3747        NS_WARNING("build ID mismatch false alarm");
   3748        eventName = u"oop-browser-crashed"_ns;
   3749 #if defined(MOZ_TELEMETRY_REPORTING)
   3750        sendTelemetryFalsePositive = true;
   3751 #endif  // defined(MOZ_TELEMETRY_REPORTING)
   3752      }
   3753    }
   3754  } else {
   3755    NS_WARNING("No build ID mismatch");
   3756    eventName = u"oop-browser-crashed"_ns;
   3757  }
   3758 
   3759 #if defined(MOZ_TELEMETRY_REPORTING)
   3760  if (sendTelemetryFalsePositive && !haveSentTelemetryFalsePositive) {
   3761    glean::dom_contentprocess::build_id_mismatch_false_positive.Add(1);
   3762    haveSentTelemetryFalsePositive = true;
   3763  }
   3764 
   3765  if (sendTelemetryTrueMismatch && !haveSentTelemetryTrueMismatch) {
   3766    glean::dom_contentprocess::build_id_mismatch.Add(1);
   3767    haveSentTelemetryTrueMismatch = true;
   3768  }
   3769 #endif  // defined(MOZ_TELEMETRY_REPORTING)
   3770 
   3771  FrameCrashedEventInit init;
   3772  init.mBubbles = true;
   3773  init.mCancelable = true;
   3774  if (aBrowsingContext) {
   3775    init.mBrowsingContextId = aBrowsingContext->Id();
   3776    init.mIsTopFrame = aBrowsingContext->IsTop();
   3777    init.mChildID = aChildID;
   3778  }
   3779 
   3780  RefPtr<FrameCrashedEvent> event = FrameCrashedEvent::Constructor(
   3781      mOwnerContent->OwnerDoc(), eventName, init);
   3782  event->SetTrusted(true);
   3783 
   3784  RefPtr<Element> ownerContent = mOwnerContent;
   3785  EventDispatcher::DispatchDOMEvent(ownerContent, nullptr, event, nullptr,
   3786                                    nullptr);
   3787 }
   3788 
   3789 bool nsFrameLoader::EnsureBrowsingContextAttached() {
   3790  nsresult rv;
   3791 
   3792  Document* parentDoc = mOwnerContent->OwnerDoc();
   3793  MOZ_ASSERT(parentDoc);
   3794  BrowsingContext* parentContext = parentDoc->GetBrowsingContext();
   3795  MOZ_ASSERT(parentContext);
   3796 
   3797  // Inherit the `use` flags from our parent BrowsingContext.
   3798  bool usePrivateBrowsing = parentContext->UsePrivateBrowsing();
   3799  bool useRemoteSubframes = parentContext->UseRemoteSubframes();
   3800  bool useRemoteTabs = parentContext->UseRemoteTabs();
   3801 
   3802  // Determine the exact OriginAttributes which should be used for our
   3803  // BrowsingContext. This will be used to initialize OriginAttributes if the
   3804  // BrowsingContext has not already been created.
   3805  OriginAttributes attrs;
   3806  if (mPendingBrowsingContext->IsContent()) {
   3807    if (mPendingBrowsingContext->GetParent()) {
   3808      MOZ_ASSERT(mPendingBrowsingContext->GetParent() == parentContext);
   3809      parentContext->GetOriginAttributes(attrs);
   3810    }
   3811 
   3812    // Inherit the `mFirstPartyDomain` flag from our parent document's result
   3813    // principal, if it was set.
   3814    if (parentContext->IsContent() &&
   3815        !parentDoc->NodePrincipal()->IsSystemPrincipal()) {
   3816      OriginAttributes docAttrs =
   3817          parentDoc->NodePrincipal()->OriginAttributesRef();
   3818      // We only want to inherit firstPartyDomain here, other attributes should
   3819      // be constant.
   3820      MOZ_ASSERT(attrs.EqualsIgnoringFPD(docAttrs));
   3821      attrs.mFirstPartyDomain = docAttrs.mFirstPartyDomain;
   3822    }
   3823 
   3824    // Inherit the PrivateBrowsing flag across content/chrome boundaries.
   3825    attrs.SyncAttributesWithPrivateBrowsing(usePrivateBrowsing);
   3826 
   3827    // A <browser> element may have overridden userContextId or
   3828    // geckoViewUserContextId.
   3829    rv = PopulateOriginContextIdsFromAttributes(attrs);
   3830    if (NS_WARN_IF(NS_FAILED(rv))) {
   3831      return false;
   3832    }
   3833  }
   3834 
   3835  // If we've already been attached, return.
   3836  if (mPendingBrowsingContext->EverAttached()) {
   3837    MOZ_DIAGNOSTIC_ASSERT(mPendingBrowsingContext->UsePrivateBrowsing() ==
   3838                          usePrivateBrowsing);
   3839    MOZ_DIAGNOSTIC_ASSERT(mPendingBrowsingContext->UseRemoteTabs() ==
   3840                          useRemoteTabs);
   3841    MOZ_DIAGNOSTIC_ASSERT(mPendingBrowsingContext->UseRemoteSubframes() ==
   3842                          useRemoteSubframes);
   3843    // Don't assert that our OriginAttributes match, as we could have different
   3844    // OriginAttributes in the case where we were opened using window.open.
   3845    return true;
   3846  }
   3847 
   3848  // Initialize non-synced OriginAttributes and related fields.
   3849  rv = mPendingBrowsingContext->SetOriginAttributes(attrs);
   3850  NS_ENSURE_SUCCESS(rv, false);
   3851  rv = mPendingBrowsingContext->SetUsePrivateBrowsing(usePrivateBrowsing);
   3852  NS_ENSURE_SUCCESS(rv, false);
   3853  rv = mPendingBrowsingContext->SetRemoteTabs(useRemoteTabs);
   3854  NS_ENSURE_SUCCESS(rv, false);
   3855  rv = mPendingBrowsingContext->SetRemoteSubframes(useRemoteSubframes);
   3856  NS_ENSURE_SUCCESS(rv, false);
   3857 
   3858  // Finish attaching.
   3859  mPendingBrowsingContext->EnsureAttached();
   3860  return true;
   3861 }
   3862 
   3863 void nsFrameLoader::InvokeBrowsingContextReadyCallback() {
   3864  if (mOpenWindowInfo) {
   3865    if (RefPtr<nsIBrowsingContextReadyCallback> callback =
   3866            mOpenWindowInfo->BrowsingContextReadyCallback()) {
   3867      callback->BrowsingContextReady(mPendingBrowsingContext);
   3868    }
   3869  }
   3870 }