/* -*- 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 <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cstdint>
#include <
new>
#include <type_traits>
#include <utility>
#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/CompilationAndEvaluation.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/BounceTrackingStorageObserver.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/ScrollContainerFrame.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/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/Credential.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/CloseWatcher.h"
#include "mozilla/dom/CloseWatcherManager.h"
#include "mozilla/dom/Console.h"
#include "mozilla/dom/CookieStore.h"
#include "mozilla/dom/ContentChild.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/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/JSExecutionUtils.h" // mozilla::dom::Compile, mozilla::dom::EvaluationExceptionToNSResult
#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/Navigation.h"
#include "mozilla/dom/NavigatorBinding.h"
#include "mozilla/dom/Nullable.h"
#include "mozilla/dom/PartitionedLocalStorage.h"
#include "mozilla/dom/Performance.h"
#include "mozilla/dom/PerformanceMainThread.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/TrustedTypePolicyFactory.h"
#include "mozilla/dom/TrustedTypeUtils.h"
#include "mozilla/dom/TrustedTypesConstants.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/WindowOrWorkerGlobalScopeBinding.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 "nsIClipboard.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 "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 "mozilla/ThrottledEventQueue.h"
#include "nsIDOMXULControlElement.h"
#ifdef NS_PRINTING
# include
"nsIPrintSettings.h"
#endif
#ifdef MOZ_WEBSPEECH
# include
"mozilla/dom/SpeechSynthesis.h"
#endif
#ifdef ANDROID
# include <android/log.h>
#endif
#ifdef XP_WIN
# include
"mozilla/Debug.h"
# include <process.h>
# define getpid _getpid
#else
# include <unistd.h>
// 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<nsGlobalWindowOuter> 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<nsGlobalWindowOuter> 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<nsGlobalWindowOuter> 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->SerialEventTarget() : 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<IdleRequestExecutor> 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<nsGlobalWindowInner> 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<TimeoutHandler> 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<int32_t> 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<nsGlobalWindowInner> window(mWindow);
window->ExecuteIdleRequest(mDeadline);
}
return NS_OK;
}
nsresult IdleRequestExecutor::Cancel() {
MOZ_ASSERT(NS_IsMainThread());
if (mDelayedExecutorHandle && mWindow) {
mWindow->GetTimeoutManager()->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<uint32_t>(delay.ToMilliseconds()));
}
void IdleRequestExecutor::ScheduleDispatch() {
MOZ_ASSERT(mWindow);
mDelayedExecutorHandle = Nothing();
RefPtr<IdleRequestExecutor> 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->GetTimeoutManager()->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<IdleRequest> request(aRequest);
RemoveIdleCallback(request);
request->IdleRun(
this, aDeadline, aDidTimeout);
}
void nsGlobalWindowInner::ExecuteIdleRequest(TimeStamp aDeadline) {
AssertIsOnMainThread();
RefPtr<IdleRequest> 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<nsGlobalWindowInner> window(nsGlobalWindowInner::Cast(mWindow));
RefPtr<IdleRequest> request(mIdleRequest);
window->RunIdleRequest(request, 0.0,
true);
return true;
}
private:
~IdleRequestTimeoutHandler() override =
default;
RefPtr<IdleRequest> mIdleRequest;
nsCOMPtr<nsPIDOMWindowInner> 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<IdleRequest> request =
new IdleRequest(&aCallback, handle);
if (aOptions.mTimeout.WasPassed()) {
int32_t timeoutHandle;
RefPtr<TimeoutHandler> 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<IdleRequest> 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<JS::Value> 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<Promise> mPromise;
RefPtr<PromiseDocumentFlushedCallback> 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();
SetIsOnMainThread();
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<dom::TimeoutManager>(
*
this, StaticPrefs::dom_timeout_max_idle_defer_ms(),
static_cast<nsISerialEventTarget*>(nsPIDOMWindowInner::From(
this)
->GetBrowsingContextGroup()
->GetTimerEventQueue()));
mObserver =
new nsGlobalWindowObserver(
this);
if (nsCOMPtr<nsIObserverService> 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<StorageNotifierService> sns = StorageNotifierService::GetOrCreate();
if (sns) {
sns->
Register(mObserver);
}
if (XRE_IsContentProcess()) {
nsCOMPtr<nsIDocShell> 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<
void*>(ToCanonicalSupports(
this)), getpid(), mSerial,
static_cast<
void*>(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<nsFrameMessageManager*>(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<
void*>(ToCanonicalSupports(
this)), getpid(), mSerial,
static_cast<
void*>(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<nsIDeviceSensors> 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.
if (
auto* reporter = nsWindowMemoryReporter::Get()) {
reporter->ObserveDOMWindowDetached(
this);
}
// Kill all of the workers for this window.
CancelWorkersForWindow(*
this);
for (RefPtr<mozilla::dom::SharedWorker> 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;
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<nsIObserverService> 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<StorageNotifierService> 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;
mCookieStore = nullptr;
mPaintWorklet = nullptr;
mExternal = nullptr;
mInstallTrigger = nullptr;
if (mLocalStorage) {
mLocalStorage->Disconnect();
mLocalStorage = nullptr;
}
mSessionStorage = nullptr;
if (mPerformance) {
// Since window is dying, nothing is going to be painted
// with meaningful sizes, so these temp data for LCP is
// no longer needed.
static_cast<PerformanceMainThread*>(mPerformance.get())
->ClearGeneratedTempDataForLCP();
}
mPerformance = nullptr;
mContentMediaController = nullptr;
if (mWebTaskScheduler) {
mWebTaskScheduler->Disconnect();
mWebTaskScheduler = nullptr;
}
mTrustedTypePolicyFactory = 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(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)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTrustedTypePolicyFactory)
#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(mNavigation)
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(mCloseWatcherManager)
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(mCookieStore)
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)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCurrentPasteDataTransfer)
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)
}
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTrustedTypePolicyFactory)
#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(mNavigation)
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)
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(mCookieStore)
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)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCurrentPasteDataTransfer)
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<nsFrameMessageManager*>(
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)
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<mozilla::ipc::PrincipalInfo, nsresult>
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();
}
// https://html.spec.whatwg.org/multipage/web-messaging.html#eligible-for-messaging
// * a Window object whose associated Document is fully active
bool nsGlobalWindowInner::IsEligibleForMessaging() {
return IsFullyActive(); }
void nsGlobalWindowInner::ReportToConsole(
uint32_t aErrorFlags,
const nsCString& aCategory,
nsContentUtils::PropertiesFile aFile,
const nsCString& aMessageName,
const nsTArray<nsString>& aParams,
const mozilla::SourceLocation& aLocation) {
nsContentUtils::ReportToConsole(aErrorFlags, aCategory, mDoc, aFile,
aMessageName.get(), aParams, aLocation);
}
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<nsIPermissionManager> 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<nsIPrincipal> principal = GetPrincipal();
RefPtr<WindowContext> 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<PermissionDelegateHandler> 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<nsILoadInfo> loadInfo;
nsCOMPtr<nsIChannel> channel = mDoc->GetChannel();
if (channel) {
nsCOMPtr<nsIURI> uri;
Unused << channel->GetURI(getter_AddRefs(uri));
bool ignoreLoadInfo =
false;
if (uri->SchemeIs(
"about")) {
ignoreLoadInfo =
NS_IsAboutBlankAllowQueryAndFragment(uri) || NS_IsAboutSrcdoc(uri);
}
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<ClientSource> 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<ClientSource> 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<nsIPrincipal> 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<nsIPrincipal> 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.
//
--> --------------------
--> maximum size reached
--> --------------------