tor-browser

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

BrowsingContext.cpp (161972B)


      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 #include "mozilla/dom/BrowsingContext.h"
      8 
      9 #include "ipc/IPCMessageUtils.h"
     10 
     11 #ifdef ACCESSIBILITY
     12 #  include "mozilla/a11y/DocAccessibleParent.h"
     13 #  include "mozilla/a11y/Platform.h"
     14 #  include "nsAccessibilityService.h"
     15 #  if defined(XP_WIN)
     16 #    include "mozilla/a11y/AccessibleWrap.h"
     17 #    include "mozilla/a11y/Compatibility.h"
     18 #    include "mozilla/a11y/nsWinUtils.h"
     19 #  endif
     20 #endif
     21 #include "js/LocaleSensitive.h"
     22 #include "mozilla/AppShutdown.h"
     23 #include "mozilla/dom/CanonicalBrowsingContext.h"
     24 #include "mozilla/dom/BindingIPCUtils.h"
     25 #include "mozilla/dom/BrowserHost.h"
     26 #include "mozilla/dom/BrowserChild.h"
     27 #include "mozilla/dom/BrowserParent.h"
     28 #include "mozilla/dom/BrowsingContextGroup.h"
     29 #include "mozilla/dom/BrowsingContextBinding.h"
     30 #include "mozilla/dom/ContentChild.h"
     31 #include "mozilla/dom/ContentParent.h"
     32 #include "mozilla/dom/Document.h"
     33 #include "mozilla/dom/DocumentPictureInPicture.h"
     34 #include "mozilla/dom/Element.h"
     35 #include "mozilla/dom/Geolocation.h"
     36 #include "mozilla/dom/HTMLEmbedElement.h"
     37 #include "mozilla/dom/HTMLIFrameElement.h"
     38 #include "mozilla/dom/Location.h"
     39 #include "mozilla/dom/LocationBinding.h"
     40 #include "mozilla/dom/MediaDevices.h"
     41 #include "mozilla/dom/Navigation.h"
     42 #include "mozilla/dom/Navigator.h"
     43 #include "mozilla/dom/PopupBlocker.h"
     44 #include "mozilla/dom/ReferrerInfo.h"
     45 #include "mozilla/dom/ScriptSettings.h"
     46 #include "mozilla/dom/SessionStoreChild.h"
     47 #include "mozilla/dom/SessionStorageManager.h"
     48 #include "mozilla/dom/StructuredCloneTags.h"
     49 #include "mozilla/dom/UserActivationIPCUtils.h"
     50 #include "mozilla/dom/WindowBinding.h"
     51 #include "mozilla/dom/WindowContext.h"
     52 #include "mozilla/dom/WindowGlobalChild.h"
     53 #include "mozilla/dom/WindowGlobalParent.h"
     54 #include "mozilla/dom/WindowProxyHolder.h"
     55 #include "mozilla/dom/SyncedContextInlines.h"
     56 #include "mozilla/dom/XULFrameElement.h"
     57 #include "mozilla/ipc/ProtocolUtils.h"
     58 #include "mozilla/net/DocumentLoadListener.h"
     59 #include "mozilla/net/RequestContextService.h"
     60 #include "mozilla/Assertions.h"
     61 #include "mozilla/AsyncEventDispatcher.h"
     62 #include "mozilla/ClearOnShutdown.h"
     63 #include "mozilla/Components.h"
     64 #include "mozilla/Logging.h"
     65 #include "mozilla/MediaFeatureChange.h"
     66 #include "mozilla/Services.h"
     67 #include "mozilla/StaticPrefs_fission.h"
     68 #include "mozilla/StaticPrefs_media.h"
     69 #include "mozilla/StaticPrefs_page_load.h"
     70 #include "mozilla/StaticPtr.h"
     71 #include "mozilla/URLQueryStringStripper.h"
     72 #include "mozilla/EventStateManager.h"
     73 #include "mozilla/glean/DomMetrics.h"
     74 #include "mozilla/StartupTimeline.h"
     75 #include "GeckoProfiler.h"
     76 #include "mozilla/ProfilerMarkers.h"
     77 #include "nsIURIFixup.h"
     78 #include "nsIXULRuntime.h"
     79 
     80 #include "nsDocShell.h"
     81 #include "nsDocShellLoadState.h"
     82 #include "nsFocusManager.h"
     83 #include "nsGlobalWindowInner.h"
     84 #include "nsGlobalWindowOuter.h"
     85 #include "PresShell.h"
     86 #include "nsIObserverService.h"
     87 #include "nsISHistory.h"
     88 #include "nsJSUtils.h"
     89 #include "nsContentUtils.h"
     90 #include "nsQueryObject.h"
     91 #include "nsSandboxFlags.h"
     92 #include "nsScreen.h"
     93 #include "nsScriptError.h"
     94 #include "nsThreadUtils.h"
     95 #include "xpcprivate.h"
     96 
     97 #include "AutoplayPolicy.h"
     98 #include "GVAutoplayRequestStatusIPC.h"
     99 
    100 extern mozilla::LazyLogModule gAutoplayPermissionLog;
    101 extern mozilla::LazyLogModule gNavigationAPILog;
    102 extern mozilla::LazyLogModule gTimeoutDeferralLog;
    103 
    104 #define AUTOPLAY_LOG(msg, ...) \
    105  MOZ_LOG(gAutoplayPermissionLog, LogLevel::Debug, (msg, ##__VA_ARGS__))
    106 
    107 namespace IPC {
    108 // Allow serialization and deserialization of OrientationType over IPC
    109 template <>
    110 struct ParamTraits<mozilla::dom::OrientationType>
    111    : public mozilla::dom::WebIDLEnumSerializer<mozilla::dom::OrientationType> {
    112 };
    113 
    114 template <>
    115 struct ParamTraits<mozilla::dom::DisplayMode>
    116    : public mozilla::dom::WebIDLEnumSerializer<mozilla::dom::DisplayMode> {};
    117 
    118 template <>
    119 struct ParamTraits<mozilla::dom::PrefersColorSchemeOverride>
    120    : public mozilla::dom::WebIDLEnumSerializer<
    121          mozilla::dom::PrefersColorSchemeOverride> {};
    122 
    123 template <>
    124 struct ParamTraits<mozilla::dom::ForcedColorsOverride>
    125    : public mozilla::dom::WebIDLEnumSerializer<
    126          mozilla::dom::ForcedColorsOverride> {};
    127 
    128 template <>
    129 struct ParamTraits<mozilla::dom::ExplicitActiveStatus>
    130    : public ContiguousEnumSerializer<
    131          mozilla::dom::ExplicitActiveStatus,
    132          mozilla::dom::ExplicitActiveStatus::None,
    133          mozilla::dom::ExplicitActiveStatus::EndGuard_> {};
    134 
    135 // Allow serialization and deserialization of TouchEventsOverride over IPC
    136 template <>
    137 struct ParamTraits<mozilla::dom::TouchEventsOverride>
    138    : public mozilla::dom::WebIDLEnumSerializer<
    139          mozilla::dom::TouchEventsOverride> {};
    140 
    141 template <>
    142 struct ParamTraits<mozilla::dom::EmbedderColorSchemes> {
    143  using paramType = mozilla::dom::EmbedderColorSchemes;
    144 
    145  static void Write(MessageWriter* aWriter, const paramType& aParam) {
    146    WriteParam(aWriter, aParam.mUsed);
    147    WriteParam(aWriter, aParam.mPreferred);
    148  }
    149 
    150  static bool Read(MessageReader* aReader, paramType* aResult) {
    151    return ReadParam(aReader, &aResult->mUsed) &&
    152           ReadParam(aReader, &aResult->mPreferred);
    153  }
    154 };
    155 
    156 }  // namespace IPC
    157 
    158 namespace mozilla {
    159 namespace dom {
    160 
    161 // Explicit specialization of the `Transaction` type. Required by the `extern
    162 // template class` declaration in the header.
    163 template class syncedcontext::Transaction<BrowsingContext>;
    164 
    165 extern mozilla::LazyLogModule gUserInteractionPRLog;
    166 
    167 #define USER_ACTIVATION_LOG(msg, ...) \
    168  MOZ_LOG(gUserInteractionPRLog, LogLevel::Debug, (msg, ##__VA_ARGS__))
    169 
    170 static LazyLogModule gBrowsingContextLog("BrowsingContext");
    171 static LazyLogModule gBrowsingContextSyncLog("BrowsingContextSync");
    172 
    173 typedef nsTHashMap<nsUint64HashKey, BrowsingContext*> BrowsingContextMap;
    174 
    175 // All BrowsingContexts indexed by Id
    176 static StaticAutoPtr<BrowsingContextMap> sBrowsingContexts;
    177 // Top-level Content BrowsingContexts only, indexed by BrowserId instead of Id
    178 static StaticAutoPtr<BrowsingContextMap> sCurrentTopByBrowserId;
    179 
    180 static bool gIPCEnabledAnnotation = false;
    181 static bool gFissionEnabledAnnotation = false;
    182 
    183 static void UnregisterBrowserId(BrowsingContext* aBrowsingContext) {
    184  if (!aBrowsingContext->IsTopContent() || !sCurrentTopByBrowserId) {
    185    return;
    186  }
    187 
    188  // Avoids an extra lookup
    189  auto browserIdEntry =
    190      sCurrentTopByBrowserId->Lookup(aBrowsingContext->BrowserId());
    191  if (browserIdEntry && browserIdEntry.Data() == aBrowsingContext) {
    192    browserIdEntry.Remove();
    193  }
    194 }
    195 
    196 static void Register(BrowsingContext* aBrowsingContext) {
    197  sBrowsingContexts->InsertOrUpdate(aBrowsingContext->Id(), aBrowsingContext);
    198  if (aBrowsingContext->IsTopContent()) {
    199    sCurrentTopByBrowserId->InsertOrUpdate(aBrowsingContext->BrowserId(),
    200                                           aBrowsingContext);
    201  }
    202 
    203  aBrowsingContext->Group()->Register(aBrowsingContext);
    204 }
    205 
    206 // static
    207 void BrowsingContext::UpdateCurrentTopByBrowserId(
    208    BrowsingContext* aNewBrowsingContext) {
    209  if (aNewBrowsingContext->IsTopContent()) {
    210    sCurrentTopByBrowserId->InsertOrUpdate(aNewBrowsingContext->BrowserId(),
    211                                           aNewBrowsingContext);
    212  }
    213 }
    214 
    215 BrowsingContext* BrowsingContext::GetParent() const {
    216  return mParentWindow ? mParentWindow->GetBrowsingContext() : nullptr;
    217 }
    218 
    219 bool BrowsingContext::IsInSubtreeOf(BrowsingContext* aContext) {
    220  BrowsingContext* bc = this;
    221  do {
    222    if (bc == aContext) {
    223      return true;
    224    }
    225  } while ((bc = bc->GetParent()));
    226  return false;
    227 }
    228 
    229 BrowsingContext* BrowsingContext::Top() {
    230  BrowsingContext* bc = this;
    231  while (bc->mParentWindow) {
    232    bc = bc->GetParent();
    233  }
    234  return bc;
    235 }
    236 
    237 const BrowsingContext* BrowsingContext::Top() const {
    238  const BrowsingContext* bc = this;
    239  while (bc->mParentWindow) {
    240    bc = bc->GetParent();
    241  }
    242  return bc;
    243 }
    244 
    245 int32_t BrowsingContext::IndexOf(BrowsingContext* aChild) {
    246  int32_t index = -1;
    247  for (BrowsingContext* child : Children()) {
    248    ++index;
    249    if (child == aChild) {
    250      break;
    251    }
    252  }
    253  return index;
    254 }
    255 
    256 WindowContext* BrowsingContext::GetTopWindowContext() const {
    257  if (mParentWindow) {
    258    return mParentWindow->TopWindowContext();
    259  }
    260  return mCurrentWindowContext;
    261 }
    262 
    263 /* static */
    264 void BrowsingContext::Init() {
    265  if (!sBrowsingContexts) {
    266    sBrowsingContexts = new BrowsingContextMap();
    267    sCurrentTopByBrowserId = new BrowsingContextMap();
    268    ClearOnShutdown(&sBrowsingContexts);
    269    ClearOnShutdown(&sCurrentTopByBrowserId);
    270    CrashReporter::RegisterAnnotationBool(
    271        CrashReporter::Annotation::DOMIPCEnabled, &gIPCEnabledAnnotation);
    272    CrashReporter::RegisterAnnotationBool(
    273        CrashReporter::Annotation::DOMFissionEnabled,
    274        &gFissionEnabledAnnotation);
    275  }
    276 }
    277 
    278 /* static */
    279 LogModule* BrowsingContext::GetLog() { return gBrowsingContextLog; }
    280 
    281 /* static */
    282 LogModule* BrowsingContext::GetSyncLog() { return gBrowsingContextSyncLog; }
    283 
    284 /* static */
    285 already_AddRefed<BrowsingContext> BrowsingContext::Get(uint64_t aId) {
    286  if (sBrowsingContexts) {
    287    return do_AddRef(sBrowsingContexts->Get(aId));
    288  }
    289 
    290  return nullptr;
    291 }
    292 
    293 /* static */
    294 already_AddRefed<BrowsingContext> BrowsingContext::GetCurrentTopByBrowserId(
    295    uint64_t aBrowserId) {
    296  return do_AddRef(sCurrentTopByBrowserId->Get(aBrowserId));
    297 }
    298 
    299 /* static */
    300 already_AddRefed<BrowsingContext> BrowsingContext::GetFromWindow(
    301    WindowProxyHolder& aProxy) {
    302  return do_AddRef(aProxy.get());
    303 }
    304 
    305 CanonicalBrowsingContext* BrowsingContext::Canonical() {
    306  return CanonicalBrowsingContext::Cast(this);
    307 }
    308 
    309 bool BrowsingContext::IsOwnedByProcess() const {
    310  return mIsInProcess && mDocShell &&
    311         !nsDocShell::Cast(mDocShell)->WillChangeProcess();
    312 }
    313 
    314 bool BrowsingContext::SameOriginWithTop() {
    315  MOZ_ASSERT(IsInProcess());
    316  // If the top BrowsingContext is not same-process to us, it is cross-origin
    317  if (!Top()->IsInProcess()) {
    318    return false;
    319  }
    320 
    321  nsIDocShell* docShell = GetDocShell();
    322  if (!docShell) {
    323    return false;
    324  }
    325  Document* doc = docShell->GetDocument();
    326  if (!doc) {
    327    return false;
    328  }
    329  nsIPrincipal* principal = doc->NodePrincipal();
    330 
    331  nsIDocShell* topDocShell = Top()->GetDocShell();
    332  if (!topDocShell) {
    333    return false;
    334  }
    335  Document* topDoc = topDocShell->GetDocument();
    336  if (!topDoc) {
    337    return false;
    338  }
    339  nsIPrincipal* topPrincipal = topDoc->NodePrincipal();
    340 
    341  return principal->Equals(topPrincipal);
    342 }
    343 
    344 /* static */
    345 already_AddRefed<BrowsingContext> BrowsingContext::CreateDetached(
    346    nsGlobalWindowInner* aParent, BrowsingContext* aOpener,
    347    BrowsingContextGroup* aSpecificGroup, const nsAString& aName, Type aType,
    348    CreateDetachedOptions aOptions) {
    349  if (aParent) {
    350    MOZ_DIAGNOSTIC_ASSERT(aParent->GetWindowContext());
    351    MOZ_DIAGNOSTIC_ASSERT(aParent->GetBrowsingContext()->mType == aType);
    352    MOZ_DIAGNOSTIC_ASSERT(aParent->GetBrowsingContext()->GetBrowserId() != 0);
    353  }
    354 
    355  MOZ_DIAGNOSTIC_ASSERT(aType != Type::Chrome || XRE_IsParentProcess());
    356 
    357  uint64_t id = nsContentUtils::GenerateBrowsingContextId();
    358 
    359  MOZ_LOG(GetLog(), LogLevel::Debug,
    360          ("Creating 0x%08" PRIx64 " in %s", id,
    361           XRE_IsParentProcess() ? "Parent" : "Child"));
    362 
    363  RefPtr<BrowsingContext> parentBC =
    364      aParent ? aParent->GetBrowsingContext() : nullptr;
    365  RefPtr<WindowContext> parentWC =
    366      aParent ? aParent->GetWindowContext() : nullptr;
    367  BrowsingContext* inherit = parentBC ? parentBC.get() : aOpener;
    368 
    369  // Determine which BrowsingContextGroup this context should be created in.
    370  RefPtr<BrowsingContextGroup> group = aSpecificGroup;
    371  if (aType == Type::Chrome) {
    372    MOZ_DIAGNOSTIC_ASSERT(!group);
    373    group = BrowsingContextGroup::GetChromeGroup();
    374  } else if (!group) {
    375    group = BrowsingContextGroup::Select(parentWC, aOpener);
    376  }
    377 
    378  // Configure initial values for synced fields.
    379  FieldValues fields;
    380  fields.Get<IDX_Name>() = aName;
    381 
    382  if (aOpener) {
    383    MOZ_DIAGNOSTIC_ASSERT(!aParent,
    384                          "new BC with both initial opener and parent");
    385    MOZ_DIAGNOSTIC_ASSERT(aOpener->Group() == group);
    386    MOZ_DIAGNOSTIC_ASSERT(aOpener->mType == aType);
    387    fields.Get<IDX_OpenerId>() = aOpener->Id();
    388    fields.Get<IDX_HadOriginalOpener>() = true;
    389    fields.Get<IDX_MessageManagerGroup>() =
    390        aOpener->Top()->GetMessageManagerGroup();
    391 
    392    if (aType == Type::Chrome && !aParent) {
    393      // See SetOpener for why we do this inheritance.
    394      fields.Get<IDX_PrefersColorSchemeOverride>() =
    395          aOpener->Top()->GetPrefersColorSchemeOverride();
    396    }
    397  }
    398 
    399  if (aParent) {
    400    MOZ_DIAGNOSTIC_ASSERT(parentBC->Group() == group);
    401    MOZ_DIAGNOSTIC_ASSERT(parentBC->mType == aType);
    402    fields.Get<IDX_EmbedderInnerWindowId>() = aParent->WindowID();
    403    // Non-toplevel content documents are always embededed within content.
    404    fields.Get<IDX_EmbeddedInContentDocument>() =
    405        parentBC->mType == Type::Content;
    406 
    407    // XXX(farre): Can/Should we check aParent->IsLoading() here? (Bug
    408    // 1608448) Check if the parent was itself loading already
    409    auto readystate = aParent->GetDocument()->GetReadyStateEnum();
    410    fields.Get<IDX_AncestorLoading>() =
    411        parentBC->GetAncestorLoading() ||
    412        readystate == Document::ReadyState::READYSTATE_LOADING ||
    413        readystate == Document::ReadyState::READYSTATE_INTERACTIVE;
    414  }
    415 
    416  fields.Get<IDX_BrowserId>() =
    417      parentBC ? parentBC->GetBrowserId() : nsContentUtils::GenerateBrowserId();
    418 
    419  fields.Get<IDX_OpenerPolicy>() = nsILoadInfo::OPENER_POLICY_UNSAFE_NONE;
    420  if (aOpener && aOpener->SameOriginWithTop()) {
    421    // We inherit the opener policy if there is a creator and if the creator's
    422    // origin is same origin with the creator's top-level origin.
    423    // If it is cross origin we should not inherit the CrossOriginOpenerPolicy
    424    fields.Get<IDX_OpenerPolicy>() = aOpener->Top()->GetOpenerPolicy();
    425 
    426    // If we inherit a policy which is potentially cross-origin isolated, we
    427    // must be in a potentially cross-origin isolated BCG.
    428    bool isPotentiallyCrossOriginIsolated =
    429        fields.Get<IDX_OpenerPolicy>() ==
    430        nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP;
    431    MOZ_RELEASE_ASSERT(isPotentiallyCrossOriginIsolated ==
    432                       group->IsPotentiallyCrossOriginIsolated());
    433  } else if (aOpener) {
    434    // They are not same origin
    435    auto topPolicy = aOpener->Top()->GetOpenerPolicy();
    436    MOZ_RELEASE_ASSERT(
    437        topPolicy == nsILoadInfo::OPENER_POLICY_UNSAFE_NONE ||
    438        topPolicy == nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_ALLOW_POPUPS ||
    439        aOptions.isForPrinting);
    440    if (aOptions.isForPrinting) {
    441      // Ensure our opener policy is consistent for printing for our top.
    442      fields.Get<IDX_OpenerPolicy>() = topPolicy;
    443    }
    444  } else if (!aParent && group->IsPotentiallyCrossOriginIsolated()) {
    445    // If we're creating a brand-new toplevel BC in a potentially cross-origin
    446    // isolated group, it should start out with a strict opener policy.
    447    fields.Get<IDX_OpenerPolicy>() =
    448        nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP;
    449  }
    450 
    451  fields.Get<IDX_HistoryID>() = nsID::GenerateUUID();
    452  fields.Get<IDX_ExplicitActive>() = [&] {
    453    if (parentBC || aType == Type::Content) {
    454      // Non-root browsing-contexts inherit their status from its parent.
    455      // Top-content either gets managed by the top chrome, or gets manually
    456      // managed by the front-end (see ManuallyManagesActiveness). In any case
    457      // we want to start off as inactive.
    458      return ExplicitActiveStatus::None;
    459    }
    460    // Chrome starts as active.
    461    return ExplicitActiveStatus::Active;
    462  }();
    463 
    464  fields.Get<IDX_FullZoom>() = parentBC ? parentBC->FullZoom() : 1.0f;
    465  fields.Get<IDX_TextZoom>() = parentBC ? parentBC->TextZoom() : 1.0f;
    466 
    467  bool allowContentRetargeting =
    468      inherit ? inherit->GetAllowContentRetargetingOnChildren() : true;
    469  fields.Get<IDX_AllowContentRetargeting>() = allowContentRetargeting;
    470  fields.Get<IDX_AllowContentRetargetingOnChildren>() = allowContentRetargeting;
    471 
    472  // Assume top allows fullscreen for its children unless otherwise stated.
    473  // Subframes start with it false unless otherwise noted in SetEmbedderElement.
    474  fields.Get<IDX_FullscreenAllowedByOwner>() = !aParent;
    475 
    476  fields.Get<IDX_DefaultLoadFlags>() =
    477      inherit ? inherit->GetDefaultLoadFlags() : nsIRequest::LOAD_NORMAL;
    478 
    479  fields.Get<IDX_OrientationLock>() = mozilla::hal::ScreenOrientation::None;
    480 
    481  fields.Get<IDX_UseGlobalHistory>() =
    482      inherit ? inherit->GetUseGlobalHistory() : false;
    483 
    484  fields.Get<IDX_UseErrorPages>() = true;
    485 
    486  fields.Get<IDX_TouchEventsOverrideInternal>() = TouchEventsOverride::None;
    487 
    488  fields.Get<IDX_AllowJavascript>() =
    489      inherit ? inherit->GetAllowJavascript() : true;
    490 
    491  fields.Get<IDX_IPAddressSpace>() = inherit
    492                                         ? inherit->GetIPAddressSpace()
    493                                         : nsILoadInfo::IPAddressSpace::Unknown;
    494 
    495  bool parentalControlsEnabled;
    496  if (inherit) {
    497    parentalControlsEnabled = inherit->GetParentalControlsEnabled();
    498  } else if (XRE_IsParentProcess()) {
    499    parentalControlsEnabled =
    500        CanonicalBrowsingContext::ShouldEnforceParentalControls();
    501  } else {
    502    parentalControlsEnabled = false;
    503  }
    504 
    505  fields.Get<IDX_ParentalControlsEnabled>() = parentalControlsEnabled;
    506 
    507  fields.Get<IDX_IsPopupRequested>() = aOptions.isPopupRequested;
    508 
    509  fields.Get<IDX_TopLevelCreatedByWebContent>() =
    510      aOptions.topLevelCreatedByWebContent;
    511 
    512  if (!parentBC) {
    513    fields.Get<IDX_ShouldDelayMediaFromStart>() =
    514        StaticPrefs::media_block_autoplay_until_in_foreground();
    515  }
    516 
    517  RefPtr<BrowsingContext> context;
    518  if (XRE_IsParentProcess()) {
    519    context = new CanonicalBrowsingContext(parentWC, group, id,
    520                                           /* aOwnerProcessId */ 0,
    521                                           /* aEmbedderProcessId */ 0, aType,
    522                                           std::move(fields));
    523  } else {
    524    context =
    525        new BrowsingContext(parentWC, group, id, aType, std::move(fields));
    526  }
    527 
    528  context->mWindowless = aOptions.windowless;
    529  context->mEmbeddedByThisProcess = XRE_IsParentProcess() || aParent;
    530  context->mCreatedDynamically = aOptions.createdDynamically;
    531  if (inherit) {
    532    context->mPrivateBrowsingId = inherit->mPrivateBrowsingId;
    533    context->mUseRemoteTabs = inherit->mUseRemoteTabs;
    534    context->mUseRemoteSubframes = inherit->mUseRemoteSubframes;
    535    context->mOriginAttributes = inherit->mOriginAttributes;
    536  }
    537 
    538  nsCOMPtr<nsIRequestContextService> rcsvc =
    539      net::RequestContextService::GetOrCreate();
    540  if (rcsvc) {
    541    nsCOMPtr<nsIRequestContext> requestContext;
    542    nsresult rv = rcsvc->NewRequestContext(getter_AddRefs(requestContext));
    543    if (NS_SUCCEEDED(rv) && requestContext) {
    544      context->mRequestContextId = requestContext->GetID();
    545    }
    546  }
    547 
    548  return context.forget();
    549 }
    550 
    551 already_AddRefed<BrowsingContext> BrowsingContext::CreateIndependent(
    552    Type aType, bool aWindowless) {
    553  MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess(),
    554                        "BCs created in the content process must be related to "
    555                        "some BrowserChild");
    556  RefPtr<BrowsingContext> bc(
    557      CreateDetached(nullptr, nullptr, nullptr, u""_ns, aType, {}));
    558  bc->mWindowless = aWindowless;
    559  bc->mEmbeddedByThisProcess = true;
    560  bc->EnsureAttached();
    561  return bc.forget();
    562 }
    563 
    564 void BrowsingContext::EnsureAttached() {
    565  if (!mEverAttached) {
    566    Register(this);
    567 
    568    // Attach the browsing context to the tree.
    569    Attach(/* aFromIPC */ false, /* aOriginProcess */ nullptr);
    570  }
    571 }
    572 
    573 /* static */
    574 mozilla::ipc::IPCResult BrowsingContext::CreateFromIPC(
    575    BrowsingContext::IPCInitializer&& aInit, BrowsingContextGroup* aGroup,
    576    ContentParent* aOriginProcess) {
    577  MOZ_DIAGNOSTIC_ASSERT(aOriginProcess || XRE_IsContentProcess());
    578  MOZ_DIAGNOSTIC_ASSERT(aGroup);
    579 
    580  uint64_t originId = 0;
    581  if (aOriginProcess) {
    582    originId = aOriginProcess->ChildID();
    583    aGroup->EnsureHostProcess(aOriginProcess);
    584  }
    585 
    586  MOZ_LOG(GetLog(), LogLevel::Debug,
    587          ("Creating 0x%08" PRIx64 " from IPC (origin=0x%08" PRIx64 ")",
    588           aInit.mId, originId));
    589 
    590  RefPtr<WindowContext> parent = aInit.GetParent();
    591 
    592  RefPtr<BrowsingContext> context;
    593  if (XRE_IsParentProcess()) {
    594    // If the new BrowsingContext has a parent, it is a sub-frame embedded in
    595    // whatever process sent the message. If it doesn't, and is not windowless,
    596    // it is a new window or tab, and will be embedded in the parent process.
    597    uint64_t embedderProcessId = (aInit.mWindowless || parent) ? originId : 0;
    598    context = new CanonicalBrowsingContext(parent, aGroup, aInit.mId, originId,
    599                                           embedderProcessId, Type::Content,
    600                                           std::move(aInit.mFields));
    601  } else {
    602    context = new BrowsingContext(parent, aGroup, aInit.mId, Type::Content,
    603                                  std::move(aInit.mFields));
    604  }
    605 
    606  context->mWindowless = aInit.mWindowless;
    607  context->mCreatedDynamically = aInit.mCreatedDynamically;
    608  context->mChildOffset = aInit.mChildOffset;
    609  if (context->GetHasSessionHistory()) {
    610    context->CreateChildSHistory();
    611    if (mozilla::SessionHistoryInParent()) {
    612      context->GetChildSessionHistory()->SetIndexAndLength(
    613          aInit.mSessionHistoryIndex, aInit.mSessionHistoryCount, nsID());
    614    }
    615  }
    616 
    617  // NOTE: Call through the `Set` methods for these values to ensure that any
    618  // relevant process-local state is also updated.
    619  context->SetOriginAttributes(aInit.mOriginAttributes);
    620  context->SetRemoteTabs(aInit.mUseRemoteTabs);
    621  context->SetRemoteSubframes(aInit.mUseRemoteSubframes);
    622  context->mRequestContextId = aInit.mRequestContextId;
    623  // NOTE: Private browsing ID is set by `SetOriginAttributes`.
    624 
    625  if (const char* failure =
    626          context->BrowsingContextCoherencyChecks(aOriginProcess)) {
    627    mozilla::ipc::IProtocol* actor = aOriginProcess;
    628    if (!actor) {
    629      actor = ContentChild::GetSingleton();
    630    }
    631    return IPC_FAIL_UNSAFE_PRINTF(actor, "Incoherent BrowsingContext: %s",
    632                                  failure);
    633  }
    634 
    635  Register(context);
    636 
    637  context->Attach(/* aFromIPC */ true, aOriginProcess);
    638  return IPC_OK();
    639 }
    640 
    641 BrowsingContext::BrowsingContext(WindowContext* aParentWindow,
    642                                 BrowsingContextGroup* aGroup,
    643                                 uint64_t aBrowsingContextId, Type aType,
    644                                 FieldValues&& aInit)
    645    : mFields(std::move(aInit)),
    646      mType(aType),
    647      mBrowsingContextId(aBrowsingContextId),
    648      mGroup(aGroup),
    649      mParentWindow(aParentWindow),
    650      mPrivateBrowsingId(0),
    651      mEverAttached(false),
    652      mIsInProcess(false),
    653      mIsDiscarded(false),
    654      mWindowless(false),
    655      mDanglingRemoteOuterProxies(false),
    656      mEmbeddedByThisProcess(false),
    657      mUseRemoteTabs(false),
    658      mUseRemoteSubframes(false),
    659      mCreatedDynamically(false),
    660      mIsInBFCache(false),
    661      mCanExecuteScripts(true),
    662      mChildOffset(0) {
    663  MOZ_RELEASE_ASSERT(!mParentWindow || mParentWindow->Group() == mGroup);
    664  MOZ_RELEASE_ASSERT(mBrowsingContextId != 0);
    665  MOZ_RELEASE_ASSERT(mGroup);
    666 }
    667 
    668 void BrowsingContext::SetDocShell(nsIDocShell* aDocShell) {
    669  // XXX(nika): We should communicate that we are now an active BrowsingContext
    670  // process to the parent & do other validation here.
    671  MOZ_DIAGNOSTIC_ASSERT(mEverAttached);
    672  MOZ_RELEASE_ASSERT(aDocShell->GetBrowsingContext() == this);
    673  mDocShell = aDocShell;
    674  mDanglingRemoteOuterProxies = !mIsInProcess;
    675  mIsInProcess = true;
    676  if (mChildSessionHistory) {
    677    mChildSessionHistory->SetIsInProcess(true);
    678  }
    679 
    680  RecomputeCanExecuteScripts();
    681  ClearCachedValuesOfLocations();
    682 }
    683 
    684 // This class implements a callback that will return the remote window proxy for
    685 // mBrowsingContext in that compartment, if it has one. It also removes the
    686 // proxy from the map, because the object will be transplanted into another kind
    687 // of object.
    688 class MOZ_STACK_CLASS CompartmentRemoteProxyTransplantCallback
    689    : public js::CompartmentTransplantCallback {
    690 public:
    691  explicit CompartmentRemoteProxyTransplantCallback(
    692      BrowsingContext* aBrowsingContext)
    693      : mBrowsingContext(aBrowsingContext) {}
    694 
    695  virtual JSObject* getObjectToTransplant(
    696      JS::Compartment* compartment) override {
    697    auto* priv = xpc::CompartmentPrivate::Get(compartment);
    698    if (!priv) {
    699      return nullptr;
    700    }
    701 
    702    auto& map = priv->GetRemoteProxyMap();
    703    auto result = map.lookup(mBrowsingContext);
    704    if (!result) {
    705      return nullptr;
    706    }
    707    JSObject* resultObject = result->value();
    708    map.remove(result);
    709 
    710    return resultObject;
    711  }
    712 
    713 private:
    714  BrowsingContext* mBrowsingContext;
    715 };
    716 
    717 void BrowsingContext::CleanUpDanglingRemoteOuterWindowProxies(
    718    JSContext* aCx, JS::MutableHandle<JSObject*> aOuter) {
    719  if (!mDanglingRemoteOuterProxies) {
    720    return;
    721  }
    722  mDanglingRemoteOuterProxies = false;
    723 
    724  CompartmentRemoteProxyTransplantCallback cb(this);
    725  js::RemapRemoteWindowProxies(aCx, &cb, aOuter);
    726 }
    727 
    728 bool BrowsingContext::IsActive() const {
    729  const BrowsingContext* current = this;
    730  do {
    731    auto explicit_ = current->GetExplicitActive();
    732    if (explicit_ != ExplicitActiveStatus::None) {
    733      return explicit_ == ExplicitActiveStatus::Active;
    734    }
    735    if (mParentWindow && !mParentWindow->IsCurrent()) {
    736      return false;
    737    }
    738  } while ((current = current->GetParent()));
    739 
    740  return false;
    741 }
    742 
    743 bool BrowsingContext::GetIsActiveBrowserWindow() {
    744  if (!XRE_IsParentProcess()) {
    745    return Top()->GetIsActiveBrowserWindowInternal();
    746  }
    747 
    748  // chrome:// urls loaded in the parent won't receive
    749  // their own activation so we defer to the top chrome
    750  // Browsing Context when in the parent process.
    751  return Canonical()
    752      ->TopCrossChromeBoundary()
    753      ->GetIsActiveBrowserWindowInternal();
    754 }
    755 
    756 void BrowsingContext::SetIsActiveBrowserWindow(bool aActive) {
    757  (void)SetIsActiveBrowserWindowInternal(aActive);
    758 }
    759 
    760 bool BrowsingContext::FullscreenAllowed() const {
    761  for (auto* current = this; current; current = current->GetParent()) {
    762    if (!current->GetFullscreenAllowedByOwner()) {
    763      return false;
    764    }
    765  }
    766  return true;
    767 }
    768 
    769 static bool OwnerAllowsFullscreen(const Element& aEmbedder) {
    770  if (aEmbedder.IsXULElement()) {
    771    return !aEmbedder.HasAttr(nsGkAtoms::disablefullscreen);
    772  }
    773  if (aEmbedder.IsHTMLElement(nsGkAtoms::iframe)) {
    774    // This is controlled by feature policy.
    775    return true;
    776  }
    777  if (const auto* embed = HTMLEmbedElement::FromNode(aEmbedder)) {
    778    return embed->AllowFullscreen();
    779  }
    780  return false;
    781 }
    782 
    783 void BrowsingContext::SetEmbedderElement(Element* aEmbedder) {
    784  mEmbeddedByThisProcess = true;
    785 
    786  if (RefPtr<WindowContext> parent = GetParentWindowContext()) {
    787    parent->ClearLightDOMChildren();
    788  }
    789 
    790  // Update embedder-element-specific fields in a shared transaction.
    791  // Don't do this when clearing our embedder, as we're being destroyed either
    792  // way.
    793  if (aEmbedder) {
    794    Transaction txn;
    795    txn.SetEmbedderElementType(Some(aEmbedder->LocalName()));
    796    txn.SetEmbeddedInContentDocument(
    797        aEmbedder->OwnerDoc()->IsContentDocument());
    798    if (nsCOMPtr<nsPIDOMWindowInner> inner =
    799            do_QueryInterface(aEmbedder->GetOwnerGlobal())) {
    800      txn.SetEmbedderInnerWindowId(inner->WindowID());
    801    }
    802    txn.SetFullscreenAllowedByOwner(OwnerAllowsFullscreen(*aEmbedder));
    803    if (XRE_IsParentProcess() && aEmbedder->IsXULElement() && IsTopContent()) {
    804      nsAutoString messageManagerGroup;
    805      aEmbedder->GetAttr(nsGkAtoms::messagemanagergroup, messageManagerGroup);
    806      txn.SetMessageManagerGroup(messageManagerGroup);
    807      txn.SetUseGlobalHistory(
    808          !aEmbedder->HasAttr(nsGkAtoms::disableglobalhistory));
    809      if (!aEmbedder->HasAttr(nsGkAtoms::manualactiveness)) {
    810        // We're active iff the parent cross-chrome-boundary is active. Note we
    811        // can't just use this->Canonical()->GetParentCrossChromeBoundary here,
    812        // since mEmbedderElement is still null at this point.
    813        RefPtr bc = aEmbedder->OwnerDoc()->GetBrowsingContext();
    814        const bool isActive = bc && bc->IsActive();
    815        txn.SetExplicitActive(isActive ? ExplicitActiveStatus::Active
    816                                       : ExplicitActiveStatus::Inactive);
    817        if (auto* bp = Canonical()->GetBrowserParent()) {
    818          bp->SetRenderLayers(isActive);
    819        }
    820      }
    821    }
    822 
    823    MOZ_ALWAYS_SUCCEEDS(txn.Commit(this));
    824  }
    825 
    826  if (XRE_IsParentProcess() && IsTopContent()) {
    827    Canonical()->MaybeSetPermanentKey(aEmbedder);
    828  }
    829 
    830  mEmbedderElement = aEmbedder;
    831 
    832  if (mEmbedderElement) {
    833    if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
    834      obs->NotifyWhenScriptSafe(ToSupports(this),
    835                                "browsing-context-did-set-embedder", nullptr);
    836    }
    837 
    838    if (IsEmbedderTypeObjectOrEmbed()) {
    839      (void)SetIsSyntheticDocumentContainer(true);
    840    }
    841  }
    842 }
    843 
    844 bool BrowsingContext::IsEmbedderTypeObjectOrEmbed() {
    845  if (const Maybe<nsString>& type = GetEmbedderElementType()) {
    846    return nsGkAtoms::object->Equals(*type) || nsGkAtoms::embed->Equals(*type);
    847  }
    848  return false;
    849 }
    850 
    851 void BrowsingContext::Embed() {
    852  if (auto* frame = HTMLIFrameElement::FromNode(mEmbedderElement)) {
    853    frame->BindToBrowsingContext(this);
    854  }
    855 }
    856 
    857 const char* BrowsingContext::BrowsingContextCoherencyChecks(
    858    ContentParent* aOriginProcess) {
    859 #define COHERENCY_ASSERT(condition) \
    860  if (!(condition)) return "Assertion " #condition " failed";
    861 
    862  if (mGroup->IsPotentiallyCrossOriginIsolated() !=
    863      (Top()->GetOpenerPolicy() ==
    864       nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP)) {
    865    return "Invalid CrossOriginIsolated state";
    866  }
    867 
    868  if (aOriginProcess && !IsContent()) {
    869    return "Content cannot create chrome BCs";
    870  }
    871 
    872  // LoadContext should generally match our opener or parent.
    873  if (IsContent()) {
    874    if (RefPtr<BrowsingContext> opener = GetOpener()) {
    875      COHERENCY_ASSERT(opener->mType == mType);
    876      COHERENCY_ASSERT(opener->mGroup == mGroup);
    877      COHERENCY_ASSERT(opener->mUseRemoteTabs == mUseRemoteTabs);
    878      COHERENCY_ASSERT(opener->mUseRemoteSubframes == mUseRemoteSubframes);
    879      COHERENCY_ASSERT(opener->mPrivateBrowsingId == mPrivateBrowsingId);
    880      COHERENCY_ASSERT(
    881          opener->mOriginAttributes.EqualsIgnoringFPD(mOriginAttributes));
    882    }
    883  }
    884  if (RefPtr<BrowsingContext> parent = GetParent()) {
    885    COHERENCY_ASSERT(parent->mType == mType);
    886    COHERENCY_ASSERT(parent->mGroup == mGroup);
    887    COHERENCY_ASSERT(parent->mUseRemoteTabs == mUseRemoteTabs);
    888    COHERENCY_ASSERT(parent->mUseRemoteSubframes == mUseRemoteSubframes);
    889    COHERENCY_ASSERT(parent->mPrivateBrowsingId == mPrivateBrowsingId);
    890    COHERENCY_ASSERT(
    891        parent->mOriginAttributes.EqualsIgnoringFPD(mOriginAttributes));
    892  }
    893 
    894  // UseRemoteSubframes and UseRemoteTabs must match.
    895  if (mUseRemoteSubframes && !mUseRemoteTabs) {
    896    return "Cannot set useRemoteSubframes without also setting useRemoteTabs";
    897  }
    898 
    899  // Double-check OriginAttributes/Private Browsing
    900  // Chrome browsing contexts must not have a private browsing OriginAttribute
    901  // Content browsing contexts must maintain the equality:
    902  // mOriginAttributes.mPrivateBrowsingId == mPrivateBrowsingId
    903  if (IsChrome()) {
    904    COHERENCY_ASSERT(mOriginAttributes.mPrivateBrowsingId == 0);
    905  } else {
    906    COHERENCY_ASSERT(mOriginAttributes.mPrivateBrowsingId ==
    907                     mPrivateBrowsingId);
    908  }
    909 #undef COHERENCY_ASSERT
    910 
    911  return nullptr;
    912 }
    913 
    914 void BrowsingContext::Attach(bool aFromIPC, ContentParent* aOriginProcess) {
    915  MOZ_DIAGNOSTIC_ASSERT(!mEverAttached);
    916  MOZ_DIAGNOSTIC_ASSERT_IF(aFromIPC, aOriginProcess || XRE_IsContentProcess());
    917  mEverAttached = true;
    918 
    919  if (MOZ_LOG_TEST(GetLog(), LogLevel::Debug)) {
    920    nsAutoCString suffix;
    921    mOriginAttributes.CreateSuffix(suffix);
    922    MOZ_LOG(GetLog(), LogLevel::Debug,
    923            ("%s: Connecting 0x%08" PRIx64 " to 0x%08" PRIx64
    924             " (private=%d, remote=%d, fission=%d, oa=%s)",
    925             XRE_IsParentProcess() ? "Parent" : "Child", Id(),
    926             GetParent() ? GetParent()->Id() : 0, (int)mPrivateBrowsingId,
    927             (int)mUseRemoteTabs, (int)mUseRemoteSubframes, suffix.get()));
    928  }
    929 
    930  MOZ_DIAGNOSTIC_ASSERT(mGroup);
    931  MOZ_DIAGNOSTIC_ASSERT(!mIsDiscarded);
    932 
    933 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
    934  // We'll already have checked this if `aFromIPC` is set before calling this
    935  // function.
    936  if (!aFromIPC) {
    937    if (const char* failure = BrowsingContextCoherencyChecks(aOriginProcess)) {
    938      MOZ_CRASH_UNSAFE_PRINTF("Incoherent BrowsingContext: %s", failure);
    939    }
    940  }
    941 #endif
    942 
    943  // Add ourselves either to our parent or BrowsingContextGroup's child list.
    944  // Important: We shouldn't return IPC_FAIL after this point, since the
    945  // BrowsingContext will have already been added to the tree.
    946  if (mParentWindow) {
    947    if (!aFromIPC) {
    948      MOZ_DIAGNOSTIC_ASSERT(!mParentWindow->IsDiscarded(),
    949                            "local attach in discarded window");
    950      MOZ_DIAGNOSTIC_ASSERT(!GetParent()->IsDiscarded(),
    951                            "local attach call in discarded bc");
    952      MOZ_DIAGNOSTIC_ASSERT(mParentWindow->GetWindowGlobalChild(),
    953                            "local attach call with oop parent window");
    954      MOZ_DIAGNOSTIC_ASSERT(mParentWindow->GetWindowGlobalChild()->CanSend(),
    955                            "local attach call with dead parent window");
    956    }
    957    mChildOffset =
    958        mCreatedDynamically ? -1 : mParentWindow->Children().Length();
    959    mParentWindow->AppendChildBrowsingContext(this);
    960    RecomputeCanExecuteScripts();
    961  } else {
    962    mGroup->Toplevels().AppendElement(this);
    963  }
    964 
    965  if (GetIsPopupSpam()) {
    966    PopupBlocker::RegisterOpenPopupSpam();
    967  }
    968 
    969  if (IsTop() && GetHasSessionHistory() && !mChildSessionHistory) {
    970    CreateChildSHistory();
    971  }
    972 
    973  // Why the context is being attached. This will always be "attach" in the
    974  // content process, but may be "replace" if it's known the context being
    975  // replaced in the parent process.
    976  const char16_t* why = u"attach";
    977 
    978  if (XRE_IsContentProcess() && !aFromIPC) {
    979    // Send attach to our parent if we need to.
    980    ContentChild::GetSingleton()->SendCreateBrowsingContext(
    981        mGroup->Id(), GetIPCInitializer());
    982  } else if (XRE_IsParentProcess()) {
    983    // If this window was created as a subframe by a content process, it must be
    984    // being hosted within the same BrowserParent as its mParentWindow.
    985    // Toplevel BrowsingContexts created by content have their BrowserParent
    986    // configured during `RecvConstructPopupBrowser`.
    987    if (mParentWindow && aOriginProcess) {
    988      MOZ_DIAGNOSTIC_ASSERT(
    989          mParentWindow->Canonical()->GetContentParent() == aOriginProcess,
    990          "Creator process isn't the same as our embedder?");
    991      Canonical()->SetCurrentBrowserParent(
    992          mParentWindow->Canonical()->GetBrowserParent());
    993    }
    994 
    995    mGroup->EachOtherParent(aOriginProcess, [&](ContentParent* aParent) {
    996      MOZ_DIAGNOSTIC_ASSERT(IsContent(),
    997                            "chrome BCG cannot be synced to content process");
    998      if (!Canonical()->IsEmbeddedInProcess(aParent->ChildID())) {
    999        (void)aParent->SendCreateBrowsingContext(mGroup->Id(),
   1000                                                 GetIPCInitializer());
   1001      }
   1002    });
   1003 
   1004    if (IsTop() && IsContent() && Canonical()->GetWebProgress()) {
   1005      why = u"replace";
   1006    }
   1007 
   1008    // We want to create a BrowsingContextWebProgress for all content
   1009    // BrowsingContexts.
   1010    if (IsContent() && !Canonical()->mWebProgress) {
   1011      Canonical()->mWebProgress = new BrowsingContextWebProgress(Canonical());
   1012    }
   1013  }
   1014 
   1015  if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
   1016    obs->NotifyWhenScriptSafe(ToSupports(this), "browsing-context-attached",
   1017                              why);
   1018  }
   1019 
   1020  if (XRE_IsParentProcess()) {
   1021    Canonical()->CanonicalAttach();
   1022  }
   1023 }
   1024 
   1025 void BrowsingContext::Detach(bool aFromIPC) {
   1026  MOZ_LOG(GetLog(), LogLevel::Debug,
   1027          ("%s: Detaching 0x%08" PRIx64 " from 0x%08" PRIx64,
   1028           XRE_IsParentProcess() ? "Parent" : "Child", Id(),
   1029           GetParent() ? GetParent()->Id() : 0));
   1030 
   1031  MOZ_DIAGNOSTIC_ASSERT(mEverAttached);
   1032  MOZ_DIAGNOSTIC_ASSERT(!mIsDiscarded);
   1033 
   1034  if (XRE_IsParentProcess()) {
   1035    Canonical()->AddPendingDiscard();
   1036    Canonical()->mActiveEntryList = nullptr;
   1037  }
   1038  auto callListeners =
   1039      MakeScopeExit([&, listeners = std::move(mDiscardListeners), id = Id()] {
   1040        for (const auto& listener : listeners) {
   1041          listener(id);
   1042        }
   1043        if (XRE_IsParentProcess()) {
   1044          Canonical()->RemovePendingDiscard();
   1045        }
   1046      });
   1047 
   1048  nsCOMPtr<nsIRequestContextService> rcsvc =
   1049      net::RequestContextService::GetOrCreate();
   1050  if (rcsvc) {
   1051    rcsvc->RemoveRequestContext(GetRequestContextId());
   1052  }
   1053 
   1054  // This will only ever be null if the cycle-collector has unlinked us. Don't
   1055  // try to detach ourselves in that case.
   1056  if (NS_WARN_IF(!mGroup)) {
   1057    MOZ_ASSERT_UNREACHABLE();
   1058    return;
   1059  }
   1060 
   1061  if (mParentWindow) {
   1062    mParentWindow->RemoveChildBrowsingContext(this);
   1063  } else {
   1064    mGroup->Toplevels().RemoveElement(this);
   1065  }
   1066 
   1067  if (XRE_IsParentProcess()) {
   1068    RefPtr<CanonicalBrowsingContext> self{Canonical()};
   1069    Group()->EachParent([&](ContentParent* aParent) {
   1070      // Only the embedder process is allowed to initiate a BrowsingContext
   1071      // detach, so if we've gotten here, the host process already knows we've
   1072      // been detached, and there's no need to tell it again.
   1073      //
   1074      // If the owner process is not the same as the embedder process, its
   1075      // BrowsingContext will be detached when its nsWebBrowser instance is
   1076      // destroyed.
   1077      bool doDiscard = !Canonical()->IsEmbeddedInProcess(aParent->ChildID()) &&
   1078                       !Canonical()->IsOwnedByProcess(aParent->ChildID());
   1079 
   1080      // Hold a strong reference to ourself, and keep our BrowsingContextGroup
   1081      // alive, until the responses comes back to ensure we don't die while
   1082      // messages relating to this context are in-flight.
   1083      //
   1084      // When the callback is called, the keepalive on our group will be
   1085      // destroyed, and the reference to the BrowsingContext will be dropped,
   1086      // which may cause it to be fully destroyed.
   1087      mGroup->AddKeepAlive();
   1088      self->AddPendingDiscard();
   1089      auto callback = [self](auto) {
   1090        self->mGroup->RemoveKeepAlive();
   1091        self->RemovePendingDiscard();
   1092      };
   1093 
   1094      aParent->SendDiscardBrowsingContext(this, doDiscard, callback, callback);
   1095    });
   1096  } else {
   1097    // Hold a strong reference to ourself until the responses come back to
   1098    // ensure the BrowsingContext isn't cleaned up before the parent process
   1099    // acknowledges the discard request.
   1100    auto callback = [self = RefPtr{this}](auto) {};
   1101    ContentChild::GetSingleton()->SendDiscardBrowsingContext(
   1102        this, !aFromIPC, callback, callback);
   1103  }
   1104 
   1105  mGroup->Unregister(this);
   1106  UnregisterBrowserId(this);
   1107  mIsDiscarded = true;
   1108 
   1109  if (XRE_IsParentProcess()) {
   1110    nsFocusManager* fm = nsFocusManager::GetFocusManager();
   1111    if (fm) {
   1112      fm->BrowsingContextDetached(this);
   1113    }
   1114  }
   1115 
   1116  if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
   1117    // Why the context is being discarded. This will always be "discard" in the
   1118    // content process, but may be "replace" if it's known the context being
   1119    // replaced in the parent process.
   1120    const char16_t* why = u"discard";
   1121    if (XRE_IsParentProcess() && Canonical()->IsReplaced()) {
   1122      why = u"replace";
   1123    }
   1124    obs->NotifyObservers(ToSupports(this), "browsing-context-discarded", why);
   1125  }
   1126 
   1127  // NOTE: Doesn't use SetClosed, as it will be set in all processes
   1128  // automatically by calls to Detach()
   1129  mFields.SetWithoutSyncing<IDX_Closed>(true);
   1130 
   1131  if (GetIsPopupSpam()) {
   1132    PopupBlocker::UnregisterOpenPopupSpam();
   1133    // NOTE: Doesn't use SetIsPopupSpam, as it will be set all processes
   1134    // automatically.
   1135    mFields.SetWithoutSyncing<IDX_IsPopupSpam>(false);
   1136  }
   1137 
   1138  AssertOriginAttributesMatchPrivateBrowsing();
   1139 
   1140  if (XRE_IsParentProcess()) {
   1141    Canonical()->CanonicalDiscard();
   1142  }
   1143 }
   1144 
   1145 void BrowsingContext::AddDiscardListener(
   1146    std::function<void(uint64_t)>&& aListener) {
   1147  if (mIsDiscarded) {
   1148    aListener(Id());
   1149    return;
   1150  }
   1151  mDiscardListeners.AppendElement(std::move(aListener));
   1152 }
   1153 
   1154 void BrowsingContext::PrepareForProcessChange() {
   1155  MOZ_LOG(GetLog(), LogLevel::Debug,
   1156          ("%s: Preparing 0x%08" PRIx64 " for a process change",
   1157           XRE_IsParentProcess() ? "Parent" : "Child", Id()));
   1158 
   1159  MOZ_ASSERT(mIsInProcess, "Must currently be an in-process frame");
   1160  MOZ_ASSERT(!mIsDiscarded, "We're already closed?");
   1161 
   1162  mIsInProcess = false;
   1163  mUserGestureStart = TimeStamp();
   1164 
   1165  ClearCachedValuesOfLocations();
   1166 
   1167  // NOTE: For now, clear our nsDocShell reference, as we're primarily in a
   1168  // different process now. This may need to change in the future with
   1169  // Cross-Process BFCache.
   1170  mDocShell = nullptr;
   1171  if (mChildSessionHistory) {
   1172    // This can be removed once session history is stored exclusively in the
   1173    // parent process.
   1174    mChildSessionHistory->SetIsInProcess(false);
   1175  }
   1176 
   1177  if (!mWindowProxy) {
   1178    return;
   1179  }
   1180 
   1181  // We have to go through mWindowProxy rather than calling GetDOMWindow() on
   1182  // mDocShell because the mDocshell reference gets cleared immediately after
   1183  // the window is closed.
   1184  nsGlobalWindowOuter::PrepareForProcessChange(mWindowProxy);
   1185  MOZ_ASSERT(!mWindowProxy);
   1186 }
   1187 
   1188 bool BrowsingContext::IsTargetable() const {
   1189  return !GetClosed() && AncestorsAreCurrent();
   1190 }
   1191 
   1192 void BrowsingContext::SetOpener(BrowsingContext* aOpener) {
   1193  MOZ_DIAGNOSTIC_ASSERT(!aOpener || aOpener->Group() == Group());
   1194  MOZ_DIAGNOSTIC_ASSERT(!aOpener || aOpener->mType == mType);
   1195 
   1196  MOZ_ALWAYS_SUCCEEDS(SetOpenerId(aOpener ? aOpener->Id() : 0));
   1197 
   1198  if (IsChrome() && IsTop() && aOpener) {
   1199    // Inherit color scheme overrides from parent window. This is to inherit the
   1200    // color scheme of dark themed PBM windows in dialogs opened by such
   1201    // windows.
   1202    auto openerOverride = aOpener->Top()->PrefersColorSchemeOverride();
   1203    if (openerOverride != PrefersColorSchemeOverride()) {
   1204      MOZ_ALWAYS_SUCCEEDS(SetPrefersColorSchemeOverride(openerOverride));
   1205    }
   1206  }
   1207 }
   1208 
   1209 bool BrowsingContext::HasOpener() const {
   1210  if (sBrowsingContexts) {
   1211    return sBrowsingContexts->Contains(GetOpenerId());
   1212  }
   1213 
   1214  return false;
   1215 }
   1216 
   1217 bool BrowsingContext::AncestorsAreCurrent() const {
   1218  const BrowsingContext* bc = this;
   1219  while (true) {
   1220    if (bc->IsDiscarded()) {
   1221      return false;
   1222    }
   1223 
   1224    if (WindowContext* wc = bc->GetParentWindowContext()) {
   1225      if (!wc->IsCurrent() || wc->IsDiscarded()) {
   1226        return false;
   1227      }
   1228 
   1229      bc = wc->GetBrowsingContext();
   1230    } else {
   1231      return true;
   1232    }
   1233  }
   1234 }
   1235 
   1236 bool BrowsingContext::IsInBFCache() const {
   1237  if (mozilla::SessionHistoryInParent()) {
   1238    return mIsInBFCache;
   1239  }
   1240  return mParentWindow &&
   1241         mParentWindow->TopWindowContext()->GetWindowStateSaved();
   1242 }
   1243 
   1244 Span<RefPtr<BrowsingContext>> BrowsingContext::Children() const {
   1245  if (WindowContext* current = mCurrentWindowContext) {
   1246    return current->Children();
   1247  }
   1248  return Span<RefPtr<BrowsingContext>>();
   1249 }
   1250 
   1251 void BrowsingContext::GetChildren(
   1252    nsTArray<RefPtr<BrowsingContext>>& aChildren) {
   1253  aChildren.AppendElements(Children());
   1254 }
   1255 
   1256 Span<RefPtr<BrowsingContext>> BrowsingContext::NonSyntheticChildren() const {
   1257  if (WindowContext* current = mCurrentWindowContext) {
   1258    return current->NonSyntheticChildren();
   1259  }
   1260  return Span<RefPtr<BrowsingContext>>();
   1261 }
   1262 
   1263 BrowsingContext* BrowsingContext::NonSyntheticLightDOMChildAt(
   1264    uint32_t aIndex) const {
   1265  if (WindowContext* current = mCurrentWindowContext) {
   1266    return current->NonSyntheticLightDOMChildAt(aIndex);
   1267  }
   1268  return nullptr;
   1269 }
   1270 
   1271 uint32_t BrowsingContext::NonSyntheticLightDOMChildrenCount() const {
   1272  if (WindowContext* current = mCurrentWindowContext) {
   1273    return current->NonSyntheticLightDOMChildrenCount();
   1274  }
   1275  return 0;
   1276 }
   1277 
   1278 void BrowsingContext::GetWindowContexts(
   1279    nsTArray<RefPtr<WindowContext>>& aWindows) {
   1280  aWindows.AppendElements(mWindowContexts);
   1281 }
   1282 
   1283 void BrowsingContext::RegisterWindowContext(WindowContext* aWindow) {
   1284  MOZ_ASSERT(!mWindowContexts.Contains(aWindow),
   1285             "WindowContext already registered!");
   1286  MOZ_ASSERT(aWindow->GetBrowsingContext() == this);
   1287 
   1288  mWindowContexts.AppendElement(aWindow);
   1289 
   1290  // If the newly registered WindowContext is for our current inner window ID,
   1291  // re-run the `DidSet` handler to re-establish the relationship.
   1292  if (aWindow->InnerWindowId() == GetCurrentInnerWindowId()) {
   1293    DidSet(FieldIndex<IDX_CurrentInnerWindowId>());
   1294    MOZ_DIAGNOSTIC_ASSERT(mCurrentWindowContext == aWindow);
   1295  }
   1296 }
   1297 
   1298 void BrowsingContext::UnregisterWindowContext(WindowContext* aWindow) {
   1299  MOZ_ASSERT(mWindowContexts.Contains(aWindow),
   1300             "WindowContext not registered!");
   1301  mWindowContexts.RemoveElement(aWindow);
   1302 
   1303  // If our currently active window was unregistered, clear our reference to it.
   1304  if (aWindow == mCurrentWindowContext) {
   1305    // Re-read our `CurrentInnerWindowId` value and use it to set
   1306    // `mCurrentWindowContext`. As `aWindow` is now unregistered and discarded,
   1307    // we won't find it, and the value will be cleared back to `nullptr`.
   1308    DidSet(FieldIndex<IDX_CurrentInnerWindowId>());
   1309    MOZ_DIAGNOSTIC_ASSERT(mCurrentWindowContext == nullptr);
   1310  }
   1311 }
   1312 
   1313 void BrowsingContext::PreOrderWalkVoid(
   1314    const std::function<void(BrowsingContext*)>& aCallback) {
   1315  aCallback(this);
   1316 
   1317  AutoTArray<RefPtr<BrowsingContext>, 8> children;
   1318  children.AppendElements(Children());
   1319 
   1320  for (auto& child : children) {
   1321    child->PreOrderWalkVoid(aCallback);
   1322  }
   1323 }
   1324 
   1325 BrowsingContext::WalkFlag BrowsingContext::PreOrderWalkFlag(
   1326    const std::function<WalkFlag(BrowsingContext*)>& aCallback) {
   1327  switch (aCallback(this)) {
   1328    case WalkFlag::Skip:
   1329      return WalkFlag::Next;
   1330    case WalkFlag::Stop:
   1331      return WalkFlag::Stop;
   1332    case WalkFlag::Next:
   1333    default:
   1334      break;
   1335  }
   1336 
   1337  AutoTArray<RefPtr<BrowsingContext>, 8> children;
   1338  children.AppendElements(Children());
   1339 
   1340  for (auto& child : children) {
   1341    switch (child->PreOrderWalkFlag(aCallback)) {
   1342      case WalkFlag::Stop:
   1343        return WalkFlag::Stop;
   1344      default:
   1345        break;
   1346    }
   1347  }
   1348 
   1349  return WalkFlag::Next;
   1350 }
   1351 
   1352 void BrowsingContext::PostOrderWalk(
   1353    const std::function<void(BrowsingContext*)>& aCallback) {
   1354  AutoTArray<RefPtr<BrowsingContext>, 8> children;
   1355  children.AppendElements(Children());
   1356 
   1357  for (auto& child : children) {
   1358    child->PostOrderWalk(aCallback);
   1359  }
   1360 
   1361  aCallback(this);
   1362 }
   1363 
   1364 void BrowsingContext::GetAllBrowsingContextsInSubtree(
   1365    nsTArray<RefPtr<BrowsingContext>>& aBrowsingContexts) {
   1366  PreOrderWalk([&](BrowsingContext* aContext) {
   1367    aBrowsingContexts.AppendElement(aContext);
   1368  });
   1369 }
   1370 
   1371 BrowsingContext* BrowsingContext::FindChildWithName(
   1372    const nsAString& aName, WindowGlobalChild& aRequestingWindow) {
   1373  if (aName.IsEmpty()) {
   1374    // You can't find a browsing context with the empty name.
   1375    return nullptr;
   1376  }
   1377 
   1378  for (BrowsingContext* child : NonSyntheticChildren()) {
   1379    if (child->NameEquals(aName) && aRequestingWindow.CanNavigate(child) &&
   1380        child->IsTargetable()) {
   1381      return child;
   1382    }
   1383  }
   1384 
   1385  return nullptr;
   1386 }
   1387 
   1388 BrowsingContext* BrowsingContext::FindWithSpecialName(
   1389    const nsAString& aName, WindowGlobalChild& aRequestingWindow) {
   1390  // TODO(farre): Neither BrowsingContext nor nsDocShell checks if the
   1391  // browsing context pointed to by a special name is active. Should
   1392  // it be? See Bug 1527913.
   1393  if (aName.LowerCaseEqualsLiteral("_self")) {
   1394    return this;
   1395  }
   1396 
   1397  if (aName.LowerCaseEqualsLiteral("_parent")) {
   1398    if (BrowsingContext* parent = GetParent()) {
   1399      return aRequestingWindow.CanNavigate(parent) ? parent : nullptr;
   1400    }
   1401    return this;
   1402  }
   1403 
   1404  if (aName.LowerCaseEqualsLiteral("_top")) {
   1405    BrowsingContext* top = Top();
   1406 
   1407    return aRequestingWindow.CanNavigate(top) ? top : nullptr;
   1408  }
   1409 
   1410  return nullptr;
   1411 }
   1412 
   1413 BrowsingContext* BrowsingContext::FindWithNameInSubtree(
   1414    const nsAString& aName, WindowGlobalChild* aRequestingWindow) {
   1415  MOZ_DIAGNOSTIC_ASSERT(!aName.IsEmpty());
   1416 
   1417  if (NameEquals(aName) &&
   1418      (!aRequestingWindow || aRequestingWindow->CanNavigate(this)) &&
   1419      IsTargetable()) {
   1420    return this;
   1421  }
   1422 
   1423  for (BrowsingContext* child : NonSyntheticChildren()) {
   1424    if (BrowsingContext* found =
   1425            child->FindWithNameInSubtree(aName, aRequestingWindow)) {
   1426      return found;
   1427    }
   1428  }
   1429 
   1430  return nullptr;
   1431 }
   1432 
   1433 bool BrowsingContext::IsSandboxedFrom(BrowsingContext* aTarget) {
   1434  // If no target then not sandboxed.
   1435  if (!aTarget) {
   1436    return false;
   1437  }
   1438 
   1439  // We cannot be sandboxed from ourselves.
   1440  if (aTarget == this) {
   1441    return false;
   1442  }
   1443 
   1444  // Default the sandbox flags to our flags, so that if we can't retrieve the
   1445  // active document, we will still enforce our own.
   1446  uint32_t sandboxFlags = GetSandboxFlags();
   1447  if (mDocShell) {
   1448    if (RefPtr<Document> doc = mDocShell->GetExtantDocument()) {
   1449      sandboxFlags = doc->GetSandboxFlags();
   1450    }
   1451  }
   1452 
   1453  // If no flags, we are not sandboxed at all.
   1454  if (!sandboxFlags) {
   1455    return false;
   1456  }
   1457 
   1458  // If aTarget has an ancestor, it is not top level.
   1459  if (RefPtr<BrowsingContext> ancestorOfTarget = aTarget->GetParent()) {
   1460    do {
   1461      // We are not sandboxed if we are an ancestor of target.
   1462      if (ancestorOfTarget == this) {
   1463        return false;
   1464      }
   1465      ancestorOfTarget = ancestorOfTarget->GetParent();
   1466    } while (ancestorOfTarget);
   1467 
   1468    // Otherwise, we are sandboxed from aTarget.
   1469    return true;
   1470  }
   1471 
   1472  // aTarget is top level, are we the "one permitted sandboxed
   1473  // navigator", i.e. did we open aTarget?
   1474  if (aTarget->GetOnePermittedSandboxedNavigatorId() == Id()) {
   1475    return false;
   1476  }
   1477 
   1478  // If SANDBOXED_TOPLEVEL_NAVIGATION flag is not on, we are not sandboxed
   1479  // from our top.
   1480  if (!(sandboxFlags & SANDBOXED_TOPLEVEL_NAVIGATION) && aTarget == Top()) {
   1481    return false;
   1482  }
   1483 
   1484  // If SANDBOXED_TOPLEVEL_NAVIGATION_USER_ACTIVATION flag is not on, we are not
   1485  // sandboxed from our top if we have user interaction. We assume there is a
   1486  // valid transient user gesture interaction if this check happens in the
   1487  // target process given that we have checked in the triggering process
   1488  // already.
   1489  if (!(sandboxFlags & SANDBOXED_TOPLEVEL_NAVIGATION_USER_ACTIVATION) &&
   1490      mCurrentWindowContext &&
   1491      (!mCurrentWindowContext->IsInProcess() ||
   1492       mCurrentWindowContext->HasValidTransientUserGestureActivation()) &&
   1493      aTarget == Top()) {
   1494    return false;
   1495  }
   1496 
   1497  // Otherwise, we are sandboxed from aTarget.
   1498  return true;
   1499 }
   1500 
   1501 RefPtr<SessionStorageManager> BrowsingContext::GetSessionStorageManager() {
   1502  RefPtr<SessionStorageManager>& manager = Top()->mSessionStorageManager;
   1503  if (!manager) {
   1504    manager = new SessionStorageManager(this);
   1505  }
   1506  return manager;
   1507 }
   1508 
   1509 bool BrowsingContext::CrossOriginIsolated() {
   1510  MOZ_ASSERT(NS_IsMainThread());
   1511 
   1512  return StaticPrefs::
   1513             dom_postMessage_sharedArrayBuffer_withCOOP_COEP_AtStartup() &&
   1514         Top()->GetOpenerPolicy() ==
   1515             nsILoadInfo::
   1516                 OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP &&
   1517         XRE_IsContentProcess() &&
   1518         StringBeginsWith(ContentChild::GetSingleton()->GetRemoteType(),
   1519                          WITH_COOP_COEP_REMOTE_TYPE_PREFIX);
   1520 }
   1521 
   1522 void BrowsingContext::SetTriggeringAndInheritPrincipals(
   1523    nsIPrincipal* aTriggeringPrincipal, nsIPrincipal* aPrincipalToInherit,
   1524    uint64_t aLoadIdentifier) {
   1525  mTriggeringPrincipal = Some(
   1526      PrincipalWithLoadIdentifierTuple(aTriggeringPrincipal, aLoadIdentifier));
   1527  if (aPrincipalToInherit) {
   1528    mPrincipalToInherit = Some(
   1529        PrincipalWithLoadIdentifierTuple(aPrincipalToInherit, aLoadIdentifier));
   1530  }
   1531 }
   1532 
   1533 std::tuple<nsCOMPtr<nsIPrincipal>, nsCOMPtr<nsIPrincipal>>
   1534 BrowsingContext::GetTriggeringAndInheritPrincipalsForCurrentLoad() {
   1535  nsCOMPtr<nsIPrincipal> triggeringPrincipal =
   1536      GetSavedPrincipal(mTriggeringPrincipal);
   1537  nsCOMPtr<nsIPrincipal> principalToInherit =
   1538      GetSavedPrincipal(mPrincipalToInherit);
   1539  return std::make_tuple(triggeringPrincipal, principalToInherit);
   1540 }
   1541 
   1542 nsIPrincipal* BrowsingContext::GetSavedPrincipal(
   1543    Maybe<PrincipalWithLoadIdentifierTuple> aPrincipalTuple) {
   1544  if (aPrincipalTuple) {
   1545    nsCOMPtr<nsIPrincipal> principal;
   1546    uint64_t loadIdentifier;
   1547    std::tie(principal, loadIdentifier) = *aPrincipalTuple;
   1548    // We want to return a principal only if the load identifier for it
   1549    // matches the current one for this BC.
   1550    if (auto current = GetCurrentLoadIdentifier();
   1551        current && *current == loadIdentifier) {
   1552      return principal;
   1553    }
   1554  }
   1555  return nullptr;
   1556 }
   1557 
   1558 BrowsingContext::~BrowsingContext() {
   1559  MOZ_DIAGNOSTIC_ASSERT(!mParentWindow ||
   1560                        !mParentWindow->mChildren.Contains(this));
   1561  MOZ_DIAGNOSTIC_ASSERT(!mGroup || !mGroup->Toplevels().Contains(this));
   1562 
   1563  mDeprioritizedLoadRunner.clear();
   1564 
   1565  if (sBrowsingContexts) {
   1566    sBrowsingContexts->Remove(Id());
   1567  }
   1568  UnregisterBrowserId(this);
   1569 
   1570  ClearCachedValuesOfLocations();
   1571  mLocations.clear();
   1572 }
   1573 
   1574 /* static */
   1575 void BrowsingContext::DiscardFromContentParent(ContentParent* aCP) {
   1576  MOZ_ASSERT(XRE_IsParentProcess());
   1577 
   1578  if (sBrowsingContexts) {
   1579    AutoTArray<RefPtr<BrowsingContext>, 8> toDiscard;
   1580    for (const auto& data : sBrowsingContexts->Values()) {
   1581      auto* bc = data->Canonical();
   1582      if (!bc->IsDiscarded() && bc->IsEmbeddedInProcess(aCP->ChildID())) {
   1583        toDiscard.AppendElement(bc);
   1584      }
   1585    }
   1586 
   1587    for (BrowsingContext* bc : toDiscard) {
   1588      bc->Detach(/* aFromIPC */ true);
   1589    }
   1590  }
   1591 }
   1592 
   1593 nsISupports* BrowsingContext::GetParentObject() const {
   1594  return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
   1595 }
   1596 
   1597 JSObject* BrowsingContext::WrapObject(JSContext* aCx,
   1598                                      JS::Handle<JSObject*> aGivenProto) {
   1599  return BrowsingContext_Binding::Wrap(aCx, this, aGivenProto);
   1600 }
   1601 
   1602 bool BrowsingContext::WriteStructuredClone(JSContext* aCx,
   1603                                           JSStructuredCloneWriter* aWriter,
   1604                                           StructuredCloneHolder* aHolder) {
   1605  MOZ_DIAGNOSTIC_ASSERT(mEverAttached);
   1606  return (JS_WriteUint32Pair(aWriter, SCTAG_DOM_BROWSING_CONTEXT, 0) &&
   1607          JS_WriteUint32Pair(aWriter, uint32_t(Id()), uint32_t(Id() >> 32)));
   1608 }
   1609 
   1610 /* static */
   1611 JSObject* BrowsingContext::ReadStructuredClone(JSContext* aCx,
   1612                                               JSStructuredCloneReader* aReader,
   1613                                               StructuredCloneHolder* aHolder) {
   1614  uint32_t idLow = 0;
   1615  uint32_t idHigh = 0;
   1616  if (!JS_ReadUint32Pair(aReader, &idLow, &idHigh)) {
   1617    return nullptr;
   1618  }
   1619  uint64_t id = uint64_t(idHigh) << 32 | idLow;
   1620 
   1621  // Note: Do this check after reading our ID data. Returning null will abort
   1622  // the decode operation anyway, but we should at least be as safe as possible.
   1623  if (NS_WARN_IF(!NS_IsMainThread())) {
   1624    MOZ_DIAGNOSTIC_CRASH(
   1625        "We shouldn't be trying to decode a BrowsingContext "
   1626        "on a background thread.");
   1627    return nullptr;
   1628  }
   1629 
   1630  JS::Rooted<JS::Value> val(aCx, JS::NullValue());
   1631  // We'll get rooting hazard errors from the RefPtr destructor if it isn't
   1632  // destroyed before we try to return a raw JSObject*, so create it in its own
   1633  // scope.
   1634  if (RefPtr<BrowsingContext> context = Get(id)) {
   1635    if (!GetOrCreateDOMReflector(aCx, context, &val) || !val.isObject()) {
   1636      return nullptr;
   1637    }
   1638  }
   1639  return val.toObjectOrNull();
   1640 }
   1641 
   1642 bool BrowsingContext::CanSetOriginAttributes() {
   1643  // A discarded BrowsingContext has already been destroyed, and cannot modify
   1644  // its OriginAttributes.
   1645  if (NS_WARN_IF(IsDiscarded())) {
   1646    return false;
   1647  }
   1648 
   1649  // Before attaching is the safest time to set OriginAttributes, and the only
   1650  // allowed time for content BrowsingContexts.
   1651  if (!EverAttached()) {
   1652    return true;
   1653  }
   1654 
   1655  // Attached content BrowsingContexts may have been synced to other processes.
   1656  if (NS_WARN_IF(IsContent())) {
   1657    MOZ_CRASH();
   1658    return false;
   1659  }
   1660  MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
   1661 
   1662  // Cannot set OriginAttributes after we've created our child BrowsingContext.
   1663  if (NS_WARN_IF(!Children().IsEmpty())) {
   1664    return false;
   1665  }
   1666 
   1667  // Only allow setting OriginAttributes if we have no associated document, or
   1668  // the document is still `about:blank`.
   1669  // TODO: Bug 1273058 - should have no document when setting origin attributes.
   1670  if (WindowGlobalParent* window = Canonical()->GetCurrentWindowGlobal()) {
   1671    if (nsIURI* uri = window->GetDocumentURI()) {
   1672      MOZ_ASSERT(NS_IsAboutBlank(uri));
   1673      return NS_IsAboutBlank(uri);
   1674    }
   1675  }
   1676  return true;
   1677 }
   1678 
   1679 Nullable<WindowProxyHolder> BrowsingContext::GetAssociatedWindow() {
   1680  // nsILoadContext usually only returns same-process windows,
   1681  // so we intentionally return nullptr if this BC is out of
   1682  // process.
   1683  if (IsInProcess()) {
   1684    return WindowProxyHolder(this);
   1685  }
   1686  return nullptr;
   1687 }
   1688 
   1689 Nullable<WindowProxyHolder> BrowsingContext::GetTopWindow() {
   1690  return Top()->GetAssociatedWindow();
   1691 }
   1692 
   1693 Element* BrowsingContext::GetTopFrameElement() {
   1694  return Top()->GetEmbedderElement();
   1695 }
   1696 
   1697 void BrowsingContext::SetUsePrivateBrowsing(bool aUsePrivateBrowsing,
   1698                                            ErrorResult& aError) {
   1699  nsresult rv = SetUsePrivateBrowsing(aUsePrivateBrowsing);
   1700  if (NS_FAILED(rv)) {
   1701    aError.Throw(rv);
   1702  }
   1703 }
   1704 
   1705 void BrowsingContext::SetUseTrackingProtectionWebIDL(
   1706    bool aUseTrackingProtection, ErrorResult& aRv) {
   1707  SetForceEnableTrackingProtection(aUseTrackingProtection, aRv);
   1708 }
   1709 
   1710 void BrowsingContext::GetOriginAttributes(JSContext* aCx,
   1711                                          JS::MutableHandle<JS::Value> aVal,
   1712                                          ErrorResult& aError) {
   1713  AssertOriginAttributesMatchPrivateBrowsing();
   1714 
   1715  if (!ToJSValue(aCx, mOriginAttributes, aVal)) {
   1716    aError.NoteJSContextException(aCx);
   1717  }
   1718 }
   1719 
   1720 NS_IMETHODIMP BrowsingContext::GetAssociatedWindow(
   1721    mozIDOMWindowProxy** aAssociatedWindow) {
   1722  nsCOMPtr<mozIDOMWindowProxy> win = GetDOMWindow();
   1723  win.forget(aAssociatedWindow);
   1724  return NS_OK;
   1725 }
   1726 
   1727 NS_IMETHODIMP BrowsingContext::GetTopWindow(mozIDOMWindowProxy** aTopWindow) {
   1728  return Top()->GetAssociatedWindow(aTopWindow);
   1729 }
   1730 
   1731 NS_IMETHODIMP BrowsingContext::GetTopFrameElement(Element** aTopFrameElement) {
   1732  RefPtr<Element> topFrameElement = GetTopFrameElement();
   1733  topFrameElement.forget(aTopFrameElement);
   1734  return NS_OK;
   1735 }
   1736 
   1737 NS_IMETHODIMP BrowsingContext::GetIsContent(bool* aIsContent) {
   1738  *aIsContent = IsContent();
   1739  return NS_OK;
   1740 }
   1741 
   1742 NS_IMETHODIMP BrowsingContext::GetUsePrivateBrowsing(
   1743    bool* aUsePrivateBrowsing) {
   1744  *aUsePrivateBrowsing = mPrivateBrowsingId > 0;
   1745  return NS_OK;
   1746 }
   1747 
   1748 NS_IMETHODIMP BrowsingContext::SetUsePrivateBrowsing(bool aUsePrivateBrowsing) {
   1749  if (!CanSetOriginAttributes()) {
   1750    bool changed = aUsePrivateBrowsing != (mPrivateBrowsingId > 0);
   1751    if (changed) {
   1752      NS_WARNING("SetUsePrivateBrowsing when !CanSetOriginAttributes()");
   1753    }
   1754    return changed ? NS_ERROR_FAILURE : NS_OK;
   1755  }
   1756 
   1757  return SetPrivateBrowsing(aUsePrivateBrowsing);
   1758 }
   1759 
   1760 NS_IMETHODIMP BrowsingContext::SetPrivateBrowsing(bool aPrivateBrowsing) {
   1761  if (!CanSetOriginAttributes()) {
   1762    NS_WARNING("Attempt to set PrivateBrowsing when !CanSetOriginAttributes");
   1763    return NS_ERROR_FAILURE;
   1764  }
   1765 
   1766  bool changed = aPrivateBrowsing != (mPrivateBrowsingId > 0);
   1767  if (changed) {
   1768    mPrivateBrowsingId = aPrivateBrowsing ? 1 : 0;
   1769    if (IsContent()) {
   1770      mOriginAttributes.SyncAttributesWithPrivateBrowsing(aPrivateBrowsing);
   1771    }
   1772 
   1773    if (XRE_IsParentProcess()) {
   1774      Canonical()->AdjustPrivateBrowsingCount(aPrivateBrowsing);
   1775    }
   1776  }
   1777  AssertOriginAttributesMatchPrivateBrowsing();
   1778 
   1779  if (changed && mDocShell) {
   1780    nsDocShell::Cast(mDocShell)->NotifyPrivateBrowsingChanged();
   1781  }
   1782  return NS_OK;
   1783 }
   1784 
   1785 NS_IMETHODIMP BrowsingContext::GetUseRemoteTabs(bool* aUseRemoteTabs) {
   1786  *aUseRemoteTabs = mUseRemoteTabs;
   1787  return NS_OK;
   1788 }
   1789 
   1790 NS_IMETHODIMP BrowsingContext::SetRemoteTabs(bool aUseRemoteTabs) {
   1791  if (!CanSetOriginAttributes()) {
   1792    NS_WARNING("Attempt to set RemoteTabs when !CanSetOriginAttributes");
   1793    return NS_ERROR_FAILURE;
   1794  }
   1795 
   1796  if (aUseRemoteTabs && !gIPCEnabledAnnotation) {
   1797    gIPCEnabledAnnotation = true;
   1798  }
   1799 
   1800  // Don't allow non-remote tabs with remote subframes.
   1801  if (NS_WARN_IF(!aUseRemoteTabs && mUseRemoteSubframes)) {
   1802    return NS_ERROR_UNEXPECTED;
   1803  }
   1804 
   1805  mUseRemoteTabs = aUseRemoteTabs;
   1806  return NS_OK;
   1807 }
   1808 
   1809 NS_IMETHODIMP BrowsingContext::GetUseRemoteSubframes(
   1810    bool* aUseRemoteSubframes) {
   1811  *aUseRemoteSubframes = mUseRemoteSubframes;
   1812  return NS_OK;
   1813 }
   1814 
   1815 NS_IMETHODIMP BrowsingContext::SetRemoteSubframes(bool aUseRemoteSubframes) {
   1816  if (!CanSetOriginAttributes()) {
   1817    NS_WARNING("Attempt to set RemoteSubframes when !CanSetOriginAttributes");
   1818    return NS_ERROR_FAILURE;
   1819  }
   1820 
   1821  if (aUseRemoteSubframes && !gFissionEnabledAnnotation) {
   1822    gFissionEnabledAnnotation = true;
   1823  }
   1824 
   1825  // Don't allow non-remote tabs with remote subframes.
   1826  if (NS_WARN_IF(aUseRemoteSubframes && !mUseRemoteTabs)) {
   1827    return NS_ERROR_UNEXPECTED;
   1828  }
   1829 
   1830  mUseRemoteSubframes = aUseRemoteSubframes;
   1831  return NS_OK;
   1832 }
   1833 
   1834 NS_IMETHODIMP BrowsingContext::GetUseTrackingProtection(
   1835    bool* aUseTrackingProtection) {
   1836  *aUseTrackingProtection = false;
   1837 
   1838  if (GetForceEnableTrackingProtection() ||
   1839      StaticPrefs::privacy_trackingprotection_enabled() ||
   1840      (UsePrivateBrowsing() &&
   1841       StaticPrefs::privacy_trackingprotection_pbmode_enabled())) {
   1842    *aUseTrackingProtection = true;
   1843    return NS_OK;
   1844  }
   1845 
   1846  if (GetParent()) {
   1847    return GetParent()->GetUseTrackingProtection(aUseTrackingProtection);
   1848  }
   1849 
   1850  return NS_OK;
   1851 }
   1852 
   1853 NS_IMETHODIMP BrowsingContext::SetUseTrackingProtection(
   1854    bool aUseTrackingProtection) {
   1855  return SetForceEnableTrackingProtection(aUseTrackingProtection);
   1856 }
   1857 
   1858 NS_IMETHODIMP BrowsingContext::GetScriptableOriginAttributes(
   1859    JSContext* aCx, JS::MutableHandle<JS::Value> aVal) {
   1860  AssertOriginAttributesMatchPrivateBrowsing();
   1861 
   1862  bool ok = ToJSValue(aCx, mOriginAttributes, aVal);
   1863  NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
   1864  return NS_OK;
   1865 }
   1866 
   1867 NS_IMETHODIMP_(void)
   1868 BrowsingContext::GetOriginAttributes(OriginAttributes& aAttrs) {
   1869  aAttrs = mOriginAttributes;
   1870  AssertOriginAttributesMatchPrivateBrowsing();
   1871 }
   1872 
   1873 nsresult BrowsingContext::SetOriginAttributes(const OriginAttributes& aAttrs) {
   1874  if (!CanSetOriginAttributes()) {
   1875    NS_WARNING("Attempt to set OriginAttributes when !CanSetOriginAttributes");
   1876    return NS_ERROR_FAILURE;
   1877  }
   1878 
   1879  AssertOriginAttributesMatchPrivateBrowsing();
   1880  mOriginAttributes = aAttrs;
   1881 
   1882  bool isPrivate = mOriginAttributes.mPrivateBrowsingId !=
   1883                   nsIScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID;
   1884  // Chrome Browsing Context can not contain OriginAttributes.mPrivateBrowsingId
   1885  if (IsChrome() && isPrivate) {
   1886    mOriginAttributes.mPrivateBrowsingId =
   1887        nsIScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID;
   1888  }
   1889  SetPrivateBrowsing(isPrivate);
   1890  AssertOriginAttributesMatchPrivateBrowsing();
   1891 
   1892  return NS_OK;
   1893 }
   1894 
   1895 void BrowsingContext::AssertOriginAttributesMatchPrivateBrowsing() {
   1896  // Chrome browsing contexts must not have a private browsing OriginAttribute
   1897  // Content browsing contexts must maintain the equality:
   1898  // mOriginAttributes.mPrivateBrowsingId == mPrivateBrowsingId
   1899  if (IsChrome()) {
   1900    MOZ_DIAGNOSTIC_ASSERT(mOriginAttributes.mPrivateBrowsingId == 0);
   1901  } else {
   1902    MOZ_DIAGNOSTIC_ASSERT(mOriginAttributes.mPrivateBrowsingId ==
   1903                          mPrivateBrowsingId);
   1904  }
   1905 }
   1906 
   1907 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BrowsingContext)
   1908  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   1909  NS_INTERFACE_MAP_ENTRY(nsILoadContext)
   1910  NS_INTERFACE_MAP_ENTRY(nsISupports)
   1911 NS_INTERFACE_MAP_END
   1912 
   1913 NS_IMPL_CYCLE_COLLECTION_CLASS(BrowsingContext)
   1914 
   1915 NS_IMPL_CYCLE_COLLECTING_ADDREF(BrowsingContext)
   1916 NS_IMPL_CYCLE_COLLECTING_RELEASE(BrowsingContext)
   1917 
   1918 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(BrowsingContext)
   1919  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
   1920 NS_IMPL_CYCLE_COLLECTION_TRACE_END
   1921 
   1922 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(BrowsingContext)
   1923  if (sBrowsingContexts) {
   1924    sBrowsingContexts->Remove(tmp->Id());
   1925  }
   1926  UnregisterBrowserId(tmp);
   1927 
   1928  if (tmp->GetIsPopupSpam()) {
   1929    PopupBlocker::UnregisterOpenPopupSpam();
   1930    // NOTE: Doesn't use SetIsPopupSpam, as it will be set all processes
   1931    // automatically.
   1932    tmp->mFields.SetWithoutSyncing<IDX_IsPopupSpam>(false);
   1933  }
   1934 
   1935  NS_IMPL_CYCLE_COLLECTION_UNLINK(
   1936      mDocShell, mParentWindow, mGroup, mEmbedderElement, mWindowContexts,
   1937      mCurrentWindowContext, mSessionStorageManager, mChildSessionHistory)
   1938  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
   1939 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
   1940 
   1941 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(BrowsingContext)
   1942  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(
   1943      mDocShell, mParentWindow, mGroup, mEmbedderElement, mWindowContexts,
   1944      mCurrentWindowContext, mSessionStorageManager, mChildSessionHistory)
   1945 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
   1946 
   1947 static bool IsCertainlyAliveForCC(BrowsingContext* aContext) {
   1948  return aContext->HasKnownLiveWrapper() ||
   1949         (AppShutdown::GetCurrentShutdownPhase() ==
   1950              ShutdownPhase::NotInShutdown &&
   1951          aContext->EverAttached() && !aContext->IsDiscarded());
   1952 }
   1953 
   1954 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(BrowsingContext)
   1955  if (IsCertainlyAliveForCC(tmp)) {
   1956    if (tmp->PreservingWrapper()) {
   1957      tmp->MarkWrapperLive();
   1958    }
   1959    return true;
   1960  }
   1961 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
   1962 
   1963 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(BrowsingContext)
   1964  return IsCertainlyAliveForCC(tmp) && tmp->HasNothingToTrace(tmp);
   1965 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
   1966 
   1967 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(BrowsingContext)
   1968  return IsCertainlyAliveForCC(tmp);
   1969 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
   1970 
   1971 class RemoteLocationProxy
   1972    : public RemoteObjectProxy<BrowsingContext::LocationProxy,
   1973                               Location_Binding::sCrossOriginProperties> {
   1974 public:
   1975  typedef RemoteObjectProxy Base;
   1976 
   1977  constexpr RemoteLocationProxy()
   1978      : RemoteObjectProxy(prototypes::id::Location) {}
   1979 
   1980  void NoteChildren(JSObject* aProxy,
   1981                    nsCycleCollectionTraversalCallback& aCb) const override {
   1982    auto location =
   1983        static_cast<BrowsingContext::LocationProxy*>(GetNative(aProxy));
   1984    CycleCollectionNoteChild(aCb, location->GetBrowsingContext(),
   1985                             "JS::GetPrivate(obj)->GetBrowsingContext()");
   1986  }
   1987 };
   1988 
   1989 static const RemoteLocationProxy sSingleton;
   1990 
   1991 // Give RemoteLocationProxy 2 reserved slots, like the other wrappers,
   1992 // so JSObject::swap can swap it with CrossCompartmentWrappers without requiring
   1993 // malloc.
   1994 template <>
   1995 const JSClass RemoteLocationProxy::Base::sClass =
   1996    PROXY_CLASS_DEF("Proxy", JSCLASS_HAS_RESERVED_SLOTS(2));
   1997 
   1998 void BrowsingContext::Location(JSContext* aCx,
   1999                               JS::MutableHandle<JSObject*> aLocation,
   2000                               ErrorResult& aError) {
   2001  aError.MightThrowJSException();
   2002  sSingleton.GetProxyObject(aCx, &mLocation, /* aTransplantTo = */ nullptr,
   2003                            aLocation);
   2004  if (!aLocation) {
   2005    aError.StealExceptionFromJSContext(aCx);
   2006  }
   2007 }
   2008 
   2009 bool BrowsingContext::RemoveRootFromBFCacheSync() {
   2010  if (WindowContext* wc = GetParentWindowContext()) {
   2011    if (RefPtr<Document> doc = wc->TopWindowContext()->GetDocument()) {
   2012      return doc->RemoveFromBFCacheSync();
   2013    }
   2014  }
   2015  return false;
   2016 }
   2017 
   2018 nsresult BrowsingContext::CheckSandboxFlags(nsDocShellLoadState* aLoadState) {
   2019  const auto& sourceBC = aLoadState->SourceBrowsingContext();
   2020  if (sourceBC.IsNull()) {
   2021    return NS_OK;
   2022  }
   2023 
   2024  // We might be called after the source BC has been discarded, but before we've
   2025  // destroyed our in-process instance of the BrowsingContext object in some
   2026  // situations (e.g. after creating a new pop-up with window.open while the
   2027  // window is being closed). In these situations we want to still perform the
   2028  // sandboxing check against our in-process copy. If we've forgotten about the
   2029  // context already, assume it is sanboxed. (bug 1643450)
   2030  BrowsingContext* bc = sourceBC.GetMaybeDiscarded();
   2031  if (!bc || bc->IsSandboxedFrom(this)) {
   2032    return NS_ERROR_DOM_SECURITY_ERR;
   2033  }
   2034  return NS_OK;
   2035 }
   2036 
   2037 nsresult BrowsingContext::CheckFramebusting(nsDocShellLoadState* aLoadState) {
   2038  if (!StaticPrefs::dom_security_framebusting_intervention_enabled()) {
   2039    return NS_OK;
   2040  }
   2041 
   2042  if (XRE_IsParentProcess()) {
   2043    return NS_OK;
   2044  }
   2045 
   2046  // Only applies to top-level navigations.
   2047  if (!IsTop()) {
   2048    return NS_OK;
   2049  }
   2050 
   2051  if (aLoadState->HasValidUserGestureActivation()) {
   2052    return NS_OK;
   2053  }
   2054 
   2055  const auto& sourceBC = aLoadState->SourceBrowsingContext();
   2056  if (sourceBC.IsNull()) {
   2057    return NS_OK;
   2058  }
   2059 
   2060  if (BrowsingContext* bc = sourceBC.GetMaybeDiscarded()) {
   2061    if (bc->IsFramebustingAllowed(this)) {
   2062      return NS_OK;
   2063    }
   2064 
   2065    if (bc->GetDOMWindow()) {
   2066      nsGlobalWindowOuter::Cast(bc->GetDOMWindow())
   2067          ->FireRedirectBlockedEvent(aLoadState->URI());
   2068    }
   2069 
   2070    nsAutoCString frameURL;
   2071    if (bc->GetDocument() &&
   2072        NS_SUCCEEDED(
   2073            bc->GetDocument()->GetPrincipal()->GetAsciiSpec(frameURL))) {
   2074      nsContentUtils::ReportToConsoleNonLocalized(
   2075          NS_ConvertUTF8toUTF16(nsPrintfCString(
   2076              R"(Attempting to navigate the top-level browsing context from )"
   2077              R"(frame with url "%s" which is neither same-origin nor has )"
   2078              R"(the required user interaction.)",
   2079              frameURL.get())),
   2080          nsIScriptError::errorFlag, "DOM"_ns, bc->GetDocument());
   2081    }
   2082  }
   2083 
   2084  return NS_ERROR_DOM_SECURITY_ERR;
   2085 }
   2086 
   2087 bool BrowsingContext::IsFramebustingAllowed(BrowsingContext* aTarget) {
   2088  MOZ_ASSERT(aTarget->IsTop());
   2089 
   2090  if (aTarget->BrowserId() == BrowserId()) {
   2091    return IsFramebustingAllowedInner() || IsPopupAllowed();
   2092  }
   2093 
   2094  // We should be able to safely assume that the SOP has our back here
   2095  // already. How else would this BrowsingContext have a reference?
   2096  return true;
   2097 }
   2098 
   2099 bool BrowsingContext::IsFramebustingAllowedInner() {
   2100  if (IsInProcess() && SameOriginWithTop()) {
   2101    return true;
   2102  }
   2103 
   2104  // We get the sandbox flags from the load info since the CSP header
   2105  // hasn't yet been processed at that time. The CSP sandbox directive makes
   2106  // it possible for a document to grant itself "allow-top-navigation"
   2107  // permissions by sending the appropiate header and we don't like that.
   2108  Document* doc;
   2109  nsIChannel* channel;
   2110  if ((doc = GetExtantDocument()) && (channel = doc->GetChannel())) {
   2111    nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
   2112    uint32_t sandboxFlags = loadInfo->GetSandboxFlags();
   2113    if (sandboxFlags && !(sandboxFlags & SANDBOXED_TOPLEVEL_NAVIGATION)) {
   2114      BrowsingContext* parent = GetParent();
   2115      return !parent || parent->IsFramebustingAllowedInner();
   2116    }
   2117  }
   2118 
   2119  return false;
   2120 }
   2121 
   2122 nsresult BrowsingContext::LoadURI(nsDocShellLoadState* aLoadState,
   2123                                  bool aSetNavigating) {
   2124  // Per spec, most load attempts are silently ignored when a BrowsingContext is
   2125  // null (which in our code corresponds to discarded), so we simply fail
   2126  // silently in those cases. Regardless, we cannot trigger loads in/from
   2127  // discarded BrowsingContexts via IPC, so we need to abort in any case.
   2128  if (IsDiscarded()) {
   2129    return NS_OK;
   2130  }
   2131 
   2132  MOZ_DIAGNOSTIC_ASSERT(aLoadState->TargetBrowsingContext().IsNull(),
   2133                        "Targeting occurs in InternalLoad");
   2134  aLoadState->AssertProcessCouldTriggerLoadIfSystem();
   2135 
   2136  // When this tab sets these load flags, we disable or force TRR for the
   2137  // browsing context ensuring subsequent navigations will keep the same
   2138  // TRR mode.
   2139  if (aLoadState->HasLoadFlags(nsIWebNavigation::LOAD_FLAGS_DISABLE_TRR)) {
   2140    (void)SetDefaultLoadFlags(GetDefaultLoadFlags() |
   2141                              nsIRequest::LOAD_TRR_DISABLED_MODE);
   2142  } else if (aLoadState->HasLoadFlags(nsIWebNavigation::LOAD_FLAGS_FORCE_TRR)) {
   2143    (void)SetDefaultLoadFlags(GetDefaultLoadFlags() |
   2144                              nsIRequest::LOAD_TRR_ONLY_MODE);
   2145  }
   2146 
   2147  if (mDocShell) {
   2148    nsCOMPtr<nsIDocShell> docShell = mDocShell;
   2149 
   2150    return docShell->LoadURI(aLoadState, aSetNavigating);
   2151  }
   2152 
   2153  // Note: We do this check both here and in `nsDocShell::InternalLoad`, since
   2154  // document-specific sandbox flags are only available in the process
   2155  // triggering the load, and we don't want the target process to have to trust
   2156  // the triggering process to do the appropriate checks for the
   2157  // BrowsingContext's sandbox flags.
   2158  MOZ_TRY(CheckSandboxFlags(aLoadState));
   2159  SetTriggeringAndInheritPrincipals(aLoadState->TriggeringPrincipal(),
   2160                                    aLoadState->PrincipalToInherit(),
   2161                                    aLoadState->GetLoadIdentifier());
   2162 
   2163  const auto& sourceBC = aLoadState->SourceBrowsingContext();
   2164 
   2165  if (aLoadState->URI()->SchemeIs("javascript")) {
   2166    if (!XRE_IsParentProcess()) {
   2167      // Web content should only be able to load javascript: URIs into documents
   2168      // whose principals the caller principal subsumes, which by definition
   2169      // excludes any document in a cross-process BrowsingContext.
   2170      return NS_ERROR_DOM_BAD_CROSS_ORIGIN_URI;
   2171    }
   2172    MOZ_DIAGNOSTIC_ASSERT(!sourceBC,
   2173                          "Should never see a cross-process javascript: load "
   2174                          "triggered from content");
   2175  }
   2176 
   2177  // Note: We do this check both here and in `nsDocShell::InternalLoad`.
   2178  // Same reason as for the sandbox flags.
   2179  MOZ_TRY(CheckFramebusting(aLoadState));
   2180 
   2181  MOZ_DIAGNOSTIC_ASSERT(!sourceBC || sourceBC->Group() == Group());
   2182  if (sourceBC && sourceBC->IsInProcess()) {
   2183    nsCOMPtr<nsPIDOMWindowOuter> win(sourceBC->GetDOMWindow());
   2184    if (WindowGlobalChild* wgc =
   2185            win->GetCurrentInnerWindow()->GetWindowGlobalChild()) {
   2186      if (!wgc->CanNavigate(this)) {
   2187        return NS_ERROR_DOM_PROP_ACCESS_DENIED;
   2188      }
   2189      wgc->SendLoadURI(this, mozilla::WrapNotNull(aLoadState), aSetNavigating);
   2190    }
   2191  } else if (XRE_IsParentProcess()) {
   2192    if (ContentParent* cp = Canonical()->GetContentParent()) {
   2193      // Attempt to initiate this load immediately in the parent, if it succeeds
   2194      // it'll return a unique identifier so that we can find it later.
   2195      uint64_t loadIdentifier = 0;
   2196      if (Canonical()->AttemptSpeculativeLoadInParent(aLoadState)) {
   2197        MOZ_DIAGNOSTIC_ASSERT(GetCurrentLoadIdentifier().isSome());
   2198        loadIdentifier = GetCurrentLoadIdentifier().value();
   2199        aLoadState->SetChannelInitialized(true);
   2200      }
   2201 
   2202      cp->TransmitBlobDataIfBlobURL(aLoadState->URI());
   2203 
   2204 #ifdef ANDROID
   2205      // Generate a unique android load identifier for this url load
   2206      // Used to map back to the app link launch type in
   2207      // ContentParent::RecordAndroidAppLinkTelemetry() so we can avoid
   2208      // sending the app link launch type to the content process
   2209      uint64_t androidLoadIdentifier = nsContentUtils::GenerateTabId();
   2210      MOZ_ALWAYS_SUCCEEDS(
   2211          SetAndroidAppLinkLoadIdentifier(Some(androidLoadIdentifier)));
   2212 
   2213      uint32_t appLinkLaunchType = aLoadState->GetAppLinkLaunchType();
   2214      cp->SetAndroidAppLinkLaunchType(androidLoadIdentifier, appLinkLaunchType);
   2215 
   2216      // Record timing for cold app link launches
   2217      constexpr uint32_t APPLINK_COLD = 1;
   2218      if (appLinkLaunchType == APPLINK_COLD) {
   2219        const TimeStamp loadUriTime = TimeStamp::Now();
   2220 
   2221        // Process creation to load URI timing
   2222        const TimeStamp processCreationTime = TimeStamp::ProcessCreation();
   2223        if (!processCreationTime.IsNull()) {
   2224          const TimeDuration delta = loadUriTime - processCreationTime;
   2225          mozilla::glean::perf::cold_applink_process_launch_to_load_uri
   2226              .AccumulateRawDuration(delta);
   2227 
   2228          PROFILER_MARKER("Cold App Link Process Creation to Load URI", NETWORK,
   2229                          MarkerOptions(MarkerTiming::Interval(
   2230                              processCreationTime, loadUriTime)),
   2231                          Tracing, "AppLink");
   2232        }
   2233 
   2234        // StartupTimeline::MAIN to load URI timing
   2235        const TimeStamp mainTime = StartupTimeline::Get(StartupTimeline::MAIN);
   2236        if (!mainTime.IsNull()) {
   2237          const TimeDuration mainDelta = loadUriTime - mainTime;
   2238          mozilla::glean::perf::cold_applink_main_to_load_uri
   2239              .AccumulateRawDuration(mainDelta);
   2240 
   2241          PROFILER_MARKER(
   2242              "Cold App Link Main to Load URI", NETWORK,
   2243              MarkerOptions(MarkerTiming::Interval(mainTime, loadUriTime)),
   2244              Tracing, "AppLink");
   2245        }
   2246      }
   2247 
   2248      PROFILER_MARKER_FMT("BrowsingContext::LoadURI", NETWORK, {},
   2249                          "android appLinkLaunchType {} URL {}",
   2250                          appLinkLaunchType,
   2251                          aLoadState->URI()->GetSpecOrDefault().get());
   2252 #endif
   2253 
   2254      // Setup a confirmation callback once the content process receives this
   2255      // load. Normally we'd expect a PDocumentChannel actor to have been
   2256      // created to claim the load identifier by that time. If not, then it
   2257      // won't be coming, so make sure we clean up and deregister.
   2258      cp->SendLoadURI(this, mozilla::WrapNotNull(aLoadState), aSetNavigating)
   2259          ->Then(GetMainThreadSerialEventTarget(), __func__,
   2260                 [loadIdentifier](
   2261                     const PContentParent::LoadURIPromise::ResolveOrRejectValue&
   2262                         aValue) {
   2263                   if (loadIdentifier) {
   2264                     net::DocumentLoadListener::CleanupParentLoadAttempt(
   2265                         loadIdentifier);
   2266                   }
   2267                 });
   2268    }
   2269  } else {
   2270    if (!sourceBC) {
   2271      return NS_ERROR_UNEXPECTED;
   2272    }
   2273    // If we're in a content process and the source BC is no longer in-process,
   2274    // just fail silently.
   2275  }
   2276  return NS_OK;
   2277 }
   2278 
   2279 nsresult BrowsingContext::InternalLoad(nsDocShellLoadState* aLoadState) {
   2280  if (IsDiscarded()) {
   2281    return NS_OK;
   2282  }
   2283  SetTriggeringAndInheritPrincipals(aLoadState->TriggeringPrincipal(),
   2284                                    aLoadState->PrincipalToInherit(),
   2285                                    aLoadState->GetLoadIdentifier());
   2286 
   2287  MOZ_DIAGNOSTIC_ASSERT(aLoadState->Target().IsEmpty(),
   2288                        "should already have retargeted");
   2289  MOZ_DIAGNOSTIC_ASSERT(!aLoadState->TargetBrowsingContext().IsNull(),
   2290                        "should have target bc set");
   2291  MOZ_DIAGNOSTIC_ASSERT(aLoadState->TargetBrowsingContext() == this,
   2292                        "must be targeting this BrowsingContext");
   2293  aLoadState->AssertProcessCouldTriggerLoadIfSystem();
   2294 
   2295  if (mDocShell) {
   2296    RefPtr<nsDocShell> docShell = nsDocShell::Cast(mDocShell);
   2297    return docShell->InternalLoad(aLoadState);
   2298  }
   2299 
   2300  // Note: We do this check both here and in `nsDocShell::InternalLoad`, since
   2301  // document-specific sandbox flags are only available in the process
   2302  // triggering the load, and we don't want the target process to have to trust
   2303  // the triggering process to do the appropriate checks for the
   2304  // BrowsingContext's sandbox flags.
   2305  MOZ_TRY(CheckSandboxFlags(aLoadState));
   2306 
   2307  const auto& sourceBC = aLoadState->SourceBrowsingContext();
   2308 
   2309  if (aLoadState->URI()->SchemeIs("javascript")) {
   2310    if (!XRE_IsParentProcess()) {
   2311      // Web content should only be able to load javascript: URIs into documents
   2312      // whose principals the caller principal subsumes, which by definition
   2313      // excludes any document in a cross-process BrowsingContext.
   2314      return NS_ERROR_DOM_BAD_CROSS_ORIGIN_URI;
   2315    }
   2316    MOZ_DIAGNOSTIC_ASSERT(!sourceBC,
   2317                          "Should never see a cross-process javascript: load "
   2318                          "triggered from content");
   2319  }
   2320 
   2321  // Note: We do this check both here and in `nsDocShell::InternalLoad`.
   2322  // Same reason as for the sandbox flags.
   2323  MOZ_TRY(CheckFramebusting(aLoadState));
   2324 
   2325  if (XRE_IsParentProcess()) {
   2326    ContentParent* cp = Canonical()->GetContentParent();
   2327    if (!cp || !cp->CanSend()) {
   2328      return NS_ERROR_FAILURE;
   2329    }
   2330 
   2331    MOZ_ALWAYS_SUCCEEDS(
   2332        SetCurrentLoadIdentifier(Some(aLoadState->GetLoadIdentifier())));
   2333    (void)cp->SendInternalLoad(mozilla::WrapNotNull(aLoadState));
   2334  } else {
   2335    MOZ_DIAGNOSTIC_ASSERT(sourceBC);
   2336    MOZ_DIAGNOSTIC_ASSERT(sourceBC->Group() == Group());
   2337 
   2338    nsCOMPtr<nsPIDOMWindowOuter> win(sourceBC->GetDOMWindow());
   2339    WindowGlobalChild* wgc =
   2340        win->GetCurrentInnerWindow()->GetWindowGlobalChild();
   2341    if (!wgc || !wgc->CanSend()) {
   2342      return NS_ERROR_FAILURE;
   2343    }
   2344    if (!wgc->CanNavigate(this)) {
   2345      return NS_ERROR_DOM_PROP_ACCESS_DENIED;
   2346    }
   2347 
   2348    MOZ_ALWAYS_SUCCEEDS(
   2349        SetCurrentLoadIdentifier(Some(aLoadState->GetLoadIdentifier())));
   2350    wgc->SendInternalLoad(mozilla::WrapNotNull(aLoadState));
   2351  }
   2352 
   2353  return NS_OK;
   2354 }
   2355 
   2356 already_AddRefed<nsDocShellLoadState>
   2357 BrowsingContext::CheckURLAndCreateLoadState(nsIURI* aURI,
   2358                                            nsIPrincipal& aSubjectPrincipal,
   2359                                            Document* aSourceDocument,
   2360                                            ErrorResult& aRv) {
   2361  nsCOMPtr<nsIPrincipal> triggeringPrincipal;
   2362  nsCOMPtr<nsIURI> sourceURI;
   2363  ReferrerPolicy referrerPolicy = ReferrerPolicy::_empty;
   2364  nsCOMPtr<nsIReferrerInfo> referrerInfo;
   2365 
   2366  // Get security manager.
   2367  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
   2368  if (NS_WARN_IF(!ssm)) {
   2369    aRv.Throw(NS_ERROR_UNEXPECTED);
   2370    return nullptr;
   2371  }
   2372 
   2373  // Check to see if URI is allowed.  We're not going to worry about a
   2374  // window ID here because it's not 100% clear which window's id we
   2375  // would want, and we're throwing a content-visible exception
   2376  // anyway.
   2377  nsresult rv = ssm->CheckLoadURIWithPrincipal(
   2378      &aSubjectPrincipal, aURI, nsIScriptSecurityManager::STANDARD, 0);
   2379  if (NS_WARN_IF(NS_FAILED(rv))) {
   2380    nsAutoCString spec;
   2381    aURI->GetSpec(spec);
   2382    aRv.ThrowTypeError<MSG_URL_NOT_LOADABLE>(spec);
   2383    return nullptr;
   2384  }
   2385 
   2386  // Create load info
   2387  RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aURI);
   2388 
   2389  if (!aSourceDocument) {
   2390    // No document; just use our subject principal as the triggering principal.
   2391    loadState->SetTriggeringPrincipal(&aSubjectPrincipal);
   2392    return loadState.forget();
   2393  }
   2394 
   2395  nsCOMPtr<nsIURI> docOriginalURI, docCurrentURI, principalURI;
   2396  docOriginalURI = aSourceDocument->GetOriginalURI();
   2397  docCurrentURI = aSourceDocument->GetDocumentURI();
   2398  nsCOMPtr<nsIPrincipal> principal = aSourceDocument->NodePrincipal();
   2399 
   2400  triggeringPrincipal = aSourceDocument->NodePrincipal();
   2401  referrerPolicy = aSourceDocument->GetReferrerPolicy();
   2402 
   2403  bool urisEqual = false;
   2404  if (docOriginalURI && docCurrentURI && principal) {
   2405    principal->EqualsURI(docOriginalURI, &urisEqual);
   2406  }
   2407  if (urisEqual) {
   2408    referrerInfo = new ReferrerInfo(docCurrentURI, referrerPolicy);
   2409  } else {
   2410    principal->CreateReferrerInfo(referrerPolicy, getter_AddRefs(referrerInfo));
   2411  }
   2412  loadState->SetTriggeringPrincipal(triggeringPrincipal);
   2413  loadState->SetTriggeringSandboxFlags(aSourceDocument->GetSandboxFlags());
   2414  loadState->SetPolicyContainer(aSourceDocument->GetPolicyContainer());
   2415  if (referrerInfo) {
   2416    loadState->SetReferrerInfo(referrerInfo);
   2417  }
   2418  loadState->SetHasValidUserGestureActivation(
   2419      aSourceDocument->HasValidTransientUserGestureActivation());
   2420 
   2421  loadState->SetTextDirectiveUserActivation(
   2422      aSourceDocument->ConsumeTextDirectiveUserActivation() ||
   2423      loadState->HasValidUserGestureActivation());
   2424  loadState->SetTriggeringWindowId(aSourceDocument->InnerWindowID());
   2425  loadState->SetTriggeringStorageAccess(aSourceDocument->UsingStorageAccess());
   2426  loadState->SetTriggeringClassificationFlags(
   2427      aSourceDocument->GetScriptTrackingFlags());
   2428 
   2429  return loadState.forget();
   2430 }
   2431 
   2432 // https://html.spec.whatwg.org/#navigate
   2433 // In its current state, this method is not closely following the spec.
   2434 // https://bugzil.la/1974717 tracks the work to align this method with the spec.
   2435 void BrowsingContext::Navigate(
   2436    nsIURI* aURI, Document* aSourceDocument, nsIPrincipal& aSubjectPrincipal,
   2437    ErrorResult& aRv, NavigationHistoryBehavior aHistoryHandling,
   2438    bool aNeedsCompletelyLoadedDocument,
   2439    nsIStructuredCloneContainer* aNavigationAPIState,
   2440    dom::NavigationAPIMethodTracker* aNavigationAPIMethodTracker) {
   2441  MOZ_LOG_FMT(gNavigationAPILog, LogLevel::Debug, "Navigate to {} as {}", *aURI,
   2442              aHistoryHandling);
   2443  CallerType callerType = aSubjectPrincipal.IsSystemPrincipal()
   2444                              ? CallerType::System
   2445                              : CallerType::NonSystem;
   2446 
   2447  nsresult rv = CheckNavigationRateLimit(callerType);
   2448  if (NS_FAILED(rv)) {
   2449    aRv.Throw(rv);
   2450    return;
   2451  }
   2452 
   2453  RefPtr<nsDocShellLoadState> loadState =
   2454      CheckURLAndCreateLoadState(aURI, aSubjectPrincipal, aSourceDocument, aRv);
   2455  if (aRv.Failed()) {
   2456    return;
   2457  }
   2458 
   2459  if (mozilla::SessionHistoryInParent()) {
   2460    loadState->SetNeedsCompletelyLoadedDocument(aNeedsCompletelyLoadedDocument);
   2461    loadState->SetHistoryBehavior(aHistoryHandling);
   2462  }
   2463 
   2464  if (aHistoryHandling == NavigationHistoryBehavior::Replace) {
   2465    loadState->SetLoadType(LOAD_STOP_CONTENT_AND_REPLACE);
   2466  } else {
   2467    loadState->SetLoadType(LOAD_STOP_CONTENT);
   2468  }
   2469 
   2470  // Get the incumbent script's browsing context to set as source.
   2471  nsCOMPtr<nsPIDOMWindowInner> sourceWindow =
   2472      nsContentUtils::IncumbentInnerWindow();
   2473  if (sourceWindow) {
   2474    WindowContext* context = sourceWindow->GetWindowContext();
   2475    loadState->SetSourceBrowsingContext(sourceWindow->GetBrowsingContext());
   2476    loadState->SetHasValidUserGestureActivation(
   2477        context && context->HasValidTransientUserGestureActivation());
   2478  }
   2479 
   2480  loadState->SetLoadFlags(nsIWebNavigation::LOAD_FLAGS_NONE);
   2481  loadState->SetFirstParty(true);
   2482  loadState->SetNavigationAPIState(aNavigationAPIState);
   2483  loadState->SetNavigationAPIMethodTracker(aNavigationAPIMethodTracker);
   2484 
   2485  rv = LoadURI(loadState);
   2486  if (NS_WARN_IF(NS_FAILED(rv))) {
   2487    if (rv == NS_ERROR_DOM_BAD_CROSS_ORIGIN_URI &&
   2488        loadState->URI()->SchemeIs("javascript")) {
   2489      // Per spec[1], attempting to load a javascript: URI into a cross-origin
   2490      // BrowsingContext is a no-op, and should not raise an exception.
   2491      // Technically, Location setters run with exceptions enabled should only
   2492      // throw an exception[2] when the caller is not allowed to navigate[3] the
   2493      // target browsing context due to sandboxing flags or not being
   2494      // closely-related enough, though in practice we currently throw for other
   2495      // reasons as well.
   2496      //
   2497      // [1]:
   2498      // https://html.spec.whatwg.org/multipage/browsing-the-web.html#javascript-protocol
   2499      // [2]:
   2500      // https://html.spec.whatwg.org/multipage/browsing-the-web.html#navigate
   2501      // [3]:
   2502      // https://html.spec.whatwg.org/multipage/browsers.html#allowed-to-navigate
   2503      return;
   2504    }
   2505    aRv.Throw(rv);
   2506    return;
   2507  }
   2508 
   2509  Document* doc = GetDocument();
   2510  if (doc && nsContentUtils::IsExternalProtocol(aURI)) {
   2511    doc->EnsureNotEnteringAndExitFullscreen();
   2512  }
   2513 }
   2514 
   2515 void BrowsingContext::DisplayLoadError(const nsAString& aURI) {
   2516  MOZ_LOG(GetLog(), LogLevel::Debug, ("DisplayLoadError"));
   2517  MOZ_DIAGNOSTIC_ASSERT(!IsDiscarded());
   2518  MOZ_DIAGNOSTIC_ASSERT(mDocShell || XRE_IsParentProcess());
   2519 
   2520  if (mDocShell) {
   2521    bool didDisplayLoadError = false;
   2522    nsCOMPtr<nsIDocShell> docShell = mDocShell;
   2523    docShell->DisplayLoadError(NS_ERROR_MALFORMED_URI, nullptr,
   2524                               PromiseFlatString(aURI).get(), nullptr,
   2525                               &didDisplayLoadError);
   2526  } else {
   2527    if (ContentParent* cp = Canonical()->GetContentParent()) {
   2528      (void)cp->SendDisplayLoadError(this, PromiseFlatString(aURI));
   2529    }
   2530  }
   2531 }
   2532 
   2533 WindowProxyHolder BrowsingContext::Window() {
   2534  return WindowProxyHolder(Self());
   2535 }
   2536 
   2537 WindowProxyHolder BrowsingContext::GetFrames(ErrorResult& aError) {
   2538  return Window();
   2539 }
   2540 
   2541 void BrowsingContext::Close(CallerType aCallerType, ErrorResult& aError) {
   2542  if (mIsDiscarded) {
   2543    return;
   2544  }
   2545 
   2546  if (IsSubframe()) {
   2547    // .close() on frames is a no-op.
   2548    return;
   2549  }
   2550 
   2551  if (GetDOMWindow()) {
   2552    nsGlobalWindowOuter::Cast(GetDOMWindow())
   2553        ->CloseOuter(aCallerType == CallerType::System);
   2554    return;
   2555  }
   2556 
   2557  // This is a bit of a hack for webcompat. Content needs to see an updated
   2558  // |window.closed| value as early as possible, so we set this before we
   2559  // actually send the DOMWindowClose event, which happens in the process where
   2560  // the document for this browsing context is loaded.
   2561  MOZ_ALWAYS_SUCCEEDS(SetClosed(true));
   2562 
   2563  if (ContentChild* cc = ContentChild::GetSingleton()) {
   2564    cc->SendWindowClose(this, aCallerType == CallerType::System);
   2565  } else if (ContentParent* cp = Canonical()->GetContentParent()) {
   2566    (void)cp->SendWindowClose(this, aCallerType == CallerType::System);
   2567  }
   2568 }
   2569 
   2570 template <typename FuncT>
   2571 inline bool ApplyToDocumentsForPopup(Document* doc, FuncT func) {
   2572  // HACK: Some pages using bogus library + UA sniffing call window.open()
   2573  // from a blank iframe, only on Firefox, see bug 1685056.
   2574  //
   2575  // This is a hack-around to preserve behavior in that particular and
   2576  // specific case, by consuming activation on the parent document, so we
   2577  // don't care about the InProcessParent bits not being fission-safe or what
   2578  // not.
   2579  if (func(doc)) {
   2580    return true;
   2581  }
   2582  if (!doc->IsInitialDocument()) {
   2583    return false;
   2584  }
   2585  Document* parentDoc = doc->GetInProcessParentDocument();
   2586  if (!parentDoc || !parentDoc->NodePrincipal()->Equals(doc->NodePrincipal())) {
   2587    return false;
   2588  }
   2589  return func(parentDoc);
   2590 }
   2591 
   2592 PopupBlocker::PopupControlState BrowsingContext::RevisePopupAbuseLevel(
   2593    PopupBlocker::PopupControlState aControl) {
   2594  if (!IsContent()) {
   2595    return PopupBlocker::openAllowed;
   2596  }
   2597 
   2598  RefPtr<Document> doc = GetExtantDocument();
   2599  PopupBlocker::PopupControlState abuse = aControl;
   2600  switch (abuse) {
   2601    case PopupBlocker::openControlled:
   2602    case PopupBlocker::openOverridden:
   2603      if (IsPopupAllowed()) {
   2604        // Go down one state enum step:
   2605        //   openControlled (1) -> openAllowed (0)
   2606        //   openOverridden (4) -> openAbused (3)
   2607        abuse = PopupBlocker::PopupControlState(abuse - 1);
   2608      }
   2609      break;
   2610    case PopupBlocker::openAbused:
   2611      if (IsPopupAllowed() ||
   2612          (doc && doc->HasValidTransientUserGestureActivation())) {
   2613        // Always go down to openControlled:
   2614        //   openAbused (3)  -> openControlled (1), skip openBlocked (2)
   2615        abuse = PopupBlocker::openControlled;
   2616      }
   2617      break;
   2618    case PopupBlocker::openAllowed:
   2619      break;
   2620    case PopupBlocker::openBlocked:
   2621      if (IsPopupAllowed() ||
   2622          (doc && doc->HasValidTransientUserGestureActivation())) {
   2623        // Go down one state enum step:
   2624        //   openBlocked (2) -> openControlled (1)
   2625        abuse = PopupBlocker::openControlled;
   2626      }
   2627      break;
   2628    default:
   2629      NS_WARNING("Strange PopupControlState!");
   2630  }
   2631 
   2632  // limit the number of simultaneously open popups
   2633  if (abuse == PopupBlocker::openAbused || abuse == PopupBlocker::openBlocked ||
   2634      abuse == PopupBlocker::openControlled) {
   2635    int32_t popupMax = StaticPrefs::dom_popup_maximum();
   2636    if (popupMax >= 0 &&
   2637        PopupBlocker::GetOpenPopupSpamCount() >= (uint32_t)popupMax) {
   2638      abuse = PopupBlocker::openOverridden;
   2639    }
   2640  }
   2641 
   2642  // If we're currently in-process, attempt to consume transient user gesture
   2643  // activations.
   2644  if (doc) {
   2645    auto ConsumeTransientUserActivationForMultiplePopupBlocking =
   2646        [&]() -> bool {
   2647      return ApplyToDocumentsForPopup(doc, [](Document* doc) {
   2648        return doc->ConsumeTransientUserGestureActivation();
   2649      });
   2650    };
   2651 
   2652    // If this popup is allowed, let's block any other for this event, forcing
   2653    // PopupBlocker::openBlocked state.
   2654    if ((abuse == PopupBlocker::openAllowed ||
   2655         abuse == PopupBlocker::openControlled) &&
   2656        !IsPopupAllowed() &&
   2657        !ConsumeTransientUserActivationForMultiplePopupBlocking()) {
   2658      nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "DOM"_ns,
   2659                                      doc, nsContentUtils::eDOM_PROPERTIES,
   2660                                      "MultiplePopupsBlockedNoUserActivation");
   2661      abuse = PopupBlocker::openBlocked;
   2662    }
   2663  }
   2664 
   2665  return abuse;
   2666 }
   2667 
   2668 void BrowsingContext::GetUserActivationModifiersForPopup(
   2669    UserActivation::Modifiers* aModifiers) {
   2670  RefPtr<Document> doc = GetExtantDocument();
   2671  if (doc) {
   2672    // Unlike RevisePopupAbuseLevel, modifiers can always be used regardless
   2673    // of PopupControlState.
   2674    (void)ApplyToDocumentsForPopup(doc, [&](Document* doc) {
   2675      return doc->GetTransientUserGestureActivationModifiers(aModifiers);
   2676    });
   2677  }
   2678 }
   2679 
   2680 void BrowsingContext::IncrementHistoryEntryCountForBrowsingContext() {
   2681  (void)SetHistoryEntryCount(GetHistoryEntryCount() + 1);
   2682 }
   2683 
   2684 // https://wicg.github.io/document-picture-in-picture/#focusing-the-opener-window
   2685 static bool ConsumePiPWindowTransientActivation(nsPIDOMWindowOuter* outer) {
   2686  NS_ENSURE_TRUE(outer, false);
   2687 
   2688  nsPIDOMWindowInner* inner = outer->GetCurrentInnerWindow();
   2689  NS_ENSURE_TRUE(inner, false);
   2690 
   2691  DocumentPictureInPicture* dpip = inner->GetExtantDocumentPictureInPicture();
   2692  if (!dpip) {
   2693    return false;
   2694  }
   2695  nsGlobalWindowInner* pipWindow = dpip->GetWindow();
   2696  if (!pipWindow) {
   2697    return false;
   2698  }
   2699 
   2700  WindowContext* wc = pipWindow->GetWindowContext();
   2701  NS_ENSURE_TRUE(wc, false);
   2702 
   2703  return wc->ConsumeTransientUserGestureActivation();
   2704 }
   2705 
   2706 std::tuple<bool, bool> BrowsingContext::CanFocusCheck(CallerType aCallerType) {
   2707  nsFocusManager* fm = nsFocusManager::GetFocusManager();
   2708  if (!fm) {
   2709    return {false, false};
   2710  }
   2711 
   2712  nsCOMPtr<nsPIDOMWindowInner> caller = do_QueryInterface(GetEntryGlobal());
   2713  BrowsingContext* callerBC = caller ? caller->GetBrowsingContext() : nullptr;
   2714  RefPtr<BrowsingContext> openerBC = GetOpener();
   2715  MOZ_DIAGNOSTIC_ASSERT(!openerBC || openerBC->Group() == Group());
   2716 
   2717  // Enforce dom.disable_window_flip (for non-chrome), but still allow the
   2718  // window which opened us to raise us at times when popups are allowed
   2719  // (bugs 355482 and 369306).
   2720  bool canFocus = aCallerType == CallerType::System ||
   2721                  !Preferences::GetBool("dom.disable_window_flip", true);
   2722  if (!canFocus && openerBC == callerBC) {
   2723    canFocus =
   2724        (callerBC ? callerBC : this)
   2725            ->RevisePopupAbuseLevel(PopupBlocker::GetPopupControlState()) <
   2726        PopupBlocker::openBlocked;
   2727  }
   2728 
   2729  // Allow the opener to get system focus if the PIP window has transient
   2730  // activation
   2731  if (!canFocus && IsTopContent() &&
   2732      ConsumePiPWindowTransientActivation(GetDOMWindow())) {
   2733    canFocus = true;
   2734  }
   2735 
   2736  bool isActive = false;
   2737  if (XRE_IsParentProcess()) {
   2738    CanonicalBrowsingContext* chromeTop = Canonical()->TopCrossChromeBoundary();
   2739    nsCOMPtr<nsPIDOMWindowOuter> activeWindow = fm->GetActiveWindow();
   2740    isActive = activeWindow == chromeTop->GetDOMWindow();
   2741  } else {
   2742    isActive = fm->GetActiveBrowsingContext() == Top();
   2743  }
   2744 
   2745  return {canFocus, isActive};
   2746 }
   2747 
   2748 void BrowsingContext::Focus(CallerType aCallerType, ErrorResult& aError) {
   2749  // These checks need to happen before the RequestFrameFocus call, which
   2750  // is why they are done in an untrusted process. If we wanted to enforce
   2751  // these in the parent, we'd need to do the checks there _also_.
   2752  // These should be kept in sync with nsGlobalWindowOuter::FocusOuter.
   2753 
   2754  auto [canFocus, isActive] = CanFocusCheck(aCallerType);
   2755 
   2756  if (!(canFocus || isActive)) {
   2757    return;
   2758  }
   2759 
   2760  // Permission check passed
   2761 
   2762  if (mEmbedderElement) {
   2763    // Make the activeElement in this process update synchronously.
   2764    nsContentUtils::RequestFrameFocus(*mEmbedderElement, true, aCallerType);
   2765  }
   2766  uint64_t actionId = nsFocusManager::GenerateFocusActionId();
   2767  if (ContentChild* cc = ContentChild::GetSingleton()) {
   2768    cc->SendWindowFocus(this, aCallerType, actionId);
   2769  } else if (ContentParent* cp = Canonical()->GetContentParent()) {
   2770    (void)cp->SendWindowFocus(this, aCallerType, actionId);
   2771  }
   2772 }
   2773 
   2774 bool BrowsingContext::CanBlurCheck(CallerType aCallerType) {
   2775  // If dom.disable_window_flip == true, then content should not be allowed
   2776  // to do blur (this would allow popunders, bug 369306)
   2777  return aCallerType == CallerType::System ||
   2778         !Preferences::GetBool("dom.disable_window_flip", true);
   2779 }
   2780 
   2781 void BrowsingContext::Blur(CallerType aCallerType, ErrorResult& aError) {
   2782  if (!CanBlurCheck(aCallerType)) {
   2783    return;
   2784  }
   2785 
   2786  if (ContentChild* cc = ContentChild::GetSingleton()) {
   2787    cc->SendWindowBlur(this, aCallerType);
   2788  } else if (ContentParent* cp = Canonical()->GetContentParent()) {
   2789    (void)cp->SendWindowBlur(this, aCallerType);
   2790  }
   2791 }
   2792 
   2793 Nullable<WindowProxyHolder> BrowsingContext::GetWindow() {
   2794  if (XRE_IsParentProcess() && !IsInProcess()) {
   2795    return nullptr;
   2796  }
   2797  return WindowProxyHolder(this);
   2798 }
   2799 
   2800 Nullable<WindowProxyHolder> BrowsingContext::GetTop(ErrorResult& aError) {
   2801  if (mIsDiscarded) {
   2802    return nullptr;
   2803  }
   2804 
   2805  // We never return null or throw an error, but the implementation in
   2806  // nsGlobalWindow does and we need to use the same signature.
   2807  return WindowProxyHolder(Top());
   2808 }
   2809 
   2810 void BrowsingContext::GetOpener(JSContext* aCx,
   2811                                JS::MutableHandle<JS::Value> aOpener,
   2812                                ErrorResult& aError) const {
   2813  RefPtr<BrowsingContext> opener = GetOpener();
   2814  if (!opener) {
   2815    aOpener.setNull();
   2816    return;
   2817  }
   2818 
   2819  if (!ToJSValue(aCx, WindowProxyHolder(opener), aOpener)) {
   2820    aError.NoteJSContextException(aCx);
   2821  }
   2822 }
   2823 
   2824 // We never throw an error, but the implementation in nsGlobalWindow does and
   2825 // we need to use the same signature.
   2826 Nullable<WindowProxyHolder> BrowsingContext::GetParent(ErrorResult& aError) {
   2827  if (mIsDiscarded) {
   2828    return nullptr;
   2829  }
   2830 
   2831  if (GetParent()) {
   2832    return WindowProxyHolder(GetParent());
   2833  }
   2834  return WindowProxyHolder(this);
   2835 }
   2836 
   2837 void BrowsingContext::PostMessageMoz(JSContext* aCx,
   2838                                     JS::Handle<JS::Value> aMessage,
   2839                                     const nsAString& aTargetOrigin,
   2840                                     const Sequence<JSObject*>& aTransfer,
   2841                                     nsIPrincipal& aSubjectPrincipal,
   2842                                     ErrorResult& aError) {
   2843  if (mIsDiscarded) {
   2844    return;
   2845  }
   2846 
   2847  RefPtr<BrowsingContext> sourceBc;
   2848  PostMessageData data;
   2849  data.targetOrigin() = aTargetOrigin;
   2850  data.subjectPrincipal() = &aSubjectPrincipal;
   2851  RefPtr<nsGlobalWindowInner> callerInnerWindow;
   2852  nsAutoCString scriptLocation;
   2853  // We don't need to get the caller's agentClusterId since that is used for
   2854  // checking whether it's okay to sharing memory (and it's not allowed to share
   2855  // memory cross processes)
   2856  if (!nsGlobalWindowOuter::GatherPostMessageData(
   2857          aCx, aTargetOrigin, getter_AddRefs(sourceBc), data.origin(),
   2858          getter_AddRefs(data.targetOriginURI()),
   2859          getter_AddRefs(data.callerPrincipal()),
   2860          getter_AddRefs(callerInnerWindow), getter_AddRefs(data.callerURI()),
   2861          /* aCallerAgentClusterId */ nullptr, &scriptLocation, aError)) {
   2862    return;
   2863  }
   2864  if (sourceBc && sourceBc->IsDiscarded()) {
   2865    return;
   2866  }
   2867  data.source() = sourceBc;
   2868  data.isFromPrivateWindow() =
   2869      callerInnerWindow &&
   2870      nsScriptErrorBase::ComputeIsFromPrivateWindow(callerInnerWindow);
   2871  data.innerWindowId() = callerInnerWindow ? callerInnerWindow->WindowID() : 0;
   2872  data.scriptLocation() = scriptLocation;
   2873  JS::Rooted<JS::Value> transferArray(aCx);
   2874  aError = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransfer,
   2875                                                             &transferArray);
   2876  if (NS_WARN_IF(aError.Failed())) {
   2877    return;
   2878  }
   2879 
   2880  JS::CloneDataPolicy clonePolicy;
   2881  if (callerInnerWindow && callerInnerWindow->IsSharedMemoryAllowed()) {
   2882    clonePolicy.allowSharedMemoryObjects();
   2883  }
   2884 
   2885  // We will see if the message is required to be in the same process or it can
   2886  // be in the different process after Write().
   2887  ipc::StructuredCloneData message = ipc::StructuredCloneData(
   2888      StructuredCloneHolder::StructuredCloneScope::UnknownDestination,
   2889      StructuredCloneHolder::TransferringSupported);
   2890  message.Write(aCx, aMessage, transferArray, clonePolicy, aError);
   2891  if (NS_WARN_IF(aError.Failed())) {
   2892    return;
   2893  }
   2894 
   2895  ClonedOrErrorMessageData messageData;
   2896  if (ContentChild* cc = ContentChild::GetSingleton()) {
   2897    // The clone scope gets set when we write the message data based on the
   2898    // requirements of that data that we're writing.
   2899    // If the message data contains a shared memory object, then CloneScope
   2900    // would return SameProcess. Otherwise, it returns DifferentProcess.
   2901    if (message.CloneScope() ==
   2902        StructuredCloneHolder::StructuredCloneScope::DifferentProcess) {
   2903      ClonedMessageData clonedMessageData;
   2904      if (!message.BuildClonedMessageData(clonedMessageData)) {
   2905        aError.Throw(NS_ERROR_FAILURE);
   2906        return;
   2907      }
   2908 
   2909      messageData = std::move(clonedMessageData);
   2910    } else {
   2911      MOZ_ASSERT(message.CloneScope() ==
   2912                 StructuredCloneHolder::StructuredCloneScope::SameProcess);
   2913 
   2914      messageData = ErrorMessageData();
   2915 
   2916      nsContentUtils::ReportToConsole(
   2917          nsIScriptError::warningFlag, "DOM Window"_ns,
   2918          callerInnerWindow ? callerInnerWindow->GetDocument() : nullptr,
   2919          nsContentUtils::eDOM_PROPERTIES,
   2920          "PostMessageSharedMemoryObjectToCrossOriginWarning");
   2921    }
   2922 
   2923    cc->SendWindowPostMessage(this, messageData, data);
   2924  } else if (ContentParent* cp = Canonical()->GetContentParent()) {
   2925    if (message.CloneScope() ==
   2926        StructuredCloneHolder::StructuredCloneScope::DifferentProcess) {
   2927      ClonedMessageData clonedMessageData;
   2928      if (!message.BuildClonedMessageData(clonedMessageData)) {
   2929        aError.Throw(NS_ERROR_FAILURE);
   2930        return;
   2931      }
   2932 
   2933      messageData = std::move(clonedMessageData);
   2934    } else {
   2935      MOZ_ASSERT(message.CloneScope() ==
   2936                 StructuredCloneHolder::StructuredCloneScope::SameProcess);
   2937 
   2938      messageData = ErrorMessageData();
   2939 
   2940      nsContentUtils::ReportToConsole(
   2941          nsIScriptError::warningFlag, "DOM Window"_ns,
   2942          callerInnerWindow ? callerInnerWindow->GetDocument() : nullptr,
   2943          nsContentUtils::eDOM_PROPERTIES,
   2944          "PostMessageSharedMemoryObjectToCrossOriginWarning");
   2945    }
   2946 
   2947    (void)cp->SendWindowPostMessage(this, messageData, data);
   2948  }
   2949 }
   2950 
   2951 void BrowsingContext::PostMessageMoz(JSContext* aCx,
   2952                                     JS::Handle<JS::Value> aMessage,
   2953                                     const WindowPostMessageOptions& aOptions,
   2954                                     nsIPrincipal& aSubjectPrincipal,
   2955                                     ErrorResult& aError) {
   2956  PostMessageMoz(aCx, aMessage, aOptions.mTargetOrigin, aOptions.mTransfer,
   2957                 aSubjectPrincipal, aError);
   2958 }
   2959 
   2960 void BrowsingContext::SendCommitTransaction(ContentParent* aParent,
   2961                                            const BaseTransaction& aTxn,
   2962                                            uint64_t aEpoch) {
   2963  (void)aParent->SendCommitBrowsingContextTransaction(this, aTxn, aEpoch);
   2964 }
   2965 
   2966 void BrowsingContext::SendCommitTransaction(ContentChild* aChild,
   2967                                            const BaseTransaction& aTxn,
   2968                                            uint64_t aEpoch) {
   2969  aChild->SendCommitBrowsingContextTransaction(this, aTxn, aEpoch);
   2970 }
   2971 
   2972 BrowsingContext::IPCInitializer BrowsingContext::GetIPCInitializer() {
   2973  MOZ_DIAGNOSTIC_ASSERT(mEverAttached);
   2974  MOZ_DIAGNOSTIC_ASSERT(mType == Type::Content);
   2975 
   2976  IPCInitializer init;
   2977  init.mId = Id();
   2978  init.mParentId = mParentWindow ? mParentWindow->Id() : 0;
   2979  init.mWindowless = mWindowless;
   2980  init.mUseRemoteTabs = mUseRemoteTabs;
   2981  init.mUseRemoteSubframes = mUseRemoteSubframes;
   2982  init.mCreatedDynamically = mCreatedDynamically;
   2983  init.mChildOffset = mChildOffset;
   2984  init.mOriginAttributes = mOriginAttributes;
   2985  if (mChildSessionHistory && mozilla::SessionHistoryInParent()) {
   2986    init.mSessionHistoryIndex = mChildSessionHistory->Index();
   2987    init.mSessionHistoryCount = mChildSessionHistory->Count();
   2988  }
   2989  init.mRequestContextId = mRequestContextId;
   2990  init.mFields = mFields.RawValues();
   2991  return init;
   2992 }
   2993 
   2994 already_AddRefed<WindowContext> BrowsingContext::IPCInitializer::GetParent() {
   2995  RefPtr<WindowContext> parent;
   2996  if (mParentId != 0) {
   2997    parent = WindowContext::GetById(mParentId);
   2998    MOZ_RELEASE_ASSERT(parent);
   2999  }
   3000  return parent.forget();
   3001 }
   3002 
   3003 already_AddRefed<BrowsingContext> BrowsingContext::IPCInitializer::GetOpener() {
   3004  RefPtr<BrowsingContext> opener;
   3005  if (GetOpenerId() != 0) {
   3006    opener = BrowsingContext::Get(GetOpenerId());
   3007    MOZ_RELEASE_ASSERT(opener);
   3008  }
   3009  return opener.forget();
   3010 }
   3011 
   3012 void BrowsingContext::StartDelayedAutoplayMediaComponents() {
   3013  if (!mDocShell) {
   3014    return;
   3015  }
   3016  AUTOPLAY_LOG("%s : StartDelayedAutoplayMediaComponents for bc 0x%08" PRIx64,
   3017               XRE_IsParentProcess() ? "Parent" : "Child", Id());
   3018  mDocShell->StartDelayedAutoplayMediaComponents();
   3019 }
   3020 
   3021 nsresult BrowsingContext::ResetGVAutoplayRequestStatus() {
   3022  MOZ_ASSERT(IsTop(),
   3023             "Should only set GVAudibleAutoplayRequestStatus in the top-level "
   3024             "browsing context");
   3025 
   3026  Transaction txn;
   3027  txn.SetGVAudibleAutoplayRequestStatus(GVAutoplayRequestStatus::eUNKNOWN);
   3028  txn.SetGVInaudibleAutoplayRequestStatus(GVAutoplayRequestStatus::eUNKNOWN);
   3029  return txn.Commit(this);
   3030 }
   3031 
   3032 template <typename Callback>
   3033 void BrowsingContext::WalkPresContexts(Callback&& aCallback) {
   3034  PreOrderWalk([&](BrowsingContext* aContext) {
   3035    if (nsIDocShell* shell = aContext->GetDocShell()) {
   3036      if (RefPtr pc = shell->GetPresContext()) {
   3037        aCallback(pc.get());
   3038      }
   3039    }
   3040  });
   3041 }
   3042 
   3043 void BrowsingContext::PresContextAffectingFieldChanged() {
   3044  WalkPresContexts([&](nsPresContext* aPc) {
   3045    aPc->RecomputeBrowsingContextDependentData();
   3046  });
   3047 }
   3048 
   3049 void BrowsingContext::DidSet(FieldIndex<IDX_SessionStoreEpoch>,
   3050                             uint32_t aOldValue) {
   3051  if (!mCurrentWindowContext) {
   3052    return;
   3053  }
   3054  SessionStoreChild* sessionStoreChild =
   3055      SessionStoreChild::From(mCurrentWindowContext->GetWindowGlobalChild());
   3056  if (!sessionStoreChild) {
   3057    return;
   3058  }
   3059 
   3060  sessionStoreChild->SetEpoch(GetSessionStoreEpoch());
   3061 }
   3062 
   3063 void BrowsingContext::DidSet(FieldIndex<IDX_GVAudibleAutoplayRequestStatus>) {
   3064  MOZ_ASSERT(IsTop(),
   3065             "Should only set GVAudibleAutoplayRequestStatus in the top-level "
   3066             "browsing context");
   3067 }
   3068 
   3069 void BrowsingContext::DidSet(FieldIndex<IDX_GVInaudibleAutoplayRequestStatus>) {
   3070  MOZ_ASSERT(IsTop(),
   3071             "Should only set GVAudibleAutoplayRequestStatus in the top-level "
   3072             "browsing context");
   3073 }
   3074 
   3075 bool BrowsingContext::CanSet(FieldIndex<IDX_ExplicitActive>,
   3076                             const ExplicitActiveStatus&,
   3077                             ContentParent* aSource) {
   3078  return XRE_IsParentProcess() && IsTop() && !aSource;
   3079 }
   3080 
   3081 void BrowsingContext::DidSet(FieldIndex<IDX_ExplicitActive>,
   3082                             ExplicitActiveStatus aOldValue) {
   3083  MOZ_ASSERT(IsTop());
   3084 
   3085  const bool isActive = IsActive();
   3086  const bool wasActive = [&] {
   3087    if (aOldValue != ExplicitActiveStatus::None) {
   3088      return aOldValue == ExplicitActiveStatus::Active;
   3089    }
   3090    return GetParent() && GetParent()->IsActive();
   3091  }();
   3092 
   3093  if (isActive == wasActive) {
   3094    return;
   3095  }
   3096 
   3097  Group()->UpdateToplevelsSuspendedIfNeeded();
   3098  if (XRE_IsParentProcess()) {
   3099    if (BrowserParent* bp = Canonical()->GetBrowserParent()) {
   3100      bp->RecomputeProcessPriority();
   3101 #if defined(XP_WIN) && defined(ACCESSIBILITY)
   3102      if (a11y::Compatibility::IsDolphin()) {
   3103        // update active accessible documents on windows
   3104        if (a11y::DocAccessibleParent* tabDoc =
   3105                bp->GetTopLevelDocAccessible()) {
   3106          HWND window = tabDoc->GetEmulatedWindowHandle();
   3107          MOZ_ASSERT(window);
   3108          if (window) {
   3109            if (isActive) {
   3110              a11y::nsWinUtils::ShowNativeWindow(window);
   3111            } else {
   3112              a11y::nsWinUtils::HideNativeWindow(window);
   3113            }
   3114          }
   3115        }
   3116      }
   3117 #endif
   3118    }
   3119 
   3120    // NOTE(emilio): Ideally we'd want to reuse the ExplicitActiveStatus::None
   3121    // set-up, but that's non-trivial to do because in content processes we
   3122    // can't access the top-cross-chrome-boundary bc.
   3123    auto manageTopDescendant = [&](auto* aChild) {
   3124      if (!aChild->ManuallyManagesActiveness()) {
   3125        aChild->SetIsActiveInternal(isActive, IgnoreErrors());
   3126        if (BrowserParent* bp = aChild->GetBrowserParent()) {
   3127          bp->SetRenderLayers(isActive);
   3128        }
   3129      }
   3130      return CallState::Continue;
   3131    };
   3132    Canonical()->CallOnTopDescendants(
   3133        manageTopDescendant,
   3134        CanonicalBrowsingContext::TopDescendantKind::NonNested);
   3135  }
   3136 
   3137  PreOrderWalk([&](BrowsingContext* aContext) {
   3138    if (nsCOMPtr<nsIDocShell> ds = aContext->GetDocShell()) {
   3139      if (auto* bc = BrowserChild::GetFrom(ds)) {
   3140        bc->UpdateVisibility();
   3141      }
   3142      nsDocShell::Cast(ds)->ActivenessMaybeChanged();
   3143    }
   3144  });
   3145 }
   3146 
   3147 void BrowsingContext::DidSet(FieldIndex<IDX_InRDMPane>, bool aOldValue) {
   3148  MOZ_ASSERT(IsTop(),
   3149             "Should only set InRDMPane in the top-level browsing context");
   3150  if (GetInRDMPane() == aOldValue) {
   3151    return;
   3152  }
   3153 
   3154  // Reset screen orientation override when disabling RDM.
   3155  if (!GetInRDMPane()) {
   3156    ResetOrientationOverride();
   3157  }
   3158 
   3159  PresContextAffectingFieldChanged();
   3160 }
   3161 
   3162 void BrowsingContext::DidSet(FieldIndex<IDX_HasOrientationOverride>,
   3163                             bool aOldValue) {
   3164  bool hasOrientationOverride = GetHasOrientationOverride();
   3165  OrientationType type = GetCurrentOrientationType();
   3166  float angle = GetCurrentOrientationAngle();
   3167 
   3168  PreOrderWalk([&](BrowsingContext* aBrowsingContext) {
   3169    if (RefPtr<WindowContext> windowContext =
   3170            aBrowsingContext->GetCurrentWindowContext()) {
   3171      if (nsCOMPtr<nsPIDOMWindowInner> window =
   3172              windowContext->GetInnerWindow()) {
   3173        ScreenOrientation* orientation =
   3174            nsGlobalWindowInner::Cast(window)->Screen()->Orientation();
   3175 
   3176        float screenOrientationAngle =
   3177            orientation->DeviceAngle(CallerType::System);
   3178        OrientationType screenOrientationType =
   3179            orientation->DeviceType(CallerType::System);
   3180 
   3181        bool overrideIsDifferentThanDevice =
   3182            screenOrientationType != type || screenOrientationAngle != angle;
   3183 
   3184        // Reset orientation override.
   3185        if (!hasOrientationOverride && aOldValue) {
   3186          (void)aBrowsingContext->SetCurrentOrientation(screenOrientationType,
   3187                                                        screenOrientationAngle);
   3188        } else if (!aBrowsingContext->IsTop()) {
   3189          // Sync orientation override in the existing frames.
   3190          (void)aBrowsingContext->SetCurrentOrientation(type, angle);
   3191        }
   3192 
   3193        orientation->MaybeDispatchEventsForOverride(
   3194            aBrowsingContext, aOldValue, overrideIsDifferentThanDevice);
   3195      }
   3196    }
   3197  });
   3198 }
   3199 
   3200 void BrowsingContext::DidSet(FieldIndex<IDX_ForceDesktopViewport>,
   3201                             bool aOldValue) {
   3202  MOZ_ASSERT(IsTop(), "Should only set in the top-level browsing context");
   3203  if (ForceDesktopViewport() == aOldValue) {
   3204    return;
   3205  }
   3206  PresContextAffectingFieldChanged();
   3207  if (nsIDocShell* shell = GetDocShell()) {
   3208    if (RefPtr ps = shell->GetPresShell()) {
   3209      ps->MaybeRecreateMobileViewportManager(/* aAfterInitialization= */ true);
   3210    }
   3211  }
   3212 }
   3213 
   3214 bool BrowsingContext::CanSet(FieldIndex<IDX_PageAwakeRequestCount>,
   3215                             uint32_t aNewValue, ContentParent* aSource) {
   3216  return IsTop() && XRE_IsParentProcess() && !aSource;
   3217 }
   3218 
   3219 void BrowsingContext::DidSet(FieldIndex<IDX_PageAwakeRequestCount>,
   3220                             uint32_t aOldValue) {
   3221  if (!IsTop() || aOldValue == GetPageAwakeRequestCount()) {
   3222    return;
   3223  }
   3224  Group()->UpdateToplevelsSuspendedIfNeeded();
   3225 }
   3226 
   3227 auto BrowsingContext::CanSet(FieldIndex<IDX_AllowJavascript>, bool aValue,
   3228                             ContentParent* aSource) -> CanSetResult {
   3229  if (mozilla::SessionHistoryInParent()) {
   3230    return XRE_IsParentProcess() && !aSource ? CanSetResult::Allow
   3231                                             : CanSetResult::Deny;
   3232  }
   3233 
   3234  // Without Session History in Parent, session restore code still needs to set
   3235  // this from content processes.
   3236  return LegacyRevertIfNotOwningOrParentProcess(aSource);
   3237 }
   3238 
   3239 void BrowsingContext::DidSet(FieldIndex<IDX_AllowJavascript>, bool aOldValue) {
   3240  RecomputeCanExecuteScripts();
   3241 }
   3242 
   3243 void BrowsingContext::RecomputeCanExecuteScripts() {
   3244  const bool old = mCanExecuteScripts;
   3245  if (!AllowJavascript()) {
   3246    // Scripting has been explicitly disabled on our BrowsingContext.
   3247    mCanExecuteScripts = false;
   3248  } else if (GetParentWindowContext()) {
   3249    // Otherwise, inherit parent.
   3250    mCanExecuteScripts = GetParentWindowContext()->CanExecuteScripts();
   3251  } else {
   3252    // Otherwise, we're the root of the tree, and we haven't explicitly disabled
   3253    // script. Allow.
   3254    mCanExecuteScripts = true;
   3255  }
   3256 
   3257  if (old != mCanExecuteScripts) {
   3258    for (WindowContext* wc : GetWindowContexts()) {
   3259      wc->RecomputeCanExecuteScripts();
   3260    }
   3261  }
   3262 }
   3263 
   3264 bool BrowsingContext::InactiveForSuspend() const {
   3265  if (!StaticPrefs::dom_suspend_inactive_enabled()) {
   3266    return false;
   3267  }
   3268  // We should suspend a page only when it's inactive and doesn't have any awake
   3269  // request that is used to prevent page from being suspended because web page
   3270  // might still need to run their script. Eg. waiting for media keys to resume
   3271  // media, playing web audio, waiting in a video call conference room.
   3272  return !IsActive() && GetPageAwakeRequestCount() == 0;
   3273 }
   3274 
   3275 bool BrowsingContext::CanSet(FieldIndex<IDX_TouchEventsOverrideInternal>,
   3276                             dom::TouchEventsOverride, ContentParent* aSource) {
   3277  return XRE_IsParentProcess() && !aSource;
   3278 }
   3279 
   3280 void BrowsingContext::DidSet(FieldIndex<IDX_TouchEventsOverrideInternal>,
   3281                             dom::TouchEventsOverride&& aOldValue) {
   3282  if (GetTouchEventsOverrideInternal() == aOldValue) {
   3283    return;
   3284  }
   3285  WalkPresContexts([&](nsPresContext* aPc) {
   3286    aPc->MediaFeatureValuesChanged(
   3287        {MediaFeatureChangeReason::SystemMetricsChange},
   3288        // We're already iterating through sub documents, so we don't need to
   3289        // propagate the change again.
   3290        MediaFeatureChangePropagation::JustThisDocument);
   3291  });
   3292 }
   3293 
   3294 void BrowsingContext::DidSet(FieldIndex<IDX_EmbedderColorSchemes>,
   3295                             EmbedderColorSchemes&& aOldValue) {
   3296  if (GetEmbedderColorSchemes() == aOldValue) {
   3297    return;
   3298  }
   3299  PresContextAffectingFieldChanged();
   3300 }
   3301 
   3302 void BrowsingContext::DidSet(FieldIndex<IDX_PrefersColorSchemeOverride>,
   3303                             dom::PrefersColorSchemeOverride aOldValue) {
   3304  MOZ_ASSERT(IsTop());
   3305  if (PrefersColorSchemeOverride() == aOldValue) {
   3306    return;
   3307  }
   3308  PresContextAffectingFieldChanged();
   3309 }
   3310 
   3311 void BrowsingContext::DidSet(FieldIndex<IDX_ForcedColorsOverride>,
   3312                             dom::ForcedColorsOverride aOldValue) {
   3313  MOZ_ASSERT(IsTop());
   3314  if (ForcedColorsOverride() == aOldValue) {
   3315    return;
   3316  }
   3317  PresContextAffectingFieldChanged();
   3318 }
   3319 
   3320 void BrowsingContext::DidSet(FieldIndex<IDX_LanguageOverride>,
   3321                             nsCString&& aOldValue) {
   3322  MOZ_ASSERT(IsTop());
   3323 
   3324  const nsCString& languageOverride = GetLanguageOverride();
   3325 
   3326  PreOrderWalk([&](BrowsingContext* aBrowsingContext) {
   3327    if (RefPtr<WindowContext> windowContext =
   3328            aBrowsingContext->GetCurrentWindowContext()) {
   3329      if (nsCOMPtr<nsPIDOMWindowInner> window =
   3330              windowContext->GetInnerWindow()) {
   3331        JSObject* global =
   3332            nsGlobalWindowInner::Cast(window)->GetGlobalJSObject();
   3333        JS::Realm* realm = JS::GetObjectRealmOrNull(global);
   3334 
   3335        if (mDefaultLocale == nullptr) {
   3336          AutoJSAPI jsapi;
   3337          if (jsapi.Init(window)) {
   3338            JSContext* context = jsapi.cx();
   3339            mDefaultLocale = JS_GetDefaultLocale(context);
   3340          }
   3341        }
   3342 
   3343        if (languageOverride.IsEmpty()) {
   3344          JS::SetRealmLocaleOverride(realm, mDefaultLocale.get());
   3345          mDefaultLocale = nullptr;
   3346        } else {
   3347          JS::SetRealmLocaleOverride(
   3348              realm, PromiseFlatCString(languageOverride).get());
   3349        }
   3350 
   3351        if (Navigator* navigator = window->Navigator()) {
   3352          navigator->ClearLanguageCache();
   3353        }
   3354      }
   3355    }
   3356  });
   3357 }
   3358 
   3359 void BrowsingContext::DidSet(FieldIndex<IDX_MediumOverride>,
   3360                             nsString&& aOldValue) {
   3361  MOZ_ASSERT(IsTop());
   3362  if (GetMediumOverride() == aOldValue) {
   3363    return;
   3364  }
   3365  PresContextAffectingFieldChanged();
   3366 }
   3367 
   3368 void BrowsingContext::DidSet(FieldIndex<IDX_DisplayMode>,
   3369                             enum DisplayMode aOldValue) {
   3370  MOZ_ASSERT(IsTop());
   3371 
   3372  if (GetDisplayMode() == aOldValue) {
   3373    return;
   3374  }
   3375 
   3376  WalkPresContexts([&](nsPresContext* aPc) {
   3377    aPc->MediaFeatureValuesChanged(
   3378        {MediaFeatureChangeReason::DisplayModeChange},
   3379        // We're already iterating through sub documents, so we don't need
   3380        // to propagate the change again.
   3381        //
   3382        // Images and other resources don't change their display-mode
   3383        // evaluation, display-mode is a property of the browsing context.
   3384        MediaFeatureChangePropagation::JustThisDocument);
   3385  });
   3386 }
   3387 
   3388 void BrowsingContext::DidSet(FieldIndex<IDX_Muted>) {
   3389  MOZ_ASSERT(IsTop(), "Set muted flag on non top-level context!");
   3390  USER_ACTIVATION_LOG("Set audio muted %d for %s browsing context 0x%08" PRIx64,
   3391                      GetMuted(), XRE_IsParentProcess() ? "Parent" : "Child",
   3392                      Id());
   3393  PreOrderWalk([&](BrowsingContext* aContext) {
   3394    nsPIDOMWindowOuter* win = aContext->GetDOMWindow();
   3395    if (win) {
   3396      win->RefreshMediaElementsVolume();
   3397    }
   3398  });
   3399 }
   3400 
   3401 bool BrowsingContext::CanSet(FieldIndex<IDX_IsAppTab>, const bool& aValue,
   3402                             ContentParent* aSource) {
   3403  return XRE_IsParentProcess() && !aSource && IsTop();
   3404 }
   3405 
   3406 bool BrowsingContext::CanSet(FieldIndex<IDX_HasSiblings>, const bool& aValue,
   3407                             ContentParent* aSource) {
   3408  return XRE_IsParentProcess() && !aSource && IsTop();
   3409 }
   3410 
   3411 bool BrowsingContext::CanSet(FieldIndex<IDX_ShouldDelayMediaFromStart>,
   3412                             const bool& aValue, ContentParent* aSource) {
   3413  return IsTop();
   3414 }
   3415 
   3416 void BrowsingContext::DidSet(FieldIndex<IDX_ShouldDelayMediaFromStart>,
   3417                             bool aOldValue) {
   3418  MOZ_ASSERT(IsTop(), "Set attribute on non top-level context!");
   3419  if (aOldValue == GetShouldDelayMediaFromStart()) {
   3420    return;
   3421  }
   3422  if (!GetShouldDelayMediaFromStart()) {
   3423    PreOrderWalk([&](BrowsingContext* aContext) {
   3424      if (nsPIDOMWindowOuter* win = aContext->GetDOMWindow()) {
   3425        win->ActivateMediaComponents();
   3426      }
   3427    });
   3428  }
   3429 }
   3430 
   3431 bool BrowsingContext::CanSet(FieldIndex<IDX_OverrideDPPX>, const float& aValue,
   3432                             ContentParent* aSource) {
   3433  return XRE_IsParentProcess() && !aSource && IsTop();
   3434 }
   3435 
   3436 void BrowsingContext::DidSet(FieldIndex<IDX_OverrideDPPX>, float aOldValue) {
   3437  MOZ_ASSERT(IsTop());
   3438  if (GetOverrideDPPX() == aOldValue) {
   3439    return;
   3440  }
   3441  PresContextAffectingFieldChanged();
   3442 }
   3443 
   3444 void BrowsingContext::SetCustomUserAgent(const nsAString& aUserAgent,
   3445                                         ErrorResult& aRv) {
   3446  Top()->SetUserAgentOverride(aUserAgent, aRv);
   3447 }
   3448 
   3449 nsresult BrowsingContext::SetCustomUserAgent(const nsAString& aUserAgent) {
   3450  return Top()->SetUserAgentOverride(aUserAgent);
   3451 }
   3452 
   3453 void BrowsingContext::DidSet(FieldIndex<IDX_UserAgentOverride>) {
   3454  MOZ_ASSERT(IsTop());
   3455 
   3456  PreOrderWalk([&](BrowsingContext* aContext) {
   3457    nsIDocShell* shell = aContext->GetDocShell();
   3458    if (shell) {
   3459      shell->ClearCachedUserAgent();
   3460    }
   3461 
   3462    if (nsCOMPtr<Document> doc = aContext->GetExtantDocument()) {
   3463      if (nsCOMPtr<nsIHttpChannel> httpChannel =
   3464              do_QueryInterface(doc->GetChannel())) {
   3465        (void)httpChannel->SetIsUserAgentHeaderOutdated(true);
   3466      }
   3467    }
   3468  });
   3469 }
   3470 
   3471 bool BrowsingContext::CanSet(FieldIndex<IDX_IsInBFCache>, bool,
   3472                             ContentParent* aSource) {
   3473  return IsTop() && !aSource && mozilla::BFCacheInParent();
   3474 }
   3475 
   3476 void BrowsingContext::DidSet(FieldIndex<IDX_IsInBFCache>) {
   3477  MOZ_RELEASE_ASSERT(mozilla::BFCacheInParent());
   3478  MOZ_DIAGNOSTIC_ASSERT(IsTop());
   3479 
   3480  const bool isInBFCache = GetIsInBFCache();
   3481  if (!isInBFCache) {
   3482    UpdateCurrentTopByBrowserId(this);
   3483    PreOrderWalk([&](BrowsingContext* aContext) {
   3484      aContext->mIsInBFCache = false;
   3485      nsCOMPtr<nsIDocShell> shell = aContext->GetDocShell();
   3486      if (shell) {
   3487        nsDocShell::Cast(shell)->ThawFreezeNonRecursive(true);
   3488      }
   3489    });
   3490  }
   3491 
   3492  if (isInBFCache && XRE_IsContentProcess() && mDocShell) {
   3493    nsDocShell::Cast(mDocShell)->MaybeDisconnectChildListenersOnPageHide();
   3494  }
   3495 
   3496  if (isInBFCache) {
   3497    PreOrderWalk([&](BrowsingContext* aContext) {
   3498      nsCOMPtr<nsIDocShell> shell = aContext->GetDocShell();
   3499      if (shell) {
   3500        nsDocShell::Cast(shell)->FirePageHideShowNonRecursive(false);
   3501      }
   3502    });
   3503  } else {
   3504    PostOrderWalk([&](BrowsingContext* aContext) {
   3505      nsCOMPtr<nsIDocShell> shell = aContext->GetDocShell();
   3506      if (shell) {
   3507        nsDocShell::Cast(shell)->FirePageHideShowNonRecursive(true);
   3508      }
   3509    });
   3510  }
   3511 
   3512  if (isInBFCache) {
   3513    PreOrderWalk([&](BrowsingContext* aContext) {
   3514      nsCOMPtr<nsIDocShell> shell = aContext->GetDocShell();
   3515      if (shell) {
   3516        nsDocShell::Cast(shell)->ThawFreezeNonRecursive(false);
   3517        if (nsPresContext* pc = shell->GetPresContext()) {
   3518          pc->EventStateManager()->ResetHoverState();
   3519        }
   3520      }
   3521      aContext->mIsInBFCache = true;
   3522      Document* doc = aContext->GetDocument();
   3523      if (doc) {
   3524        // Notifying needs to happen after mIsInBFCache is set to true.
   3525        doc->NotifyActivityChanged();
   3526      }
   3527    });
   3528 
   3529    if (XRE_IsParentProcess()) {
   3530      if (mCurrentWindowContext &&
   3531          mCurrentWindowContext->Canonical()->Fullscreen()) {
   3532        mCurrentWindowContext->Canonical()->ExitTopChromeDocumentFullscreen();
   3533      }
   3534    }
   3535  }
   3536 }
   3537 
   3538 void BrowsingContext::DidSet(FieldIndex<IDX_IsSyntheticDocumentContainer>) {
   3539  if (WindowContext* parentWindowContext = GetParentWindowContext()) {
   3540    parentWindowContext->UpdateChildSynthetic(
   3541        this, GetIsSyntheticDocumentContainer());
   3542  }
   3543 }
   3544 
   3545 void BrowsingContext::SetCustomPlatform(const nsAString& aPlatform,
   3546                                        ErrorResult& aRv) {
   3547  Top()->SetPlatformOverride(aPlatform, aRv);
   3548 }
   3549 
   3550 void BrowsingContext::DidSet(FieldIndex<IDX_PlatformOverride>) {
   3551  MOZ_ASSERT(IsTop());
   3552 
   3553  PreOrderWalk([&](BrowsingContext* aContext) {
   3554    nsIDocShell* shell = aContext->GetDocShell();
   3555    if (shell) {
   3556      shell->ClearCachedPlatform();
   3557    }
   3558  });
   3559 }
   3560 
   3561 auto BrowsingContext::LegacyRevertIfNotOwningOrParentProcess(
   3562    ContentParent* aSource) -> CanSetResult {
   3563  if (aSource) {
   3564    MOZ_ASSERT(XRE_IsParentProcess());
   3565 
   3566    if (!Canonical()->IsOwnedByProcess(aSource->ChildID())) {
   3567      return CanSetResult::Revert;
   3568    }
   3569  } else if (!IsInProcess() && !XRE_IsParentProcess()) {
   3570    // Don't allow this to be set from content processes that
   3571    // don't own the BrowsingContext.
   3572    return CanSetResult::Deny;
   3573  }
   3574 
   3575  return CanSetResult::Allow;
   3576 }
   3577 
   3578 bool BrowsingContext::CanSet(FieldIndex<IDX_IsActiveBrowserWindowInternal>,
   3579                             const bool& aValue, ContentParent* aSource) {
   3580  // Should only be set in the parent process.
   3581  return XRE_IsParentProcess() && !aSource && IsTop();
   3582 }
   3583 
   3584 void BrowsingContext::DidSet(FieldIndex<IDX_IsActiveBrowserWindowInternal>,
   3585                             bool aOldValue) {
   3586  bool isActivateEvent = GetIsActiveBrowserWindowInternal();
   3587  // The browser window containing this context has changed
   3588  // activation state so update window inactive document states
   3589  // for all in-process documents.
   3590  PreOrderWalk([isActivateEvent](BrowsingContext* aContext) {
   3591    if (RefPtr<Document> doc = aContext->GetExtantDocument()) {
   3592      doc->UpdateDocumentStates(DocumentState::WINDOW_INACTIVE, true);
   3593 
   3594      RefPtr<nsPIDOMWindowInner> win = doc->GetInnerWindow();
   3595      if (win) {
   3596        RefPtr<MediaDevices> devices;
   3597        if (isActivateEvent && (devices = win->GetExtantMediaDevices())) {
   3598          devices->BrowserWindowBecameActive();
   3599        }
   3600 
   3601        if (XRE_IsContentProcess() &&
   3602            (!aContext->GetParent() || !aContext->GetParent()->IsInProcess())) {
   3603          // Send the inner window an activate/deactivate event if
   3604          // the context is the top of a sub-tree of in-process
   3605          // contexts.
   3606          nsContentUtils::DispatchEventOnlyToChrome(
   3607              doc, nsGlobalWindowInner::Cast(win),
   3608              isActivateEvent ? u"activate"_ns : u"deactivate"_ns,
   3609              CanBubble::eYes, Cancelable::eYes, nullptr);
   3610        }
   3611      }
   3612    }
   3613  });
   3614 }
   3615 
   3616 bool BrowsingContext::CanSet(FieldIndex<IDX_OpenerPolicy>,
   3617                             nsILoadInfo::CrossOriginOpenerPolicy aPolicy,
   3618                             ContentParent* aSource) {
   3619  // A potentially cross-origin isolated BC can't change opener policy, nor can
   3620  // a BC become potentially cross-origin isolated. An unchanged policy is
   3621  // always OK.
   3622  return GetOpenerPolicy() == aPolicy ||
   3623         (GetOpenerPolicy() !=
   3624              nsILoadInfo::
   3625                  OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP &&
   3626          aPolicy !=
   3627              nsILoadInfo::
   3628                  OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP);
   3629 }
   3630 
   3631 auto BrowsingContext::CanSet(FieldIndex<IDX_AllowContentRetargeting>,
   3632                             const bool& aAllowContentRetargeting,
   3633                             ContentParent* aSource) -> CanSetResult {
   3634  return LegacyRevertIfNotOwningOrParentProcess(aSource);
   3635 }
   3636 
   3637 auto BrowsingContext::CanSet(FieldIndex<IDX_AllowContentRetargetingOnChildren>,
   3638                             const bool& aAllowContentRetargetingOnChildren,
   3639                             ContentParent* aSource) -> CanSetResult {
   3640  return LegacyRevertIfNotOwningOrParentProcess(aSource);
   3641 }
   3642 
   3643 bool BrowsingContext::CanSet(FieldIndex<IDX_FullscreenAllowedByOwner>,
   3644                             const bool& aAllowed, ContentParent* aSource) {
   3645  return CheckOnlyEmbedderCanSet(aSource);
   3646 }
   3647 
   3648 bool BrowsingContext::CanSet(FieldIndex<IDX_UseErrorPages>,
   3649                             const bool& aUseErrorPages,
   3650                             ContentParent* aSource) {
   3651  return CheckOnlyEmbedderCanSet(aSource);
   3652 }
   3653 
   3654 TouchEventsOverride BrowsingContext::TouchEventsOverride() const {
   3655  for (const auto* bc = this; bc; bc = bc->GetParent()) {
   3656    auto tev = bc->GetTouchEventsOverrideInternal();
   3657    if (tev != dom::TouchEventsOverride::None) {
   3658      return tev;
   3659    }
   3660  }
   3661  return dom::TouchEventsOverride::None;
   3662 }
   3663 
   3664 bool BrowsingContext::TargetTopLevelLinkClicksToBlank() const {
   3665  return Top()->GetTargetTopLevelLinkClicksToBlankInternal();
   3666 }
   3667 
   3668 // We map `watchedByDevTools` WebIDL attribute to `watchedByDevToolsInternal`
   3669 // BC field. And we map it to the top level BrowsingContext.
   3670 bool BrowsingContext::WatchedByDevTools() {
   3671  return Top()->GetWatchedByDevToolsInternal();
   3672 }
   3673 
   3674 // Enforce that the watchedByDevTools BC field can only be set on the top level
   3675 // Browsing Context.
   3676 bool BrowsingContext::CanSet(FieldIndex<IDX_WatchedByDevToolsInternal>,
   3677                             const bool& aWatchedByDevTools,
   3678                             ContentParent* aSource) {
   3679  return IsTop();
   3680 }
   3681 void BrowsingContext::SetWatchedByDevTools(bool aWatchedByDevTools,
   3682                                           ErrorResult& aRv) {
   3683  if (!IsTop()) {
   3684    aRv.ThrowInvalidModificationError(
   3685        "watchedByDevTools can only be set on top BrowsingContext");
   3686    return;
   3687  }
   3688  SetWatchedByDevToolsInternal(aWatchedByDevTools, aRv);
   3689 }
   3690 
   3691 RefPtr<nsGeolocationService> BrowsingContext::GetGeolocationServiceOverride() {
   3692  // Override can be set only to the top-level browsing context,
   3693  // but when the geolocation coordinates are requested for iframe,
   3694  // we should return the override which is set for its top-level context.
   3695  return Top()->mGeolocationServiceOverride;
   3696 }
   3697 
   3698 void BrowsingContext::SetGeolocationServiceOverride(
   3699    const Optional<nsIDOMGeoPosition*>& aGeolocationOverride) {
   3700  MOZ_ASSERT(
   3701      IsTop(),
   3702      "Should only set GeolocationServiceOverride in the top browsing context");
   3703  if (aGeolocationOverride.WasPassed()) {
   3704    if (!mGeolocationServiceOverride) {
   3705      mGeolocationServiceOverride = new nsGeolocationService();
   3706      mGeolocationServiceOverride->Init();
   3707    }
   3708    mGeolocationServiceOverride->Update(aGeolocationOverride.Value());
   3709  } else if (RefPtr<nsGeolocationService> serviceOverride =
   3710                 mGeolocationServiceOverride.forget()) {
   3711    // Create an original service and move the locators.
   3712    RefPtr<nsGeolocationService> service =
   3713        nsGeolocationService::GetGeolocationService();
   3714    serviceOverride->MoveLocators(service);
   3715  }
   3716 }
   3717 
   3718 void BrowsingContext::DidSet(FieldIndex<IDX_TimezoneOverride>,
   3719                             nsString&& aOldValue) {
   3720  MOZ_ASSERT(IsTop());
   3721 
   3722  PreOrderWalk([&](BrowsingContext* aBrowsingContext) {
   3723    if (RefPtr<WindowContext> windowContext =
   3724            aBrowsingContext->GetCurrentWindowContext()) {
   3725      if (nsCOMPtr<nsPIDOMWindowInner> window =
   3726              windowContext->GetInnerWindow()) {
   3727        JSObject* global =
   3728            nsGlobalWindowInner::Cast(window)->GetGlobalJSObject();
   3729        JS::Realm* realm = JS::GetObjectRealmOrNull(global);
   3730 
   3731        if (GetTimezoneOverride().IsEmpty()) {
   3732          JS::SetRealmTimezoneOverride(realm, nullptr);
   3733        } else {
   3734          JS::SetRealmTimezoneOverride(
   3735              realm, NS_ConvertUTF16toUTF8(GetTimezoneOverride()).get());
   3736        }
   3737      }
   3738    }
   3739  });
   3740 }
   3741 
   3742 auto BrowsingContext::CanSet(FieldIndex<IDX_DefaultLoadFlags>,
   3743                             const uint32_t& aDefaultLoadFlags,
   3744                             ContentParent* aSource) -> CanSetResult {
   3745  // Bug 1623565 - Are these flags only used by the debugger, which makes it
   3746  // possible that this field can only be settable by the parent process?
   3747  return LegacyRevertIfNotOwningOrParentProcess(aSource);
   3748 }
   3749 
   3750 void BrowsingContext::DidSet(FieldIndex<IDX_DefaultLoadFlags>) {
   3751  auto loadFlags = GetDefaultLoadFlags();
   3752  if (GetDocShell()) {
   3753    nsDocShell::Cast(GetDocShell())->SetLoadGroupDefaultLoadFlags(loadFlags);
   3754  }
   3755 
   3756  if (XRE_IsParentProcess()) {
   3757    PreOrderWalk([&](BrowsingContext* aContext) {
   3758      if (aContext != this) {
   3759        // Setting load flags on a discarded context has no effect.
   3760        (void)aContext->SetDefaultLoadFlags(loadFlags);
   3761      }
   3762    });
   3763  }
   3764 }
   3765 
   3766 bool BrowsingContext::CanSet(FieldIndex<IDX_UseGlobalHistory>,
   3767                             const bool& aUseGlobalHistory,
   3768                             ContentParent* aSource) {
   3769  // Should only be set in the parent process.
   3770  //  return XRE_IsParentProcess() && !aSource;
   3771  return true;
   3772 }
   3773 
   3774 auto BrowsingContext::CanSet(FieldIndex<IDX_UserAgentOverride>,
   3775                             const nsString& aUserAgent, ContentParent* aSource)
   3776    -> CanSetResult {
   3777  if (!IsTop()) {
   3778    return CanSetResult::Deny;
   3779  }
   3780 
   3781  return LegacyRevertIfNotOwningOrParentProcess(aSource);
   3782 }
   3783 
   3784 auto BrowsingContext::CanSet(FieldIndex<IDX_PlatformOverride>,
   3785                             const nsString& aPlatform, ContentParent* aSource)
   3786    -> CanSetResult {
   3787  if (!IsTop()) {
   3788    return CanSetResult::Deny;
   3789  }
   3790 
   3791  return LegacyRevertIfNotOwningOrParentProcess(aSource);
   3792 }
   3793 
   3794 bool BrowsingContext::CheckOnlyEmbedderCanSet(ContentParent* aSource) {
   3795  if (XRE_IsParentProcess()) {
   3796    uint64_t childId = aSource ? aSource->ChildID() : 0;
   3797    return Canonical()->IsEmbeddedInProcess(childId);
   3798  }
   3799  return mEmbeddedByThisProcess;
   3800 }
   3801 
   3802 bool BrowsingContext::CanSet(FieldIndex<IDX_EmbedderInnerWindowId>,
   3803                             const uint64_t& aValue, ContentParent* aSource) {
   3804  // If we have a parent window, our embedder inner window ID must match it.
   3805  if (mParentWindow) {
   3806    return mParentWindow->Id() == aValue;
   3807  }
   3808 
   3809  // For toplevel BrowsingContext instances, this value may only be set by the
   3810  // parent process, or initialized to `0`.
   3811  return CheckOnlyEmbedderCanSet(aSource);
   3812 }
   3813 
   3814 bool BrowsingContext::CanSet(FieldIndex<IDX_EmbedderElementType>,
   3815                             const Maybe<nsString>&, ContentParent* aSource) {
   3816  return CheckOnlyEmbedderCanSet(aSource);
   3817 }
   3818 
   3819 auto BrowsingContext::CanSet(FieldIndex<IDX_CurrentInnerWindowId>,
   3820                             const uint64_t& aValue, ContentParent* aSource)
   3821    -> CanSetResult {
   3822  // Generally allow clearing this. We may want to be more precise about this
   3823  // check in the future.
   3824  if (aValue == 0) {
   3825    return CanSetResult::Allow;
   3826  }
   3827 
   3828  // We must have access to the specified context.
   3829  RefPtr<WindowContext> window = WindowContext::GetById(aValue);
   3830  if (!window || window->GetBrowsingContext() != this) {
   3831    return CanSetResult::Deny;
   3832  }
   3833 
   3834  if (aSource) {
   3835    // If the sending process is no longer the current owner, revert
   3836    MOZ_ASSERT(XRE_IsParentProcess());
   3837    if (!Canonical()->IsOwnedByProcess(aSource->ChildID())) {
   3838      return CanSetResult::Revert;
   3839    }
   3840  } else if (XRE_IsContentProcess() && !IsOwnedByProcess()) {
   3841    return CanSetResult::Deny;
   3842  }
   3843 
   3844  return CanSetResult::Allow;
   3845 }
   3846 
   3847 bool BrowsingContext::CanSet(FieldIndex<IDX_ParentInitiatedNavigationEpoch>,
   3848                             const uint64_t& aValue, ContentParent* aSource) {
   3849  return XRE_IsParentProcess() && !aSource;
   3850 }
   3851 
   3852 void BrowsingContext::DidSet(FieldIndex<IDX_CurrentInnerWindowId>) {
   3853  RefPtr<WindowContext> prevWindowContext = mCurrentWindowContext.forget();
   3854  mCurrentWindowContext = WindowContext::GetById(GetCurrentInnerWindowId());
   3855  MOZ_ASSERT(
   3856      !mCurrentWindowContext || mWindowContexts.Contains(mCurrentWindowContext),
   3857      "WindowContext not registered?");
   3858 
   3859  // Clear our cached `children` value, to ensure that JS sees the up-to-date
   3860  // value.
   3861  BrowsingContext_Binding::ClearCachedChildrenValue(this);
   3862 
   3863  if (XRE_IsParentProcess()) {
   3864    if (prevWindowContext != mCurrentWindowContext) {
   3865      if (prevWindowContext) {
   3866        prevWindowContext->Canonical()->DidBecomeCurrentWindowGlobal(false);
   3867      }
   3868      if (mCurrentWindowContext) {
   3869        // We set a timer when we set the current inner window. This
   3870        // will then flush the session storage to session store to
   3871        // make sure that we don't miss to store session storage to
   3872        // session store that is a result of navigation. This is due
   3873        // to Bug 1700623. We wish to fix this in Bug 1711886, where
   3874        // making sure to store everything would make this timer
   3875        // unnecessary.
   3876        Canonical()->MaybeScheduleSessionStoreUpdate();
   3877        mCurrentWindowContext->Canonical()->DidBecomeCurrentWindowGlobal(true);
   3878      }
   3879    }
   3880    BrowserParent::UpdateFocusFromBrowsingContext();
   3881  }
   3882 }
   3883 
   3884 bool BrowsingContext::CanSet(FieldIndex<IDX_IsPopupSpam>, const bool& aValue,
   3885                             ContentParent* aSource) {
   3886  // Ensure that we only mark a browsing context as popup spam once and never
   3887  // unmark it.
   3888  return aValue && !GetIsPopupSpam();
   3889 }
   3890 
   3891 void BrowsingContext::DidSet(FieldIndex<IDX_IsPopupSpam>) {
   3892  if (GetIsPopupSpam()) {
   3893    PopupBlocker::RegisterOpenPopupSpam();
   3894  }
   3895 }
   3896 
   3897 bool BrowsingContext::CanSet(FieldIndex<IDX_MessageManagerGroup>,
   3898                             const nsString& aMessageManagerGroup,
   3899                             ContentParent* aSource) {
   3900  // Should only be set in the parent process on toplevel.
   3901  return XRE_IsParentProcess() && !aSource && IsTopContent();
   3902 }
   3903 
   3904 bool BrowsingContext::CanSet(
   3905    FieldIndex<IDX_OrientationLock>,
   3906    const mozilla::hal::ScreenOrientation& aOrientationLock,
   3907    ContentParent* aSource) {
   3908  return IsTop();
   3909 }
   3910 
   3911 bool BrowsingContext::IsLoading() {
   3912  if (GetLoading()) {
   3913    return true;
   3914  }
   3915 
   3916  // If we're in the same process as the page, we're possibly just
   3917  // updating the flag.
   3918  nsIDocShell* shell = GetDocShell();
   3919  if (shell) {
   3920    Document* doc = shell->GetDocument();
   3921    return doc && doc->GetReadyStateEnum() < Document::READYSTATE_COMPLETE;
   3922  }
   3923 
   3924  return false;
   3925 }
   3926 
   3927 void BrowsingContext::DidSet(FieldIndex<IDX_Loading>) {
   3928  if (mFields.Get<IDX_Loading>()) {
   3929    return;
   3930  }
   3931 
   3932  while (!mDeprioritizedLoadRunner.isEmpty()) {
   3933    nsCOMPtr<nsIRunnable> runner = mDeprioritizedLoadRunner.popFirst();
   3934    NS_DispatchToCurrentThread(runner.forget());
   3935  }
   3936 
   3937  if (IsTop()) {
   3938    Group()->FlushPostMessageEvents();
   3939  }
   3940 }
   3941 
   3942 // Inform the Document for this context of the (potential) change in
   3943 // loading state
   3944 void BrowsingContext::DidSet(FieldIndex<IDX_AncestorLoading>) {
   3945  nsPIDOMWindowOuter* outer = GetDOMWindow();
   3946  if (!outer) {
   3947    MOZ_LOG(gTimeoutDeferralLog, mozilla::LogLevel::Debug,
   3948            ("DidSetAncestorLoading BC: %p -- No outer window", (void*)this));
   3949    return;
   3950  }
   3951  Document* document = nsGlobalWindowOuter::Cast(outer)->GetExtantDoc();
   3952  if (document) {
   3953    MOZ_LOG(gTimeoutDeferralLog, mozilla::LogLevel::Debug,
   3954            ("DidSetAncestorLoading BC: %p -- NotifyLoading(%d, %d, %d)",
   3955             (void*)this, GetAncestorLoading(), document->GetReadyStateEnum(),
   3956             document->GetReadyStateEnum()));
   3957    document->NotifyLoading(GetAncestorLoading(), document->GetReadyStateEnum(),
   3958                            document->GetReadyStateEnum());
   3959  }
   3960 }
   3961 
   3962 void BrowsingContext::DidSet(FieldIndex<IDX_AuthorStyleDisabledDefault>) {
   3963  MOZ_ASSERT(IsTop(),
   3964             "Should only set AuthorStyleDisabledDefault in the top "
   3965             "browsing context");
   3966 
   3967  // We don't need to handle changes to this field, since PageStyleChild.sys.mjs
   3968  // will respond to the PageStyle:Disable message in all content processes.
   3969  //
   3970  // But we store the state here on the top BrowsingContext so that the
   3971  // docshell has somewhere to look for the current author style disabling
   3972  // state when new iframes are inserted.
   3973 }
   3974 
   3975 void BrowsingContext::DidSet(FieldIndex<IDX_TextZoom>, float aOldValue) {
   3976  if (GetTextZoom() == aOldValue) {
   3977    return;
   3978  }
   3979 
   3980  if (IsInProcess()) {
   3981    if (nsIDocShell* shell = GetDocShell()) {
   3982      if (nsPresContext* pc = shell->GetPresContext()) {
   3983        pc->RecomputeBrowsingContextDependentData();
   3984      }
   3985    }
   3986 
   3987    for (BrowsingContext* child : Children()) {
   3988      // Setting text zoom on a discarded context has no effect.
   3989      (void)child->SetTextZoom(GetTextZoom());
   3990    }
   3991  }
   3992 
   3993  if (IsTop() && XRE_IsParentProcess()) {
   3994    if (Element* element = GetEmbedderElement()) {
   3995      AsyncEventDispatcher::RunDOMEventWhenSafe(*element, u"TextZoomChange"_ns,
   3996                                                CanBubble::eYes,
   3997                                                ChromeOnlyDispatch::eYes);
   3998    }
   3999  }
   4000 }
   4001 
   4002 // TODO(emilio): It'd be potentially nicer and cheaper to allow to set this only
   4003 // on the Top() browsing context, but there are a lot of tests that rely on
   4004 // zooming a subframe so...
   4005 void BrowsingContext::DidSet(FieldIndex<IDX_FullZoom>, float aOldValue) {
   4006  if (GetFullZoom() == aOldValue) {
   4007    return;
   4008  }
   4009 
   4010  if (IsInProcess()) {
   4011    if (nsIDocShell* shell = GetDocShell()) {
   4012      if (nsPresContext* pc = shell->GetPresContext()) {
   4013        pc->RecomputeBrowsingContextDependentData();
   4014      }
   4015    }
   4016 
   4017    for (BrowsingContext* child : Children()) {
   4018      // When passing the outer document's full-zoom down to the inner
   4019      // document, scale by the effective CSS 'zoom' on the embedder element:
   4020      auto fullZoom = GetFullZoom();
   4021      if (auto* elem = child->GetEmbedderElement()) {
   4022        if (auto* frame = elem->GetPrimaryFrame()) {
   4023          fullZoom = frame->Style()->EffectiveZoom().Zoom(fullZoom);
   4024        }
   4025      }
   4026      // Setting full zoom on a discarded context has no effect.
   4027      (void)child->SetFullZoom(fullZoom);
   4028    }
   4029  }
   4030 
   4031  if (IsTop() && XRE_IsParentProcess()) {
   4032    if (Element* element = GetEmbedderElement()) {
   4033      AsyncEventDispatcher::RunDOMEventWhenSafe(*element, u"FullZoomChange"_ns,
   4034                                                CanBubble::eYes,
   4035                                                ChromeOnlyDispatch::eYes);
   4036    }
   4037  }
   4038 }
   4039 
   4040 void BrowsingContext::AddDeprioritizedLoadRunner(nsIRunnable* aRunner) {
   4041  MOZ_ASSERT(IsLoading());
   4042  MOZ_ASSERT(Top() == this);
   4043 
   4044  RefPtr<DeprioritizedLoadRunner> runner = new DeprioritizedLoadRunner(aRunner);
   4045  mDeprioritizedLoadRunner.insertBack(runner);
   4046  NS_DispatchToCurrentThreadQueue(runner.forget(), EventQueuePriority::Low);
   4047 }
   4048 
   4049 bool BrowsingContext::IsDynamic() const {
   4050  const BrowsingContext* current = this;
   4051  do {
   4052    if (current->CreatedDynamically()) {
   4053      return true;
   4054    }
   4055  } while ((current = current->GetParent()));
   4056 
   4057  return false;
   4058 }
   4059 
   4060 bool BrowsingContext::GetOffsetPath(nsTArray<uint32_t>& aPath) const {
   4061  for (const BrowsingContext* current = this; current && current->GetParent();
   4062       current = current->GetParent()) {
   4063    if (current->CreatedDynamically()) {
   4064      return false;
   4065    }
   4066    aPath.AppendElement(current->ChildOffset());
   4067  }
   4068  return true;
   4069 }
   4070 
   4071 void BrowsingContext::GetHistoryID(JSContext* aCx,
   4072                                   JS::MutableHandle<JS::Value> aVal,
   4073                                   ErrorResult& aError) {
   4074  if (!xpc::ID2JSValue(aCx, GetHistoryID(), aVal)) {
   4075    aError.Throw(NS_ERROR_OUT_OF_MEMORY);
   4076  }
   4077 }
   4078 
   4079 void BrowsingContext::InitSessionHistory() {
   4080  MOZ_ASSERT(!IsDiscarded());
   4081  MOZ_ASSERT(IsTop());
   4082  MOZ_ASSERT(EverAttached());
   4083 
   4084  if (!GetHasSessionHistory()) {
   4085    MOZ_ALWAYS_SUCCEEDS(SetHasSessionHistory(true));
   4086  }
   4087 }
   4088 
   4089 ChildSHistory* BrowsingContext::GetChildSessionHistory() {
   4090  if (!mozilla::SessionHistoryInParent()) {
   4091    // For now we're checking that the session history object for the child
   4092    // process is available before returning the ChildSHistory object, because
   4093    // it is the actual implementation that ChildSHistory forwards to. This can
   4094    // be removed once session history is stored exclusively in the parent
   4095    // process.
   4096    return mChildSessionHistory && mChildSessionHistory->IsInProcess()
   4097               ? mChildSessionHistory.get()
   4098               : nullptr;
   4099  }
   4100 
   4101  return mChildSessionHistory;
   4102 }
   4103 
   4104 void BrowsingContext::CreateChildSHistory() {
   4105  MOZ_ASSERT(IsTop());
   4106  MOZ_ASSERT(GetHasSessionHistory());
   4107  MOZ_ASSERT(!mChildSessionHistory);
   4108 
   4109  // Because session history is global in a browsing context tree, every process
   4110  // that has access to a browsing context tree needs access to its session
   4111  // history. That is why we create the ChildSHistory object in every process
   4112  // where we have access to this browsing context (which is the top one).
   4113  mChildSessionHistory = new ChildSHistory(this);
   4114 
   4115  // If the top browsing context (this one) is loaded in this process then we
   4116  // also create the session history implementation for the child process.
   4117  // This can be removed once session history is stored exclusively in the
   4118  // parent process.
   4119  mChildSessionHistory->SetIsInProcess(IsInProcess());
   4120 }
   4121 
   4122 void BrowsingContext::DidSet(FieldIndex<IDX_HasSessionHistory>,
   4123                             bool aOldValue) {
   4124  MOZ_ASSERT(GetHasSessionHistory() || !aOldValue,
   4125             "We don't support turning off session history.");
   4126 
   4127  if (GetHasSessionHistory() && !aOldValue) {
   4128    CreateChildSHistory();
   4129  }
   4130 }
   4131 
   4132 bool BrowsingContext::CanSet(
   4133    FieldIndex<IDX_TargetTopLevelLinkClicksToBlankInternal>,
   4134    const bool& aTargetTopLevelLinkClicksToBlankInternal,
   4135    ContentParent* aSource) {
   4136  return XRE_IsParentProcess() && !aSource && IsTop();
   4137 }
   4138 
   4139 bool BrowsingContext::CanSet(FieldIndex<IDX_BrowserId>, const uint32_t& aValue,
   4140                             ContentParent* aSource) {
   4141  // We should only be able to set this for toplevel contexts which don't have
   4142  // an ID yet.
   4143  return GetBrowserId() == 0 && IsTop() && Children().IsEmpty();
   4144 }
   4145 
   4146 bool BrowsingContext::CanSet(FieldIndex<IDX_PendingInitialization>,
   4147                             bool aNewValue, ContentParent* aSource) {
   4148  // Can only be cleared from `true` to `false`, and should only ever be set on
   4149  // the toplevel BrowsingContext.
   4150  return IsTop() && GetPendingInitialization() && !aNewValue;
   4151 }
   4152 
   4153 bool BrowsingContext::CanSet(FieldIndex<IDX_TopLevelCreatedByWebContent>,
   4154                             const bool& aNewValue, ContentParent* aSource) {
   4155  // Should only be set after creation in the parent process.
   4156  return XRE_IsParentProcess() && !aSource && IsTop();
   4157 }
   4158 
   4159 bool BrowsingContext::CanSet(FieldIndex<IDX_HasRestoreData>, bool aNewValue,
   4160                             ContentParent* aSource) {
   4161  return IsTop();
   4162 }
   4163 
   4164 bool BrowsingContext::CanSet(FieldIndex<IDX_IsUnderHiddenEmbedderElement>,
   4165                             const bool& aIsUnderHiddenEmbedderElement,
   4166                             ContentParent* aSource) {
   4167  return true;
   4168 }
   4169 
   4170 bool BrowsingContext::CanSet(FieldIndex<IDX_ForceOffline>, bool aNewValue,
   4171                             ContentParent* aSource) {
   4172  return XRE_IsParentProcess() && !aSource;
   4173 }
   4174 
   4175 void BrowsingContext::DidSet(FieldIndex<IDX_IsUnderHiddenEmbedderElement>,
   4176                             bool aOldValue) {
   4177  nsIDocShell* shell = GetDocShell();
   4178  if (!shell) {
   4179    return;
   4180  }
   4181  const bool newValue = IsUnderHiddenEmbedderElement();
   4182  if (NS_WARN_IF(aOldValue == newValue)) {
   4183    return;
   4184  }
   4185 
   4186  if (auto* bc = BrowserChild::GetFrom(shell)) {
   4187    bc->UpdateVisibility();
   4188  }
   4189 
   4190  if (PresShell* presShell = shell->GetPresShell()) {
   4191    presShell->SetIsUnderHiddenEmbedderElement(newValue);
   4192  }
   4193 
   4194  // Propagate to children.
   4195  auto PropagateToChild = [&newValue](BrowsingContext* aChild) {
   4196    Element* embedderElement = aChild->GetEmbedderElement();
   4197    if (!embedderElement) {
   4198      // TODO: We shouldn't need to null check here since `child` and the
   4199      // element returned by `child->GetEmbedderElement()` are in our
   4200      // process (the actual browsing context represented by `child` may not
   4201      // be, but that doesn't matter).  However, there are currently a very
   4202      // small number of crashes due to `embedderElement` being null, somehow
   4203      // - see bug 1551241.  For now we wallpaper the crash.
   4204      return CallState::Continue;
   4205    }
   4206 
   4207    bool embedderFrameIsHidden = true;
   4208    if (auto* embedderFrame = embedderElement->GetPrimaryFrame()) {
   4209      embedderFrameIsHidden = !embedderFrame->StyleVisibility()->IsVisible();
   4210    }
   4211 
   4212    bool hidden = newValue || embedderFrameIsHidden;
   4213    if (aChild->IsUnderHiddenEmbedderElement() != hidden) {
   4214      (void)aChild->SetIsUnderHiddenEmbedderElement(hidden);
   4215    }
   4216 
   4217    return CallState::Continue;
   4218  };
   4219 
   4220  for (BrowsingContext* child : Children()) {
   4221    PropagateToChild(child);
   4222  }
   4223 
   4224  if (XRE_IsParentProcess()) {
   4225    Canonical()->CallOnTopDescendants(
   4226        PropagateToChild,
   4227        CanonicalBrowsingContext::TopDescendantKind::ChildrenOnly);
   4228  }
   4229 }
   4230 
   4231 void BrowsingContext::DidSet(FieldIndex<IDX_ForceOffline>, bool aOldValue) {
   4232  const bool newValue = ForceOffline();
   4233  if (newValue == aOldValue) {
   4234    return;
   4235  }
   4236  PreOrderWalk([&](BrowsingContext* aBrowsingContext) {
   4237    if (RefPtr<WindowContext> windowContext =
   4238            aBrowsingContext->GetCurrentWindowContext()) {
   4239      if (nsCOMPtr<nsPIDOMWindowInner> window =
   4240              windowContext->GetInnerWindow()) {
   4241        nsGlobalWindowInner::Cast(window)->FireOfflineStatusEventIfChanged();
   4242      }
   4243    }
   4244  });
   4245 }
   4246 
   4247 bool BrowsingContext::IsPopupAllowed() {
   4248  for (auto* context = GetCurrentWindowContext(); context;
   4249       context = context->GetParentWindowContext()) {
   4250    if (context->CanShowPopup()) {
   4251      return true;
   4252    }
   4253  }
   4254 
   4255  return false;
   4256 }
   4257 
   4258 /* static */
   4259 bool BrowsingContext::ShouldAddEntryForRefresh(
   4260    nsIURI* aPreviousURI, const SessionHistoryInfo& aInfo) {
   4261  return ShouldAddEntryForRefresh(aPreviousURI, aInfo.GetURI(),
   4262                                  aInfo.HasPostData());
   4263 }
   4264 
   4265 /* static */
   4266 bool BrowsingContext::ShouldAddEntryForRefresh(nsIURI* aPreviousURI,
   4267                                               nsIURI* aNewURI,
   4268                                               bool aHasPostData) {
   4269  if (aHasPostData) {
   4270    return true;
   4271  }
   4272 
   4273  bool equalsURI = false;
   4274  if (aPreviousURI) {
   4275    aPreviousURI->Equals(aNewURI, &equalsURI);
   4276  }
   4277  return !equalsURI;
   4278 }
   4279 
   4280 bool BrowsingContext::AddSHEntryWouldIncreaseLength(
   4281    SessionHistoryInfo* aCurrentEntry) const {
   4282  // nsSHistory::AddEntry and AddNestedSHEntry do a replace load if the current
   4283  // entry is marked as transient.
   4284  const bool isCurrentTransientEntry =
   4285      aCurrentEntry && aCurrentEntry->IsTransient();
   4286 
   4287  // If this is the first entry for an iframe, it would be added to the parent
   4288  // entry instead of creating a new top-level entry.
   4289  const bool wouldAddToParentEntry = !IsTop() && !aCurrentEntry;
   4290 
   4291  return !isCurrentTransientEntry && !wouldAddToParentEntry;
   4292 }
   4293 
   4294 void BrowsingContext::SessionHistoryCommit(
   4295    const LoadingSessionHistoryInfo& aInfo, uint32_t aLoadType,
   4296    nsIURI* aPreviousURI, SessionHistoryInfo* aPreviousActiveEntry,
   4297    bool aCloneEntryChildren, bool aChannelExpired, uint32_t aCacheKey) {
   4298  nsID changeID = {};
   4299  if (XRE_IsContentProcess()) {
   4300    RefPtr<ChildSHistory> rootSH = Top()->GetChildSessionHistory();
   4301    if (rootSH) {
   4302      if (!aInfo.mLoadIsFromSessionHistory) {
   4303        // We try to mimic as closely as possible whether
   4304        // CanonicalBrowsingContext::SessionHistoryCommit will increase
   4305        // the session history length.
   4306        // It is possible that this leads to wrong length temporarily, but
   4307        // so would not having these checks.
   4308        // The child process does not have access to the current entry, so we
   4309        // use the previous active entry as the best approximation. When that's
   4310        // not the current entry then the length might be wrong briefly, until
   4311        // the parent process commits the actual length.
   4312        const bool isReplaceLoad = LOAD_TYPE_HAS_FLAGS(
   4313                       aLoadType, nsIWebNavigation::LOAD_FLAGS_REPLACE_HISTORY),
   4314                   isRefreshLoad = LOAD_TYPE_HAS_FLAGS(
   4315                       aLoadType, nsIWebNavigation::LOAD_FLAGS_IS_REFRESH);
   4316        if (!isReplaceLoad &&
   4317            AddSHEntryWouldIncreaseLength(aPreviousActiveEntry) &&
   4318            ShouldUpdateSessionHistory(aLoadType) &&
   4319            (!isRefreshLoad ||
   4320             ShouldAddEntryForRefresh(aPreviousURI, aInfo.mInfo))) {
   4321          changeID = rootSH->AddPendingHistoryChange();
   4322        }
   4323      } else {
   4324        // History load doesn't change the length, only index.
   4325        changeID = rootSH->AddPendingHistoryChange(aInfo.mOffset, 0);
   4326      }
   4327    }
   4328    ContentChild* cc = ContentChild::GetSingleton();
   4329    (void)cc->SendHistoryCommit(this, aInfo.mLoadId, changeID, aLoadType,
   4330                                aCloneEntryChildren, aChannelExpired,
   4331                                aCacheKey);
   4332  } else {
   4333    Canonical()->SessionHistoryCommit(aInfo.mLoadId, changeID, aLoadType,
   4334                                      aCloneEntryChildren, aChannelExpired,
   4335                                      aCacheKey);
   4336  }
   4337 }
   4338 
   4339 void BrowsingContext::SetActiveSessionHistoryEntry(
   4340    const Maybe<nsPoint>& aPreviousScrollPos, SessionHistoryInfo* aInfo,
   4341    SessionHistoryInfo* aPreviousActiveEntry, uint32_t aLoadType,
   4342    uint32_t aUpdatedCacheKey, bool aUpdateLength) {
   4343  if (IsTop() &&
   4344      !nsDocShell::ShouldAddToSessionHistory(aInfo->GetURI(), nullptr)) {
   4345    aInfo->SetTransient();
   4346  }
   4347  if (XRE_IsContentProcess()) {
   4348    // XXX Why we update cache key only in content process case?
   4349    if (aUpdatedCacheKey != 0) {
   4350      aInfo->SetCacheKey(aUpdatedCacheKey);
   4351    }
   4352 
   4353    nsID changeID = {};
   4354    if (aUpdateLength) {
   4355      RefPtr<ChildSHistory> shistory = Top()->GetChildSessionHistory();
   4356      if (shistory) {
   4357        // We try to mimic what will happen in
   4358        // CanonicalBrowsingContext::SendSetActiveSessionHistoryEntry
   4359        if (AddSHEntryWouldIncreaseLength(aPreviousActiveEntry)) {
   4360          changeID = shistory->AddPendingHistoryChange();
   4361        }
   4362      }
   4363    }
   4364    ContentChild::GetSingleton()->SendSetActiveSessionHistoryEntry(
   4365        this, aPreviousScrollPos, *aInfo, aLoadType, aUpdatedCacheKey,
   4366        changeID);
   4367  } else {
   4368    Canonical()->SetActiveSessionHistoryEntry(
   4369        aPreviousScrollPos, aInfo, aLoadType, aUpdatedCacheKey, nsID());
   4370  }
   4371 }
   4372 
   4373 void BrowsingContext::ReplaceActiveSessionHistoryEntry(
   4374    SessionHistoryInfo* aInfo) {
   4375  if (XRE_IsContentProcess()) {
   4376    ContentChild::GetSingleton()->SendReplaceActiveSessionHistoryEntry(this,
   4377                                                                       *aInfo);
   4378  } else {
   4379    Canonical()->ReplaceActiveSessionHistoryEntry(aInfo);
   4380  }
   4381 }
   4382 
   4383 void BrowsingContext::RemoveDynEntriesFromActiveSessionHistoryEntry() {
   4384  if (XRE_IsContentProcess()) {
   4385    ContentChild::GetSingleton()
   4386        ->SendRemoveDynEntriesFromActiveSessionHistoryEntry(this);
   4387  } else {
   4388    Canonical()->RemoveDynEntriesFromActiveSessionHistoryEntry();
   4389  }
   4390 }
   4391 
   4392 void BrowsingContext::RemoveFromSessionHistory(const nsID& aChangeID) {
   4393  if (XRE_IsContentProcess()) {
   4394    ContentChild::GetSingleton()->SendRemoveFromSessionHistory(this, aChangeID);
   4395  } else {
   4396    Canonical()->RemoveFromSessionHistory(aChangeID);
   4397  }
   4398 }
   4399 
   4400 void BrowsingContext::HistoryGo(
   4401    int32_t aOffset, uint64_t aHistoryEpoch, bool aRequireUserInteraction,
   4402    bool aUserActivation, std::function<void(Maybe<int32_t>&&)>&& aResolver) {
   4403  // We always pass checkForCancelation as true here, since we'll never call
   4404  // HistoryGo in a context where we beforehand know that we want to skip
   4405  // onbeforeunload or onnavigate.
   4406  bool checkForCancelation = true;
   4407  if (XRE_IsContentProcess()) {
   4408    ContentChild::GetSingleton()->SendHistoryGo(
   4409        this, aOffset, aHistoryEpoch, aRequireUserInteraction, aUserActivation,
   4410        checkForCancelation, std::move(aResolver),
   4411        [](mozilla::ipc::
   4412               ResponseRejectReason) { /* FIXME Is ignoring this fine? */ });
   4413  } else {
   4414    RefPtr<CanonicalBrowsingContext> self = Canonical();
   4415    aResolver(self->HistoryGo(aOffset, aHistoryEpoch, aRequireUserInteraction,
   4416                              aUserActivation, checkForCancelation,
   4417                              self->GetContentParent()
   4418                                  ? Some(self->GetContentParent()->ChildID())
   4419                                  : Nothing()));
   4420  }
   4421 }
   4422 
   4423 void BrowsingContext::NavigationTraverse(
   4424    const nsID& aKey, uint64_t aHistoryEpoch, bool aUserActivation,
   4425    bool aCheckForCancelation, std::function<void(nsresult)>&& aResolver) {
   4426  if (XRE_IsContentProcess()) {
   4427    ContentChild::GetSingleton()->SendNavigationTraverse(
   4428        this, aKey, aHistoryEpoch, aUserActivation, aCheckForCancelation,
   4429        std::move(aResolver),
   4430        [](mozilla::ipc::
   4431               ResponseRejectReason) { /* FIXME Is ignoring this fine? */ });
   4432  } else {
   4433    RefPtr<CanonicalBrowsingContext> self = Canonical();
   4434    self->NavigationTraverse(
   4435        aKey, aHistoryEpoch, aUserActivation, aCheckForCancelation,
   4436        self->GetContentParent() ? Some(self->GetContentParent()->ChildID())
   4437                                 : Nothing(),
   4438        std::move(aResolver));
   4439  }
   4440 }
   4441 
   4442 void BrowsingContext::SetChildSHistory(ChildSHistory* aChildSHistory) {
   4443  mChildSessionHistory = aChildSHistory;
   4444  mChildSessionHistory->SetBrowsingContext(this);
   4445  mFields.SetWithoutSyncing<IDX_HasSessionHistory>(true);
   4446 }
   4447 
   4448 bool BrowsingContext::ShouldUpdateSessionHistory(uint32_t aLoadType) {
   4449  // We don't update session history on reload unless we're loading
   4450  // an iframe in shift-reload case.
   4451  return nsDocShell::ShouldUpdateGlobalHistory(aLoadType) &&
   4452         (!(aLoadType & nsIDocShell::LOAD_CMD_RELOAD) ||
   4453          (IsForceReloadType(aLoadType) && IsSubframe()));
   4454 }
   4455 
   4456 nsresult BrowsingContext::CheckNavigationRateLimit(CallerType aCallerType) {
   4457  // We only rate limit non system callers
   4458  if (aCallerType == CallerType::System) {
   4459    return NS_OK;
   4460  }
   4461 
   4462  // Fetch rate limiting preferences
   4463  uint32_t limitCount = StaticPrefs::dom_navigation_navigationRateLimit_count();
   4464  uint32_t timeSpanSeconds =
   4465      StaticPrefs::dom_navigation_navigationRateLimit_timespan();
   4466 
   4467  // Disable throttling if either of the preferences is set to 0.
   4468  if (limitCount == 0 || timeSpanSeconds == 0) {
   4469    return NS_OK;
   4470  }
   4471 
   4472  TimeDuration throttleSpan = TimeDuration::FromSeconds(timeSpanSeconds);
   4473 
   4474  if (mNavigationRateLimitSpanStart.IsNull() ||
   4475      ((TimeStamp::Now() - mNavigationRateLimitSpanStart) > throttleSpan)) {
   4476    // Initial call or timespan exceeded, reset counter and timespan.
   4477    mNavigationRateLimitSpanStart = TimeStamp::Now();
   4478    mNavigationRateLimitCount = 1;
   4479    return NS_OK;
   4480  }
   4481 
   4482  if (mNavigationRateLimitCount >= limitCount) {
   4483    // Rate limit reached
   4484 
   4485    Document* doc = GetDocument();
   4486    if (doc) {
   4487      nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, "DOM"_ns, doc,
   4488                                      nsContentUtils::eDOM_PROPERTIES,
   4489                                      "LocChangeFloodingPrevented");
   4490    }
   4491 
   4492    return NS_ERROR_DOM_SECURITY_ERR;
   4493  }
   4494 
   4495  mNavigationRateLimitCount++;
   4496  return NS_OK;
   4497 }
   4498 
   4499 void BrowsingContext::ResetNavigationRateLimit() {
   4500  // Resetting the timestamp object will cause the check function to
   4501  // init again and reset the rate limit.
   4502  mNavigationRateLimitSpanStart = TimeStamp();
   4503 }
   4504 
   4505 void BrowsingContext::LocationCreated(dom::Location* aLocation) {
   4506  MOZ_ASSERT(!aLocation->isInList());
   4507  mLocations.insertBack(aLocation);
   4508 }
   4509 
   4510 void BrowsingContext::ClearCachedValuesOfLocations() {
   4511  for (dom::Location* loc = mLocations.getFirst(); loc; loc = loc->getNext()) {
   4512    loc->ClearCachedValues();
   4513  }
   4514 }
   4515 
   4516 // https://html.spec.whatwg.org/multipage/interaction.html#consume-history-action-user-activation
   4517 // Step 3 onward
   4518 void BrowsingContext::ConsumeHistoryActivation() {
   4519  // 3. Let navigables be the inclusive descendant navigables of top's active
   4520  // document.
   4521  // 4. Let windows be the list of Window objects constructed by taking the
   4522  // active window of each item in navigables.
   4523  PreOrderWalk([&](BrowsingContext* aBrowsingContext) {
   4524    RefPtr<WindowContext> windowContext =
   4525        aBrowsingContext->GetCurrentWindowContext();
   4526    // 5. For each window in windows, set window's last history-action
   4527    // activation timestamp to window's last activation timestamp.
   4528    if (aBrowsingContext->IsInProcess() && windowContext &&
   4529        windowContext->GetUserActivationState() ==
   4530            UserActivation::State::FullActivated) {
   4531      windowContext->UpdateLastHistoryActivation();
   4532    }
   4533  });
   4534 }
   4535 
   4536 void BrowsingContext::SynchronizeNavigationAPIState(
   4537    nsIStructuredCloneContainer* aState) {
   4538  if (!aState) {
   4539    return;
   4540  }
   4541 
   4542  if (XRE_IsContentProcess()) {
   4543    ClonedMessageData data;
   4544    DebugOnly<bool> result = static_cast<nsStructuredCloneContainer*>(aState)
   4545                                 ->BuildClonedMessageData(data);
   4546    MOZ_ASSERT(result);
   4547 
   4548    MOZ_ASSERT(ContentChild::GetSingleton());
   4549    ContentChild::GetSingleton()->SendSynchronizeNavigationAPIState(this, data);
   4550  } else {
   4551    Canonical()->SynchronizeNavigationAPIState(aState);
   4552  }
   4553 }
   4554 
   4555 }  // namespace dom
   4556 }  // namespace mozilla
   4557 
   4558 namespace IPC {
   4559 
   4560 using mozilla::dom::BrowsingContext;
   4561 using mozilla::dom::MaybeDiscarded;
   4562 
   4563 void ParamTraits<MaybeDiscarded<BrowsingContext>>::Write(
   4564    IPC::MessageWriter* aWriter,
   4565    const MaybeDiscarded<BrowsingContext>& aParam) {
   4566  MOZ_DIAGNOSTIC_ASSERT(!aParam.GetMaybeDiscarded() ||
   4567                        aParam.GetMaybeDiscarded()->EverAttached());
   4568  uint64_t id = aParam.ContextId();
   4569  WriteParam(aWriter, id);
   4570 }
   4571 
   4572 bool ParamTraits<MaybeDiscarded<BrowsingContext>>::Read(
   4573    IPC::MessageReader* aReader, MaybeDiscarded<BrowsingContext>* aResult) {
   4574  uint64_t id = 0;
   4575  if (!ReadParam(aReader, &id)) {
   4576    return false;
   4577  }
   4578 
   4579  if (id == 0) {
   4580    *aResult = nullptr;
   4581  } else if (RefPtr<BrowsingContext> bc = BrowsingContext::Get(id)) {
   4582    *aResult = std::move(bc);
   4583  } else {
   4584    aResult->SetDiscarded(id);
   4585  }
   4586  return true;
   4587 }
   4588 
   4589 void ParamTraits<BrowsingContext::IPCInitializer>::Write(
   4590    IPC::MessageWriter* aWriter, const paramType& aInit) {
   4591  // Write actor ID parameters.
   4592  WriteParam(aWriter, aInit.mId);
   4593  WriteParam(aWriter, aInit.mParentId);
   4594  WriteParam(aWriter, aInit.mWindowless);
   4595  WriteParam(aWriter, aInit.mUseRemoteTabs);
   4596  WriteParam(aWriter, aInit.mUseRemoteSubframes);
   4597  WriteParam(aWriter, aInit.mCreatedDynamically);
   4598  WriteParam(aWriter, aInit.mChildOffset);
   4599  WriteParam(aWriter, aInit.mOriginAttributes);
   4600  WriteParam(aWriter, aInit.mRequestContextId);
   4601  WriteParam(aWriter, aInit.mSessionHistoryIndex);
   4602  WriteParam(aWriter, aInit.mSessionHistoryCount);
   4603  WriteParam(aWriter, aInit.mFields);
   4604 }
   4605 
   4606 bool ParamTraits<BrowsingContext::IPCInitializer>::Read(
   4607    IPC::MessageReader* aReader, paramType* aInit) {
   4608  // Read actor ID parameters.
   4609  return ReadParam(aReader, &aInit->mId) &&
   4610         ReadParam(aReader, &aInit->mParentId) &&
   4611         ReadParam(aReader, &aInit->mWindowless) &&
   4612         ReadParam(aReader, &aInit->mUseRemoteTabs) &&
   4613         ReadParam(aReader, &aInit->mUseRemoteSubframes) &&
   4614         ReadParam(aReader, &aInit->mCreatedDynamically) &&
   4615         ReadParam(aReader, &aInit->mChildOffset) &&
   4616         ReadParam(aReader, &aInit->mOriginAttributes) &&
   4617         ReadParam(aReader, &aInit->mRequestContextId) &&
   4618         ReadParam(aReader, &aInit->mSessionHistoryIndex) &&
   4619         ReadParam(aReader, &aInit->mSessionHistoryCount) &&
   4620         ReadParam(aReader, &aInit->mFields);
   4621 }
   4622 
   4623 template struct ParamTraits<BrowsingContext::BaseTransaction>;
   4624 
   4625 }  // namespace IPC