From 36d22d82aa202bb199967e9512281e9a53db42c9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 21:33:14 +0200 Subject: Adding upstream version 115.7.0esr. Signed-off-by: Daniel Baumann --- dom/base/nsGlobalWindowInner.cpp | 7912 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 7912 insertions(+) create mode 100644 dom/base/nsGlobalWindowInner.cpp (limited to 'dom/base/nsGlobalWindowInner.cpp') diff --git a/dom/base/nsGlobalWindowInner.cpp b/dom/base/nsGlobalWindowInner.cpp new file mode 100644 index 0000000000..0e5afafcb5 --- /dev/null +++ b/dom/base/nsGlobalWindowInner.cpp @@ -0,0 +1,7912 @@ +/* -*- 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 "nsGlobalWindowInner.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include "AudioChannelService.h" +#include "AutoplayPolicy.h" +#include "Crypto.h" +#include "MainThreadUtils.h" +#include "Navigator.h" +#include "PaintWorkletImpl.h" +#include "SessionStorageCache.h" +#include "Units.h" +#include "VRManagerChild.h" +#include "WindowDestroyedEvent.h" +#include "WindowNamedPropertiesHandler.h" +#include "js/ComparisonOperators.h" +#include "js/CompileOptions.h" +#include "js/friend/PerformanceHint.h" +#include "js/Id.h" +#include "js/loader/LoadedScript.h" +#include "js/PropertyAndElement.h" // JS_DefineProperty, JS_GetProperty +#include "js/PropertyDescriptor.h" +#include "js/RealmOptions.h" +#include "js/RootingAPI.h" +#include "js/TypeDecls.h" +#include "js/Value.h" +#include "js/Warnings.h" +#include "js/shadow/String.h" +#include "jsapi.h" +#include "jsfriendapi.h" +#include "mozIDOMWindow.h" +#include "moz_external_vr.h" +#include "mozilla/AlreadyAddRefed.h" +#include "mozilla/ArrayIterator.h" +#include "mozilla/ArrayUtils.h" +#include "mozilla/Attributes.h" +#include "mozilla/BaseProfilerMarkersPrerequisites.h" +#include "mozilla/BasicEvents.h" +#include "mozilla/CallState.h" +#include "mozilla/CycleCollectedJSContext.h" +#include "mozilla/DOMEventTargetHelper.h" +#include "mozilla/ErrorResult.h" +#include "mozilla/EventDispatcher.h" +#include "mozilla/EventListenerManager.h" +#include "mozilla/EventQueue.h" +#include "mozilla/ExtensionPolicyService.h" +#include "mozilla/FloatingPoint.h" +#include "mozilla/FlushType.h" +#include "mozilla/Likely.h" +#include "mozilla/LinkedList.h" +#include "mozilla/LookAndFeel.h" +#include "mozilla/Logging.h" +#include "mozilla/MacroForEach.h" +#include "mozilla/Maybe.h" +#include "mozilla/OwningNonNull.h" +#include "mozilla/PermissionDelegateHandler.h" +#include "mozilla/Preferences.h" +#include "mozilla/PresShell.h" +#include "mozilla/ProcessHangMonitor.h" +#include "mozilla/RefPtr.h" +#include "mozilla/Result.h" +#include "mozilla/ScrollTypes.h" +#include "mozilla/Components.h" +#include "mozilla/SizeOfState.h" +#include "mozilla/Span.h" +#include "mozilla/SpinEventLoopUntil.h" +#include "mozilla/Sprintf.h" +#include "mozilla/StaticPrefs_browser.h" +#include "mozilla/StaticPrefs_docshell.h" +#include "mozilla/StaticPrefs_dom.h" +#include "mozilla/StaticPrefs_extensions.h" +#include "mozilla/StaticPrefs_privacy.h" +#include "mozilla/StorageAccess.h" +#include "mozilla/StoragePrincipalHelper.h" +#include "mozilla/TaskCategory.h" +#include "mozilla/Telemetry.h" +#include "mozilla/TelemetryHistogramEnums.h" +#include "mozilla/TimeStamp.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/Unused.h" +#include "mozilla/dom/AudioContext.h" +#include "mozilla/dom/AutoEntryScript.h" +#include "mozilla/dom/BarProps.h" +#include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/dom/BindingUtils.h" +#include "mozilla/dom/BrowserChild.h" +#include "mozilla/dom/BrowsingContext.h" +#include "mozilla/dom/CSPEvalChecker.h" +#include "mozilla/dom/CallbackDebuggerNotification.h" +#include "mozilla/dom/ChromeMessageBroadcaster.h" +#include "mozilla/dom/ClientInfo.h" +#include "mozilla/dom/ClientManager.h" +#include "mozilla/dom/ClientSource.h" +#include "mozilla/dom/ClientState.h" +#include "mozilla/dom/ClientsBinding.h" +#include "mozilla/dom/Console.h" +#include "mozilla/dom/ContentFrameMessageManager.h" +#include "mozilla/dom/ContentMediaController.h" +#include "mozilla/dom/CustomElementRegistry.h" +#include "mozilla/dom/DebuggerNotification.h" +#include "mozilla/dom/DebuggerNotificationBinding.h" +#include "mozilla/dom/DebuggerNotificationManager.h" +#include "mozilla/dom/DispatcherTrait.h" +#include "mozilla/dom/DocGroup.h" +#include "mozilla/dom/Document.h" +#include "mozilla/dom/DocumentInlines.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/Event.h" +#include "mozilla/dom/EventTarget.h" +#include "mozilla/dom/External.h" +#include "mozilla/dom/Fetch.h" +#include "mozilla/dom/Gamepad.h" +#include "mozilla/dom/GamepadHandle.h" +#include "mozilla/dom/GamepadManager.h" +#include "mozilla/dom/HashChangeEvent.h" +#include "mozilla/dom/HashChangeEventBinding.h" +#include "mozilla/dom/IDBFactory.h" +#include "mozilla/dom/IdleRequest.h" +#include "mozilla/dom/ImageBitmap.h" +#include "mozilla/dom/ImageBitmapSource.h" +#include "mozilla/dom/InstallTriggerBinding.h" +#include "mozilla/dom/IntlUtils.h" +#include "mozilla/dom/JSExecutionContext.h" +#include "mozilla/dom/LSObject.h" +#include "mozilla/dom/LocalStorage.h" +#include "mozilla/dom/LocalStorageCommon.h" +#include "mozilla/dom/Location.h" +#include "mozilla/dom/MediaDevices.h" +#include "mozilla/dom/MediaKeys.h" +#include "mozilla/dom/NavigatorBinding.h" +#include "mozilla/dom/Nullable.h" +#include "mozilla/dom/PartitionedLocalStorage.h" +#include "mozilla/dom/Performance.h" +#include "mozilla/dom/PopStateEvent.h" +#include "mozilla/dom/PopStateEventBinding.h" +#include "mozilla/dom/PopupBlocker.h" +#include "mozilla/dom/PrimitiveConversions.h" +#include "mozilla/dom/Promise.h" +#include "mozilla/dom/RootedDictionary.h" +#include "mozilla/dom/WebTaskSchedulerMainThread.h" +#include "mozilla/dom/ScriptLoader.h" +#include "mozilla/dom/ScriptSettings.h" +#include "mozilla/dom/ServiceWorker.h" +#include "mozilla/dom/ServiceWorkerDescriptor.h" +#include "mozilla/dom/ServiceWorkerRegistration.h" +#include "mozilla/dom/SessionStorageManager.h" +#include "mozilla/dom/SharedWorker.h" +#include "mozilla/dom/Storage.h" +#include "mozilla/dom/StorageEvent.h" +#include "mozilla/dom/StorageEventBinding.h" +#include "mozilla/dom/StorageNotifierService.h" +#include "mozilla/dom/StorageUtils.h" +#include "mozilla/dom/TabMessageTypes.h" +#include "mozilla/dom/Timeout.h" +#include "mozilla/dom/TimeoutHandler.h" +#include "mozilla/dom/TimeoutManager.h" +#include "mozilla/dom/ToJSValue.h" +#include "mozilla/dom/VRDisplay.h" +#include "mozilla/dom/VRDisplayEvent.h" +#include "mozilla/dom/VRDisplayEventBinding.h" +#include "mozilla/dom/VREventObserver.h" +#include "mozilla/dom/VisualViewport.h" +#include "mozilla/dom/WebIDLGlobalNameHash.h" +#include "mozilla/dom/WindowBinding.h" +#include "mozilla/dom/WindowContext.h" +#include "mozilla/dom/WindowGlobalChild.h" +#include "mozilla/dom/WindowProxyHolder.h" +#include "mozilla/dom/WorkerCommon.h" +#include "mozilla/dom/Worklet.h" +#include "mozilla/dom/XRPermissionRequest.h" +#include "mozilla/dom/cache/CacheStorage.h" +#include "mozilla/dom/cache/Types.h" +#include "mozilla/glean/bindings/Glean.h" +#include "mozilla/glean/bindings/GleanPings.h" +#include "mozilla/extensions/WebExtensionPolicy.h" +#include "mozilla/fallible.h" +#include "mozilla/gfx/BasePoint.h" +#include "mozilla/gfx/BaseRect.h" +#include "mozilla/gfx/BaseSize.h" +#include "mozilla/gfx/Rect.h" +#include "mozilla/gfx/Types.h" +#include "mozilla/intl/LocaleService.h" +#include "mozilla/ipc/BackgroundUtils.h" +#include "mozilla/ipc/PBackgroundSharedTypes.h" +#include "mozilla/net/CookieJarSettings.h" +#include "nsAtom.h" +#include "nsBaseHashtable.h" +#include "nsCCUncollectableMarker.h" +#include "nsCOMPtr.h" +#include "nsCRT.h" +#include "nsCRTGlue.h" +#include "nsCanvasFrame.h" +#include "nsCharTraits.h" +#include "nsCheapSets.h" +#include "nsContentUtils.h" +#include "nsCoord.h" +#include "nsCycleCollectionNoteChild.h" +#include "nsCycleCollectionTraversalCallback.h" +#include "nsDOMNavigationTiming.h" +#include "nsDebug.h" +#include "nsDeviceContext.h" +#include "nsDocShell.h" +#include "nsFocusManager.h" +#include "nsFrameMessageManager.h" +#include "nsGkAtoms.h" +#include "nsGlobalWindowOuter.h" +#include "nsHashKeys.h" +#include "nsHistory.h" +#include "nsIAddonPolicyService.h" +#include "nsIArray.h" +#include "nsIBaseWindow.h" +#include "nsIBrowserChild.h" +#include "nsICancelableRunnable.h" +#include "nsIChannel.h" +#include "nsIContentSecurityPolicy.h" +#include "nsIControllers.h" +#include "nsICookieJarSettings.h" +#include "nsICookieService.h" +#include "nsID.h" +#include "nsIDOMStorageManager.h" +#include "nsIDeviceSensors.h" +#include "nsIDocShell.h" +#include "nsIDocShellTreeItem.h" +#include "nsIDocShellTreeOwner.h" +#include "nsIDocumentLoader.h" +#include "nsIDragService.h" +#include "nsIFocusManager.h" +#include "nsIFrame.h" +#include "nsIGlobalObject.h" +#include "nsIIOService.h" +#include "nsIIdleRunnable.h" +#include "nsIInterfaceRequestorUtils.h" +#include "nsILoadContext.h" +#include "nsILoadGroup.h" +#include "nsILoadInfo.h" +#include "nsINamed.h" +#include "nsINode.h" +#include "nsIObserver.h" +#include "nsIObserverService.h" +#include "nsIPermission.h" +#include "nsIPermissionManager.h" +#include "nsIPrefBranch.h" +#include "nsIPrincipal.h" +#include "nsIPrompt.h" +#include "nsIRunnable.h" +#include "nsIScreen.h" +#include "nsIScreenManager.h" +#include "nsIScriptContext.h" +#include "nsIScriptGlobalObject.h" +#include "nsIScriptObjectPrincipal.h" +#include "nsIScrollableFrame.h" +#include "nsISerialEventTarget.h" +#include "nsISimpleEnumerator.h" +#include "nsISizeOfEventTarget.h" +#include "nsISlowScriptDebug.h" +#include "nsISupportsUtils.h" +#include "nsIThread.h" +#include "nsITimedChannel.h" +#include "nsIURI.h" +#include "nsIWeakReference.h" +#include "nsIWebBrowserChrome.h" +#include "nsIWebNavigation.h" +#include "nsIWebProgressListener.h" +#include "nsIWidget.h" +#include "nsIWidgetListener.h" +#include "nsIXULRuntime.h" +#include "nsJSPrincipals.h" +#include "nsJSUtils.h" +#include "nsLayoutStatics.h" +#include "nsLiteralString.h" +#include "nsNetUtil.h" +#include "nsPIDOMWindow.h" +#include "nsPIDOMWindowInlines.h" +#include "nsPIWindowRoot.h" +#include "nsPoint.h" +#include "nsPresContext.h" +#include "nsQueryObject.h" +#include "nsSandboxFlags.h" +#include "nsScreen.h" +#include "nsServiceManagerUtils.h" +#include "nsString.h" +#include "nsStringFlags.h" +#include "nsStringFwd.h" +#include "nsTArray.h" +#include "nsTLiteralString.h" +#include "nsTObserverArray.h" +#include "nsTStringRepr.h" +#include "nsThreadUtils.h" +#include "nsWeakReference.h" +#include "nsWindowMemoryReporter.h" +#include "nsWindowSizes.h" +#include "nsWrapperCache.h" +#include "nsWrapperCacheInlines.h" +#include "nsXULAppAPI.h" +#include "nsrootidl.h" +#include "prclist.h" +#include "prtypes.h" +#include "xpcprivate.h" +#include "xpcpublic.h" + +#include "nsIDOMXULControlElement.h" + +#ifdef NS_PRINTING +# include "nsIPrintSettings.h" +#endif + +#ifdef MOZ_WEBSPEECH +# include "mozilla/dom/SpeechSynthesis.h" +#endif + +#ifdef ANDROID +# include +#endif + +#ifdef XP_WIN +# include "mozilla/Debug.h" +# include +# define getpid _getpid +#else +# include // for getpid() +#endif + +using namespace mozilla; +using namespace mozilla::dom; +using namespace mozilla::dom::ipc; +using mozilla::TimeDuration; +using mozilla::TimeStamp; +using mozilla::dom::GamepadHandle; +using mozilla::dom::cache::CacheStorage; + +#define FORWARD_TO_OUTER(method, args, err_rval) \ + PR_BEGIN_MACRO \ + RefPtr outer = GetOuterWindowInternal(); \ + if (!HasActiveDocument()) { \ + NS_WARNING(outer ? "Inner window does not have active document." \ + : "No outer window available!"); \ + return err_rval; \ + } \ + return outer->method args; \ + PR_END_MACRO + +static nsGlobalWindowOuter* GetOuterWindowForForwarding( + nsGlobalWindowInner* aInner, ErrorResult& aError) { + nsGlobalWindowOuter* outer = aInner->GetOuterWindowInternal(); + if (MOZ_LIKELY(aInner->HasActiveDocument())) { + return outer; + } + if (!outer) { + NS_WARNING("No outer window available!"); + aError.Throw(NS_ERROR_NOT_INITIALIZED); + } else { + aError.Throw(NS_ERROR_XPC_SECURITY_MANAGER_VETO); + } + return nullptr; +} + +#define FORWARD_TO_OUTER_OR_THROW(method, args, rv, err_rval) \ + PR_BEGIN_MACRO \ + RefPtr outer = GetOuterWindowForForwarding(this, rv); \ + if (MOZ_LIKELY(outer)) { \ + return outer->method args; \ + } \ + return err_rval; \ + PR_END_MACRO + +#define FORWARD_TO_OUTER_VOID(method, args) \ + PR_BEGIN_MACRO \ + RefPtr outer = GetOuterWindowInternal(); \ + if (!HasActiveDocument()) { \ + NS_WARNING(outer ? "Inner window does not have active document." \ + : "No outer window available!"); \ + return; \ + } \ + outer->method args; \ + return; \ + PR_END_MACRO + +#define ENSURE_ACTIVE_DOCUMENT(errorresult, err_rval) \ + PR_BEGIN_MACRO \ + if (MOZ_UNLIKELY(!HasActiveDocument())) { \ + aError.Throw(NS_ERROR_XPC_SECURITY_MANAGER_VETO); \ + return err_rval; \ + } \ + PR_END_MACRO + +#define DOM_TOUCH_LISTENER_ADDED "dom-touch-listener-added" +#define MEMORY_PRESSURE_OBSERVER_TOPIC "memory-pressure" +#define PERMISSION_CHANGED_TOPIC "perm-changed" + +static LazyLogModule gDOMLeakPRLogInner("DOMLeakInner"); +extern mozilla::LazyLogModule gTimeoutLog; + +#ifdef DEBUG +static LazyLogModule gDocShellAndDOMWindowLeakLogging( + "DocShellAndDOMWindowLeak"); +#endif + +static FILE* gDumpFile = nullptr; + +nsGlobalWindowInner::InnerWindowByIdTable* + nsGlobalWindowInner::sInnerWindowsById = nullptr; + +bool nsGlobalWindowInner::sDragServiceDisabled = false; +bool nsGlobalWindowInner::sMouseDown = false; + +/** + * An indirect observer object that means we don't have to implement nsIObserver + * on nsGlobalWindow, where any script could see it. + */ +class nsGlobalWindowObserver final : public nsIObserver, + public nsIInterfaceRequestor, + public StorageNotificationObserver { + public: + explicit nsGlobalWindowObserver(nsGlobalWindowInner* aWindow) + : mWindow(aWindow) {} + NS_DECL_ISUPPORTS + NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic, + const char16_t* aData) override { + if (!mWindow) return NS_OK; + return mWindow->Observe(aSubject, aTopic, aData); + } + void Forget() { mWindow = nullptr; } + NS_IMETHOD GetInterface(const nsIID& aIID, void** aResult) override { + if (mWindow && aIID.Equals(NS_GET_IID(nsIDOMWindow)) && mWindow) { + return mWindow->QueryInterface(aIID, aResult); + } + return NS_NOINTERFACE; + } + + void ObserveStorageNotification(StorageEvent* aEvent, + const char16_t* aStorageType, + bool aPrivateBrowsing) override { + if (mWindow) { + mWindow->ObserveStorageNotification(aEvent, aStorageType, + aPrivateBrowsing); + } + } + + nsIPrincipal* GetEffectiveCookiePrincipal() const override { + return mWindow ? mWindow->GetEffectiveCookiePrincipal() : nullptr; + } + + nsIPrincipal* GetEffectiveStoragePrincipal() const override { + return mWindow ? mWindow->GetEffectiveStoragePrincipal() : nullptr; + } + + bool IsPrivateBrowsing() const override { + return mWindow ? mWindow->IsPrivateBrowsing() : false; + } + + nsIEventTarget* GetEventTarget() const override { + return mWindow ? mWindow->EventTargetFor(TaskCategory::Other) : nullptr; + } + + private: + ~nsGlobalWindowObserver() = default; + + // This reference is non-owning and safe because it's cleared by + // nsGlobalWindowInner::FreeInnerObjects(). + nsGlobalWindowInner* MOZ_NON_OWNING_REF mWindow; +}; + +NS_IMPL_ISUPPORTS(nsGlobalWindowObserver, nsIObserver, nsIInterfaceRequestor) + +class IdleRequestExecutor; + +class IdleRequestExecutorTimeoutHandler final : public TimeoutHandler { + public: + explicit IdleRequestExecutorTimeoutHandler(IdleRequestExecutor* aExecutor) + : mExecutor(aExecutor) {} + + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_CLASS(IdleRequestExecutorTimeoutHandler) + + bool Call(const char* /* unused */) override; + + private: + ~IdleRequestExecutorTimeoutHandler() override = default; + RefPtr mExecutor; +}; + +NS_IMPL_CYCLE_COLLECTION(IdleRequestExecutorTimeoutHandler, mExecutor) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(IdleRequestExecutorTimeoutHandler) +NS_IMPL_CYCLE_COLLECTING_RELEASE(IdleRequestExecutorTimeoutHandler) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestExecutorTimeoutHandler) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +class IdleRequestExecutor final : public nsIRunnable, + public nsICancelableRunnable, + public nsINamed, + public nsIIdleRunnable { + public: + explicit IdleRequestExecutor(nsGlobalWindowInner* aWindow) + : mDispatched(false), mDeadline(TimeStamp::Now()), mWindow(aWindow) { + MOZ_DIAGNOSTIC_ASSERT(mWindow); + + mIdlePeriodLimit = {mDeadline, mWindow->LastIdleRequestHandle()}; + mDelayedExecutorDispatcher = new IdleRequestExecutorTimeoutHandler(this); + } + + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(IdleRequestExecutor, nsIRunnable) + + NS_DECL_NSIRUNNABLE + NS_DECL_NSINAMED + nsresult Cancel() override; + void SetDeadline(TimeStamp aDeadline) override; + + bool IsCancelled() const { return !mWindow || mWindow->IsDying(); } + // Checks if aRequest shouldn't execute in the current idle period + // since it has been queued from a chained call to + // requestIdleCallback from within a running idle callback. + bool IneligibleForCurrentIdlePeriod(IdleRequest* aRequest) const { + return aRequest->Handle() >= mIdlePeriodLimit.mLastRequestIdInIdlePeriod && + TimeStamp::Now() <= mIdlePeriodLimit.mEndOfIdlePeriod; + } + + void MaybeUpdateIdlePeriodLimit(); + + // Maybe dispatch the IdleRequestExecutor. MabyeDispatch will + // schedule a delayed dispatch if the associated window is in the + // background or if given a time to wait until dispatching. + void MaybeDispatch(TimeStamp aDelayUntil = TimeStamp()); + void ScheduleDispatch(); + + private: + struct IdlePeriodLimit { + TimeStamp mEndOfIdlePeriod; + uint32_t mLastRequestIdInIdlePeriod; + }; + + void DelayedDispatch(uint32_t aDelay); + + ~IdleRequestExecutor() override = default; + + bool mDispatched; + TimeStamp mDeadline; + IdlePeriodLimit mIdlePeriodLimit; + RefPtr mWindow; + // The timeout handler responsible for dispatching this executor in + // the case of immediate dispatch to the idle queue isn't + // desirable. This is used if we've dispatched all idle callbacks + // that are allowed to run in the current idle period, or if the + // associated window is currently in the background. + RefPtr mDelayedExecutorDispatcher; + // If not Nothing() then this value is the handle to the currently + // scheduled delayed executor dispatcher. This is needed to be able + // to cancel the timeout handler in case of the executor being + // cancelled. + Maybe mDelayedExecutorHandle; +}; + +NS_IMPL_CYCLE_COLLECTION(IdleRequestExecutor, mWindow, + mDelayedExecutorDispatcher) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(IdleRequestExecutor) +NS_IMPL_CYCLE_COLLECTING_RELEASE(IdleRequestExecutor) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestExecutor) + NS_INTERFACE_MAP_ENTRY(nsIRunnable) + NS_INTERFACE_MAP_ENTRY(nsICancelableRunnable) + NS_INTERFACE_MAP_ENTRY(nsINamed) + NS_INTERFACE_MAP_ENTRY(nsIIdleRunnable) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRunnable) +NS_INTERFACE_MAP_END + +NS_IMETHODIMP +IdleRequestExecutor::GetName(nsACString& aName) { + aName.AssignLiteral("IdleRequestExecutor"); + return NS_OK; +} + +// MOZ_CAN_RUN_SCRIPT_BOUNDARY until nsIRunnable::Run is MOZ_CAN_RUN_SCRIPT. +// See bug 1535398. +MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP IdleRequestExecutor::Run() { + MOZ_ASSERT(NS_IsMainThread()); + + mDispatched = false; + if (mWindow) { + RefPtr window(mWindow); + window->ExecuteIdleRequest(mDeadline); + } + + return NS_OK; +} + +nsresult IdleRequestExecutor::Cancel() { + MOZ_ASSERT(NS_IsMainThread()); + + if (mDelayedExecutorHandle && mWindow) { + mWindow->TimeoutManager().ClearTimeout( + mDelayedExecutorHandle.value(), Timeout::Reason::eIdleCallbackTimeout); + } + + mWindow = nullptr; + return NS_OK; +} + +void IdleRequestExecutor::SetDeadline(TimeStamp aDeadline) { + MOZ_ASSERT(NS_IsMainThread()); + + if (!mWindow) { + return; + } + + mDeadline = aDeadline; +} + +void IdleRequestExecutor::MaybeUpdateIdlePeriodLimit() { + if (TimeStamp::Now() > mIdlePeriodLimit.mEndOfIdlePeriod) { + mIdlePeriodLimit = {mDeadline, mWindow->LastIdleRequestHandle()}; + } +} + +void IdleRequestExecutor::MaybeDispatch(TimeStamp aDelayUntil) { + // If we've already dispatched the executor we don't want to do it + // again. Also, if we've called IdleRequestExecutor::Cancel mWindow + // will be null, which indicates that we shouldn't dispatch this + // executor either. + if (mDispatched || IsCancelled()) { + return; + } + + mDispatched = true; + + nsPIDOMWindowOuter* outer = mWindow->GetOuterWindow(); + if (outer && outer->IsBackground()) { + // Set a timeout handler with a timeout of 0 ms to throttle idle + // callback requests coming from a backround window using + // background timeout throttling. + DelayedDispatch(0); + return; + } + + TimeStamp now = TimeStamp::Now(); + if (!aDelayUntil || aDelayUntil < now) { + ScheduleDispatch(); + return; + } + + TimeDuration delay = aDelayUntil - now; + DelayedDispatch(static_cast(delay.ToMilliseconds())); +} + +void IdleRequestExecutor::ScheduleDispatch() { + MOZ_ASSERT(mWindow); + mDelayedExecutorHandle = Nothing(); + RefPtr request = this; + NS_DispatchToCurrentThreadQueue(request.forget(), EventQueuePriority::Idle); +} + +void IdleRequestExecutor::DelayedDispatch(uint32_t aDelay) { + MOZ_ASSERT(mWindow); + MOZ_ASSERT(mDelayedExecutorHandle.isNothing()); + int32_t handle; + mWindow->TimeoutManager().SetTimeout( + mDelayedExecutorDispatcher, aDelay, false, + Timeout::Reason::eIdleCallbackTimeout, &handle); + mDelayedExecutorHandle = Some(handle); +} + +bool IdleRequestExecutorTimeoutHandler::Call(const char* /* unused */) { + if (!mExecutor->IsCancelled()) { + mExecutor->ScheduleDispatch(); + } + return true; +} + +void nsGlobalWindowInner::ScheduleIdleRequestDispatch() { + AssertIsOnMainThread(); + + if (!mIdleRequestExecutor) { + mIdleRequestExecutor = new IdleRequestExecutor(this); + } + + mIdleRequestExecutor->MaybeDispatch(); +} + +void nsGlobalWindowInner::SuspendIdleRequests() { + if (mIdleRequestExecutor) { + mIdleRequestExecutor->Cancel(); + mIdleRequestExecutor = nullptr; + } +} + +void nsGlobalWindowInner::ResumeIdleRequests() { + MOZ_ASSERT(!mIdleRequestExecutor); + + ScheduleIdleRequestDispatch(); +} + +void nsGlobalWindowInner::RemoveIdleCallback( + mozilla::dom::IdleRequest* aRequest) { + AssertIsOnMainThread(); + + if (aRequest->HasTimeout()) { + mTimeoutManager->ClearTimeout(aRequest->GetTimeoutHandle(), + Timeout::Reason::eIdleCallbackTimeout); + } + + aRequest->removeFrom(mIdleRequestCallbacks); +} + +void nsGlobalWindowInner::RunIdleRequest(IdleRequest* aRequest, + DOMHighResTimeStamp aDeadline, + bool aDidTimeout) { + AssertIsOnMainThread(); + // XXXbz Do we still need this RefPtr? MOZ_CAN_RUN_SCRIPT should + // guarantee that caller is holding a strong ref on the stack. + RefPtr request(aRequest); + RemoveIdleCallback(request); + request->IdleRun(this, aDeadline, aDidTimeout); +} + +void nsGlobalWindowInner::ExecuteIdleRequest(TimeStamp aDeadline) { + AssertIsOnMainThread(); + RefPtr request = mIdleRequestCallbacks.getFirst(); + + if (!request) { + // There are no more idle requests, so stop scheduling idle + // request callbacks. + return; + } + + // If the request that we're trying to execute has been queued + // during the current idle period, then dispatch it again at the end + // of the idle period. + if (mIdleRequestExecutor->IneligibleForCurrentIdlePeriod(request)) { + mIdleRequestExecutor->MaybeDispatch(aDeadline); + return; + } + + DOMHighResTimeStamp deadline = 0.0; + + if (Performance* perf = GetPerformance()) { + deadline = perf->GetDOMTiming()->TimeStampToDOMHighRes(aDeadline); + } + + mIdleRequestExecutor->MaybeUpdateIdlePeriodLimit(); + RunIdleRequest(request, deadline, false); + + // Running the idle callback could've suspended the window, in which + // case mIdleRequestExecutor will be null. + if (mIdleRequestExecutor) { + mIdleRequestExecutor->MaybeDispatch(); + } +} + +class IdleRequestTimeoutHandler final : public TimeoutHandler { + public: + IdleRequestTimeoutHandler(JSContext* aCx, IdleRequest* aIdleRequest, + nsPIDOMWindowInner* aWindow) + : TimeoutHandler(aCx), mIdleRequest(aIdleRequest), mWindow(aWindow) {} + + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_CLASS(IdleRequestTimeoutHandler) + + MOZ_CAN_RUN_SCRIPT bool Call(const char* /* unused */) override { + RefPtr window(nsGlobalWindowInner::Cast(mWindow)); + RefPtr request(mIdleRequest); + window->RunIdleRequest(request, 0.0, true); + return true; + } + + private: + ~IdleRequestTimeoutHandler() override = default; + + RefPtr mIdleRequest; + nsCOMPtr mWindow; +}; + +NS_IMPL_CYCLE_COLLECTION(IdleRequestTimeoutHandler, mIdleRequest, mWindow) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(IdleRequestTimeoutHandler) +NS_IMPL_CYCLE_COLLECTING_RELEASE(IdleRequestTimeoutHandler) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestTimeoutHandler) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +uint32_t nsGlobalWindowInner::RequestIdleCallback( + JSContext* aCx, IdleRequestCallback& aCallback, + const IdleRequestOptions& aOptions, ErrorResult& aError) { + AssertIsOnMainThread(); + + if (IsDying()) { + return 0; + } + + uint32_t handle = mIdleRequestCallbackCounter++; + + RefPtr request = new IdleRequest(&aCallback, handle); + + if (aOptions.mTimeout.WasPassed()) { + int32_t timeoutHandle; + RefPtr handler( + new IdleRequestTimeoutHandler(aCx, request, this)); + + nsresult rv = mTimeoutManager->SetTimeout( + handler, aOptions.mTimeout.Value(), false, + Timeout::Reason::eIdleCallbackTimeout, &timeoutHandle); + + if (NS_WARN_IF(NS_FAILED(rv))) { + return 0; + } + + request->SetTimeoutHandle(timeoutHandle); + } + + mIdleRequestCallbacks.insertBack(request); + + if (!IsSuspended()) { + ScheduleIdleRequestDispatch(); + } + + return handle; +} + +void nsGlobalWindowInner::CancelIdleCallback(uint32_t aHandle) { + for (IdleRequest* r : mIdleRequestCallbacks) { + if (r->Handle() == aHandle) { + RemoveIdleCallback(r); + break; + } + } +} + +void nsGlobalWindowInner::DisableIdleCallbackRequests() { + if (mIdleRequestExecutor) { + mIdleRequestExecutor->Cancel(); + mIdleRequestExecutor = nullptr; + } + + while (!mIdleRequestCallbacks.isEmpty()) { + RefPtr request = mIdleRequestCallbacks.getFirst(); + RemoveIdleCallback(request); + } +} + +bool nsGlobalWindowInner::IsBackgroundInternal() const { + return !mOuterWindow || mOuterWindow->IsBackground(); +} + +class PromiseDocumentFlushedResolver final { + public: + PromiseDocumentFlushedResolver(Promise* aPromise, + PromiseDocumentFlushedCallback& aCallback) + : mPromise(aPromise), mCallback(&aCallback) {} + + virtual ~PromiseDocumentFlushedResolver() = default; + + void Call() { + nsMutationGuard guard; + ErrorResult error; + JS::Rooted returnVal(RootingCx()); + mCallback->Call(&returnVal, error); + + if (error.Failed()) { + mPromise->MaybeReject(std::move(error)); + } else if (guard.Mutated(0)) { + // Something within the callback mutated the DOM. + mPromise->MaybeRejectWithNoModificationAllowedError( + "DOM mutated from promiseDocumentFlushed callbacks"); + } else { + mPromise->MaybeResolve(returnVal); + } + } + + RefPtr mPromise; + RefPtr mCallback; +}; + +//***************************************************************************** +//*** nsGlobalWindowInner: Object Management +//***************************************************************************** + +nsGlobalWindowInner::nsGlobalWindowInner(nsGlobalWindowOuter* aOuterWindow, + WindowGlobalChild* aActor) + : nsPIDOMWindowInner(aOuterWindow, aActor), + mHasOrientationChangeListeners(false), + mWasOffline(false), + mHasHadSlowScript(false), + mIsChrome(false), + mCleanMessageManager(false), + mNeedsFocus(true), + mHasFocus(false), + mFocusByKeyOccurred(false), + mDidFireDocElemInserted(false), + mHasGamepad(false), + mHasXRSession(false), + mHasVRDisplayActivateEvents(false), + mXRRuntimeDetectionInFlight(false), + mXRPermissionRequestInFlight(false), + mXRPermissionGranted(false), + mWasCurrentInnerWindow(false), + mHasSeenGamepadInput(false), + mHintedWasLoading(false), + mHasOpenedExternalProtocolFrame(false), + mScrollMarksOnHScrollbar(false), + mStorageAllowedReasonCache(0), + mSuspendDepth(0), + mFreezeDepth(0), +#ifdef DEBUG + mSerial(0), +#endif + mFocusMethod(0), + mIdleRequestCallbackCounter(1), + mIdleRequestExecutor(nullptr), + mObservingRefresh(false), + mIteratingDocumentFlushedResolvers(false), + mCanSkipCCGeneration(0) { + mIsInnerWindow = true; + + AssertIsOnMainThread(); + nsLayoutStatics::AddRef(); + + // Initialize the PRCList (this). + PR_INIT_CLIST(this); + + // add this inner window to the outer window list of inners. + PR_INSERT_AFTER(this, aOuterWindow); + + mTimeoutManager = MakeUnique( + *this, StaticPrefs::dom_timeout_max_idle_defer_ms()); + + mObserver = new nsGlobalWindowObserver(this); + if (nsCOMPtr os = services::GetObserverService()) { + // Watch for online/offline status changes so we can fire events. Use + // a strong reference. + os->AddObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC, false); + os->AddObserver(mObserver, MEMORY_PRESSURE_OBSERVER_TOPIC, false); + os->AddObserver(mObserver, PERMISSION_CHANGED_TOPIC, false); + os->AddObserver(mObserver, "screen-information-changed", false); + } + + Preferences::AddStrongObserver(mObserver, "intl.accept_languages"); + + // Watch for storage notifications so we can fire storage events. + RefPtr sns = StorageNotifierService::GetOrCreate(); + if (sns) { + sns->Register(mObserver); + } + + if (XRE_IsContentProcess()) { + nsCOMPtr docShell = GetDocShell(); + if (docShell) { + mBrowserChild = docShell->GetBrowserChild(); + } + } + + if (gDumpFile == nullptr) { + nsAutoCString fname; + Preferences::GetCString("browser.dom.window.dump.file", fname); + if (!fname.IsEmpty()) { + // If this fails to open, Dump() knows to just go to stdout on null. + gDumpFile = fopen(fname.get(), "wb+"); + } else { + gDumpFile = stdout; + } + } + +#ifdef DEBUG + mSerial = nsContentUtils::InnerOrOuterWindowCreated(); + + MOZ_LOG(gDocShellAndDOMWindowLeakLogging, LogLevel::Info, + ("++DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p]\n", + nsContentUtils::GetCurrentInnerOrOuterWindowCount(), + static_cast(ToCanonicalSupports(this)), getpid(), mSerial, + static_cast(ToCanonicalSupports(aOuterWindow)))); +#endif + + MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug, + ("DOMWINDOW %p created outer=%p", this, aOuterWindow)); + + // Add ourselves to the inner windows list. + MOZ_ASSERT(sInnerWindowsById, "Inner Windows hash table must be created!"); + MOZ_ASSERT(!sInnerWindowsById->Contains(mWindowID), + "This window shouldn't be in the hash table yet!"); + // We seem to see crashes in release builds because of null + // |sInnerWindowsById|. + if (sInnerWindowsById) { + sInnerWindowsById->InsertOrUpdate(mWindowID, this); + } +} + +#ifdef DEBUG + +/* static */ +void nsGlobalWindowInner::AssertIsOnMainThread() { + MOZ_ASSERT(NS_IsMainThread()); +} + +#endif // DEBUG + +/* static */ +void nsGlobalWindowInner::Init() { + AssertIsOnMainThread(); + + NS_ASSERTION(gDOMLeakPRLogInner, + "gDOMLeakPRLogInner should have been initialized!"); + + sInnerWindowsById = new InnerWindowByIdTable(); +} + +nsGlobalWindowInner::~nsGlobalWindowInner() { + AssertIsOnMainThread(); + MOZ_ASSERT(!mHintedWasLoading); + + if (IsChromeWindow()) { + MOZ_ASSERT(mCleanMessageManager, + "chrome windows may always disconnect the msg manager"); + + DisconnectAndClearGroupMessageManagers(); + + if (mChromeFields.mMessageManager) { + static_cast(mChromeFields.mMessageManager.get()) + ->Disconnect(); + } + + mCleanMessageManager = false; + } + + // In most cases this should already have been called, but call it again + // here to catch any corner cases. + FreeInnerObjects(); + + if (sInnerWindowsById) { + sInnerWindowsById->Remove(mWindowID); + } + + nsContentUtils::InnerOrOuterWindowDestroyed(); + +#ifdef DEBUG + if (MOZ_LOG_TEST(gDocShellAndDOMWindowLeakLogging, LogLevel::Info)) { + nsAutoCString url; + if (mLastOpenedURI) { + url = mLastOpenedURI->GetSpecOrDefault(); + + // Data URLs can be very long, so truncate to avoid flooding the log. + const uint32_t maxURLLength = 1000; + if (url.Length() > maxURLLength) { + url.Truncate(maxURLLength); + } + } + + nsGlobalWindowOuter* outer = nsGlobalWindowOuter::Cast(mOuterWindow); + MOZ_LOG( + gDocShellAndDOMWindowLeakLogging, LogLevel::Info, + ("--DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p] [url = " + "%s]\n", + nsContentUtils::GetCurrentInnerOrOuterWindowCount(), + static_cast(ToCanonicalSupports(this)), getpid(), mSerial, + static_cast(ToCanonicalSupports(outer)), url.get())); + } +#endif + MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug, + ("DOMWINDOW %p destroyed", this)); + + Telemetry::Accumulate(Telemetry::INNERWINDOWS_WITH_MUTATION_LISTENERS, + mMutationBits ? 1 : 0); + + // An inner window is destroyed, pull it out of the outer window's + // list if inner windows. + + PR_REMOVE_LINK(this); + + // If our outer window's inner window is this window, null out the + // outer window's reference to this window that's being deleted. + nsGlobalWindowOuter* outer = GetOuterWindowInternal(); + if (outer) { + outer->MaybeClearInnerWindow(this); + } + + // We don't have to leave the tab group if we are an inner window. + + nsCOMPtr ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID); + if (ac) ac->RemoveWindowAsListener(this); + + nsLayoutStatics::Release(); +} + +// static +void nsGlobalWindowInner::ShutDown() { + AssertIsOnMainThread(); + + if (gDumpFile && gDumpFile != stdout) { + fclose(gDumpFile); + } + gDumpFile = nullptr; + + delete sInnerWindowsById; + sInnerWindowsById = nullptr; +} + +void nsGlobalWindowInner::FreeInnerObjects() { + if (IsDying()) { + return; + } + StartDying(); + + if (mDoc && mDoc->GetWindowContext()) { + // The document is about to lose its window, so this is a good time to send + // our page use counters. + // + // (We also do this in Document::SetScriptGlobalObject(nullptr), which + // catches most cases of documents losing their window, but not all.) + mDoc->SendPageUseCounters(); + } + + // Make sure that this is called before we null out the document and + // other members that the window destroyed observers could + // re-create. + NotifyDOMWindowDestroyed(this); + if (auto* reporter = nsWindowMemoryReporter::Get()) { + reporter->ObserveDOMWindowDetached(this); + } + + // Kill all of the workers for this window. + CancelWorkersForWindow(*this); + + for (RefPtr pinnedWorker : + mSharedWorkers.ForwardRange()) { + pinnedWorker->Close(); + } + + if (mTimeoutManager) { + mTimeoutManager->ClearAllTimeouts(); + } + + DisableIdleCallbackRequests(); + + mChromeEventHandler = nullptr; + + if (mListenerManager) { + mListenerManager->RemoveAllListeners(); + mListenerManager->Disconnect(); + mListenerManager = nullptr; + } + + mHistory = nullptr; + + if (mNavigator) { + mNavigator->OnNavigation(); + mNavigator->Invalidate(); + mNavigator = nullptr; + } + + mScreen = nullptr; + + if (mDoc) { + // Remember the document's principal, URI, and CSP. + mDocumentPrincipal = mDoc->NodePrincipal(); + mDocumentCookiePrincipal = mDoc->EffectiveCookiePrincipal(); + mDocumentStoragePrincipal = mDoc->EffectiveStoragePrincipal(); + mDocumentPartitionedPrincipal = mDoc->PartitionedPrincipal(); + mDocumentURI = mDoc->GetDocumentURI(); + mDocBaseURI = mDoc->GetDocBaseURI(); + mDocumentCsp = mDoc->GetCsp(); + + while (mDoc->EventHandlingSuppressed()) { + mDoc->UnsuppressEventHandlingAndFireEvents(false); + } + } + + // Remove our reference to the document and the document principal. + mFocusedElement = nullptr; + + if (mIndexedDB) { + mIndexedDB->DisconnectFromGlobal(this); + mIndexedDB = nullptr; + } + + nsIGlobalObject::UnlinkObjectsInGlobal(); + + NotifyWindowIDDestroyed("inner-window-destroyed"); + + for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) { + mAudioContexts[i]->OnWindowDestroy(); + } + mAudioContexts.Clear(); + + for (MediaKeys* mediaKeys : mMediaKeysInstances) { + mediaKeys->OnInnerWindowDestroy(); + } + mMediaKeysInstances.Clear(); + + DisableGamepadUpdates(); + mHasGamepad = false; + mGamepads.Clear(); + DisableVRUpdates(); + mHasXRSession = false; + mHasVRDisplayActivateEvents = false; + mXRRuntimeDetectionInFlight = false; + mXRPermissionRequestInFlight = false; + mXRPermissionGranted = false; + mVRDisplays.Clear(); + + // This breaks a cycle between the window and the ClientSource object. + mClientSource.reset(); + + if (mWindowGlobalChild) { + // Remove any remaining listeners. + int64_t nListeners = mWindowGlobalChild->BeforeUnloadListeners(); + for (int64_t i = 0; i < nListeners; ++i) { + mWindowGlobalChild->BeforeUnloadRemoved(); + } + MOZ_ASSERT(mWindowGlobalChild->BeforeUnloadListeners() == 0); + } + + // If we have any promiseDocumentFlushed callbacks, fire them now so + // that the Promises can resolve. + CallDocumentFlushedResolvers(/* aUntilExhaustion = */ true); + + DisconnectGlobalTeardownObservers(); + +#ifdef MOZ_WIDGET_ANDROID + DisableOrientationChangeListener(); +#endif + + if (mObserver) { + if (nsCOMPtr os = services::GetObserverService()) { + os->RemoveObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC); + os->RemoveObserver(mObserver, MEMORY_PRESSURE_OBSERVER_TOPIC); + os->RemoveObserver(mObserver, PERMISSION_CHANGED_TOPIC); + os->RemoveObserver(mObserver, "screen-information-changed"); + } + + RefPtr sns = StorageNotifierService::GetOrCreate(); + if (sns) { + sns->Unregister(mObserver); + } + + Preferences::RemoveObserver(mObserver, "intl.accept_languages"); + + // Drop its reference to this dying window, in case for some bogus reason + // the object stays around. + mObserver->Forget(); + } + + mMenubar = nullptr; + mToolbar = nullptr; + mLocationbar = nullptr; + mPersonalbar = nullptr; + mStatusbar = nullptr; + mScrollbars = nullptr; + + mConsole = nullptr; + + mPaintWorklet = nullptr; + + mExternal = nullptr; + mInstallTrigger = nullptr; + + if (mLocalStorage) { + mLocalStorage->Disconnect(); + mLocalStorage = nullptr; + } + mSessionStorage = nullptr; + mPerformance = nullptr; + + mContentMediaController = nullptr; + + if (mWebTaskScheduler) { + mWebTaskScheduler->Disconnect(); + mWebTaskScheduler = nullptr; + } + + mSharedWorkers.Clear(); + +#ifdef MOZ_WEBSPEECH + mSpeechSynthesis = nullptr; +#endif + + mGlean = nullptr; + mGleanPings = nullptr; + + mParentTarget = nullptr; + + if (mCleanMessageManager) { + MOZ_ASSERT(mIsChrome, "only chrome should have msg manager cleaned"); + if (mChromeFields.mMessageManager) { + mChromeFields.mMessageManager->Disconnect(); + } + } + + if (mWindowGlobalChild && !mWindowGlobalChild->IsClosed()) { + mWindowGlobalChild->Destroy(); + } + + mIntlUtils = nullptr; + + HintIsLoading(false); +} + +//***************************************************************************** +// nsGlobalWindowInner::nsISupports +//***************************************************************************** + +// QueryInterface implementation for nsGlobalWindowInner +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGlobalWindowInner) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, EventTarget) + NS_INTERFACE_MAP_ENTRY(nsIDOMWindow) + NS_INTERFACE_MAP_ENTRY(nsIGlobalObject) + NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObject) + NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal) + NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget) + NS_INTERFACE_MAP_ENTRY(nsPIDOMWindowInner) + NS_INTERFACE_MAP_ENTRY(mozIDOMWindow) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDOMChromeWindow, IsChromeWindow()) + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) + NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGlobalWindowInner) +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGlobalWindowInner) + +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGlobalWindowInner) + if (tmp->IsBlackForCC(false)) { + if (nsCCUncollectableMarker::InGeneration(tmp->mCanSkipCCGeneration)) { + return true; + } + tmp->mCanSkipCCGeneration = nsCCUncollectableMarker::sGeneration; + if (EventListenerManager* elm = tmp->GetExistingListenerManager()) { + elm->MarkForCC(); + } + if (tmp->mTimeoutManager) { + tmp->mTimeoutManager->UnmarkGrayTimers(); + } + return true; + } +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END + +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsGlobalWindowInner) + return tmp->IsBlackForCC(true); +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END + +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsGlobalWindowInner) + return tmp->IsBlackForCC(false); +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END + +NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalWindowInner) + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindowInner) + if (MOZ_UNLIKELY(cb.WantDebugInfo())) { + char name[512]; + nsAutoCString uri; + if (tmp->mDoc && tmp->mDoc->GetDocumentURI()) { + uri = tmp->mDoc->GetDocumentURI()->GetSpecOrDefault(); + } + SprintfLiteral(name, "nsGlobalWindowInner # %" PRIu64 " inner %s", + tmp->mWindowID, uri.get()); + cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name); + } else { + NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsGlobalWindowInner, tmp->mRefCnt.get()) + } + + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNavigator) + + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance) + + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWebTaskScheduler) + +#ifdef MOZ_WEBSPEECH + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSpeechSynthesis) +#endif + + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlean) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGleanPings) + + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOuterWindow) + + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTopInnerWindow) + + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager) + + if (tmp->mTimeoutManager) { + tmp->mTimeoutManager->ForEachUnorderedTimeout([&cb](Timeout* timeout) { + cb.NoteNativeChild(timeout, NS_CYCLE_COLLECTION_PARTICIPANT(Timeout)); + }); + } + + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocation) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHistory) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCustomElements) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSharedWorkers) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStorage) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSessionStorage) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndexedDB) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPrincipal) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentCookiePrincipal) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentStoragePrincipal) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPartitionedPrincipal) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentCsp) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowserChild) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDoc) + + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleRequestExecutor) + for (IdleRequest* request : tmp->mIdleRequestCallbacks) { + cb.NoteNativeChild(request, NS_CYCLE_COLLECTION_PARTICIPANT(IdleRequest)); + } + + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mClientSource) + + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGamepads) + + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCacheStorage) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVRDisplays) + + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDebuggerNotificationManager) + + // Traverse stuff from nsPIDOMWindow + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeEventHandler) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentTarget) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFocusedElement) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowsingContext) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindowGlobalChild) + + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMenubar) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mToolbar) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocationbar) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPersonalbar) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStatusbar) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScrollbars) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCrypto) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsole) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPaintWorklet) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExternal) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInstallTrigger) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIntlUtils) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVisualViewport) + + tmp->TraverseObjectsInGlobal(cb); + + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeFields.mMessageManager) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeFields.mGroupMessageManagers) + + for (size_t i = 0; i < tmp->mDocumentFlushedResolvers.Length(); i++) { + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentFlushedResolvers[i]->mPromise); + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentFlushedResolvers[i]->mCallback); + } + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindowInner) + NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE + if (sInnerWindowsById) { + sInnerWindowsById->Remove(tmp->mWindowID); + } + + JSObject* wrapper = tmp->GetWrapperPreserveColor(); + if (wrapper) { + // Mark our realm as dead, so the JS engine won't hand out our + // global after this point. + JS::SetRealmNonLive(js::GetNonCCWObjectRealm(wrapper)); + } + + NS_IMPL_CYCLE_COLLECTION_UNLINK(mNavigator) + + NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance) + + if (tmp->mWebTaskScheduler) { + tmp->mWebTaskScheduler->Disconnect(); + NS_IMPL_CYCLE_COLLECTION_UNLINK(mWebTaskScheduler) + } + +#ifdef MOZ_WEBSPEECH + NS_IMPL_CYCLE_COLLECTION_UNLINK(mSpeechSynthesis) +#endif + + NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlean) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mGleanPings) + + if (tmp->mOuterWindow) { + nsGlobalWindowOuter::Cast(tmp->mOuterWindow)->MaybeClearInnerWindow(tmp); + NS_IMPL_CYCLE_COLLECTION_UNLINK(mOuterWindow) + } + + if (tmp->mListenerManager) { + tmp->mListenerManager->Disconnect(); + NS_IMPL_CYCLE_COLLECTION_UNLINK(mListenerManager) + } + + // Here the Timeouts list would've been unlinked, but we rely on + // that Timeout objects have been traced and will remove themselves + // while unlinking. + + tmp->UpdateTopInnerWindow(); + NS_IMPL_CYCLE_COLLECTION_UNLINK(mTopInnerWindow) + + NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocation) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mHistory) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mCustomElements) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mSharedWorkers) + if (tmp->mLocalStorage) { + tmp->mLocalStorage->Disconnect(); + NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalStorage) + } + NS_IMPL_CYCLE_COLLECTION_UNLINK(mSessionStorage) + if (tmp->mIndexedDB) { + tmp->mIndexedDB->DisconnectFromGlobal(tmp); + NS_IMPL_CYCLE_COLLECTION_UNLINK(mIndexedDB) + } + NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPrincipal) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentCookiePrincipal) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentStoragePrincipal) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPartitionedPrincipal) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentCsp) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowserChild) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mDoc) + + NS_IMPL_CYCLE_COLLECTION_UNLINK(mGamepads) + + NS_IMPL_CYCLE_COLLECTION_UNLINK(mCacheStorage) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mVRDisplays) + + NS_IMPL_CYCLE_COLLECTION_UNLINK(mDebuggerNotificationManager) + + // Unlink stuff from nsPIDOMWindow + NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeEventHandler) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mParentTarget) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mFocusedElement) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowsingContext) + + MOZ_DIAGNOSTIC_ASSERT( + !tmp->mWindowGlobalChild || tmp->mWindowGlobalChild->IsClosed(), + "How are we unlinking a window before its actor has been destroyed?"); + NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindowGlobalChild) + + NS_IMPL_CYCLE_COLLECTION_UNLINK(mMenubar) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mToolbar) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocationbar) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mPersonalbar) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mStatusbar) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mScrollbars) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mCrypto) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mPaintWorklet) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mExternal) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mInstallTrigger) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mIntlUtils) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mVisualViewport) + + tmp->UnlinkObjectsInGlobal(); + + NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleRequestExecutor) + + // Here the IdleRequest list would've been unlinked, but we rely on + // that IdleRequest objects have been traced and will remove + // themselves while unlinking. + + NS_IMPL_CYCLE_COLLECTION_UNLINK(mClientSource) + + if (tmp->IsChromeWindow()) { + if (tmp->mChromeFields.mMessageManager) { + static_cast( + tmp->mChromeFields.mMessageManager.get()) + ->Disconnect(); + NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeFields.mMessageManager) + } + tmp->DisconnectAndClearGroupMessageManagers(); + NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeFields.mGroupMessageManagers) + } + + for (size_t i = 0; i < tmp->mDocumentFlushedResolvers.Length(); i++) { + NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentFlushedResolvers[i]->mPromise); + NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentFlushedResolvers[i]->mCallback); + } + tmp->mDocumentFlushedResolvers.Clear(); + + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +#ifdef DEBUG +void nsGlobalWindowInner::RiskyUnlink() { + NS_CYCLE_COLLECTION_INNERNAME.Unlink(this); +} +#endif + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGlobalWindowInner) + NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +bool nsGlobalWindowInner::IsBlackForCC(bool aTracingNeeded) { + if (!nsCCUncollectableMarker::sGeneration) { + return false; + } + + return (nsCCUncollectableMarker::InGeneration(GetMarkedCCGeneration()) || + HasKnownLiveWrapper()) && + (!aTracingNeeded || HasNothingToTrace(ToSupports(this))); +} + +//***************************************************************************** +// nsGlobalWindowInner::nsIScriptGlobalObject +//***************************************************************************** + +bool nsGlobalWindowInner::ShouldResistFingerprinting( + RFPTarget aTarget /* = RFPTarget::Unknown */) const { + if (mDoc) { + return mDoc->ShouldResistFingerprinting(aTarget); + } + return nsContentUtils::ShouldResistFingerprinting( + "If we do not have a document then we do not have any context" + "to make an informed RFP choice, so we fall back to the global pref", + aTarget); +} + +OriginTrials nsGlobalWindowInner::Trials() const { + return OriginTrials::FromWindow(this); +} + +FontFaceSet* nsGlobalWindowInner::GetFonts() { + if (mDoc) { + return mDoc->Fonts(); + } + return nullptr; +} + +mozilla::Result +nsGlobalWindowInner::GetStorageKey() { + MOZ_ASSERT(NS_IsMainThread()); + + nsIPrincipal* principal = GetEffectiveStoragePrincipal(); + if (!principal) { + return mozilla::Err(NS_ERROR_FAILURE); + } + + mozilla::ipc::PrincipalInfo principalInfo; + nsresult rv = PrincipalToPrincipalInfo(principal, &principalInfo); + if (NS_FAILED(rv)) { + return mozilla::Err(rv); + } + + // Block expanded and null principals, let content and system through. + if (principalInfo.type() != + mozilla::ipc::PrincipalInfo::TContentPrincipalInfo && + principalInfo.type() != + mozilla::ipc::PrincipalInfo::TSystemPrincipalInfo) { + return Err(NS_ERROR_DOM_SECURITY_ERR); + } + + return std::move(principalInfo); +} + +mozilla::dom::StorageManager* nsGlobalWindowInner::GetStorageManager() { + return Navigator()->Storage(); +} + +nsresult nsGlobalWindowInner::EnsureScriptEnvironment() { + // NOTE: We can't use FORWARD_TO_OUTER here because we don't want to fail if + // we're called on an inactive inner window. + nsGlobalWindowOuter* outer = GetOuterWindowInternal(); + if (!outer) { + NS_WARNING("No outer window available!"); + return NS_ERROR_FAILURE; + } + return outer->EnsureScriptEnvironment(); +} + +nsIScriptContext* nsGlobalWindowInner::GetScriptContext() { + nsGlobalWindowOuter* outer = GetOuterWindowInternal(); + if (!outer) { + return nullptr; + } + return outer->GetScriptContext(); +} + +void nsGlobalWindowInner::TraceGlobalJSObject(JSTracer* aTrc) { + TraceWrapper(aTrc, "active window global"); +} + +void nsGlobalWindowInner::UpdateAutoplayPermission() { + if (!GetWindowContext()) { + return; + } + uint32_t perm = + media::AutoplayPolicy::GetSiteAutoplayPermission(GetPrincipal()); + if (GetWindowContext()->GetAutoplayPermission() == perm) { + return; + } + + // Setting autoplay permission on a discarded context has no effect. + Unused << GetWindowContext()->SetAutoplayPermission(perm); +} + +void nsGlobalWindowInner::UpdateShortcutsPermission() { + if (!GetWindowContext() || + !GetWindowContext()->GetBrowsingContext()->IsTop()) { + // We only cache the shortcuts permission on top-level WindowContexts + // since we always check the top-level principal for the permission. + return; + } + + uint32_t perm = GetShortcutsPermission(GetPrincipal()); + + if (GetWindowContext()->GetShortcutsPermission() == perm) { + return; + } + + // If the WindowContext is discarded this has no effect. + Unused << GetWindowContext()->SetShortcutsPermission(perm); +} + +/* static */ +uint32_t nsGlobalWindowInner::GetShortcutsPermission(nsIPrincipal* aPrincipal) { + uint32_t perm = nsIPermissionManager::DENY_ACTION; + nsCOMPtr permMgr = + mozilla::components::PermissionManager::Service(); + if (aPrincipal && permMgr) { + permMgr->TestExactPermissionFromPrincipal(aPrincipal, "shortcuts"_ns, + &perm); + } + return perm; +} + +void nsGlobalWindowInner::UpdatePopupPermission() { + if (!GetWindowContext()) { + return; + } + + uint32_t perm = PopupBlocker::GetPopupPermission(GetPrincipal()); + if (GetWindowContext()->GetPopupPermission() == perm) { + return; + } + + // If the WindowContext is discarded this has no effect. + Unused << GetWindowContext()->SetPopupPermission(perm); +} + +void nsGlobalWindowInner::UpdatePermissions() { + if (!GetWindowContext()) { + return; + } + + nsCOMPtr principal = GetPrincipal(); + RefPtr windowContext = GetWindowContext(); + + WindowContext::Transaction txn; + txn.SetAutoplayPermission( + media::AutoplayPolicy::GetSiteAutoplayPermission(principal)); + txn.SetPopupPermission(PopupBlocker::GetPopupPermission(principal)); + + if (windowContext->IsTop()) { + txn.SetShortcutsPermission(GetShortcutsPermission(principal)); + } + + // Setting permissions on a discarded WindowContext has no effect + Unused << txn.Commit(windowContext); +} + +void nsGlobalWindowInner::InitDocumentDependentState(JSContext* aCx) { + MOZ_ASSERT(mDoc); + + if (MOZ_LOG_TEST(gDOMLeakPRLogInner, LogLevel::Debug)) { + nsIURI* uri = mDoc->GetDocumentURI(); + MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug, + ("DOMWINDOW %p SetNewDocument %s", this, + uri ? uri->GetSpecOrDefault().get() : "")); + } + + mFocusedElement = nullptr; + mLocalStorage = nullptr; + mSessionStorage = nullptr; + mPerformance = nullptr; + if (mWebTaskScheduler) { + mWebTaskScheduler->Disconnect(); + mWebTaskScheduler = nullptr; + } + + // This must be called after nullifying the internal objects because here we + // could recreate them, calling the getter methods, and store them into the JS + // slots. If we nullify them after, the slot values and the objects will be + // out of sync. + ClearDocumentDependentSlots(aCx); + + if (!mWindowGlobalChild) { + mWindowGlobalChild = WindowGlobalChild::Create(this); + } + MOZ_ASSERT(!GetWindowContext()->HasBeenUserGestureActivated(), + "WindowContext should always not have user gesture activation at " + "this point."); + + UpdatePermissions(); + + RefPtr permDelegateHandler = + mDoc->GetPermissionDelegateHandler(); + + if (permDelegateHandler) { + permDelegateHandler->PopulateAllDelegatedPermissions(); + } + +#if defined(MOZ_WIDGET_ANDROID) + // When we insert the new document to the window in the top-level browsing + // context, we should reset the status of the request which is used for the + // previous document. + if (mWindowGlobalChild && GetBrowsingContext() && + !GetBrowsingContext()->GetParent()) { + // Return value of setting synced field should be checked. See bug 1656492. + Unused << GetBrowsingContext()->ResetGVAutoplayRequestStatus(); + } +#endif + +#ifdef DEBUG + mLastOpenedURI = mDoc->GetDocumentURI(); +#endif + + Telemetry::Accumulate(Telemetry::INNERWINDOWS_WITH_MUTATION_LISTENERS, + mMutationBits ? 1 : 0); + + // Clear our mutation bitfield. + mMutationBits = 0; +} + +nsresult nsGlobalWindowInner::EnsureClientSource() { + MOZ_DIAGNOSTIC_ASSERT(mDoc); + + bool newClientSource = false; + + // Get the load info for the document if we performed a load. Be careful not + // to look at local URLs, though. Local URLs are those that have a scheme of: + // * about: + // * data: + // * blob: + // We also do an additional check here so that we only treat about:blank + // and about:srcdoc as local URLs. Other internal firefox about: URLs should + // not be treated this way. + nsCOMPtr loadInfo; + nsCOMPtr channel = mDoc->GetChannel(); + if (channel) { + nsCOMPtr uri; + Unused << channel->GetURI(getter_AddRefs(uri)); + + bool ignoreLoadInfo = false; + + // Note, this is mostly copied from NS_IsAboutBlank(). Its duplicated + // here so we can efficiently check about:srcdoc as well. + if (uri->SchemeIs("about")) { + nsCString spec = uri->GetSpecOrDefault(); + ignoreLoadInfo = spec.EqualsLiteral("about:blank") || + spec.EqualsLiteral("about:srcdoc"); + } else { + // Its not an about: URL, so now check for our other URL types. + ignoreLoadInfo = uri->SchemeIs("data") || uri->SchemeIs("blob"); + } + + if (!ignoreLoadInfo) { + loadInfo = channel->LoadInfo(); + } + } + + // Take the initial client source from the docshell immediately. Even if we + // don't end up using it here we should consume it. + UniquePtr initialClientSource; + nsIDocShell* docshell = GetDocShell(); + if (docshell) { + initialClientSource = docshell->TakeInitialClientSource(); + } + + // Try to get the reserved client from the LoadInfo. A Client is + // reserved at the start of the channel load if there is not an + // initial about:blank document that will be reused. It is also + // created if the channel load encounters a cross-origin redirect. + if (loadInfo) { + UniquePtr reservedClient = + loadInfo->TakeReservedClientSource(); + if (reservedClient) { + mClientSource.reset(); + mClientSource = std::move(reservedClient); + newClientSource = true; + } + } + + // We don't have a LoadInfo reserved client, but maybe we should + // be inheriting an initial one from the docshell. This means + // that the docshell started the channel load before creating the + // initial about:blank document. This is an optimization, though, + // and it created an initial Client as a placeholder for the document. + // In this case we want to inherit this placeholder Client here. + if (!mClientSource) { + mClientSource = std::move(initialClientSource); + if (mClientSource) { + newClientSource = true; + } + } + + nsCOMPtr foreignPartitionedPrincipal; + + nsresult rv = StoragePrincipalHelper::GetPrincipal( + this, + StaticPrefs::privacy_partition_serviceWorkers() + ? StoragePrincipalHelper::eForeignPartitionedPrincipal + : StoragePrincipalHelper::eRegularPrincipal, + getter_AddRefs(foreignPartitionedPrincipal)); + NS_ENSURE_SUCCESS(rv, rv); + + // Verify the final ClientSource principal matches the final document + // principal. The ClientChannelHelper handles things like network + // redirects, but there are other ways the document principal can change. + // For example, if something sets the nsIChannel.owner property, then + // the final channel principal can be anything. Unfortunately there is + // no good way to detect this until after the channel completes loading. + // + // For now we handle this just by reseting the ClientSource. This will + // result in a new ClientSource with the correct principal being created. + // To APIs like ServiceWorker and Clients API it will look like there was + // an initial content page created that was then immediately replaced. + // This is pretty close to what we are actually doing. + if (mClientSource) { + auto principalOrErr = mClientSource->Info().GetPrincipal(); + nsCOMPtr clientPrincipal = + principalOrErr.isOk() ? principalOrErr.unwrap() : nullptr; + if (!clientPrincipal || + !clientPrincipal->Equals(foreignPartitionedPrincipal)) { + mClientSource.reset(); + } + } + + // If we don't have a reserved client or an initial client, then create + // one now. This can happen in certain cases where we avoid preallocating + // the client in the docshell. This mainly occurs in situations where + // the principal is not clearly inherited from the parent; e.g. sandboxed + // iframes, window.open(), etc. + // + // We also do this late ClientSource creation if the final document ended + // up with a different principal. + // + // TODO: We may not be marking initial about:blank documents created + // this way as controlled by a service worker properly. The + // controller should be coming from the same place as the inheritted + // principal. We do this in docshell, but as mentioned we aren't + // smart enough to handle all cases yet. For example, a + // window.open() with new URL should inherit the controller from + // the opener, but we probably don't handle that yet. + if (!mClientSource) { + mClientSource = ClientManager::CreateSource( + ClientType::Window, EventTargetFor(TaskCategory::Other), + foreignPartitionedPrincipal); + MOZ_DIAGNOSTIC_ASSERT(mClientSource); + newClientSource = true; + + // Note, we don't apply the loadinfo controller below if we create + // the ClientSource here. + } + + // The load may have started controlling the Client as well. If + // so, mark it as controlled immediately here. The actor may + // or may not have been notified by the parent side about being + // controlled yet. + // + // Note: We should be careful not to control a client that was created late. + // These clients were not seen by the ServiceWorkerManager when it + // marked the LoadInfo controlled and it won't know about them. Its + // also possible we are creating the client late due to the final + // principal changing and these clients should definitely not be + // controlled by a service worker with a different principal. + else if (loadInfo) { + const Maybe controller = loadInfo->GetController(); + if (controller.isSome()) { + mClientSource->SetController(controller.ref()); + } + + // We also have to handle the case where te initial about:blank is + // controlled due to inheritting the service worker from its parent, + // but the actual nsIChannel load is not covered by any service worker. + // In this case we want the final page to be uncontrolled. There is + // an open spec issue about how exactly this should be handled, but for + // now we just force creation of a new ClientSource to clear the + // controller. + // + // https://github.com/w3c/ServiceWorker/issues/1232 + // + else if (mClientSource->GetController().isSome()) { + mClientSource.reset(); + mClientSource = ClientManager::CreateSource( + ClientType::Window, EventTargetFor(TaskCategory::Other), + foreignPartitionedPrincipal); + MOZ_DIAGNOSTIC_ASSERT(mClientSource); + newClientSource = true; + } + } + + if (mClientSource) { + // Generally the CSP is stored within the Client and cached on the document. + // At the time of CSP parsing however, the Client has not been created yet, + // hence we store the CSP on the document and propagate/sync the CSP with + // Client here when we create the Client. + mClientSource->SetCsp(mDoc->GetCsp()); + + DocGroup* docGroup = GetDocGroup(); + MOZ_DIAGNOSTIC_ASSERT(docGroup); + mClientSource->SetAgentClusterId(docGroup->AgentClusterId()); + + if (mWindowGlobalChild) { + mWindowGlobalChild->SendSetClientInfo(mClientSource->Info().ToIPC()); + } + } + + // Its possible that we got a client just after being frozen in + // the bfcache. In that case freeze the client immediately. + if (newClientSource && IsFrozen()) { + mClientSource->Freeze(); + } + + return NS_OK; +} + +nsresult nsGlobalWindowInner::ExecutionReady() { + nsresult rv = EnsureClientSource(); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mClientSource->WindowExecutionReady(this); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +void nsGlobalWindowInner::UpdateParentTarget() { + // NOTE: This method is identical to + // nsGlobalWindowOuter::UpdateParentTarget(). IF YOU UPDATE THIS METHOD, + // UPDATE THE OTHER ONE TOO! + + // Try to get our frame element's tab child global (its in-process message + // manager). If that fails, fall back to the chrome event handler's tab + // child global, and if it doesn't have one, just use the chrome event + // handler itself. + + nsPIDOMWindowOuter* outer = GetOuterWindow(); + if (!outer) { + return; + } + nsCOMPtr frameElement = outer->GetFrameElementInternal(); + nsCOMPtr eventTarget = + nsContentUtils::TryGetBrowserChildGlobal(frameElement); + + if (!eventTarget) { + nsGlobalWindowOuter* topWin = GetInProcessScriptableTopInternal(); + if (topWin) { + frameElement = topWin->GetFrameElementInternal(); + eventTarget = nsContentUtils::TryGetBrowserChildGlobal(frameElement); + } + } + + if (!eventTarget) { + eventTarget = nsContentUtils::TryGetBrowserChildGlobal(mChromeEventHandler); + } + + if (!eventTarget) { + eventTarget = mChromeEventHandler; + } + + mParentTarget = eventTarget; +} + +EventTarget* nsGlobalWindowInner::GetTargetForDOMEvent() { + return GetOuterWindowInternal(); +} + +void nsGlobalWindowInner::GetEventTargetParent(EventChainPreVisitor& aVisitor) { + EventMessage msg = aVisitor.mEvent->mMessage; + + aVisitor.mCanHandle = true; + aVisitor.mForceContentDispatch = true; // FIXME! Bug 329119 + if (msg == eResize && aVisitor.mEvent->IsTrusted()) { + // Checking whether the event target is an inner window or not, so we can + // keep the old behavior also in case a child window is handling resize. + if (aVisitor.mEvent->mOriginalTarget && + aVisitor.mEvent->mOriginalTarget->IsInnerWindow()) { + mIsHandlingResizeEvent = true; + } + } else if (msg == eMouseDown && aVisitor.mEvent->IsTrusted()) { + sMouseDown = true; + } else if ((msg == eMouseUp || msg == eDragEnd) && + aVisitor.mEvent->IsTrusted()) { + sMouseDown = false; + if (sDragServiceDisabled) { + nsCOMPtr ds = + do_GetService("@mozilla.org/widget/dragservice;1"); + if (ds) { + sDragServiceDisabled = false; + ds->Unsuppress(); + } + } + } + + aVisitor.SetParentTarget(GetParentTarget(), true); +} + +void nsGlobalWindowInner::FireFrameLoadEvent() { + // If we're not in a content frame, or are at a BrowsingContext tree boundary, + // such as the content-chrome boundary, don't fire the "load" event. + if (GetBrowsingContext()->IsTopContent() || + GetBrowsingContext()->IsChrome()) { + return; + } + + // If embedder is same-process, fire the event on our embedder element. + // + // XXX: Bug 1440212 is looking into potentially changing this behaviour to act + // more like the remote case when in-process. + RefPtr element = GetBrowsingContext()->GetEmbedderElement(); + if (element) { + nsEventStatus status = nsEventStatus_eIgnore; + WidgetEvent event(/* aIsTrusted = */ true, eLoad); + event.mFlags.mBubbles = false; + event.mFlags.mCancelable = false; + + if (mozilla::dom::DocGroup::TryToLoadIframesInBackground()) { + nsDocShell* ds = nsDocShell::Cast(GetDocShell()); + + if (ds && !ds->HasFakeOnLoadDispatched()) { + EventDispatcher::Dispatch(element, nullptr, &event, nullptr, &status); + } + } else { + // Most of the time we could get a pres context to pass in here, + // but not always (i.e. if this window is not shown there won't + // be a pres context available). Since we're not firing a GUI + // event we don't need a pres context anyway so we just pass + // null as the pres context all the time here. + EventDispatcher::Dispatch(element, nullptr, &event, nullptr, &status); + } + return; + } + + // We don't have an in-process embedder. Try to get our `BrowserChild` actor + // to send a message to that embedder. We want to double-check that our outer + // window is actually the one at the root of this browserChild though, just in + // case. + RefPtr browserChild = + BrowserChild::GetFrom(static_cast(this)); + if (browserChild) { + // Double-check that our outer window is actually at the root of this + // `BrowserChild`, in case we're in an odd maybe-unhosted situation like a + // print preview dialog. + nsCOMPtr rootOuter = + do_GetInterface(browserChild->WebNavigation()); + if (!rootOuter || rootOuter != GetOuterWindow()) { + return; + } + + mozilla::Unused << browserChild->SendMaybeFireEmbedderLoadEvents( + EmbedderElementEventType::LoadEvent); + } +} + +nsresult nsGlobalWindowInner::PostHandleEvent(EventChainPostVisitor& aVisitor) { + // Return early if there is nothing to do. + switch (aVisitor.mEvent->mMessage) { + case eResize: + case eUnload: + case eLoad: + break; + default: + return NS_OK; + } + + /* mChromeEventHandler and mContext go dangling in the middle of this + function under some circumstances (events that destroy the window) + without this addref. */ + RefPtr kungFuDeathGrip1(mChromeEventHandler); + mozilla::Unused + << kungFuDeathGrip1; // These aren't referred to through the function + nsCOMPtr kungFuDeathGrip2(GetContextInternal()); + mozilla::Unused + << kungFuDeathGrip2; // These aren't referred to through the function + + if (aVisitor.mEvent->mMessage == eResize) { + mIsHandlingResizeEvent = false; + } else if (aVisitor.mEvent->mMessage == eUnload && + aVisitor.mEvent->IsTrusted()) { + // If any VR display presentation is active at unload, the next page + // will receive a vrdisplayactive event to indicate that it should + // immediately begin vr presentation. This should occur when navigating + // forwards, navigating backwards, and on page reload. + for (const auto& display : mVRDisplays) { + if (display->IsPresenting()) { + display->StartVRNavigation(); + // Save this VR display ID to trigger vrdisplayactivate event + // after the next load event. + nsGlobalWindowOuter* outer = GetOuterWindowInternal(); + if (outer) { + outer->SetAutoActivateVRDisplayID(display->DisplayId()); + } + + // XXX The WebVR 1.1 spec does not define which of multiple VR + // presenting VR displays will be chosen during navigation. + // As the underlying platform VR API's currently only allow a single + // VR display, it is safe to choose the first VR display for now. + break; + } + } + mIsDocumentLoaded = false; + // Tell the parent process that the document is not loaded. + if (mWindowGlobalChild) { + mWindowGlobalChild->SendUpdateDocumentHasLoaded(mIsDocumentLoaded); + } + } else if (aVisitor.mEvent->mMessage == eLoad && + aVisitor.mEvent->IsTrusted()) { + // This is page load event since load events don't propagate to |window|. + // @see Document::GetEventTargetParent. + mIsDocumentLoaded = true; + // Tell the parent process that the document is loaded. + if (mWindowGlobalChild) { + mWindowGlobalChild->SendUpdateDocumentHasLoaded(mIsDocumentLoaded); + } + + mTimeoutManager->OnDocumentLoaded(); + + MOZ_ASSERT(aVisitor.mEvent->IsTrusted()); + FireFrameLoadEvent(); + + if (mVREventObserver) { + mVREventObserver->NotifyAfterLoad(); + } + + uint32_t autoActivateVRDisplayID = 0; + nsGlobalWindowOuter* outer = GetOuterWindowInternal(); + if (outer) { + autoActivateVRDisplayID = outer->GetAutoActivateVRDisplayID(); + } + if (autoActivateVRDisplayID) { + DispatchVRDisplayActivate(autoActivateVRDisplayID, + VRDisplayEventReason::Navigation); + } + } + + return NS_OK; +} + +nsresult nsGlobalWindowInner::DefineArgumentsProperty(nsIArray* aArguments) { + nsIScriptContext* ctx = GetOuterWindowInternal()->mContext; + NS_ENSURE_TRUE(aArguments && ctx, NS_ERROR_NOT_INITIALIZED); + + JS::Rooted obj(RootingCx(), GetWrapperPreserveColor()); + return ctx->SetProperty(obj, "arguments", aArguments); +} + +//***************************************************************************** +// nsGlobalWindowInner::nsIScriptObjectPrincipal +//***************************************************************************** + +nsIPrincipal* nsGlobalWindowInner::GetPrincipal() { + if (mDoc) { + // If we have a document, get the principal from the document + return mDoc->NodePrincipal(); + } + + if (mDocumentPrincipal) { + return mDocumentPrincipal; + } + + // If we don't have a principal and we don't have a document we + // ask the parent window for the principal. This can happen when + // loading a frameset that has a , in + // that case the global window is used in JS before we've loaded + // a document into the window. + + nsCOMPtr objPrincipal = + do_QueryInterface(GetInProcessParentInternal()); + + if (objPrincipal) { + return objPrincipal->GetPrincipal(); + } + + return nullptr; +} + +nsIPrincipal* nsGlobalWindowInner::GetEffectiveCookiePrincipal() { + if (mDoc) { + // If we have a document, get the principal from the document + return mDoc->EffectiveCookiePrincipal(); + } + + if (mDocumentCookiePrincipal) { + return mDocumentCookiePrincipal; + } + + // If we don't have a cookie principal and we don't have a document we ask + // the parent window for the cookie principal. + + nsCOMPtr objPrincipal = + do_QueryInterface(GetInProcessParentInternal()); + + if (objPrincipal) { + return objPrincipal->GetEffectiveCookiePrincipal(); + } + + return nullptr; +} + +nsIPrincipal* nsGlobalWindowInner::GetEffectiveStoragePrincipal() { + if (mDoc) { + // If we have a document, get the principal from the document + return mDoc->EffectiveStoragePrincipal(); + } + + if (mDocumentStoragePrincipal) { + return mDocumentStoragePrincipal; + } + + // If we don't have a cookie principal and we don't have a document we ask + // the parent window for the cookie principal. + + nsCOMPtr objPrincipal = + do_QueryInterface(GetInProcessParentInternal()); + + if (objPrincipal) { + return objPrincipal->GetEffectiveStoragePrincipal(); + } + + return nullptr; +} + +nsIPrincipal* nsGlobalWindowInner::PartitionedPrincipal() { + if (mDoc) { + // If we have a document, get the principal from the document + return mDoc->PartitionedPrincipal(); + } + + if (mDocumentPartitionedPrincipal) { + return mDocumentPartitionedPrincipal; + } + + // If we don't have a partitioned principal and we don't have a document we + // ask the parent window for the partitioned principal. + + nsCOMPtr objPrincipal = + do_QueryInterface(GetInProcessParentInternal()); + + if (objPrincipal) { + return objPrincipal->PartitionedPrincipal(); + } + + return nullptr; +} + +//***************************************************************************** +// nsGlobalWindowInner::nsIDOMWindow +//***************************************************************************** + +bool nsPIDOMWindowInner::AddAudioContext(AudioContext* aAudioContext) { + mAudioContexts.AppendElement(aAudioContext); + + // Return true if the context should be muted and false if not. + nsIDocShell* docShell = GetDocShell(); + return docShell && !docShell->GetAllowMedia() && !aAudioContext->IsOffline(); +} + +void nsPIDOMWindowInner::RemoveAudioContext(AudioContext* aAudioContext) { + mAudioContexts.RemoveElement(aAudioContext); +} + +void nsPIDOMWindowInner::MuteAudioContexts() { + for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) { + if (!mAudioContexts[i]->IsOffline()) { + mAudioContexts[i]->Mute(); + } + } +} + +void nsPIDOMWindowInner::UnmuteAudioContexts() { + for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) { + if (!mAudioContexts[i]->IsOffline()) { + mAudioContexts[i]->Unmute(); + } + } +} + +WindowProxyHolder nsGlobalWindowInner::Window() { + return WindowProxyHolder(GetBrowsingContext()); +} + +Navigator* nsPIDOMWindowInner::Navigator() { + if (!mNavigator) { + mNavigator = new mozilla::dom::Navigator(this); + } + + return mNavigator; +} + +MediaDevices* nsPIDOMWindowInner::GetExtantMediaDevices() const { + return mNavigator ? mNavigator->GetExtantMediaDevices() : nullptr; +} + +VisualViewport* nsGlobalWindowInner::VisualViewport() { + if (!mVisualViewport) { + mVisualViewport = new mozilla::dom::VisualViewport(this); + } + + return mVisualViewport; +} + +nsScreen* nsGlobalWindowInner::GetScreen(ErrorResult& aError) { + if (!mScreen) { + mScreen = nsScreen::Create(this); + if (!mScreen) { + aError.Throw(NS_ERROR_UNEXPECTED); + return nullptr; + } + } + + return mScreen; +} + +nsHistory* nsGlobalWindowInner::GetHistory(ErrorResult& aError) { + if (!mHistory) { + mHistory = new nsHistory(this); + } + + return mHistory; +} + +CustomElementRegistry* nsGlobalWindowInner::CustomElements() { + if (!mCustomElements) { + mCustomElements = new CustomElementRegistry(this); + } + + return mCustomElements; +} + +CustomElementRegistry* nsGlobalWindowInner::GetExistingCustomElements() { + return mCustomElements; +} + +Performance* nsPIDOMWindowInner::GetPerformance() { + CreatePerformanceObjectIfNeeded(); + return mPerformance; +} + +void nsPIDOMWindowInner::QueuePerformanceNavigationTiming() { + CreatePerformanceObjectIfNeeded(); + if (mPerformance) { + mPerformance->QueueNavigationTimingEntry(); + } +} + +void nsPIDOMWindowInner::CreatePerformanceObjectIfNeeded() { + if (mPerformance || !mDoc) { + return; + } + RefPtr timing = mDoc->GetNavigationTiming(); + nsCOMPtr timedChannel(do_QueryInterface(mDoc->GetChannel())); + bool timingEnabled = false; + if (!timedChannel || + !NS_SUCCEEDED(timedChannel->GetTimingEnabled(&timingEnabled)) || + !timingEnabled) { + timedChannel = nullptr; + } + if (timing) { + mPerformance = Performance::CreateForMainThread(this, mDoc->NodePrincipal(), + timing, timedChannel); + } +} + +bool nsPIDOMWindowInner::IsSecureContext() const { + return nsGlobalWindowInner::Cast(this)->IsSecureContext(); +} + +void nsPIDOMWindowInner::Suspend(bool aIncludeSubWindows) { + nsGlobalWindowInner::Cast(this)->Suspend(aIncludeSubWindows); +} + +void nsPIDOMWindowInner::Resume(bool aIncludeSubWindows) { + nsGlobalWindowInner::Cast(this)->Resume(aIncludeSubWindows); +} + +void nsPIDOMWindowInner::SyncStateFromParentWindow() { + nsGlobalWindowInner::Cast(this)->SyncStateFromParentWindow(); +} + +Maybe nsPIDOMWindowInner::GetClientInfo() const { + return nsGlobalWindowInner::Cast(this)->GetClientInfo(); +} + +Maybe nsPIDOMWindowInner::GetClientState() const { + return nsGlobalWindowInner::Cast(this)->GetClientState(); +} + +Maybe nsPIDOMWindowInner::GetController() const { + return nsGlobalWindowInner::Cast(this)->GetController(); +} + +void nsPIDOMWindowInner::SetCsp(nsIContentSecurityPolicy* aCsp) { + return nsGlobalWindowInner::Cast(this)->SetCsp(aCsp); +} + +void nsPIDOMWindowInner::SetPreloadCsp(nsIContentSecurityPolicy* aPreloadCsp) { + return nsGlobalWindowInner::Cast(this)->SetPreloadCsp(aPreloadCsp); +} + +nsIContentSecurityPolicy* nsPIDOMWindowInner::GetCsp() { + return nsGlobalWindowInner::Cast(this)->GetCsp(); +} + +void nsPIDOMWindowInner::NoteCalledRegisterForServiceWorkerScope( + const nsACString& aScope) { + nsGlobalWindowInner::Cast(this)->NoteCalledRegisterForServiceWorkerScope( + aScope); +} + +void nsPIDOMWindowInner::NoteDOMContentLoaded() { + nsGlobalWindowInner::Cast(this)->NoteDOMContentLoaded(); +} + +bool nsGlobalWindowInner::ShouldReportForServiceWorkerScope( + const nsAString& aScope) { + bool result = false; + + nsPIDOMWindowOuter* topOuter = GetInProcessScriptableTop(); + NS_ENSURE_TRUE(topOuter, false); + + nsGlobalWindowInner* topInner = + nsGlobalWindowInner::Cast(topOuter->GetCurrentInnerWindow()); + NS_ENSURE_TRUE(topInner, false); + + topInner->ShouldReportForServiceWorkerScopeInternal( + NS_ConvertUTF16toUTF8(aScope), &result); + return result; +} + +InstallTriggerImpl* nsGlobalWindowInner::GetInstallTrigger() { + if (!mInstallTrigger && + !StaticPrefs::extensions_InstallTriggerImpl_enabled()) { + // Return nullptr when InstallTriggerImpl is disabled by pref, + // which does not yet break the "typeof InstallTrigger !== 'undefined" + // "UA detection" use case, but prevents access to the InstallTriggerImpl + // methods and properties. + // + // NOTE: a separate pref ("extensions.InstallTrigger.enabled"), associated + // to this property using the [Pref] extended attribute in Window.webidl, + // does instead hide the entire InstallTrigger property. + // + // See Bug 1754441 for more details about this deprecation. + return nullptr; + } + if (!mInstallTrigger) { + ErrorResult rv; + mInstallTrigger = ConstructJSImplementation( + "@mozilla.org/addons/installtrigger;1", this, rv); + if (rv.Failed()) { + rv.SuppressException(); + return nullptr; + } + } + + return mInstallTrigger; +} + +nsIDOMWindowUtils* nsGlobalWindowInner::GetWindowUtils(ErrorResult& aRv) { + FORWARD_TO_OUTER_OR_THROW(WindowUtils, (), aRv, nullptr); +} + +CallState nsGlobalWindowInner::ShouldReportForServiceWorkerScopeInternal( + const nsACString& aScope, bool* aResultOut) { + MOZ_DIAGNOSTIC_ASSERT(aResultOut); + + // First check to see if this window is controlled. If so, then we have + // found a match and are done. + const Maybe swd = GetController(); + if (swd.isSome() && swd.ref().Scope() == aScope) { + *aResultOut = true; + return CallState::Stop; + } + + // Next, check to see if this window has called + // navigator.serviceWorker.register() for this scope. If so, then treat this + // as a match so console reports appear in the devtools console. + if (mClientSource && + mClientSource->CalledRegisterForServiceWorkerScope(aScope)) { + *aResultOut = true; + return CallState::Stop; + } + + // Finally check the current docshell nsILoadGroup to see if there are any + // outstanding navigation requests. If so, match the scope against the + // channel's URL. We want to show console reports during the FetchEvent + // intercepting the navigation itself. + nsCOMPtr loader(do_QueryInterface(GetDocShell())); + if (loader) { + nsCOMPtr loadgroup; + Unused << loader->GetLoadGroup(getter_AddRefs(loadgroup)); + if (loadgroup) { + nsCOMPtr iter; + Unused << loadgroup->GetRequests(getter_AddRefs(iter)); + if (iter) { + nsCOMPtr tmp; + bool hasMore = true; + // Check each network request in the load group. + while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) { + iter->GetNext(getter_AddRefs(tmp)); + nsCOMPtr loadingChannel(do_QueryInterface(tmp)); + // Ignore subresource requests. Logging for a subresource + // FetchEvent should be handled above since the client is + // already controlled. + if (!loadingChannel || + !nsContentUtils::IsNonSubresourceRequest(loadingChannel)) { + continue; + } + nsCOMPtr loadingURL; + Unused << loadingChannel->GetURI(getter_AddRefs(loadingURL)); + if (!loadingURL) { + continue; + } + nsAutoCString loadingSpec; + Unused << loadingURL->GetSpec(loadingSpec); + // Perform a simple substring comparison to match the scope + // against the channel URL. + if (StringBeginsWith(loadingSpec, aScope)) { + *aResultOut = true; + return CallState::Stop; + } + } + } + } + } + + // The current window doesn't care about this service worker, but maybe + // one of our child frames does. + return CallOnInProcessChildren( + &nsGlobalWindowInner::ShouldReportForServiceWorkerScopeInternal, aScope, + aResultOut); +} + +void nsGlobalWindowInner::NoteCalledRegisterForServiceWorkerScope( + const nsACString& aScope) { + if (!mClientSource) { + return; + } + + mClientSource->NoteCalledRegisterForServiceWorkerScope(aScope); +} + +void nsGlobalWindowInner::NoteDOMContentLoaded() { + if (!mClientSource) { + return; + } + + mClientSource->NoteDOMContentLoaded(); +} + +void nsGlobalWindowInner::UpdateTopInnerWindow() { + if (IsTopInnerWindow() || !mTopInnerWindow) { + return; + } + + mTopInnerWindow->UpdateWebSocketCount(-(int32_t)mNumOfOpenWebSockets); +} + +bool nsGlobalWindowInner::IsInSyncOperation() { + return GetExtantDoc() && GetExtantDoc()->IsInSyncOperation(); +} + +bool nsGlobalWindowInner::IsSharedMemoryAllowedInternal( + nsIPrincipal* aPrincipal) const { + MOZ_ASSERT(NS_IsMainThread()); + + if (StaticPrefs:: + dom_postMessage_sharedArrayBuffer_bypassCOOP_COEP_insecure_enabled()) { + return true; + } + + if (ExtensionPolicyService::GetSingleton().IsExtensionProcess()) { + if (auto* basePrincipal = BasePrincipal::Cast(aPrincipal)) { + if (auto* policy = basePrincipal->AddonPolicy()) { + return policy->IsPrivileged(); + } + } + } + + return CrossOriginIsolated(); +} + +bool nsGlobalWindowInner::CrossOriginIsolated() const { + MOZ_ASSERT(NS_IsMainThread()); + + RefPtr bc = GetBrowsingContext(); + MOZ_DIAGNOSTIC_ASSERT(bc); + return bc->CrossOriginIsolated(); +} + +WindowContext* TopWindowContext(nsPIDOMWindowInner& aWindow) { + WindowContext* wc = aWindow.GetWindowContext(); + if (!wc) { + return nullptr; + } + + return wc->TopWindowContext(); +} + +void nsPIDOMWindowInner::AddPeerConnection() { + MOZ_ASSERT(NS_IsMainThread()); + ++mActivePeerConnections; + if (mActivePeerConnections == 1 && mWindowGlobalChild) { + mWindowGlobalChild->SendUpdateActivePeerConnectionStatus( + /*aIsAdded*/ true); + + // We need to present having active peer connections immediately. If we need + // to wait for the parent process to come back with this information we + // might start throttling. + if (WindowContext* top = TopWindowContext(*this)) { + top->TransientSetHasActivePeerConnections(); + } + } +} + +void nsPIDOMWindowInner::RemovePeerConnection() { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mActivePeerConnections > 0); + --mActivePeerConnections; + if (mActivePeerConnections == 0 && mWindowGlobalChild) { + mWindowGlobalChild->SendUpdateActivePeerConnectionStatus( + /*aIsAdded*/ false); + } +} + +bool nsPIDOMWindowInner::HasActivePeerConnections() { + MOZ_ASSERT(NS_IsMainThread()); + + WindowContext* topWindowContext = TopWindowContext(*this); + return topWindowContext && topWindowContext->GetHasActivePeerConnections(); +} + +void nsPIDOMWindowInner::AddMediaKeysInstance(MediaKeys* aMediaKeys) { + MOZ_ASSERT(NS_IsMainThread()); + mMediaKeysInstances.AppendElement(aMediaKeys); + if (mWindowGlobalChild && mMediaKeysInstances.Length() == 1) { + mWindowGlobalChild->BlockBFCacheFor(BFCacheStatus::CONTAINS_EME_CONTENT); + } +} + +void nsPIDOMWindowInner::RemoveMediaKeysInstance(MediaKeys* aMediaKeys) { + MOZ_ASSERT(NS_IsMainThread()); + mMediaKeysInstances.RemoveElement(aMediaKeys); + if (mWindowGlobalChild && mMediaKeysInstances.IsEmpty()) { + mWindowGlobalChild->UnblockBFCacheFor(BFCacheStatus::CONTAINS_EME_CONTENT); + } +} + +bool nsPIDOMWindowInner::HasActiveMediaKeysInstance() { + MOZ_ASSERT(NS_IsMainThread()); + return !mMediaKeysInstances.IsEmpty(); +} + +bool nsPIDOMWindowInner::IsPlayingAudio() { + for (uint32_t i = 0; i < mAudioContexts.Length(); i++) { + if (mAudioContexts[i]->IsRunning()) { + return true; + } + } + RefPtr acs = AudioChannelService::Get(); + if (!acs) { + return false; + } + auto outer = GetOuterWindow(); + if (!outer) { + // We've been unlinked and are about to die. Not a good time to pretend to + // be playing audio. + return false; + } + return acs->IsWindowActive(outer); +} + +bool nsPIDOMWindowInner::IsDocumentLoaded() const { return mIsDocumentLoaded; } + +mozilla::dom::TimeoutManager& nsPIDOMWindowInner::TimeoutManager() { + return *mTimeoutManager; +} + +bool nsPIDOMWindowInner::IsRunningTimeout() { + return TimeoutManager().IsRunningTimeout(); +} + +void nsPIDOMWindowInner::TryToCacheTopInnerWindow() { + if (mHasTriedToCacheTopInnerWindow) { + return; + } + + nsGlobalWindowInner* window = nsGlobalWindowInner::Cast(this); + + MOZ_ASSERT(!window->IsDying()); + + mHasTriedToCacheTopInnerWindow = true; + + MOZ_ASSERT(window); + + if (nsCOMPtr topOutter = + window->GetInProcessScriptableTop()) { + mTopInnerWindow = topOutter->GetCurrentInnerWindow(); + } +} + +void nsPIDOMWindowInner::UpdateActiveIndexedDBDatabaseCount(int32_t aDelta) { + MOZ_ASSERT(NS_IsMainThread()); + + if (aDelta == 0) { + return; + } + + // We count databases but not transactions because only active databases + // could block throttling. + uint32_t& counter = mTopInnerWindow + ? mTopInnerWindow->mNumOfIndexedDBDatabases + : mNumOfIndexedDBDatabases; + + counter += aDelta; +} + +bool nsPIDOMWindowInner::HasActiveIndexedDBDatabases() { + MOZ_ASSERT(NS_IsMainThread()); + + return mTopInnerWindow ? mTopInnerWindow->mNumOfIndexedDBDatabases > 0 + : mNumOfIndexedDBDatabases > 0; +} + +void nsPIDOMWindowInner::UpdateWebSocketCount(int32_t aDelta) { + MOZ_ASSERT(NS_IsMainThread()); + + if (aDelta == 0) { + return; + } + + if (mTopInnerWindow && !IsTopInnerWindow()) { + mTopInnerWindow->UpdateWebSocketCount(aDelta); + } + + MOZ_DIAGNOSTIC_ASSERT( + aDelta > 0 || ((aDelta + mNumOfOpenWebSockets) < mNumOfOpenWebSockets)); + + mNumOfOpenWebSockets += aDelta; +} + +bool nsPIDOMWindowInner::HasOpenWebSockets() const { + MOZ_ASSERT(NS_IsMainThread()); + + return mNumOfOpenWebSockets || + (mTopInnerWindow && mTopInnerWindow->mNumOfOpenWebSockets); +} + +bool nsPIDOMWindowInner::IsCurrentInnerWindow() const { + if (mozilla::SessionHistoryInParent() && mBrowsingContext && + mBrowsingContext->IsInBFCache()) { + return false; + } + + if (!mBrowsingContext || mBrowsingContext->IsDiscarded()) { + // If our BrowsingContext has been discarded, we consider ourselves + // still-current if we were current at the time it was discarded. + return mOuterWindow && WasCurrentInnerWindow(); + } + + nsPIDOMWindowOuter* outer = mBrowsingContext->GetDOMWindow(); + return outer && outer->GetCurrentInnerWindow() == this; +} + +bool nsPIDOMWindowInner::IsFullyActive() const { + WindowContext* wc = GetWindowContext(); + if (!wc || wc->IsDiscarded() || !wc->IsCurrent()) { + return false; + } + return GetBrowsingContext()->AncestorsAreCurrent(); +} + +void nsPIDOMWindowInner::SetAudioCapture(bool aCapture) { + RefPtr service = AudioChannelService::GetOrCreate(); + if (service) { + service->SetWindowAudioCaptured(GetOuterWindow(), mWindowID, aCapture); + } +} + +void nsGlobalWindowInner::SetActiveLoadingState(bool aIsLoading) { + MOZ_LOG( + gTimeoutLog, mozilla::LogLevel::Debug, + ("SetActiveLoadingState innerwindow %p: %d", (void*)this, aIsLoading)); + if (GetBrowsingContext()) { + // Setting loading on a discarded context has no effect. + Unused << GetBrowsingContext()->SetLoading(aIsLoading); + } + + if (!nsGlobalWindowInner::Cast(this)->IsChromeWindow()) { + mTimeoutManager->SetLoading(aIsLoading); + } + + HintIsLoading(aIsLoading); +} + +void nsGlobalWindowInner::HintIsLoading(bool aIsLoading) { + // Hint to tell the JS GC to use modified triggers during pageload. + if (mHintedWasLoading != aIsLoading) { + using namespace js::gc; + SetPerformanceHint(danger::GetJSContext(), aIsLoading + ? PerformanceHint::InPageLoad + : PerformanceHint::Normal); + mHintedWasLoading = aIsLoading; + } +} + +// nsISpeechSynthesisGetter + +#ifdef MOZ_WEBSPEECH +SpeechSynthesis* nsGlobalWindowInner::GetSpeechSynthesis(ErrorResult& aError) { + if (!mSpeechSynthesis) { + mSpeechSynthesis = new SpeechSynthesis(this); + } + + return mSpeechSynthesis; +} + +bool nsGlobalWindowInner::HasActiveSpeechSynthesis() { + if (mSpeechSynthesis) { + return !mSpeechSynthesis->HasEmptyQueue(); + } + + return false; +} + +#endif + +mozilla::glean::Glean* nsGlobalWindowInner::Glean() { + if (!mGlean) { + mGlean = new mozilla::glean::Glean(); + } + + return mGlean; +} + +mozilla::glean::GleanPings* nsGlobalWindowInner::GleanPings() { + if (!mGleanPings) { + mGleanPings = new mozilla::glean::GleanPings(); + } + + return mGleanPings; +} + +Nullable nsGlobalWindowInner::GetParent( + ErrorResult& aError) { + FORWARD_TO_OUTER_OR_THROW(GetParentOuter, (), aError, nullptr); +} + +/** + * GetInProcessScriptableParent used to be called when a script read + * window.parent. Under Fission, that is now handled by + * BrowsingContext::GetParent, and the result is a WindowProxyHolder rather than + * an actual global window. This method still exists for legacy callers which + * relied on the old logic, and require in-process windows. However, it only + * works correctly when no out-of-process frames exist between this window and + * the top-level window, so it should not be used in new code. + * + * In contrast to GetRealParent, GetInProcessScriptableParent respects