/* -*- 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/U2F.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/WakeLock.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 "nsDOMOfflineResourceList.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 (mApplicationCache) { static_cast(mApplicationCache.get()) ->Disconnect(); mApplicationCache = 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); DisconnectEventTargetObjects(); #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(mApplicationCache) 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(mU2F) 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->mApplicationCache) { static_cast(tmp->mApplicationCache.get()) ->Disconnect(); NS_IMPL_CYCLE_COLLECTION_UNLINK(mApplicationCache) } 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(mU2F) 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