tor-browser

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

CanonicalBrowsingContext.cpp (140608B)


      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/CanonicalBrowsingContext.h"
      8 
      9 #include "ContentAnalysis.h"
     10 #include "ErrorList.h"
     11 #include "mozilla/CheckedInt.h"
     12 #include "mozilla/Components.h"
     13 #include "mozilla/ErrorResult.h"
     14 #include "mozilla/EventForwards.h"
     15 #include "mozilla/AsyncEventDispatcher.h"
     16 #include "mozilla/dom/BrowserParent.h"
     17 #include "mozilla/dom/BrowsingContextBinding.h"
     18 #include "mozilla/dom/BrowsingContextGroup.h"
     19 #include "mozilla/dom/ContentParent.h"
     20 #include "mozilla/dom/EventTarget.h"
     21 #include "mozilla/dom/Navigation.h"
     22 #include "mozilla/dom/NavigationUtils.h"
     23 #include "mozilla/dom/PBrowserParent.h"
     24 #include "mozilla/dom/PBackgroundSessionStorageCache.h"
     25 #include "mozilla/dom/PWindowGlobalParent.h"
     26 #include "mozilla/dom/Promise.h"
     27 #include "mozilla/dom/Promise-inl.h"
     28 #include "mozilla/dom/WindowGlobalParent.h"
     29 #include "mozilla/dom/ContentProcessManager.h"
     30 #include "mozilla/dom/MediaController.h"
     31 #include "mozilla/dom/MediaControlService.h"
     32 #include "mozilla/dom/ContentPlaybackController.h"
     33 #include "mozilla/dom/SessionStorageManager.h"
     34 #include "mozilla/ipc/ProtocolUtils.h"
     35 #include "mozilla/layers/CompositorBridgeChild.h"
     36 #ifdef NS_PRINTING
     37 #  include "mozilla/layout/RemotePrintJobParent.h"
     38 #endif
     39 #include "mozilla/net/DocumentLoadListener.h"
     40 #include "mozilla/NullPrincipal.h"
     41 #include "mozilla/StaticPrefs_browser.h"
     42 #include "mozilla/StaticPrefs_docshell.h"
     43 #include "mozilla/StaticPrefs_fission.h"
     44 #include "mozilla/StaticPrefs_security.h"
     45 #include "mozilla/glean/DomMetrics.h"
     46 #include "nsILayoutHistoryState.h"
     47 #include "nsIParentalControlsService.h"
     48 #include "nsIPrintSettings.h"
     49 #include "nsIPrintSettingsService.h"
     50 #include "nsISupports.h"
     51 #include "nsIWebNavigation.h"
     52 #include "nsDocShell.h"
     53 #include "nsFrameLoader.h"
     54 #include "nsFrameLoaderOwner.h"
     55 #include "nsGlobalWindowOuter.h"
     56 #include "nsIContentAnalysis.h"
     57 #include "nsIWebBrowserChrome.h"
     58 #include "nsIXULRuntime.h"
     59 #include "nsNetUtil.h"
     60 #include "nsSHistory.h"
     61 #include "nsSecureBrowserUI.h"
     62 #include "nsQueryObject.h"
     63 #include "nsBrowserStatusFilter.h"
     64 #include "nsIBrowser.h"
     65 #include "nsTHashSet.h"
     66 #include "nsISessionStoreFunctions.h"
     67 #include "nsIXPConnect.h"
     68 #include "nsImportModule.h"
     69 #include "UnitTransforms.h"
     70 #include "nsIOpenWindowInfo.h"
     71 #include "nsOpenWindowInfo.h"
     72 
     73 using namespace mozilla::ipc;
     74 
     75 extern mozilla::LazyLogModule gAutoplayPermissionLog;
     76 extern mozilla::LazyLogModule gNavigationAPILog;
     77 extern mozilla::LazyLogModule gSHLog;
     78 extern mozilla::LazyLogModule gSHIPBFCacheLog;
     79 
     80 #define AUTOPLAY_LOG(msg, ...) \
     81  MOZ_LOG(gAutoplayPermissionLog, LogLevel::Debug, (msg, ##__VA_ARGS__))
     82 
     83 static mozilla::LazyLogModule sPBContext("PBContext");
     84 
     85 // Global count of canonical browsing contexts with the private attribute set
     86 static uint32_t gNumberOfPrivateContexts = 0;
     87 
     88 static void IncreasePrivateCount() {
     89  gNumberOfPrivateContexts++;
     90  MOZ_LOG(sPBContext, mozilla::LogLevel::Debug,
     91          ("%s: Private browsing context count %d -> %d", __func__,
     92           gNumberOfPrivateContexts - 1, gNumberOfPrivateContexts));
     93  if (gNumberOfPrivateContexts > 1) {
     94    return;
     95  }
     96 
     97  static bool sHasSeenPrivateContext = false;
     98  if (!sHasSeenPrivateContext) {
     99    sHasSeenPrivateContext = true;
    100    mozilla::glean::dom_parentprocess::private_window_used.Set(true);
    101  }
    102 }
    103 
    104 static void DecreasePrivateCount() {
    105  MOZ_ASSERT(gNumberOfPrivateContexts > 0);
    106  gNumberOfPrivateContexts--;
    107 
    108  MOZ_LOG(sPBContext, mozilla::LogLevel::Debug,
    109          ("%s: Private browsing context count %d -> %d", __func__,
    110           gNumberOfPrivateContexts + 1, gNumberOfPrivateContexts));
    111  if (!gNumberOfPrivateContexts &&
    112      !mozilla::StaticPrefs::browser_privatebrowsing_autostart()) {
    113    nsCOMPtr<nsIObserverService> observerService =
    114        mozilla::services::GetObserverService();
    115    if (observerService) {
    116      MOZ_LOG(sPBContext, mozilla::LogLevel::Debug,
    117              ("%s: last-pb-context-exited fired", __func__));
    118      observerService->NotifyObservers(nullptr, "last-pb-context-exited",
    119                                       nullptr);
    120    }
    121  }
    122 }
    123 
    124 namespace mozilla::dom {
    125 
    126 extern mozilla::LazyLogModule gUserInteractionPRLog;
    127 
    128 #define USER_ACTIVATION_LOG(msg, ...) \
    129  MOZ_LOG(gUserInteractionPRLog, LogLevel::Debug, (msg, ##__VA_ARGS__))
    130 
    131 CanonicalBrowsingContext::CanonicalBrowsingContext(WindowContext* aParentWindow,
    132                                                   BrowsingContextGroup* aGroup,
    133                                                   uint64_t aBrowsingContextId,
    134                                                   uint64_t aOwnerProcessId,
    135                                                   uint64_t aEmbedderProcessId,
    136                                                   BrowsingContext::Type aType,
    137                                                   FieldValues&& aInit)
    138    : BrowsingContext(aParentWindow, aGroup, aBrowsingContextId, aType,
    139                      std::move(aInit)),
    140      mProcessId(aOwnerProcessId),
    141      mEmbedderProcessId(aEmbedderProcessId),
    142      mPermanentKey(JS::NullValue()) {
    143  // You are only ever allowed to create CanonicalBrowsingContexts in the
    144  // parent process.
    145  MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
    146 
    147  // The initial URI in a BrowsingContext is always "about:blank".
    148  MOZ_ALWAYS_SUCCEEDS(
    149      NS_NewURI(getter_AddRefs(mCurrentRemoteURI), "about:blank"));
    150 
    151  mozilla::HoldJSObjects(this);
    152 }
    153 
    154 CanonicalBrowsingContext::~CanonicalBrowsingContext() {
    155  mPermanentKey.setNull();
    156 
    157  mozilla::DropJSObjects(this);
    158 
    159  if (mSessionHistory) {
    160    mSessionHistory->SetBrowsingContext(nullptr);
    161  }
    162 
    163  mActiveEntryList = nullptr;
    164 }
    165 
    166 /* static */
    167 already_AddRefed<CanonicalBrowsingContext> CanonicalBrowsingContext::Get(
    168    uint64_t aId) {
    169  MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
    170  return BrowsingContext::Get(aId).downcast<CanonicalBrowsingContext>();
    171 }
    172 
    173 /* static */
    174 CanonicalBrowsingContext* CanonicalBrowsingContext::Cast(
    175    BrowsingContext* aContext) {
    176  MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
    177  return static_cast<CanonicalBrowsingContext*>(aContext);
    178 }
    179 
    180 /* static */
    181 const CanonicalBrowsingContext* CanonicalBrowsingContext::Cast(
    182    const BrowsingContext* aContext) {
    183  MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
    184  return static_cast<const CanonicalBrowsingContext*>(aContext);
    185 }
    186 
    187 already_AddRefed<CanonicalBrowsingContext> CanonicalBrowsingContext::Cast(
    188    already_AddRefed<BrowsingContext>&& aContext) {
    189  MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
    190  return aContext.downcast<CanonicalBrowsingContext>();
    191 }
    192 
    193 ContentParent* CanonicalBrowsingContext::GetContentParent() const {
    194  if (mProcessId == 0) {
    195    return nullptr;
    196  }
    197 
    198  ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
    199  if (!cpm) {
    200    return nullptr;
    201  }
    202  return cpm->GetContentProcessById(ContentParentId(mProcessId));
    203 }
    204 
    205 void CanonicalBrowsingContext::GetCurrentRemoteType(nsACString& aRemoteType,
    206                                                    ErrorResult& aRv) const {
    207  // If we're in the parent process, dump out the void string.
    208  if (mProcessId == 0) {
    209    aRemoteType = NOT_REMOTE_TYPE;
    210    return;
    211  }
    212 
    213  ContentParent* cp = GetContentParent();
    214  if (!cp) {
    215    aRv.Throw(NS_ERROR_UNEXPECTED);
    216    return;
    217  }
    218 
    219  aRemoteType = cp->GetRemoteType();
    220 }
    221 
    222 void CanonicalBrowsingContext::SetOwnerProcessId(uint64_t aProcessId) {
    223  MOZ_LOG(GetLog(), LogLevel::Debug,
    224          ("SetOwnerProcessId for 0x%08" PRIx64 " (0x%08" PRIx64
    225           " -> 0x%08" PRIx64 ")",
    226           Id(), mProcessId, aProcessId));
    227 
    228  mProcessId = aProcessId;
    229 }
    230 
    231 nsISecureBrowserUI* CanonicalBrowsingContext::GetSecureBrowserUI() {
    232  if (!IsTop()) {
    233    return nullptr;
    234  }
    235  if (!mSecureBrowserUI) {
    236    mSecureBrowserUI = new nsSecureBrowserUI(this);
    237  }
    238  return mSecureBrowserUI;
    239 }
    240 
    241 void CanonicalBrowsingContext::ReplacedBy(
    242    CanonicalBrowsingContext* aNewContext,
    243    const NavigationIsolationOptions& aRemotenessOptions) {
    244  MOZ_ASSERT(!aNewContext->mWebProgress);
    245  MOZ_ASSERT(!aNewContext->mSessionHistory);
    246  MOZ_ASSERT(IsTop() && aNewContext->IsTop());
    247 
    248  mIsReplaced = true;
    249  aNewContext->mIsReplaced = false;
    250 
    251  if (mStatusFilter) {
    252    mStatusFilter->RemoveProgressListener(mDocShellProgressBridge);
    253    mStatusFilter = nullptr;
    254  }
    255 
    256  mWebProgress->ContextReplaced(aNewContext);
    257  aNewContext->mWebProgress = std::move(mWebProgress);
    258 
    259  // Use the Transaction for the fields which need to be updated whether or not
    260  // the new context has been attached before.
    261  // SetWithoutSyncing can be used if context hasn't been attached.
    262  Transaction txn;
    263  txn.SetBrowserId(GetBrowserId());
    264  txn.SetIsAppTab(GetIsAppTab());
    265  txn.SetIsCaptivePortalTab(GetIsCaptivePortalTab());
    266  txn.SetHasSiblings(GetHasSiblings());
    267  txn.SetTopLevelCreatedByWebContent(GetTopLevelCreatedByWebContent());
    268  txn.SetHistoryID(GetHistoryID());
    269  txn.SetExplicitActive(GetExplicitActive());
    270  txn.SetEmbedderColorSchemes(GetEmbedderColorSchemes());
    271  txn.SetHasRestoreData(GetHasRestoreData());
    272  txn.SetShouldDelayMediaFromStart(GetShouldDelayMediaFromStart());
    273  txn.SetForceOffline(GetForceOffline());
    274  txn.SetTopInnerSizeForRFP(GetTopInnerSizeForRFP());
    275  txn.SetIPAddressSpace(GetIPAddressSpace());
    276  txn.SetParentalControlsEnabled(GetParentalControlsEnabled());
    277 
    278  if (!GetLanguageOverride().IsEmpty()) {
    279    // Reapply language override to update the corresponding realm.
    280    txn.SetLanguageOverride(GetLanguageOverride());
    281  }
    282  if (!GetTimezoneOverride().IsEmpty()) {
    283    // Reapply timezone override to update the corresponding realm.
    284    txn.SetTimezoneOverride(GetTimezoneOverride());
    285  }
    286 
    287  // Propagate some settings on BrowsingContext replacement so they're not lost
    288  // on bfcached navigations. These are important for GeckoView (see bug
    289  // 1781936).
    290  txn.SetAllowJavascript(GetAllowJavascript());
    291  txn.SetForceEnableTrackingProtection(GetForceEnableTrackingProtection());
    292  txn.SetUserAgentOverride(GetUserAgentOverride());
    293  txn.SetSuspendMediaWhenInactive(GetSuspendMediaWhenInactive());
    294  txn.SetDisplayMode(GetDisplayMode());
    295  txn.SetForceDesktopViewport(GetForceDesktopViewport());
    296  txn.SetIsUnderHiddenEmbedderElement(GetIsUnderHiddenEmbedderElement());
    297 
    298  // When using site-specific zoom, we let the frontend manage the zoom level
    299  // of BFCache'd contexts. Overriding those zoom levels can cause weirdness
    300  // like bug 1846141. We always copy to new contexts to avoid bug 1914149.
    301  if (!aNewContext->EverAttached() ||
    302      !StaticPrefs::browser_zoom_siteSpecific()) {
    303    txn.SetFullZoom(GetFullZoom());
    304    txn.SetTextZoom(GetTextZoom());
    305  }
    306 
    307  // Propagate the default load flags so that the TRR mode flags are forwarded
    308  // to the new browsing context. See bug 1828643.
    309  txn.SetDefaultLoadFlags(GetDefaultLoadFlags());
    310 
    311  // As this is a different BrowsingContext, set InitialSandboxFlags to the
    312  // current flags in the new context so that they also apply to any initial
    313  // about:blank documents created in it.
    314  txn.SetSandboxFlags(GetSandboxFlags());
    315  txn.SetInitialSandboxFlags(GetSandboxFlags());
    316  txn.SetTargetTopLevelLinkClicksToBlankInternal(
    317      TargetTopLevelLinkClicksToBlank());
    318  if (aNewContext->EverAttached()) {
    319    MOZ_ALWAYS_SUCCEEDS(txn.Commit(aNewContext));
    320  } else {
    321    txn.CommitWithoutSyncing(aNewContext);
    322  }
    323 
    324  aNewContext->mRestoreState = mRestoreState.forget();
    325  Transaction selfTxn;
    326  selfTxn.SetHasRestoreData(false);
    327  selfTxn.SetExplicitActive(ExplicitActiveStatus::Inactive);
    328  MOZ_ALWAYS_SUCCEEDS(selfTxn.Commit(this));
    329 
    330  // XXXBFCache name handling is still a bit broken in Fission in general,
    331  // at least in case name should be cleared.
    332  if (aRemotenessOptions.mTryUseBFCache) {
    333    MOZ_ASSERT(!aNewContext->EverAttached());
    334    aNewContext->mFields.SetWithoutSyncing<IDX_Name>(GetName());
    335    // We don't copy over HasLoadedNonInitialDocument here, we'll actually end
    336    // up loading a new initial document at this point, before the real load.
    337    // The real load will then end up setting HasLoadedNonInitialDocument to
    338    // true.
    339  }
    340 
    341  if (mSessionHistory) {
    342    mSessionHistory->SetBrowsingContext(aNewContext);
    343    // At this point we will be creating a new ChildSHistory in the child.
    344    // That means that the child's epoch will be reset, so it makes sense to
    345    // reset the epoch in the parent too.
    346    mSessionHistory->SetEpoch(0, Nothing());
    347    mSessionHistory.swap(aNewContext->mSessionHistory);
    348    RefPtr<ChildSHistory> childSHistory = ForgetChildSHistory();
    349    aNewContext->SetChildSHistory(childSHistory);
    350  }
    351 
    352  BackgroundSessionStorageManager::PropagateManager(Id(), aNewContext->Id());
    353 
    354  // Transfer the ownership of the priority active status from the old context
    355  // to the new context.
    356  aNewContext->mPriorityActive = mPriorityActive;
    357  mPriorityActive = false;
    358 
    359  MOZ_ASSERT(aNewContext->mLoadingEntries.IsEmpty());
    360  mLoadingEntries.SwapElements(aNewContext->mLoadingEntries);
    361  MOZ_ASSERT(!aNewContext->mActiveEntry);
    362  mActiveEntry.swap(aNewContext->mActiveEntry);
    363  if (Navigation::IsAPIEnabled()) {
    364    MOZ_ASSERT(!aNewContext->mActiveEntryList);
    365    aNewContext->mActiveEntryList = std::move(mActiveEntryList);
    366  }
    367 
    368  aNewContext->mPermanentKey = mPermanentKey;
    369  mPermanentKey.setNull();
    370 }
    371 
    372 void CanonicalBrowsingContext::UpdateSecurityState() {
    373  if (mSecureBrowserUI) {
    374    mSecureBrowserUI->RecomputeSecurityFlags();
    375  }
    376 }
    377 
    378 void CanonicalBrowsingContext::GetWindowGlobals(
    379    nsTArray<RefPtr<WindowGlobalParent>>& aWindows) {
    380  aWindows.SetCapacity(GetWindowContexts().Length());
    381  for (auto& window : GetWindowContexts()) {
    382    aWindows.AppendElement(static_cast<WindowGlobalParent*>(window.get()));
    383  }
    384 }
    385 
    386 WindowGlobalParent* CanonicalBrowsingContext::GetCurrentWindowGlobal() const {
    387  return static_cast<WindowGlobalParent*>(GetCurrentWindowContext());
    388 }
    389 
    390 WindowGlobalParent* CanonicalBrowsingContext::GetParentWindowContext() {
    391  return static_cast<WindowGlobalParent*>(
    392      BrowsingContext::GetParentWindowContext());
    393 }
    394 
    395 WindowGlobalParent* CanonicalBrowsingContext::GetTopWindowContext() {
    396  return static_cast<WindowGlobalParent*>(
    397      BrowsingContext::GetTopWindowContext());
    398 }
    399 
    400 already_AddRefed<nsIWidget>
    401 CanonicalBrowsingContext::GetParentProcessWidgetContaining() {
    402  // If our document is loaded in-process, such as chrome documents, get the
    403  // widget directly from our outer window. Otherwise, try to get the widget
    404  // from the toplevel content's browser's element.
    405  nsCOMPtr<nsIWidget> widget;
    406  if (nsGlobalWindowOuter* window = nsGlobalWindowOuter::Cast(GetDOMWindow())) {
    407    widget = window->GetNearestWidget();
    408  } else if (Element* topEmbedder = Top()->GetEmbedderElement()) {
    409    widget = nsContentUtils::WidgetForContent(topEmbedder);
    410    if (!widget) {
    411      widget = nsContentUtils::WidgetForDocument(topEmbedder->OwnerDoc());
    412    }
    413  }
    414 
    415  if (widget) {
    416    widget = widget->GetTopLevelWidget();
    417  }
    418 
    419  return widget.forget();
    420 }
    421 
    422 already_AddRefed<nsIBrowserDOMWindow>
    423 CanonicalBrowsingContext::GetBrowserDOMWindow() {
    424  RefPtr<CanonicalBrowsingContext> chromeTop = TopCrossChromeBoundary();
    425  nsGlobalWindowOuter* topWin;
    426  if ((topWin = nsGlobalWindowOuter::Cast(chromeTop->GetDOMWindow())) &&
    427      topWin->IsChromeWindow()) {
    428    return do_AddRef(topWin->GetBrowserDOMWindow());
    429  }
    430  return nullptr;
    431 }
    432 
    433 already_AddRefed<WindowGlobalParent>
    434 CanonicalBrowsingContext::GetEmbedderWindowGlobal() const {
    435  uint64_t windowId = GetEmbedderInnerWindowId();
    436  if (windowId == 0) {
    437    return nullptr;
    438  }
    439 
    440  return WindowGlobalParent::GetByInnerWindowId(windowId);
    441 }
    442 
    443 CanonicalBrowsingContext*
    444 CanonicalBrowsingContext::GetParentCrossChromeBoundary() {
    445  if (GetParent()) {
    446    return Cast(GetParent());
    447  }
    448  if (auto* embedder = GetEmbedderElement()) {
    449    return Cast(embedder->OwnerDoc()->GetBrowsingContext());
    450  }
    451  return nullptr;
    452 }
    453 
    454 CanonicalBrowsingContext* CanonicalBrowsingContext::TopCrossChromeBoundary() {
    455  CanonicalBrowsingContext* bc = this;
    456  while (auto* parent = bc->GetParentCrossChromeBoundary()) {
    457    bc = parent;
    458  }
    459  return bc;
    460 }
    461 
    462 Nullable<WindowProxyHolder> CanonicalBrowsingContext::GetTopChromeWindow() {
    463  RefPtr<CanonicalBrowsingContext> bc = TopCrossChromeBoundary();
    464  if (bc->IsChrome()) {
    465    return WindowProxyHolder(bc.forget());
    466  }
    467  return nullptr;
    468 }
    469 
    470 nsISHistory* CanonicalBrowsingContext::GetSessionHistory() {
    471  if (!IsTop()) {
    472    return Cast(Top())->GetSessionHistory();
    473  }
    474 
    475  // Check GetChildSessionHistory() to make sure that this BrowsingContext has
    476  // session history enabled.
    477  if (!mSessionHistory && GetChildSessionHistory()) {
    478    mSessionHistory = new nsSHistory(this);
    479  }
    480 
    481  return mSessionHistory;
    482 }
    483 
    484 SessionHistoryEntry* CanonicalBrowsingContext::GetActiveSessionHistoryEntry() {
    485  return mActiveEntry;
    486 }
    487 
    488 void CanonicalBrowsingContext::SetActiveSessionHistoryEntryFromBFCache(
    489    SessionHistoryEntry* aEntry) {
    490  mActiveEntry = aEntry;
    491  auto* activeEntries = GetActiveEntries();
    492  if (Navigation::IsAPIEnabled() && activeEntries) {
    493    if (StaticPrefs::dom_navigation_api_strict_enabled()) {
    494      MOZ_DIAGNOSTIC_ASSERT(!aEntry || activeEntries->contains(aEntry));
    495      MOZ_DIAGNOSTIC_ASSERT(aEntry || activeEntries->isEmpty());
    496    } else {
    497      MOZ_ASSERT(!aEntry || activeEntries->contains(aEntry));
    498      MOZ_ASSERT(aEntry || activeEntries->isEmpty());
    499    }
    500  }
    501 }
    502 
    503 bool CanonicalBrowsingContext::HasHistoryEntry(nsISHEntry* aEntry) {
    504  // XXX Should we check also loading entries?
    505  return aEntry && mActiveEntry == aEntry;
    506 }
    507 
    508 void CanonicalBrowsingContext::SwapHistoryEntries(nsISHEntry* aOldEntry,
    509                                                  nsISHEntry* aNewEntry) {
    510  // XXX Should we check also loading entries?
    511  if (mActiveEntry != aOldEntry) {
    512    return;
    513  }
    514 
    515  nsCOMPtr<SessionHistoryEntry> newEntry = do_QueryInterface(aNewEntry);
    516  auto* activeEntries = GetActiveEntries();
    517  MOZ_LOG(gSHLog, LogLevel::Verbose,
    518          ("Swapping History Entries: mActiveEntry=%p, aNewEntry=%p. "
    519           "Is in list? mActiveEntry %s, aNewEntry %s. "
    520           "Is aNewEntry in current mActiveEntryList? %s.",
    521           mActiveEntry.get(), aNewEntry,
    522           mActiveEntry && mActiveEntry->isInList() ? "yes" : "no",
    523           newEntry && newEntry->isInList() ? "yes" : "no",
    524           activeEntries->contains(newEntry) ? "yes" : "no"));
    525  if (!newEntry) {
    526    activeEntries->clear();
    527    mActiveEntry = nullptr;
    528    return;
    529  }
    530  if (Navigation::IsAPIEnabled() && mActiveEntry->isInList()) {
    531    RefPtr beforeOldEntry = mActiveEntry->removeAndGetPrevious();
    532    if (beforeOldEntry != newEntry) {
    533      if (newEntry->isInList()) {
    534        newEntry->setNext(mActiveEntry);
    535        newEntry->remove();
    536      }
    537 
    538      if (beforeOldEntry) {
    539        beforeOldEntry->setNext(newEntry);
    540      } else {
    541        activeEntries->insertFront(newEntry);
    542      }
    543    } else {
    544      newEntry->setPrevious(mActiveEntry);
    545    }
    546  }
    547  mActiveEntry = newEntry.forget();
    548 }
    549 
    550 void CanonicalBrowsingContext::AddLoadingSessionHistoryEntry(
    551    uint64_t aLoadId, SessionHistoryEntry* aEntry) {
    552  (void)SetHistoryID(aEntry->DocshellID());
    553  mLoadingEntries.AppendElement(LoadingSessionHistoryEntry{aLoadId, aEntry});
    554 }
    555 
    556 void CanonicalBrowsingContext::GetLoadingSessionHistoryInfoFromParent(
    557    Maybe<LoadingSessionHistoryInfo>& aLoadingInfo) {
    558  nsISHistory* shistory = GetSessionHistory();
    559  if (!shistory || !GetParent()) {
    560    return;
    561  }
    562 
    563  SessionHistoryEntry* parentSHE =
    564      GetParent()->Canonical()->GetActiveSessionHistoryEntry();
    565  if (parentSHE) {
    566    int32_t index = -1;
    567    for (BrowsingContext* sibling : GetParent()->Children()) {
    568      ++index;
    569      if (sibling == this) {
    570        nsCOMPtr<nsISHEntry> shEntry;
    571        parentSHE->GetChildSHEntryIfHasNoDynamicallyAddedChild(
    572            index, getter_AddRefs(shEntry));
    573        nsCOMPtr<SessionHistoryEntry> she = do_QueryInterface(shEntry);
    574        if (she) {
    575          aLoadingInfo.emplace(she);
    576          mLoadingEntries.AppendElement(LoadingSessionHistoryEntry{
    577              aLoadingInfo.value().mLoadId, she.get()});
    578          (void)SetHistoryID(she->DocshellID());
    579        }
    580        break;
    581      }
    582    }
    583  }
    584 }
    585 
    586 UniquePtr<LoadingSessionHistoryInfo>
    587 CanonicalBrowsingContext::CreateLoadingSessionHistoryEntryForLoad(
    588    nsDocShellLoadState* aLoadState, SessionHistoryEntry* existingEntry,
    589    nsIChannel* aChannel) {
    590  RefPtr<SessionHistoryEntry> entry;
    591  const LoadingSessionHistoryInfo* existingLoadingInfo =
    592      aLoadState->GetLoadingSessionHistoryInfo();
    593  MOZ_ASSERT_IF(!existingLoadingInfo, !existingEntry);
    594  if (existingLoadingInfo) {
    595    if (existingEntry) {
    596      entry = existingEntry;
    597    } else {
    598      MOZ_ASSERT(!existingLoadingInfo->mLoadIsFromSessionHistory);
    599 
    600      SessionHistoryEntry::LoadingEntry* loadingEntry =
    601          SessionHistoryEntry::GetByLoadId(existingLoadingInfo->mLoadId);
    602      MOZ_LOG(gSHLog, LogLevel::Verbose,
    603              ("SHEntry::GetByLoadId(%" PRIu64 ") -> %p",
    604               existingLoadingInfo->mLoadId, entry.get()));
    605      if (!loadingEntry) {
    606        return nullptr;
    607      }
    608      entry = loadingEntry->mEntry;
    609    }
    610 
    611    // If the entry was updated, update also the LoadingSessionHistoryInfo.
    612    UniquePtr<LoadingSessionHistoryInfo> lshi =
    613        MakeUnique<LoadingSessionHistoryInfo>(entry, existingLoadingInfo);
    614    aLoadState->SetLoadingSessionHistoryInfo(std::move(lshi));
    615    existingLoadingInfo = aLoadState->GetLoadingSessionHistoryInfo();
    616    (void)SetHistoryEntryCount(entry->BCHistoryLength());
    617  } else if (aLoadState->LoadType() == LOAD_REFRESH &&
    618             !ShouldAddEntryForRefresh(aLoadState->URI(),
    619                                       aLoadState->PostDataStream()) &&
    620             mActiveEntry) {
    621    entry = mActiveEntry;
    622  } else {
    623    entry = new SessionHistoryEntry(aLoadState, aChannel);
    624    if (IsTop() &&
    625        !nsDocShell::ShouldAddToSessionHistory(aLoadState->URI(), aChannel)) {
    626      entry->SetTransient();
    627    }
    628    if (!IsTop() && (mActiveEntry || !mLoadingEntries.IsEmpty())) {
    629      entry->SetIsSubFrame(true);
    630    }
    631    entry->SetDocshellID(GetHistoryID());
    632    entry->SetIsDynamicallyAdded(CreatedDynamically());
    633    entry->SetForInitialLoad(true);
    634  }
    635  MOZ_DIAGNOSTIC_ASSERT(entry);
    636 
    637  if (aLoadState->GetNavigationType() == NavigationType::Replace) {
    638    MaybeReuseNavigationKeyFromActiveEntry(entry);
    639  }
    640 
    641  UniquePtr<LoadingSessionHistoryInfo> loadingInfo;
    642  if (existingLoadingInfo) {
    643    loadingInfo = MakeUnique<LoadingSessionHistoryInfo>(*existingLoadingInfo);
    644  } else {
    645    loadingInfo = MakeUnique<LoadingSessionHistoryInfo>(entry);
    646    mLoadingEntries.AppendElement(
    647        LoadingSessionHistoryEntry{loadingInfo->mLoadId, entry});
    648  }
    649 
    650  // When adding a new entry we need to make sure that the navigation object
    651  // gets it's entries list initialized to the contiguous entries ending in the
    652  // new entry.
    653  if (Navigation::IsAPIEnabled()) {
    654    bool sessionHistoryLoad =
    655        existingLoadingInfo && existingLoadingInfo->mLoadIsFromSessionHistory;
    656 
    657    if (sessionHistoryLoad && !mActiveEntry) {
    658      auto* activeEntries = GetActiveEntries();
    659      if (activeEntries && activeEntries->isEmpty()) {
    660        nsSHistory* shistory = static_cast<nsSHistory*>(GetSessionHistory());
    661        shistory->ReconstructContiguousEntryListFrom(entry);
    662      }
    663    }
    664 
    665    MOZ_LOG_FMT(gNavigationAPILog, LogLevel::Debug,
    666                "Determining navigation type from loadType={}",
    667                aLoadState->LoadType());
    668    Maybe<NavigationType> navigationType =
    669        NavigationUtils::NavigationTypeFromLoadType(aLoadState->LoadType());
    670    if (!navigationType) {
    671      MOZ_LOG_FMT(gNavigationAPILog, LogLevel::Debug,
    672                  "Failed to determine navigation type");
    673      return loadingInfo;
    674    }
    675 
    676    loadingInfo->mTriggeringEntry =
    677        mActiveEntry ? Some(mActiveEntry->Info()) : Nothing();
    678    MOZ_LOG_FMT(gNavigationAPILog, LogLevel::Verbose,
    679                "Triggering entry was {}.",
    680                fmt::ptr(loadingInfo->mTriggeringEntry
    681                             .map([](auto& entry) { return &entry; })
    682                             .valueOr(nullptr)));
    683 
    684    if (!existingLoadingInfo ||
    685        !existingLoadingInfo->mTriggeringNavigationType) {
    686      loadingInfo->mTriggeringNavigationType = navigationType;
    687    }
    688 
    689    MOZ_LOG_FMT(gNavigationAPILog, LogLevel::Verbose,
    690                "Triggering navigation type was {}.", *navigationType);
    691 
    692    GetContiguousEntriesForLoad(*loadingInfo, entry);
    693 
    694    if (MOZ_LOG_TEST(gNavigationAPILog, LogLevel::Debug)) {
    695      int32_t index = 0;
    696      MOZ_LOG_FMT(gNavigationAPILog, LogLevel::Debug,
    697                  "Preparing contiguous for {} ({}load))",
    698                  entry->Info().GetURI()->GetSpecOrDefault(),
    699                  sessionHistoryLoad ? "history " : "");
    700      for (const auto& entry : loadingInfo->mContiguousEntries) {
    701        MOZ_LOG_FMT(gNavigationAPILog, LogLevel::Debug,
    702                    "{}+- {} SHI {} {}\n   URL = {}",
    703                    (mActiveEntry && entry == mActiveEntry->Info()) ? ">" : " ",
    704                    index++, entry.NavigationKey().ToString().get(),
    705                    entry.NavigationId().ToString().get(),
    706                    entry.GetURI()->GetSpecOrDefault());
    707      }
    708    }
    709 
    710    [[maybe_unused]] auto pred = [&](auto& entry) {
    711      return entry.NavigationKey() == loadingInfo->mInfo.NavigationKey();
    712    };
    713    if (StaticPrefs::dom_navigation_api_strict_enabled()) {
    714      // https://bugzil.la/1989045
    715      MOZ_DIAGNOSTIC_ASSERT(
    716          mozilla::AnyOf(loadingInfo->mContiguousEntries.begin(),
    717                         loadingInfo->mContiguousEntries.end(), pred),
    718          "The target entry now needs to be a part of the contiguous list of "
    719          "entries.");
    720    } else {
    721      MOZ_ASSERT(
    722          mozilla::AnyOf(loadingInfo->mContiguousEntries.begin(),
    723                         loadingInfo->mContiguousEntries.end(), pred),
    724          "The target entry now needs to be a part of the contiguous list of "
    725          "entries.");
    726    }
    727  }
    728 
    729  MOZ_ASSERT(SessionHistoryEntry::GetByLoadId(loadingInfo->mLoadId)->mEntry ==
    730             entry);
    731 
    732  return loadingInfo;
    733 }
    734 
    735 UniquePtr<LoadingSessionHistoryInfo>
    736 CanonicalBrowsingContext::ReplaceLoadingSessionHistoryEntryForLoad(
    737    LoadingSessionHistoryInfo* aInfo, nsIChannel* aNewChannel) {
    738  MOZ_ASSERT(aInfo);
    739  MOZ_ASSERT(aNewChannel);
    740 
    741  SessionHistoryInfo newInfo =
    742      SessionHistoryInfo(aNewChannel, aInfo->mInfo.LoadType(),
    743                         aInfo->mInfo.GetPartitionedPrincipalToInherit(),
    744                         aInfo->mInfo.GetPolicyContainer());
    745 
    746  for (size_t i = 0; i < mLoadingEntries.Length(); ++i) {
    747    if (mLoadingEntries[i].mLoadId == aInfo->mLoadId) {
    748      RefPtr<SessionHistoryEntry> loadingEntry = mLoadingEntries[i].mEntry;
    749      loadingEntry->SetInfo(&newInfo);
    750 
    751      if (IsTop()) {
    752        // Only top level pages care about Get/SetTransient.
    753        nsCOMPtr<nsIURI> uri;
    754        aNewChannel->GetURI(getter_AddRefs(uri));
    755        if (!nsDocShell::ShouldAddToSessionHistory(uri, aNewChannel)) {
    756          loadingEntry->SetTransient();
    757        }
    758      } else {
    759        loadingEntry->SetIsSubFrame(aInfo->mInfo.IsSubFrame());
    760      }
    761      loadingEntry->SetDocshellID(GetHistoryID());
    762      loadingEntry->SetIsDynamicallyAdded(CreatedDynamically());
    763 
    764      if (aInfo->mTriggeringNavigationType &&
    765          *aInfo->mTriggeringNavigationType == NavigationType::Replace) {
    766        MaybeReuseNavigationKeyFromActiveEntry(loadingEntry);
    767      }
    768 
    769      auto result = MakeUnique<LoadingSessionHistoryInfo>(loadingEntry, aInfo);
    770      MOZ_LOG_FMT(
    771          gNavigationAPILog, LogLevel::Debug,
    772          "CanonicalBrowsingContext::ReplaceLoadingSessionHistoryEntryForLoad: "
    773          "Recreating the contiguous entries list after redirected navigation "
    774          "to {}.",
    775          ToMaybeRef(result->mInfo.GetURI())
    776              .map(std::mem_fn(&nsIURI::GetSpecOrDefault))
    777              .valueOr("(null URI)."_ns));
    778      GetContiguousEntriesForLoad(*result, loadingEntry);
    779      return result;
    780    }
    781  }
    782  return nullptr;
    783 }
    784 
    785 void CanonicalBrowsingContext::GetContiguousEntriesForLoad(
    786    LoadingSessionHistoryInfo& aLoadingInfo,
    787    const RefPtr<SessionHistoryEntry>& aEntry) {
    788  nsCOMPtr<nsIURI> uri =
    789      mActiveEntry ? mActiveEntry->GetURIOrInheritedForAboutBlank() : nullptr;
    790  nsCOMPtr<nsIURI> targetURI = aEntry->GetURIOrInheritedForAboutBlank();
    791  bool sameOrigin =
    792      NS_SUCCEEDED(nsContentUtils::GetSecurityManager()->CheckSameOriginURI(
    793          targetURI, uri, false, false));
    794  if (aEntry->isInList() ||
    795      (mActiveEntry && mActiveEntry->isInList() && sameOrigin)) {
    796    MOZ_DIAGNOSTIC_ASSERT(aLoadingInfo.mTriggeringNavigationType);
    797    NavigationType navigationType =
    798        aLoadingInfo.mTriggeringNavigationType.valueOr(NavigationType::Push);
    799    nsSHistory::WalkContiguousEntriesInOrder(
    800        aEntry->isInList() ? aEntry : mActiveEntry,
    801        [activeEntry = mActiveEntry, entries = &aLoadingInfo.mContiguousEntries,
    802         navigationType](auto* aEntry) {
    803          nsCOMPtr<SessionHistoryEntry> entry = do_QueryObject(aEntry);
    804          MOZ_ASSERT(entry);
    805          if (navigationType == NavigationType::Replace &&
    806              entry == activeEntry) {
    807            // In the case of a replace navigation, we end up dropping the
    808            // active entry and all following entries.
    809            return false;
    810          }
    811          entries->AppendElement(entry->Info());
    812          // In the case of a push navigation, we end up keeping the
    813          // current active entry but drop all following entries.
    814          return !(navigationType == NavigationType::Push &&
    815                   entry == activeEntry);
    816        });
    817  }
    818 
    819  if (!aLoadingInfo.mLoadIsFromSessionHistory || !sameOrigin) {
    820    aLoadingInfo.mContiguousEntries.AppendElement(aEntry->Info());
    821  }
    822 }
    823 
    824 void CanonicalBrowsingContext::MaybeReuseNavigationKeyFromActiveEntry(
    825    SessionHistoryEntry* aEntry) {
    826  MOZ_ASSERT(aEntry);
    827 
    828  // https://html.spec.whatwg.org/#finalize-a-cross-document-navigation
    829  // 9. If entryToReplace is null, then: ...
    830  //    Otherwise: ...
    831  //      4. If historyEntry's document state's origin is same origin with
    832  //         entryToReplace's document state's origin, then set
    833  //         historyEntry's navigation API key to entryToReplace's
    834  //         navigation API key.
    835  if (!mActiveEntry) {
    836    return;
    837  }
    838 
    839  nsCOMPtr<nsIURI> uri = mActiveEntry->GetURIOrInheritedForAboutBlank();
    840  nsCOMPtr<nsIURI> targetURI = aEntry->GetURIOrInheritedForAboutBlank();
    841  bool sameOrigin =
    842      NS_SUCCEEDED(nsContentUtils::GetSecurityManager()->CheckSameOriginURI(
    843          targetURI, uri, false, false));
    844  if (!sameOrigin) {
    845    return;
    846  }
    847 
    848  aEntry->SetNavigationKey(mActiveEntry->Info().NavigationKey());
    849 }
    850 
    851 using PrintPromise = CanonicalBrowsingContext::PrintPromise;
    852 #ifdef NS_PRINTING
    853 // Clients must call StaticCloneForPrintingCreated or
    854 // NoStaticCloneForPrintingWillBeCreated before the underlying promise can
    855 // resolve.
    856 class PrintListenerAdapter final : public nsIWebProgressListener {
    857 public:
    858  explicit PrintListenerAdapter(PrintPromise::Private* aPromise)
    859      : mPromise(aPromise) {}
    860 
    861  NS_DECL_ISUPPORTS
    862 
    863  // NS_DECL_NSIWEBPROGRESSLISTENER
    864  NS_IMETHOD OnStateChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
    865                           uint32_t aStateFlags, nsresult aStatus) override {
    866    MOZ_ASSERT(NS_IsMainThread());
    867    if (aStateFlags & nsIWebProgressListener::STATE_STOP &&
    868        aStateFlags & nsIWebProgressListener::STATE_IS_DOCUMENT && mPromise) {
    869      mPrintJobFinished = true;
    870      if (mHaveSetBrowsingContext) {
    871        mPromise->Resolve(mClonedStaticBrowsingContext, __func__);
    872        mPromise = nullptr;
    873      }
    874    }
    875    return NS_OK;
    876  }
    877  NS_IMETHOD OnStatusChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
    878                            nsresult aStatus,
    879                            const char16_t* aMessage) override {
    880    if (aStatus != NS_OK && mPromise) {
    881      mPromise->Reject(aStatus, __func__);
    882      mPromise = nullptr;
    883    }
    884    return NS_OK;
    885  }
    886  NS_IMETHOD OnProgressChange(nsIWebProgress* aWebProgress,
    887                              nsIRequest* aRequest, int32_t aCurSelfProgress,
    888                              int32_t aMaxSelfProgress,
    889                              int32_t aCurTotalProgress,
    890                              int32_t aMaxTotalProgress) override {
    891    return NS_OK;
    892  }
    893  NS_IMETHOD OnLocationChange(nsIWebProgress* aWebProgress,
    894                              nsIRequest* aRequest, nsIURI* aLocation,
    895                              uint32_t aFlags) override {
    896    return NS_OK;
    897  }
    898  NS_IMETHOD OnSecurityChange(nsIWebProgress* aWebProgress,
    899                              nsIRequest* aRequest, uint32_t aState) override {
    900    return NS_OK;
    901  }
    902  NS_IMETHOD OnContentBlockingEvent(nsIWebProgress* aWebProgress,
    903                                    nsIRequest* aRequest,
    904                                    uint32_t aEvent) override {
    905    return NS_OK;
    906  }
    907 
    908  void StaticCloneForPrintingCreated(
    909      MaybeDiscardedBrowsingContext&& aClonedStaticBrowsingContext) {
    910    MOZ_ASSERT(NS_IsMainThread());
    911    mClonedStaticBrowsingContext = std::move(aClonedStaticBrowsingContext);
    912    mHaveSetBrowsingContext = true;
    913    if (mPrintJobFinished && mPromise) {
    914      mPromise->Resolve(mClonedStaticBrowsingContext, __func__);
    915      mPromise = nullptr;
    916    }
    917  }
    918 
    919  void NoStaticCloneForPrintingWillBeCreated() {
    920    StaticCloneForPrintingCreated(nullptr);
    921  }
    922 
    923 private:
    924  ~PrintListenerAdapter() = default;
    925 
    926  RefPtr<PrintPromise::Private> mPromise;
    927  MaybeDiscardedBrowsingContext mClonedStaticBrowsingContext = nullptr;
    928  bool mHaveSetBrowsingContext = false;
    929  bool mPrintJobFinished = false;
    930 };
    931 
    932 NS_IMPL_ISUPPORTS(PrintListenerAdapter, nsIWebProgressListener)
    933 #endif
    934 
    935 already_AddRefed<Promise> CanonicalBrowsingContext::PrintJS(
    936    nsIPrintSettings* aPrintSettings, ErrorResult& aRv) {
    937  RefPtr<Promise> promise = Promise::Create(GetIncumbentGlobal(), aRv);
    938  if (NS_WARN_IF(aRv.Failed())) {
    939    return promise.forget();
    940  }
    941 
    942  Print(aPrintSettings)
    943      ->Then(
    944          GetCurrentSerialEventTarget(), __func__,
    945          [promise](MaybeDiscardedBrowsingContext) {
    946            promise->MaybeResolveWithUndefined();
    947          },
    948          [promise](nsresult aResult) { promise->MaybeReject(aResult); });
    949  return promise.forget();
    950 }
    951 
    952 RefPtr<PrintPromise> CanonicalBrowsingContext::Print(
    953    nsIPrintSettings* aPrintSettings) {
    954 #ifndef NS_PRINTING
    955  return PrintPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__);
    956 #else
    957 // Content analysis is not supported on non-Windows platforms.
    958 #  if defined(XP_WIN)
    959  bool needContentAnalysis = false;
    960  nsCOMPtr<nsIContentAnalysis> contentAnalysis =
    961      mozilla::components::nsIContentAnalysis::Service();
    962  (void)NS_WARN_IF(!contentAnalysis);
    963  if (contentAnalysis) {
    964    nsresult rv = contentAnalysis->GetIsActive(&needContentAnalysis);
    965    (void)NS_WARN_IF(NS_FAILED(rv));
    966  }
    967  if (needContentAnalysis) {
    968    auto done = MakeRefPtr<PrintPromise::Private>(__func__);
    969    contentanalysis::ContentAnalysis::PrintToPDFToDetermineIfPrintAllowed(
    970        this, aPrintSettings)
    971        ->Then(
    972            GetCurrentSerialEventTarget(), __func__,
    973            [done, aPrintSettings = RefPtr{aPrintSettings},
    974             self = RefPtr{this}](
    975                contentanalysis::ContentAnalysis::PrintAllowedResult aResponse)
    976                MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA mutable {
    977                  if (aResponse.mAllowed) {
    978                    self->PrintWithNoContentAnalysis(
    979                            aPrintSettings, false,
    980                            aResponse.mCachedStaticDocumentBrowsingContext)
    981                        ->ChainTo(done.forget(), __func__);
    982                  } else {
    983                    // Since we are not doing the second print in this case,
    984                    // release the clone that is no longer needed.
    985                    self->ReleaseClonedPrint(
    986                        aResponse.mCachedStaticDocumentBrowsingContext);
    987                    done->Reject(NS_ERROR_CONTENT_BLOCKED, __func__);
    988                  }
    989                },
    990            [done, self = RefPtr{this}](
    991                contentanalysis::ContentAnalysis::PrintAllowedError
    992                    aErrorResponse) MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA {
    993              // Since we are not doing the second print in this case, release
    994              // the clone that is no longer needed.
    995              self->ReleaseClonedPrint(
    996                  aErrorResponse.mCachedStaticDocumentBrowsingContext);
    997              done->Reject(aErrorResponse.mError, __func__);
    998            });
    999    return done;
   1000  }
   1001 #  endif
   1002  return PrintWithNoContentAnalysis(aPrintSettings, false, nullptr);
   1003 #endif
   1004 }
   1005 
   1006 void CanonicalBrowsingContext::ReleaseClonedPrint(
   1007    const MaybeDiscardedBrowsingContext& aClonedStaticBrowsingContext) {
   1008 #ifdef NS_PRINTING
   1009  auto* browserParent = GetBrowserParent();
   1010  if (NS_WARN_IF(!browserParent)) {
   1011    return;
   1012  }
   1013  (void)browserParent->SendDestroyPrintClone(aClonedStaticBrowsingContext);
   1014 #endif
   1015 }
   1016 
   1017 RefPtr<PrintPromise> CanonicalBrowsingContext::PrintWithNoContentAnalysis(
   1018    nsIPrintSettings* aPrintSettings, bool aForceStaticDocument,
   1019    const MaybeDiscardedBrowsingContext& aCachedStaticDocument) {
   1020 #ifndef NS_PRINTING
   1021  return PrintPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__);
   1022 #else
   1023  auto promise = MakeRefPtr<PrintPromise::Private>(__func__);
   1024  auto listener = MakeRefPtr<PrintListenerAdapter>(promise);
   1025  if (IsInProcess()) {
   1026    RefPtr<nsGlobalWindowOuter> outerWindow =
   1027        nsGlobalWindowOuter::Cast(GetDOMWindow());
   1028    if (NS_WARN_IF(!outerWindow)) {
   1029      promise->Reject(NS_ERROR_FAILURE, __func__);
   1030      return promise;
   1031    }
   1032 
   1033    ErrorResult rv;
   1034    listener->NoStaticCloneForPrintingWillBeCreated();
   1035    outerWindow->Print(aPrintSettings,
   1036                       /* aRemotePrintJob = */ nullptr, listener,
   1037                       /* aDocShellToCloneInto = */ nullptr,
   1038                       nsGlobalWindowOuter::IsPreview::No,
   1039                       nsGlobalWindowOuter::IsForWindowDotPrint::No,
   1040                       /* aPrintPreviewCallback = */ nullptr,
   1041                       /* aCachedBrowsingContext = */ nullptr, rv);
   1042    if (rv.Failed()) {
   1043      promise->Reject(rv.StealNSResult(), __func__);
   1044    }
   1045    return promise;
   1046  }
   1047 
   1048  auto* browserParent = GetBrowserParent();
   1049  if (NS_WARN_IF(!browserParent)) {
   1050    promise->Reject(NS_ERROR_FAILURE, __func__);
   1051    return promise;
   1052  }
   1053 
   1054  nsCOMPtr<nsIPrintSettingsService> printSettingsSvc =
   1055      do_GetService("@mozilla.org/gfx/printsettings-service;1");
   1056  if (NS_WARN_IF(!printSettingsSvc)) {
   1057    promise->Reject(NS_ERROR_FAILURE, __func__);
   1058    return promise;
   1059  }
   1060 
   1061  nsresult rv;
   1062  nsCOMPtr<nsIPrintSettings> printSettings = aPrintSettings;
   1063  if (!printSettings) {
   1064    rv =
   1065        printSettingsSvc->CreateNewPrintSettings(getter_AddRefs(printSettings));
   1066    if (NS_WARN_IF(NS_FAILED(rv))) {
   1067      promise->Reject(rv, __func__);
   1068      return promise;
   1069    }
   1070  }
   1071 
   1072  embedding::PrintData printData;
   1073  rv = printSettingsSvc->SerializeToPrintData(printSettings, &printData);
   1074  if (NS_WARN_IF(NS_FAILED(rv))) {
   1075    promise->Reject(rv, __func__);
   1076    return promise;
   1077  }
   1078 
   1079  layout::RemotePrintJobParent* remotePrintJob =
   1080      new layout::RemotePrintJobParent(printSettings);
   1081  printData.remotePrintJob() =
   1082      browserParent->Manager()->SendPRemotePrintJobConstructor(remotePrintJob);
   1083 
   1084  remotePrintJob->RegisterListener(listener);
   1085 
   1086  if (!aCachedStaticDocument.IsNullOrDiscarded()) {
   1087    // There is no cloned static browsing context that
   1088    // SendPrintClonedPage() will return, so indicate this
   1089    // so listener can resolve its promise.
   1090    listener->NoStaticCloneForPrintingWillBeCreated();
   1091    if (NS_WARN_IF(!browserParent->SendPrintClonedPage(
   1092            this, printData, aCachedStaticDocument))) {
   1093      promise->Reject(NS_ERROR_FAILURE, __func__);
   1094    }
   1095  } else {
   1096    RefPtr<PBrowserParent::PrintPromise> printPromise =
   1097        browserParent->SendPrint(this, printData, aForceStaticDocument);
   1098    printPromise->Then(
   1099        GetMainThreadSerialEventTarget(), __func__,
   1100        [listener](MaybeDiscardedBrowsingContext cachedStaticDocument) {
   1101          // promise will get resolved by the listener
   1102          listener->StaticCloneForPrintingCreated(
   1103              std::move(cachedStaticDocument));
   1104        },
   1105        [promise](ResponseRejectReason reason) {
   1106          NS_WARNING("SendPrint() failed");
   1107          promise->Reject(NS_ERROR_FAILURE, __func__);
   1108        });
   1109  }
   1110  return promise.forget();
   1111 #endif
   1112 }
   1113 
   1114 void CanonicalBrowsingContext::CallOnTopDescendants(
   1115    const FunctionRef<CallState(CanonicalBrowsingContext*)>& aCallback,
   1116    TopDescendantKind aKind) {
   1117  // Calling with All on something other than a chrome root is unlikely to be
   1118  // what you want, so lacking a use-case for it, we assert against it for now.
   1119  MOZ_ASSERT_IF(aKind == TopDescendantKind::All,
   1120                IsChrome() && !GetParentCrossChromeBoundary());
   1121  // Similarly, calling with {NonNested,All} on a non-top bc is unlikely to be
   1122  // what you want.
   1123  MOZ_ASSERT_IF(aKind != TopDescendantKind::ChildrenOnly, IsTop());
   1124 
   1125  if (!IsInProcess()) {
   1126    // We rely on top levels having to be embedded in the parent process, so
   1127    // we can only have top level descendants if embedded here...
   1128    return;
   1129  }
   1130 
   1131  const auto* ourTop = Top();
   1132 
   1133  AutoTArray<RefPtr<BrowsingContextGroup>, 32> groups;
   1134  BrowsingContextGroup::GetAllGroups(groups);
   1135  for (auto& browsingContextGroup : groups) {
   1136    for (auto& topLevel : browsingContextGroup->Toplevels()) {
   1137      if (topLevel == ourTop) {
   1138        // A nested toplevel can't be a descendant of our same toplevel.
   1139        continue;
   1140      }
   1141 
   1142      // Walk up the CanonicalBrowsingContext tree, looking for a match.
   1143      const bool topLevelIsRelevant = [&] {
   1144        auto* current = topLevel->Canonical();
   1145        while (auto* parent = current->GetParentCrossChromeBoundary()) {
   1146          if (parent == this) {
   1147            return true;
   1148          }
   1149          // If we've reached aKind's stop condition, break out early.
   1150          if (aKind == TopDescendantKind::ChildrenOnly ||
   1151              (aKind == TopDescendantKind::NonNested && parent->IsTop())) {
   1152            return false;
   1153          }
   1154          current = parent;
   1155        }
   1156        return false;
   1157      }();
   1158 
   1159      if (!topLevelIsRelevant) {
   1160        continue;
   1161      }
   1162 
   1163      if (aCallback(topLevel->Canonical()) == CallState::Stop) {
   1164        return;
   1165      }
   1166    }
   1167  }
   1168 }
   1169 
   1170 void CanonicalBrowsingContext::SessionHistoryCommit(
   1171    uint64_t aLoadId, const nsID& aChangeID, uint32_t aLoadType,
   1172    bool aCloneEntryChildren, bool aChannelExpired, uint32_t aCacheKey) {
   1173  MOZ_LOG(gSHLog, LogLevel::Verbose,
   1174          ("CanonicalBrowsingContext::SessionHistoryCommit %p %" PRIu64, this,
   1175           aLoadId));
   1176  MOZ_ASSERT(aLoadId != UINT64_MAX,
   1177             "Must not send special about:blank loadinfo to parent.");
   1178  for (size_t i = 0; i < mLoadingEntries.Length(); ++i) {
   1179    if (mLoadingEntries[i].mLoadId == aLoadId) {
   1180      nsSHistory* shistory = static_cast<nsSHistory*>(GetSessionHistory());
   1181      if (!shistory) {
   1182        SessionHistoryEntry::RemoveLoadId(aLoadId);
   1183        mLoadingEntries.RemoveElementAt(i);
   1184        return;
   1185      }
   1186 
   1187      RefPtr<SessionHistoryEntry> newActiveEntry = mLoadingEntries[i].mEntry;
   1188      if (aCacheKey != 0) {
   1189        newActiveEntry->SetCacheKey(aCacheKey);
   1190      }
   1191 
   1192      if (aChannelExpired) {
   1193        newActiveEntry->SharedInfo()->mExpired = true;
   1194      }
   1195 
   1196      bool loadFromSessionHistory = !newActiveEntry->ForInitialLoad();
   1197      newActiveEntry->SetForInitialLoad(false);
   1198      SessionHistoryEntry::RemoveLoadId(aLoadId);
   1199      mLoadingEntries.RemoveElementAt(i);
   1200 
   1201      int32_t indexOfHistoryLoad = -1;
   1202      if (loadFromSessionHistory) {
   1203        nsCOMPtr<nsISHEntry> root = nsSHistory::GetRootSHEntry(newActiveEntry);
   1204        indexOfHistoryLoad = shistory->GetIndexOfEntry(root);
   1205        if (indexOfHistoryLoad < 0) {
   1206          // Entry has been removed from the session history.
   1207          return;
   1208        }
   1209      }
   1210 
   1211      CallerWillNotifyHistoryIndexAndLengthChanges caller(shistory);
   1212 
   1213      // If there is a name in the new entry, clear the name of all contiguous
   1214      // entries. This is for https://html.spec.whatwg.org/#history-traversal
   1215      // Step 4.4.2.
   1216      nsAutoString nameOfNewEntry;
   1217      newActiveEntry->GetName(nameOfNewEntry);
   1218      if (!nameOfNewEntry.IsEmpty()) {
   1219        nsSHistory::WalkContiguousEntries(
   1220            newActiveEntry,
   1221            [](nsISHEntry* aEntry) { aEntry->SetName(EmptyString()); });
   1222      }
   1223 
   1224      auto* activeEntries = GetActiveEntries();
   1225      MOZ_LOG(gSHLog, LogLevel::Verbose,
   1226              ("SessionHistoryCommit called with mActiveEntry=%p, "
   1227               "newActiveEntry=%p, "
   1228               "active entry list does%s contain the active entry.",
   1229               mActiveEntry.get(), newActiveEntry.get(),
   1230               activeEntries->contains(mActiveEntry) ? "" : "n't"));
   1231 
   1232      bool addEntry = ShouldUpdateSessionHistory(aLoadType);
   1233      if (IsTop()) {
   1234        if (mActiveEntry && !mActiveEntry->GetFrameLoader()) {
   1235          bool sharesDocument = true;
   1236          mActiveEntry->SharesDocumentWith(newActiveEntry, &sharesDocument);
   1237          if (!sharesDocument) {
   1238            // If the old page won't be in the bfcache,
   1239            // clear the dynamic entries.
   1240            RemoveDynEntriesFromActiveSessionHistoryEntry();
   1241          }
   1242        }
   1243 
   1244        if (LOAD_TYPE_HAS_FLAGS(aLoadType,
   1245                                nsIWebNavigation::LOAD_FLAGS_REPLACE_HISTORY)) {
   1246          // Replace the current entry with the new entry.
   1247          int32_t index = shistory->GetTargetIndexForHistoryOperation();
   1248 
   1249          // If we're trying to replace an inexistant shistory entry then we
   1250          // should append instead.
   1251          addEntry = index < 0;
   1252          MOZ_LOG_FMT(gSHLog, LogLevel::Verbose,
   1253                      "IsTop: Replacing history with addEntry={}", addEntry);
   1254 
   1255          if (!addEntry) {
   1256            shistory->ReplaceEntry(index, newActiveEntry);
   1257            if (Navigation::IsAPIEnabled() && mActiveEntry &&
   1258                mActiveEntry->isInList() && !newActiveEntry->isInList()) {
   1259              mActiveEntry->setNext(newActiveEntry);
   1260              mActiveEntry->remove();
   1261            }
   1262          }
   1263          if (Navigation::IsAPIEnabled() && !newActiveEntry->isInList()) {
   1264            activeEntries->insertBack(newActiveEntry);
   1265          }
   1266          mActiveEntry = newActiveEntry;
   1267        } else if (LOAD_TYPE_HAS_FLAGS(
   1268                       aLoadType, nsIWebNavigation::LOAD_FLAGS_IS_REFRESH) &&
   1269                   !ShouldAddEntryForRefresh(newActiveEntry) && mActiveEntry) {
   1270          MOZ_LOG_FMT(gSHLog, LogLevel::Verbose,
   1271                      "IsTop: Refresh without adding entry");
   1272          addEntry = false;
   1273          mActiveEntry->ReplaceWith(*newActiveEntry);
   1274        } else if (!loadFromSessionHistory && mActiveEntry) {
   1275          MOZ_LOG_FMT(gSHLog, LogLevel::Verbose, "IsTop: Adding new entry");
   1276 
   1277          if (Navigation::IsAPIEnabled() && mActiveEntry->isInList()) {
   1278            RefPtr entry = mActiveEntry->getNext();
   1279            while (entry) {
   1280              entry = entry->removeAndGetNext();
   1281            }
   1282            // TODO(avandolder): Can this check ever actually be false?
   1283            if (!newActiveEntry->isInList()) {
   1284              activeEntries->insertBack(newActiveEntry);
   1285            }
   1286          }
   1287          mActiveEntry = newActiveEntry;
   1288        } else if (!mActiveEntry) {
   1289          MOZ_LOG_FMT(gSHLog, LogLevel::Verbose,
   1290                      "IsTop: No active entry, adding new entry");
   1291          if (Navigation::IsAPIEnabled() && !newActiveEntry->isInList()) {
   1292            activeEntries->insertBack(newActiveEntry);
   1293          }
   1294          mActiveEntry = newActiveEntry;
   1295        } else {
   1296          MOZ_LOG_FMT(gSHLog, LogLevel::Verbose,
   1297                      "IsTop: Loading from session history");
   1298          mActiveEntry = newActiveEntry;
   1299          if (Navigation::IsAPIEnabled() && !mActiveEntry->isInList()) {
   1300            activeEntries->insertBack(mActiveEntry);
   1301          }
   1302        }
   1303 
   1304        if (loadFromSessionHistory) {
   1305          // XXX Synchronize browsing context tree and session history tree?
   1306          shistory->InternalSetRequestedIndex(indexOfHistoryLoad);
   1307          shistory->UpdateIndex();
   1308 
   1309          if (IsTop()) {
   1310            mActiveEntry->SetWireframe(Nothing());
   1311          }
   1312        } else if (addEntry) {
   1313          shistory->AddEntry(mActiveEntry);
   1314          shistory->InternalSetRequestedIndex(-1);
   1315        }
   1316      } else {
   1317        // FIXME The old implementations adds it to the parent's mLSHE if there
   1318        //       is one, need to figure out if that makes sense here (peterv
   1319        //       doesn't think it would).
   1320        if (loadFromSessionHistory) {
   1321          if (mActiveEntry) {
   1322            // mActiveEntry is null if we're loading iframes from session
   1323            // history while also parent page is loading from session history.
   1324            // In that case there isn't anything to sync.
   1325            mActiveEntry->SyncTreesForSubframeNavigation(newActiveEntry, Top(),
   1326                                                         this);
   1327          }
   1328          MOZ_LOG_FMT(gSHLog, LogLevel::Verbose,
   1329                      "NotTop: Loading from session history");
   1330          mActiveEntry = newActiveEntry;
   1331          if (Navigation::IsAPIEnabled() && !mActiveEntry->isInList()) {
   1332            shistory->ReconstructContiguousEntryListFrom(mActiveEntry);
   1333          }
   1334          shistory->InternalSetRequestedIndex(indexOfHistoryLoad);
   1335          // FIXME UpdateIndex() here may update index too early (but even the
   1336          //       old implementation seems to have similar issues).
   1337          shistory->UpdateIndex();
   1338        } else if (addEntry) {
   1339          if (mActiveEntry) {
   1340            if (LOAD_TYPE_HAS_FLAGS(
   1341                    aLoadType, nsIWebNavigation::LOAD_FLAGS_REPLACE_HISTORY) ||
   1342                (LOAD_TYPE_HAS_FLAGS(aLoadType,
   1343                                     nsIWebNavigation::LOAD_FLAGS_IS_REFRESH) &&
   1344                 !ShouldAddEntryForRefresh(newActiveEntry))) {
   1345              // FIXME We need to make sure that when we create the info we
   1346              //       make a copy of the shared state.
   1347              mActiveEntry->ReplaceWith(*newActiveEntry);
   1348              MOZ_LOG_FMT(gSHLog, LogLevel::Verbose,
   1349                          "NotTop: replace current active entry");
   1350            } else {
   1351              // AddNestedSHEntry does update the index of the session
   1352              // history!
   1353              MOZ_LOG_FMT(gSHLog, LogLevel::Verbose,
   1354                          "NotTop: Adding entry with an active entry");
   1355              shistory->AddNestedSHEntry(mActiveEntry, newActiveEntry, Top(),
   1356                                         aCloneEntryChildren);
   1357              if (Navigation::IsAPIEnabled()) {
   1358                if (!mActiveEntry->isInList()) {
   1359                  activeEntries->insertBack(mActiveEntry);
   1360                }
   1361                mActiveEntry->setNext(newActiveEntry);
   1362              }
   1363              mActiveEntry = newActiveEntry;
   1364            }
   1365          } else {
   1366            SessionHistoryEntry* parentEntry = GetParent()->mActiveEntry;
   1367            // XXX What should happen if parent doesn't have mActiveEntry?
   1368            //     Or can that even happen ever?
   1369            if (parentEntry) {
   1370              MOZ_LOG_FMT(gSHLog, LogLevel::Verbose,
   1371                          "NotTop: Adding entry without an active entry");
   1372              mActiveEntry = newActiveEntry;
   1373              if (Navigation::IsAPIEnabled() && !mActiveEntry->isInList()) {
   1374                activeEntries->insertBack(mActiveEntry);
   1375              }
   1376              // FIXME Using IsInProcess for aUseRemoteSubframes isn't quite
   1377              //       right, but aUseRemoteSubframes should be going away.
   1378              parentEntry->AddChild(
   1379                  mActiveEntry,
   1380                  CreatedDynamically() ? -1 : GetParent()->IndexOf(this),
   1381                  IsInProcess());
   1382            }
   1383          }
   1384          shistory->InternalSetRequestedIndex(-1);
   1385        }
   1386      }
   1387 
   1388      ResetSHEntryHasUserInteractionCache();
   1389 
   1390      HistoryCommitIndexAndLength(aChangeID, caller);
   1391 
   1392      shistory->LogHistory();
   1393 
   1394      return;
   1395    }
   1396    // XXX Should the loading entries before [i] be removed?
   1397  }
   1398  // FIXME Should we throw an error if we don't find an entry for
   1399  // aSessionHistoryEntryId?
   1400 }
   1401 
   1402 already_AddRefed<nsDocShellLoadState> CanonicalBrowsingContext::CreateLoadInfo(
   1403    SessionHistoryEntry* aEntry, NavigationType aNavigationType) {
   1404  const SessionHistoryInfo& info = aEntry->Info();
   1405  RefPtr<nsDocShellLoadState> loadState(new nsDocShellLoadState(info.GetURI()));
   1406  info.FillLoadInfo(*loadState);
   1407  UniquePtr<LoadingSessionHistoryInfo> loadingInfo;
   1408  loadingInfo = MakeUnique<LoadingSessionHistoryInfo>(aEntry);
   1409  loadingInfo->mTriggeringNavigationType = Some(aNavigationType);
   1410  mLoadingEntries.AppendElement(
   1411      LoadingSessionHistoryEntry{loadingInfo->mLoadId, aEntry});
   1412  loadState->SetLoadingSessionHistoryInfo(std::move(loadingInfo));
   1413 
   1414  return loadState.forget();
   1415 }
   1416 
   1417 void CanonicalBrowsingContext::NotifyOnHistoryReload(
   1418    bool aForceReload, bool& aCanReload,
   1419    Maybe<NotNull<RefPtr<nsDocShellLoadState>>>& aLoadState,
   1420    Maybe<bool>& aReloadActiveEntry) {
   1421  MOZ_DIAGNOSTIC_ASSERT(!aLoadState);
   1422 
   1423  aCanReload = true;
   1424  nsISHistory* shistory = GetSessionHistory();
   1425  NS_ENSURE_TRUE_VOID(shistory);
   1426 
   1427  shistory->NotifyOnHistoryReload(&aCanReload);
   1428  if (!aCanReload) {
   1429    return;
   1430  }
   1431 
   1432  if (mActiveEntry) {
   1433    aLoadState.emplace(WrapMovingNotNull(
   1434        RefPtr{CreateLoadInfo(mActiveEntry, NavigationType::Reload)}));
   1435    aReloadActiveEntry.emplace(true);
   1436    if (aForceReload) {
   1437      shistory->RemoveFrameEntries(mActiveEntry);
   1438    }
   1439  } else if (!mLoadingEntries.IsEmpty()) {
   1440    const LoadingSessionHistoryEntry& loadingEntry =
   1441        mLoadingEntries.LastElement();
   1442    uint64_t loadId = loadingEntry.mLoadId;
   1443    aLoadState.emplace(WrapMovingNotNull(
   1444        RefPtr{CreateLoadInfo(loadingEntry.mEntry, NavigationType::Reload)}));
   1445    aReloadActiveEntry.emplace(false);
   1446    if (aForceReload) {
   1447      SessionHistoryEntry::LoadingEntry* entry =
   1448          SessionHistoryEntry::GetByLoadId(loadId);
   1449      if (entry) {
   1450        shistory->RemoveFrameEntries(entry->mEntry);
   1451      }
   1452    }
   1453  }
   1454 
   1455  if (aLoadState) {
   1456    // Use 0 as the offset, since aLoadState will be be used for reload.
   1457    aLoadState.ref()->SetLoadIsFromSessionHistory(0,
   1458                                                  aReloadActiveEntry.value());
   1459  }
   1460  // If we don't have an active entry and we don't have a loading entry then
   1461  // the nsDocShell will create a load state based on its document.
   1462 }
   1463 
   1464 void CanonicalBrowsingContext::SetActiveSessionHistoryEntry(
   1465    const Maybe<nsPoint>& aPreviousScrollPos, SessionHistoryInfo* aInfo,
   1466    uint32_t aLoadType, uint32_t aUpdatedCacheKey, const nsID& aChangeID) {
   1467  nsISHistory* shistory = GetSessionHistory();
   1468  if (!shistory) {
   1469    return;
   1470  }
   1471  CallerWillNotifyHistoryIndexAndLengthChanges caller(shistory);
   1472 
   1473  RefPtr<SessionHistoryEntry> oldActiveEntry = mActiveEntry;
   1474  if (aPreviousScrollPos.isSome() && oldActiveEntry) {
   1475    oldActiveEntry->SetScrollPosition(aPreviousScrollPos.ref().x,
   1476                                      aPreviousScrollPos.ref().y);
   1477  }
   1478  mActiveEntry = new SessionHistoryEntry(aInfo);
   1479  mActiveEntry->SetDocshellID(GetHistoryID());
   1480  mActiveEntry->AdoptBFCacheEntry(oldActiveEntry);
   1481  if (aUpdatedCacheKey != 0) {
   1482    mActiveEntry->SharedInfo()->mCacheKey = aUpdatedCacheKey;
   1483  }
   1484 
   1485  if (IsTop()) {
   1486    Maybe<int32_t> previousEntryIndex, loadedEntryIndex;
   1487    shistory->AddToRootSessionHistory(true, oldActiveEntry, this, mActiveEntry,
   1488                                      aLoadType, &previousEntryIndex,
   1489                                      &loadedEntryIndex);
   1490  } else {
   1491    if (oldActiveEntry) {
   1492      shistory->AddNestedSHEntry(oldActiveEntry, mActiveEntry, Top(), true);
   1493    } else if (GetParent() && GetParent()->mActiveEntry) {
   1494      GetParent()->mActiveEntry->AddChild(
   1495          mActiveEntry, CreatedDynamically() ? -1 : GetParent()->IndexOf(this),
   1496          UseRemoteSubframes());
   1497    }
   1498  }
   1499 
   1500  auto* activeEntries = GetActiveEntries();
   1501  MOZ_LOG(
   1502      gSHLog, LogLevel::Verbose,
   1503      ("SetActiveSessionHistoryEntry called with oldActiveEntry=%p, "
   1504       "mActiveEntry=%p, active entry list does%s contain the active entry. ",
   1505       oldActiveEntry.get(), mActiveEntry.get(),
   1506       activeEntries->contains(mActiveEntry) ? "" : "n't"));
   1507 
   1508  if (Navigation::IsAPIEnabled() &&
   1509      (!oldActiveEntry || oldActiveEntry->isInList())) {
   1510    RefPtr toRemove =
   1511        oldActiveEntry ? oldActiveEntry->getNext() : activeEntries->getFirst();
   1512    while (toRemove) {
   1513      toRemove = toRemove->removeAndGetNext();
   1514    }
   1515    activeEntries->insertBack(mActiveEntry);
   1516  }
   1517 
   1518  ResetSHEntryHasUserInteractionCache();
   1519 
   1520  shistory->InternalSetRequestedIndex(-1);
   1521 
   1522  // FIXME Need to do the equivalent of EvictDocumentViewersOrReplaceEntry.
   1523  HistoryCommitIndexAndLength(aChangeID, caller);
   1524 
   1525  static_cast<nsSHistory*>(shistory)->LogHistory();
   1526 }
   1527 
   1528 void CanonicalBrowsingContext::ReplaceActiveSessionHistoryEntry(
   1529    SessionHistoryInfo* aInfo) {
   1530  if (!mActiveEntry) {
   1531    return;
   1532  }
   1533 
   1534  // aInfo comes from the entry stored in the current document's docshell, whose
   1535  // interaction state does not get updated. So we instead propagate state from
   1536  // the previous canonical entry. See bug 1917369.
   1537  const bool hasUserInteraction = mActiveEntry->GetHasUserInteraction();
   1538  mActiveEntry->SetInfo(aInfo);
   1539  mActiveEntry->SetHasUserInteraction(hasUserInteraction);
   1540  // Notify children of the update
   1541  nsSHistory* shistory = static_cast<nsSHistory*>(GetSessionHistory());
   1542  if (shistory) {
   1543    shistory->NotifyOnHistoryReplaceEntry();
   1544  }
   1545 
   1546  ResetSHEntryHasUserInteractionCache();
   1547 
   1548  if (IsTop()) {
   1549    mActiveEntry->SetWireframe(Nothing());
   1550  }
   1551 
   1552  MOZ_LOG(gSHLog, LogLevel::Verbose,
   1553          ("Replacing active session history entry"));
   1554  if (Navigation::IsAPIEnabled() && mActiveEntry->isInList()) {
   1555    RefPtr toRemove = mActiveEntry->getNext();
   1556    while (toRemove) {
   1557      toRemove = toRemove->removeAndGetNext();
   1558    }
   1559  }
   1560 
   1561  // FIXME Need to do the equivalent of EvictDocumentViewersOrReplaceEntry.
   1562 }
   1563 
   1564 void CanonicalBrowsingContext::RemoveDynEntriesFromActiveSessionHistoryEntry() {
   1565  nsISHistory* shistory = GetSessionHistory();
   1566  // In theory shistory can be null here if the method is called right after
   1567  // CanonicalBrowsingContext::ReplacedBy call.
   1568  NS_ENSURE_TRUE_VOID(shistory);
   1569  nsCOMPtr<nsISHEntry> root = nsSHistory::GetRootSHEntry(mActiveEntry);
   1570  shistory->RemoveDynEntries(shistory->GetIndexOfEntry(root), mActiveEntry);
   1571 }
   1572 
   1573 void CanonicalBrowsingContext::RemoveFromSessionHistory(const nsID& aChangeID) {
   1574  nsSHistory* shistory = static_cast<nsSHistory*>(GetSessionHistory());
   1575  if (shistory) {
   1576    CallerWillNotifyHistoryIndexAndLengthChanges caller(shistory);
   1577    nsCOMPtr<nsISHEntry> root = nsSHistory::GetRootSHEntry(mActiveEntry);
   1578    bool didRemove;
   1579    AutoTArray<nsID, 16> ids({GetHistoryID()});
   1580    shistory->RemoveEntries(ids, shistory->GetIndexOfEntry(root), &didRemove);
   1581    if (didRemove) {
   1582      RefPtr<BrowsingContext> rootBC = shistory->GetBrowsingContext();
   1583      if (rootBC) {
   1584        if (!rootBC->IsInProcess()) {
   1585          if (ContentParent* cp = rootBC->Canonical()->GetContentParent()) {
   1586            (void)cp->SendDispatchLocationChangeEvent(rootBC);
   1587          }
   1588        } else if (rootBC->GetDocShell()) {
   1589          rootBC->GetDocShell()->DispatchLocationChangeEvent();
   1590        }
   1591      }
   1592    }
   1593    HistoryCommitIndexAndLength(aChangeID, caller);
   1594  }
   1595 }
   1596 
   1597 // https://html.spec.whatwg.org/#apply-the-history-step
   1598 // This might not seem to be #apply-the-history-step, but it is in fact exactly
   1599 // what it is.
   1600 Maybe<int32_t> CanonicalBrowsingContext::HistoryGo(
   1601    int32_t aOffset, uint64_t aHistoryEpoch, bool aRequireUserInteraction,
   1602    bool aUserActivation, bool aCheckForCancelation,
   1603    Maybe<ContentParentId> aContentId,
   1604    std::function<void(nsresult)>&& aResolver) {
   1605  if (aRequireUserInteraction && aOffset != -1 && aOffset != 1) {
   1606    NS_ERROR(
   1607        "aRequireUserInteraction may only be used with an offset of -1 or 1");
   1608    return Nothing();
   1609  }
   1610 
   1611  nsSHistory* shistory = static_cast<nsSHistory*>(GetSessionHistory());
   1612  if (!shistory) {
   1613    return Nothing();
   1614  }
   1615 
   1616  CheckedInt<int32_t> index = shistory->GetTargetIndexForHistoryOperation();
   1617  MOZ_LOG(gSHLog, LogLevel::Debug,
   1618          ("HistoryGo(%d->%d) epoch %" PRIu64 "/id %" PRIu64, aOffset,
   1619           (index + aOffset).value(), aHistoryEpoch,
   1620           (uint64_t)(aContentId.isSome() ? aContentId.value() : 0)));
   1621 
   1622  while (true) {
   1623    index += aOffset;
   1624    if (!index.isValid()) {
   1625      MOZ_LOG(gSHLog, LogLevel::Debug, ("Invalid index"));
   1626      return Nothing();
   1627    }
   1628 
   1629    // Check for user interaction if desired, except for the first and last
   1630    // history entries. We compare with >= to account for the case where
   1631    // aOffset >= length.
   1632    if (!StaticPrefs::browser_navigation_requireUserInteraction() ||
   1633        !aRequireUserInteraction || index.value() >= shistory->Length() - 1 ||
   1634        index.value() <= 0) {
   1635      break;
   1636    }
   1637    if (shistory->HasUserInteractionAtIndex(index.value())) {
   1638      break;
   1639    }
   1640  }
   1641 
   1642  // Implement aborting additional history navigations from within the same
   1643  // event spin of the content process.
   1644 
   1645  uint64_t epoch;
   1646  bool sameEpoch = false;
   1647  Maybe<ContentParentId> id;
   1648  shistory->GetEpoch(epoch, id);
   1649 
   1650  if (aContentId == id && epoch >= aHistoryEpoch) {
   1651    sameEpoch = true;
   1652    MOZ_LOG(gSHLog, LogLevel::Debug, ("Same epoch/id"));
   1653  }
   1654  // Don't update the epoch until we know if the target index is valid
   1655 
   1656  // GoToIndex checks that index is >= 0 and < length.
   1657  nsTArray<nsSHistory::LoadEntryResult> loadResults;
   1658  const int32_t oldRequestedIndex = shistory->GetRequestedIndex();
   1659 
   1660  nsresult rv = shistory->GotoIndex(this, index.value(), loadResults, sameEpoch,
   1661                                    aOffset == 0, aUserActivation);
   1662  if (NS_FAILED(rv)) {
   1663    MOZ_LOG(gSHLog, LogLevel::Debug,
   1664            ("Dropping HistoryGo - bad index or same epoch (not in same doc)"));
   1665    return Nothing();
   1666  }
   1667 
   1668  for (auto& loadResult : loadResults) {
   1669    if (nsresult result = loadResult.mBrowsingContext->CheckSandboxFlags(
   1670            loadResult.mLoadState);
   1671        NS_FAILED(result)) {
   1672      aResolver(result);
   1673      MOZ_LOG(gSHLog, LogLevel::Debug,
   1674              ("Dropping HistoryGo - sandbox check failed"));
   1675      shistory->InternalSetRequestedIndex(oldRequestedIndex);
   1676      return Nothing();
   1677    }
   1678  }
   1679 
   1680  if (epoch < aHistoryEpoch || aContentId != id) {
   1681    MOZ_LOG(gSHLog, LogLevel::Debug, ("Set epoch"));
   1682    shistory->SetEpoch(aHistoryEpoch, aContentId);
   1683  }
   1684  int32_t requestedIndex = shistory->GetRequestedIndex();
   1685  RefPtr traversable = Top();
   1686  nsSHistory::LoadURIs(loadResults, aCheckForCancelation, aResolver,
   1687                       traversable);
   1688  return Some(requestedIndex);
   1689 }
   1690 
   1691 // https://html.spec.whatwg.org/#performing-a-navigation-api-traversal
   1692 // Sub-steps for step 12
   1693 void CanonicalBrowsingContext::NavigationTraverse(
   1694    const nsID& aKey, uint64_t aHistoryEpoch, bool aUserActivation,
   1695    bool aCheckForCancelation, Maybe<ContentParentId> aContentId,
   1696    std::function<void(nsresult)>&& aResolver) {
   1697  MOZ_LOG_FMT(gNavigationAPILog, LogLevel::Debug, "Traverse navigation to {}",
   1698              aKey.ToString().get());
   1699  nsSHistory* shistory = static_cast<nsSHistory*>(GetSessionHistory());
   1700  if (!shistory) {
   1701    return aResolver(NS_ERROR_DOM_INVALID_STATE_ERR);
   1702  }
   1703 
   1704  RefPtr<SessionHistoryEntry> targetEntry;
   1705  // 12.1 Let navigableSHEs be the result of getting session history entries
   1706  //      given navigable.
   1707  nsSHistory::WalkContiguousEntriesInOrder(
   1708      mActiveEntry, [&targetEntry, aKey](auto* aEntry) {
   1709        auto* entry = static_cast<SessionHistoryEntry*>(aEntry);
   1710        if (entry->Info().NavigationKey() == aKey) {
   1711          targetEntry = entry;
   1712          return false;
   1713        }
   1714        return true;
   1715      });
   1716 
   1717  // Step 12.2
   1718  if (!targetEntry) {
   1719    return aResolver(NS_ERROR_DOM_INVALID_STATE_ERR);
   1720  }
   1721 
   1722  // Step 12.3
   1723  if (targetEntry == mActiveEntry) {
   1724    return aResolver(NS_OK);
   1725  }
   1726 
   1727  nsCOMPtr targetRoot = nsSHistory::GetRootSHEntry(targetEntry);
   1728  nsCOMPtr activeRoot = nsSHistory::GetRootSHEntry(mActiveEntry);
   1729  if (!targetRoot || !activeRoot) {
   1730    return aResolver(NS_ERROR_DOM_INVALID_STATE_ERR);
   1731  }
   1732  int32_t targetIndex = shistory->GetIndexOfEntry(targetRoot);
   1733  int32_t activeIndex = shistory->GetIndexOfEntry(activeRoot);
   1734  if (targetIndex == -1 || activeIndex == -1) {
   1735    return aResolver(NS_ERROR_DOM_INVALID_STATE_ERR);
   1736  }
   1737 
   1738  int32_t offset = targetIndex - activeIndex;
   1739 
   1740  int32_t requestedIndex = shistory->GetTargetIndexForHistoryOperation();
   1741  // Step 12.3
   1742  if (requestedIndex == targetIndex) {
   1743    return aResolver(NS_OK);
   1744  }
   1745 
   1746  // Reset the requested index since this is not a relative traversal, and the
   1747  // offset is overriding any currently ongoing history traversals.
   1748  shistory->InternalSetRequestedIndex(-1);
   1749 
   1750  HistoryGo(offset, aHistoryEpoch, false, aUserActivation, aCheckForCancelation,
   1751            aContentId, std::move(aResolver));
   1752 }
   1753 
   1754 JSObject* CanonicalBrowsingContext::WrapObject(
   1755    JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
   1756  return CanonicalBrowsingContext_Binding::Wrap(aCx, this, aGivenProto);
   1757 }
   1758 
   1759 void CanonicalBrowsingContext::DispatchWheelZoomChange(bool aIncrease) {
   1760  Element* element = Top()->GetEmbedderElement();
   1761  if (!element) {
   1762    return;
   1763  }
   1764 
   1765  auto event = aIncrease ? u"DoZoomEnlargeBy10"_ns : u"DoZoomReduceBy10"_ns;
   1766  auto dispatcher = MakeRefPtr<AsyncEventDispatcher>(
   1767      element, event, CanBubble::eYes, ChromeOnlyDispatch::eYes);
   1768  dispatcher->PostDOMEvent();
   1769 }
   1770 
   1771 void CanonicalBrowsingContext::CanonicalDiscard() {
   1772  if (mTabMediaController) {
   1773    mTabMediaController->Shutdown();
   1774    mTabMediaController = nullptr;
   1775  }
   1776 
   1777  if (mCurrentLoad) {
   1778    mCurrentLoad->Cancel(NS_BINDING_ABORTED,
   1779                         "CanonicalBrowsingContext::CanonicalDiscard"_ns);
   1780  }
   1781 
   1782  if (mWebProgress) {
   1783    RefPtr<BrowsingContextWebProgress> progress = mWebProgress;
   1784    progress->ContextDiscarded();
   1785  }
   1786 
   1787  if (IsTop()) {
   1788    BackgroundSessionStorageManager::RemoveManager(Id());
   1789  }
   1790 
   1791  CancelSessionStoreUpdate();
   1792 
   1793  if (UsePrivateBrowsing() && EverAttached() && IsContent()) {
   1794    DecreasePrivateCount();
   1795  }
   1796 }
   1797 
   1798 void CanonicalBrowsingContext::CanonicalAttach() {
   1799  if (UsePrivateBrowsing() && IsContent()) {
   1800    IncreasePrivateCount();
   1801  }
   1802 }
   1803 
   1804 void CanonicalBrowsingContext::AddPendingDiscard() {
   1805  MOZ_ASSERT(!mFullyDiscarded);
   1806  mPendingDiscards++;
   1807 }
   1808 
   1809 void CanonicalBrowsingContext::RemovePendingDiscard() {
   1810  mPendingDiscards--;
   1811  if (!mPendingDiscards) {
   1812    mFullyDiscarded = true;
   1813    auto listeners = std::move(mFullyDiscardedListeners);
   1814    for (const auto& listener : listeners) {
   1815      listener(Id());
   1816    }
   1817  }
   1818 }
   1819 
   1820 void CanonicalBrowsingContext::AddFinalDiscardListener(
   1821    std::function<void(uint64_t)>&& aListener) {
   1822  if (mFullyDiscarded) {
   1823    aListener(Id());
   1824    return;
   1825  }
   1826  mFullyDiscardedListeners.AppendElement(std::move(aListener));
   1827 }
   1828 
   1829 void CanonicalBrowsingContext::SetForceAppWindowActive(bool aForceActive,
   1830                                                       ErrorResult& aRv) {
   1831  MOZ_DIAGNOSTIC_ASSERT(IsChrome());
   1832  MOZ_DIAGNOSTIC_ASSERT(IsTop());
   1833  if (!IsChrome() || !IsTop()) {
   1834    return aRv.ThrowNotAllowedError(
   1835        "You shouldn't need to force this BrowsingContext to be active, use "
   1836        ".isActive instead");
   1837  }
   1838  if (mForceAppWindowActive == aForceActive) {
   1839    return;
   1840  }
   1841  mForceAppWindowActive = aForceActive;
   1842  RecomputeAppWindowVisibility();
   1843 }
   1844 
   1845 void CanonicalBrowsingContext::RecomputeAppWindowVisibility() {
   1846  MOZ_RELEASE_ASSERT(IsChrome());
   1847  MOZ_RELEASE_ASSERT(IsTop());
   1848 
   1849  const bool wasAlreadyActive = IsActive();
   1850 
   1851  nsCOMPtr<nsIWidget> widget;
   1852  if (auto* docShell = GetDocShell()) {
   1853    widget = nsDocShell::Cast(docShell)->GetMainWidget();
   1854  }
   1855 
   1856  (void)NS_WARN_IF(!widget);
   1857  const bool isNowActive =
   1858      ForceAppWindowActive() || (widget && !widget->IsFullyOccluded() &&
   1859                                 widget->SizeMode() != nsSizeMode_Minimized);
   1860 
   1861  if (isNowActive == wasAlreadyActive) {
   1862    return;
   1863  }
   1864 
   1865  SetIsActiveInternal(isNowActive, IgnoreErrors());
   1866  if (widget) {
   1867    // Pause if we are not active, resume if we are active.
   1868    widget->PauseOrResumeCompositor(!isNowActive);
   1869  }
   1870 }
   1871 
   1872 void CanonicalBrowsingContext::AdjustPrivateBrowsingCount(
   1873    bool aPrivateBrowsing) {
   1874  if (IsDiscarded() || !EverAttached() || IsChrome()) {
   1875    return;
   1876  }
   1877 
   1878  MOZ_DIAGNOSTIC_ASSERT(aPrivateBrowsing == UsePrivateBrowsing());
   1879  if (aPrivateBrowsing) {
   1880    IncreasePrivateCount();
   1881  } else {
   1882    DecreasePrivateCount();
   1883  }
   1884 }
   1885 
   1886 void CanonicalBrowsingContext::NotifyStartDelayedAutoplayMedia() {
   1887  WindowContext* windowContext = GetCurrentWindowContext();
   1888  if (!windowContext) {
   1889    return;
   1890  }
   1891 
   1892  // As this function would only be called when user click the play icon on the
   1893  // tab bar. That's clear user intent to play, so gesture activate the window
   1894  // context so that the block-autoplay logic allows the media to autoplay.
   1895  windowContext->NotifyUserGestureActivation();
   1896  AUTOPLAY_LOG("NotifyStartDelayedAutoplayMedia for chrome bc 0x%08" PRIx64,
   1897               Id());
   1898  StartDelayedAutoplayMediaComponents();
   1899  // Notfiy all content browsing contexts which are related with the canonical
   1900  // browsing content tree to start delayed autoplay media.
   1901 
   1902  Group()->EachParent([&](ContentParent* aParent) {
   1903    (void)aParent->SendStartDelayedAutoplayMediaComponents(this);
   1904  });
   1905 }
   1906 
   1907 void CanonicalBrowsingContext::NotifyMediaMutedChanged(bool aMuted,
   1908                                                       ErrorResult& aRv) {
   1909  MOZ_ASSERT(!GetParent(),
   1910             "Notify media mute change on non top-level context!");
   1911  SetMuted(aMuted, aRv);
   1912 }
   1913 
   1914 uint32_t CanonicalBrowsingContext::CountSiteOrigins(
   1915    GlobalObject& aGlobal,
   1916    const Sequence<OwningNonNull<BrowsingContext>>& aRoots) {
   1917  nsTHashSet<nsCString> uniqueSiteOrigins;
   1918 
   1919  for (const auto& root : aRoots) {
   1920    root->PreOrderWalk([&](BrowsingContext* aContext) {
   1921      WindowGlobalParent* windowGlobalParent =
   1922          aContext->Canonical()->GetCurrentWindowGlobal();
   1923      if (windowGlobalParent) {
   1924        nsIPrincipal* documentPrincipal =
   1925            windowGlobalParent->DocumentPrincipal();
   1926 
   1927        bool isContentPrincipal = documentPrincipal->GetIsContentPrincipal();
   1928        if (isContentPrincipal) {
   1929          nsCString siteOrigin;
   1930          documentPrincipal->GetSiteOrigin(siteOrigin);
   1931          uniqueSiteOrigins.Insert(siteOrigin);
   1932        }
   1933      }
   1934    });
   1935  }
   1936 
   1937  return uniqueSiteOrigins.Count();
   1938 }
   1939 
   1940 /* static */
   1941 bool CanonicalBrowsingContext::IsPrivateBrowsingActive() {
   1942  return gNumberOfPrivateContexts > 0;
   1943 }
   1944 
   1945 void CanonicalBrowsingContext::UpdateMediaControlAction(
   1946    const MediaControlAction& aAction) {
   1947  if (IsDiscarded()) {
   1948    return;
   1949  }
   1950  ContentMediaControlKeyHandler::HandleMediaControlAction(this, aAction);
   1951  Group()->EachParent([&](ContentParent* aParent) {
   1952    (void)aParent->SendUpdateMediaControlAction(this, aAction);
   1953  });
   1954 }
   1955 
   1956 void CanonicalBrowsingContext::LoadURI(nsIURI* aURI,
   1957                                       const LoadURIOptions& aOptions,
   1958                                       ErrorResult& aError) {
   1959  RefPtr<nsDocShellLoadState> loadState;
   1960  nsresult rv = nsDocShellLoadState::CreateFromLoadURIOptions(
   1961      this, aURI, aOptions, getter_AddRefs(loadState));
   1962  MOZ_ASSERT(rv != NS_ERROR_MALFORMED_URI);
   1963 
   1964  if (NS_FAILED(rv)) {
   1965    aError.Throw(rv);
   1966    return;
   1967  }
   1968 
   1969  // Set the captive portal tab flag on the browsing context if requested
   1970  if (loadState->GetIsCaptivePortalTab()) {
   1971    (void)SetIsCaptivePortalTab(true);
   1972  }
   1973 
   1974  LoadURI(loadState, true);
   1975 }
   1976 
   1977 void CanonicalBrowsingContext::FixupAndLoadURIString(
   1978    const nsAString& aURI, const LoadURIOptions& aOptions,
   1979    ErrorResult& aError) {
   1980  RefPtr<nsDocShellLoadState> loadState;
   1981  nsresult rv = nsDocShellLoadState::CreateFromLoadURIOptions(
   1982      this, aURI, aOptions, getter_AddRefs(loadState));
   1983 
   1984  if (rv == NS_ERROR_MALFORMED_URI) {
   1985    DisplayLoadError(aURI);
   1986    return;
   1987  }
   1988 
   1989  if (NS_FAILED(rv)) {
   1990    aError.Throw(rv);
   1991    return;
   1992  }
   1993 
   1994  // Set the captive portal tab flag on the browsing context if requested
   1995  if (loadState->GetIsCaptivePortalTab()) {
   1996    (void)SetIsCaptivePortalTab(true);
   1997  }
   1998 
   1999  LoadURI(loadState, true);
   2000 }
   2001 
   2002 void CanonicalBrowsingContext::GoBack(
   2003    const Optional<int32_t>& aCancelContentJSEpoch,
   2004    bool aRequireUserInteraction, bool aUserActivation) {
   2005  if (IsDiscarded()) {
   2006    return;
   2007  }
   2008 
   2009  // Stop any known network loads if necessary.
   2010  if (mCurrentLoad) {
   2011    mCurrentLoad->Cancel(NS_BINDING_CANCELLED_OLD_LOAD, ""_ns);
   2012  }
   2013 
   2014  if (RefPtr<nsDocShell> docShell = nsDocShell::Cast(GetDocShell())) {
   2015    if (aCancelContentJSEpoch.WasPassed()) {
   2016      docShell->SetCancelContentJSEpoch(aCancelContentJSEpoch.Value());
   2017    }
   2018    docShell->GoBack(aRequireUserInteraction, aUserActivation);
   2019  } else if (ContentParent* cp = GetContentParent()) {
   2020    Maybe<int32_t> cancelContentJSEpoch;
   2021    if (aCancelContentJSEpoch.WasPassed()) {
   2022      cancelContentJSEpoch = Some(aCancelContentJSEpoch.Value());
   2023    }
   2024    (void)cp->SendGoBack(this, cancelContentJSEpoch, aRequireUserInteraction,
   2025                         aUserActivation);
   2026  }
   2027 }
   2028 void CanonicalBrowsingContext::GoForward(
   2029    const Optional<int32_t>& aCancelContentJSEpoch,
   2030    bool aRequireUserInteraction, bool aUserActivation) {
   2031  if (IsDiscarded()) {
   2032    return;
   2033  }
   2034 
   2035  // Stop any known network loads if necessary.
   2036  if (mCurrentLoad) {
   2037    mCurrentLoad->Cancel(NS_BINDING_CANCELLED_OLD_LOAD, ""_ns);
   2038  }
   2039 
   2040  if (RefPtr<nsDocShell> docShell = nsDocShell::Cast(GetDocShell())) {
   2041    if (aCancelContentJSEpoch.WasPassed()) {
   2042      docShell->SetCancelContentJSEpoch(aCancelContentJSEpoch.Value());
   2043    }
   2044    docShell->GoForward(aRequireUserInteraction, aUserActivation);
   2045  } else if (ContentParent* cp = GetContentParent()) {
   2046    Maybe<int32_t> cancelContentJSEpoch;
   2047    if (aCancelContentJSEpoch.WasPassed()) {
   2048      cancelContentJSEpoch.emplace(aCancelContentJSEpoch.Value());
   2049    }
   2050    (void)cp->SendGoForward(this, cancelContentJSEpoch, aRequireUserInteraction,
   2051                            aUserActivation);
   2052  }
   2053 }
   2054 void CanonicalBrowsingContext::GoToIndex(
   2055    int32_t aIndex, const Optional<int32_t>& aCancelContentJSEpoch,
   2056    bool aUserActivation) {
   2057  if (IsDiscarded()) {
   2058    return;
   2059  }
   2060 
   2061  // Stop any known network loads if necessary.
   2062  if (mCurrentLoad) {
   2063    mCurrentLoad->Cancel(NS_BINDING_CANCELLED_OLD_LOAD, ""_ns);
   2064  }
   2065 
   2066  if (RefPtr<nsDocShell> docShell = nsDocShell::Cast(GetDocShell())) {
   2067    if (aCancelContentJSEpoch.WasPassed()) {
   2068      docShell->SetCancelContentJSEpoch(aCancelContentJSEpoch.Value());
   2069    }
   2070    docShell->GotoIndex(aIndex, aUserActivation);
   2071  } else if (ContentParent* cp = GetContentParent()) {
   2072    Maybe<int32_t> cancelContentJSEpoch;
   2073    if (aCancelContentJSEpoch.WasPassed()) {
   2074      cancelContentJSEpoch.emplace(aCancelContentJSEpoch.Value());
   2075    }
   2076    (void)cp->SendGoToIndex(this, aIndex, cancelContentJSEpoch,
   2077                            aUserActivation);
   2078  }
   2079 }
   2080 
   2081 void CanonicalBrowsingContext::Reload(uint32_t aReloadFlags) {
   2082  if (IsDiscarded()) {
   2083    return;
   2084  }
   2085 
   2086  // Stop any known network loads if necessary.
   2087  if (mCurrentLoad) {
   2088    mCurrentLoad->Cancel(NS_BINDING_CANCELLED_OLD_LOAD, ""_ns);
   2089  }
   2090 
   2091  if (RefPtr<nsDocShell> docShell = nsDocShell::Cast(GetDocShell())) {
   2092    docShell->Reload(aReloadFlags);
   2093  } else if (ContentParent* cp = GetContentParent()) {
   2094    (void)cp->SendReload(this, aReloadFlags);
   2095  }
   2096 }
   2097 
   2098 void CanonicalBrowsingContext::Stop(uint32_t aStopFlags) {
   2099  if (IsDiscarded()) {
   2100    return;
   2101  }
   2102 
   2103  // Stop any known network loads if necessary.
   2104  if (mCurrentLoad && (aStopFlags & nsIWebNavigation::STOP_NETWORK)) {
   2105    mCurrentLoad->Cancel(NS_BINDING_ABORTED,
   2106                         "CanonicalBrowsingContext::Stop"_ns);
   2107  }
   2108 
   2109  // Ask the docshell to stop to handle loads that haven't
   2110  // yet reached here, as well as non-network activity.
   2111  if (auto* docShell = nsDocShell::Cast(GetDocShell())) {
   2112    docShell->Stop(aStopFlags);
   2113  } else if (ContentParent* cp = GetContentParent()) {
   2114    (void)cp->SendStopLoad(this, aStopFlags);
   2115  }
   2116 }
   2117 
   2118 void CanonicalBrowsingContext::PendingRemotenessChange::ProcessLaunched() {
   2119  if (!mPromise) {
   2120    return;
   2121  }
   2122 
   2123  if (mContentParentKeepAlive) {
   2124    // If our new content process is still unloading from a previous process
   2125    // switch, wait for that unload to complete before continuing.
   2126    auto found = mTarget->FindUnloadingHost(mContentParentKeepAlive->ChildID());
   2127    if (found != mTarget->mUnloadingHosts.end()) {
   2128      found->mCallbacks.AppendElement(
   2129          [self = RefPtr{this}]()
   2130              MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA { self->ProcessReady(); });
   2131      return;
   2132    }
   2133  }
   2134 
   2135  ProcessReady();
   2136 }
   2137 
   2138 void CanonicalBrowsingContext::PendingRemotenessChange::ProcessReady() {
   2139  if (!mPromise) {
   2140    return;
   2141  }
   2142 
   2143  MOZ_ASSERT(!mProcessReady);
   2144  mProcessReady = true;
   2145  MaybeFinish();
   2146 }
   2147 
   2148 void CanonicalBrowsingContext::PendingRemotenessChange::MaybeFinish() {
   2149  if (!mPromise) {
   2150    return;
   2151  }
   2152 
   2153  if (!mProcessReady || mWaitingForPrepareToChange) {
   2154    return;
   2155  }
   2156 
   2157  // If this BrowsingContext is embedded within the parent process, perform the
   2158  // process switch directly.
   2159  nsresult rv = mTarget->IsTopContent() ? FinishTopContent() : FinishSubframe();
   2160  if (NS_FAILED(rv)) {
   2161    NS_WARNING("Error finishing PendingRemotenessChange!");
   2162    Cancel(rv);
   2163  } else {
   2164    Clear();
   2165  }
   2166 }
   2167 
   2168 // Logic for finishing a toplevel process change embedded within the parent
   2169 // process. Due to frontend integration the logic differs substantially from
   2170 // subframe process switches, and is handled separately.
   2171 nsresult CanonicalBrowsingContext::PendingRemotenessChange::FinishTopContent() {
   2172  MOZ_DIAGNOSTIC_ASSERT(mTarget->IsTop(),
   2173                        "We shouldn't be trying to change the remoteness of "
   2174                        "non-remote iframes");
   2175 
   2176  // Abort if our ContentParent died while process switching.
   2177  if (mContentParentKeepAlive &&
   2178      NS_WARN_IF(mContentParentKeepAlive->IsShuttingDown())) {
   2179    return NS_ERROR_FAILURE;
   2180  }
   2181 
   2182  // While process switching, we need to check if any of our ancestors are
   2183  // discarded or no longer current, in which case the process switch needs to
   2184  // be aborted.
   2185  RefPtr<CanonicalBrowsingContext> target(mTarget);
   2186  if (target->IsDiscarded() || !target->AncestorsAreCurrent()) {
   2187    return NS_ERROR_FAILURE;
   2188  }
   2189 
   2190  Element* browserElement = target->GetEmbedderElement();
   2191  if (!browserElement) {
   2192    return NS_ERROR_FAILURE;
   2193  }
   2194 
   2195  nsCOMPtr<nsIBrowser> browser = browserElement->AsBrowser();
   2196  if (!browser) {
   2197    return NS_ERROR_FAILURE;
   2198  }
   2199 
   2200  RefPtr<nsFrameLoaderOwner> frameLoaderOwner = do_QueryObject(browserElement);
   2201  MOZ_RELEASE_ASSERT(frameLoaderOwner,
   2202                     "embedder browser must be nsFrameLoaderOwner");
   2203 
   2204  // If we're process switching a browsing context in private browsing
   2205  // mode we might decrease the private browsing count to '0', which
   2206  // would make us fire "last-pb-context-exited" and drop the private
   2207  // session. To prevent that we artificially increment the number of
   2208  // private browsing contexts with '1' until the process switch is done.
   2209  bool usePrivateBrowsing = mTarget->UsePrivateBrowsing();
   2210  if (usePrivateBrowsing) {
   2211    IncreasePrivateCount();
   2212  }
   2213 
   2214  auto restorePrivateCount = MakeScopeExit([usePrivateBrowsing]() {
   2215    if (usePrivateBrowsing) {
   2216      DecreasePrivateCount();
   2217    }
   2218  });
   2219 
   2220  // Tell frontend code that this browser element is about to change process.
   2221  nsresult rv = browser->BeforeChangeRemoteness();
   2222  if (NS_FAILED(rv)) {
   2223    return rv;
   2224  }
   2225 
   2226  // Some frontend code checks the value of the `remote` attribute on the
   2227  // browser to determine if it is remote, so update the value.
   2228  browserElement->SetAttr(kNameSpaceID_None, nsGkAtoms::remote,
   2229                          mContentParentKeepAlive ? u"true"_ns : u"false"_ns,
   2230                          /* notify */ true);
   2231 
   2232  // The process has been created, hand off to nsFrameLoaderOwner to finish
   2233  // the process switch.
   2234  ErrorResult error;
   2235  RefPtr keepAlive = mContentParentKeepAlive.get();
   2236  RefPtr specificGroup = mSpecificGroup;
   2237  frameLoaderOwner->ChangeRemotenessToProcess(keepAlive, mOptions,
   2238                                              specificGroup, error);
   2239  if (error.Failed()) {
   2240    return error.StealNSResult();
   2241  }
   2242 
   2243  // Tell frontend the load is done.
   2244  bool loadResumed = false;
   2245  rv = browser->FinishChangeRemoteness(mPendingSwitchId, &loadResumed);
   2246  if (NS_WARN_IF(NS_FAILED(rv))) {
   2247    return rv;
   2248  }
   2249 
   2250  // We did it! The process switch is complete.
   2251  RefPtr<nsFrameLoader> frameLoader = frameLoaderOwner->GetFrameLoader();
   2252  RefPtr<BrowserParent> newBrowser = frameLoader->GetBrowserParent();
   2253  if (!newBrowser) {
   2254    if (mContentParentKeepAlive) {
   2255      // Failed to create the BrowserParent somehow! Abort the process switch
   2256      // attempt.
   2257      return NS_ERROR_UNEXPECTED;
   2258    }
   2259 
   2260    if (!loadResumed) {
   2261      RefPtr<nsDocShell> newDocShell = frameLoader->GetDocShell(error);
   2262      if (error.Failed()) {
   2263        return error.StealNSResult();
   2264      }
   2265 
   2266      rv = newDocShell->ResumeRedirectedLoad(mPendingSwitchId,
   2267                                             /* aHistoryIndex */ -1);
   2268      if (NS_FAILED(rv)) {
   2269        return rv;
   2270      }
   2271    }
   2272  } else if (!loadResumed) {
   2273    newBrowser->ResumeLoad(mPendingSwitchId);
   2274  }
   2275 
   2276  mPromise->Resolve(
   2277      std::pair{newBrowser,
   2278                RefPtr{frameLoader->GetBrowsingContext()->Canonical()}},
   2279      __func__);
   2280  return NS_OK;
   2281 }
   2282 
   2283 nsresult CanonicalBrowsingContext::PendingRemotenessChange::FinishSubframe() {
   2284  MOZ_DIAGNOSTIC_ASSERT(!mOptions.mReplaceBrowsingContext,
   2285                        "Cannot replace BC for subframe");
   2286  MOZ_DIAGNOSTIC_ASSERT(!mTarget->IsTop());
   2287 
   2288  // While process switching, we need to check if any of our ancestors are
   2289  // discarded or no longer current, in which case the process switch needs to
   2290  // be aborted.
   2291  RefPtr<CanonicalBrowsingContext> target(mTarget);
   2292  if (target->IsDiscarded() || !target->AncestorsAreCurrent()) {
   2293    return NS_ERROR_FAILURE;
   2294  }
   2295 
   2296  if (NS_WARN_IF(!mContentParentKeepAlive)) {
   2297    return NS_ERROR_FAILURE;
   2298  }
   2299 
   2300  RefPtr<WindowGlobalParent> embedderWindow = target->GetParentWindowContext();
   2301  if (NS_WARN_IF(!embedderWindow) || NS_WARN_IF(!embedderWindow->CanSend())) {
   2302    return NS_ERROR_FAILURE;
   2303  }
   2304 
   2305  RefPtr<BrowserParent> embedderBrowser = embedderWindow->GetBrowserParent();
   2306  if (NS_WARN_IF(!embedderBrowser)) {
   2307    return NS_ERROR_FAILURE;
   2308  }
   2309 
   2310  // If we're creating a new remote browser, and the host process is already
   2311  // dead, abort the process switch.
   2312  if (mContentParentKeepAlive != embedderBrowser->Manager() &&
   2313      NS_WARN_IF(mContentParentKeepAlive->IsShuttingDown())) {
   2314    return NS_ERROR_FAILURE;
   2315  }
   2316 
   2317  RefPtr<BrowserParent> oldBrowser = target->GetBrowserParent();
   2318  target->SetCurrentBrowserParent(nullptr);
   2319 
   2320  // If we were in a remote frame, trigger unloading of the remote window. The
   2321  // previous BrowserParent is registered in `mUnloadingHosts` and will only be
   2322  // cleared when the BrowserParent is fully destroyed.
   2323  bool wasRemote = oldBrowser && oldBrowser->GetBrowsingContext() == target;
   2324  if (wasRemote) {
   2325    MOZ_DIAGNOSTIC_ASSERT(oldBrowser != embedderBrowser);
   2326    MOZ_DIAGNOSTIC_ASSERT(oldBrowser->IsDestroyed() ||
   2327                          oldBrowser->GetBrowserBridgeParent());
   2328 
   2329    // `oldBrowser` will clear the `UnloadingHost` status once the actor has
   2330    // been destroyed.
   2331    if (oldBrowser->CanSend()) {
   2332      target->StartUnloadingHost(oldBrowser->Manager()->ChildID());
   2333      (void)oldBrowser->SendWillChangeProcess();
   2334      oldBrowser->Destroy();
   2335    }
   2336  }
   2337 
   2338  // Update which process is considered the current owner
   2339  target->SetOwnerProcessId(mContentParentKeepAlive->ChildID());
   2340 
   2341  // If we're switching from remote to local, we don't need to create a
   2342  // BrowserBridge, and can instead perform the switch directly.
   2343  if (mContentParentKeepAlive == embedderBrowser->Manager()) {
   2344    MOZ_DIAGNOSTIC_ASSERT(
   2345        mPendingSwitchId,
   2346        "We always have a PendingSwitchId, except for print-preview loads, "
   2347        "which will never perform a process-switch to being in-process with "
   2348        "their embedder");
   2349    MOZ_DIAGNOSTIC_ASSERT(wasRemote,
   2350                          "Attempt to process-switch from local to local?");
   2351 
   2352    target->SetCurrentBrowserParent(embedderBrowser);
   2353    (void)embedderWindow->SendMakeFrameLocal(target, mPendingSwitchId);
   2354    mPromise->Resolve(std::pair{embedderBrowser, target}, __func__);
   2355    return NS_OK;
   2356  }
   2357 
   2358  // The BrowsingContext will be remote, either as an already-remote frame
   2359  // changing processes, or as a local frame becoming remote. Construct a new
   2360  // BrowserBridgeParent to host the remote content.
   2361  target->SetCurrentBrowserParent(nullptr);
   2362 
   2363  MOZ_DIAGNOSTIC_ASSERT(target->UseRemoteTabs() && target->UseRemoteSubframes(),
   2364                        "Not supported without fission");
   2365  uint32_t chromeFlags = nsIWebBrowserChrome::CHROME_REMOTE_WINDOW |
   2366                         nsIWebBrowserChrome::CHROME_FISSION_WINDOW;
   2367  if (target->UsePrivateBrowsing()) {
   2368    chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW;
   2369  }
   2370 
   2371  nsCOMPtr<nsIPrincipal> initialPrincipal =
   2372      NullPrincipal::Create(target->OriginAttributesRef());
   2373  RefPtr<nsOpenWindowInfo> openWindowInfo = new nsOpenWindowInfo();
   2374  openWindowInfo->mPrincipalToInheritForAboutBlank = initialPrincipal;
   2375  WindowGlobalInit windowInit =
   2376      WindowGlobalActor::AboutBlankInitializer(target, initialPrincipal);
   2377 
   2378  // Create and initialize our new BrowserBridgeParent.
   2379  TabId tabId(nsContentUtils::GenerateTabId());
   2380  RefPtr<BrowserBridgeParent> bridge = new BrowserBridgeParent();
   2381  nsresult rv =
   2382      bridge->InitWithProcess(embedderBrowser, mContentParentKeepAlive.get(),
   2383                              windowInit, chromeFlags, tabId);
   2384  if (NS_WARN_IF(NS_FAILED(rv))) {
   2385    // If we've already destroyed our previous document, make a best-effort
   2386    // attempt to recover from this failure and show the crashed tab UI. We only
   2387    // do this in the previously-remote case, as previously in-process frames
   2388    // will have their navigation cancelled, and will remain visible.
   2389    if (wasRemote) {
   2390      target->ShowSubframeCrashedUI(oldBrowser->GetBrowserBridgeParent());
   2391    }
   2392    return rv;
   2393  }
   2394 
   2395  // Tell the embedder process a remoteness change is in-process. When this is
   2396  // acknowledged, reset the in-flight ID if it used to be an in-process load.
   2397  RefPtr<BrowserParent> newBrowser = bridge->GetBrowserParent();
   2398  {
   2399    // If we weren't remote, mark our embedder window browser as unloading until
   2400    // our embedder process has acked our MakeFrameRemote message.
   2401    Maybe<uint64_t> clearChildID;
   2402    if (!wasRemote) {
   2403      clearChildID = Some(embedderBrowser->Manager()->ChildID());
   2404      target->StartUnloadingHost(*clearChildID);
   2405    }
   2406    auto callback = [target, clearChildID](auto&&) {
   2407      if (clearChildID) {
   2408        target->ClearUnloadingHost(*clearChildID);
   2409      }
   2410    };
   2411 
   2412    ManagedEndpoint<PBrowserBridgeChild> endpoint =
   2413        embedderBrowser->OpenPBrowserBridgeEndpoint(bridge);
   2414    MOZ_DIAGNOSTIC_ASSERT(endpoint.IsValid());
   2415    embedderWindow->SendMakeFrameRemote(target, std::move(endpoint), tabId,
   2416                                        newBrowser->GetLayersId(), callback,
   2417                                        callback);
   2418  }
   2419 
   2420  // Resume the pending load in our new process.
   2421  if (mPendingSwitchId) {
   2422    newBrowser->ResumeLoad(mPendingSwitchId);
   2423  }
   2424 
   2425  // We did it! The process switch is complete.
   2426  mPromise->Resolve(std::pair{newBrowser, target}, __func__);
   2427  return NS_OK;
   2428 }
   2429 
   2430 void CanonicalBrowsingContext::PendingRemotenessChange::Cancel(nsresult aRv) {
   2431  if (!mPromise) {
   2432    return;
   2433  }
   2434 
   2435  mPromise->Reject(aRv, __func__);
   2436  Clear();
   2437 }
   2438 
   2439 void CanonicalBrowsingContext::PendingRemotenessChange::Clear() {
   2440  // Make sure we don't die while we're doing cleanup.
   2441  RefPtr<PendingRemotenessChange> kungFuDeathGrip(this);
   2442  if (mTarget) {
   2443    MOZ_DIAGNOSTIC_ASSERT(mTarget->mPendingRemotenessChange == this);
   2444    mTarget->mPendingRemotenessChange = nullptr;
   2445  }
   2446 
   2447  // When this PendingRemotenessChange was created, it was given a
   2448  // `mContentParentKeepAlive`.
   2449  mContentParentKeepAlive = nullptr;
   2450 
   2451  // If we were given a specific group, stop keeping that group alive manually.
   2452  if (mSpecificGroup) {
   2453    mSpecificGroup->RemoveKeepAlive();
   2454    mSpecificGroup = nullptr;
   2455  }
   2456 
   2457  mPromise = nullptr;
   2458  mTarget = nullptr;
   2459 }
   2460 
   2461 CanonicalBrowsingContext::PendingRemotenessChange::PendingRemotenessChange(
   2462    CanonicalBrowsingContext* aTarget, RemotenessPromise::Private* aPromise,
   2463    uint64_t aPendingSwitchId, const NavigationIsolationOptions& aOptions)
   2464    : mTarget(aTarget),
   2465      mPromise(aPromise),
   2466      mPendingSwitchId(aPendingSwitchId),
   2467      mOptions(aOptions) {}
   2468 
   2469 CanonicalBrowsingContext::PendingRemotenessChange::~PendingRemotenessChange() {
   2470  MOZ_ASSERT(
   2471      !mPromise && !mTarget && !mContentParentKeepAlive && !mSpecificGroup,
   2472      "should've already been Cancel() or Complete()-ed");
   2473 }
   2474 
   2475 BrowserParent* CanonicalBrowsingContext::GetBrowserParent() const {
   2476  return mCurrentBrowserParent;
   2477 }
   2478 
   2479 void CanonicalBrowsingContext::SetCurrentBrowserParent(
   2480    BrowserParent* aBrowserParent) {
   2481  MOZ_DIAGNOSTIC_ASSERT(!mCurrentBrowserParent || !aBrowserParent,
   2482                        "BrowsingContext already has a current BrowserParent!");
   2483  MOZ_DIAGNOSTIC_ASSERT_IF(aBrowserParent, aBrowserParent->CanSend());
   2484  MOZ_DIAGNOSTIC_ASSERT_IF(aBrowserParent,
   2485                           aBrowserParent->Manager()->ChildID() == mProcessId);
   2486 
   2487  // BrowserParent must either be directly for this BrowsingContext, or the
   2488  // manager out our embedder WindowGlobal.
   2489  MOZ_DIAGNOSTIC_ASSERT_IF(
   2490      aBrowserParent && aBrowserParent->GetBrowsingContext() != this,
   2491      GetParentWindowContext() &&
   2492          GetParentWindowContext()->Manager() == aBrowserParent);
   2493 
   2494  if (aBrowserParent && IsTopContent() && !ManuallyManagesActiveness()) {
   2495    aBrowserParent->SetRenderLayers(IsActive());
   2496  }
   2497 
   2498  mCurrentBrowserParent = aBrowserParent;
   2499 }
   2500 
   2501 bool CanonicalBrowsingContext::ManuallyManagesActiveness() const {
   2502  auto* el = GetEmbedderElement();
   2503  return el && el->IsXULElement() && el->HasAttr(nsGkAtoms::manualactiveness);
   2504 }
   2505 
   2506 RefPtr<CanonicalBrowsingContext::RemotenessPromise>
   2507 CanonicalBrowsingContext::ChangeRemoteness(
   2508    const NavigationIsolationOptions& aOptions, uint64_t aPendingSwitchId) {
   2509  MOZ_DIAGNOSTIC_ASSERT(IsContent(),
   2510                        "cannot change the process of chrome contexts");
   2511  MOZ_DIAGNOSTIC_ASSERT(
   2512      IsTop() == IsEmbeddedInProcess(0),
   2513      "toplevel content must be embedded in the parent process");
   2514  MOZ_DIAGNOSTIC_ASSERT(!aOptions.mReplaceBrowsingContext || IsTop(),
   2515                        "Cannot replace BrowsingContext for subframes");
   2516  MOZ_DIAGNOSTIC_ASSERT(
   2517      aOptions.mSpecificGroupId == 0 || aOptions.mReplaceBrowsingContext,
   2518      "Cannot specify group ID unless replacing BC");
   2519  MOZ_DIAGNOSTIC_ASSERT(aPendingSwitchId || !IsTop(),
   2520                        "Should always have aPendingSwitchId for top-level "
   2521                        "frames");
   2522 
   2523  if (!AncestorsAreCurrent()) {
   2524    NS_WARNING("An ancestor context is no longer current");
   2525    return RemotenessPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
   2526  }
   2527 
   2528  // Ensure our embedder hasn't been destroyed or asked to shutdown already.
   2529  RefPtr<WindowGlobalParent> embedderWindowGlobal = GetEmbedderWindowGlobal();
   2530  if (!embedderWindowGlobal) {
   2531    NS_WARNING("Non-embedded BrowsingContext");
   2532    return RemotenessPromise::CreateAndReject(NS_ERROR_UNEXPECTED, __func__);
   2533  }
   2534 
   2535  if (!embedderWindowGlobal->CanSend()) {
   2536    NS_WARNING("Embedder already been destroyed.");
   2537    return RemotenessPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__);
   2538  }
   2539 
   2540  RefPtr<BrowserParent> embedderBrowser =
   2541      embedderWindowGlobal->GetBrowserParent();
   2542  if (embedderBrowser && embedderBrowser->Manager()->IsShuttingDown()) {
   2543    NS_WARNING("Embedder already asked to shutdown.");
   2544    return RemotenessPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__);
   2545  }
   2546 
   2547  if (aOptions.mRemoteType.IsEmpty() && (!IsTop() || !GetEmbedderElement())) {
   2548    NS_WARNING("Cannot load non-remote subframes");
   2549    return RemotenessPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
   2550  }
   2551 
   2552  // Cancel ongoing remoteness changes.
   2553  if (mPendingRemotenessChange) {
   2554    mPendingRemotenessChange->Cancel(NS_ERROR_ABORT);
   2555    MOZ_DIAGNOSTIC_ASSERT(!mPendingRemotenessChange, "Should have cleared");
   2556  }
   2557 
   2558  auto promise = MakeRefPtr<RemotenessPromise::Private>(__func__);
   2559  promise->UseDirectTaskDispatch(__func__);
   2560 
   2561  RefPtr<PendingRemotenessChange> change =
   2562      new PendingRemotenessChange(this, promise, aPendingSwitchId, aOptions);
   2563  mPendingRemotenessChange = change;
   2564 
   2565  // If we're replacing BrowsingContext, determine which BrowsingContextGroup
   2566  // we'll switch into, taking into account load options.
   2567  if (aOptions.mReplaceBrowsingContext) {
   2568    change->mSpecificGroup =
   2569        aOptions.mSpecificGroupId
   2570            ? BrowsingContextGroup::GetOrCreate(aOptions.mSpecificGroupId)
   2571            : BrowsingContextGroup::Create(aOptions.mShouldCrossOriginIsolate);
   2572    change->mSpecificGroup->AddKeepAlive();
   2573  }
   2574 
   2575  // Call `prepareToChangeRemoteness` in parallel with starting a new process
   2576  // for <browser> loads.
   2577  if (IsTop() && GetEmbedderElement()) {
   2578    nsCOMPtr<nsIBrowser> browser = GetEmbedderElement()->AsBrowser();
   2579    if (!browser) {
   2580      change->Cancel(NS_ERROR_FAILURE);
   2581      return promise.forget();
   2582    }
   2583 
   2584    RefPtr<Promise> blocker;
   2585    nsresult rv = browser->PrepareToChangeRemoteness(getter_AddRefs(blocker));
   2586    if (NS_FAILED(rv)) {
   2587      change->Cancel(rv);
   2588      return promise.forget();
   2589    }
   2590 
   2591    // Mark prepareToChange as unresolved, and wait for it to become resolved.
   2592    if (blocker && blocker->State() != Promise::PromiseState::Resolved) {
   2593      change->mWaitingForPrepareToChange = true;
   2594      blocker->AddCallbacksWithCycleCollectedArgs(
   2595          [change](JSContext*, JS::Handle<JS::Value>, ErrorResult&)
   2596              MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA {
   2597                change->mWaitingForPrepareToChange = false;
   2598                change->MaybeFinish();
   2599              },
   2600          [change](JSContext*, JS::Handle<JS::Value> aValue, ErrorResult&) {
   2601            change->Cancel(
   2602                Promise::TryExtractNSResultFromRejectionValue(aValue));
   2603          });
   2604    }
   2605  }
   2606 
   2607  // Switching a subframe to be local within it's embedding process.
   2608  if (embedderBrowser &&
   2609      aOptions.mRemoteType == embedderBrowser->Manager()->GetRemoteType()) {
   2610    MOZ_DIAGNOSTIC_ASSERT(
   2611        aPendingSwitchId,
   2612        "We always have a PendingSwitchId, except for print-preview loads, "
   2613        "which will never perform a process-switch to being in-process with "
   2614        "their embedder");
   2615    MOZ_DIAGNOSTIC_ASSERT(!aOptions.mReplaceBrowsingContext);
   2616    MOZ_DIAGNOSTIC_ASSERT(!aOptions.mRemoteType.IsEmpty());
   2617    MOZ_DIAGNOSTIC_ASSERT(!change->mWaitingForPrepareToChange);
   2618    MOZ_DIAGNOSTIC_ASSERT(!change->mSpecificGroup);
   2619 
   2620    // Switching to local, so we don't need to create a new process, and will
   2621    // instead use our embedder process.
   2622    change->mContentParentKeepAlive =
   2623        embedderBrowser->Manager()->AddKeepAlive(BrowserId());
   2624    change->ProcessLaunched();
   2625    return promise.forget();
   2626  }
   2627 
   2628  // Switching to the parent process.
   2629  if (aOptions.mRemoteType.IsEmpty()) {
   2630    change->ProcessLaunched();
   2631    return promise.forget();
   2632  }
   2633 
   2634  // If we're aiming to end up in a new process of the same type as our old
   2635  // process, and then putting our previous document in the BFCache, try to stay
   2636  // in the same process to avoid creating new processes unnecessarily.
   2637  RefPtr<ContentParent> existingProcess = GetContentParent();
   2638  if (existingProcess && !existingProcess->IsShuttingDown() &&
   2639      aOptions.mReplaceBrowsingContext &&
   2640      aOptions.mRemoteType == existingProcess->GetRemoteType()) {
   2641    change->mContentParentKeepAlive =
   2642        existingProcess->AddKeepAlive(BrowserId());
   2643    change->ProcessLaunched();
   2644    return promise.forget();
   2645  }
   2646 
   2647  // Try to predict which BrowsingContextGroup will be used for the final load
   2648  // in this BrowsingContext. This has to be accurate if switching into an
   2649  // existing group, as it will control what pool of processes will be used
   2650  // for process selection.
   2651  //
   2652  // It's _technically_ OK to provide a group here if we're actually going to
   2653  // switch into a brand new group, though it's sub-optimal, as it can
   2654  // restrict the set of processes we're using.
   2655  BrowsingContextGroup* finalGroup =
   2656      aOptions.mReplaceBrowsingContext ? change->mSpecificGroup.get() : Group();
   2657 
   2658  bool preferUsed =
   2659      StaticPrefs::browser_tabs_remote_subframesPreferUsed() && !IsTop();
   2660 
   2661  change->mContentParentKeepAlive =
   2662      ContentParent::GetNewOrUsedLaunchingBrowserProcess(
   2663          /* aRemoteType = */ aOptions.mRemoteType,
   2664          /* aGroup = */ finalGroup,
   2665          /* aPriority = */ hal::PROCESS_PRIORITY_FOREGROUND,
   2666          /* aPreferUsed = */ preferUsed,
   2667          /* aBrowserId */ BrowserId());
   2668  if (!change->mContentParentKeepAlive) {
   2669    change->Cancel(NS_ERROR_FAILURE);
   2670    return promise.forget();
   2671  }
   2672 
   2673  if (change->mContentParentKeepAlive->IsLaunching()) {
   2674    change->mContentParentKeepAlive
   2675        ->WaitForLaunchAsync(/* aPriority */ hal::PROCESS_PRIORITY_FOREGROUND,
   2676                             /* aBrowserId */ BrowserId())
   2677        ->Then(
   2678            GetMainThreadSerialEventTarget(), __func__,
   2679            [change](UniqueContentParentKeepAlive&&)
   2680                MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA {
   2681                  change->ProcessLaunched();
   2682                },
   2683            [change]() { change->Cancel(NS_ERROR_FAILURE); });
   2684  } else {
   2685    change->ProcessLaunched();
   2686  }
   2687  return promise.forget();
   2688 }
   2689 
   2690 void CanonicalBrowsingContext::MaybeSetPermanentKey(Element* aEmbedder) {
   2691  MOZ_DIAGNOSTIC_ASSERT(IsTop());
   2692 
   2693  if (aEmbedder) {
   2694    if (nsCOMPtr<nsIBrowser> browser = aEmbedder->AsBrowser()) {
   2695      JS::Rooted<JS::Value> key(RootingCx());
   2696      if (NS_SUCCEEDED(browser->GetPermanentKey(&key)) && key.isObject()) {
   2697        mPermanentKey = key;
   2698      }
   2699    }
   2700  }
   2701 }
   2702 
   2703 MediaController* CanonicalBrowsingContext::GetMediaController() {
   2704  // We would only create one media controller per tab, so accessing the
   2705  // controller via the top-level browsing context.
   2706  if (GetParent()) {
   2707    return Cast(Top())->GetMediaController();
   2708  }
   2709 
   2710  MOZ_ASSERT(!GetParent(),
   2711             "Must access the controller from the top-level browsing context!");
   2712  // Only content browsing context can create media controller, we won't create
   2713  // controller for chrome document, such as the browser UI.
   2714  if (!mTabMediaController && !IsDiscarded() && IsContent()) {
   2715    mTabMediaController = new MediaController(Id());
   2716  }
   2717  return mTabMediaController;
   2718 }
   2719 
   2720 bool CanonicalBrowsingContext::HasCreatedMediaController() const {
   2721  return !!mTabMediaController;
   2722 }
   2723 
   2724 bool CanonicalBrowsingContext::SupportsLoadingInParent(
   2725    nsDocShellLoadState* aLoadState, uint64_t* aOuterWindowId) {
   2726  // We currently don't support initiating loads in the parent when they are
   2727  // watched by devtools. This is because devtools tracks loads using content
   2728  // process notifications, which happens after the load is initiated in this
   2729  // case. Devtools clears all prior requests when it detects a new navigation,
   2730  // so it drops the main document load that happened here.
   2731  if (WatchedByDevTools()) {
   2732    return false;
   2733  }
   2734 
   2735  // Session-history-in-parent implementation relies currently on getting a
   2736  // round trip through a child process.
   2737  if (aLoadState->LoadIsFromSessionHistory()) {
   2738    return false;
   2739  }
   2740 
   2741  // DocumentChannel currently only supports connecting channels into the
   2742  // content process, so we can only support schemes that will always be loaded
   2743  // there for now. Restrict to just http(s) for simplicity.
   2744  if (!net::SchemeIsHttpOrHttps(aLoadState->URI())) {
   2745    return false;
   2746  }
   2747 
   2748  if (WindowGlobalParent* global = GetCurrentWindowGlobal()) {
   2749    nsCOMPtr<nsIURI> currentURI = global->GetDocumentURI();
   2750    if (currentURI) {
   2751      nsCOMPtr<nsIURI> uri = aLoadState->URI();
   2752      bool newURIHasRef = false;
   2753      uri->GetHasRef(&newURIHasRef);
   2754      bool equalsExceptRef = false;
   2755      uri->EqualsExceptRef(currentURI, &equalsExceptRef);
   2756 
   2757      if (equalsExceptRef && newURIHasRef) {
   2758        // This navigation is same-doc WRT the current one, we should pass it
   2759        // down to the docshell to be handled.
   2760        return false;
   2761      }
   2762    }
   2763 
   2764    // If unloading the current document will cause a beforeunload listener to
   2765    // run, then we need to start the load in that process after we fire the
   2766    // event.
   2767    if (PreOrderWalkFlag([&](BrowsingContext* aBC) {
   2768          WindowContext* wc = aBC->GetCurrentWindowContext();
   2769          if (wc && wc->NeedsBeforeUnload()) {
   2770            // We can stop as soon as we know at least one beforeunload listener
   2771            // exists.
   2772            return WalkFlag::Stop;
   2773          }
   2774          return WalkFlag::Next;
   2775        }) == WalkFlag::Stop) {
   2776      return false;
   2777    }
   2778 
   2779    *aOuterWindowId = global->OuterWindowId();
   2780  }
   2781  return true;
   2782 }
   2783 
   2784 bool CanonicalBrowsingContext::AttemptSpeculativeLoadInParent(
   2785    nsDocShellLoadState* aLoadState) {
   2786  // We currently only support starting loads directly from the
   2787  // CanonicalBrowsingContext for top-level BCs.
   2788  // We currently only support starting loads directly from the
   2789  // CanonicalBrowsingContext for top-level BCs.
   2790  if (!IsTopContent() || !GetContentParent()) {
   2791    return false;
   2792  }
   2793 
   2794  uint64_t outerWindowId = 0;
   2795  if (!SupportsLoadingInParent(aLoadState, &outerWindowId)) {
   2796    return false;
   2797  }
   2798 
   2799  // If we successfully open the DocumentChannel, then it'll register
   2800  // itself using aLoadIdentifier and be kept alive until it completes
   2801  // loading.
   2802  return net::DocumentLoadListener::SpeculativeLoadInParent(this, aLoadState);
   2803 }
   2804 
   2805 bool CanonicalBrowsingContext::StartDocumentLoad(
   2806    net::DocumentLoadListener* aLoad) {
   2807  mCurrentLoad = aLoad;
   2808 
   2809  if (NS_FAILED(SetCurrentLoadIdentifier(Some(aLoad->GetLoadIdentifier())))) {
   2810    mCurrentLoad = nullptr;
   2811    return false;
   2812  }
   2813 
   2814  return true;
   2815 }
   2816 
   2817 void CanonicalBrowsingContext::EndDocumentLoad(bool aContinueNavigating) {
   2818  mCurrentLoad = nullptr;
   2819 
   2820  if (!aContinueNavigating) {
   2821    // Resetting the current load identifier on a discarded context
   2822    // has no effect when a document load has finished.
   2823    (void)SetCurrentLoadIdentifier(Nothing());
   2824  }
   2825 }
   2826 
   2827 already_AddRefed<nsIURI> CanonicalBrowsingContext::GetCurrentURI() const {
   2828  nsCOMPtr<nsIURI> currentURI;
   2829  if (nsIDocShell* docShell = GetDocShell()) {
   2830    MOZ_ALWAYS_SUCCEEDS(
   2831        nsDocShell::Cast(docShell)->GetCurrentURI(getter_AddRefs(currentURI)));
   2832  } else {
   2833    currentURI = mCurrentRemoteURI;
   2834  }
   2835  return currentURI.forget();
   2836 }
   2837 
   2838 void CanonicalBrowsingContext::SetCurrentRemoteURI(nsIURI* aCurrentRemoteURI) {
   2839  MOZ_ASSERT(!GetDocShell());
   2840  mCurrentRemoteURI = aCurrentRemoteURI;
   2841 }
   2842 
   2843 void CanonicalBrowsingContext::ResetSHEntryHasUserInteractionCache() {
   2844  WindowContext* topWc = GetTopWindowContext();
   2845  if (topWc && !topWc->IsDiscarded()) {
   2846    MOZ_ALWAYS_SUCCEEDS(topWc->SetSHEntryHasUserInteraction(false));
   2847  }
   2848 }
   2849 
   2850 void CanonicalBrowsingContext::HistoryCommitIndexAndLength() {
   2851  nsID changeID = {};
   2852  CallerWillNotifyHistoryIndexAndLengthChanges caller(nullptr);
   2853  HistoryCommitIndexAndLength(changeID, caller);
   2854 }
   2855 void CanonicalBrowsingContext::HistoryCommitIndexAndLength(
   2856    const nsID& aChangeID,
   2857    const CallerWillNotifyHistoryIndexAndLengthChanges& aProofOfCaller) {
   2858  if (!IsTop()) {
   2859    Cast(Top())->HistoryCommitIndexAndLength(aChangeID, aProofOfCaller);
   2860    return;
   2861  }
   2862 
   2863  nsISHistory* shistory = GetSessionHistory();
   2864  if (!shistory) {
   2865    return;
   2866  }
   2867  int32_t index = 0;
   2868  shistory->GetIndex(&index);
   2869  int32_t length = shistory->GetCount();
   2870 
   2871  GetChildSessionHistory()->SetIndexAndLength(index, length, aChangeID);
   2872 
   2873  shistory->EvictOutOfRangeDocumentViewers(index);
   2874 
   2875  Group()->EachParent([&](ContentParent* aParent) {
   2876    (void)aParent->SendHistoryCommitIndexAndLength(this, index, length,
   2877                                                   aChangeID);
   2878  });
   2879 }
   2880 
   2881 void CanonicalBrowsingContext::SynchronizeLayoutHistoryState() {
   2882  if (mActiveEntry) {
   2883    if (IsInProcess()) {
   2884      nsIDocShell* docShell = GetDocShell();
   2885      if (docShell) {
   2886        docShell->PersistLayoutHistoryState();
   2887 
   2888        nsCOMPtr<nsILayoutHistoryState> state;
   2889        docShell->GetLayoutHistoryState(getter_AddRefs(state));
   2890        if (state) {
   2891          mActiveEntry->SetLayoutHistoryState(state);
   2892        }
   2893      }
   2894    } else if (ContentParent* cp = GetContentParent()) {
   2895      cp->SendGetLayoutHistoryState(this)->Then(
   2896          GetCurrentSerialEventTarget(), __func__,
   2897          [activeEntry = mActiveEntry](
   2898              const std::tuple<RefPtr<nsILayoutHistoryState>, Maybe<Wireframe>>&
   2899                  aResult) {
   2900            if (std::get<0>(aResult)) {
   2901              activeEntry->SetLayoutHistoryState(std::get<0>(aResult));
   2902            }
   2903            if (std::get<1>(aResult)) {
   2904              activeEntry->SetWireframe(std::get<1>(aResult));
   2905            }
   2906          },
   2907          []() {});
   2908    }
   2909  }
   2910 }
   2911 
   2912 void CanonicalBrowsingContext::SynchronizeNavigationAPIState(
   2913    nsIStructuredCloneContainer* aState) {
   2914  if (mActiveEntry) {
   2915    mActiveEntry->SetNavigationAPIState(aState);
   2916  }
   2917 }
   2918 
   2919 void CanonicalBrowsingContext::ResetScalingZoom() {
   2920  // This currently only ever gets called in the parent process, and we
   2921  // pass the message on to the WindowGlobalChild for the rootmost browsing
   2922  // context.
   2923  if (WindowGlobalParent* topWindow = GetTopWindowContext()) {
   2924    (void)topWindow->SendResetScalingZoom();
   2925  }
   2926 }
   2927 
   2928 void CanonicalBrowsingContext::SetRestoreData(SessionStoreRestoreData* aData,
   2929                                              ErrorResult& aError) {
   2930  MOZ_DIAGNOSTIC_ASSERT(aData);
   2931 
   2932  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
   2933  RefPtr<Promise> promise = Promise::Create(global, aError);
   2934  if (aError.Failed()) {
   2935    return;
   2936  }
   2937 
   2938  if (NS_WARN_IF(NS_FAILED(SetHasRestoreData(true)))) {
   2939    aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   2940    return;
   2941  }
   2942 
   2943  mRestoreState = new RestoreState();
   2944  mRestoreState->mData = aData;
   2945  mRestoreState->mPromise = promise;
   2946 }
   2947 
   2948 already_AddRefed<Promise> CanonicalBrowsingContext::GetRestorePromise() {
   2949  if (mRestoreState) {
   2950    return do_AddRef(mRestoreState->mPromise);
   2951  }
   2952  return nullptr;
   2953 }
   2954 
   2955 void CanonicalBrowsingContext::ClearRestoreState() {
   2956  if (IsDiscarded()) {
   2957    return;
   2958  }
   2959 
   2960  if (!mRestoreState) {
   2961    MOZ_DIAGNOSTIC_ASSERT(!GetHasRestoreData());
   2962    return;
   2963  }
   2964  if (mRestoreState->mPromise) {
   2965    mRestoreState->mPromise->MaybeRejectWithUndefined();
   2966  }
   2967  mRestoreState = nullptr;
   2968 
   2969  MOZ_ALWAYS_SUCCEEDS(SetHasRestoreData(false));
   2970 }
   2971 
   2972 void CanonicalBrowsingContext::RequestRestoreTabContent(
   2973    WindowGlobalParent* aWindow) {
   2974  MOZ_DIAGNOSTIC_ASSERT(IsTop());
   2975 
   2976  if (IsDiscarded() || !mRestoreState || !mRestoreState->mData) {
   2977    return;
   2978  }
   2979 
   2980  CanonicalBrowsingContext* context = aWindow->GetBrowsingContext();
   2981  MOZ_DIAGNOSTIC_ASSERT(!context->IsDiscarded());
   2982 
   2983  RefPtr<SessionStoreRestoreData> data =
   2984      mRestoreState->mData->FindDataForChild(context);
   2985 
   2986  if (context->IsTop()) {
   2987    MOZ_DIAGNOSTIC_ASSERT(context == this);
   2988 
   2989    // We need to wait until the appropriate load event has fired before we
   2990    // can "complete" the restore process, so if we're holding an empty data
   2991    // object, just resolve the promise immediately.
   2992    if (mRestoreState->mData->IsEmpty()) {
   2993      MOZ_DIAGNOSTIC_ASSERT(!data || data->IsEmpty());
   2994      mRestoreState->Resolve();
   2995      ClearRestoreState();
   2996      return;
   2997    }
   2998 
   2999    // Since we're following load event order, we'll only arrive here for a
   3000    // toplevel context after we've already sent down data for all child frames,
   3001    // so it's safe to clear this reference now. The completion callback below
   3002    // relies on the mData field being null to determine if all requests have
   3003    // been sent out.
   3004    mRestoreState->ClearData();
   3005    MOZ_ALWAYS_SUCCEEDS(SetHasRestoreData(false));
   3006  }
   3007 
   3008  if (data && !data->IsEmpty()) {
   3009    auto onTabRestoreComplete = [self = RefPtr{this},
   3010                                 state = RefPtr{mRestoreState}](auto) {
   3011      state->mResolves++;
   3012      if (!state->mData && state->mRequests == state->mResolves) {
   3013        state->Resolve();
   3014        if (state == self->mRestoreState) {
   3015          self->ClearRestoreState();
   3016        }
   3017      }
   3018    };
   3019 
   3020    mRestoreState->mRequests++;
   3021 
   3022    if (data->CanRestoreInto(aWindow->GetDocumentURI())) {
   3023      if (!aWindow->IsInProcess()) {
   3024        aWindow->SendRestoreTabContent(WrapNotNull(data.get()),
   3025                                       onTabRestoreComplete,
   3026                                       onTabRestoreComplete);
   3027        return;
   3028      }
   3029      data->RestoreInto(context);
   3030    }
   3031 
   3032    // This must be called both when we're doing an in-process restore, and when
   3033    // we didn't do a restore at all due to a URL mismatch.
   3034    onTabRestoreComplete(true);
   3035  }
   3036 }
   3037 
   3038 void CanonicalBrowsingContext::RestoreState::Resolve() {
   3039  MOZ_DIAGNOSTIC_ASSERT(mPromise);
   3040  mPromise->MaybeResolveWithUndefined();
   3041  mPromise = nullptr;
   3042 }
   3043 
   3044 nsresult CanonicalBrowsingContext::WriteSessionStorageToSessionStore(
   3045    const nsTArray<SSCacheCopy>& aSesssionStorage, uint32_t aEpoch) {
   3046  nsCOMPtr<nsISessionStoreFunctions> sessionStoreFuncs =
   3047      do_GetService("@mozilla.org/toolkit/sessionstore-functions;1");
   3048  if (!sessionStoreFuncs) {
   3049    return NS_ERROR_FAILURE;
   3050  }
   3051 
   3052  nsCOMPtr<nsIXPConnectWrappedJS> wrapped =
   3053      do_QueryInterface(sessionStoreFuncs);
   3054  AutoJSAPI jsapi;
   3055  if (!jsapi.Init(wrapped->GetJSObjectGlobal())) {
   3056    return NS_ERROR_FAILURE;
   3057  }
   3058 
   3059  JS::Rooted<JS::Value> key(jsapi.cx(), Top()->PermanentKey());
   3060 
   3061  Record<nsCString, Record<nsString, nsString>> storage;
   3062  JS::Rooted<JS::Value> update(jsapi.cx());
   3063 
   3064  if (!aSesssionStorage.IsEmpty()) {
   3065    SessionStoreUtils::ConstructSessionStorageValues(this, aSesssionStorage,
   3066                                                     storage);
   3067    if (!ToJSValue(jsapi.cx(), storage, &update)) {
   3068      return NS_ERROR_FAILURE;
   3069    }
   3070  } else {
   3071    update.setNull();
   3072  }
   3073 
   3074  return sessionStoreFuncs->UpdateSessionStoreForStorage(
   3075      Top()->GetEmbedderElement(), this, key, aEpoch, update);
   3076 }
   3077 
   3078 void CanonicalBrowsingContext::UpdateSessionStoreSessionStorage(
   3079    const std::function<void()>& aDone) {
   3080  using DataPromise = BackgroundSessionStorageManager::DataPromise;
   3081  BackgroundSessionStorageManager::GetData(
   3082      this, StaticPrefs::browser_sessionstore_dom_storage_limit(),
   3083      /* aClearSessionStoreTimer = */ true)
   3084      ->Then(GetCurrentSerialEventTarget(), __func__,
   3085             [self = RefPtr{this}, aDone, epoch = GetSessionStoreEpoch()](
   3086                 const DataPromise::ResolveOrRejectValue& valueList) {
   3087               if (valueList.IsResolve()) {
   3088                 self->WriteSessionStorageToSessionStore(
   3089                     valueList.ResolveValue(), epoch);
   3090               }
   3091               aDone();
   3092             });
   3093 }
   3094 
   3095 /* static */
   3096 void CanonicalBrowsingContext::UpdateSessionStoreForStorage(
   3097    uint64_t aBrowsingContextId) {
   3098  RefPtr<CanonicalBrowsingContext> browsingContext = Get(aBrowsingContextId);
   3099 
   3100  if (!browsingContext) {
   3101    return;
   3102  }
   3103 
   3104  browsingContext->UpdateSessionStoreSessionStorage([]() {});
   3105 }
   3106 
   3107 void CanonicalBrowsingContext::MaybeScheduleSessionStoreUpdate() {
   3108  if (!SessionStorePlatformCollection()) {
   3109    return;
   3110  }
   3111 
   3112  if (!IsTop()) {
   3113    Top()->MaybeScheduleSessionStoreUpdate();
   3114    return;
   3115  }
   3116 
   3117  if (IsInBFCache()) {
   3118    return;
   3119  }
   3120 
   3121  if (mSessionStoreSessionStorageUpdateTimer) {
   3122    return;
   3123  }
   3124 
   3125  if (!StaticPrefs::browser_sessionstore_debug_no_auto_updates()) {
   3126    auto result = NS_NewTimerWithFuncCallback(
   3127        [](nsITimer*, void* aClosure) {
   3128          auto* context = static_cast<CanonicalBrowsingContext*>(aClosure);
   3129          context->UpdateSessionStoreSessionStorage([]() {});
   3130        },
   3131        this, StaticPrefs::browser_sessionstore_interval(),
   3132        nsITimer::TYPE_ONE_SHOT,
   3133        "CanonicalBrowsingContext::MaybeScheduleSessionStoreUpdate"_ns);
   3134 
   3135    if (result.isErr()) {
   3136      return;
   3137    }
   3138 
   3139    mSessionStoreSessionStorageUpdateTimer = result.unwrap();
   3140  }
   3141 }
   3142 
   3143 void CanonicalBrowsingContext::CancelSessionStoreUpdate() {
   3144  if (mSessionStoreSessionStorageUpdateTimer) {
   3145    mSessionStoreSessionStorageUpdateTimer->Cancel();
   3146    mSessionStoreSessionStorageUpdateTimer = nullptr;
   3147  }
   3148 }
   3149 
   3150 void CanonicalBrowsingContext::SetContainerFeaturePolicy(
   3151    Maybe<FeaturePolicyInfo>&& aContainerFeaturePolicyInfo) {
   3152  mContainerFeaturePolicyInfo = std::move(aContainerFeaturePolicyInfo);
   3153 }
   3154 
   3155 already_AddRefed<CanonicalBrowsingContext>
   3156 CanonicalBrowsingContext::GetCrossGroupOpener() const {
   3157  return Get(mCrossGroupOpenerId);
   3158 }
   3159 
   3160 void CanonicalBrowsingContext::SetCrossGroupOpenerId(uint64_t aOpenerId) {
   3161  MOZ_DIAGNOSTIC_ASSERT(IsTopContent());
   3162  MOZ_DIAGNOSTIC_ASSERT(mCrossGroupOpenerId == 0,
   3163                        "Can only set CrossGroupOpenerId once");
   3164  mCrossGroupOpenerId = aOpenerId;
   3165 }
   3166 
   3167 void CanonicalBrowsingContext::SetCrossGroupOpener(
   3168    CanonicalBrowsingContext* aCrossGroupOpener, ErrorResult& aRv) {
   3169  if (!IsTopContent()) {
   3170    aRv.ThrowNotAllowedError(
   3171        "Can only set crossGroupOpener on toplevel content");
   3172    return;
   3173  }
   3174  if (mCrossGroupOpenerId != 0) {
   3175    aRv.ThrowNotAllowedError("Can only set crossGroupOpener once");
   3176    return;
   3177  }
   3178  if (!aCrossGroupOpener) {
   3179    aRv.ThrowNotAllowedError("Can't set crossGroupOpener to null");
   3180    return;
   3181  }
   3182 
   3183  SetCrossGroupOpenerId(aCrossGroupOpener->Id());
   3184 }
   3185 
   3186 auto CanonicalBrowsingContext::FindUnloadingHost(uint64_t aChildID)
   3187    -> nsTArray<UnloadingHost>::iterator {
   3188  return std::find_if(
   3189      mUnloadingHosts.begin(), mUnloadingHosts.end(),
   3190      [&](const auto& host) { return host.mChildID == aChildID; });
   3191 }
   3192 
   3193 void CanonicalBrowsingContext::ClearUnloadingHost(uint64_t aChildID) {
   3194  // Notify any callbacks which were waiting for the host to finish unloading
   3195  // that it has.
   3196  auto found = FindUnloadingHost(aChildID);
   3197  if (found != mUnloadingHosts.end()) {
   3198    auto callbacks = std::move(found->mCallbacks);
   3199    mUnloadingHosts.RemoveElementAt(found);
   3200    for (const auto& callback : callbacks) {
   3201      callback();
   3202    }
   3203  }
   3204 }
   3205 
   3206 void CanonicalBrowsingContext::StartUnloadingHost(uint64_t aChildID) {
   3207  MOZ_DIAGNOSTIC_ASSERT(FindUnloadingHost(aChildID) == mUnloadingHosts.end());
   3208  mUnloadingHosts.AppendElement(UnloadingHost{aChildID, {}});
   3209 }
   3210 
   3211 void CanonicalBrowsingContext::BrowserParentDestroyed(
   3212    BrowserParent* aBrowserParent, bool aAbnormalShutdown) {
   3213  ClearUnloadingHost(aBrowserParent->Manager()->ChildID());
   3214 
   3215  // Handling specific to when the current BrowserParent has been destroyed.
   3216  if (mCurrentBrowserParent == aBrowserParent) {
   3217    mCurrentBrowserParent = nullptr;
   3218 
   3219    // If this BrowserParent is for a subframe, attempt to recover from a
   3220    // subframe crash by rendering the subframe crashed page in the embedding
   3221    // content.
   3222    if (aAbnormalShutdown) {
   3223      ShowSubframeCrashedUI(aBrowserParent->GetBrowserBridgeParent());
   3224    }
   3225  }
   3226 }
   3227 
   3228 void CanonicalBrowsingContext::ShowSubframeCrashedUI(
   3229    BrowserBridgeParent* aBridge) {
   3230  if (!aBridge || IsDiscarded() || !aBridge->CanSend()) {
   3231    return;
   3232  }
   3233 
   3234  MOZ_DIAGNOSTIC_ASSERT(!aBridge->GetBrowsingContext() ||
   3235                        aBridge->GetBrowsingContext() == this);
   3236 
   3237  // There is no longer a current inner window within this
   3238  // BrowsingContext, update the `CurrentInnerWindowId` field to reflect
   3239  // this.
   3240  MOZ_ALWAYS_SUCCEEDS(SetCurrentInnerWindowId(0));
   3241 
   3242  // The owning process will now be the embedder to render the subframe
   3243  // crashed page, switch ownership back over.
   3244  SetOwnerProcessId(aBridge->Manager()->Manager()->ChildID());
   3245  SetCurrentBrowserParent(aBridge->Manager());
   3246 
   3247  (void)aBridge->SendSubFrameCrashed();
   3248 }
   3249 
   3250 static void LogBFCacheBlockingForDoc(BrowsingContext* aBrowsingContext,
   3251                                     uint32_t aBFCacheCombo, bool aIsSubDoc) {
   3252  if (aIsSubDoc) {
   3253    nsAutoCString uri("[no uri]");
   3254    nsCOMPtr<nsIURI> currentURI =
   3255        aBrowsingContext->Canonical()->GetCurrentURI();
   3256    if (currentURI) {
   3257      uri = currentURI->GetSpecOrDefault();
   3258    }
   3259    MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug,
   3260            (" ** Blocked for document %s", uri.get()));
   3261  }
   3262  if (aBFCacheCombo & BFCacheStatus::EVENT_HANDLING_SUPPRESSED) {
   3263    MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug,
   3264            (" * event handling suppression"));
   3265  }
   3266  if (aBFCacheCombo & BFCacheStatus::SUSPENDED) {
   3267    MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, (" * suspended Window"));
   3268  }
   3269  if (aBFCacheCombo & BFCacheStatus::UNLOAD_LISTENER) {
   3270    MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, (" * unload listener"));
   3271  }
   3272  if (aBFCacheCombo & BFCacheStatus::REQUEST) {
   3273    MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, (" * requests in the loadgroup"));
   3274  }
   3275  if (aBFCacheCombo & BFCacheStatus::ACTIVE_GET_USER_MEDIA) {
   3276    MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, (" * GetUserMedia"));
   3277  }
   3278  if (aBFCacheCombo & BFCacheStatus::ACTIVE_PEER_CONNECTION) {
   3279    MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, (" * PeerConnection"));
   3280  }
   3281  if (aBFCacheCombo & BFCacheStatus::CONTAINS_EME_CONTENT) {
   3282    MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, (" * EME content"));
   3283  }
   3284  if (aBFCacheCombo & BFCacheStatus::CONTAINS_MSE_CONTENT) {
   3285    MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, (" * MSE use"));
   3286  }
   3287  if (aBFCacheCombo & BFCacheStatus::HAS_ACTIVE_SPEECH_SYNTHESIS) {
   3288    MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, (" * Speech use"));
   3289  }
   3290  if (aBFCacheCombo & BFCacheStatus::HAS_USED_VR) {
   3291    MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, (" * used VR"));
   3292  }
   3293  if (aBFCacheCombo & BFCacheStatus::BEFOREUNLOAD_LISTENER) {
   3294    MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, (" * beforeunload listener"));
   3295  }
   3296  if (aBFCacheCombo & BFCacheStatus::ACTIVE_LOCK) {
   3297    MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, (" * has active Web Locks"));
   3298  }
   3299  if (aBFCacheCombo & BFCacheStatus::PAGE_LOADING) {
   3300    MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, (" * has page loading"));
   3301  }
   3302 }
   3303 
   3304 bool CanonicalBrowsingContext::AllowedInBFCache(
   3305    const Maybe<uint64_t>& aChannelId, nsIURI* aNewURI) {
   3306  if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Debug))) {
   3307    nsAutoCString uri("[no uri]");
   3308    nsCOMPtr<nsIURI> currentURI = GetCurrentURI();
   3309    if (currentURI) {
   3310      uri = currentURI->GetSpecOrDefault();
   3311    }
   3312    MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, ("Checking %s", uri.get()));
   3313  }
   3314 
   3315  if (IsInProcess()) {
   3316    return false;
   3317  }
   3318 
   3319  uint32_t bfcacheCombo = 0;
   3320  if (mRestoreState) {
   3321    bfcacheCombo |= BFCacheStatus::RESTORING;
   3322    MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, (" * during session restore"));
   3323  }
   3324 
   3325  if (Group()->Toplevels().Length() > 1) {
   3326    bfcacheCombo |= BFCacheStatus::NOT_ONLY_TOPLEVEL_IN_BCG;
   3327    MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug,
   3328            (" * auxiliary BrowsingContexts"));
   3329  }
   3330 
   3331  // There are not a lot of about:* pages that are allowed to load in
   3332  // subframes, so it's OK to allow those few about:* pages enter BFCache.
   3333  MOZ_ASSERT(IsTop(), "Trying to put a non top level BC into BFCache");
   3334 
   3335  WindowGlobalParent* wgp = GetCurrentWindowGlobal();
   3336  if (wgp && wgp->GetDocumentURI()) {
   3337    nsCOMPtr<nsIURI> currentURI = wgp->GetDocumentURI();
   3338    // Exempt about:* pages from bfcache, with the exception of about:blank
   3339    if (currentURI->SchemeIs("about") &&
   3340        !NS_IsAboutBlankAllowQueryAndFragment(currentURI)) {
   3341      bfcacheCombo |= BFCacheStatus::ABOUT_PAGE;
   3342      MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, (" * about:* page"));
   3343    }
   3344 
   3345    if (aNewURI) {
   3346      bool equalUri = false;
   3347      aNewURI->Equals(currentURI, &equalUri);
   3348      if (equalUri) {
   3349        // When loading the same uri, disable bfcache so that
   3350        // nsDocShell::OnNewURI transforms the load to LOAD_NORMAL_REPLACE.
   3351        return false;
   3352      }
   3353    }
   3354  }
   3355 
   3356  // For telemetry we're collecting all the flags for all the BCs hanging
   3357  // from this top-level BC.
   3358  PreOrderWalk([&](BrowsingContext* aBrowsingContext) {
   3359    WindowGlobalParent* wgp =
   3360        aBrowsingContext->Canonical()->GetCurrentWindowGlobal();
   3361    uint32_t subDocBFCacheCombo = wgp ? wgp->GetBFCacheStatus() : 0;
   3362    if (wgp) {
   3363      const Maybe<uint64_t>& singleChannelId = wgp->GetSingleChannelId();
   3364      if (singleChannelId.isSome()) {
   3365        if (singleChannelId.value() == 0 || aChannelId.isNothing() ||
   3366            singleChannelId.value() != aChannelId.value()) {
   3367          subDocBFCacheCombo |= BFCacheStatus::REQUEST;
   3368        }
   3369      }
   3370    }
   3371 
   3372    if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Debug))) {
   3373      LogBFCacheBlockingForDoc(aBrowsingContext, subDocBFCacheCombo,
   3374                               aBrowsingContext != this);
   3375    }
   3376 
   3377    bfcacheCombo |= subDocBFCacheCombo;
   3378  });
   3379 
   3380  nsDocShell::ReportBFCacheComboTelemetry(bfcacheCombo);
   3381  if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Debug))) {
   3382    nsAutoCString uri("[no uri]");
   3383    nsCOMPtr<nsIURI> currentURI = GetCurrentURI();
   3384    if (currentURI) {
   3385      uri = currentURI->GetSpecOrDefault();
   3386    }
   3387    MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug,
   3388            (" +> %s %s be blocked from going into the BFCache", uri.get(),
   3389             bfcacheCombo == 0 ? "shouldn't" : "should"));
   3390  }
   3391 
   3392  if (StaticPrefs::docshell_shistory_bfcache_allow_unload_listeners()) {
   3393    bfcacheCombo &= ~BFCacheStatus::UNLOAD_LISTENER;
   3394  }
   3395 
   3396  return bfcacheCombo == 0;
   3397 }
   3398 
   3399 struct ClearSiteWalkHistoryData {
   3400  nsIPrincipal* mPrincipal = nullptr;
   3401  bool mShouldClear = false;
   3402 };
   3403 
   3404 // static
   3405 nsresult CanonicalBrowsingContext::ContainsSameOriginBfcacheEntry(
   3406    nsISHEntry* aEntry, mozilla::dom::BrowsingContext* aBC, int32_t aChildIndex,
   3407    void* aData) {
   3408  if (!aEntry) {
   3409    return NS_OK;
   3410  }
   3411 
   3412  nsCOMPtr<nsIPrincipal> entryPrincipal;
   3413  nsresult rv =
   3414      aEntry->GetPartitionedPrincipalToInherit(getter_AddRefs(entryPrincipal));
   3415 
   3416  if (NS_FAILED(rv) || !entryPrincipal) {
   3417    return NS_OK;
   3418  }
   3419 
   3420  ClearSiteWalkHistoryData* data =
   3421      static_cast<ClearSiteWalkHistoryData*>(aData);
   3422  if (data->mPrincipal->OriginAttributesRef() ==
   3423      entryPrincipal->OriginAttributesRef()) {
   3424    nsCOMPtr<nsIURI> entryURI = aEntry->GetURI();
   3425    if (data->mPrincipal->IsSameOrigin(entryURI)) {
   3426      data->mShouldClear = true;
   3427    } else {
   3428      nsSHistory::WalkHistoryEntries(aEntry, aBC,
   3429                                     ContainsSameOriginBfcacheEntry, aData);
   3430    }
   3431  }
   3432  return NS_OK;
   3433 }
   3434 
   3435 // static
   3436 nsresult CanonicalBrowsingContext::ClearBfcacheByPrincipal(
   3437    nsIPrincipal* aPrincipal) {
   3438  NS_ENSURE_ARG_POINTER(aPrincipal);
   3439  MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
   3440 
   3441  // Allow disabling the feature if unexpected regressions occur
   3442  if (!StaticPrefs::privacy_clearSiteDataHeader_cache_bfcache_enabled()) {
   3443    return NS_OK;
   3444  }
   3445 
   3446  // Iter through all open tabs by going through all top-level browsing
   3447  // contexts.
   3448  AutoTArray<RefPtr<BrowsingContextGroup>, 32> groups;
   3449  BrowsingContextGroup::GetAllGroups(groups);
   3450  for (auto& browsingContextGroup : groups) {
   3451    for (auto& topLevel : browsingContextGroup->Toplevels()) {
   3452      if (topLevel->IsDiscarded()) {
   3453        continue;
   3454      }
   3455 
   3456      auto* bc = topLevel->Canonical();
   3457      nsSHistory* sh = static_cast<nsSHistory*>(bc->GetSessionHistory());
   3458      if (!sh) {
   3459        continue;
   3460      }
   3461 
   3462      AutoTArray<nsCOMPtr<nsISHEntry>, 4> entriesToDelete;
   3463      // We only need to traverse all top-level history items due to bfcache
   3464      // only caching top level sites and partitioning origins. If an iframe has
   3465      // the same origin, we only want to clear it, if the top level has the
   3466      // same origin.
   3467      for (nsCOMPtr<nsISHEntry>& entry : sh->Entries()) {
   3468        // Determine whether this history entry matches the origin, or contains
   3469        // an iframe with that origin
   3470        ClearSiteWalkHistoryData data;
   3471        data.mPrincipal = aPrincipal;
   3472        CanonicalBrowsingContext::ContainsSameOriginBfcacheEntry(entry, nullptr,
   3473                                                                 0, &data);
   3474 
   3475        if (data.mShouldClear) {
   3476          entriesToDelete.AppendElement(entry);
   3477        }
   3478      }
   3479      for (nsCOMPtr<nsISHEntry>& entry : entriesToDelete) {
   3480        sh->EvictDocumentViewerForEntry(entry);
   3481      }
   3482    }
   3483  }
   3484  return NS_OK;
   3485 }
   3486 
   3487 void CanonicalBrowsingContext::SetIsActive(bool aIsActive, ErrorResult& aRv) {
   3488 #ifdef DEBUG
   3489  if (MOZ_UNLIKELY(!ManuallyManagesActiveness())) {
   3490    xpc_DumpJSStack(true, true, false);
   3491    MOZ_ASSERT_UNREACHABLE(
   3492        "Trying to manually manage activeness of a browsing context that isn't "
   3493        "manually managed (see manualactiveness attribute)");
   3494  }
   3495 #endif
   3496  SetIsActiveInternal(aIsActive, aRv);
   3497 }
   3498 
   3499 void CanonicalBrowsingContext::SetTouchEventsOverride(
   3500    dom::TouchEventsOverride aOverride, ErrorResult& aRv) {
   3501  SetTouchEventsOverrideInternal(aOverride, aRv);
   3502 }
   3503 
   3504 void CanonicalBrowsingContext::SetTargetTopLevelLinkClicksToBlank(
   3505    bool aTargetTopLevelLinkClicksToBlank, ErrorResult& aRv) {
   3506  SetTargetTopLevelLinkClicksToBlankInternal(aTargetTopLevelLinkClicksToBlank,
   3507                                             aRv);
   3508 }
   3509 
   3510 void CanonicalBrowsingContext::AddPageAwakeRequest() {
   3511  MOZ_ASSERT(IsTop());
   3512  auto count = GetPageAwakeRequestCount();
   3513  MOZ_ASSERT(count < UINT32_MAX);
   3514  (void)SetPageAwakeRequestCount(++count);
   3515 }
   3516 
   3517 void CanonicalBrowsingContext::RemovePageAwakeRequest() {
   3518  MOZ_ASSERT(IsTop());
   3519  auto count = GetPageAwakeRequestCount();
   3520  MOZ_ASSERT(count > 0);
   3521  (void)SetPageAwakeRequestCount(--count);
   3522 }
   3523 
   3524 void CanonicalBrowsingContext::CloneDocumentTreeInto(
   3525    CanonicalBrowsingContext* aSource, const nsACString& aRemoteType,
   3526    embedding::PrintData&& aPrintData) {
   3527  NavigationIsolationOptions options;
   3528  options.mRemoteType = aRemoteType;
   3529 
   3530  mClonePromise =
   3531      ChangeRemoteness(options, /* aPendingSwitchId = */ 0)
   3532          ->Then(
   3533              GetMainThreadSerialEventTarget(), __func__,
   3534              [source = MaybeDiscardedBrowsingContext{aSource},
   3535               data = std::move(aPrintData)](
   3536                  const std::pair<RefPtr<BrowserParent>,
   3537                                  RefPtr<CanonicalBrowsingContext>>& aResult)
   3538                  -> RefPtr<GenericNonExclusivePromise> {
   3539                const auto& [browserParent, browsingContext] = aResult;
   3540 
   3541                RefPtr<BrowserBridgeParent> bridge =
   3542                    browserParent->GetBrowserBridgeParent();
   3543                return browserParent
   3544                    ->SendCloneDocumentTreeIntoSelf(source, data)
   3545                    ->Then(
   3546                        GetMainThreadSerialEventTarget(), __func__,
   3547                        [bridge](
   3548                            BrowserParent::CloneDocumentTreeIntoSelfPromise::
   3549                                ResolveOrRejectValue&& aValue) {
   3550                          // We're cloning a remote iframe, so we created a
   3551                          // BrowserBridge which makes us register an OOP load
   3552                          // (see Document::OOPChildLoadStarted), even though
   3553                          // this isn't a real load. We call
   3554                          // SendMaybeFireEmbedderLoadEvents here so that we do
   3555                          // register the end of the load (see
   3556                          // Document::OOPChildLoadDone).
   3557                          if (bridge) {
   3558                            (void)bridge->SendMaybeFireEmbedderLoadEvents(
   3559                                EmbedderElementEventType::NoEvent);
   3560                          }
   3561                          if (aValue.IsResolve() && aValue.ResolveValue()) {
   3562                            return GenericNonExclusivePromise::CreateAndResolve(
   3563                                true, __func__);
   3564                          }
   3565                          return GenericNonExclusivePromise::CreateAndReject(
   3566                              NS_ERROR_FAILURE, __func__);
   3567                        });
   3568              },
   3569              [](nsresult aRv) -> RefPtr<GenericNonExclusivePromise> {
   3570                NS_WARNING(
   3571                    nsPrintfCString("Remote clone failed: %x\n", unsigned(aRv))
   3572                        .get());
   3573                return GenericNonExclusivePromise::CreateAndReject(
   3574                    NS_ERROR_FAILURE, __func__);
   3575              });
   3576 
   3577  mClonePromise->Then(
   3578      GetMainThreadSerialEventTarget(), __func__,
   3579      [self = RefPtr{this}]() { self->mClonePromise = nullptr; });
   3580 }
   3581 
   3582 bool CanonicalBrowsingContext::StartApzAutoscroll(float aAnchorX,
   3583                                                  float aAnchorY,
   3584                                                  nsViewID aScrollId,
   3585                                                  uint32_t aPresShellId) {
   3586  nsCOMPtr<nsIWidget> widget;
   3587  mozilla::layers::LayersId layersId{0};
   3588 
   3589  if (IsInProcess()) {
   3590    nsCOMPtr<nsPIDOMWindowOuter> outer = GetDOMWindow();
   3591    if (!outer) {
   3592      return false;
   3593    }
   3594 
   3595    widget = widget::WidgetUtils::DOMWindowToWidget(outer);
   3596    if (widget) {
   3597      layersId = widget->GetRootLayerTreeId();
   3598    }
   3599  } else {
   3600    RefPtr<BrowserParent> parent = GetBrowserParent();
   3601    if (!parent) {
   3602      return false;
   3603    }
   3604 
   3605    widget = parent->GetWidget();
   3606    layersId = parent->GetLayersId();
   3607  }
   3608 
   3609  if (!widget || !widget->AsyncPanZoomEnabled()) {
   3610    return false;
   3611  }
   3612 
   3613  // The anchor coordinates that are passed in are relative to the origin of the
   3614  // screen, but we are sending them to APZ which only knows about coordinates
   3615  // relative to the widget, so convert them accordingly.
   3616  const LayoutDeviceIntPoint anchor =
   3617      RoundedToInt(LayoutDevicePoint(aAnchorX, aAnchorY)) -
   3618      widget->WidgetToScreenOffset();
   3619 
   3620  mozilla::layers::ScrollableLayerGuid guid(layersId, aPresShellId, aScrollId);
   3621 
   3622  return widget->StartAsyncAutoscroll(
   3623      ViewAs<ScreenPixel>(
   3624          anchor, PixelCastJustification::LayoutDeviceIsScreenForBounds),
   3625      guid);
   3626 }
   3627 
   3628 void CanonicalBrowsingContext::StopApzAutoscroll(nsViewID aScrollId,
   3629                                                 uint32_t aPresShellId) {
   3630  nsCOMPtr<nsIWidget> widget;
   3631  mozilla::layers::LayersId layersId{0};
   3632 
   3633  if (IsInProcess()) {
   3634    nsCOMPtr<nsPIDOMWindowOuter> outer = GetDOMWindow();
   3635    if (!outer) {
   3636      return;
   3637    }
   3638 
   3639    widget = widget::WidgetUtils::DOMWindowToWidget(outer);
   3640    if (widget) {
   3641      layersId = widget->GetRootLayerTreeId();
   3642    }
   3643  } else {
   3644    RefPtr<BrowserParent> parent = GetBrowserParent();
   3645    if (!parent) {
   3646      return;
   3647    }
   3648 
   3649    widget = parent->GetWidget();
   3650    layersId = parent->GetLayersId();
   3651  }
   3652 
   3653  if (!widget || !widget->AsyncPanZoomEnabled()) {
   3654    return;
   3655  }
   3656 
   3657  mozilla::layers::ScrollableLayerGuid guid(layersId, aPresShellId, aScrollId);
   3658  widget->StopAsyncAutoscroll(guid);
   3659 }
   3660 
   3661 already_AddRefed<nsISHEntry>
   3662 CanonicalBrowsingContext::GetMostRecentLoadingSessionHistoryEntry() {
   3663  if (mLoadingEntries.IsEmpty()) {
   3664    return nullptr;
   3665  }
   3666 
   3667  RefPtr<SessionHistoryEntry> entry = mLoadingEntries.LastElement().mEntry;
   3668  return entry.forget();
   3669 }
   3670 
   3671 already_AddRefed<BounceTrackingState>
   3672 CanonicalBrowsingContext::GetBounceTrackingState() {
   3673  if (!mWebProgress) {
   3674    return nullptr;
   3675  }
   3676  return mWebProgress->GetBounceTrackingState();
   3677 }
   3678 
   3679 bool CanonicalBrowsingContext::CanOpenModalPicker() {
   3680  if (!mozilla::StaticPrefs::browser_disable_pickers_background_tabs()) {
   3681    return true;
   3682  }
   3683 
   3684  // Alway allows to open picker from chrome.
   3685  if (IsChrome()) {
   3686    return true;
   3687  }
   3688 
   3689  if (!IsActive()) {
   3690    return false;
   3691  }
   3692 
   3693  mozilla::dom::Element* topFrameElement = GetTopFrameElement();
   3694  if (!mozilla::StaticPrefs::
   3695          browser_disable_pickers_in_hidden_extension_pages() &&
   3696      Windowless()) {
   3697    WindowGlobalParent* wgp = GetCurrentWindowGlobal();
   3698    if (wgp && BasePrincipal::Cast(wgp->DocumentPrincipal())->AddonPolicy()) {
   3699      // This may be a HiddenExtensionPage, e.g. an extension background page.
   3700      return true;
   3701    }
   3702  }
   3703 
   3704  RefPtr<Document> chromeDoc = TopCrossChromeBoundary()->GetExtantDocument();
   3705  if (!chromeDoc || !chromeDoc->HasFocus(mozilla::IgnoreErrors())) {
   3706    return false;
   3707  }
   3708 
   3709  // Only allow web content to open a picker when it has focus. For example, if
   3710  // the focus is on the URL bar, web content cannot open a picker, even if it
   3711  // is the foreground tab.
   3712  // topFrameElement may be a <browser> embedded in another <browser>. In that
   3713  // case, verify that the full chain of <browser> elements has focus.
   3714  while (topFrameElement) {
   3715    RefPtr<Document> doc = topFrameElement->OwnerDoc();
   3716    if (doc->GetActiveElement() != topFrameElement) {
   3717      return false;
   3718    }
   3719    topFrameElement = doc->GetBrowsingContext()->GetTopFrameElement();
   3720    // Eventually topFrameElement == nullptr, implying that we have reached the
   3721    // top browser window (and chromeDoc == doc).
   3722  }
   3723  return true;
   3724 }
   3725 
   3726 bool CanonicalBrowsingContext::ShouldEnforceParentalControls() {
   3727  if (StaticPrefs::security_restrict_to_adults_always()) {
   3728    return true;
   3729  }
   3730  if (StaticPrefs::security_restrict_to_adults_respect_platform()) {
   3731    bool enabled;
   3732    nsCOMPtr<nsIParentalControlsService> pcs =
   3733        do_CreateInstance("@mozilla.org/parental-controls-service;1");
   3734    nsresult rv = pcs->GetParentalControlsEnabled(&enabled);
   3735    if (NS_FAILED(rv)) {
   3736      return false;
   3737    }
   3738    return enabled;
   3739  }
   3740  return false;
   3741 }
   3742 
   3743 void CanonicalBrowsingContext::MaybeReconstructActiveEntryList() {
   3744  MOZ_ASSERT(IsTop());
   3745  if (!Navigation::IsAPIEnabled()) {
   3746    return;
   3747  }
   3748 
   3749  auto* shistory = static_cast<nsSHistory*>(GetSessionHistory());
   3750  if (mActiveEntry && !shistory->ContainsEntry(mActiveEntry)) {
   3751    shistory->ReconstructContiguousEntryList();
   3752  }
   3753 }
   3754 
   3755 // https://html.spec.whatwg.org/#concept-internal-location-ancestor-origin-objects-list
   3756 // Creates the internal ancestor origins list (we store it on a canonical
   3757 // browsing context). `aThisDocumentPrincipal` represents the origin for the
   3758 // document who we are computing the list for, and
   3759 // `aFrameReferrerPolicyAttribute` is the referrer policy attribute on the frame
   3760 // that hosts the document.
   3761 // For normal navigations `aFrameReferrerPolicyAttribute` will have been
   3762 // snapshotted at a spec-appropriate time and passed in here, whereas
   3763 // about:blank can read the attribute directly without the attribute having time
   3764 // to change which makes the timing consistent with "normal" documents and for
   3765 // about:blank this happens in `ContentParent::RecvUpdateAncestorOriginsList`.
   3766 void CanonicalBrowsingContext::CreateRedactedAncestorOriginsList(
   3767    nsIPrincipal* aThisDocumentPrincipal,
   3768    ReferrerPolicy aFrameReferrerPolicyAttribute) {
   3769  MOZ_DIAGNOSTIC_ASSERT(aThisDocumentPrincipal);
   3770  nsTArray<nsCOMPtr<nsIPrincipal>> ancestorPrincipals;
   3771  // 4. if parentDoc is null, then return output
   3772  CanonicalBrowsingContext* parent = GetParent();
   3773  if (!parent) {
   3774    mPossiblyRedactedAncestorOriginsList = std::move(ancestorPrincipals);
   3775    return;
   3776  }
   3777 
   3778  // 7. Let ancestorOrigins be parentLocation's internal ancestor origin objects
   3779  // list.
   3780  const Span<const nsCOMPtr<nsIPrincipal>> parentAncestorOriginsList =
   3781      parent->GetPossiblyRedactedAncestorOriginsList();
   3782 
   3783  // 8. Let container be innerDoc's node navigable's container.
   3784  WindowGlobalParent* ancestorWGP = GetParentWindowContext();
   3785 
   3786  // 10. If container is an iframe element, then set referrerPolicy to
   3787  // container's referrerpolicy attribute's state's corresponding keyword.
   3788  // Note: becomes the empty string if there is none
   3789  auto referrerPolicy = aFrameReferrerPolicyAttribute;
   3790 
   3791  // 11. Let masked be false.
   3792  bool masked = false;
   3793 
   3794  if (referrerPolicy == ReferrerPolicy::No_referrer) {
   3795    // 12. If referrerPolicy is "no-referrer", then set masked to true.
   3796    masked = true;
   3797  } else if (referrerPolicy == ReferrerPolicy::Same_origin &&
   3798             !ancestorWGP->DocumentPrincipal()->Equals(
   3799                 aThisDocumentPrincipal)) {
   3800    // 13. Otherwise, if referrerPolicy is "same-origin" and parentDoc's
   3801    // origin is not same origin with innerDoc's origin, then set masked to
   3802    // true.
   3803    masked = true;
   3804  }
   3805 
   3806  if (masked) {
   3807    // 14. If masked is true, then append a new opaque origin to output.
   3808    ancestorPrincipals.AppendElement(nullptr);
   3809  } else {
   3810    // 15. Otherwise, append parentDoc's origin to output.
   3811    auto* principal = ancestorWGP->DocumentPrincipal();
   3812    // when we serialize a "null principal", we leak information. Represent
   3813    // them as actual nullptr instead.
   3814    ancestorPrincipals.AppendElement(
   3815        principal->GetIsNullPrincipal() ? nullptr : principal);
   3816  }
   3817 
   3818  // 16. For each ancestorOrigin of ancestorOrigins:
   3819  for (const auto& ancestorOrigin : parentAncestorOriginsList) {
   3820    // 16.1 if masked is true
   3821    if (masked && ancestorOrigin &&
   3822        ancestorOrigin->Equals(ancestorWGP->DocumentPrincipal())) {
   3823      //  16.1.1. If ancestorOrigin is same origin with parentDoc's origin, then
   3824      //  append a new opaque origin to output.
   3825      ancestorPrincipals.AppendElement(nullptr);
   3826    } else {
   3827      // 16.1.2. Otherwise, append ancestorOrigin to output and set masked to
   3828      // false. or 16.2. Otherwise, append ancestorOrigin to output.
   3829      ancestorPrincipals.AppendElement(ancestorOrigin);
   3830      masked = false;
   3831    }
   3832  }
   3833 
   3834  // 17. Return output.
   3835  // Only we don't return it. We're in the parent process.
   3836  mPossiblyRedactedAncestorOriginsList = std::move(ancestorPrincipals);
   3837 }
   3838 
   3839 Span<const nsCOMPtr<nsIPrincipal>>
   3840 CanonicalBrowsingContext::GetPossiblyRedactedAncestorOriginsList() const {
   3841  return mPossiblyRedactedAncestorOriginsList;
   3842 }
   3843 
   3844 void CanonicalBrowsingContext::SetPossiblyRedactedAncestorOriginsList(
   3845    nsTArray<nsCOMPtr<nsIPrincipal>> aAncestorOriginsList) {
   3846  mPossiblyRedactedAncestorOriginsList = std::move(aAncestorOriginsList);
   3847 }
   3848 
   3849 EntryList* CanonicalBrowsingContext::GetActiveEntries() {
   3850  if (!mActiveEntryList) {
   3851    auto* shistory = static_cast<nsSHistory*>(GetSessionHistory());
   3852    if (shistory) {
   3853      mActiveEntryList = shistory->EntryListFor(GetHistoryID());
   3854    }
   3855  }
   3856  return mActiveEntryList;
   3857 }
   3858 
   3859 void CanonicalBrowsingContext::SetEmbedderFrameReferrerPolicy(
   3860    ReferrerPolicy aPolicy) {
   3861  mEmbedderFrameReferrerPolicy = aPolicy;
   3862 }
   3863 
   3864 already_AddRefed<net::DocumentLoadListener>
   3865 CanonicalBrowsingContext::GetCurrentLoad() {
   3866  return do_AddRef(this->mCurrentLoad);
   3867 }
   3868 
   3869 NS_IMPL_CYCLE_COLLECTION_CLASS(CanonicalBrowsingContext)
   3870 
   3871 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(CanonicalBrowsingContext,
   3872                                                BrowsingContext)
   3873  tmp->mActiveEntryList = nullptr;
   3874  tmp->mPermanentKey.setNull();
   3875  if (tmp->mSessionHistory) {
   3876    tmp->mSessionHistory->SetBrowsingContext(nullptr);
   3877  }
   3878  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSessionHistory, mCurrentBrowserParent,
   3879                                  mWebProgress,
   3880                                  mSessionStoreSessionStorageUpdateTimer)
   3881 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
   3882 
   3883 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(CanonicalBrowsingContext,
   3884                                                  BrowsingContext)
   3885  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSessionHistory, mCurrentBrowserParent,
   3886                                    mWebProgress,
   3887                                    mSessionStoreSessionStorageUpdateTimer)
   3888 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
   3889 
   3890 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(CanonicalBrowsingContext,
   3891                                               BrowsingContext)
   3892  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mPermanentKey)
   3893 NS_IMPL_CYCLE_COLLECTION_TRACE_END
   3894 
   3895 NS_IMPL_ADDREF_INHERITED(CanonicalBrowsingContext, BrowsingContext)
   3896 NS_IMPL_RELEASE_INHERITED(CanonicalBrowsingContext, BrowsingContext)
   3897 
   3898 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CanonicalBrowsingContext)
   3899 NS_INTERFACE_MAP_END_INHERITING(BrowsingContext)
   3900 
   3901 }  // namespace mozilla::dom