/* -*- 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 nsPIDOMWindow_h__ #define nsPIDOMWindow_h__ #include "nsIDOMWindow.h" #include "mozIDOMWindow.h" #include "nsCOMPtr.h" #include "nsTArray.h" #include "Units.h" #include "mozilla/dom/EventTarget.h" #include "mozilla/EventForwards.h" #include "mozilla/Maybe.h" #include "mozilla/TaskCategory.h" #include "js/TypeDecls.h" #include "nsRefPtrHashtable.h" #include "nsILoadInfo.h" // Only fired for inner windows. #define DOM_WINDOW_DESTROYED_TOPIC "dom-window-destroyed" #define DOM_WINDOW_FROZEN_TOPIC "dom-window-frozen" #define DOM_WINDOW_THAWED_TOPIC "dom-window-thawed" class nsGlobalWindowInner; class nsGlobalWindowOuter; class nsIArray; class nsIBaseWindow; class nsIChannel; class nsIContent; class nsIContentSecurityPolicy; class nsICSSDeclaration; class nsIDocShell; class nsIDocShellTreeOwner; class nsDocShellLoadState; class nsIPrincipal; class nsIRunnable; class nsIScriptTimeoutHandler; class nsISerialEventTarget; class nsIURI; class nsIWebBrowserChrome; class nsPIDOMWindowInner; class nsPIDOMWindowOuter; class nsPIWindowRoot; using SuspendTypes = uint32_t; namespace mozilla::dom { class AudioContext; class BrowsingContext; class BrowsingContextGroup; class ClientInfo; class ClientState; class ContentFrameMessageManager; class DocGroup; class Document; class Element; class Location; class MediaDevices; class MediaKeys; class Navigator; class Performance; class Selection; class ServiceWorker; class ServiceWorkerDescriptor; class Timeout; class TimeoutManager; class WindowContext; class WindowGlobalChild; class CustomElementRegistry; enum class CallerType : uint32_t; } // namespace mozilla::dom enum class FullscreenReason { // Toggling the fullscreen mode requires trusted context. ForFullscreenMode, // Fullscreen API is the API provided to untrusted content. ForFullscreenAPI, // This reason can only be used with exiting fullscreen. // It is otherwise identical to eForFullscreenAPI except it would // suppress the fullscreen transition. ForForceExitFullscreen }; // Must be kept in sync with xpcom/rust/xpcom/src/interfaces/nonidl.rs #define NS_PIDOMWINDOWINNER_IID \ { \ 0x775dabc9, 0x8f43, 0x4277, { \ 0x9a, 0xdb, 0xf1, 0x99, 0x0d, 0x77, 0xcf, 0xfb \ } \ } // Must be kept in sync with xpcom/rust/xpcom/src/interfaces/nonidl.rs #define NS_PIDOMWINDOWOUTER_IID \ { \ 0x769693d4, 0xb009, 0x4fe2, { \ 0xaf, 0x18, 0x7d, 0xc8, 0xdf, 0x74, 0x96, 0xdf \ } \ } class nsPIDOMWindowInner : public mozIDOMWindow { protected: using Document = mozilla::dom::Document; friend nsGlobalWindowInner; friend nsGlobalWindowOuter; nsPIDOMWindowInner(nsPIDOMWindowOuter* aOuterWindow, mozilla::dom::WindowGlobalChild* aActor); ~nsPIDOMWindowInner(); public: NS_DECLARE_STATIC_IID_ACCESSOR(NS_PIDOMWINDOWINNER_IID) nsIGlobalObject* AsGlobal(); const nsIGlobalObject* AsGlobal() const; nsPIDOMWindowOuter* GetOuterWindow() const { return mOuterWindow; } static nsPIDOMWindowInner* From(mozIDOMWindow* aFrom) { return static_cast(aFrom); } NS_IMPL_FROMEVENTTARGET_HELPER_WITH_GETTER(nsPIDOMWindowInner, GetAsWindowInner()) // Returns true if this object is the currently-active inner window for its // BrowsingContext. bool IsCurrentInnerWindow() const; // Returns true if the document of this window is the active document. This // is identical to IsCurrentInnerWindow() now that document.open() no longer // creates new inner windows for the document it is called on. inline bool HasActiveDocument() const; // Return true if this object is the currently-active inner window for its // BrowsingContext and any container document is also fully active. // For https://html.spec.whatwg.org/multipage/browsers.html#fully-active bool IsFullyActive() const; // Returns true if this window is the same as mTopInnerWindow inline bool IsTopInnerWindow() const; // Returns true if this was the current window for its BrowsingContext when it // was discarded. virtual bool WasCurrentInnerWindow() const = 0; // Check whether a document is currently loading (really checks if the // load event has completed). May not be reset to false on errors. inline bool IsLoading() const; inline bool IsHandlingResizeEvent() const; // Note: not related to IsLoading. Set to false if there's an error, etc. virtual void SetActiveLoadingState(bool aIsActiveLoading) = 0; bool AddAudioContext(mozilla::dom::AudioContext* aAudioContext); void RemoveAudioContext(mozilla::dom::AudioContext* aAudioContext); void MuteAudioContexts(); void UnmuteAudioContexts(); void SetAudioCapture(bool aCapture); /** * Associate this inner window with a MediaKeys instance. */ void AddMediaKeysInstance(mozilla::dom::MediaKeys* aMediaKeysInstance); /** * Remove an association between this inner window and a MediaKeys instance. */ void RemoveMediaKeysInstance(mozilla::dom::MediaKeys* aMediaKeysInstance); /** * Return if any MediaKeys instances are associated with this window. */ bool HasActiveMediaKeysInstance(); mozilla::dom::Performance* GetPerformance(); void QueuePerformanceNavigationTiming(); bool HasMutationListeners(uint32_t aMutationEventType) const { if (!mOuterWindow) { NS_ERROR("HasMutationListeners() called on orphan inner window!"); return false; } return (mMutationBits & aMutationEventType) != 0; } void SetMutationListeners(uint32_t aType) { if (!mOuterWindow) { NS_ERROR("HasMutationListeners() called on orphan inner window!"); return; } mMutationBits |= aType; } /** * Call this to check whether some node (this window, its document, * or content in that document) has a mouseenter/leave event listener. */ bool HasMouseEnterLeaveEventListeners() const { return mMayHaveMouseEnterLeaveEventListener; } /** * Call this to indicate that some node (this window, its document, * or content in that document) has a mouseenter/leave event listener. */ void SetHasMouseEnterLeaveEventListeners() { mMayHaveMouseEnterLeaveEventListener = true; } /** * Call this to check whether some node (this window, its document, * or content in that document) has a Pointerenter/leave event listener. */ bool HasPointerEnterLeaveEventListeners() const { return mMayHavePointerEnterLeaveEventListener; } /** * Call this to indicate that some node (this window, its document, * or content in that document) has a Pointerenter/leave event listener. */ void SetHasPointerEnterLeaveEventListeners() { mMayHavePointerEnterLeaveEventListener = true; } /** * Call this to check whether some node (this window, its document, * or content in that document) has a transition* event listeners. */ bool HasTransitionEventListeners() { return mMayHaveTransitionEventListener; } /** * Call this to indicate that some node (this window, its document, * or content in that document) has a transition* event listener. */ void SetHasTransitionEventListeners() { mMayHaveTransitionEventListener = true; } /** * Call this to check whether some node (this window, its document, * or content in that document) has a beforeinput event listener. * Returing false may be wrong if some nodes have come from another document * with `Document.adoptNode`. */ bool HasBeforeInputEventListenersForTelemetry() const { return mMayHaveBeforeInputEventListenerForTelemetry; } /** * Call this to indicate that some node (this window, its document, * or content in that document) has a beforeinput event listener. */ void SetHasBeforeInputEventListenersForTelemetry() { mMayHaveBeforeInputEventListenerForTelemetry = true; } /** * Call this to check whether some node (The document, or content in the * document) has been observed by web apps with a mutation observer. * (i.e., `MutationObserver.observe()` called by chrome script and addon's * script does not make this returns true). * Returing false may be wrong if some nodes have come from another document * with `Document.adoptNode`. */ bool MutationObserverHasObservedNodeForTelemetry() const { return mMutationObserverHasObservedNodeForTelemetry; } /** * Call this to indicate that some node (The document, or content in the * document) is observed by web apps with a mutation observer. */ void SetMutationObserverHasObservedNodeForTelemetry() { mMutationObserverHasObservedNodeForTelemetry = true; } // Sets the event for window.event. Does NOT take ownership, so // the caller is responsible for clearing the event before the // event gets deallocated. Pass nullptr to set window.event to // undefined. Returns the previous value. mozilla::dom::Event* SetEvent(mozilla::dom::Event* aEvent) { mozilla::dom::Event* old = mEvent; mEvent = aEvent; return old; } /** * Check whether this window is a secure context. */ bool IsSecureContext() const; bool IsSecureContextIfOpenerIgnored() const; // Calling suspend should prevent any asynchronous tasks from // executing javascript for this window. This means setTimeout, // requestAnimationFrame, and events should not be fired. Suspending // a window maybe also suspends its children. Workers may // continue to perform computations in the background. A window // can have Suspend() called multiple times and will only resume after // a matching number of Resume() calls. void Suspend(bool aIncludeSubWindows = true); void Resume(bool aIncludeSubWindows = true); // Whether or not this window was suspended by the BrowserContextGroup bool GetWasSuspendedByGroup() const { return mWasSuspendedByGroup; } void SetWasSuspendedByGroup(bool aSuspended) { mWasSuspendedByGroup = aSuspended; } // Apply the parent window's suspend, freeze, and modal state to the current // window. void SyncStateFromParentWindow(); /** * Increment active peer connection count. */ void AddPeerConnection(); /** * Decrement active peer connection count. */ void RemovePeerConnection(); /** * Check whether the active peer connection count is non-zero. */ bool HasActivePeerConnections(); bool IsPlayingAudio(); bool IsDocumentLoaded() const; mozilla::dom::TimeoutManager& TimeoutManager(); bool IsRunningTimeout(); // To cache top inner-window if available after constructed for tab-wised // indexedDB counters. void TryToCacheTopInnerWindow(); // Increase/Decrease the number of active IndexedDB databases for the // decision making of timeout-throttling. void UpdateActiveIndexedDBDatabaseCount(int32_t aDelta); // Return true if there is any active IndexedDB databases which could block // timeout-throttling. bool HasActiveIndexedDBDatabases(); // Increase/Decrease the number of open WebSockets. void UpdateWebSocketCount(int32_t aDelta); // Return true if there are any open WebSockets that could block // timeout-throttling. bool HasOpenWebSockets() const; mozilla::Maybe GetClientInfo() const; mozilla::Maybe GetClientState() const; mozilla::Maybe GetController() const; void SetCsp(nsIContentSecurityPolicy* aCsp); void SetPreloadCsp(nsIContentSecurityPolicy* aPreloadCsp); nsIContentSecurityPolicy* GetCsp(); void NoteCalledRegisterForServiceWorkerScope(const nsACString& aScope); void NoteDOMContentLoaded(); virtual mozilla::dom::CustomElementRegistry* CustomElements() = 0; // XXX: This is called on inner windows virtual nsPIDOMWindowOuter* GetInProcessScriptableTop() = 0; virtual nsPIDOMWindowOuter* GetInProcessScriptableParent() = 0; virtual already_AddRefed GetTopWindowRoot() = 0; mozilla::dom::EventTarget* GetChromeEventHandler() const { return mChromeEventHandler; } mozilla::dom::EventTarget* GetParentTarget() { if (!mParentTarget) { UpdateParentTarget(); } return mParentTarget; } virtual void MaybeUpdateTouchState() {} Document* GetExtantDoc() const { return mDoc; } nsIURI* GetDocumentURI() const; nsIURI* GetDocBaseURI() const; Document* GetDoc() { if (!mDoc) { MaybeCreateDoc(); } return mDoc; } mozilla::dom::WindowContext* GetWindowContext() const; mozilla::dom::WindowGlobalChild* GetWindowGlobalChild() const { return mWindowGlobalChild; } // Removes this inner window from the BFCache, if it is cached, and returns // true if it was. bool RemoveFromBFCacheSync(); // Determine if the window is suspended or frozen. Outer windows // will forward this call to the inner window for convenience. If // there is no inner window then the outer window is considered // suspended and frozen by default. virtual bool IsSuspended() const = 0; virtual bool IsFrozen() const = 0; // Fire any DOM notification events related to things that happened while // the window was frozen. virtual nsresult FireDelayedDOMEvents(bool aIncludeSubWindows) = 0; /** * Get the docshell in this window. */ inline nsIDocShell* GetDocShell() const; /** * Get the browsing context in this window. */ inline mozilla::dom::BrowsingContext* GetBrowsingContext() const; /** * Get the browsing context group this window belongs to. */ mozilla::dom::BrowsingContextGroup* GetBrowsingContextGroup() const; /** * Call this to indicate that some node (this window, its document, * or content in that document) has a paint event listener. */ void SetHasPaintEventListeners() { mMayHavePaintEventListener = true; } /** * Call this to check whether some node (this window, its document, * or content in that document) has a paint event listener. */ bool HasPaintEventListeners() { return mMayHavePaintEventListener; } /** * Call this to indicate that some node (this window, its document, * or content in that document) has a touch event listener. */ void SetHasTouchEventListeners() { if (!mMayHaveTouchEventListener) { mMayHaveTouchEventListener = true; MaybeUpdateTouchState(); } } /** * Call this to indicate that some node (this window, its document, * or content in that document) has a selectionchange event listener. */ void SetHasSelectionChangeEventListeners() { mMayHaveSelectionChangeEventListener = true; } /** * Call this to check whether some node (this window, its document, * or content in that document) has a selectionchange event listener. */ bool HasSelectionChangeEventListeners() const { return mMayHaveSelectionChangeEventListener; } /** * Call this to indicate that some node (this window, its document, * or content in that document) has a select event listener of form controls. */ void SetHasFormSelectEventListeners() { mMayHaveFormSelectEventListener = true; } /** * Call this to check whether some node (this window, its document, * or content in that document) has a select event listener of form controls. */ bool HasFormSelectEventListeners() const { return mMayHaveFormSelectEventListener; } /* * Get and set the currently focused element within the document. If * aNeedsFocus is true, then set mNeedsFocus to true to indicate that a * document focus event is needed. * * DO NOT CALL EITHER OF THESE METHODS DIRECTLY. USE THE FOCUS MANAGER * INSTEAD. */ mozilla::dom::Element* GetFocusedElement() const { return mFocusedElement.get(); } virtual void SetFocusedElement(mozilla::dom::Element* aElement, uint32_t aFocusMethod = 0, bool aNeedsFocus = false) = 0; bool UnknownFocusMethodShouldShowOutline() const { return mUnknownFocusMethodShouldShowOutline; } /** * Retrieves the method that was used to focus the current node. */ virtual uint32_t GetFocusMethod() = 0; /* * Tells the window that it now has focus or has lost focus, based on the * state of aFocus. If this method returns true, then the document loaded * in the window has never received a focus event and expects to receive * one. If false is returned, the document has received a focus event before * and should only receive one if the window is being focused. * * aFocusMethod may be set to one of the focus method constants in * nsIFocusManager to indicate how focus was set. */ virtual bool TakeFocus(bool aFocus, uint32_t aFocusMethod) = 0; /** * Indicates that the window may now accept a document focus event. This * should be called once a document has been loaded into the window. */ virtual void SetReadyForFocus() = 0; /** * Whether the focused content within the window should show a focus ring. */ virtual bool ShouldShowFocusRing() = 0; /** * Indicates that the page in the window has been hidden. This is used to * reset the focus state. */ virtual void PageHidden() = 0; /** * Instructs this window to asynchronously dispatch a hashchange event. This * method must be called on an inner window. */ virtual nsresult DispatchAsyncHashchange(nsIURI* aOldURI, nsIURI* aNewURI) = 0; /** * Instructs this window to synchronously dispatch a popState event. */ virtual nsresult DispatchSyncPopState() = 0; /** * Tell this window that it should listen for sensor changes of the given * type. */ virtual void EnableDeviceSensor(uint32_t aType) = 0; /** * Tell this window that it should remove itself from sensor change * notifications. */ virtual void DisableDeviceSensor(uint32_t aType) = 0; #if defined(MOZ_WIDGET_ANDROID) virtual void EnableOrientationChangeListener() = 0; virtual void DisableOrientationChangeListener() = 0; #endif /** * Tell this window that there is an observer for gamepad input * * Inner windows only. */ virtual void SetHasGamepadEventListener(bool aHasGamepad = true) = 0; /** * Return the window id of this window */ uint64_t WindowID() const { return mWindowID; } // WebIDL-ish APIs void MarkUncollectableForCCGeneration(uint32_t aGeneration) { mMarkedCCGeneration = aGeneration; } uint32_t GetMarkedCCGeneration() { return mMarkedCCGeneration; } mozilla::dom::Navigator* Navigator(); mozilla::dom::MediaDevices* GetExtantMediaDevices() const; virtual mozilla::dom::Location* Location() = 0; virtual nsresult GetControllers(nsIControllers** aControllers) = 0; virtual nsresult GetInnerWidth(double* aWidth) = 0; virtual nsresult GetInnerHeight(double* aHeight) = 0; virtual already_AddRefed GetComputedStyle( mozilla::dom::Element& aElt, const nsAString& aPseudoElt, mozilla::ErrorResult& aError) = 0; virtual bool GetFullScreen() = 0; virtual nsresult Focus(mozilla::dom::CallerType aCallerType) = 0; virtual nsresult Close() = 0; mozilla::dom::DocGroup* GetDocGroup() const; virtual nsISerialEventTarget* EventTargetFor( mozilla::TaskCategory aCategory) const = 0; void SaveStorageAccessPermissionGranted(); bool HasStorageAccessPermissionGranted(); uint32_t UpdateLockCount(bool aIncrement) { MOZ_ASSERT_IF(!aIncrement, mLockCount > 0); mLockCount += aIncrement ? 1 : -1; return mLockCount; }; bool HasActiveLocks() { return mLockCount > 0; } uint32_t UpdateWebTransportCount(bool aIncrement) { MOZ_ASSERT_IF(!aIncrement, mWebTransportCount > 0); mWebTransportCount += aIncrement ? 1 : -1; return mWebTransportCount; }; bool HasActiveWebTransports() { return mWebTransportCount > 0; } protected: void CreatePerformanceObjectIfNeeded(); // Lazily instantiate an about:blank document if necessary, and if // we have what it takes to do so. void MaybeCreateDoc(); void SetChromeEventHandlerInternal( mozilla::dom::EventTarget* aChromeEventHandler) { mChromeEventHandler = aChromeEventHandler; // mParentTarget will be set when the next event is dispatched. mParentTarget = nullptr; } virtual void UpdateParentTarget() = 0; // These two variables are special in that they're set to the same // value on both the outer window and the current inner window. Make // sure you keep them in sync! nsCOMPtr mChromeEventHandler; // strong RefPtr mDoc; // Cache the URI when mDoc is cleared. nsCOMPtr mDocumentURI; // strong nsCOMPtr mDocBaseURI; // strong nsCOMPtr mParentTarget; // strong RefPtr mPerformance; mozilla::UniquePtr mTimeoutManager; RefPtr mNavigator; // These variables are only used on inner windows. uint32_t mMutationBits; uint32_t mActivePeerConnections = 0; bool mIsDocumentLoaded; bool mIsHandlingResizeEvent; bool mMayHavePaintEventListener; bool mMayHaveTouchEventListener; bool mMayHaveSelectionChangeEventListener; bool mMayHaveFormSelectEventListener; bool mMayHaveMouseEnterLeaveEventListener; bool mMayHavePointerEnterLeaveEventListener; bool mMayHaveTransitionEventListener; // Only used for telemetry probes. This may be wrong if some nodes have // come from another document with `Document.adoptNode`. bool mMayHaveBeforeInputEventListenerForTelemetry; bool mMutationObserverHasObservedNodeForTelemetry; // Our inner window's outer window. nsCOMPtr mOuterWindow; // The element within the document that is currently focused when this // window is active. RefPtr mFocusedElement; // The AudioContexts created for the current document, if any. nsTArray mAudioContexts; // Weak // Instances of MediaKeys created in this inner window. Storing these allows // us to shutdown MediaKeys when an inner windows is destroyed. We can also // use the presence of MediaKeys to assess if a window has EME activity. nsTArray mMediaKeysInstances; // Weak RefPtr mBrowsingContext; // A unique (as long as our 64-bit counter doesn't roll over) id for // this window. uint64_t mWindowID; // Set to true once we've sent the (chrome|content)-document-global-created // notification. bool mHasNotifiedGlobalCreated; // Whether when focused via an "unknown" focus method, we should show outlines // by default or not. The initial value of this is true (so as to show // outlines for stuff like html autofocus, or initial programmatic focus // without any other user interaction). bool mUnknownFocusMethodShouldShowOutline = true; uint32_t mMarkedCCGeneration; // mTopInnerWindow is used for tab-wise check by timeout throttling. It could // be null. nsCOMPtr mTopInnerWindow; // The evidence that we have tried to cache mTopInnerWindow only once from // SetNewDocument(). Note: We need this extra flag because mTopInnerWindow // could be null and we don't want it to be set multiple times. bool mHasTriedToCacheTopInnerWindow; // The number of active IndexedDB databases. uint32_t mNumOfIndexedDBDatabases; // The number of open WebSockets. uint32_t mNumOfOpenWebSockets; // The event dispatch code sets and unsets this while keeping // the event object alive. mozilla::dom::Event* mEvent; // A boolean flag indicating whether storage access is granted for the // current window. These are also set as permissions, but it could happen // that we need to access them synchronously in this context, and for // this, we need a copy here. bool mStorageAccessPermissionGranted; // The WindowGlobalChild actor for this window. // // This will be non-null during the full lifetime of the window, initialized // during SetNewDocument, and cleared during FreeInnerObjects. RefPtr mWindowGlobalChild; bool mWasSuspendedByGroup; /** * Count of the number of active LockRequest objects, including ones from * workers. */ uint32_t mLockCount = 0; /** * Count of the number of active WebTransport objects, including ones from * workers. */ uint32_t mWebTransportCount = 0; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsPIDOMWindowInner, NS_PIDOMWINDOWINNER_IID) class nsPIDOMWindowOuter : public mozIDOMWindowProxy { protected: using Document = mozilla::dom::Document; explicit nsPIDOMWindowOuter(uint64_t aWindowID); ~nsPIDOMWindowOuter(); void NotifyResumingDelayedMedia(); public: NS_DECLARE_STATIC_IID_ACCESSOR(NS_PIDOMWINDOWOUTER_IID) NS_IMPL_FROMEVENTTARGET_HELPER_WITH_GETTER(nsPIDOMWindowOuter, GetAsWindowOuter()) static nsPIDOMWindowOuter* From(mozIDOMWindowProxy* aFrom) { return static_cast(aFrom); } // Given an inner window, return its outer if the inner is the current inner. // Otherwise (argument null or not an inner or not current) return null. static nsPIDOMWindowOuter* GetFromCurrentInner(nsPIDOMWindowInner* aInner); // Check whether a document is currently loading inline bool IsLoading() const; inline bool IsHandlingResizeEvent() const; nsPIDOMWindowInner* GetCurrentInnerWindow() const { return mInnerWindow; } nsPIDOMWindowInner* EnsureInnerWindow() { // GetDoc forces inner window creation if there isn't one already GetDoc(); return GetCurrentInnerWindow(); } bool IsRootOuterWindow() { return mIsRootOuterWindow; } // Internal getter/setter for the frame element, this version of the // getter crosses chrome boundaries whereas the public scriptable // one doesn't for security reasons. mozilla::dom::Element* GetFrameElementInternal() const; void SetFrameElementInternal(mozilla::dom::Element* aFrameElement); bool IsBackground() { return mIsBackground; } // Audio API bool GetAudioMuted() const; // No longer to delay media from starting for this window. void ActivateMediaComponents(); bool ShouldDelayMediaFromStart() const; void RefreshMediaElementsVolume(); virtual nsPIDOMWindowOuter* GetPrivateRoot() = 0; /** * |top| gets the root of the window hierarchy. * * This function does not cross chrome-content boundaries, so if this * window's parent is of a different type, |top| will return this window. * * When script reads the top property, we run GetInProcessScriptableTop, * which will not cross an