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

Quelle  CanonicalBrowsingContext.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "mozilla/dom/CanonicalBrowsingContext.h"

#include "ContentAnalysis.h"
#include "ErrorList.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/Components.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/EventForwards.h"
#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/dom/BrowserParent.h"
#include "mozilla/dom/BrowsingContextBinding.h"
#include "mozilla/dom/BrowsingContextGroup.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/EventTarget.h"
#include "mozilla/dom/PBrowserParent.h"
#include "mozilla/dom/PBackgroundSessionStorageCache.h"
#include "mozilla/dom/PWindowGlobalParent.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/Promise-inl.h"
#include "mozilla/dom/WindowGlobalParent.h"
#include "mozilla/dom/ContentProcessManager.h"
#include "mozilla/dom/MediaController.h"
#include "mozilla/dom/MediaControlService.h"
#include "mozilla/dom/ContentPlaybackController.h"
#include "mozilla/dom/SessionStorageManager.h"
#include "mozilla/ipc/ProtocolUtils.h"
#include "mozilla/layers/CompositorBridgeChild.h"
#ifdef NS_PRINTING
#  include "mozilla/layout/RemotePrintJobParent.h"
#endif
#include "mozilla/net/DocumentLoadListener.h"
#include "mozilla/NullPrincipal.h"
#include "mozilla/StaticPrefs_browser.h"
#include "mozilla/StaticPrefs_docshell.h"
#include "mozilla/StaticPrefs_fission.h"
#include "mozilla/glean/DomMetrics.h"
#include "nsILayoutHistoryState.h"
#include "nsIPrintSettings.h"
#include "nsIPrintSettingsService.h"
#include "nsISupports.h"
#include "nsIWebNavigation.h"
#include "nsDocShell.h"
#include "nsFrameLoader.h"
#include "nsFrameLoaderOwner.h"
#include "nsGlobalWindowOuter.h"
#include "nsIContentAnalysis.h"
#include "nsIWebBrowserChrome.h"
#include "nsIXULRuntime.h"
#include "nsNetUtil.h"
#include "nsSHistory.h"
#include "nsSecureBrowserUI.h"
#include "nsQueryObject.h"
#include "nsBrowserStatusFilter.h"
#include "nsIBrowser.h"
#include "nsTHashSet.h"
#include "nsISessionStoreFunctions.h"
#include "nsIXPConnect.h"
#include "nsImportModule.h"
#include "UnitTransforms.h"

using namespace mozilla::ipc;

extern mozilla::LazyLogModule gAutoplayPermissionLog;
extern mozilla::LazyLogModule gSHLog;
extern mozilla::LazyLogModule gSHIPBFCacheLog;

#define AUTOPLAY_LOG(msg, ...) \
  MOZ_LOG(gAutoplayPermissionLog, LogLevel::Debug, (msg, ##__VA_ARGS__))

static mozilla::LazyLogModule sPBContext("PBContext");

// Global count of canonical browsing contexts with the private attribute set
static uint32_t gNumberOfPrivateContexts = 0;

// Current parent process epoch for parent initiated navigations
static uint64_t gParentInitiatedNavigationEpoch = 0;

static void IncreasePrivateCount() {
  gNumberOfPrivateContexts++;
  MOZ_LOG(sPBContext, mozilla::LogLevel::Debug,
          ("%s: Private browsing context count %d -> %d", __func__,
           gNumberOfPrivateContexts - 1, gNumberOfPrivateContexts));
  if (gNumberOfPrivateContexts > 1) {
    return;
  }

  static bool sHasSeenPrivateContext = false;
  if (!sHasSeenPrivateContext) {
    sHasSeenPrivateContext = true;
    mozilla::glean::dom_parentprocess::private_window_used.Set(true);
  }
}

static void DecreasePrivateCount() {
  MOZ_ASSERT(gNumberOfPrivateContexts > 0);
  gNumberOfPrivateContexts--;

  MOZ_LOG(sPBContext, mozilla::LogLevel::Debug,
          ("%s: Private browsing context count %d -> %d", __func__,
           gNumberOfPrivateContexts + 1, gNumberOfPrivateContexts));
  if (!gNumberOfPrivateContexts &&
      !mozilla::StaticPrefs::browser_privatebrowsing_autostart()) {
    nsCOMPtr<nsIObserverService> observerService =
        mozilla::services::GetObserverService();
    if (observerService) {
      MOZ_LOG(sPBContext, mozilla::LogLevel::Debug,
              ("%s: last-pb-context-exited fired", __func__));
      observerService->NotifyObservers(nullptr, "last-pb-context-exited",
                                       nullptr);
    }
  }
}

namespace mozilla::dom {

extern mozilla::LazyLogModule gUserInteractionPRLog;

#define USER_ACTIVATION_LOG(msg, ...) \
  MOZ_LOG(gUserInteractionPRLog, LogLevel::Debug, (msg, ##__VA_ARGS__))

CanonicalBrowsingContext::CanonicalBrowsingContext(WindowContext* aParentWindow,
                                                   BrowsingContextGroup* aGroup,
                                                   uint64_t aBrowsingContextId,
                                                   uint64_t aOwnerProcessId,
                                                   uint64_t aEmbedderProcessId,
                                                   BrowsingContext::Type aType,
                                                   FieldValues&& aInit)
    : BrowsingContext(aParentWindow, aGroup, aBrowsingContextId, aType,
                      std::move(aInit)),
      mProcessId(aOwnerProcessId),
      mEmbedderProcessId(aEmbedderProcessId),
      mPermanentKey(JS::NullValue()) {
  // You are only ever allowed to create CanonicalBrowsingContexts in the
  // parent process.
  MOZ_RELEASE_ASSERT(XRE_IsParentProcess());

  // The initial URI in a BrowsingContext is always "about:blank".
  MOZ_ALWAYS_SUCCEEDS(
      NS_NewURI(getter_AddRefs(mCurrentRemoteURI), "about:blank"));

  mozilla::HoldJSObjects(this);
}

CanonicalBrowsingContext::~CanonicalBrowsingContext() {
  mPermanentKey.setNull();

  mozilla::DropJSObjects(this);

  if (mSessionHistory) {
    mSessionHistory->SetBrowsingContext(nullptr);
  }
}

/* static */
already_AddRefed<CanonicalBrowsingContext> CanonicalBrowsingContext::Get(
    uint64_t aId) {
  MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
  return BrowsingContext::Get(aId).downcast<CanonicalBrowsingContext>();
}

/* static */
CanonicalBrowsingContext* CanonicalBrowsingContext::Cast(
    BrowsingContext* aContext) {
  MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
  return static_cast<CanonicalBrowsingContext*>(aContext);
}

/* static */
const CanonicalBrowsingContext* CanonicalBrowsingContext::Cast(
    const BrowsingContext* aContext) {
  MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
  return static_cast<const CanonicalBrowsingContext*>(aContext);
}

already_AddRefed<CanonicalBrowsingContext> CanonicalBrowsingContext::Cast(
    already_AddRefed<BrowsingContext>&& aContext) {
  MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
  return aContext.downcast<CanonicalBrowsingContext>();
}

ContentParent* CanonicalBrowsingContext::GetContentParent() const {
  if (mProcessId == 0) {
    return nullptr;
  }

  ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
  if (!cpm) {
    return nullptr;
  }
  return cpm->GetContentProcessById(ContentParentId(mProcessId));
}

void CanonicalBrowsingContext::GetCurrentRemoteType(nsACString& aRemoteType,
                                                    ErrorResult& aRv) const {
  // If we're in the parent process, dump out the void string.
  if (mProcessId == 0) {
    aRemoteType = NOT_REMOTE_TYPE;
    return;
  }

  ContentParent* cp = GetContentParent();
  if (!cp) {
    aRv.Throw(NS_ERROR_UNEXPECTED);
    return;
  }

  aRemoteType = cp->GetRemoteType();
}

void CanonicalBrowsingContext::SetOwnerProcessId(uint64_t aProcessId) {
  MOZ_LOG(GetLog(), LogLevel::Debug,
          ("SetOwnerProcessId for 0x%08" PRIx64 " (0x%08" PRIx64
           " -> 0x%08" PRIx64 ")",
           Id(), mProcessId, aProcessId));

  mProcessId = aProcessId;
}

nsISecureBrowserUI* CanonicalBrowsingContext::GetSecureBrowserUI() {
  if (!IsTop()) {
    return nullptr;
  }
  if (!mSecureBrowserUI) {
    mSecureBrowserUI = new nsSecureBrowserUI(this);
  }
  return mSecureBrowserUI;
}

namespace {
// The DocShellProgressBridge is attached to a root content docshell loaded in
// the parent process. Notifications are paired up with the docshell which they
// came from, so that they can be fired to the correct
// BrowsingContextWebProgress and bubble through this tree separately.
//
// Notifications are filtered by a nsBrowserStatusFilter before being received
// by the DocShellProgressBridge.
class DocShellProgressBridge : public nsIWebProgressListener {
 public:
  NS_DECL_ISUPPORTS
  // NOTE: This relies in the expansion of `NS_FORWARD_SAFE` and all listener
  // methods accepting an `aWebProgress` argument. If this changes in the
  // future, this may need to be written manually.
  NS_FORWARD_SAFE_NSIWEBPROGRESSLISTENER(GetTargetContext(aWebProgress))

  explicit DocShellProgressBridge(uint64_t aTopContextId)
      : mTopContextId(aTopContextId) {}

 private:
  virtual ~DocShellProgressBridge() = default;

  nsIWebProgressListener* GetTargetContext(nsIWebProgress* aWebProgress) {
    RefPtr<CanonicalBrowsingContext> context;
    if (nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(aWebProgress)) {
      context = docShell->GetBrowsingContext()->Canonical();
    } else {
      context = CanonicalBrowsingContext::Get(mTopContextId);
    }
    return context && !context->IsDiscarded() ? context->GetWebProgress()
                                              : nullptr;
  }

  uint64_t mTopContextId = 0;
};

NS_IMPL_ISUPPORTS(DocShellProgressBridge, nsIWebProgressListener)
}  // namespace

void CanonicalBrowsingContext::MaybeAddAsProgressListener(
    nsIWebProgress* aWebProgress) {
  // Only add as a listener if the created docshell is a toplevel content
  // docshell. We'll get notifications for all of our subframes through a single
  // listener.
  if (!IsTopContent()) {
    return;
  }

  if (!mDocShellProgressBridge) {
    mDocShellProgressBridge = new DocShellProgressBridge(Id());
    mStatusFilter = new nsBrowserStatusFilter();
    mStatusFilter->AddProgressListener(mDocShellProgressBridge,
                                       nsIWebProgress::NOTIFY_ALL);
  }

  aWebProgress->AddProgressListener(mStatusFilter, nsIWebProgress::NOTIFY_ALL);
}

void CanonicalBrowsingContext::ReplacedBy(
    CanonicalBrowsingContext* aNewContext,
    const NavigationIsolationOptions& aRemotenessOptions) {
  MOZ_ASSERT(!aNewContext->mWebProgress);
  MOZ_ASSERT(!aNewContext->mSessionHistory);
  MOZ_ASSERT(IsTop() && aNewContext->IsTop());

  mIsReplaced = true;
  aNewContext->mIsReplaced = false;

  if (mStatusFilter) {
    mStatusFilter->RemoveProgressListener(mDocShellProgressBridge);
    mStatusFilter = nullptr;
  }

  mWebProgress->ContextReplaced(aNewContext);
  aNewContext->mWebProgress = std::move(mWebProgress);

  // Use the Transaction for the fields which need to be updated whether or not
  // the new context has been attached before.
  // SetWithoutSyncing can be used if context hasn't been attached.
  Transaction txn;
  txn.SetBrowserId(GetBrowserId());
  txn.SetIsAppTab(GetIsAppTab());
  txn.SetHasSiblings(GetHasSiblings());
  txn.SetTopLevelCreatedByWebContent(GetTopLevelCreatedByWebContent());
  txn.SetHistoryID(GetHistoryID());
  txn.SetExplicitActive(GetExplicitActive());
  txn.SetEmbedderColorSchemes(GetEmbedderColorSchemes());
  txn.SetHasRestoreData(GetHasRestoreData());
  txn.SetShouldDelayMediaFromStart(GetShouldDelayMediaFromStart());
  txn.SetForceOffline(GetForceOffline());
  txn.SetTopInnerSizeForRFP(GetTopInnerSizeForRFP());

  // Propagate some settings on BrowsingContext replacement so they're not lost
  // on bfcached navigations. These are important for GeckoView (see bug
  // 1781936).
  txn.SetAllowJavascript(GetAllowJavascript());
  txn.SetForceEnableTrackingProtection(GetForceEnableTrackingProtection());
  txn.SetUserAgentOverride(GetUserAgentOverride());
  txn.SetSuspendMediaWhenInactive(GetSuspendMediaWhenInactive());
  txn.SetDisplayMode(GetDisplayMode());
  txn.SetForceDesktopViewport(GetForceDesktopViewport());
  txn.SetIsUnderHiddenEmbedderElement(GetIsUnderHiddenEmbedderElement());

  // When using site-specific zoom, we let the frontend manage the zoom level
  // of BFCache'd contexts. Overriding those zoom levels can cause weirdness
  // like bug 1846141. We always copy to new contexts to avoid bug 1914149.
  if (!aNewContext->EverAttached() ||
      !StaticPrefs::browser_zoom_siteSpecific()) {
    txn.SetFullZoom(GetFullZoom());
    txn.SetTextZoom(GetTextZoom());
  }

  // Propagate the default load flags so that the TRR mode flags are forwarded
  // to the new browsing context. See bug 1828643.
  txn.SetDefaultLoadFlags(GetDefaultLoadFlags());

  // As this is a different BrowsingContext, set InitialSandboxFlags to the
  // current flags in the new context so that they also apply to any initial
  // about:blank documents created in it.
  txn.SetSandboxFlags(GetSandboxFlags());
  txn.SetInitialSandboxFlags(GetSandboxFlags());
  txn.SetTargetTopLevelLinkClicksToBlankInternal(
      TargetTopLevelLinkClicksToBlank());
  if (aNewContext->EverAttached()) {
    MOZ_ALWAYS_SUCCEEDS(txn.Commit(aNewContext));
  } else {
    txn.CommitWithoutSyncing(aNewContext);
  }

  aNewContext->mRestoreState = mRestoreState.forget();
  MOZ_ALWAYS_SUCCEEDS(SetHasRestoreData(false));

  // XXXBFCache name handling is still a bit broken in Fission in general,
  // at least in case name should be cleared.
  if (aRemotenessOptions.mTryUseBFCache) {
    MOZ_ASSERT(!aNewContext->EverAttached());
    aNewContext->mFields.SetWithoutSyncing<IDX_Name>(GetName());
    // We don't copy over HasLoadedNonInitialDocument here, we'll actually end
    // up loading a new initial document at this point, before the real load.
    // The real load will then end up setting HasLoadedNonInitialDocument to
    // true.
  }

  if (mSessionHistory) {
    mSessionHistory->SetBrowsingContext(aNewContext);
    // At this point we will be creating a new ChildSHistory in the child.
    // That means that the child's epoch will be reset, so it makes sense to
    // reset the epoch in the parent too.
    mSessionHistory->SetEpoch(0, Nothing());
    mSessionHistory.swap(aNewContext->mSessionHistory);
    RefPtr<ChildSHistory> childSHistory = ForgetChildSHistory();
    aNewContext->SetChildSHistory(childSHistory);
  }

  BackgroundSessionStorageManager::PropagateManager(Id(), aNewContext->Id());

  // Transfer the ownership of the priority active status from the old context
  // to the new context.
  aNewContext->mPriorityActive = mPriorityActive;
  mPriorityActive = false;

  MOZ_ASSERT(aNewContext->mLoadingEntries.IsEmpty());
  mLoadingEntries.SwapElements(aNewContext->mLoadingEntries);
  MOZ_ASSERT(!aNewContext->mActiveEntry);
  mActiveEntry.swap(aNewContext->mActiveEntry);

  aNewContext->mPermanentKey = mPermanentKey;
  mPermanentKey.setNull();
}

void CanonicalBrowsingContext::UpdateSecurityState() {
  if (mSecureBrowserUI) {
    mSecureBrowserUI->RecomputeSecurityFlags();
  }
}

void CanonicalBrowsingContext::GetWindowGlobals(
    nsTArray<RefPtr<WindowGlobalParent>>& aWindows) {
  aWindows.SetCapacity(GetWindowContexts().Length());
  for (auto& window : GetWindowContexts()) {
    aWindows.AppendElement(static_cast<WindowGlobalParent*>(window.get()));
  }
}

WindowGlobalParent* CanonicalBrowsingContext::GetCurrentWindowGlobal() const {
  return static_cast<WindowGlobalParent*>(GetCurrentWindowContext());
}

WindowGlobalParent* CanonicalBrowsingContext::GetParentWindowContext() {
  return static_cast<WindowGlobalParent*>(
      BrowsingContext::GetParentWindowContext());
}

WindowGlobalParent* CanonicalBrowsingContext::GetTopWindowContext() {
  return static_cast<WindowGlobalParent*>(
      BrowsingContext::GetTopWindowContext());
}

already_AddRefed<nsIWidget>
CanonicalBrowsingContext::GetParentProcessWidgetContaining() {
  // If our document is loaded in-process, such as chrome documents, get the
  // widget directly from our outer window. Otherwise, try to get the widget
  // from the toplevel content's browser's element.
  nsCOMPtr<nsIWidget> widget;
  if (nsGlobalWindowOuter* window = nsGlobalWindowOuter::Cast(GetDOMWindow())) {
    widget = window->GetNearestWidget();
  } else if (Element* topEmbedder = Top()->GetEmbedderElement()) {
    widget = nsContentUtils::WidgetForContent(topEmbedder);
    if (!widget) {
      widget = nsContentUtils::WidgetForDocument(topEmbedder->OwnerDoc());
    }
  }

  if (widget) {
    widget = widget->GetTopLevelWidget();
  }

  return widget.forget();
}

already_AddRefed<nsIBrowserDOMWindow>
CanonicalBrowsingContext::GetBrowserDOMWindow() {
  RefPtr<CanonicalBrowsingContext> chromeTop = TopCrossChromeBoundary();
  nsGlobalWindowOuter* topWin;
  if ((topWin = nsGlobalWindowOuter::Cast(chromeTop->GetDOMWindow())) &&
      topWin->IsChromeWindow()) {
    return do_AddRef(topWin->GetBrowserDOMWindow());
  }
  return nullptr;
}

already_AddRefed<WindowGlobalParent>
CanonicalBrowsingContext::GetEmbedderWindowGlobal() const {
  uint64_t windowId = GetEmbedderInnerWindowId();
  if (windowId == 0) {
    return nullptr;
  }

  return WindowGlobalParent::GetByInnerWindowId(windowId);
}

CanonicalBrowsingContext*
CanonicalBrowsingContext::GetParentCrossChromeBoundary() {
  if (GetParent()) {
    return Cast(GetParent());
  }
  if (auto* embedder = GetEmbedderElement()) {
    return Cast(embedder->OwnerDoc()->GetBrowsingContext());
  }
  return nullptr;
}

CanonicalBrowsingContext* CanonicalBrowsingContext::TopCrossChromeBoundary() {
  CanonicalBrowsingContext* bc = this;
  while (auto* parent = bc->GetParentCrossChromeBoundary()) {
    bc = parent;
  }
  return bc;
}

Nullable<WindowProxyHolder> CanonicalBrowsingContext::GetTopChromeWindow() {
  RefPtr<CanonicalBrowsingContext> bc = TopCrossChromeBoundary();
  if (bc->IsChrome()) {
    return WindowProxyHolder(bc.forget());
  }
  return nullptr;
}

nsISHistory* CanonicalBrowsingContext::GetSessionHistory() {
  if (!IsTop()) {
    return Cast(Top())->GetSessionHistory();
  }

  // Check GetChildSessionHistory() to make sure that this BrowsingContext has
  // session history enabled.
  if (!mSessionHistory && GetChildSessionHistory()) {
    mSessionHistory = new nsSHistory(this);
  }

  return mSessionHistory;
}

SessionHistoryEntry* CanonicalBrowsingContext::GetActiveSessionHistoryEntry() {
  return mActiveEntry;
}

void CanonicalBrowsingContext::SetActiveSessionHistoryEntry(
    SessionHistoryEntry* aEntry) {
  mActiveEntry = aEntry;
}

bool CanonicalBrowsingContext::HasHistoryEntry(nsISHEntry* aEntry) {
  // XXX Should we check also loading entries?
  return aEntry && mActiveEntry == aEntry;
}

void CanonicalBrowsingContext::SwapHistoryEntries(nsISHEntry* aOldEntry,
                                                  nsISHEntry* aNewEntry) {
  // XXX Should we check also loading entries?
  if (mActiveEntry == aOldEntry) {
    nsCOMPtr<SessionHistoryEntry> newEntry = do_QueryInterface(aNewEntry);
    mActiveEntry = newEntry.forget();
  }
}

void CanonicalBrowsingContext::AddLoadingSessionHistoryEntry(
    uint64_t aLoadId, SessionHistoryEntry* aEntry) {
  Unused << SetHistoryID(aEntry->DocshellID());
  mLoadingEntries.AppendElement(LoadingSessionHistoryEntry{aLoadId, aEntry});
}

void CanonicalBrowsingContext::GetLoadingSessionHistoryInfoFromParent(
    Maybe<LoadingSessionHistoryInfo>& aLoadingInfo) {
  nsISHistory* shistory = GetSessionHistory();
  if (!shistory || !GetParent()) {
    return;
  }

  SessionHistoryEntry* parentSHE =
      GetParent()->Canonical()->GetActiveSessionHistoryEntry();
  if (parentSHE) {
    int32_t index = -1;
    for (BrowsingContext* sibling : GetParent()->Children()) {
      ++index;
      if (sibling == this) {
        nsCOMPtr<nsISHEntry> shEntry;
        parentSHE->GetChildSHEntryIfHasNoDynamicallyAddedChild(
            index, getter_AddRefs(shEntry));
        nsCOMPtr<SessionHistoryEntry> she = do_QueryInterface(shEntry);
        if (she) {
          aLoadingInfo.emplace(she);
          mLoadingEntries.AppendElement(LoadingSessionHistoryEntry{
              aLoadingInfo.value().mLoadId, she.get()});
          Unused << SetHistoryID(she->DocshellID());
        }
        break;
      }
    }
  }
}

UniquePtr<LoadingSessionHistoryInfo>
CanonicalBrowsingContext::CreateLoadingSessionHistoryEntryForLoad(
    nsDocShellLoadState* aLoadState, SessionHistoryEntry* existingEntry,
    nsIChannel* aChannel) {
  RefPtr<SessionHistoryEntry> entry;
  const LoadingSessionHistoryInfo* existingLoadingInfo =
      aLoadState->GetLoadingSessionHistoryInfo();
  MOZ_ASSERT_IF(!existingLoadingInfo, !existingEntry);
  if (existingLoadingInfo) {
    if (existingEntry) {
      entry = existingEntry;
    } else {
      MOZ_ASSERT(!existingLoadingInfo->mLoadIsFromSessionHistory);

      SessionHistoryEntry::LoadingEntry* loadingEntry =
          SessionHistoryEntry::GetByLoadId(existingLoadingInfo->mLoadId);
      MOZ_LOG(gSHLog, LogLevel::Verbose,
              ("SHEntry::GetByLoadId(%" PRIu64 ") -> %p",
               existingLoadingInfo->mLoadId, entry.get()));
      if (!loadingEntry) {
        return nullptr;
      }
      entry = loadingEntry->mEntry;
    }

    // If the entry was updated, update also the LoadingSessionHistoryInfo.
    UniquePtr<LoadingSessionHistoryInfo> lshi =
        MakeUnique<LoadingSessionHistoryInfo>(entry, existingLoadingInfo);
    aLoadState->SetLoadingSessionHistoryInfo(std::move(lshi));
    existingLoadingInfo = aLoadState->GetLoadingSessionHistoryInfo();
    Unused << SetHistoryEntryCount(entry->BCHistoryLength());
  } else if (aLoadState->LoadType() == LOAD_REFRESH &&
             !ShouldAddEntryForRefresh(aLoadState->URI(),
                                       aLoadState->PostDataStream()) &&
             mActiveEntry) {
    entry = mActiveEntry;
  } else {
    entry = new SessionHistoryEntry(aLoadState, aChannel);
    if (IsTop()) {
      // Only top level pages care about Get/SetPersist.
      entry->SetPersist(
          nsDocShell::ShouldAddToSessionHistory(aLoadState->URI(), aChannel));
    } else if (mActiveEntry || !mLoadingEntries.IsEmpty()) {
      entry->SetIsSubFrame(true);
    }
    entry->SetDocshellID(GetHistoryID());
    entry->SetIsDynamicallyAdded(CreatedDynamically());
    entry->SetForInitialLoad(true);
  }
  MOZ_DIAGNOSTIC_ASSERT(entry);

  UniquePtr<LoadingSessionHistoryInfo> loadingInfo;
  if (existingLoadingInfo) {
    loadingInfo = MakeUnique<LoadingSessionHistoryInfo>(*existingLoadingInfo);
  } else {
    loadingInfo = MakeUnique<LoadingSessionHistoryInfo>(entry);
    mLoadingEntries.AppendElement(
        LoadingSessionHistoryEntry{loadingInfo->mLoadId, entry});
  }

  MOZ_ASSERT(SessionHistoryEntry::GetByLoadId(loadingInfo->mLoadId)->mEntry ==
             entry);

  return loadingInfo;
}

UniquePtr<LoadingSessionHistoryInfo>
CanonicalBrowsingContext::ReplaceLoadingSessionHistoryEntryForLoad(
    LoadingSessionHistoryInfo* aInfo, nsIChannel* aNewChannel) {
  MOZ_ASSERT(aInfo);
  MOZ_ASSERT(aNewChannel);

  SessionHistoryInfo newInfo = SessionHistoryInfo(
      aNewChannel, aInfo->mInfo.LoadType(),
      aInfo->mInfo.GetPartitionedPrincipalToInherit(), aInfo->mInfo.GetCsp());

  for (size_t i = 0; i < mLoadingEntries.Length(); ++i) {
    if (mLoadingEntries[i].mLoadId == aInfo->mLoadId) {
      RefPtr<SessionHistoryEntry> loadingEntry = mLoadingEntries[i].mEntry;
      loadingEntry->SetInfo(&newInfo);

      if (IsTop()) {
        // Only top level pages care about Get/SetPersist.
        nsCOMPtr<nsIURI> uri;
        aNewChannel->GetURI(getter_AddRefs(uri));
        loadingEntry->SetPersist(
            nsDocShell::ShouldAddToSessionHistory(uri, aNewChannel));
      } else {
        loadingEntry->SetIsSubFrame(aInfo->mInfo.IsSubFrame());
      }
      loadingEntry->SetDocshellID(GetHistoryID());
      loadingEntry->SetIsDynamicallyAdded(CreatedDynamically());
      return MakeUnique<LoadingSessionHistoryInfo>(loadingEntry, aInfo);
    }
  }
  return nullptr;
}

using PrintPromise = CanonicalBrowsingContext::PrintPromise;
#ifdef NS_PRINTING
// Clients must call StaticCloneForPrintingCreated or
// NoStaticCloneForPrintingWillBeCreated before the underlying promise can
// resolve.
class PrintListenerAdapter final : public nsIWebProgressListener {
 public:
  explicit PrintListenerAdapter(PrintPromise::Private* aPromise)
      : mPromise(aPromise) {}

  NS_DECL_ISUPPORTS

  // NS_DECL_NSIWEBPROGRESSLISTENER
  NS_IMETHOD OnStateChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
                           uint32_t aStateFlags, nsresult aStatus) override {
    MOZ_ASSERT(NS_IsMainThread());
    if (aStateFlags & nsIWebProgressListener::STATE_STOP &&
        aStateFlags & nsIWebProgressListener::STATE_IS_DOCUMENT && mPromise) {
      mPrintJobFinished = true;
      if (mHaveSetBrowsingContext) {
        mPromise->Resolve(mClonedStaticBrowsingContext, __func__);
        mPromise = nullptr;
      }
    }
    return NS_OK;
  }
  NS_IMETHOD OnStatusChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
                            nsresult aStatus,
                            const char16_t* aMessage) override {
    if (aStatus != NS_OK && mPromise) {
      mPromise->Reject(aStatus, __func__);
      mPromise = nullptr;
    }
    return NS_OK;
  }
  NS_IMETHOD OnProgressChange(nsIWebProgress* aWebProgress,
                              nsIRequest* aRequest, int32_t aCurSelfProgress,
                              int32_t aMaxSelfProgress,
                              int32_t aCurTotalProgress,
                              int32_t aMaxTotalProgress) override {
    return NS_OK;
  }
  NS_IMETHOD OnLocationChange(nsIWebProgress* aWebProgress,
                              nsIRequest* aRequest, nsIURI* aLocation,
                              uint32_t aFlags) override {
    return NS_OK;
  }
  NS_IMETHOD OnSecurityChange(nsIWebProgress* aWebProgress,
                              nsIRequest* aRequest, uint32_t aState) override {
    return NS_OK;
  }
  NS_IMETHOD OnContentBlockingEvent(nsIWebProgress* aWebProgress,
                                    nsIRequest* aRequest,
                                    uint32_t aEvent) override {
    return NS_OK;
  }

  void StaticCloneForPrintingCreated(
      MaybeDiscardedBrowsingContext&& aClonedStaticBrowsingContext) {
    MOZ_ASSERT(NS_IsMainThread());
    mClonedStaticBrowsingContext = std::move(aClonedStaticBrowsingContext);
    mHaveSetBrowsingContext = true;
    if (mPrintJobFinished && mPromise) {
      mPromise->Resolve(mClonedStaticBrowsingContext, __func__);
      mPromise = nullptr;
    }
  }

  void NoStaticCloneForPrintingWillBeCreated() {
    StaticCloneForPrintingCreated(nullptr);
  }

 private:
  ~PrintListenerAdapter() = default;

  RefPtr<PrintPromise::Private> mPromise;
  MaybeDiscardedBrowsingContext mClonedStaticBrowsingContext = nullptr;
  bool mHaveSetBrowsingContext = false;
  bool mPrintJobFinished = false;
};

NS_IMPL_ISUPPORTS(PrintListenerAdapter, nsIWebProgressListener)
#endif

already_AddRefed<Promise> CanonicalBrowsingContext::PrintJS(
    nsIPrintSettings* aPrintSettings, ErrorResult& aRv) {
  RefPtr<Promise> promise = Promise::Create(GetIncumbentGlobal(), aRv);
  if (NS_WARN_IF(aRv.Failed())) {
    return promise.forget();
  }

  Print(aPrintSettings)
      ->Then(
          GetCurrentSerialEventTarget(), __func__,
          [promise](MaybeDiscardedBrowsingContext) {
            promise->MaybeResolveWithUndefined();
          },
          [promise](nsresult aResult) { promise->MaybeReject(aResult); });
  return promise.forget();
}

RefPtr<PrintPromise> CanonicalBrowsingContext::Print(
    nsIPrintSettings* aPrintSettings) {
#ifndef NS_PRINTING
  return PrintPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__);
#else
// Content analysis is not supported on non-Windows platforms.
#  if defined(XP_WIN)
  bool needContentAnalysis = false;
  nsCOMPtr<nsIContentAnalysis> contentAnalysis =
      mozilla::components::nsIContentAnalysis::Service();
  Unused << NS_WARN_IF(!contentAnalysis);
  if (contentAnalysis) {
    nsresult rv = contentAnalysis->GetIsActive(&needContentAnalysis);
    Unused << NS_WARN_IF(NS_FAILED(rv));
  }
  if (needContentAnalysis) {
    auto done = MakeRefPtr<PrintPromise::Private>(__func__);
    contentanalysis::ContentAnalysis::PrintToPDFToDetermineIfPrintAllowed(
        this, aPrintSettings)
        ->Then(
            GetCurrentSerialEventTarget(), __func__,
            [done, aPrintSettings = RefPtr{aPrintSettings},
             self = RefPtr{this}](
                contentanalysis::ContentAnalysis::PrintAllowedResult aResponse)
                MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA mutable {
                  if (aResponse.mAllowed) {
                    self->PrintWithNoContentAnalysis(
                            aPrintSettings, false,
                            aResponse.mCachedStaticDocumentBrowsingContext)
                        ->ChainTo(done.forget(), __func__);
                  } else {
                    // Since we are not doing the second print in this case,
                    // release the clone that is no longer needed.
                    self->ReleaseClonedPrint(
                        aResponse.mCachedStaticDocumentBrowsingContext);
                    done->Reject(NS_ERROR_CONTENT_BLOCKED, __func__);
                  }
                },
            [done, self = RefPtr{this}](
                contentanalysis::ContentAnalysis::PrintAllowedError
                    aErrorResponse) MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA {
              // Since we are not doing the second print in this case, release
              // the clone that is no longer needed.
              self->ReleaseClonedPrint(
                  aErrorResponse.mCachedStaticDocumentBrowsingContext);
              done->Reject(aErrorResponse.mError, __func__);
            });
    return done;
  }
#  endif
  return PrintWithNoContentAnalysis(aPrintSettings, false, nullptr);
#endif
}

void CanonicalBrowsingContext::ReleaseClonedPrint(
    const MaybeDiscardedBrowsingContext& aClonedStaticBrowsingContext) {
#ifdef NS_PRINTING
  auto* browserParent = GetBrowserParent();
  if (NS_WARN_IF(!browserParent)) {
    return;
  }
  Unused << browserParent->SendDestroyPrintClone(aClonedStaticBrowsingContext);
#endif
}

RefPtr<PrintPromise> CanonicalBrowsingContext::PrintWithNoContentAnalysis(
    nsIPrintSettings* aPrintSettings, bool aForceStaticDocument,
    const MaybeDiscardedBrowsingContext& aCachedStaticDocument) {
#ifndef NS_PRINTING
  return PrintPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__);
#else
  auto promise = MakeRefPtr<PrintPromise::Private>(__func__);
  auto listener = MakeRefPtr<PrintListenerAdapter>(promise);
  if (IsInProcess()) {
    RefPtr<nsGlobalWindowOuter> outerWindow =
        nsGlobalWindowOuter::Cast(GetDOMWindow());
    if (NS_WARN_IF(!outerWindow)) {
      promise->Reject(NS_ERROR_FAILURE, __func__);
      return promise;
    }

    ErrorResult rv;
    listener->NoStaticCloneForPrintingWillBeCreated();
    outerWindow->Print(aPrintSettings,
                       /* aRemotePrintJob = */ nullptr, listener,
                       /* aDocShellToCloneInto = */ nullptr,
                       nsGlobalWindowOuter::IsPreview::No,
                       nsGlobalWindowOuter::IsForWindowDotPrint::No,
                       /* aPrintPreviewCallback = */ nullptr,
                       /* aCachedBrowsingContext = */ nullptr, rv);
    if (rv.Failed()) {
      promise->Reject(rv.StealNSResult(), __func__);
    }
    return promise;
  }

  auto* browserParent = GetBrowserParent();
  if (NS_WARN_IF(!browserParent)) {
    promise->Reject(NS_ERROR_FAILURE, __func__);
    return promise;
  }

  nsCOMPtr<nsIPrintSettingsService> printSettingsSvc =
      do_GetService("@mozilla.org/gfx/printsettings-service;1");
  if (NS_WARN_IF(!printSettingsSvc)) {
    promise->Reject(NS_ERROR_FAILURE, __func__);
    return promise;
  }

  nsresult rv;
  nsCOMPtr<nsIPrintSettings> printSettings = aPrintSettings;
  if (!printSettings) {
    rv =
        printSettingsSvc->CreateNewPrintSettings(getter_AddRefs(printSettings));
    if (NS_WARN_IF(NS_FAILED(rv))) {
      promise->Reject(rv, __func__);
      return promise;
    }
  }

  embedding::PrintData printData;
  rv = printSettingsSvc->SerializeToPrintData(printSettings, &printData);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    promise->Reject(rv, __func__);
    return promise;
  }

  layout::RemotePrintJobParent* remotePrintJob =
      new layout::RemotePrintJobParent(printSettings);
  printData.remotePrintJob() =
      browserParent->Manager()->SendPRemotePrintJobConstructor(remotePrintJob);

  remotePrintJob->RegisterListener(listener);

  if (!aCachedStaticDocument.IsNullOrDiscarded()) {
    // There is no cloned static browsing context that
    // SendPrintClonedPage() will return, so indicate this
    // so listener can resolve its promise.
    listener->NoStaticCloneForPrintingWillBeCreated();
    if (NS_WARN_IF(!browserParent->SendPrintClonedPage(
            this, printData, aCachedStaticDocument))) {
      promise->Reject(NS_ERROR_FAILURE, __func__);
    }
  } else {
    RefPtr<PBrowserParent::PrintPromise> printPromise =
        browserParent->SendPrint(this, printData, aForceStaticDocument);
    printPromise->Then(
        GetMainThreadSerialEventTarget(), __func__,
        [listener](MaybeDiscardedBrowsingContext cachedStaticDocument) {
          // promise will get resolved by the listener
          listener->StaticCloneForPrintingCreated(
              std::move(cachedStaticDocument));
        },
        [promise](ResponseRejectReason reason) {
          NS_WARNING("SendPrint() failed");
          promise->Reject(NS_ERROR_FAILURE, __func__);
        });
  }
  return promise.forget();
#endif
}

void CanonicalBrowsingContext::CallOnAllTopDescendants(
    const FunctionRef<CallState(CanonicalBrowsingContext*)>& aCallback,
    bool aIncludeNestedBrowsers) {
  MOZ_ASSERT(IsTop(), "Should only call on top BC");
  MOZ_ASSERT(
      !aIncludeNestedBrowsers ||
          (IsChrome() && !GetParentCrossChromeBoundary()),
      "If aIncludeNestedBrowsers is set, should only call on top chrome BC");

  if (!IsInProcess()) {
    // We rely on top levels having to be embedded in the parent process, so
    // we can only have top level descendants if embedded here..
    return;
  }

  AutoTArray<RefPtr<BrowsingContextGroup>, 32> groups;
  BrowsingContextGroup::GetAllGroups(groups);
  for (auto& browsingContextGroup : groups) {
    for (auto& bc : browsingContextGroup->Toplevels()) {
      if (bc == this) {
        // Cannot be a descendent of myself so skip.
        continue;
      }

      if (aIncludeNestedBrowsers) {
        if (this != bc->Canonical()->TopCrossChromeBoundary()) {
          continue;
        }
      } else {
        auto* parent = bc->Canonical()->GetParentCrossChromeBoundary();
        if (!parent || this != parent->Top()) {
          continue;
        }
      }

      if (aCallback(bc->Canonical()) == CallState::Stop) {
        return;
      }
    }
  }
}

void CanonicalBrowsingContext::SessionHistoryCommit(
    uint64_t aLoadId, const nsID& aChangeID, uint32_t aLoadType, bool aPersist,
    bool aCloneEntryChildren, bool aChannelExpired, uint32_t aCacheKey) {
  MOZ_LOG(gSHLog, LogLevel::Verbose,
          ("CanonicalBrowsingContext::SessionHistoryCommit %p %" PRIu64, this,
           aLoadId));
  MOZ_ASSERT(aLoadId != UINT64_MAX,
             "Must not send special about:blank loadinfo to parent.");
  for (size_t i = 0; i < mLoadingEntries.Length(); ++i) {
    if (mLoadingEntries[i].mLoadId == aLoadId) {
      nsSHistory* shistory = static_cast<nsSHistory*>(GetSessionHistory());
      if (!shistory) {
        SessionHistoryEntry::RemoveLoadId(aLoadId);
        mLoadingEntries.RemoveElementAt(i);
        return;
      }

      RefPtr<SessionHistoryEntry> newActiveEntry = mLoadingEntries[i].mEntry;
      if (aCacheKey != 0) {
        newActiveEntry->SetCacheKey(aCacheKey);
      }

      if (aChannelExpired) {
        newActiveEntry->SharedInfo()->mExpired = true;
      }

      bool loadFromSessionHistory = !newActiveEntry->ForInitialLoad();
      newActiveEntry->SetForInitialLoad(false);
      SessionHistoryEntry::RemoveLoadId(aLoadId);
      mLoadingEntries.RemoveElementAt(i);

      int32_t indexOfHistoryLoad = -1;
      if (loadFromSessionHistory) {
        nsCOMPtr<nsISHEntry> root = nsSHistory::GetRootSHEntry(newActiveEntry);
        indexOfHistoryLoad = shistory->GetIndexOfEntry(root);
        if (indexOfHistoryLoad < 0) {
          // Entry has been removed from the session history.
          return;
        }
      }

      CallerWillNotifyHistoryIndexAndLengthChanges caller(shistory);

      // If there is a name in the new entry, clear the name of all contiguous
      // entries. This is for https://html.spec.whatwg.org/#history-traversal
      // Step 4.4.2.
      nsAutoString nameOfNewEntry;
      newActiveEntry->GetName(nameOfNewEntry);
      if (!nameOfNewEntry.IsEmpty()) {
        nsSHistory::WalkContiguousEntries(
            newActiveEntry,
            [](nsISHEntry* aEntry) { aEntry->SetName(EmptyString()); });
      }

      bool addEntry = ShouldUpdateSessionHistory(aLoadType);
      if (IsTop()) {
        if (mActiveEntry && !mActiveEntry->GetFrameLoader()) {
          bool sharesDocument = true;
          mActiveEntry->SharesDocumentWith(newActiveEntry, &sharesDocument);
          if (!sharesDocument) {
            // If the old page won't be in the bfcache,
            // clear the dynamic entries.
            RemoveDynEntriesFromActiveSessionHistoryEntry();
          }
        }

        if (LOAD_TYPE_HAS_FLAGS(aLoadType,
                                nsIWebNavigation::LOAD_FLAGS_REPLACE_HISTORY)) {
          // Replace the current entry with the new entry.
          int32_t index = shistory->GetIndexForReplace();

          // If we're trying to replace an inexistant shistory entry then we
          // should append instead.
          addEntry = index < 0;
          if (!addEntry) {
            shistory->ReplaceEntry(index, newActiveEntry);
          }
          mActiveEntry = newActiveEntry;
        } else if (LOAD_TYPE_HAS_FLAGS(
                       aLoadType, nsIWebNavigation::LOAD_FLAGS_IS_REFRESH) &&
                   !ShouldAddEntryForRefresh(newActiveEntry) && mActiveEntry) {
          addEntry = false;
          mActiveEntry->ReplaceWith(*newActiveEntry);
        } else {
          mActiveEntry = newActiveEntry;
        }

        if (loadFromSessionHistory) {
          // XXX Synchronize browsing context tree and session history tree?
          shistory->InternalSetRequestedIndex(indexOfHistoryLoad);
          shistory->UpdateIndex();

          if (IsTop()) {
            mActiveEntry->SetWireframe(Nothing());
          }
        } else if (addEntry) {
          shistory->AddEntry(mActiveEntry, aPersist);
          shistory->InternalSetRequestedIndex(-1);
        }
      } else {
        // FIXME The old implementations adds it to the parent's mLSHE if there
        //       is one, need to figure out if that makes sense here (peterv
        //       doesn't think it would).
        if (loadFromSessionHistory) {
          if (mActiveEntry) {
            // mActiveEntry is null if we're loading iframes from session
            // history while also parent page is loading from session history.
            // In that case there isn't anything to sync.
            mActiveEntry->SyncTreesForSubframeNavigation(newActiveEntry, Top(),
                                                         this);
          }
          mActiveEntry = newActiveEntry;

          shistory->InternalSetRequestedIndex(indexOfHistoryLoad);
          // FIXME UpdateIndex() here may update index too early (but even the
          //       old implementation seems to have similar issues).
          shistory->UpdateIndex();
        } else if (addEntry) {
          if (mActiveEntry) {
            if (LOAD_TYPE_HAS_FLAGS(
                    aLoadType, nsIWebNavigation::LOAD_FLAGS_REPLACE_HISTORY) ||
                (LOAD_TYPE_HAS_FLAGS(aLoadType,
                                     nsIWebNavigation::LOAD_FLAGS_IS_REFRESH) &&
                 !ShouldAddEntryForRefresh(newActiveEntry))) {
              // FIXME We need to make sure that when we create the info we
              //       make a copy of the shared state.
              mActiveEntry->ReplaceWith(*newActiveEntry);
            } else {
              // AddChildSHEntryHelper does update the index of the session
              // history!
              shistory->AddChildSHEntryHelper(mActiveEntry, newActiveEntry,
                                              Top(), aCloneEntryChildren);
              mActiveEntry = newActiveEntry;
            }
          } else {
            SessionHistoryEntry* parentEntry = GetParent()->mActiveEntry;
            // XXX What should happen if parent doesn't have mActiveEntry?
            //     Or can that even happen ever?
            if (parentEntry) {
              mActiveEntry = newActiveEntry;
              // FIXME Using IsInProcess for aUseRemoteSubframes isn't quite
              //       right, but aUseRemoteSubframes should be going away.
              parentEntry->AddChild(
                  mActiveEntry,
                  CreatedDynamically() ? -1 : GetParent()->IndexOf(this),
                  IsInProcess());
            }
          }
          shistory->InternalSetRequestedIndex(-1);
        }
      }

      ResetSHEntryHasUserInteractionCache();

      HistoryCommitIndexAndLength(aChangeID, caller);

      shistory->LogHistory();

      return;
    }
    // XXX Should the loading entries before [i] be removed?
  }
  // FIXME Should we throw an error if we don't find an entry for
  // aSessionHistoryEntryId?
}

already_AddRefed<nsDocShellLoadState> CanonicalBrowsingContext::CreateLoadInfo(
    SessionHistoryEntry* aEntry) {
  const SessionHistoryInfo& info = aEntry->Info();
  RefPtr<nsDocShellLoadState> loadState(new nsDocShellLoadState(info.GetURI()));
  info.FillLoadInfo(*loadState);
  UniquePtr<LoadingSessionHistoryInfo> loadingInfo;
  loadingInfo = MakeUnique<LoadingSessionHistoryInfo>(aEntry);
  mLoadingEntries.AppendElement(
      LoadingSessionHistoryEntry{loadingInfo->mLoadId, aEntry});
  loadState->SetLoadingSessionHistoryInfo(std::move(loadingInfo));

  return loadState.forget();
}

void CanonicalBrowsingContext::NotifyOnHistoryReload(
    bool aForceReload, bool& aCanReload,
    Maybe<NotNull<RefPtr<nsDocShellLoadState>>>& aLoadState,
    Maybe<bool>& aReloadActiveEntry) {
  MOZ_DIAGNOSTIC_ASSERT(!aLoadState);

  aCanReload = true;
  nsISHistory* shistory = GetSessionHistory();
  NS_ENSURE_TRUE_VOID(shistory);

  shistory->NotifyOnHistoryReload(&aCanReload);
  if (!aCanReload) {
    return;
  }

  if (mActiveEntry) {
    aLoadState.emplace(WrapMovingNotNull(RefPtr{CreateLoadInfo(mActiveEntry)}));
    aReloadActiveEntry.emplace(true);
    if (aForceReload) {
      shistory->RemoveFrameEntries(mActiveEntry);
    }
  } else if (!mLoadingEntries.IsEmpty()) {
    const LoadingSessionHistoryEntry& loadingEntry =
        mLoadingEntries.LastElement();
    uint64_t loadId = loadingEntry.mLoadId;
    aLoadState.emplace(
        WrapMovingNotNull(RefPtr{CreateLoadInfo(loadingEntry.mEntry)}));
    aReloadActiveEntry.emplace(false);
    if (aForceReload) {
      SessionHistoryEntry::LoadingEntry* entry =
          SessionHistoryEntry::GetByLoadId(loadId);
      if (entry) {
        shistory->RemoveFrameEntries(entry->mEntry);
      }
    }
  }

  if (aLoadState) {
    // Use 0 as the offset, since aLoadState will be be used for reload.
    aLoadState.ref()->SetLoadIsFromSessionHistory(0,
                                                  aReloadActiveEntry.value());
  }
  // If we don't have an active entry and we don't have a loading entry then
  // the nsDocShell will create a load state based on its document.
}

void CanonicalBrowsingContext::SetActiveSessionHistoryEntry(
    const Maybe<nsPoint>& aPreviousScrollPos, SessionHistoryInfo* aInfo,
    uint32_t aLoadType, uint32_t aUpdatedCacheKey, const nsID& aChangeID) {
  nsISHistory* shistory = GetSessionHistory();
  if (!shistory) {
    return;
  }
  CallerWillNotifyHistoryIndexAndLengthChanges caller(shistory);

  RefPtr<SessionHistoryEntry> oldActiveEntry = mActiveEntry;
  if (aPreviousScrollPos.isSome() && oldActiveEntry) {
    oldActiveEntry->SetScrollPosition(aPreviousScrollPos.ref().x,
                                      aPreviousScrollPos.ref().y);
  }
  mActiveEntry = new SessionHistoryEntry(aInfo);
  mActiveEntry->SetDocshellID(GetHistoryID());
  mActiveEntry->AdoptBFCacheEntry(oldActiveEntry);
  if (aUpdatedCacheKey != 0) {
    mActiveEntry->SharedInfo()->mCacheKey = aUpdatedCacheKey;
  }

  if (IsTop()) {
    Maybe<int32_t> previousEntryIndex, loadedEntryIndex;
    shistory->AddToRootSessionHistory(
        true, oldActiveEntry, this, mActiveEntry, aLoadType,
        nsDocShell::ShouldAddToSessionHistory(aInfo->GetURI(), nullptr),
        &previousEntryIndex, &loadedEntryIndex);
  } else {
    if (oldActiveEntry) {
      shistory->AddChildSHEntryHelper(oldActiveEntry, mActiveEntry, Top(),
                                      true);
    } else if (GetParent() && GetParent()->mActiveEntry) {
      GetParent()->mActiveEntry->AddChild(
          mActiveEntry, CreatedDynamically() ? -1 : GetParent()->IndexOf(this),
          UseRemoteSubframes());
    }
  }

  ResetSHEntryHasUserInteractionCache();

  shistory->InternalSetRequestedIndex(-1);

  // FIXME Need to do the equivalent of EvictDocumentViewersOrReplaceEntry.
  HistoryCommitIndexAndLength(aChangeID, caller);

  static_cast<nsSHistory*>(shistory)->LogHistory();
}

void CanonicalBrowsingContext::ReplaceActiveSessionHistoryEntry(
    SessionHistoryInfo* aInfo) {
  if (!mActiveEntry) {
    return;
  }

  // aInfo comes from the entry stored in the current document's docshell, whose
  // interaction state does not get updated. So we instead propagate state from
  // the previous canonical entry. See bug 1917369.
  const bool hasUserInteraction = mActiveEntry->GetHasUserInteraction();
  mActiveEntry->SetInfo(aInfo);
  mActiveEntry->SetHasUserInteraction(hasUserInteraction);
  // Notify children of the update
  nsSHistory* shistory = static_cast<nsSHistory*>(GetSessionHistory());
  if (shistory) {
    shistory->NotifyOnHistoryReplaceEntry();
    shistory->UpdateRootBrowsingContextState();
  }

  ResetSHEntryHasUserInteractionCache();

  if (IsTop()) {
    mActiveEntry->SetWireframe(Nothing());
  }

  // FIXME Need to do the equivalent of EvictDocumentViewersOrReplaceEntry.
}

void CanonicalBrowsingContext::RemoveDynEntriesFromActiveSessionHistoryEntry() {
  nsISHistory* shistory = GetSessionHistory();
  // In theory shistory can be null here if the method is called right after
  // CanonicalBrowsingContext::ReplacedBy call.
  NS_ENSURE_TRUE_VOID(shistory);
  nsCOMPtr<nsISHEntry> root = nsSHistory::GetRootSHEntry(mActiveEntry);
  shistory->RemoveDynEntries(shistory->GetIndexOfEntry(root), mActiveEntry);
}

void CanonicalBrowsingContext::RemoveFromSessionHistory(const nsID& aChangeID) {
  nsSHistory* shistory = static_cast<nsSHistory*>(GetSessionHistory());
  if (shistory) {
    CallerWillNotifyHistoryIndexAndLengthChanges caller(shistory);
    nsCOMPtr<nsISHEntry> root = nsSHistory::GetRootSHEntry(mActiveEntry);
    bool didRemove;
    AutoTArray<nsID, 16> ids({GetHistoryID()});
    shistory->RemoveEntries(ids, shistory->GetIndexOfEntry(root), &didRemove);
    if (didRemove) {
      RefPtr<BrowsingContext> rootBC = shistory->GetBrowsingContext();
      if (rootBC) {
        if (!rootBC->IsInProcess()) {
          if (ContentParent* cp = rootBC->Canonical()->GetContentParent()) {
            Unused << cp->SendDispatchLocationChangeEvent(rootBC);
          }
        } else if (rootBC->GetDocShell()) {
          rootBC->GetDocShell()->DispatchLocationChangeEvent();
        }
      }
    }
    HistoryCommitIndexAndLength(aChangeID, caller);
  }
}

Maybe<int32_t> CanonicalBrowsingContext::HistoryGo(
    int32_t aOffset, uint64_t aHistoryEpoch, bool aRequireUserInteraction,
    bool aUserActivation, Maybe<ContentParentId> aContentId) {
  if (aRequireUserInteraction && aOffset != -1 && aOffset != 1) {
    NS_ERROR(
        "aRequireUserInteraction may only be used with an offset of -1 or 1");
    return Nothing();
  }

  nsSHistory* shistory = static_cast<nsSHistory*>(GetSessionHistory());
  if (!shistory) {
    return Nothing();
  }

  CheckedInt<int32_t> index = shistory->GetRequestedIndex() >= 0
                                  ? shistory->GetRequestedIndex()
                                  : shistory->Index();
  MOZ_LOG(gSHLog, LogLevel::Debug,
          ("HistoryGo(%d->%d) epoch %" PRIu64 "/id %" PRIu64, aOffset,
           (index + aOffset).value(), aHistoryEpoch,
           (uint64_t)(aContentId.isSome() ? aContentId.value() : 0)));

  while (true) {
    index += aOffset;
    if (!index.isValid()) {
      MOZ_LOG(gSHLog, LogLevel::Debug, ("Invalid index"));
      return Nothing();
    }

    // Check for user interaction if desired, except for the first and last
    // history entries. We compare with >= to account for the case where
    // aOffset >= length.
    if (!StaticPrefs::browser_navigation_requireUserInteraction() ||
        !aRequireUserInteraction || index.value() >= shistory->Length() - 1 ||
        index.value() <= 0) {
      break;
    }
    if (shistory->HasUserInteractionAtIndex(index.value())) {
      break;
    }
  }

  // Implement aborting additional history navigations from within the same
  // event spin of the content process.

  uint64_t epoch;
  bool sameEpoch = false;
  Maybe<ContentParentId> id;
  shistory->GetEpoch(epoch, id);

  if (aContentId == id && epoch >= aHistoryEpoch) {
    sameEpoch = true;
    MOZ_LOG(gSHLog, LogLevel::Debug, ("Same epoch/id"));
  }
  // Don't update the epoch until we know if the target index is valid

  // GoToIndex checks that index is >= 0 and < length.
  nsTArray<nsSHistory::LoadEntryResult> loadResults;
  nsresult rv = shistory->GotoIndex(index.value(), loadResults, sameEpoch,
                                    aOffset == 0, aUserActivation);
  if (NS_FAILED(rv)) {
    MOZ_LOG(gSHLog, LogLevel::Debug,
            ("Dropping HistoryGo - bad index or same epoch (not in same doc)"));
    return Nothing();
  }
  if (epoch < aHistoryEpoch || aContentId != id) {
    MOZ_LOG(gSHLog, LogLevel::Debug, ("Set epoch"));
    shistory->SetEpoch(aHistoryEpoch, aContentId);
  }
  int32_t requestedIndex = shistory->GetRequestedIndex();
  nsSHistory::LoadURIs(loadResults);
  return Some(requestedIndex);
}

JSObject* CanonicalBrowsingContext::WrapObject(
    JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
  return CanonicalBrowsingContext_Binding::Wrap(aCx, this, aGivenProto);
}

void CanonicalBrowsingContext::DispatchWheelZoomChange(bool aIncrease) {
  Element* element = Top()->GetEmbedderElement();
  if (!element) {
    return;
  }

  auto event = aIncrease ? u"DoZoomEnlargeBy10"_ns : u"DoZoomReduceBy10"_ns;
  auto dispatcher = MakeRefPtr<AsyncEventDispatcher>(
      element, event, CanBubble::eYes, ChromeOnlyDispatch::eYes);
  dispatcher->PostDOMEvent();
}

void CanonicalBrowsingContext::CanonicalDiscard() {
  if (mTabMediaController) {
    mTabMediaController->Shutdown();
    mTabMediaController = nullptr;
  }

  if (mCurrentLoad) {
    mCurrentLoad->Cancel(NS_BINDING_ABORTED,
                         "CanonicalBrowsingContext::CanonicalDiscard"_ns);
  }

  if (mWebProgress) {
    RefPtr<BrowsingContextWebProgress> progress = mWebProgress;
    progress->ContextDiscarded();
  }

  if (IsTop()) {
    BackgroundSessionStorageManager::RemoveManager(Id());
  }

  CancelSessionStoreUpdate();

  if (UsePrivateBrowsing() && EverAttached() && IsContent()) {
    DecreasePrivateCount();
  }
}

void CanonicalBrowsingContext::CanonicalAttach() {
  if (UsePrivateBrowsing() && IsContent()) {
    IncreasePrivateCount();
  }
}

void CanonicalBrowsingContext::AddPendingDiscard() {
  MOZ_ASSERT(!mFullyDiscarded);
  mPendingDiscards++;
}

void CanonicalBrowsingContext::RemovePendingDiscard() {
  mPendingDiscards--;
  if (!mPendingDiscards) {
    mFullyDiscarded = true;
    auto listeners = std::move(mFullyDiscardedListeners);
    for (const auto& listener : listeners) {
      listener(Id());
    }
  }
}

void CanonicalBrowsingContext::AddFinalDiscardListener(
    std::function<void(uint64_t)>&& aListener) {
  if (mFullyDiscarded) {
    aListener(Id());
    return;
  }
  mFullyDiscardedListeners.AppendElement(std::move(aListener));
}

void CanonicalBrowsingContext::SetForceAppWindowActive(bool aForceActive,
                                                       ErrorResult& aRv) {
  MOZ_DIAGNOSTIC_ASSERT(IsChrome());
  MOZ_DIAGNOSTIC_ASSERT(IsTop());
  if (!IsChrome() || !IsTop()) {
    return aRv.ThrowNotAllowedError(
        "You shouldn't need to force this BrowsingContext to be active, use "
        ".isActive instead");
  }
  if (mForceAppWindowActive == aForceActive) {
    return;
  }
  mForceAppWindowActive = aForceActive;
  RecomputeAppWindowVisibility();
}

void CanonicalBrowsingContext::RecomputeAppWindowVisibility() {
  MOZ_RELEASE_ASSERT(IsChrome());
  MOZ_RELEASE_ASSERT(IsTop());

  const bool wasAlreadyActive = IsActive();

  nsCOMPtr<nsIWidget> widget;
  if (auto* docShell = GetDocShell()) {
    nsDocShell::Cast(docShell)->GetMainWidget(getter_AddRefs(widget));
  }

  Unused << NS_WARN_IF(!widget);
  const bool isNowActive =
      ForceAppWindowActive() || (widget && !widget->IsFullyOccluded() &&
                                 widget->SizeMode() != nsSizeMode_Minimized);

  if (isNowActive == wasAlreadyActive) {
    return;
  }

  SetIsActiveInternal(isNowActive, IgnoreErrors());
  if (widget) {
    // Pause if we are not active, resume if we are active.
    widget->PauseOrResumeCompositor(!isNowActive);
  }
}

void CanonicalBrowsingContext::AdjustPrivateBrowsingCount(
    bool aPrivateBrowsing) {
  if (IsDiscarded() || !EverAttached() || IsChrome()) {
    return;
  }

  MOZ_DIAGNOSTIC_ASSERT(aPrivateBrowsing == UsePrivateBrowsing());
  if (aPrivateBrowsing) {
    IncreasePrivateCount();
  } else {
    DecreasePrivateCount();
  }
}

void CanonicalBrowsingContext::NotifyStartDelayedAutoplayMedia() {
  WindowContext* windowContext = GetCurrentWindowContext();
  if (!windowContext) {
    return;
  }

  // As this function would only be called when user click the play icon on the
  // tab bar. That's clear user intent to play, so gesture activate the window
  // context so that the block-autoplay logic allows the media to autoplay.
  windowContext->NotifyUserGestureActivation();
  AUTOPLAY_LOG("NotifyStartDelayedAutoplayMedia for chrome bc 0x%08" PRIx64,
               Id());
  StartDelayedAutoplayMediaComponents();
  // Notfiy all content browsing contexts which are related with the canonical
  // browsing content tree to start delayed autoplay media.

  Group()->EachParent([&](ContentParent* aParent) {
    Unused << aParent->SendStartDelayedAutoplayMediaComponents(this);
  });
}

void CanonicalBrowsingContext::NotifyMediaMutedChanged(bool aMuted,
                                                       ErrorResult& aRv) {
  MOZ_ASSERT(!GetParent(),
             "Notify media mute change on non top-level context!");
  SetMuted(aMuted, aRv);
}

uint32_t CanonicalBrowsingContext::CountSiteOrigins(
    GlobalObject& aGlobal,
    const Sequence<OwningNonNull<BrowsingContext>>& aRoots) {
  nsTHashSet<nsCString> uniqueSiteOrigins;

  for (const auto& root : aRoots) {
    root->PreOrderWalk([&](BrowsingContext* aContext) {
      WindowGlobalParent* windowGlobalParent =
          aContext->Canonical()->GetCurrentWindowGlobal();
      if (windowGlobalParent) {
        nsIPrincipal* documentPrincipal =
            windowGlobalParent->DocumentPrincipal();

        bool isContentPrincipal = documentPrincipal->GetIsContentPrincipal();
        if (isContentPrincipal) {
          nsCString siteOrigin;
          documentPrincipal->GetSiteOrigin(siteOrigin);
          uniqueSiteOrigins.Insert(siteOrigin);
        }
      }
    });
  }

  return uniqueSiteOrigins.Count();
}

/* static */
bool CanonicalBrowsingContext::IsPrivateBrowsingActive() {
  return gNumberOfPrivateContexts > 0;
}

void CanonicalBrowsingContext::UpdateMediaControlAction(
    const MediaControlAction& aAction) {
  if (IsDiscarded()) {
    return;
  }
  ContentMediaControlKeyHandler::HandleMediaControlAction(this, aAction);
  Group()->EachParent([&](ContentParent* aParent) {
    Unused << aParent->SendUpdateMediaControlAction(this, aAction);
  });
}

void CanonicalBrowsingContext::LoadURI(nsIURI* aURI,
                                       const LoadURIOptions& aOptions,
                                       ErrorResult& aError) {
  RefPtr<nsDocShellLoadState> loadState;
  nsresult rv = nsDocShellLoadState::CreateFromLoadURIOptions(
      this, aURI, aOptions, getter_AddRefs(loadState));
  MOZ_ASSERT(rv != NS_ERROR_MALFORMED_URI);

  if (NS_FAILED(rv)) {
    aError.Throw(rv);
    return;
  }

  LoadURI(loadState, true);
}

void CanonicalBrowsingContext::FixupAndLoadURIString(
    const nsAString& aURI, const LoadURIOptions& aOptions,
    ErrorResult& aError) {
  RefPtr<nsDocShellLoadState> loadState;
  nsresult rv = nsDocShellLoadState::CreateFromLoadURIOptions(
      this, aURI, aOptions, getter_AddRefs(loadState));

  if (rv == NS_ERROR_MALFORMED_URI) {
    DisplayLoadError(aURI);
    return;
  }

  if (NS_FAILED(rv)) {
    aError.Throw(rv);
    return;
  }

  LoadURI(loadState, true);
}

void CanonicalBrowsingContext::GoBack(
    const Optional<int32_t>& aCancelContentJSEpoch,
    bool aRequireUserInteraction, bool aUserActivation) {
  if (IsDiscarded()) {
    return;
  }

  // Stop any known network loads if necessary.
  if (mCurrentLoad) {
    mCurrentLoad->Cancel(NS_BINDING_CANCELLED_OLD_LOAD, ""_ns);
  }

  if (RefPtr<nsDocShell> docShell = nsDocShell::Cast(GetDocShell())) {
    if (aCancelContentJSEpoch.WasPassed()) {
      docShell->SetCancelContentJSEpoch(aCancelContentJSEpoch.Value());
    }
    docShell->GoBack(aRequireUserInteraction, aUserActivation);
  } else if (ContentParent* cp = GetContentParent()) {
    Maybe<int32_t> cancelContentJSEpoch;
    if (aCancelContentJSEpoch.WasPassed()) {
      cancelContentJSEpoch = Some(aCancelContentJSEpoch.Value());
    }
    Unused << cp->SendGoBack(this, cancelContentJSEpoch,
                             aRequireUserInteraction, aUserActivation);
  }
}
void CanonicalBrowsingContext::GoForward(
    const Optional<int32_t>& aCancelContentJSEpoch,
    bool aRequireUserInteraction, bool aUserActivation) {
  if (IsDiscarded()) {
    return;
  }

  // Stop any known network loads if necessary.
  if (mCurrentLoad) {
    mCurrentLoad->Cancel(NS_BINDING_CANCELLED_OLD_LOAD, ""_ns);
  }

  if (RefPtr<nsDocShell> docShell = nsDocShell::Cast(GetDocShell())) {
    if (aCancelContentJSEpoch.WasPassed()) {
      docShell->SetCancelContentJSEpoch(aCancelContentJSEpoch.Value());
    }
    docShell->GoForward(aRequireUserInteraction, aUserActivation);
  } else if (ContentParent* cp = GetContentParent()) {
    Maybe<int32_t> cancelContentJSEpoch;
    if (aCancelContentJSEpoch.WasPassed()) {
      cancelContentJSEpoch.emplace(aCancelContentJSEpoch.Value());
    }
    Unused << cp->SendGoForward(this, cancelContentJSEpoch,
                                aRequireUserInteraction, aUserActivation);
  }
}
void CanonicalBrowsingContext::GoToIndex(
    int32_t aIndex, const Optional<int32_t>& aCancelContentJSEpoch,
    bool aUserActivation) {
  if (IsDiscarded()) {
    return;
  }

  // Stop any known network loads if necessary.
  if (mCurrentLoad) {
    mCurrentLoad->Cancel(NS_BINDING_CANCELLED_OLD_LOAD, ""_ns);
  }

  if (RefPtr<nsDocShell> docShell = nsDocShell::Cast(GetDocShell())) {
    if (aCancelContentJSEpoch.WasPassed()) {
      docShell->SetCancelContentJSEpoch(aCancelContentJSEpoch.Value());
    }
    docShell->GotoIndex(aIndex, aUserActivation);
  } else if (ContentParent* cp = GetContentParent()) {
    Maybe<int32_t> cancelContentJSEpoch;
    if (aCancelContentJSEpoch.WasPassed()) {
      cancelContentJSEpoch.emplace(aCancelContentJSEpoch.Value());
    }
    Unused << cp->SendGoToIndex(this, aIndex, cancelContentJSEpoch,
                                aUserActivation);
  }
}

void CanonicalBrowsingContext::Reload(uint32_t aReloadFlags) {
  if (IsDiscarded()) {
    return;
  }

  // Stop any known network loads if necessary.
  if (mCurrentLoad) {
    mCurrentLoad->Cancel(NS_BINDING_CANCELLED_OLD_LOAD, ""_ns);
  }

  if (RefPtr<nsDocShell> docShell = nsDocShell::Cast(GetDocShell())) {
    docShell->Reload(aReloadFlags);
  } else if (ContentParent* cp = GetContentParent()) {
    Unused << cp->SendReload(this, aReloadFlags);
  }
}

void CanonicalBrowsingContext::Stop(uint32_t aStopFlags) {
  if (IsDiscarded()) {
    return;
  }

  // Stop any known network loads if necessary.
  if (mCurrentLoad && (aStopFlags & nsIWebNavigation::STOP_NETWORK)) {
    mCurrentLoad->Cancel(NS_BINDING_ABORTED,
                         "CanonicalBrowsingContext::Stop"_ns);
  }

  // Ask the docshell to stop to handle loads that haven't
  // yet reached here, as well as non-network activity.
  if (auto* docShell = nsDocShell::Cast(GetDocShell())) {
    docShell->Stop(aStopFlags);
  } else if (ContentParent* cp = GetContentParent()) {
    Unused << cp->SendStopLoad(this, aStopFlags);
  }
}

void CanonicalBrowsingContext::PendingRemotenessChange::ProcessLaunched() {
  if (!mPromise) {
    return;
  }

  if (mContentParentKeepAlive) {
    // If our new content process is still unloading from a previous process
    // switch, wait for that unload to complete before continuing.
    auto found = mTarget->FindUnloadingHost(mContentParentKeepAlive->ChildID());
    if (found != mTarget->mUnloadingHosts.end()) {
      found->mCallbacks.AppendElement(
          [self = RefPtr{this}]()
              MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA { self->ProcessReady(); });
      return;
    }
  }

  ProcessReady();
}

void CanonicalBrowsingContext::PendingRemotenessChange::ProcessReady() {
  if (!mPromise) {
    return;
  }

  MOZ_ASSERT(!mProcessReady);
  mProcessReady = true;
  MaybeFinish();
}

void CanonicalBrowsingContext::PendingRemotenessChange::MaybeFinish() {
  if (!mPromise) {
    return;
  }

  if (!mProcessReady || mWaitingForPrepareToChange) {
    return;
  }

  // If this BrowsingContext is embedded within the parent process, perform the
  // process switch directly.
  nsresult rv = mTarget->IsTopContent() ? FinishTopContent() : FinishSubframe();
  if (NS_FAILED(rv)) {
    NS_WARNING("Error finishing PendingRemotenessChange!");
    Cancel(rv);
  } else {
    Clear();
  }
}

// Logic for finishing a toplevel process change embedded within the parent
// process. Due to frontend integration the logic differs substantially from
// subframe process switches, and is handled separately.
nsresult CanonicalBrowsingContext::PendingRemotenessChange::FinishTopContent() {
  MOZ_DIAGNOSTIC_ASSERT(mTarget->IsTop(),
                        "We shouldn't be trying to change the remoteness of "
                        "non-remote iframes");

  // Abort if our ContentParent died while process switching.
  if (mContentParentKeepAlive &&
      NS_WARN_IF(mContentParentKeepAlive->IsShuttingDown())) {
    return NS_ERROR_FAILURE;
  }

  // While process switching, we need to check if any of our ancestors are
  // discarded or no longer current, in which case the process switch needs to
  // be aborted.
  RefPtr<CanonicalBrowsingContext> target(mTarget);
  if (target->IsDiscarded() || !target->AncestorsAreCurrent()) {
    return NS_ERROR_FAILURE;
  }

  Element* browserElement = target->GetEmbedderElement();
  if (!browserElement) {
    return NS_ERROR_FAILURE;
  }

  nsCOMPtr<nsIBrowser> browser = browserElement->AsBrowser();
  if (!browser) {
    return NS_ERROR_FAILURE;
  }

  RefPtr<nsFrameLoaderOwner> frameLoaderOwner = do_QueryObject(browserElement);
  MOZ_RELEASE_ASSERT(frameLoaderOwner,
                     "embedder browser must be nsFrameLoaderOwner");

  // If we're process switching a browsing context in private browsing
  // mode we might decrease the private browsing count to '0', which
  // would make us fire "last-pb-context-exited" and drop the private
  // session. To prevent that we artificially increment the number of
  // private browsing contexts with '1' until the process switch is done.
  bool usePrivateBrowsing = mTarget->UsePrivateBrowsing();
  if (usePrivateBrowsing) {
    IncreasePrivateCount();
  }

  auto restorePrivateCount = MakeScopeExit([usePrivateBrowsing]() {
    if (usePrivateBrowsing) {
      DecreasePrivateCount();
    }
  });

  // Tell frontend code that this browser element is about to change process.
  nsresult rv = browser->BeforeChangeRemoteness();
  if (NS_FAILED(rv)) {
    return rv;
  }

  // Some frontend code checks the value of the `remote` attribute on the
  // browser to determine if it is remote, so update the value.
  browserElement->SetAttr(kNameSpaceID_None, nsGkAtoms::remote,
                          mContentParentKeepAlive ? u"true"_ns : u"false"_ns,
                          /* notify */ true);

  // The process has been created, hand off to nsFrameLoaderOwner to finish
--> --------------------

--> maximum size reached

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

98%


¤ Dauer der Verarbeitung: 0.36 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

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

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.