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

#ifndef mozilla_dom_BrowsingContext_h
#define mozilla_dom_BrowsingContext_h

#include <tuple>
#include "GVAutoplayRequestUtils.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/HalScreenConfiguration.h"
#include "mozilla/LinkedList.h"
#include "mozilla/Maybe.h"
#include "mozilla/RefPtr.h"
#include "mozilla/Span.h"
#include "mozilla/Tuple.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/LocationBase.h"
#include "mozilla/dom/MaybeDiscarded.h"
#include "mozilla/dom/PopupBlocker.h"
#include "mozilla/dom/UserActivation.h"
#include "mozilla/dom/BrowsingContextBinding.h"
#include "mozilla/dom/ScreenOrientationBinding.h"
#include "mozilla/dom/SyncedContext.h"
#include "nsCOMPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "nsIDocShell.h"
#include "nsTArray.h"
#include "nsWrapperCache.h"
#include "nsILoadInfo.h"
#include "nsILoadContext.h"
#include "nsThreadUtils.h"

class nsDocShellLoadState;
class nsGlobalWindowInner;
class nsGlobalWindowOuter;
class nsIPrincipal;
class nsOuterWindowProxy;
struct nsPoint;
class PickleIterator;

namespace IPC {
class Message;
class MessageReader;
class MessageWriter;
}  // namespace IPC

namespace mozilla {

class ErrorResult;
class LogModule;

namespace ipc {
class IProtocol;

template <typename T>
struct IPDLParamTraits;
}  // namespace ipc

namespace dom {
class BrowsingContent;
class BrowsingContextGroup;
class CanonicalBrowsingContext;
class ChildSHistory;
class ContentParent;
class Element;
struct LoadingSessionHistoryInfo;
template <typename>
struct Nullable;
template <typename T>
class Sequence;
class SessionHistoryInfo;
class SessionStorageManager;
class StructuredCloneHolder;
class WindowContext;
struct WindowPostMessageOptions;
class WindowProxyHolder;

enum class ExplicitActiveStatus : uint8_t {
  None,
  Active,
  Inactive,
  EndGuard_,
};

// Fields are, by default, settable by any process and readable by any process.
// Racy sets will be resolved as-if they occurred in the order the parent
// process finds out about them.
//
// The `DidSet` and `CanSet` methods may be overloaded to provide different
// behavior for a specific field.
//  * `DidSet` is called to run code in every process whenever the value is
//    updated (This currently occurs even if the value didn't change, though
//    this may change in the future).
//  * `CanSet` is called before attempting to set the value, in both the process
//    which calls `Set`, and the parent process, and will kill the misbehaving
//    process if it fails.
#define MOZ_EACH_BC_FIELD(FIELD)                                              \
  FIELD(Name, nsString)                                                       \
  FIELD(Closed, bool)                                                         \
  FIELD(ExplicitActive, ExplicitActiveStatus)                                 \
  /* Top()-only. If true, new-playing media will be suspended when in an      \
   * inactive browsing context. */                                            \
  FIELD(SuspendMediaWhenInactive, bool)                                       \
  /* If true, we're within the nested event loop in window.open, and this     \
   * context may not be used as the target of a load */                       \
  FIELD(PendingInitialization, bool)                                          \
  /* Indicates if the browser window is active for the purpose of the         \
   * :-moz-window-inactive pseudoclass. Only read from or set on the          \
   * top BrowsingContext. */                                                  \
  FIELD(IsActiveBrowserWindowInternal, bool)                                  \
  FIELD(OpenerPolicy, nsILoadInfo::CrossOriginOpenerPolicy)                   \
  /* Current opener for the BrowsingContext. Weak reference */                \
  FIELD(OpenerId, uint64_t)                                                   \
  FIELD(OnePermittedSandboxedNavigatorId, uint64_t)                           \
  /* WindowID of the inner window which embeds this BC */                     \
  FIELD(EmbedderInnerWindowId, uint64_t)                                      \
  FIELD(CurrentInnerWindowId, uint64_t)                                       \
  FIELD(HadOriginalOpener, bool)                                              \
  FIELD(IsPopupSpam, bool)                                                    \
  /* Hold the audio muted state and should be used on top level browsing      \
   * contexts only */                                                         \
  FIELD(Muted, bool)                                                          \
  /* Indicate that whether we should delay media playback, which would only   \
     be done on an unvisited tab. And this should only be used on the top     \
     level browsing contexts */                                               \
  FIELD(ShouldDelayMediaFromStart, bool)                                      \
  /* See nsSandboxFlags.h for the possible flags. */                          \
  FIELD(SandboxFlags, uint32_t)                                               \
  /* The value of SandboxFlags when the BrowsingContext is first created.     \
   * Used for sandboxing the initial about:blank document. */                 \
  FIELD(InitialSandboxFlags, uint32_t)                                        \
  /* A non-zero unique identifier for the browser element that is hosting     \
   * this                                                                     \
   * BrowsingContext tree. Every BrowsingContext in the element's tree will   \
   * return the same ID in all processes and it will remain stable            \
   * regardless of process changes. When a browser element's frameloader is   \
   * switched to another browser element this ID will remain the same but     \
   * hosted under the under the new browser element. */                       \
  FIELD(BrowserId, uint64_t)                                                  \
  FIELD(HistoryID, nsID)                                                      \
  FIELD(InRDMPane, bool)                                                      \
  FIELD(Loading, bool)                                                        \
  /* A field only set on top browsing contexts, which indicates that either:  \
   *                                                                          \
   *  * This is a browsing context created explicitly for printing or print   \
   *    preview (thus hosting static documents).                              \
   *                                                                          \
   *  * This is a browsing context where something in this tree is calling    \
   *    window.print() (and thus showing a modal dialog).                     \
   *                                                                          \
   * We use it exclusively to block navigation for both of these cases. */    \
  FIELD(IsPrinting, bool)                                                     \
  FIELD(AncestorLoading, bool)                                                \
  FIELD(AllowPlugins, bool)                                                   \
  FIELD(AllowContentRetargeting, bool)                                        \
  FIELD(AllowContentRetargetingOnChildren, bool)                              \
  FIELD(ForceEnableTrackingProtection, bool)                                  \
  FIELD(UseGlobalHistory, bool)                                               \
  FIELD(TargetTopLevelLinkClicksToBlankInternal, bool)                        \
  FIELD(FullscreenAllowedByOwner, bool)                                       \
  /*                                                                          \
   * "is popup" in the spec.                                                  \
   * Set only on top browsing contexts.                                       \
   * This doesn't indicate whether this is actually a popup or not,           \
   * but whether this browsing context is created by requesting popup or not. \
   * See also: nsWindowWatcher::ShouldOpenPopup.                              \
   */                                                                         \
  FIELD(IsPopupRequested, bool)                                               \
  /* These field are used to store the states of autoplay media request on    \
   * GeckoView only, and it would only be modified on the top level browsing  \
   * context. */                                                              \
  FIELD(GVAudibleAutoplayRequestStatus, GVAutoplayRequestStatus)              \
  FIELD(GVInaudibleAutoplayRequestStatus, GVAutoplayRequestStatus)            \
  /* ScreenOrientation-related APIs */                                        \
  FIELD(CurrentOrientationAngle, float)                                       \
  FIELD(CurrentOrientationType, mozilla::dom::OrientationType)                \
  FIELD(OrientationLock, mozilla::hal::ScreenOrientation)                     \
  FIELD(UserAgentOverride, nsString)                                          \
  FIELD(TouchEventsOverrideInternal, mozilla::dom::TouchEventsOverride)       \
  FIELD(EmbedderElementType, Maybe<nsString>)                                 \
  FIELD(MessageManagerGroup, nsString)                                        \
  FIELD(MaxTouchPointsOverride, uint8_t)                                      \
  FIELD(FullZoom, float)                                                      \
  FIELD(WatchedByDevToolsInternal, bool)                                      \
  FIELD(TextZoom, float)                                                      \
  FIELD(OverrideDPPX, float)                                                  \
  /* The current in-progress load. */                                         \
  FIELD(CurrentLoadIdentifier, Maybe<uint64_t>)                               \
  /* See nsIRequest for possible flags. */                                    \
  FIELD(DefaultLoadFlags, uint32_t)                                           \
  /* Signals that session history is enabled for this browsing context tree.  \
   * This is only ever set to true on the top BC, so consumers need to get    \
   * the value from the top BC! */                                            \
  FIELD(HasSessionHistory, bool)                                              \
  /* Tracks if this context is the only top-level document in the session     \
   * history of the context. */                                               \
  FIELD(IsSingleToplevelInHistory, bool)                                      \
  FIELD(UseErrorPages, bool)                                                  \
  FIELD(PlatformOverride, nsString)                                           \
  /* Specifies if this BC has loaded documents besides the initial            \
   * about:blank document. about:privatebrowsing, about:home, about:newtab    \
   * and non-initial about:blank are not considered to be initial             \
   * documents. */                                                            \
  FIELD(HasLoadedNonInitialDocument, bool)                                    \
  /* Default value for nsIContentViewer::authorStyleDisabled in any new       \
   * browsing contexts created as a descendant of this one.  Valid only for   \
   * top BCs. */                                                              \
  FIELD(AuthorStyleDisabledDefault, bool)                                     \
  FIELD(ServiceWorkersTestingEnabled, bool)                                   \
  FIELD(MediumOverride, nsString)                                             \
  /* DevTools override for prefers-color-scheme */                            \
  FIELD(PrefersColorSchemeOverride, dom::PrefersColorSchemeOverride)          \
  /* prefers-color-scheme override based on the color-scheme style of our     \
   * <browser> embedder element. */                                           \
  FIELD(EmbedderColorScheme, dom::PrefersColorSchemeOverride)                 \
  FIELD(DisplayMode, dom::DisplayMode)                                        \
  /* The number of entries added to the session history because of this       \
   * browsing context. */                                                     \
  FIELD(HistoryEntryCount, uint32_t)                                          \
  /* Don't use the getter of the field, but IsInBFCache() method */           \
  FIELD(IsInBFCache, bool)                                                    \
  FIELD(HasRestoreData, bool)                                                 \
  FIELD(SessionStoreEpoch, uint32_t)                                          \
  /* Whether we can execute scripts in this BrowsingContext. Has no effect    \
   * unless scripts are also allowed in the parent WindowContext. */          \
  FIELD(AllowJavascript, bool)                                                \
  /* The count of request that are used to prevent the browsing context tree  \
   * from being suspended, which would ONLY be modified on the top level      \
   * context in the chrome process because that's a non-atomic counter */     \
  FIELD(PageAwakeRequestCount, uint32_t)                                      \
  /* This field only gets incrememented when we start navigations in the      \
   * parent process. This is used for keeping track of the racing navigations \
   * between the parent and content processes. */                             \
  FIELD(ParentInitiatedNavigationEpoch, uint64_t)

// BrowsingContext, in this context, is the cross process replicated
// environment in which information about documents is stored. In
// particular the tree structure of nested browsing contexts is
// represented by the tree of BrowsingContexts.
//
// The tree of BrowsingContexts is created in step with its
// corresponding nsDocShell, and when nsDocShells are connected
// through a parent/child relationship, so are BrowsingContexts. The
// major difference is that BrowsingContexts are replicated (synced)
// to the parent process, making it possible to traverse the
// BrowsingContext tree for a tab, in both the parent and the child
// process.
//
// Trees of BrowsingContexts should only ever contain nodes of the
// same BrowsingContext::Type. This is enforced by asserts in the
// BrowsingContext::Create* methods.
class BrowsingContext : public nsILoadContext, public nsWrapperCache {
  MOZ_DECL_SYNCED_CONTEXT(BrowsingContext, MOZ_EACH_BC_FIELD)

 public:
  enum class Type { Chrome, Content };

  static void Init();
  static LogModule* GetLog();
  static LogModule* GetSyncLog();

  // Look up a BrowsingContext in the current process by ID.
  static already_AddRefed<BrowsingContext> Get(uint64_t aId);
  static already_AddRefed<BrowsingContext> Get(GlobalObject&, uint64_t aId) {
    return Get(aId);
  }
  // Look up the top-level BrowsingContext by BrowserID.
  static already_AddRefed<BrowsingContext> GetCurrentTopByBrowserId(
      uint64_t aBrowserId);
  static already_AddRefed<BrowsingContext> GetCurrentTopByBrowserId(
      GlobalObject&, uint64_t aId) {
    return GetCurrentTopByBrowserId(aId);
  }

  static void UpdateCurrentTopByBrowserId(BrowsingContext* aNewBrowsingContext);

  static already_AddRefed<BrowsingContext> GetFromWindow(
      WindowProxyHolder& aProxy);
  static already_AddRefed<BrowsingContext> GetFromWindow(
      GlobalObject&, WindowProxyHolder& aProxy) {
    return GetFromWindow(aProxy);
  }

  static void DiscardFromContentParent(ContentParent* aCP);

  // Create a brand-new toplevel BrowsingContext with no relationships to other
  // BrowsingContexts, and which is not embedded within any <browser> or frame
  // element.
  //
  // This BrowsingContext is immediately attached, and cannot have LoadContext
  // flags customized unless it is of `Type::Chrome`.
  //
  // The process which created this BrowsingContext is responsible for detaching
  // it.
  static already_AddRefed<BrowsingContext> CreateIndependent(Type aType);

  // Create a brand-new BrowsingContext object, but does not immediately attach
  // it. State such as OriginAttributes and PrivateBrowsingId may be customized
  // to configure the BrowsingContext before it is attached.
  //
  // `EnsureAttached()` must be called before the BrowsingContext is used for a
  // DocShell, BrowserParent, or BrowserBridgeChild.
  static already_AddRefed<BrowsingContext> CreateDetached(
      nsGlobalWindowInner* aParent, BrowsingContext* aOpener,
      BrowsingContextGroup* aSpecificGroup, const nsAString& aName, Type aType,
      bool aIsPopupRequested, bool aCreatedDynamically = false);

  void EnsureAttached();

  bool EverAttached() const { return mEverAttached; }

  // Cast this object to a canonical browsing context, and return it.
  CanonicalBrowsingContext* Canonical();

  // Is the most recent Document in this BrowsingContext loaded within this
  // process? This may be true with a null mDocShell after the Window has been
  // closed.
  bool IsInProcess() const { return mIsInProcess; }

  bool IsOwnedByProcess() const;

  bool CanHaveRemoteOuterProxies() const {
    return !mIsInProcess || mDanglingRemoteOuterProxies;
  }

  // Has this BrowsingContext been discarded. A discarded browsing context has
  // been destroyed, and may not be available on the other side of an IPC
  // message.
  bool IsDiscarded() const { return mIsDiscarded; }

  // Returns true if none of the BrowsingContext's ancestor BrowsingContexts or
  // WindowContexts are discarded or cached.
  bool AncestorsAreCurrent() const;

  bool Windowless() const { return mWindowless; }

  // Get the DocShell for this BrowsingContext if it is in-process, or
  // null if it's not.
  nsIDocShell* GetDocShell() const { return mDocShell; }
  void SetDocShell(nsIDocShell* aDocShell);
  void ClearDocShell() { mDocShell = nullptr; }

  // Get the Document for this BrowsingContext if it is in-process, or
  // null if it's not.
  Document* GetDocument() const {
    return mDocShell ? mDocShell->GetDocument() : nullptr;
  }
  Document* GetExtantDocument() const {
    return mDocShell ? mDocShell->GetExtantDocument() : nullptr;
  }

  // This cleans up remote outer window proxies that might have been left behind
  // when the browsing context went from being remote to local. It does this by
  // turning them into cross-compartment wrappers to aOuter. If there is already
  // a remote proxy in the compartment of aOuter, then aOuter will get swapped
  // to it and the value of aOuter will be set to the object that used to be the
  // remote proxy and is now an OuterWindowProxy.
  void CleanUpDanglingRemoteOuterWindowProxies(
      JSContext* aCx, JS::MutableHandle<JSObject*> aOuter);

  // Get the embedder element for this BrowsingContext if the embedder is
  // in-process, or null if it's not.
  Element* GetEmbedderElement() const { return mEmbedderElement; }
  void SetEmbedderElement(Element* aEmbedder);

  // Called after the BrowingContext has been embedded in a FrameLoader. This
  // happens after `SetEmbedderElement` is called on the BrowsingContext and
  // after the BrowsingContext has been set on the FrameLoader.
  void Embed();

  // Get the outer window object for this BrowsingContext if it is in-process
  // and still has a docshell, or null otherwise.
  nsPIDOMWindowOuter* GetDOMWindow() const {
    return mDocShell ? mDocShell->GetWindow() : nullptr;
  }

  uint64_t GetRequestContextId() const { return mRequestContextId; }

  // Detach the current BrowsingContext from its parent, in both the
  // child and the parent process.
  void Detach(bool aFromIPC = false);

  // Prepare this BrowsingContext to leave the current process.
  void PrepareForProcessChange();

  // Triggers a load in the process which currently owns this BrowsingContext.
  nsresult LoadURI(nsDocShellLoadState* aLoadState,
                   bool aSetNavigating = false);

  nsresult InternalLoad(nsDocShellLoadState* aLoadState);

  // Removes the root document for this BrowsingContext tree from the BFCache,
  // if it is cached, and returns true if it was.
  bool RemoveRootFromBFCacheSync();

  // If the load state includes a source BrowsingContext has been passed, check
  // to see if we are sandboxed from it as the result of an iframe or CSP
  // sandbox.
  nsresult CheckSandboxFlags(nsDocShellLoadState* aLoadState);

  void DisplayLoadError(const nsAString& aURI);

  // Check that this browsing context is targetable for navigations (i.e. that
  // it is neither closed, cached, nor discarded).
  bool IsTargetable() const;

  // True if this browsing context is inactive and is able to be suspended.
  bool InactiveForSuspend() const;

  const nsString& Name() const { return GetName(); }
  void GetName(nsAString& aName) { aName = GetName(); }
  bool NameEquals(const nsAString& aName) { return GetName().Equals(aName); }

  Type GetType() const { return mType; }
  bool IsContent() const { return mType == Type::Content; }
  bool IsChrome() const { return !IsContent(); }

  bool IsTop() const { return !GetParent(); }
  bool IsSubframe() const { return !IsTop(); }

  bool IsTopContent() const { return IsContent() && IsTop(); }

  bool IsInSubtreeOf(BrowsingContext* aContext);

  bool IsContentSubframe() const { return IsContent() && IsSubframe(); }

  // non-zero
  uint64_t Id() const { return mBrowsingContextId; }

  BrowsingContext* GetParent() const;
  BrowsingContext* Top();
  const BrowsingContext* Top() const;

  int32_t IndexOf(BrowsingContext* aChild);

  // NOTE: Unlike `GetEmbedderWindowGlobal`, `GetParentWindowContext` does not
  // cross toplevel content browser boundaries.
  WindowContext* GetParentWindowContext() const { return mParentWindow; }
  WindowContext* GetTopWindowContext() const;

  already_AddRefed<BrowsingContext> GetOpener() const {
    RefPtr<BrowsingContext> opener(Get(GetOpenerId()));
    if (!mIsDiscarded && opener && !opener->mIsDiscarded) {
      MOZ_DIAGNOSTIC_ASSERT(opener->mType == mType);
      return opener.forget();
    }
    return nullptr;
  }
  void SetOpener(BrowsingContext* aOpener) {
    MOZ_DIAGNOSTIC_ASSERT(!aOpener || aOpener->Group() == Group());
    MOZ_DIAGNOSTIC_ASSERT(!aOpener || aOpener->mType == mType);

    MOZ_ALWAYS_SUCCEEDS(SetOpenerId(aOpener ? aOpener->Id() : 0));
  }

  bool HasOpener() const;

  bool HadOriginalOpener() const { return GetHadOriginalOpener(); }

  // Returns true if the browsing context and top context are same origin
  bool SameOriginWithTop();

  /**
   * When a new browsing context is opened by a sandboxed document, it needs to
   * keep track of the browsing context that opened it, so that it can be
   * navigated by it.  This is the "one permitted sandboxed navigator".
   */
  already_AddRefed<BrowsingContext> GetOnePermittedSandboxedNavigator() const {
    return Get(GetOnePermittedSandboxedNavigatorId());
  }
  [[nodiscard]] nsresult SetOnePermittedSandboxedNavigator(
      BrowsingContext* aNavigator) {
    if (GetOnePermittedSandboxedNavigatorId()) {
      MOZ_ASSERT(false,
                 "One Permitted Sandboxed Navigator should only be set once.");
      return NS_ERROR_FAILURE;
    } else {
      return SetOnePermittedSandboxedNavigatorId(aNavigator ? aNavigator->Id()
                                                            : 0);
    }
  }

  uint32_t SandboxFlags() const { return GetSandboxFlags(); }

  Span<RefPtr<BrowsingContext>> Children() const;
  void GetChildren(nsTArray<RefPtr<BrowsingContext>>& aChildren);

  const nsTArray<RefPtr<WindowContext>>& GetWindowContexts() {
    return mWindowContexts;
  }
  void GetWindowContexts(nsTArray<RefPtr<WindowContext>>& aWindows);

  void RegisterWindowContext(WindowContext* aWindow);
  void UnregisterWindowContext(WindowContext* aWindow);
  WindowContext* GetCurrentWindowContext() const {
    return mCurrentWindowContext;
  }

  // Helpers to traverse this BrowsingContext subtree. Note that these will only
  // traverse active contexts, and will ignore ones in the BFCache.
  enum class WalkFlag {
    Next,
    Skip,
    Stop,
  };

  /**
   * Walk the browsing context tree in pre-order and call `aCallback`
   * for every node in the tree. PreOrderWalk accepts two types of
   * callbacks, either of the type `void(BrowsingContext*)` or
   * `WalkFlag(BrowsingContext*)`. The former traverses the entire
   * tree, but the latter let's you control if a sub-tree should be
   * skipped by returning `WalkFlag::Skip`, completely abort traversal
   * by returning `WalkFlag::Stop` or continue as normal with
   * `WalkFlag::Next`.
   */
  template <typename F>
  void PreOrderWalk(F&& aCallback) {
    if constexpr (std::is_void_v<
                      typename std::invoke_result_t<F, BrowsingContext*>>) {
      PreOrderWalkVoid(std::forward<F>(aCallback));
    } else {
      PreOrderWalkFlag(std::forward<F>(aCallback));
    }
  }

  void PreOrderWalkVoid(const std::function<void(BrowsingContext*)>& aCallback);
  WalkFlag PreOrderWalkFlag(
      const std::function<WalkFlag(BrowsingContext*)>& aCallback);

  void PostOrderWalk(const std::function<void(BrowsingContext*)>& aCallback);

  void GetAllBrowsingContextsInSubtree(
      nsTArray<RefPtr<BrowsingContext>>& aBrowsingContexts);

  BrowsingContextGroup* Group() { return mGroup; }

  // WebIDL bindings for nsILoadContext
  Nullable<WindowProxyHolder> GetAssociatedWindow();
  Nullable<WindowProxyHolder> GetTopWindow();
  Element* GetTopFrameElement();
  bool GetIsContent() { return IsContent(); }
  void SetUsePrivateBrowsing(bool aUsePrivateBrowsing, ErrorResult& aError);
  // Needs a different name to disambiguate from the xpidl method with
  // the same signature but different return value.
  void SetUseTrackingProtectionWebIDL(bool aUseTrackingProtection,
                                      ErrorResult& aRv);
  bool UseTrackingProtectionWebIDL() { return UseTrackingProtection(); }
  void GetOriginAttributes(JSContext* aCx, JS::MutableHandle<JS::Value> aVal,
                           ErrorResult& aError);

  bool InRDMPane() const { return GetInRDMPane(); }

  bool WatchedByDevTools();
  void SetWatchedByDevTools(bool aWatchedByDevTools, ErrorResult& aRv);

  dom::TouchEventsOverride TouchEventsOverride() const;
  bool TargetTopLevelLinkClicksToBlank() const;

  bool FullscreenAllowed() const;

  float FullZoom() const { return GetFullZoom(); }
  float TextZoom() const { return GetTextZoom(); }

  float OverrideDPPX() const { return Top()->GetOverrideDPPX(); }

  bool SuspendMediaWhenInactive() const {
    return GetSuspendMediaWhenInactive();
  }

  bool IsActive() const;
  void SetIsActive(bool aIsActive, mozilla::ErrorResult& aRv) {
    SetExplicitActive(aIsActive ? ExplicitActiveStatus::Active
                                : ExplicitActiveStatus::Inactive,
                      aRv);
  }

  bool AuthorStyleDisabledDefault() const {
    return GetAuthorStyleDisabledDefault();
  }

  bool UseGlobalHistory() const { return GetUseGlobalHistory(); }

  bool GetIsActiveBrowserWindow();

  void SetIsActiveBrowserWindow(bool aActive);

  uint64_t BrowserId() const { return GetBrowserId(); }

  bool IsLoading();

  void GetEmbedderElementType(nsString& aElementType) {
    if (GetEmbedderElementType().isSome()) {
      aElementType = GetEmbedderElementType().value();
    }
  }

  bool IsLoadingIdentifier(uint64_t aLoadIdentifer) {
    if (GetCurrentLoadIdentifier() &&
        *GetCurrentLoadIdentifier() == aLoadIdentifer) {
      return true;
    }
    return false;
  }

  // ScreenOrientation related APIs
  [[nodiscard]] nsresult SetCurrentOrientation(OrientationType aType,
                                               float aAngle) {
    Transaction txn;
    txn.SetCurrentOrientationType(aType);
    txn.SetCurrentOrientationAngle(aAngle);
    return txn.Commit(this);
  }

  void SetRDMPaneOrientation(OrientationType aType, float aAngle,
                             ErrorResult& aRv) {
    if (InRDMPane()) {
      if (NS_FAILED(SetCurrentOrientation(aType, aAngle))) {
        aRv.ThrowInvalidStateError("Browsing context is discarded");
      }
    }
  }

  void SetRDMPaneMaxTouchPoints(uint8_t aMaxTouchPoints, ErrorResult& aRv) {
    if (InRDMPane()) {
      SetMaxTouchPointsOverride(aMaxTouchPoints, aRv);
    }
  }

  // Using the rules for choosing a browsing context we try to find
  // the browsing context with the given name in the set of
  // transitively reachable browsing contexts. Performs access control
  // checks with regard to this.
  // See
  // https://html.spec.whatwg.org/multipage/browsers.html#the-rules-for-choosing-a-browsing-context-given-a-browsing-context-name.
  //
  // BrowsingContext::FindWithName(const nsAString&) is equivalent to
  // calling nsIDocShellTreeItem::FindItemWithName(aName, nullptr,
  // nullptr, false, <return value>).
  BrowsingContext* FindWithName(const nsAString& aName,
                                bool aUseEntryGlobalForAccessCheck = true);

  // Find a browsing context in this context's list of
  // children. Doesn't consider the special names, '_self', '_parent',
  // '_top', or '_blank'. Performs access control checks with regard to
  // 'this'.
  BrowsingContext* FindChildWithName(const nsAString& aName,
                                     BrowsingContext& aRequestingContext);

  // Find a browsing context in the subtree rooted at 'this' Doesn't
  // consider the special names, '_self', '_parent', '_top', or
  // '_blank'. Performs access control checks with regard to
  // 'aRequestingContext'.
  BrowsingContext* FindWithNameInSubtree(const nsAString& aName,
                                         BrowsingContext& aRequestingContext);

  nsISupports* GetParentObject() const;
  JSObject* WrapObject(JSContext* aCx,
                       JS::Handle<JSObject*> aGivenProto) override;

  // Return the window proxy object that corresponds to this browsing context.
  inline JSObject* GetWindowProxy() const { return mWindowProxy; }
  inline JSObject* GetUnbarrieredWindowProxy() const {
    return mWindowProxy.unbarrieredGet();
  }

  // Set the window proxy object that corresponds to this browsing context.
  void SetWindowProxy(JS::Handle<JSObject*> aWindowProxy) {
    mWindowProxy = aWindowProxy;
  }

  Nullable<WindowProxyHolder> GetWindow();

  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
  NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(BrowsingContext)
  NS_DECL_NSILOADCONTEXT

  // Window APIs that are cross-origin-accessible (from the HTML spec).
  WindowProxyHolder Window();
  BrowsingContext* GetBrowsingContext() { return this; };
  BrowsingContext* Self() { return this; }
  void Location(JSContext* aCx, JS::MutableHandle<JSObject*> aLocation,
                ErrorResult& aError);
  void Close(CallerType aCallerType, ErrorResult& aError);
  bool GetClosed(ErrorResult&) { return GetClosed(); }
  void Focus(CallerType aCallerType, ErrorResult& aError);
  void Blur(CallerType aCallerType, ErrorResult& aError);
  WindowProxyHolder GetFrames(ErrorResult& aError);
  int32_t Length() const { return Children().Length(); }
  Nullable<WindowProxyHolder> GetTop(ErrorResult& aError);
  void GetOpener(JSContext* aCx, JS::MutableHandle<JS::Value> aOpener,
                 ErrorResult& aError) const;
  Nullable<WindowProxyHolder> GetParent(ErrorResult& aError);
  void PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                      const nsAString& aTargetOrigin,
                      const Sequence<JSObject*>& aTransfer,
                      nsIPrincipal& aSubjectPrincipal, ErrorResult& aError);
  void PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                      const WindowPostMessageOptions& aOptions,
                      nsIPrincipal& aSubjectPrincipal, ErrorResult& aError);

  void GetCustomUserAgent(nsAString& aUserAgent) {
    aUserAgent = Top()->GetUserAgentOverride();
  }
  nsresult SetCustomUserAgent(const nsAString& aUserAgent);
  void SetCustomUserAgent(const nsAString& aUserAgent, ErrorResult& aRv);

  void GetCustomPlatform(nsAString& aPlatform) {
    aPlatform = Top()->GetPlatformOverride();
  }
  void SetCustomPlatform(const nsAString& aPlatform, ErrorResult& aRv);

  JSObject* WrapObject(JSContext* aCx);

  static JSObject* ReadStructuredClone(JSContext* aCx,
                                       JSStructuredCloneReader* aReader,
                                       StructuredCloneHolder* aHolder);
  bool WriteStructuredClone(JSContext* aCx, JSStructuredCloneWriter* aWriter,
                            StructuredCloneHolder* aHolder);

  void StartDelayedAutoplayMediaComponents();

  [[nodiscard]] nsresult ResetGVAutoplayRequestStatus();

  /**
   * Information required to initialize a BrowsingContext in another process.
   * This object may be serialized over IPC.
   */
  struct IPCInitializer {
    uint64_t mId = 0;

    // IDs are used for Parent and Opener to allow for this object to be
    // deserialized before other BrowsingContext in the BrowsingContextGroup
    // have been initialized.
    uint64_t mParentId = 0;
    already_AddRefed<WindowContext> GetParent();
    already_AddRefed<BrowsingContext> GetOpener();

    uint64_t GetOpenerId() const { return mFields.mOpenerId; }

    bool mWindowless = false;
    bool mUseRemoteTabs = false;
    bool mUseRemoteSubframes = false;
    bool mCreatedDynamically = false;
    int32_t mChildOffset = 0;
    int32_t mSessionHistoryIndex = -1;
    int32_t mSessionHistoryCount = 0;
    OriginAttributes mOriginAttributes;
    uint64_t mRequestContextId = 0;

    FieldValues mFields;
  };

  // Create an IPCInitializer object for this BrowsingContext.
  IPCInitializer GetIPCInitializer();

  // Create a BrowsingContext object from over IPC.
  static void CreateFromIPC(IPCInitializer&& aInitializer,
                            BrowsingContextGroup* aGroup,
                            ContentParent* aOriginProcess);

  // Performs access control to check that 'this' can access 'aTarget'.
  bool CanAccess(BrowsingContext* aTarget, bool aConsiderOpener = true);

  bool IsSandboxedFrom(BrowsingContext* aTarget);

  // The runnable will be called once there is idle time, or the top level
  // page has been loaded or if a timeout has fired.
  // Must be called only on the top level BrowsingContext.
  void AddDeprioritizedLoadRunner(nsIRunnable* aRunner);

  RefPtr<SessionStorageManager> GetSessionStorageManager();

  // Set PendingInitialization on this BrowsingContext before the context has
  // been attached.
  void InitPendingInitialization(bool aPendingInitialization) {
    MOZ_ASSERT(!EverAttached());
    mFields.SetWithoutSyncing<IDX_PendingInitialization>(
        aPendingInitialization);
  }

  bool CreatedDynamically() const { return mCreatedDynamically; }

  // Returns true if this browsing context, or any ancestor to this browsing
  // context was created dynamically. See also `CreatedDynamically`.
  bool IsDynamic() const;

  int32_t ChildOffset() const { return mChildOffset; }

  bool GetOffsetPath(nsTArray<uint32_t>& aPath) const;

  const OriginAttributes& OriginAttributesRef() { return mOriginAttributes; }
  nsresult SetOriginAttributes(const OriginAttributes& aAttrs);

  void GetHistoryID(JSContext* aCx, JS::MutableHandle<JS::Value> aVal,
                    ErrorResult& aError);

  // This should only be called on the top browsing context.
  void InitSessionHistory();

  // This will only ever return a non-null value if called on the top browsing
  // context.
  ChildSHistory* GetChildSessionHistory();

  bool CrossOriginIsolated();

  // Check if it is allowed to open a popup from the current browsing
  // context or any of its ancestors.
  bool IsPopupAllowed();

  // aCurrentURI is only required to be non-null if the load type contains the
  // nsIWebNavigation::LOAD_FLAGS_IS_REFRESH flag and aInfo is for a refresh to
  // the current URI.
  void SessionHistoryCommit(const LoadingSessionHistoryInfo& aInfo,
                            uint32_t aLoadType, nsIURI* aCurrentURI,
                            bool aHadActiveEntry, bool aPersist,
                            bool aCloneEntryChildren, bool aChannelExpired,
                            uint32_t aCacheKey);

  // Set a new active entry on this browsing context. This is used for
  // implementing history.pushState/replaceState and same document navigations.
  // The new active entry will be linked to the current active entry through
  // its shared state.
  // aPreviousScrollPos is the scroll position that needs to be saved on the
  // previous active entry.
  // aUpdatedCacheKey is the cache key to set on the new active entry. If
  // aUpdatedCacheKey is 0 then it will be ignored.
  void SetActiveSessionHistoryEntry(const Maybe<nsPoint>& aPreviousScrollPos,
                                    SessionHistoryInfo* aInfo,
                                    uint32_t aLoadType,
                                    uint32_t aUpdatedCacheKey,
                                    bool aUpdateLength = true);

  // Replace the active entry for this browsing context. This is used for
  // implementing history.replaceState and same document navigations.
  void ReplaceActiveSessionHistoryEntry(SessionHistoryInfo* aInfo);

  // Removes dynamic child entries of the active entry.
  void RemoveDynEntriesFromActiveSessionHistoryEntry();

  // Removes entries corresponding to this BrowsingContext from session history.
  void RemoveFromSessionHistory(const nsID& aChangeID);

  void SetTriggeringAndInheritPrincipals(nsIPrincipal* aTriggeringPrincipal,
                                         nsIPrincipal* aPrincipalToInherit,
                                         uint64_t aLoadIdentifier);

  // Return mTriggeringPrincipal and mPrincipalToInherit if the load id
  // saved with the principal matches the current load identifier of this BC.
  Tuple<nsCOMPtr<nsIPrincipal>, nsCOMPtr<nsIPrincipal>>
  GetTriggeringAndInheritPrincipalsForCurrentLoad();

  void HistoryGo(int32_t aOffset, uint64_t aHistoryEpoch,
                 bool aRequireUserInteraction, bool aUserActivation,
                 std::function<void(int32_t&&)>&& aResolver);

  bool ShouldUpdateSessionHistory(uint32_t aLoadType);

  // Checks if we reached the rate limit for calls to Location and History API.
  // The rate limit is controlled by the
  // "dom.navigation.locationChangeRateLimit" prefs.
  // Rate limit applies per BrowsingContext.
  // Returns NS_OK if we are below the rate limit and increments the counter.
  // Returns NS_ERROR_DOM_SECURITY_ERR if limit is reached.
  nsresult CheckLocationChangeRateLimit(CallerType aCallerType);

  void ResetLocationChangeRateLimit();

  mozilla::dom::DisplayMode DisplayMode() { return Top()->GetDisplayMode(); }

  // Returns canFocus, isActive
  std::tuple<bool, bool> CanFocusCheck(CallerType aCallerType);

  bool CanBlurCheck(CallerType aCallerType);

  PopupBlocker::PopupControlState RevisePopupAbuseLevel(
      PopupBlocker::PopupControlState aControl);

  void IncrementHistoryEntryCountForBrowsingContext();

  bool ServiceWorkersTestingEnabled() const {
    return GetServiceWorkersTestingEnabled();
  }

  void GetMediumOverride(nsAString& aOverride) const {
    aOverride = GetMediumOverride();
  }

  dom::PrefersColorSchemeOverride PrefersColorSchemeOverride() const {
    return GetPrefersColorSchemeOverride();
  }

  bool IsInBFCache() const;

  bool AllowJavascript() const { return GetAllowJavascript(); }
  bool CanExecuteScripts() const { return mCanExecuteScripts; }

  uint32_t DefaultLoadFlags() const { return GetDefaultLoadFlags(); }

  // When request for page awake, it would increase a count that is used to
  // prevent whole browsing context tree from being suspended. The request can
  // be called multiple times. When calling the revoke, it would decrease the
  // count and once the count reaches to zero, the browsing context tree could
  // be suspended when the tree is inactive.
  void RequestForPageAwake();
  void RevokeForPageAwake();

  void AddDiscardListener(std::function<void(uint64_t)>&& aListener);

 protected:
  virtual ~BrowsingContext();
  BrowsingContext(WindowContext* aParentWindow, BrowsingContextGroup* aGroup,
                  uint64_t aBrowsingContextId, Type aType, FieldValues&& aInit);

  void SetChildSHistory(ChildSHistory* aChildSHistory);
  already_AddRefed<ChildSHistory> ForgetChildSHistory() {
    // FIXME Do we need to unset mHasSessionHistory?
    return mChildSessionHistory.forget();
  }

  static bool ShouldAddEntryForRefresh(nsIURI* aCurrentURI,
                                       const SessionHistoryInfo& aInfo);
  static bool ShouldAddEntryForRefresh(nsIURI* aCurrentURI, nsIURI* aNewURI,
                                       bool aHasPostData);

 private:
  void Attach(bool aFromIPC, ContentParent* aOriginProcess);

  // Recomputes whether we can execute scripts in this BrowsingContext based on
  // the value of AllowJavascript() and whether scripts are allowed in the
  // parent WindowContext. Called whenever the AllowJavascript() flag or the
  // parent WC changes.
  void RecomputeCanExecuteScripts();

  // Find the special browsing context if aName is '_self', '_parent',
  // '_top', but not '_blank'. The latter is handled in FindWithName
  BrowsingContext* FindWithSpecialName(const nsAString& aName,
                                       BrowsingContext& aRequestingContext);

  // Is it early enough in the BrowsingContext's lifecycle that it is still
  // OK to set OriginAttributes?
  bool CanSetOriginAttributes();

  void AssertOriginAttributesMatchPrivateBrowsing();

  // Assert that the BrowsingContext's LoadContext flags appear coherent
  // relative to related BrowsingContexts.
  void AssertCoherentLoadContext();

  friend class ::nsOuterWindowProxy;
  friend class ::nsGlobalWindowOuter;
  friend class WindowContext;

  // Update the window proxy object that corresponds to this browsing context.
  // This should be called from the window proxy object's objectMoved hook, if
  // the object mWindowProxy points to was moved by the JS GC.
  void UpdateWindowProxy(JSObject* obj, JSObject* old) {
    if (mWindowProxy) {
      MOZ_ASSERT(mWindowProxy == old);
      mWindowProxy = obj;
    }
  }
  // Clear the window proxy object that corresponds to this browsing context.
  // This should be called if the window proxy object is finalized, or it can't
  // reach its browsing context anymore.
  void ClearWindowProxy() { mWindowProxy = nullptr; }

  friend class Location;
  friend class RemoteLocationProxy;
  /**
   * LocationProxy is the class for the native object stored as a private in a
   * RemoteLocationProxy proxy representing a Location object in a different
   * process. It forwards all operations to its BrowsingContext and aggregates
   * its refcount to that BrowsingContext.
   */
  class LocationProxy final : public LocationBase {
   public:
    MozExternalRefCountType AddRef() { return GetBrowsingContext()->AddRef(); }
    MozExternalRefCountType Release() {
      return GetBrowsingContext()->Release();
    }

   protected:
    friend class RemoteLocationProxy;
    BrowsingContext* GetBrowsingContext() override {
      return reinterpret_cast<BrowsingContext*>(
          uintptr_t(this) - offsetof(BrowsingContext, mLocation));
    }

    already_AddRefed<nsIDocShell> GetDocShell() override { return nullptr; }
  };

  // Send a given `BaseTransaction` object to the correct remote.
  void SendCommitTransaction(ContentParent* aParent,
                             const BaseTransaction& aTxn, uint64_t aEpoch);
  void SendCommitTransaction(ContentChild* aChild, const BaseTransaction& aTxn,
                             uint64_t aEpoch);

  bool CanSet(FieldIndex<IDX_SessionStoreEpoch>, uint32_t aEpoch,
              ContentParent* aSource) {
    return IsTop() && !aSource;
  }

  void DidSet(FieldIndex<IDX_SessionStoreEpoch>, uint32_t aOldValue);

  using CanSetResult = syncedcontext::CanSetResult;

  // Ensure that opener is in the same BrowsingContextGroup.
  bool CanSet(FieldIndex<IDX_OpenerId>, const uint64_t& aValue,
              ContentParent* aSource) {
    if (aValue != 0) {
      RefPtr<BrowsingContext> opener = Get(aValue);
      return opener && opener->Group() == Group();
    }
    return true;
  }

  bool CanSet(FieldIndex<IDX_ServiceWorkersTestingEnabled>, bool,
              ContentParent*) {
    return IsTop();
  }

  bool CanSet(FieldIndex<IDX_MediumOverride>, const nsString&, ContentParent*) {
    return IsTop();
  }

  bool CanSet(FieldIndex<IDX_EmbedderColorScheme>,
              dom::PrefersColorSchemeOverride, ContentParent* aSource) {
    return CheckOnlyEmbedderCanSet(aSource);
  }

  bool CanSet(FieldIndex<IDX_PrefersColorSchemeOverride>,
              dom::PrefersColorSchemeOverride, ContentParent*) {
    return IsTop();
  }

  void DidSet(FieldIndex<IDX_InRDMPane>, bool aOldValue);

  void DidSet(FieldIndex<IDX_EmbedderColorScheme>,
              dom::PrefersColorSchemeOverride aOldValue);

  void DidSet(FieldIndex<IDX_PrefersColorSchemeOverride>,
              dom::PrefersColorSchemeOverride aOldValue);

  void PresContextAffectingFieldChanged();

  void DidSet(FieldIndex<IDX_MediumOverride>, nsString&& aOldValue);

  bool CanSet(FieldIndex<IDX_SuspendMediaWhenInactive>, bool, ContentParent*) {
    return IsTop();
  }

  bool CanSet(FieldIndex<IDX_TouchEventsOverrideInternal>,
              dom::TouchEventsOverride aTouchEventsOverride,
              ContentParent* aSource);

  bool CanSet(FieldIndex<IDX_DisplayMode>, const enum DisplayMode& aDisplayMode,
              ContentParent* aSource) {
    return IsTop();
  }

  void DidSet(FieldIndex<IDX_DisplayMode>, enum DisplayMode aOldValue);

  void DidSet(FieldIndex<IDX_ExplicitActive>, ExplicitActiveStatus aOldValue);

  bool CanSet(FieldIndex<IDX_IsActiveBrowserWindowInternal>, const bool& aValue,
              ContentParent* aSource);
  void DidSet(FieldIndex<IDX_IsActiveBrowserWindowInternal>, bool aOldValue);

  // Ensure that we only set the flag on the top level browsingContext.
  // And then, we do a pre-order walk in the tree to refresh the
  // volume of all media elements.
  void DidSet(FieldIndex<IDX_Muted>);

  bool CanSet(FieldIndex<IDX_ShouldDelayMediaFromStart>, const bool& aValue,
              ContentParent* aSource);
  void DidSet(FieldIndex<IDX_ShouldDelayMediaFromStart>, bool aOldValue);

  bool CanSet(FieldIndex<IDX_OverrideDPPX>, const float& aValue,
              ContentParent* aSource);
  void DidSet(FieldIndex<IDX_OverrideDPPX>, float aOldValue);

  bool CanSet(FieldIndex<IDX_EmbedderInnerWindowId>, const uint64_t& aValue,
              ContentParent* aSource);

  CanSetResult CanSet(FieldIndex<IDX_CurrentInnerWindowId>,
                      const uint64_t& aValue, ContentParent* aSource);

  void DidSet(FieldIndex<IDX_CurrentInnerWindowId>);

  bool CanSet(FieldIndex<IDX_ParentInitiatedNavigationEpoch>,
              const uint64_t& aValue, ContentParent* aSource);

  bool CanSet(FieldIndex<IDX_IsPopupSpam>, const bool& aValue,
              ContentParent* aSource);

  void DidSet(FieldIndex<IDX_IsPopupSpam>);

  void DidSet(FieldIndex<IDX_GVAudibleAutoplayRequestStatus>);
  void DidSet(FieldIndex<IDX_GVInaudibleAutoplayRequestStatus>);

  void DidSet(FieldIndex<IDX_Loading>);

  void DidSet(FieldIndex<IDX_AncestorLoading>);

  void DidSet(FieldIndex<IDX_PlatformOverride>);
  CanSetResult CanSet(FieldIndex<IDX_PlatformOverride>,
                      const nsString& aPlatformOverride,
                      ContentParent* aSource);

  void DidSet(FieldIndex<IDX_UserAgentOverride>);
  CanSetResult CanSet(FieldIndex<IDX_UserAgentOverride>,
                      const nsString& aUserAgent, ContentParent* aSource);
  bool CanSet(FieldIndex<IDX_OrientationLock>,
              const mozilla::hal::ScreenOrientation& aOrientationLock,
              ContentParent* aSource);

  bool CanSet(FieldIndex<IDX_EmbedderElementType>,
              const Maybe<nsString>& aInitiatorType, ContentParent* aSource);

  bool CanSet(FieldIndex<IDX_MessageManagerGroup>,
              const nsString& aMessageManagerGroup, ContentParent* aSource);

  CanSetResult CanSet(FieldIndex<IDX_AllowContentRetargeting>,
                      const bool& aAllowContentRetargeting,
                      ContentParent* aSource);
  CanSetResult CanSet(FieldIndex<IDX_AllowContentRetargetingOnChildren>,
                      const bool& aAllowContentRetargetingOnChildren,
                      ContentParent* aSource);
  CanSetResult CanSet(FieldIndex<IDX_AllowPlugins>, const bool& aAllowPlugins,
                      ContentParent* aSource);
  bool CanSet(FieldIndex<IDX_FullscreenAllowedByOwner>, const bool&,
              ContentParent*);
  bool CanSet(FieldIndex<IDX_WatchedByDevToolsInternal>,
              const bool& aWatchedByDevToolsInternal, ContentParent* aSource);

  CanSetResult CanSet(FieldIndex<IDX_DefaultLoadFlags>,
                      const uint32_t& aDefaultLoadFlags,
                      ContentParent* aSource);
  void DidSet(FieldIndex<IDX_DefaultLoadFlags>);

  bool CanSet(FieldIndex<IDX_UseGlobalHistory>, const bool& aUseGlobalHistory,
              ContentParent* aSource);

  bool CanSet(FieldIndex<IDX_TargetTopLevelLinkClicksToBlankInternal>,
              const bool& aTargetTopLevelLinkClicksToBlankInternal,
              ContentParent* aSource);

  void DidSet(FieldIndex<IDX_HasSessionHistory>, bool aOldValue);

  bool CanSet(FieldIndex<IDX_BrowserId>, const uint32_t& aValue,
              ContentParent* aSource);

  bool CanSet(FieldIndex<IDX_UseErrorPages>, const bool& aUseErrorPages,
              ContentParent* aSource);

  bool CanSet(FieldIndex<IDX_PendingInitialization>, bool aNewValue,
              ContentParent* aSource);

  bool CanSet(FieldIndex<IDX_PageAwakeRequestCount>, uint32_t aNewValue,
              ContentParent* aSource);
  void DidSet(FieldIndex<IDX_PageAwakeRequestCount>, uint32_t aOldValue);

  CanSetResult CanSet(FieldIndex<IDX_AllowJavascript>, bool aValue,
                      ContentParent* aSource);
  void DidSet(FieldIndex<IDX_AllowJavascript>, bool aOldValue);

  bool CanSet(FieldIndex<IDX_HasRestoreData>, bool aNewValue,
              ContentParent* aSource);

  template <size_t I, typename T>
  bool CanSet(FieldIndex<I>, const T&, ContentParent*) {
    return true;
  }

  // Overload `DidSet` to get notifications for a particular field being set.
  //
  // You can also overload the variant that gets the old value if you need it.
  template <size_t I>
  void DidSet(FieldIndex<I>) {}
  template <size_t I, typename T>
  void DidSet(FieldIndex<I>, T&& aOldValue) {}

  void DidSet(FieldIndex<IDX_FullZoom>, float aOldValue);
  void DidSet(FieldIndex<IDX_TextZoom>, float aOldValue);
  void DidSet(FieldIndex<IDX_AuthorStyleDisabledDefault>);

  bool CanSet(FieldIndex<IDX_IsInBFCache>, bool, ContentParent* aSource);
  void DidSet(FieldIndex<IDX_IsInBFCache>);

  // Allow if the process attemping to set field is the same as the owning
  // process. Deprecated. New code that might use this should generally be moved
  // to WindowContext or be settable only by the parent process.
  CanSetResult LegacyRevertIfNotOwningOrParentProcess(ContentParent* aSource);

  // True if the process attempting to set field is the same as the embedder's
  // process.
  bool CheckOnlyEmbedderCanSet(ContentParent* aSource);

  void CreateChildSHistory();

  using PrincipalWithLoadIdentifierTuple =
      Tuple<nsCOMPtr<nsIPrincipal>, uint64_t>;

  nsIPrincipal* GetSavedPrincipal(
      Maybe<PrincipalWithLoadIdentifierTuple> aPrincipalTuple);

  // Type of BrowsingContent
  const Type mType;

  // Unique id identifying BrowsingContext
  const uint64_t mBrowsingContextId;

  RefPtr<BrowsingContextGroup> mGroup;
  RefPtr<WindowContext> mParentWindow;
  nsCOMPtr<nsIDocShell> mDocShell;

  RefPtr<Element> mEmbedderElement;

  nsTArray<RefPtr<WindowContext>> mWindowContexts;
  RefPtr<WindowContext> mCurrentWindowContext;

  // This is not a strong reference, but using a JS::Heap for that should be
  // fine. The JSObject stored in here should be a proxy with a
  // nsOuterWindowProxy handler, which will update the pointer from its
  // objectMoved hook and clear it from its finalize hook.
  JS::Heap<JSObject*> mWindowProxy;
  LocationProxy mLocation;

  // OriginAttributes for this BrowsingContext. May not be changed after this
  // BrowsingContext is attached.
  OriginAttributes mOriginAttributes;

  // The network request context id, representing the nsIRequestContext
  // associated with this BrowsingContext, and LoadGroups created for it.
  uint64_t mRequestContextId = 0;

  // Determines if private browsing should be used. May not be changed after
  // this BrowsingContext is attached. This field matches mOriginAttributes in
  // content Browsing Contexts. Currently treated as a binary value: 1 - in
  // private mode, 0 - not private mode.
  uint32_t mPrivateBrowsingId;

  // True if Attach() has been called on this BrowsingContext already.
  bool mEverAttached : 1;

  // Is the most recent Document in this BrowsingContext loaded within this
  // process? This may be true with a null mDocShell after the Window has been
  // closed.
  bool mIsInProcess : 1;

  // Has this browsing context been discarded? BrowsingContexts should
  // only be discarded once.
  bool mIsDiscarded : 1;

  // True if this BrowsingContext has no associated visible window, and is owned
  // by whichever process created it, even if top-level.
  bool mWindowless : 1;

  // This is true if the BrowsingContext was out of process, but is now in
  // process, and might have remote window proxies that need to be cleaned up.
  bool mDanglingRemoteOuterProxies : 1;

  // True if this BrowsingContext has been embedded in a element in this
  // process.
  bool mEmbeddedByThisProcess : 1;

  // Determines if remote (out-of-process) tabs should be used. May not be
  // changed after this BrowsingContext is attached.
  bool mUseRemoteTabs : 1;

  // Determines if out-of-process iframes should be used. May not be changed
  // after this BrowsingContext is attached.
  bool mUseRemoteSubframes : 1;

  // True if this BrowsingContext is for a frame that was added dynamically.
  bool mCreatedDynamically : 1;

  // Set to true if the browsing context is in the bfcache and pagehide has been
  // dispatched. When coming out from the bfcache, the value is set to false
  // before dispatching pageshow.
  bool mIsInBFCache : 1;

  // Determines if we can execute scripts in this BrowsingContext. True if
  // AllowJavascript() is true and script execution is allowed in the parent
  // WindowContext.
  bool mCanExecuteScripts : 1;

  // The original offset of this context in its container. This property is -1
  // if this BrowsingContext is for a frame that was added dynamically.
  int32_t mChildOffset;

  // The start time of user gesture, this is only available if the browsing
  // context is in process.
  TimeStamp mUserGestureStart;

  // Triggering principal and principal to inherit need to point to original
  // principal instances if the document is loaded in the same process as the
  // process that initiated the load. When the load starts we save the
  // principals along with the current load id.
  // These principals correspond to the most recent load that took place within
  // the process of this browsing context.
  Maybe<PrincipalWithLoadIdentifierTuple> mTriggeringPrincipal;
  Maybe<PrincipalWithLoadIdentifierTuple> mPrincipalToInherit;

  class DeprioritizedLoadRunner
      : public mozilla::Runnable,
        public mozilla::LinkedListElement<DeprioritizedLoadRunner> {
   public:
    explicit DeprioritizedLoadRunner(nsIRunnable* aInner)
        : Runnable("DeprioritizedLoadRunner"), mInner(aInner) {}

    NS_IMETHOD Run() override {
      if (mInner) {
        RefPtr<nsIRunnable> inner = std::move(mInner);
        inner->Run();
      }

      return NS_OK;
    }

   private:
    RefPtr<nsIRunnable> mInner;
  };

  mozilla::LinkedList<DeprioritizedLoadRunner> mDeprioritizedLoadRunner;

  RefPtr<SessionStorageManager> mSessionStorageManager;
  RefPtr<ChildSHistory> mChildSessionHistory;

  nsTArray<std::function<void(uint64_t)>> mDiscardListeners;

  // Counter and time span for rate limiting Location and History API calls.
  // Used by CheckLocationChangeRateLimit. Do not apply cross-process.
  uint32_t mLocationChangeRateLimitCount;
  mozilla::TimeStamp mLocationChangeRateLimitSpanStart;
};

/**
 * Gets a WindowProxy object for a BrowsingContext that lives in a different
 * process (creating the object if it doesn't already exist). The WindowProxy
 * object will be in the compartment that aCx is currently in. This should only
 * be called if aContext doesn't hold a docshell, otherwise the BrowsingContext
 * lives in this process, and a same-process WindowProxy should be used (see
 * nsGlobalWindowOuter). This should only be called by bindings code, ToJSValue
 * is the right API to get a WindowProxy for a BrowsingContext.
 *
 * If aTransplantTo is non-null, then the WindowProxy object will eventually be
 * transplanted onto it. Therefore it should be used as the value in the remote
 * proxy map.
 */
extern bool GetRemoteOuterWindowProxy(JSContext* aCx, BrowsingContext* aContext,
                                      JS::Handle<JSObject*> aTransplantTo,
                                      JS::MutableHandle<JSObject*> aRetVal);

using BrowsingContextTransaction = BrowsingContext::BaseTransaction;
using BrowsingContextInitializer = BrowsingContext::IPCInitializer;
using MaybeDiscardedBrowsingContext = MaybeDiscarded<BrowsingContext>;

// Specialize the transaction object for every translation unit it's used in.
extern template class syncedcontext::Transaction<BrowsingContext>;

}  // namespace dom

// Allow sending BrowsingContext objects over IPC.
namespace ipc {
template <>
struct IPDLParamTraits<dom::MaybeDiscarded<dom::BrowsingContext>> {
  static void Write(IPC::MessageWriter* aWriter, IProtocol* aActor,
                    const dom::MaybeDiscarded<dom::BrowsingContext>& aParam);
  static bool Read(IPC::MessageReader* aReader, IProtocol* aActor,
                   dom::MaybeDiscarded<dom::BrowsingContext>* aResult);
};

template <>
struct IPDLParamTraits<dom::BrowsingContext::IPCInitializer> {
  static void Write(IPC::MessageWriter* aWriter, IProtocol* aActor,
                    const dom::BrowsingContext::IPCInitializer& aInitializer);

  static bool Read(IPC::MessageReader* aReader, IProtocol* aActor,
                   dom::BrowsingContext::IPCInitializer* aInitializer);
};
}  // namespace ipc
}  // namespace mozilla

#endif  // !defined(mozilla_dom_BrowsingContext_h)
