summaryrefslogtreecommitdiffstats
path: root/dom/base/nsGlobalWindowInner.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /dom/base/nsGlobalWindowInner.cpp
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/base/nsGlobalWindowInner.cpp')
-rw-r--r--dom/base/nsGlobalWindowInner.cpp7728
1 files changed, 7728 insertions, 0 deletions
diff --git a/dom/base/nsGlobalWindowInner.cpp b/dom/base/nsGlobalWindowInner.cpp
new file mode 100644
index 0000000000..8b69389790
--- /dev/null
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -0,0 +1,7728 @@
+/* -*- 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/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/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/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/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/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/VRDisplay.h"
+#include "mozilla/dom/VRDisplayEvent.h"
+#include "mozilla/dom/VRDisplayEventBinding.h"
+#include "mozilla/dom/VREventObserver.h"
+#include "mozilla/dom/VisualViewport.h"
+#include "mozilla/dom/WebIDLGlobalNameHash.h"
+#include "mozilla/dom/WindowBinding.h"
+#include "mozilla/dom/WindowContext.h"
+#include "mozilla/dom/WindowGlobalChild.h"
+#include "mozilla/dom/WindowProxyHolder.h"
+#include "mozilla/dom/WorkerCommon.h"
+#include "mozilla/dom/Worklet.h"
+#include "mozilla/dom/XRPermissionRequest.h"
+#include "mozilla/dom/cache/CacheStorage.h"
+#include "mozilla/dom/cache/Types.h"
+#include "mozilla/glean/bindings/Glean.h"
+#include "mozilla/glean/bindings/GleanPings.h"
+#include "mozilla/extensions/WebExtensionPolicy.h"
+#include "mozilla/fallible.h"
+#include "mozilla/gfx/BasePoint.h"
+#include "mozilla/gfx/BaseRect.h"
+#include "mozilla/gfx/BaseSize.h"
+#include "mozilla/gfx/Rect.h"
+#include "mozilla/gfx/Types.h"
+#include "mozilla/intl/LocaleService.h"
+#include "mozilla/ipc/BackgroundUtils.h"
+#include "mozilla/ipc/PBackgroundSharedTypes.h"
+#include "mozilla/net/CookieJarSettings.h"
+#include "nsAtom.h"
+#include "nsBaseHashtable.h"
+#include "nsCCUncollectableMarker.h"
+#include "nsCOMPtr.h"
+#include "nsCRT.h"
+#include "nsCRTGlue.h"
+#include "nsCanvasFrame.h"
+#include "nsCharTraits.h"
+#include "nsCheapSets.h"
+#include "nsContentUtils.h"
+#include "nsCoord.h"
+#include "nsCycleCollectionNoteChild.h"
+#include "nsCycleCollectionTraversalCallback.h"
+#include "nsDOMNavigationTiming.h"
+#include "nsDebug.h"
+#include "nsDeviceContext.h"
+#include "nsDocShell.h"
+#include "nsFocusManager.h"
+#include "nsFrameMessageManager.h"
+#include "nsGkAtoms.h"
+#include "nsGlobalWindowOuter.h"
+#include "nsHashKeys.h"
+#include "nsHistory.h"
+#include "nsIAddonPolicyService.h"
+#include "nsIArray.h"
+#include "nsIBaseWindow.h"
+#include "nsIBrowserChild.h"
+#include "nsICancelableRunnable.h"
+#include "nsIChannel.h"
+#include "nsIContentSecurityPolicy.h"
+#include "nsIControllers.h"
+#include "nsICookieJarSettings.h"
+#include "nsICookieService.h"
+#include "nsID.h"
+#include "nsIDOMStorageManager.h"
+#include "nsIDeviceSensors.h"
+#include "nsIDocShell.h"
+#include "nsIDocShellTreeItem.h"
+#include "nsIDocShellTreeOwner.h"
+#include "nsIDocumentLoader.h"
+#include "nsIDragService.h"
+#include "nsIFocusManager.h"
+#include "nsIFrame.h"
+#include "nsIGlobalObject.h"
+#include "nsIIOService.h"
+#include "nsIIdleRunnable.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsILoadContext.h"
+#include "nsILoadGroup.h"
+#include "nsILoadInfo.h"
+#include "nsINamed.h"
+#include "nsINode.h"
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "nsIPermission.h"
+#include "nsIPermissionManager.h"
+#include "nsIPrefBranch.h"
+#include "nsIPrincipal.h"
+#include "nsIPrompt.h"
+#include "nsIRunnable.h"
+#include "nsIScreen.h"
+#include "nsIScreenManager.h"
+#include "nsIScriptContext.h"
+#include "nsIScriptGlobalObject.h"
+#include "nsIScriptObjectPrincipal.h"
+#include "nsIScrollableFrame.h"
+#include "nsISerialEventTarget.h"
+#include "nsISimpleEnumerator.h"
+#include "nsISizeOfEventTarget.h"
+#include "nsISlowScriptDebug.h"
+#include "nsISupportsUtils.h"
+#include "nsIThread.h"
+#include "nsITimedChannel.h"
+#include "nsIURI.h"
+#include "nsIWeakReference.h"
+#include "nsIWebBrowserChrome.h"
+#include "nsIWebNavigation.h"
+#include "nsIWebProgressListener.h"
+#include "nsIWidget.h"
+#include "nsIWidgetListener.h"
+#include "nsIXULRuntime.h"
+#include "nsJSPrincipals.h"
+#include "nsJSUtils.h"
+#include "nsLayoutStatics.h"
+#include "nsLiteralString.h"
+#include "nsNetUtil.h"
+#include "nsPIDOMWindow.h"
+#include "nsPIDOMWindowInlines.h"
+#include "nsPIWindowRoot.h"
+#include "nsPoint.h"
+#include "nsPresContext.h"
+#include "nsQueryObject.h"
+#include "nsSandboxFlags.h"
+#include "nsScreen.h"
+#include "nsServiceManagerUtils.h"
+#include "nsString.h"
+#include "nsStringFlags.h"
+#include "nsStringFwd.h"
+#include "nsTArray.h"
+#include "nsTLiteralString.h"
+#include "nsTObserverArray.h"
+#include "nsTStringRepr.h"
+#include "nsThreadUtils.h"
+#include "nsWeakReference.h"
+#include "nsWindowMemoryReporter.h"
+#include "nsWindowSizes.h"
+#include "nsWrapperCache.h"
+#include "nsWrapperCacheInlines.h"
+#include "nsXULAppAPI.h"
+#include "nsrootidl.h"
+#include "prclist.h"
+#include "prtypes.h"
+#include "xpcprivate.h"
+#include "xpcpublic.h"
+
+#include "nsIDOMXULControlElement.h"
+
+#ifdef NS_PRINTING
+# include "nsIPrintSettings.h"
+#endif
+
+#ifdef MOZ_WEBSPEECH
+# include "mozilla/dom/SpeechSynthesis.h"
+#endif
+
+#ifdef ANDROID
+# include <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->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<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->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<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());
+
+ 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.
+ NotifyDOMWindowDestroyed(this);
+ 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;
+
+ 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;
+ }
+
+ 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)
+
+#ifdef MOZ_WEBSPEECH
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSpeechSynthesis)
+#endif
+
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlean)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGleanPings)
+
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOuterWindow)
+
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTopInnerWindow)
+
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager)
+
+ if (tmp->mTimeoutManager) {
+ tmp->mTimeoutManager->ForEachUnorderedTimeout([&cb](Timeout* timeout) {
+ cb.NoteNativeChild(timeout, NS_CYCLE_COLLECTION_PARTICIPANT(Timeout));
+ });
+ }
+
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocation)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHistory)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCustomElements)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSharedWorkers)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStorage)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSessionStorage)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndexedDB)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPrincipal)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentCookiePrincipal)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentStoragePrincipal)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPartitionedPrincipal)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentCsp)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowserChild)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDoc)
+
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleRequestExecutor)
+ for (IdleRequest* request : tmp->mIdleRequestCallbacks) {
+ cb.NoteNativeChild(request, NS_CYCLE_COLLECTION_PARTICIPANT(IdleRequest));
+ }
+
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mClientSource)
+
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGamepads)
+
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCacheStorage)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVRDisplays)
+
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDebuggerNotificationManager)
+
+ // Traverse stuff from nsPIDOMWindow
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeEventHandler)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentTarget)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFocusedElement)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowsingContext)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindowGlobalChild)
+
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMenubar)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mToolbar)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocationbar)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPersonalbar)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStatusbar)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScrollbars)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCrypto)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsole)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPaintWorklet)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExternal)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInstallTrigger)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIntlUtils)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVisualViewport)
+
+ tmp->TraverseObjectsInGlobal(cb);
+
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeFields.mMessageManager)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeFields.mGroupMessageManagers)
+
+ for (size_t i = 0; i < tmp->mDocumentFlushedResolvers.Length(); i++) {
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentFlushedResolvers[i]->mPromise);
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentFlushedResolvers[i]->mCallback);
+ }
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindowInner)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
+ if (sInnerWindowsById) {
+ sInnerWindowsById->Remove(tmp->mWindowID);
+ }
+
+ JSObject* wrapper = tmp->GetWrapperPreserveColor();
+ if (wrapper) {
+ // Mark our realm as dead, so the JS engine won't hand out our
+ // global after this point.
+ JS::SetRealmNonLive(js::GetNonCCWObjectRealm(wrapper));
+ }
+
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mNavigator)
+
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance)
+
+ if (tmp->mWebTaskScheduler) {
+ tmp->mWebTaskScheduler->Disconnect();
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mWebTaskScheduler)
+ }
+
+#ifdef MOZ_WEBSPEECH
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mSpeechSynthesis)
+#endif
+
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlean)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mGleanPings)
+
+ if (tmp->mOuterWindow) {
+ nsGlobalWindowOuter::Cast(tmp->mOuterWindow)->MaybeClearInnerWindow(tmp);
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mOuterWindow)
+ }
+
+ if (tmp->mListenerManager) {
+ tmp->mListenerManager->Disconnect();
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mListenerManager)
+ }
+
+ // Here the Timeouts list would've been unlinked, but we rely on
+ // that Timeout objects have been traced and will remove themselves
+ // while unlinking.
+
+ tmp->UpdateTopInnerWindow();
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mTopInnerWindow)
+
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocation)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mHistory)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mCustomElements)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mSharedWorkers)
+ if (tmp->mLocalStorage) {
+ tmp->mLocalStorage->Disconnect();
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalStorage)
+ }
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mSessionStorage)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mIndexedDB)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPrincipal)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentCookiePrincipal)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentStoragePrincipal)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPartitionedPrincipal)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentCsp)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowserChild)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mDoc)
+
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mGamepads)
+
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mCacheStorage)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mVRDisplays)
+
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mDebuggerNotificationManager)
+
+ // Unlink stuff from nsPIDOMWindow
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeEventHandler)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mParentTarget)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mFocusedElement)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowsingContext)
+
+ MOZ_DIAGNOSTIC_ASSERT(
+ !tmp->mWindowGlobalChild || tmp->mWindowGlobalChild->IsClosed(),
+ "How are we unlinking a window before its actor has been destroyed?");
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindowGlobalChild)
+
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mMenubar)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mToolbar)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocationbar)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mPersonalbar)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mStatusbar)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mScrollbars)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mCrypto)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mPaintWorklet)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mExternal)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mInstallTrigger)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mIntlUtils)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mVisualViewport)
+
+ tmp->UnlinkObjectsInGlobal();
+
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleRequestExecutor)
+
+ // Here the IdleRequest list would've been unlinked, but we rely on
+ // that IdleRequest objects have been traced and will remove
+ // themselves while unlinking.
+
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mClientSource)
+
+ if (tmp->IsChromeWindow()) {
+ if (tmp->mChromeFields.mMessageManager) {
+ static_cast<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(); }
+
+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;
+
+ // 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<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.
+ //
+ // 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, SerialEventTarget(), 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<ServiceWorkerDescriptor> 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, SerialEventTarget(), 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<Element> frameElement = outer->GetFrameElementInternal();
+ nsCOMPtr<EventTarget> 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<nsIDragService> 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> element = GetBrowsingContext()->GetEmbedderElement();
+ if (element) {
+ nsEventStatus status = nsEventStatus_eIgnore;
+ WidgetEvent event(/* aIsTrusted = */ true, eLoad);
+ event.mFlags.mBubbles = false;
+ event.mFlags.mCancelable = false;
+
+ // 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.
+ 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 =
+ BrowserChild::GetFrom(static_cast<nsPIDOMWindowInner*>(this));
+ if (browserChild &&
+ !GetBrowsingContext()->GetParentWindowContext()->IsInProcess()) {
+ // 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<nsPIDOMWindowOuter> 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<EventTarget> kungFuDeathGrip1(mChromeEventHandler);
+ mozilla::Unused
+ << kungFuDeathGrip1; // These aren't referred to through the function
+ nsCOMPtr<nsIScriptContext> 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<JSObject*> 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 <frame src="javascript:xxx">, in
+ // that case the global window is used in JS before we've loaded
+ // a document into the window.
+
+ nsCOMPtr<nsIScriptObjectPrincipal> 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<nsIScriptObjectPrincipal> 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<nsIScriptObjectPrincipal> 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<nsIScriptObjectPrincipal> 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::Screen() {
+ if (!mScreen) {
+ mScreen = new nsScreen(this);
+ }
+ 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<nsDOMNavigationTiming> timing = mDoc->GetNavigationTiming();
+ nsCOMPtr<nsITimedChannel> 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<ClientInfo> nsPIDOMWindowInner::GetClientInfo() const {
+ return nsGlobalWindowInner::Cast(this)->GetClientInfo();
+}
+
+Maybe<ClientState> nsPIDOMWindowInner::GetClientState() const {
+ return nsGlobalWindowInner::Cast(this)->GetClientState();
+}
+
+Maybe<ServiceWorkerDescriptor> 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<InstallTriggerImpl>(
+ "@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<ServiceWorkerDescriptor> 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<nsIDocumentLoader> loader(do_QueryInterface(GetDocShell()));
+ if (loader) {
+ nsCOMPtr<nsILoadGroup> loadgroup;
+ Unused << loader->GetLoadGroup(getter_AddRefs(loadgroup));
+ if (loadgroup) {
+ nsCOMPtr<nsISimpleEnumerator> iter;
+ Unused << loadgroup->GetRequests(getter_AddRefs(iter));
+ if (iter) {
+ nsCOMPtr<nsISupports> 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<nsIChannel> 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<nsIURI> 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<BrowsingContext> 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<AudioChannelService> 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<nsPIDOMWindowOuter> 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<AudioChannelService> 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(this);
+ }
+
+ return mGlean;
+}
+
+mozilla::glean::GleanPings* nsGlobalWindowInner::GleanPings() {
+ if (!mGleanPings) {
+ mGleanPings = new mozilla::glean::GleanPings();
+ }
+
+ return mGleanPings;
+}
+
+Nullable<WindowProxyHolder> 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 <iframe
+ * mozbrowser> boundaries, so if |this| is contained by an <iframe
+ * mozbrowser>, we will return |this| as its own parent.
+ */
+nsPIDOMWindowOuter* nsGlobalWindowInner::GetInProcessScriptableParent() {
+ FORWARD_TO_OUTER(GetInProcessScriptableParent, (), nullptr);
+}
+
+/**
+ * GetInProcessScriptableTop used to be called when a script read window.top.
+ * Under Fission, that is now handled by BrowsingContext::Top, 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 GetRealTop, GetInProcessScriptableTop respects <iframe
+ * mozbrowser> boundaries. If we encounter a window owned by an <iframe
+ * mozbrowser> while walking up the window hierarchy, we'll stop and return that
+ * window.
+ */
+nsPIDOMWindowOuter* nsGlobalWindowInner::GetInProcessScriptableTop() {
+ FORWARD_TO_OUTER(GetInProcessScriptableTop, (), nullptr);
+}
+
+void nsGlobalWindowInner::GetContent(JSContext* aCx,
+ JS::MutableHandle<JSObject*> aRetval,
+ CallerType aCallerType,
+ ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(GetContentOuter,
+ (aCx, aRetval, aCallerType, aError), aError, );
+}
+
+BarProp* nsGlobalWindowInner::GetMenubar(ErrorResult& aError) {
+ if (!mMenubar) {
+ mMenubar = new MenubarProp(this);
+ }
+
+ return mMenubar;
+}
+
+BarProp* nsGlobalWindowInner::GetToolbar(ErrorResult& aError) {
+ if (!mToolbar) {
+ mToolbar = new ToolbarProp(this);
+ }
+
+ return mToolbar;
+}
+
+BarProp* nsGlobalWindowInner::GetLocationbar(ErrorResult& aError) {
+ if (!mLocationbar) {
+ mLocationbar = new LocationbarProp(this);
+ }
+ return mLocationbar;
+}
+
+BarProp* nsGlobalWindowInner::GetPersonalbar(ErrorResult& aError) {
+ if (!mPersonalbar) {
+ mPersonalbar = new PersonalbarProp(this);
+ }
+ return mPersonalbar;
+}
+
+BarProp* nsGlobalWindowInner::GetStatusbar(ErrorResult& aError) {
+ if (!mStatusbar) {
+ mStatusbar = new StatusbarProp(this);
+ }
+ return mStatusbar;
+}
+
+BarProp* nsGlobalWindowInner::GetScrollbars(ErrorResult& aError) {
+ if (!mScrollbars) {
+ mScrollbars = new ScrollbarsProp(this);
+ }
+
+ return mScrollbars;
+}
+
+bool nsGlobalWindowInner::GetClosed(ErrorResult& aError) {
+ // If we're called from JS (which is the only way we should be getting called
+ // here) and we reach this point, that means our JS global is the current
+ // target of the WindowProxy, which means that we are the "current inner"
+ // of our outer. So if FORWARD_TO_OUTER fails to forward, that means the
+ // outer is already torn down, which corresponds to the closed state.
+ FORWARD_TO_OUTER(GetClosedOuter, (), true);
+}
+
+Nullable<WindowProxyHolder> nsGlobalWindowInner::IndexedGetter(
+ uint32_t aIndex) {
+ FORWARD_TO_OUTER(IndexedGetterOuter, (aIndex), nullptr);
+}
+
+namespace {
+
+struct InterfaceShimEntry {
+ const char* geckoName;
+ const char* domName;
+};
+
+} // anonymous namespace
+
+// We add shims from Components.interfaces.nsIDOMFoo to window.Foo for each
+// interface that has interface constants that sites might be getting off
+// of Ci.
+const InterfaceShimEntry kInterfaceShimMap[] = {
+ {"nsIXMLHttpRequest", "XMLHttpRequest"},
+ {"nsIDOMDOMException", "DOMException"},
+ {"nsIDOMNode", "Node"},
+ {"nsIDOMCSSRule", "CSSRule"},
+ {"nsIDOMEvent", "Event"},
+ {"nsIDOMNSEvent", "Event"},
+ {"nsIDOMKeyEvent", "KeyEvent"},
+ {"nsIDOMMouseEvent", "MouseEvent"},
+ {"nsIDOMMouseScrollEvent", "MouseScrollEvent"},
+ {"nsIDOMMutationEvent", "MutationEvent"},
+ {"nsIDOMUIEvent", "UIEvent"},
+ {"nsIDOMHTMLMediaElement", "HTMLMediaElement"},
+ {"nsIDOMRange", "Range"},
+ // Think about whether Ci.nsINodeFilter can just go away for websites!
+ {"nsIDOMNodeFilter", "NodeFilter"},
+ {"nsIDOMXPathResult", "XPathResult"}};
+
+bool nsGlobalWindowInner::ResolveComponentsShim(
+ JSContext* aCx, JS::Handle<JSObject*> aGlobal,
+ JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> aDesc) {
+ // Keep track of how often this happens.
+ Telemetry::Accumulate(Telemetry::COMPONENTS_SHIM_ACCESSED_BY_CONTENT, true);
+
+ // Warn once.
+ nsCOMPtr<Document> doc = GetExtantDoc();
+ if (doc) {
+ doc->WarnOnceAbout(DeprecatedOperations::eComponents, /* asError = */ true);
+ }
+
+ // Create a fake Components object.
+ AssertSameCompartment(aCx, aGlobal);
+ JS::Rooted<JSObject*> components(aCx, JS_NewPlainObject(aCx));
+ if (NS_WARN_IF(!components)) {
+ return false;
+ }
+
+ // Create a fake interfaces object.
+ JS::Rooted<JSObject*> interfaces(aCx, JS_NewPlainObject(aCx));
+ if (NS_WARN_IF(!interfaces)) {
+ return false;
+ }
+ bool ok =
+ JS_DefineProperty(aCx, components, "interfaces", interfaces,
+ JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY);
+ if (NS_WARN_IF(!ok)) {
+ return false;
+ }
+
+ // Define a bunch of shims from the Ci.nsIDOMFoo to window.Foo for DOM
+ // interfaces with constants.
+ for (uint32_t i = 0; i < ArrayLength(kInterfaceShimMap); ++i) {
+ // Grab the names from the table.
+ const char* geckoName = kInterfaceShimMap[i].geckoName;
+ const char* domName = kInterfaceShimMap[i].domName;
+
+ // Look up the appopriate interface object on the global.
+ JS::Rooted<JS::Value> v(aCx, JS::UndefinedValue());
+ ok = JS_GetProperty(aCx, aGlobal, domName, &v);
+ if (NS_WARN_IF(!ok)) {
+ return false;
+ }
+ if (!v.isObject()) {
+ NS_WARNING("Unable to find interface object on global");
+ continue;
+ }
+
+ // Define the shim on the interfaces object.
+ ok = JS_DefineProperty(
+ aCx, interfaces, geckoName, v,
+ JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY);
+ if (NS_WARN_IF(!ok)) {
+ return false;
+ }
+ }
+
+ aDesc.set(mozilla::Some(JS::PropertyDescriptor::Data(
+ JS::ObjectValue(*components),
+ {JS::PropertyAttribute::Configurable, JS::PropertyAttribute::Enumerable,
+ JS::PropertyAttribute::Writable})));
+ return true;
+}
+
+#ifdef RELEASE_OR_BETA
+# define USE_CONTROLLERS_SHIM
+#endif
+
+#ifdef USE_CONTROLLERS_SHIM
+static const JSClass ControllersShimClass = {"Controllers", 0};
+static const JSClass XULControllersShimClass = {"XULControllers", 0};
+#endif
+
+bool nsGlobalWindowInner::DoResolve(
+ JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aId,
+ JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> aDesc) {
+ // Note: Keep this in sync with MayResolve.
+
+ // Note: The infallibleInit call in GlobalResolve depends on this check.
+ if (!aId.isString()) {
+ return true;
+ }
+
+ bool found;
+ if (!WebIDLGlobalNameHash::DefineIfEnabled(aCx, aObj, aId, aDesc, &found)) {
+ return false;
+ }
+
+ if (found) {
+ return true;
+ }
+
+ // We support a cut-down Components.interfaces in case websites are
+ // using Components.interfaces.nsIFoo.CONSTANT_NAME for the ones
+ // that have constants.
+ if (StaticPrefs::dom_use_components_shim() &&
+ aId == XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_COMPONENTS)) {
+ return ResolveComponentsShim(aCx, aObj, aDesc);
+ }
+
+ // We also support a "window.controllers" thing; apparently some
+ // sites use it for browser-sniffing. See bug 1010577.
+#ifdef USE_CONTROLLERS_SHIM
+ // Note: We use |aObj| rather than |this| to get the principal here, because
+ // this is called during Window setup when the Document isn't necessarily
+ // hooked up yet.
+ if ((aId == XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_CONTROLLERS) ||
+ aId == XPCJSRuntime::Get()->GetStringID(
+ XPCJSContext::IDX_CONTROLLERS_CLASS)) &&
+ !xpc::IsXrayWrapper(aObj) &&
+ !nsContentUtils::ObjectPrincipal(aObj)->IsSystemPrincipal()) {
+ if (GetExtantDoc()) {
+ GetExtantDoc()->WarnOnceAbout(
+ DeprecatedOperations::eWindow_Cc_ontrollers);
+ }
+ const JSClass* clazz;
+ if (aId ==
+ XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_CONTROLLERS)) {
+ clazz = &XULControllersShimClass;
+ } else {
+ clazz = &ControllersShimClass;
+ }
+ MOZ_ASSERT(JS_IsGlobalObject(aObj));
+ JS::Rooted<JSObject*> shim(aCx, JS_NewObject(aCx, clazz));
+ if (NS_WARN_IF(!shim)) {
+ return false;
+ }
+
+ aDesc.set(mozilla::Some(JS::PropertyDescriptor::Data(
+ JS::ObjectValue(*shim),
+ {JS::PropertyAttribute::Configurable, JS::PropertyAttribute::Enumerable,
+ JS::PropertyAttribute::Writable})));
+ return true;
+ }
+#endif
+
+ return true;
+}
+
+/* static */
+bool nsGlobalWindowInner::MayResolve(jsid aId) {
+ // Note: This function does not fail and may not have any side-effects.
+ // Note: Keep this in sync with DoResolve.
+ if (!aId.isString()) {
+ return false;
+ }
+
+ if (aId == XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_COMPONENTS)) {
+ return true;
+ }
+
+ if (aId == XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_CONTROLLERS) ||
+ aId == XPCJSRuntime::Get()->GetStringID(
+ XPCJSContext::IDX_CONTROLLERS_CLASS)) {
+ // We only resolve .controllers/.Controllers in release builds and on
+ // non-chrome windows, but let's not worry about any of that stuff.
+ return true;
+ }
+
+ return WebIDLGlobalNameHash::MayResolve(aId);
+}
+
+void nsGlobalWindowInner::GetOwnPropertyNames(
+ JSContext* aCx, JS::MutableHandleVector<jsid> aNames, bool aEnumerableOnly,
+ ErrorResult& aRv) {
+ if (aEnumerableOnly) {
+ // The names we would return from here get defined on the window via one of
+ // two codepaths. The ones coming from the WebIDLGlobalNameHash will end up
+ // in the DefineConstructor function in BindingUtils, which always defines
+ // things as non-enumerable. The ones coming from the script namespace
+ // manager get defined by our resolve hook using FillPropertyDescriptor with
+ // 0 for the property attributes, so non-enumerable as well.
+ //
+ // So in the aEnumerableOnly case we have nothing to do.
+ return;
+ }
+
+ // "Components" is marked as enumerable but only resolved on demand :-/.
+ // aNames.AppendElement(u"Components"_ns);
+
+ JS::Rooted<JSObject*> wrapper(aCx, GetWrapper());
+
+ // There are actually two ways we can get called here: For normal
+ // enumeration or for Xray enumeration. In the latter case, we want to
+ // return all possible WebIDL names, because we don't really support
+ // deleting these names off our Xray; trying to resolve them will just make
+ // them come back. In the former case, we want to avoid returning deleted
+ // names. But the JS engine already knows about the non-deleted
+ // already-resolved names, so we can just return the so-far-unresolved ones.
+ //
+ // We can tell which case we're in by whether aCx is in our wrapper's
+ // compartment. If not, we're in the Xray case.
+ WebIDLGlobalNameHash::NameType nameType =
+ js::IsObjectInContextCompartment(wrapper, aCx)
+ ? WebIDLGlobalNameHash::UnresolvedNamesOnly
+ : WebIDLGlobalNameHash::AllNames;
+ if (!WebIDLGlobalNameHash::GetNames(aCx, wrapper, nameType, aNames)) {
+ aRv.NoteJSContextException(aCx);
+ }
+}
+
+/* static */
+bool nsGlobalWindowInner::IsPrivilegedChromeWindow(JSContext*, JSObject* aObj) {
+ // For now, have to deal with XPConnect objects here.
+ nsGlobalWindowInner* win = xpc::WindowOrNull(aObj);
+ return win && win->IsChromeWindow() &&
+ nsContentUtils::ObjectPrincipal(aObj) ==
+ nsContentUtils::GetSystemPrincipal();
+}
+
+/* static */
+bool nsGlobalWindowInner::DeviceSensorsEnabled(JSContext*, JSObject*) {
+ return Preferences::GetBool("device.sensors.enabled");
+}
+
+/* static */
+bool nsGlobalWindowInner::CachesEnabled(JSContext* aCx, JSObject* aObj) {
+ if (!IsSecureContextOrObjectIsFromSecureContext(aCx, aObj)) {
+ return StaticPrefs::dom_caches_testing_enabled() ||
+ StaticPrefs::dom_serviceWorkers_testing_enabled();
+ }
+ return true;
+}
+
+/* static */
+bool nsGlobalWindowInner::IsSizeToContentEnabled(JSContext* aCx, JSObject*) {
+ return StaticPrefs::dom_window_sizeToContent_enabled() ||
+ nsContentUtils::IsSystemCaller(aCx);
+}
+
+/* static */
+bool nsGlobalWindowInner::IsGleanNeeded(JSContext* aCx, JSObject* aObj) {
+ // Glean is needed in ChromeOnly contexts and also in privileged about pages.
+ nsIPrincipal* principal = nsContentUtils::SubjectPrincipal(aCx);
+ if (principal->IsSystemPrincipal()) {
+ return true;
+ }
+
+ uint32_t flags = 0;
+ if (NS_FAILED(principal->GetAboutModuleFlags(&flags))) {
+ return false;
+ }
+ return flags & nsIAboutModule::IS_SECURE_CHROME_UI;
+}
+
+Crypto* nsGlobalWindowInner::GetCrypto(ErrorResult& aError) {
+ if (!mCrypto) {
+ mCrypto = new Crypto(this);
+ }
+ return mCrypto;
+}
+
+nsIControllers* nsGlobalWindowInner::GetControllers(ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(GetControllersOuter, (aError), aError, nullptr);
+}
+
+nsresult nsGlobalWindowInner::GetControllers(nsIControllers** aResult) {
+ ErrorResult rv;
+ nsCOMPtr<nsIControllers> controllers = GetControllers(rv);
+ controllers.forget(aResult);
+
+ return rv.StealNSResult();
+}
+
+Nullable<WindowProxyHolder> nsGlobalWindowInner::GetOpenerWindow(
+ ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(GetOpenerWindowOuter, (), aError, nullptr);
+}
+
+void nsGlobalWindowInner::GetOpener(JSContext* aCx,
+ JS::MutableHandle<JS::Value> aRetval,
+ ErrorResult& aError) {
+ Nullable<WindowProxyHolder> opener = GetOpenerWindow(aError);
+ if (aError.Failed() || opener.IsNull()) {
+ aRetval.setNull();
+ return;
+ }
+
+ if (!ToJSValue(aCx, opener.Value(), aRetval)) {
+ aError.NoteJSContextException(aCx);
+ }
+}
+
+void nsGlobalWindowInner::SetOpener(JSContext* aCx,
+ JS::Handle<JS::Value> aOpener,
+ ErrorResult& aError) {
+ if (aOpener.isNull()) {
+ RefPtr<BrowsingContext> bc(GetBrowsingContext());
+ if (!bc->IsDiscarded()) {
+ bc->SetOpener(nullptr);
+ }
+ return;
+ }
+
+ // If something other than null is passed, just define aOpener on our inner
+ // window's JS object, wrapped into the current compartment so that for Xrays
+ // we define on the Xray expando object, but don't set it on the outer window,
+ // so that it'll get reset on navigation. This is just like replaceable
+ // properties, but we're not quite readonly.
+ RedefineProperty(aCx, "opener", aOpener, aError);
+}
+
+void nsGlobalWindowInner::GetEvent(OwningEventOrUndefined& aRetval) {
+ if (mEvent) {
+ aRetval.SetAsEvent() = mEvent;
+ } else {
+ aRetval.SetUndefined();
+ }
+}
+
+void nsGlobalWindowInner::GetStatus(nsAString& aStatus, ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(GetStatusOuter, (aStatus), aError, );
+}
+
+void nsGlobalWindowInner::SetStatus(const nsAString& aStatus,
+ ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(SetStatusOuter, (aStatus), aError, );
+}
+
+void nsGlobalWindowInner::GetName(nsAString& aName, ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(GetNameOuter, (aName), aError, );
+}
+
+void nsGlobalWindowInner::SetName(const nsAString& aName,
+ mozilla::ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(SetNameOuter, (aName, aError), aError, );
+}
+
+double nsGlobalWindowInner::GetInnerWidth(ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(GetInnerWidthOuter, (aError), aError, 0);
+}
+
+nsresult nsGlobalWindowInner::GetInnerWidth(double* aWidth) {
+ ErrorResult rv;
+ // Callee doesn't care about the caller type, but play it safe.
+ *aWidth = GetInnerWidth(rv);
+ return rv.StealNSResult();
+}
+
+double nsGlobalWindowInner::GetInnerHeight(ErrorResult& aError) {
+ // We ignore aCallerType; we only have that argument because some other things
+ // called by GetReplaceableWindowCoord need it. If this ever changes, fix
+ // nsresult nsGlobalWindowInner::GetInnerHeight(double* aInnerWidth)
+ // to actually take a useful CallerType and pass it in here.
+ FORWARD_TO_OUTER_OR_THROW(GetInnerHeightOuter, (aError), aError, 0);
+}
+
+nsresult nsGlobalWindowInner::GetInnerHeight(double* aHeight) {
+ ErrorResult rv;
+ // Callee doesn't care about the caller type, but play it safe.
+ *aHeight = GetInnerHeight(rv);
+ return rv.StealNSResult();
+}
+
+int32_t nsGlobalWindowInner::GetOuterWidth(CallerType aCallerType,
+ ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(GetOuterWidthOuter, (aCallerType, aError), aError,
+ 0);
+}
+
+int32_t nsGlobalWindowInner::GetOuterHeight(CallerType aCallerType,
+ ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(GetOuterHeightOuter, (aCallerType, aError), aError,
+ 0);
+}
+
+double nsGlobalWindowInner::ScreenEdgeSlopX() const {
+ FORWARD_TO_OUTER(ScreenEdgeSlopX, (), 0);
+}
+
+double nsGlobalWindowInner::ScreenEdgeSlopY() const {
+ FORWARD_TO_OUTER(ScreenEdgeSlopY, (), 0);
+}
+
+int32_t nsGlobalWindowInner::GetScreenX(CallerType aCallerType,
+ ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(GetScreenXOuter, (aCallerType, aError), aError, 0);
+}
+
+int32_t nsGlobalWindowInner::GetScreenY(CallerType aCallerType,
+ ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(GetScreenYOuter, (aCallerType, aError), aError, 0);
+}
+
+float nsGlobalWindowInner::GetMozInnerScreenX(CallerType aCallerType,
+ ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(GetMozInnerScreenXOuter, (aCallerType), aError, 0);
+}
+
+float nsGlobalWindowInner::GetMozInnerScreenY(CallerType aCallerType,
+ ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(GetMozInnerScreenYOuter, (aCallerType), aError, 0);
+}
+
+static nsPresContext* GetPresContextForRatio(Document* aDoc) {
+ if (nsPresContext* presContext = aDoc->GetPresContext()) {
+ return presContext;
+ }
+ // We're in an undisplayed subdocument... There's not really an awesome way
+ // to tell what the right DPI is from here, so we try to walk up our parent
+ // document chain to the extent that the docs can observe each other.
+ Document* doc = aDoc;
+ while (doc->StyleOrLayoutObservablyDependsOnParentDocumentLayout()) {
+ doc = doc->GetInProcessParentDocument();
+ if (nsPresContext* presContext = doc->GetPresContext()) {
+ return presContext;
+ }
+ }
+ return nullptr;
+}
+
+double nsGlobalWindowInner::GetDevicePixelRatio(CallerType aCallerType,
+ ErrorResult& aError) {
+ ENSURE_ACTIVE_DOCUMENT(aError, 0.0);
+
+ RefPtr<nsPresContext> presContext = GetPresContextForRatio(mDoc);
+ if (NS_WARN_IF(!presContext)) {
+ // Still nothing, oh well.
+ return 1.0;
+ }
+
+ if (nsIGlobalObject::ShouldResistFingerprinting(
+ aCallerType, RFPTarget::WindowDevicePixelRatio)) {
+ // Spoofing the DevicePixelRatio causes blurriness in some situations
+ // on HiDPI displays. pdf.js is a non-system caller; but it can't
+ // expose the fingerprintable information, so we can safely disable
+ // spoofing in this situation. It doesn't address the issue for
+ // web-rendered content (including pdf.js instances on the web.)
+ // In the future we hope to have a better solution to fix all HiDPI
+ // blurriness...
+ nsAutoCString origin;
+ nsresult rv = this->GetPrincipal()->GetOrigin(origin);
+ if (NS_FAILED(rv) || origin != "resource://pdf.js"_ns) {
+ return 1.0;
+ }
+ }
+
+ if (aCallerType == CallerType::NonSystem) {
+ float overrideDPPX = presContext->GetOverrideDPPX();
+ if (overrideDPPX > 0.0f) {
+ return overrideDPPX;
+ }
+ }
+
+ return double(AppUnitsPerCSSPixel()) /
+ double(presContext->AppUnitsPerDevPixel());
+}
+
+double nsGlobalWindowInner::GetDesktopToDeviceScale(ErrorResult& aError) {
+ ENSURE_ACTIVE_DOCUMENT(aError, 0.0);
+ nsPresContext* presContext = GetPresContextForRatio(mDoc);
+ if (!presContext) {
+ return 1.0;
+ }
+ return presContext->DeviceContext()->GetDesktopToDeviceScale().scale;
+}
+
+int32_t nsGlobalWindowInner::RequestAnimationFrame(
+ FrameRequestCallback& aCallback, ErrorResult& aError) {
+ if (!mDoc) {
+ return 0;
+ }
+
+ if (GetWrapperPreserveColor()) {
+ js::NotifyAnimationActivity(GetWrapperPreserveColor());
+ }
+
+ DebuggerNotificationDispatch(this,
+ DebuggerNotificationType::RequestAnimationFrame);
+
+ int32_t handle;
+ aError = mDoc->ScheduleFrameRequestCallback(aCallback, &handle);
+ return handle;
+}
+
+void nsGlobalWindowInner::CancelAnimationFrame(int32_t aHandle,
+ ErrorResult& aError) {
+ if (!mDoc) {
+ return;
+ }
+
+ DebuggerNotificationDispatch(this,
+ DebuggerNotificationType::CancelAnimationFrame);
+
+ mDoc->CancelFrameRequestCallback(aHandle);
+}
+
+already_AddRefed<MediaQueryList> nsGlobalWindowInner::MatchMedia(
+ const nsACString& aMediaQueryList, CallerType aCallerType,
+ ErrorResult& aError) {
+ ENSURE_ACTIVE_DOCUMENT(aError, nullptr);
+ return mDoc->MatchMedia(aMediaQueryList, aCallerType);
+}
+
+int32_t nsGlobalWindowInner::GetScrollMinX(ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(GetScrollBoundaryOuter, (eSideLeft), aError, 0);
+}
+
+int32_t nsGlobalWindowInner::GetScrollMinY(ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(GetScrollBoundaryOuter, (eSideTop), aError, 0);
+}
+
+int32_t nsGlobalWindowInner::GetScrollMaxX(ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(GetScrollBoundaryOuter, (eSideRight), aError, 0);
+}
+
+int32_t nsGlobalWindowInner::GetScrollMaxY(ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(GetScrollBoundaryOuter, (eSideBottom), aError, 0);
+}
+
+double nsGlobalWindowInner::GetScrollX(ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(GetScrollXOuter, (), aError, 0);
+}
+
+double nsGlobalWindowInner::GetScrollY(ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(GetScrollYOuter, (), aError, 0);
+}
+
+uint32_t nsGlobalWindowInner::Length() { FORWARD_TO_OUTER(Length, (), 0); }
+
+Nullable<WindowProxyHolder> nsGlobalWindowInner::GetTop(
+ mozilla::ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(GetTopOuter, (), aError, nullptr);
+}
+
+already_AddRefed<BrowsingContext> nsGlobalWindowInner::GetChildWindow(
+ const nsAString& aName) {
+ if (GetOuterWindowInternal()) {
+ return GetOuterWindowInternal()->GetChildWindow(aName);
+ }
+ return nullptr;
+}
+
+void nsGlobalWindowInner::RefreshRealmPrincipal() {
+ JS::SetRealmPrincipals(js::GetNonCCWObjectRealm(GetWrapperPreserveColor()),
+ nsJSPrincipals::get(mDoc->NodePrincipal()));
+}
+
+void nsGlobalWindowInner::RefreshReduceTimerPrecisionCallerType() {
+ JS::SetRealmReduceTimerPrecisionCallerType(
+ js::GetNonCCWObjectRealm(GetWrapperPreserveColor()),
+ RTPCallerTypeToToken(GetRTPCallerType()));
+}
+
+already_AddRefed<nsIWidget> nsGlobalWindowInner::GetMainWidget() {
+ FORWARD_TO_OUTER(GetMainWidget, (), nullptr);
+}
+
+nsIWidget* nsGlobalWindowInner::GetNearestWidget() const {
+ if (GetOuterWindowInternal()) {
+ return GetOuterWindowInternal()->GetNearestWidget();
+ }
+ return nullptr;
+}
+
+void nsGlobalWindowInner::SetFullScreen(bool aFullscreen,
+ mozilla::ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(SetFullscreenOuter, (aFullscreen, aError), aError,
+ /* void */);
+}
+
+bool nsGlobalWindowInner::GetFullScreen(ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(GetFullscreenOuter, (), aError, false);
+}
+
+bool nsGlobalWindowInner::GetFullScreen() {
+ ErrorResult dummy;
+ bool fullscreen = GetFullScreen(dummy);
+ dummy.SuppressException();
+ return fullscreen;
+}
+
+void nsGlobalWindowInner::Dump(const nsAString& aStr) {
+ if (!nsJSUtils::DumpEnabled()) {
+ return;
+ }
+
+ char* cstr = ToNewUTF8String(aStr);
+
+#if defined(XP_MACOSX)
+ // have to convert \r to \n so that printing to the console works
+ char *c = cstr, *cEnd = cstr + strlen(cstr);
+ while (c < cEnd) {
+ if (*c == '\r') *c = '\n';
+ c++;
+ }
+#endif
+
+ if (cstr) {
+ MOZ_LOG(nsContentUtils::DOMDumpLog(), LogLevel::Debug,
+ ("[Window.Dump] %s", cstr));
+#ifdef XP_WIN
+ PrintToDebugger(cstr);
+#endif
+#ifdef ANDROID
+ __android_log_write(ANDROID_LOG_INFO, "GeckoDump", cstr);
+#endif
+ FILE* fp = gDumpFile ? gDumpFile : stdout;
+ fputs(cstr, fp);
+ fflush(fp);
+ free(cstr);
+ }
+}
+
+void nsGlobalWindowInner::Alert(nsIPrincipal& aSubjectPrincipal,
+ ErrorResult& aError) {
+ Alert(u""_ns, aSubjectPrincipal, aError);
+}
+
+void nsGlobalWindowInner::Alert(const nsAString& aMessage,
+ nsIPrincipal& aSubjectPrincipal,
+ ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(AlertOuter, (aMessage, aSubjectPrincipal, aError),
+ aError, );
+}
+
+bool nsGlobalWindowInner::Confirm(const nsAString& aMessage,
+ nsIPrincipal& aSubjectPrincipal,
+ ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(ConfirmOuter, (aMessage, aSubjectPrincipal, aError),
+ aError, false);
+}
+
+already_AddRefed<Promise> nsGlobalWindowInner::Fetch(
+ const RequestOrUSVString& aInput, const RequestInit& aInit,
+ CallerType aCallerType, ErrorResult& aRv) {
+ return FetchRequest(this, aInput, aInit, aCallerType, aRv);
+}
+
+void nsGlobalWindowInner::Prompt(const nsAString& aMessage,
+ const nsAString& aInitial, nsAString& aReturn,
+ nsIPrincipal& aSubjectPrincipal,
+ ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(
+ PromptOuter, (aMessage, aInitial, aReturn, aSubjectPrincipal, aError),
+ aError, );
+}
+
+void nsGlobalWindowInner::Focus(CallerType aCallerType, ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(FocusOuter,
+ (aCallerType, /* aFromOtherProcess */ false,
+ nsFocusManager::GenerateFocusActionId()),
+ aError, );
+}
+
+nsresult nsGlobalWindowInner::Focus(CallerType aCallerType) {
+ ErrorResult rv;
+ Focus(aCallerType, rv);
+
+ return rv.StealNSResult();
+}
+
+void nsGlobalWindowInner::Blur(CallerType aCallerType, ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(BlurOuter, (aCallerType), aError, );
+}
+
+void nsGlobalWindowInner::Stop(ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(StopOuter, (aError), aError, );
+}
+
+void nsGlobalWindowInner::Print(ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(PrintOuter, (aError), aError, );
+}
+
+Nullable<WindowProxyHolder> nsGlobalWindowInner::PrintPreview(
+ nsIPrintSettings* aSettings, nsIWebProgressListener* aListener,
+ nsIDocShell* aDocShellToCloneInto, ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(
+ Print,
+ (aSettings,
+ /* aRemotePrintJob = */ nullptr, aListener, aDocShellToCloneInto,
+ nsGlobalWindowOuter::IsPreview::Yes,
+ nsGlobalWindowOuter::IsForWindowDotPrint::No,
+ /* aPrintPreviewCallback = */ nullptr, aError),
+ aError, nullptr);
+}
+
+void nsGlobalWindowInner::MoveTo(int32_t aXPos, int32_t aYPos,
+ CallerType aCallerType, ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(MoveToOuter, (aXPos, aYPos, aCallerType, aError),
+ aError, );
+}
+
+void nsGlobalWindowInner::MoveBy(int32_t aXDif, int32_t aYDif,
+ CallerType aCallerType, ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(MoveByOuter, (aXDif, aYDif, aCallerType, aError),
+ aError, );
+}
+
+void nsGlobalWindowInner::ResizeTo(int32_t aWidth, int32_t aHeight,
+ CallerType aCallerType,
+ ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(ResizeToOuter,
+ (aWidth, aHeight, aCallerType, aError), aError, );
+}
+
+void nsGlobalWindowInner::ResizeBy(int32_t aWidthDif, int32_t aHeightDif,
+ CallerType aCallerType,
+ ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(
+ ResizeByOuter, (aWidthDif, aHeightDif, aCallerType, aError), aError, );
+}
+
+void nsGlobalWindowInner::SizeToContent(CallerType aCallerType,
+ ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(SizeToContentOuter, (aCallerType, {}, aError),
+ aError, );
+}
+
+void nsGlobalWindowInner::SizeToContentConstrained(
+ const SizeToContentConstraints& aConstraints, ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(
+ SizeToContentOuter, (CallerType::System, aConstraints, aError), aError, );
+}
+
+already_AddRefed<nsPIWindowRoot> nsGlobalWindowInner::GetTopWindowRoot() {
+ nsGlobalWindowOuter* outer = GetOuterWindowInternal();
+ if (!outer) {
+ return nullptr;
+ }
+ return outer->GetTopWindowRoot();
+}
+
+void nsGlobalWindowInner::Scroll(double aXScroll, double aYScroll) {
+ // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
+ auto scrollPos = CSSIntPoint::Truncate(mozilla::ToZeroIfNonfinite(aXScroll),
+ mozilla::ToZeroIfNonfinite(aYScroll));
+ ScrollTo(scrollPos, ScrollOptions());
+}
+
+void nsGlobalWindowInner::ScrollTo(double aXScroll, double aYScroll) {
+ // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
+ auto scrollPos = CSSIntPoint::Truncate(mozilla::ToZeroIfNonfinite(aXScroll),
+ mozilla::ToZeroIfNonfinite(aYScroll));
+ ScrollTo(scrollPos, ScrollOptions());
+}
+
+void nsGlobalWindowInner::ScrollTo(const ScrollToOptions& aOptions) {
+ // When scrolling to a non-zero offset, we need to determine whether that
+ // position is within our scrollable range, so we need updated layout
+ // information which requires a layout flush, otherwise all we need is to
+ // flush frames to be able to access our scrollable frame here.
+ FlushType flushType =
+ ((aOptions.mLeft.WasPassed() && aOptions.mLeft.Value() > 0) ||
+ (aOptions.mTop.WasPassed() && aOptions.mTop.Value() > 0))
+ ? FlushType::Layout
+ : FlushType::Frames;
+ FlushPendingNotifications(flushType);
+ nsIScrollableFrame* sf = GetScrollFrame();
+
+ if (sf) {
+ CSSIntPoint scrollPos = sf->GetRoundedScrollPositionCSSPixels();
+ if (aOptions.mLeft.WasPassed()) {
+ scrollPos.x = static_cast<int32_t>(
+ mozilla::ToZeroIfNonfinite(aOptions.mLeft.Value()));
+ }
+ if (aOptions.mTop.WasPassed()) {
+ scrollPos.y = static_cast<int32_t>(
+ mozilla::ToZeroIfNonfinite(aOptions.mTop.Value()));
+ }
+
+ ScrollTo(scrollPos, aOptions);
+ }
+}
+
+void nsGlobalWindowInner::Scroll(const ScrollToOptions& aOptions) {
+ ScrollTo(aOptions);
+}
+
+void nsGlobalWindowInner::ScrollTo(const CSSIntPoint& aScroll,
+ const ScrollOptions& aOptions) {
+ // When scrolling to a non-zero offset, we need to determine whether that
+ // position is within our scrollable range, so we need updated layout
+ // information which requires a layout flush, otherwise all we need is to
+ // flush frames to be able to access our scrollable frame here.
+ FlushType flushType =
+ (aScroll.x || aScroll.y) ? FlushType::Layout : FlushType::Frames;
+ FlushPendingNotifications(flushType);
+ nsIScrollableFrame* sf = GetScrollFrame();
+
+ if (sf) {
+ // Here we calculate what the max pixel value is that we can
+ // scroll to, we do this by dividing maxint with the pixel to
+ // twips conversion factor, and subtracting 4, the 4 comes from
+ // experimenting with this value, anything less makes the view
+ // code not scroll correctly, I have no idea why. -- jst
+ const int32_t maxpx = nsPresContext::AppUnitsToIntCSSPixels(0x7fffffff) - 4;
+
+ CSSIntPoint scroll(aScroll);
+ if (scroll.x > maxpx) {
+ scroll.x = maxpx;
+ }
+
+ if (scroll.y > maxpx) {
+ scroll.y = maxpx;
+ }
+
+ ScrollMode scrollMode = sf->IsSmoothScroll(aOptions.mBehavior)
+ ? ScrollMode::SmoothMsd
+ : ScrollMode::Instant;
+
+ sf->ScrollToCSSPixels(scroll, scrollMode);
+ }
+}
+
+void nsGlobalWindowInner::ScrollBy(double aXScrollDif, double aYScrollDif) {
+ FlushPendingNotifications(FlushType::Layout);
+ nsIScrollableFrame* sf = GetScrollFrame();
+
+ if (sf) {
+ // It seems like it would make more sense for ScrollBy to use
+ // SMOOTH mode, but tests seem to depend on the synchronous behaviour.
+ // Perhaps Web content does too.
+ ScrollToOptions options;
+ options.mLeft.Construct(aXScrollDif);
+ options.mTop.Construct(aYScrollDif);
+ ScrollBy(options);
+ }
+}
+
+void nsGlobalWindowInner::ScrollBy(const ScrollToOptions& aOptions) {
+ FlushPendingNotifications(FlushType::Layout);
+ nsIScrollableFrame* sf = GetScrollFrame();
+
+ if (sf) {
+ CSSIntPoint scrollDelta;
+ if (aOptions.mLeft.WasPassed()) {
+ scrollDelta.x = static_cast<int32_t>(
+ mozilla::ToZeroIfNonfinite(aOptions.mLeft.Value()));
+ }
+ if (aOptions.mTop.WasPassed()) {
+ scrollDelta.y = static_cast<int32_t>(
+ mozilla::ToZeroIfNonfinite(aOptions.mTop.Value()));
+ }
+
+ ScrollMode scrollMode = sf->IsSmoothScroll(aOptions.mBehavior)
+ ? ScrollMode::SmoothMsd
+ : ScrollMode::Instant;
+
+ sf->ScrollByCSSPixels(scrollDelta, scrollMode);
+ }
+}
+
+void nsGlobalWindowInner::ScrollByLines(int32_t numLines,
+ const ScrollOptions& aOptions) {
+ FlushPendingNotifications(FlushType::Layout);
+ nsIScrollableFrame* sf = GetScrollFrame();
+ if (sf) {
+ // It seems like it would make more sense for ScrollByLines to use
+ // SMOOTH mode, but tests seem to depend on the synchronous behaviour.
+ // Perhaps Web content does too.
+ ScrollMode scrollMode = sf->IsSmoothScroll(aOptions.mBehavior)
+ ? ScrollMode::SmoothMsd
+ : ScrollMode::Instant;
+
+ sf->ScrollBy(nsIntPoint(0, numLines), ScrollUnit::LINES, scrollMode);
+ }
+}
+
+void nsGlobalWindowInner::ScrollByPages(int32_t numPages,
+ const ScrollOptions& aOptions) {
+ FlushPendingNotifications(FlushType::Layout);
+ nsIScrollableFrame* sf = GetScrollFrame();
+ if (sf) {
+ // It seems like it would make more sense for ScrollByPages to use
+ // SMOOTH mode, but tests seem to depend on the synchronous behaviour.
+ // Perhaps Web content does too.
+ ScrollMode scrollMode = sf->IsSmoothScroll(aOptions.mBehavior)
+ ? ScrollMode::SmoothMsd
+ : ScrollMode::Instant;
+
+ sf->ScrollBy(nsIntPoint(0, numPages), ScrollUnit::PAGES, scrollMode);
+ }
+}
+
+void nsGlobalWindowInner::MozScrollSnap() {
+ FlushPendingNotifications(FlushType::Layout);
+ nsIScrollableFrame* sf = GetScrollFrame();
+ if (sf) {
+ sf->ScrollSnap();
+ }
+}
+
+void nsGlobalWindowInner::ClearTimeout(int32_t aHandle) {
+ DebuggerNotificationDispatch(this, DebuggerNotificationType::ClearTimeout);
+
+ if (aHandle > 0) {
+ mTimeoutManager->ClearTimeout(aHandle, Timeout::Reason::eTimeoutOrInterval);
+ }
+}
+
+void nsGlobalWindowInner::ClearInterval(int32_t aHandle) {
+ DebuggerNotificationDispatch(this, DebuggerNotificationType::ClearInterval);
+
+ if (aHandle > 0) {
+ mTimeoutManager->ClearTimeout(aHandle, Timeout::Reason::eTimeoutOrInterval);
+ }
+}
+
+void nsGlobalWindowInner::SetResizable(bool aResizable) const {
+ // nop
+}
+
+void nsGlobalWindowInner::CaptureEvents() {
+ if (mDoc) {
+ mDoc->WarnOnceAbout(DeprecatedOperations::eUseOfCaptureEvents);
+ }
+}
+
+void nsGlobalWindowInner::ReleaseEvents() {
+ if (mDoc) {
+ mDoc->WarnOnceAbout(DeprecatedOperations::eUseOfReleaseEvents);
+ }
+}
+
+Nullable<WindowProxyHolder> nsGlobalWindowInner::Open(const nsAString& aUrl,
+ const nsAString& aName,
+ const nsAString& aOptions,
+ ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(OpenOuter, (aUrl, aName, aOptions, aError), aError,
+ nullptr);
+}
+
+Nullable<WindowProxyHolder> nsGlobalWindowInner::OpenDialog(
+ JSContext* aCx, const nsAString& aUrl, const nsAString& aName,
+ const nsAString& aOptions, const Sequence<JS::Value>& aExtraArgument,
+ ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(
+ OpenDialogOuter, (aCx, aUrl, aName, aOptions, aExtraArgument, aError),
+ aError, nullptr);
+}
+
+WindowProxyHolder nsGlobalWindowInner::GetFrames(ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(GetFramesOuter, (), aError, Window());
+}
+
+void nsGlobalWindowInner::PostMessageMoz(JSContext* aCx,
+ JS::Handle<JS::Value> aMessage,
+ const nsAString& aTargetOrigin,
+ JS::Handle<JS::Value> aTransfer,
+ nsIPrincipal& aSubjectPrincipal,
+ ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(
+ PostMessageMozOuter,
+ (aCx, aMessage, aTargetOrigin, aTransfer, aSubjectPrincipal, aError),
+ aError, );
+}
+
+void nsGlobalWindowInner::PostMessageMoz(JSContext* aCx,
+ JS::Handle<JS::Value> aMessage,
+ const nsAString& aTargetOrigin,
+ const Sequence<JSObject*>& aTransfer,
+ nsIPrincipal& aSubjectPrincipal,
+ ErrorResult& aRv) {
+ JS::Rooted<JS::Value> transferArray(aCx, JS::UndefinedValue());
+
+ aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransfer,
+ &transferArray);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
+
+ PostMessageMoz(aCx, aMessage, aTargetOrigin, transferArray, aSubjectPrincipal,
+ aRv);
+}
+
+void nsGlobalWindowInner::PostMessageMoz(
+ JSContext* aCx, JS::Handle<JS::Value> aMessage,
+ const WindowPostMessageOptions& aOptions, nsIPrincipal& aSubjectPrincipal,
+ ErrorResult& aRv) {
+ JS::Rooted<JS::Value> transferArray(aCx, JS::UndefinedValue());
+
+ aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(
+ aCx, aOptions.mTransfer, &transferArray);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
+
+ PostMessageMoz(aCx, aMessage, aOptions.mTargetOrigin, transferArray,
+ aSubjectPrincipal, aRv);
+}
+
+void nsGlobalWindowInner::Close(CallerType aCallerType, ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(CloseOuter, (aCallerType == CallerType::System),
+ aError, );
+}
+
+nsresult nsGlobalWindowInner::Close() {
+ FORWARD_TO_OUTER(Close, (), NS_ERROR_UNEXPECTED);
+}
+
+bool nsGlobalWindowInner::IsInModalState() {
+ FORWARD_TO_OUTER(IsInModalState, (), false);
+}
+
+// static
+void nsGlobalWindowInner::NotifyDOMWindowDestroyed(
+ nsGlobalWindowInner* aWindow) {
+ nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
+ if (observerService) {
+ observerService->NotifyObservers(ToSupports(aWindow),
+ DOM_WINDOW_DESTROYED_TOPIC, nullptr);
+ }
+}
+
+void nsGlobalWindowInner::NotifyWindowIDDestroyed(const char* aTopic) {
+ nsCOMPtr<nsIRunnable> runnable =
+ new WindowDestroyedEvent(this, mWindowID, aTopic);
+ Dispatch(runnable.forget());
+}
+
+// static
+void nsGlobalWindowInner::NotifyDOMWindowFrozen(nsGlobalWindowInner* aWindow) {
+ if (aWindow) {
+ nsCOMPtr<nsIObserverService> observerService =
+ services::GetObserverService();
+ if (observerService) {
+ observerService->NotifyObservers(ToSupports(aWindow),
+ DOM_WINDOW_FROZEN_TOPIC, nullptr);
+ }
+ }
+}
+
+// static
+void nsGlobalWindowInner::NotifyDOMWindowThawed(nsGlobalWindowInner* aWindow) {
+ if (aWindow) {
+ nsCOMPtr<nsIObserverService> observerService =
+ services::GetObserverService();
+ if (observerService) {
+ observerService->NotifyObservers(ToSupports(aWindow),
+ DOM_WINDOW_THAWED_TOPIC, nullptr);
+ }
+ }
+}
+
+Element* nsGlobalWindowInner::GetFrameElement(nsIPrincipal& aSubjectPrincipal,
+ ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(GetFrameElement, (aSubjectPrincipal), aError,
+ nullptr);
+}
+
+Element* nsGlobalWindowInner::GetRealFrameElement(ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(GetFrameElement, (), aError, nullptr);
+}
+
+void nsGlobalWindowInner::UpdateCommands(const nsAString& anAction) {
+ if (GetOuterWindowInternal()) {
+ GetOuterWindowInternal()->UpdateCommands(anAction);
+ }
+}
+
+Selection* nsGlobalWindowInner::GetSelection(ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(GetSelectionOuter, (), aError, nullptr);
+}
+
+WebTaskScheduler* nsGlobalWindowInner::Scheduler() {
+ if (!mWebTaskScheduler) {
+ mWebTaskScheduler = WebTaskScheduler::CreateForMainThread(this);
+ }
+ MOZ_ASSERT(mWebTaskScheduler);
+ return mWebTaskScheduler;
+}
+
+bool nsGlobalWindowInner::Find(const nsAString& aString, bool aCaseSensitive,
+ bool aBackwards, bool aWrapAround,
+ bool aWholeWord, bool aSearchInFrames,
+ bool aShowDialog, ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(FindOuter,
+ (aString, aCaseSensitive, aBackwards, aWrapAround,
+ aWholeWord, aSearchInFrames, aShowDialog, aError),
+ aError, false);
+}
+
+void nsGlobalWindowInner::GetOrigin(nsAString& aOrigin) {
+ nsContentUtils::GetWebExposedOriginSerialization(GetPrincipal(), aOrigin);
+}
+
+// See also AutoJSAPI::ReportException
+void nsGlobalWindowInner::ReportError(JSContext* aCx,
+ JS::Handle<JS::Value> aError,
+ CallerType aCallerType,
+ ErrorResult& aRv) {
+ if (MOZ_UNLIKELY(!HasActiveDocument())) {
+ return aRv.Throw(NS_ERROR_XPC_SECURITY_MANAGER_VETO);
+ }
+
+ JS::ErrorReportBuilder jsReport(aCx);
+ JS::ExceptionStack exnStack(aCx, aError, nullptr);
+ if (!jsReport.init(aCx, exnStack, JS::ErrorReportBuilder::NoSideEffects)) {
+ return aRv.NoteJSContextException(aCx);
+ }
+
+ RefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport();
+ bool isChrome = aCallerType == CallerType::System;
+ xpcReport->Init(jsReport.report(), jsReport.toStringResult().c_str(),
+ isChrome, WindowID());
+
+ JS::RootingContext* rcx = JS::RootingContext::get(aCx);
+ DispatchScriptErrorEvent(this, rcx, xpcReport, exnStack.exception(),
+ exnStack.stack());
+}
+
+void nsGlobalWindowInner::Atob(const nsAString& aAsciiBase64String,
+ nsAString& aBinaryData, ErrorResult& aError) {
+ aError = nsContentUtils::Atob(aAsciiBase64String, aBinaryData);
+}
+
+void nsGlobalWindowInner::Btoa(const nsAString& aBinaryData,
+ nsAString& aAsciiBase64String,
+ ErrorResult& aError) {
+ aError = nsContentUtils::Btoa(aBinaryData, aAsciiBase64String);
+}
+
+//*****************************************************************************
+// EventTarget
+//*****************************************************************************
+
+nsPIDOMWindowOuter* nsGlobalWindowInner::GetOwnerGlobalForBindingsInternal() {
+ return nsPIDOMWindowOuter::GetFromCurrentInner(this);
+}
+
+bool nsGlobalWindowInner::DispatchEvent(Event& aEvent, CallerType aCallerType,
+ ErrorResult& aRv) {
+ if (!IsCurrentInnerWindow()) {
+ NS_WARNING(
+ "DispatchEvent called on non-current inner window, dropping. "
+ "Please check the window in the caller instead.");
+ aRv.Throw(NS_ERROR_FAILURE);
+ return false;
+ }
+
+ if (!mDoc) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return false;
+ }
+
+ // Obtain a presentation shell
+ RefPtr<nsPresContext> presContext = mDoc->GetPresContext();
+
+ nsEventStatus status = nsEventStatus_eIgnore;
+ nsresult rv = EventDispatcher::DispatchDOMEvent(this, nullptr, &aEvent,
+ presContext, &status);
+ bool retval = !aEvent.DefaultPrevented(aCallerType);
+ if (NS_FAILED(rv)) {
+ aRv.Throw(rv);
+ }
+ return retval;
+}
+
+mozilla::Maybe<mozilla::dom::EventCallbackDebuggerNotificationType>
+nsGlobalWindowInner::GetDebuggerNotificationType() const {
+ return mozilla::Some(
+ mozilla::dom::EventCallbackDebuggerNotificationType::Global);
+}
+
+bool nsGlobalWindowInner::ComputeDefaultWantsUntrusted(ErrorResult& aRv) {
+ return !nsContentUtils::IsChromeDoc(mDoc);
+}
+
+EventListenerManager* nsGlobalWindowInner::GetOrCreateListenerManager() {
+ if (!mListenerManager) {
+ mListenerManager =
+ new EventListenerManager(static_cast<EventTarget*>(this));
+ }
+
+ return mListenerManager;
+}
+
+EventListenerManager* nsGlobalWindowInner::GetExistingListenerManager() const {
+ return mListenerManager;
+}
+
+mozilla::dom::DebuggerNotificationManager*
+nsGlobalWindowInner::GetOrCreateDebuggerNotificationManager() {
+ if (!mDebuggerNotificationManager) {
+ mDebuggerNotificationManager = new DebuggerNotificationManager(this);
+ }
+
+ return mDebuggerNotificationManager;
+}
+
+mozilla::dom::DebuggerNotificationManager*
+nsGlobalWindowInner::GetExistingDebuggerNotificationManager() {
+ return mDebuggerNotificationManager;
+}
+
+//*****************************************************************************
+// nsGlobalWindowInner::nsPIDOMWindow
+//*****************************************************************************
+
+Location* nsGlobalWindowInner::Location() {
+ if (!mLocation) {
+ mLocation = new dom::Location(this);
+ }
+
+ return mLocation;
+}
+
+void nsGlobalWindowInner::MaybeUpdateTouchState() {
+ if (mMayHaveTouchEventListener) {
+ nsCOMPtr<nsIObserverService> observerService =
+ services::GetObserverService();
+
+ if (observerService) {
+ observerService->NotifyObservers(static_cast<nsIDOMWindow*>(this),
+ DOM_TOUCH_LISTENER_ADDED, nullptr);
+ }
+ }
+}
+
+void nsGlobalWindowInner::EnableGamepadUpdates() {
+ if (mHasGamepad) {
+ RefPtr<GamepadManager> gamepadManager(GamepadManager::GetService());
+ if (gamepadManager) {
+ gamepadManager->AddListener(this);
+ }
+ }
+}
+
+void nsGlobalWindowInner::DisableGamepadUpdates() {
+ if (mHasGamepad) {
+ RefPtr<GamepadManager> gamepadManager(GamepadManager::GetService());
+ if (gamepadManager) {
+ gamepadManager->RemoveListener(this);
+ }
+ }
+}
+
+void nsGlobalWindowInner::EnableVRUpdates() {
+ // We need to create a VREventObserver before we can either detect XR runtimes
+ // or start an XR session
+ if (!mVREventObserver && (mHasXRSession || mXRRuntimeDetectionInFlight)) {
+ // Assert that we are not creating the observer while IsDying() as
+ // that would result in a leak. VREventObserver holds a RefPtr to
+ // this nsGlobalWindowInner and would prevent it from being deallocated.
+ MOZ_ASSERT(!IsDying(),
+ "Creating a VREventObserver for an nsGlobalWindow that is "
+ "dying would cause it to leak.");
+ mVREventObserver = new VREventObserver(this);
+ }
+ // If the content has an XR session, then we need to tell
+ // VREventObserver that there is VR activity.
+ if (mHasXRSession) {
+ nsPIDOMWindowOuter* outer = GetOuterWindow();
+ if (outer && !outer->IsBackground()) {
+ StartVRActivity();
+ }
+ }
+}
+
+void nsGlobalWindowInner::DisableVRUpdates() {
+ if (mVREventObserver) {
+ mVREventObserver->DisconnectFromOwner();
+ mVREventObserver = nullptr;
+ }
+}
+
+void nsGlobalWindowInner::ResetVRTelemetry(bool aUpdate) {
+ if (mVREventObserver) {
+ mVREventObserver->UpdateSpentTimeIn2DTelemetry(aUpdate);
+ }
+}
+
+void nsGlobalWindowInner::StartVRActivity() {
+ /**
+ * If the content has an XR session, tell
+ * the VREventObserver that the window is accessing
+ * VR devices.
+ *
+ * It's possible to have a VREventObserver without
+ * and XR session, if we are using it to get updates
+ * about XR runtime enumeration. In this case,
+ * we would not tell the VREventObserver that
+ * we are accessing VR devices.
+ */
+ if (mVREventObserver && mHasXRSession) {
+ mVREventObserver->StartActivity();
+ }
+}
+
+void nsGlobalWindowInner::StopVRActivity() {
+ /**
+ * If the content has an XR session, tell
+ * the VReventObserver that the window is no longer
+ * accessing VR devices. This does not stop the
+ * XR session itself, which may be resumed with
+ * EnableVRUpdates.
+ * It's possible to have a VREventObserver without
+ * and XR session, if we are using it to get updates
+ * about XR runtime enumeration. In this case,
+ * we would not tell the VREventObserver that
+ * we ending an activity that accesses VR devices.
+ */
+ if (mVREventObserver && mHasXRSession) {
+ mVREventObserver->StopActivity();
+ }
+}
+
+void nsGlobalWindowInner::SetFocusedElement(Element* aElement,
+ uint32_t aFocusMethod,
+ bool aNeedsFocus) {
+ if (aElement && aElement->GetComposedDoc() != mDoc) {
+ NS_WARNING("Trying to set focus to a node from a wrong document");
+ return;
+ }
+
+ if (IsDying()) {
+ NS_ASSERTION(!aElement, "Trying to focus cleaned up window!");
+ aElement = nullptr;
+ aNeedsFocus = false;
+ }
+ if (mFocusedElement != aElement) {
+ UpdateCanvasFocus(false, aElement);
+ mFocusedElement = aElement;
+ // TODO: Maybe this should be set on refocus too?
+ mFocusMethod = aFocusMethod & nsIFocusManager::METHOD_MASK;
+ }
+
+ if (mFocusedElement) {
+ // if a node was focused by a keypress, turn on focus rings for the
+ // window.
+ if (mFocusMethod & nsIFocusManager::FLAG_BYKEY) {
+ mUnknownFocusMethodShouldShowOutline = true;
+ mFocusByKeyOccurred = true;
+ } else if (nsFocusManager::GetFocusMoveActionCause(mFocusMethod) !=
+ widget::InputContextAction::CAUSE_UNKNOWN) {
+ mUnknownFocusMethodShouldShowOutline = false;
+ } else if (aFocusMethod & nsIFocusManager::FLAG_NOSHOWRING) {
+ // If we get focused via script, and script has explicitly opted out of
+ // outlines via FLAG_NOSHOWRING, we don't want to make a refocus start
+ // showing outlines.
+ mUnknownFocusMethodShouldShowOutline = false;
+ }
+ }
+
+ if (aNeedsFocus) {
+ mNeedsFocus = aNeedsFocus;
+ }
+}
+
+uint32_t nsGlobalWindowInner::GetFocusMethod() { return mFocusMethod; }
+
+bool nsGlobalWindowInner::ShouldShowFocusRing() {
+ if (mFocusByKeyOccurred &&
+ StaticPrefs::browser_display_always_show_rings_after_key_focus()) {
+ return true;
+ }
+ return StaticPrefs::browser_display_show_focus_rings();
+}
+
+bool nsGlobalWindowInner::TakeFocus(bool aFocus, uint32_t aFocusMethod) {
+ if (IsDying()) {
+ return false;
+ }
+
+ if (aFocus) {
+ mFocusMethod = aFocusMethod & nsIFocusManager::METHOD_MASK;
+ }
+
+ if (mHasFocus != aFocus) {
+ mHasFocus = aFocus;
+ UpdateCanvasFocus(true, mFocusedElement);
+ }
+
+ // if mNeedsFocus is true, then the document has not yet received a
+ // document-level focus event. If there is a root content node, then return
+ // true to tell the calling focus manager that a focus event is expected. If
+ // there is no root content node, the document hasn't loaded enough yet, or
+ // there isn't one and there is no point in firing a focus event.
+ if (aFocus && mNeedsFocus && mDoc && mDoc->GetRootElement() != nullptr) {
+ mNeedsFocus = false;
+ return true;
+ }
+
+ mNeedsFocus = false;
+ return false;
+}
+
+void nsGlobalWindowInner::SetReadyForFocus() {
+ bool oldNeedsFocus = mNeedsFocus;
+ mNeedsFocus = false;
+
+ if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
+ nsCOMPtr<nsPIDOMWindowOuter> outerWindow = GetOuterWindow();
+ fm->WindowShown(outerWindow, oldNeedsFocus);
+ }
+}
+
+void nsGlobalWindowInner::PageHidden() {
+ // the window is being hidden, so tell the focus manager that the frame is
+ // no longer valid. Use the persisted field to determine if the document
+ // is being destroyed.
+
+ if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
+ nsCOMPtr<nsPIDOMWindowOuter> outerWindow = GetOuterWindow();
+ fm->WindowHidden(outerWindow, nsFocusManager::GenerateFocusActionId());
+ }
+
+ mNeedsFocus = true;
+}
+
+class HashchangeCallback : public Runnable {
+ public:
+ HashchangeCallback(const nsAString& aOldURL, const nsAString& aNewURL,
+ nsGlobalWindowInner* aWindow)
+ : mozilla::Runnable("HashchangeCallback"), mWindow(aWindow) {
+ MOZ_ASSERT(mWindow);
+ mOldURL.Assign(aOldURL);
+ mNewURL.Assign(aNewURL);
+ }
+
+ NS_IMETHOD Run() override {
+ MOZ_ASSERT(NS_IsMainThread(), "Should be called on the main thread.");
+ return mWindow->FireHashchange(mOldURL, mNewURL);
+ }
+
+ private:
+ nsString mOldURL;
+ nsString mNewURL;
+ RefPtr<nsGlobalWindowInner> mWindow;
+};
+
+nsresult nsGlobalWindowInner::DispatchAsyncHashchange(nsIURI* aOldURI,
+ nsIURI* aNewURI) {
+ // Make sure that aOldURI and aNewURI are identical up to the '#', and that
+ // their hashes are different.
+ bool equal = false;
+ NS_ENSURE_STATE(NS_SUCCEEDED(aOldURI->EqualsExceptRef(aNewURI, &equal)) &&
+ equal);
+ nsAutoCString oldHash, newHash;
+ bool oldHasHash, newHasHash;
+ NS_ENSURE_STATE(NS_SUCCEEDED(aOldURI->GetRef(oldHash)) &&
+ NS_SUCCEEDED(aNewURI->GetRef(newHash)) &&
+ NS_SUCCEEDED(aOldURI->GetHasRef(&oldHasHash)) &&
+ NS_SUCCEEDED(aNewURI->GetHasRef(&newHasHash)) &&
+ (oldHasHash != newHasHash || !oldHash.Equals(newHash)));
+
+ nsAutoCString oldSpec, newSpec;
+ nsresult rv = aOldURI->GetSpec(oldSpec);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = aNewURI->GetSpec(newSpec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ConvertUTF8toUTF16 oldWideSpec(oldSpec);
+ NS_ConvertUTF8toUTF16 newWideSpec(newSpec);
+
+ nsCOMPtr<nsIRunnable> callback =
+ new HashchangeCallback(oldWideSpec, newWideSpec, this);
+ return Dispatch(callback.forget());
+}
+
+nsresult nsGlobalWindowInner::FireHashchange(const nsAString& aOldURL,
+ const nsAString& aNewURL) {
+ // Don't do anything if the window is frozen.
+ if (IsFrozen()) {
+ return NS_OK;
+ }
+
+ // Get a presentation shell for use in creating the hashchange event.
+ NS_ENSURE_STATE(IsCurrentInnerWindow());
+
+ HashChangeEventInit init;
+ init.mNewURL = aNewURL;
+ init.mOldURL = aOldURL;
+
+ RefPtr<HashChangeEvent> event =
+ HashChangeEvent::Constructor(this, u"hashchange"_ns, init);
+
+ event->SetTrusted(true);
+
+ ErrorResult rv;
+ DispatchEvent(*event, rv);
+ return rv.StealNSResult();
+}
+
+nsresult nsGlobalWindowInner::DispatchSyncPopState() {
+ NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
+ "Must be safe to run script here.");
+
+ // Bail if the window is frozen.
+ if (IsFrozen()) {
+ return NS_OK;
+ }
+
+ AutoJSAPI jsapi;
+ bool result = jsapi.Init(this);
+ NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
+
+ JSContext* cx = jsapi.cx();
+
+ // Get the document's pending state object -- it contains the data we're
+ // going to send along with the popstate event. The object is serialized
+ // using structured clone.
+ JS::Rooted<JS::Value> stateJSValue(cx);
+ nsresult rv = mDoc->GetStateObject(&stateJSValue);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!JS_WrapValue(cx, &stateJSValue)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ RootedDictionary<PopStateEventInit> init(cx);
+ init.mState = stateJSValue;
+
+ RefPtr<PopStateEvent> event =
+ PopStateEvent::Constructor(this, u"popstate"_ns, init);
+ event->SetTrusted(true);
+ event->SetTarget(this);
+
+ ErrorResult err;
+ DispatchEvent(*event, err);
+ return err.StealNSResult();
+}
+
+//-------------------------------------------------------
+// Tells the HTMLFrame/CanvasFrame that is now has focus
+void nsGlobalWindowInner::UpdateCanvasFocus(bool aFocusChanged,
+ nsIContent* aNewContent) {
+ // this is called from the inner window so use GetDocShell
+ nsIDocShell* docShell = GetDocShell();
+ if (!docShell) return;
+
+ bool editable;
+ docShell->GetEditable(&editable);
+ if (editable) return;
+
+ PresShell* presShell = docShell->GetPresShell();
+ if (!presShell || !mDoc) {
+ return;
+ }
+
+ Element* rootElement = mDoc->GetRootElement();
+ if (rootElement) {
+ if ((mHasFocus || aFocusChanged) &&
+ (mFocusedElement == rootElement || aNewContent == rootElement)) {
+ nsCanvasFrame* canvasFrame = presShell->GetCanvasFrame();
+ if (canvasFrame) {
+ canvasFrame->SetHasFocus(mHasFocus && rootElement == aNewContent);
+ }
+ }
+ } else {
+ // XXXbz I would expect that there is never a canvasFrame in this case...
+ nsCanvasFrame* canvasFrame = presShell->GetCanvasFrame();
+ if (canvasFrame) {
+ canvasFrame->SetHasFocus(false);
+ }
+ }
+}
+
+already_AddRefed<nsICSSDeclaration> nsGlobalWindowInner::GetComputedStyle(
+ Element& aElt, const nsAString& aPseudoElt, ErrorResult& aError) {
+ return GetComputedStyleHelper(aElt, aPseudoElt, false, aError);
+}
+
+already_AddRefed<nsICSSDeclaration>
+nsGlobalWindowInner::GetDefaultComputedStyle(Element& aElt,
+ const nsAString& aPseudoElt,
+ ErrorResult& aError) {
+ return GetComputedStyleHelper(aElt, aPseudoElt, true, aError);
+}
+
+already_AddRefed<nsICSSDeclaration> nsGlobalWindowInner::GetComputedStyleHelper(
+ Element& aElt, const nsAString& aPseudoElt, bool aDefaultStylesOnly,
+ ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(GetComputedStyleHelperOuter,
+ (aElt, aPseudoElt, aDefaultStylesOnly, aError),
+ aError, nullptr);
+}
+
+Storage* nsGlobalWindowInner::GetSessionStorage(ErrorResult& aError) {
+ nsIPrincipal* principal = GetPrincipal();
+ nsIPrincipal* storagePrincipal;
+ if (StaticPrefs::
+ privacy_partition_always_partition_third_party_non_cookie_storage_exempt_sessionstorage()) {
+ storagePrincipal = GetEffectiveCookiePrincipal();
+ } else {
+ storagePrincipal = GetEffectiveStoragePrincipal();
+ }
+ BrowsingContext* browsingContext = GetBrowsingContext();
+
+ if (!principal || !storagePrincipal || !browsingContext ||
+ !Storage::StoragePrefIsEnabled()) {
+ return nullptr;
+ }
+
+ if (mSessionStorage) {
+ MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
+ ("nsGlobalWindowInner %p has %p sessionStorage", this,
+ mSessionStorage.get()));
+ bool canAccess =
+ principal->Subsumes(mSessionStorage->Principal()) &&
+ storagePrincipal->Subsumes(mSessionStorage->StoragePrincipal());
+ if (!canAccess) {
+ mSessionStorage = nullptr;
+ }
+ }
+
+ if (!mSessionStorage) {
+ nsString documentURI;
+ if (mDoc) {
+ aError = mDoc->GetDocumentURI(documentURI);
+ if (NS_WARN_IF(aError.Failed())) {
+ return nullptr;
+ }
+ }
+
+ if (!mDoc) {
+ aError.Throw(NS_ERROR_FAILURE);
+ return nullptr;
+ }
+
+ // If the document's sandboxed origin flag is set, then accessing
+ // sessionStorage is prohibited.
+ if (mDoc->GetSandboxFlags() & SANDBOXED_ORIGIN) {
+ aError.ThrowSecurityError(
+ "Forbidden in a sandboxed document without the 'allow-same-origin' "
+ "flag.");
+ return nullptr;
+ }
+
+ uint32_t rejectedReason = 0;
+ StorageAccess access = StorageAllowedForWindow(this, &rejectedReason);
+
+ // SessionStorage is an ephemeral per-tab per-origin storage that only lives
+ // as long as the tab is open, although it may survive browser restarts
+ // thanks to the session store. So we interpret storage access differently
+ // than we would for persistent per-origin storage like LocalStorage and so
+ // it may be okay to provide SessionStorage even when we receive a value of
+ // eDeny.
+ //
+ // ContentBlocking::ShouldAllowAccessFor will return false for 3 main
+ // reasons.
+ //
+ // 1. Cookies are entirely blocked due to a per-origin permission
+ // (nsICookiePermission::ACCESS_DENY for the top-level principal or this
+ // window's principal) or the very broad BEHAVIOR_REJECT. This will return
+ // eDeny with a reason of STATE_COOKIES_BLOCKED_BY_PERMISSION or
+ // STATE_COOKIES_BLOCKED_ALL.
+ //
+ // 2. Third-party cookies are limited via BEHAVIOR_REJECT_FOREIGN and
+ // BEHAVIOR_LIMIT_FOREIGN and this is a third-party window. This will return
+ // eDeny with a reason of STATE_COOKIES_BLOCKED_FOREIGN.
+ //
+ // 3. Tracking protection (BEHAVIOR_REJECT_TRACKER and
+ // BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN) is in effect and
+ // IsThirdPartyTrackingResourceWindow() returned true and there wasn't a
+ // permission that allows it. This will return ePartitionTrackersOrDeny with
+ // a reason of STATE_COOKIES_BLOCKED_TRACKER or
+ // STATE_COOKIES_BLOCKED_SOCIALTRACKER.
+ //
+ // In the 1st case, the user has explicitly indicated that they don't want
+ // to allow any storage to the origin or all origins and so we throw an
+ // error and deny access to SessionStorage. In the 2nd case, a legacy
+ // decision reasoned that there's no harm in providing SessionStorage
+ // because the information is not durable and cannot escape the current tab.
+ // The rationale is similar for the 3rd case.
+ if (access == StorageAccess::eDeny &&
+ rejectedReason !=
+ nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN) {
+ aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
+ return nullptr;
+ }
+
+ const RefPtr<SessionStorageManager> storageManager =
+ browsingContext->GetSessionStorageManager();
+ if (!storageManager) {
+ aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+ return nullptr;
+ }
+
+ RefPtr<Storage> storage;
+ aError = storageManager->CreateStorage(this, principal, storagePrincipal,
+ documentURI, IsPrivateBrowsing(),
+ getter_AddRefs(storage));
+ if (aError.Failed()) {
+ return nullptr;
+ }
+
+ mSessionStorage = storage;
+ MOZ_ASSERT(mSessionStorage);
+
+ MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
+ ("nsGlobalWindowInner %p tried to get a new sessionStorage %p",
+ this, mSessionStorage.get()));
+
+ if (!mSessionStorage) {
+ aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+ return nullptr;
+ }
+ }
+
+ MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
+ ("nsGlobalWindowInner %p returns %p sessionStorage", this,
+ mSessionStorage.get()));
+
+ return mSessionStorage;
+}
+
+Storage* nsGlobalWindowInner::GetLocalStorage(ErrorResult& aError) {
+ if (!Storage::StoragePrefIsEnabled()) {
+ return nullptr;
+ }
+
+ // If the document's sandboxed origin flag is set, then accessing localStorage
+ // is prohibited.
+ if (mDoc && mDoc->GetSandboxFlags() & SANDBOXED_ORIGIN) {
+ aError.ThrowSecurityError(
+ "Forbidden in a sandboxed document without the 'allow-same-origin' "
+ "flag.");
+ return nullptr;
+ }
+
+ // LocalStorage needs to be exposed in every context except for sandboxes and
+ // NullPrincipals (data: URLs, for instance). But we need to keep data
+ // separate in some scenarios: private-browsing and partitioned trackers.
+ // In private-browsing, LocalStorage keeps data in memory, and it shares
+ // StorageEvents just with other origins in the same private-browsing
+ // environment.
+ // For Partitioned Trackers, we expose a partitioned LocalStorage, which
+ // doesn't share data with other contexts, and it's just in memory.
+ // Partitioned localStorage is available only for trackers listed in the
+ // privacy.restrict3rdpartystorage.partitionedHosts pref. See
+ // nsContentUtils::IsURIInPrefList to know the syntax for the pref value.
+ // This is a temporary web-compatibility hack.
+
+ StorageAccess access = StorageAllowedForWindow(this);
+
+ // We allow partitioned localStorage only to some hosts.
+ bool isolated = false;
+ if (ShouldPartitionStorage(access)) {
+ if (!mDoc) {
+ access = StorageAccess::eDeny;
+ } else if (!StoragePartitioningEnabled(access, mDoc->CookieJarSettings())) {
+ static const char* kPrefName =
+ "privacy.restrict3rdpartystorage.partitionedHosts";
+
+ bool isInList = false;
+ mDoc->NodePrincipal()->IsURIInPrefList(kPrefName, &isInList);
+ if (!isInList) {
+ access = StorageAccess::eDeny;
+ } else {
+ isolated = true;
+ }
+ }
+ }
+
+ if (access == StorageAccess::eDeny) {
+ aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
+ return nullptr;
+ }
+
+ nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
+ if (mDoc) {
+ cookieJarSettings = mDoc->CookieJarSettings();
+ } else {
+ cookieJarSettings = net::CookieJarSettings::GetBlockingAll(
+ ShouldResistFingerprinting(RFPTarget::IsAlwaysEnabledForPrecompute));
+ }
+
+ // Note that this behavior is observable: if we grant storage permission to a
+ // tracker, we pass from the partitioned LocalStorage (or a partitioned cookie
+ // jar) to the 'normal' one. The previous data is lost and the 2
+ // window.localStorage objects, before and after the permission granted, will
+ // be different.
+ if (mLocalStorage) {
+ if ((mLocalStorage->Type() == (isolated ? Storage::ePartitionedLocalStorage
+ : Storage::eLocalStorage)) &&
+ (mLocalStorage->StoragePrincipal() == GetEffectiveStoragePrincipal())) {
+ return mLocalStorage;
+ }
+
+ // storage needs change
+ mLocalStorage = nullptr;
+ }
+
+ MOZ_ASSERT(!mLocalStorage);
+
+ if (!isolated) {
+ RefPtr<Storage> storage;
+
+ if (NextGenLocalStorageEnabled()) {
+ aError = LSObject::CreateForWindow(this, getter_AddRefs(storage));
+ } else {
+ nsresult rv;
+ nsCOMPtr<nsIDOMStorageManager> storageManager =
+ do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv);
+ if (NS_FAILED(rv)) {
+ aError.Throw(rv);
+ return nullptr;
+ }
+
+ nsString documentURI;
+ if (mDoc) {
+ aError = mDoc->GetDocumentURI(documentURI);
+ if (NS_WARN_IF(aError.Failed())) {
+ return nullptr;
+ }
+ }
+
+ nsIPrincipal* principal = GetPrincipal();
+ if (!principal) {
+ aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
+ return nullptr;
+ }
+
+ nsIPrincipal* storagePrincipal = GetEffectiveStoragePrincipal();
+ if (!storagePrincipal) {
+ aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
+ return nullptr;
+ }
+
+ aError = storageManager->CreateStorage(this, principal, storagePrincipal,
+ documentURI, IsPrivateBrowsing(),
+ getter_AddRefs(storage));
+ }
+
+ if (aError.Failed()) {
+ return nullptr;
+ }
+
+ mLocalStorage = storage;
+ } else {
+ nsresult rv;
+ nsCOMPtr<nsIDOMSessionStorageManager> storageManager =
+ do_GetService("@mozilla.org/dom/sessionStorage-manager;1", &rv);
+ if (NS_FAILED(rv)) {
+ aError.Throw(rv);
+ return nullptr;
+ }
+
+ nsIPrincipal* principal = GetPrincipal();
+ if (!principal) {
+ aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
+ return nullptr;
+ }
+
+ nsIPrincipal* storagePrincipal = GetEffectiveStoragePrincipal();
+ if (!storagePrincipal) {
+ aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
+ return nullptr;
+ }
+
+ RefPtr<SessionStorageCache> cache;
+ if (isolated) {
+ cache = new SessionStorageCache();
+ } else {
+ // This will clone the session storage if it exists.
+ rv = storageManager->GetSessionStorageCache(principal, storagePrincipal,
+ &cache);
+ if (NS_FAILED(rv)) {
+ aError.Throw(rv);
+ return nullptr;
+ }
+ }
+
+ mLocalStorage =
+ new PartitionedLocalStorage(this, principal, storagePrincipal, cache);
+ }
+
+ MOZ_ASSERT(mLocalStorage);
+ MOZ_ASSERT(
+ mLocalStorage->Type() ==
+ (isolated ? Storage::ePartitionedLocalStorage : Storage::eLocalStorage));
+ return mLocalStorage;
+}
+
+IDBFactory* nsGlobalWindowInner::GetIndexedDB(JSContext* aCx,
+ ErrorResult& aError) {
+ if (!mIndexedDB) {
+ // This may keep mIndexedDB null without setting an error.
+ auto res = IDBFactory::CreateForWindow(this);
+ if (res.isErr()) {
+ aError = res.unwrapErr();
+ } else {
+ mIndexedDB = res.unwrap();
+ }
+ }
+
+ return mIndexedDB;
+}
+
+//*****************************************************************************
+// nsGlobalWindowInner::nsIInterfaceRequestor
+//*****************************************************************************
+
+NS_IMETHODIMP
+nsGlobalWindowInner::GetInterface(const nsIID& aIID, void** aSink) {
+ nsGlobalWindowOuter* outer = GetOuterWindowInternal();
+ NS_ENSURE_TRUE(outer, NS_ERROR_NOT_INITIALIZED);
+
+ nsresult rv = outer->GetInterfaceInternal(aIID, aSink);
+ if (rv == NS_ERROR_NO_INTERFACE) {
+ return QueryInterface(aIID, aSink);
+ }
+ return rv;
+}
+
+void nsGlobalWindowInner::GetInterface(JSContext* aCx,
+ JS::Handle<JS::Value> aIID,
+ JS::MutableHandle<JS::Value> aRetval,
+ ErrorResult& aError) {
+ dom::GetInterface(aCx, this, aIID, aRetval, aError);
+}
+
+already_AddRefed<CacheStorage> nsGlobalWindowInner::GetCaches(
+ ErrorResult& aRv) {
+ if (!mCacheStorage) {
+ bool forceTrustedOrigin =
+ GetBrowsingContext() &&
+ GetBrowsingContext()->Top()->GetServiceWorkersTestingEnabled();
+ mCacheStorage = CacheStorage::CreateOnMainThread(
+ cache::DEFAULT_NAMESPACE, this, GetEffectiveStoragePrincipal(),
+ forceTrustedOrigin, aRv);
+ }
+
+ RefPtr<CacheStorage> ref = mCacheStorage;
+ return ref.forget();
+}
+
+void nsGlobalWindowInner::FireOfflineStatusEventIfChanged() {
+ if (!IsCurrentInnerWindow()) return;
+
+ // Don't fire an event if the status hasn't changed
+ if (mWasOffline == NS_IsOffline()) {
+ return;
+ }
+
+ mWasOffline = !mWasOffline;
+
+ nsAutoString name;
+ if (mWasOffline) {
+ name.AssignLiteral("offline");
+ } else {
+ name.AssignLiteral("online");
+ }
+ nsContentUtils::DispatchTrustedEvent(mDoc, this, name, CanBubble::eNo,
+ Cancelable::eNo);
+}
+
+nsGlobalWindowInner::SlowScriptResponse
+nsGlobalWindowInner::ShowSlowScriptDialog(JSContext* aCx,
+ const nsString& aAddonId,
+ const double aDuration) {
+ nsresult rv;
+
+ if (Preferences::GetBool("dom.always_stop_slow_scripts")) {
+ return KillSlowScript;
+ }
+
+ // If it isn't safe to run script, then it isn't safe to bring up the prompt
+ // (since that spins the event loop). In that (rare) case, we just kill the
+ // script and report a warning.
+ if (!nsContentUtils::IsSafeToRunScript()) {
+ JS::WarnASCII(aCx, "A long running script was terminated");
+ return KillSlowScript;
+ }
+
+ // If our document is not active, just kill the script: we've been unloaded
+ if (!HasActiveDocument()) {
+ return KillSlowScript;
+ }
+
+ // Check if we should offer the option to debug
+ JS::AutoFilename filename;
+ uint32_t lineno;
+ // Computing the line number can be very expensive (see bug 1330231 for
+ // example), and we don't use the line number anywhere except than in the
+ // parent process, so we avoid computing it elsewhere. This gives us most of
+ // the wins we are interested in, since the source of the slowness here is
+ // minified scripts which is more common in Web content that is loaded in the
+ // content process.
+ uint32_t* linenop = XRE_IsParentProcess() ? &lineno : nullptr;
+ bool hasFrame = JS::DescribeScriptedCaller(aCx, &filename, linenop);
+
+ // Record the slow script event if we haven't done so already for this inner
+ // window (which represents a particular page to the user).
+ if (!mHasHadSlowScript) {
+ Telemetry::Accumulate(Telemetry::SLOW_SCRIPT_PAGE_COUNT, 1);
+ }
+ mHasHadSlowScript = true;
+
+ // Override the cursor to something that we're sure the user can see.
+ SetCursor("auto"_ns, IgnoreErrors());
+
+ if (XRE_IsContentProcess() && ProcessHangMonitor::Get()) {
+ ProcessHangMonitor::SlowScriptAction action;
+ RefPtr<ProcessHangMonitor> monitor = ProcessHangMonitor::Get();
+ nsIDocShell* docShell = GetDocShell();
+ nsCOMPtr<nsIBrowserChild> child =
+ docShell ? docShell->GetBrowserChild() : nullptr;
+ action =
+ monitor->NotifySlowScript(child, filename.get(), aAddonId, aDuration);
+ if (action == ProcessHangMonitor::Terminate) {
+ return KillSlowScript;
+ }
+
+ if (action == ProcessHangMonitor::StartDebugger) {
+ // Spin a nested event loop so that the debugger in the parent can fetch
+ // any information it needs. Once the debugger has started, return to the
+ // script.
+ RefPtr<nsGlobalWindowOuter> outer = GetOuterWindowInternal();
+ outer->EnterModalState();
+ SpinEventLoopUntil("nsGlobalWindowInner::ShowSlowScriptDialog"_ns, [&]() {
+ return monitor->IsDebuggerStartupComplete();
+ });
+ outer->LeaveModalState();
+ return ContinueSlowScript;
+ }
+
+ return ContinueSlowScriptAndKeepNotifying;
+ }
+
+ // Reached only on non-e10s - once per slow script dialog.
+ // On e10s - we probe once at ProcessHangsMonitor.jsm
+ Telemetry::Accumulate(Telemetry::SLOW_SCRIPT_NOTICE_COUNT, 1);
+
+ // Get the nsIPrompt interface from the docshell
+ nsCOMPtr<nsIDocShell> ds = GetDocShell();
+ NS_ENSURE_TRUE(ds, KillSlowScript);
+ nsCOMPtr<nsIPrompt> prompt = do_GetInterface(ds);
+ NS_ENSURE_TRUE(prompt, KillSlowScript);
+
+ // Prioritize the SlowScriptDebug interface over JSD1.
+ nsCOMPtr<nsISlowScriptDebugCallback> debugCallback;
+
+ if (hasFrame) {
+ const char* debugCID = "@mozilla.org/dom/slow-script-debug;1";
+ nsCOMPtr<nsISlowScriptDebug> debugService = do_GetService(debugCID, &rv);
+ if (NS_SUCCEEDED(rv)) {
+ debugService->GetActivationHandler(getter_AddRefs(debugCallback));
+ }
+ }
+
+ bool failed = false;
+ auto getString = [&](const char* name,
+ nsContentUtils::PropertiesFile propFile =
+ nsContentUtils::eDOM_PROPERTIES) {
+ nsAutoString result;
+ nsresult rv = nsContentUtils::GetLocalizedString(propFile, name, result);
+
+ // GetStringFromName can return NS_OK and still give nullptr string
+ failed = failed || NS_FAILED(rv) || result.IsEmpty();
+ return result;
+ };
+
+ bool isAddonScript = !aAddonId.IsEmpty();
+ bool showDebugButton = debugCallback && !isAddonScript;
+
+ // Get localizable strings
+
+ nsAutoString title, checkboxMsg, debugButton, msg;
+ if (isAddonScript) {
+ title = getString("KillAddonScriptTitle");
+ checkboxMsg = getString("KillAddonScriptGlobalMessage");
+
+ auto appName =
+ getString("brandShortName", nsContentUtils::eBRAND_PROPERTIES);
+
+ nsCOMPtr<nsIAddonPolicyService> aps =
+ do_GetService("@mozilla.org/addons/policy-service;1");
+ nsString addonName;
+ if (!aps || NS_FAILED(aps->GetExtensionName(aAddonId, addonName))) {
+ addonName = aAddonId;
+ }
+
+ rv = nsContentUtils::FormatLocalizedString(
+ msg, nsContentUtils::eDOM_PROPERTIES, "KillAddonScriptMessage",
+ addonName, appName);
+
+ failed = failed || NS_FAILED(rv);
+ } else {
+ title = getString("KillScriptTitle");
+ checkboxMsg = getString("DontAskAgain");
+
+ if (showDebugButton) {
+ debugButton = getString("DebugScriptButton");
+ msg = getString("KillScriptWithDebugMessage");
+ } else {
+ msg = getString("KillScriptMessage");
+ }
+ }
+
+ auto stopButton = getString("StopScriptButton");
+ auto waitButton = getString("WaitForScriptButton");
+
+ if (failed) {
+ NS_ERROR("Failed to get localized strings.");
+ return ContinueSlowScript;
+ }
+
+ // Append file and line number information, if available
+ if (filename.get()) {
+ nsAutoString scriptLocation;
+ // We want to drop the middle part of too-long locations. We'll
+ // define "too-long" as longer than 60 UTF-16 code units. Just
+ // have to be a bit careful about unpaired surrogates.
+ NS_ConvertUTF8toUTF16 filenameUTF16(filename.get());
+ if (filenameUTF16.Length() > 60) {
+ // XXXbz Do we need to insert any bidi overrides here?
+ size_t cutStart = 30;
+ size_t cutLength = filenameUTF16.Length() - 60;
+ MOZ_ASSERT(cutLength > 0);
+ if (NS_IS_LOW_SURROGATE(filenameUTF16[cutStart])) {
+ // Don't truncate before the low surrogate, in case it's preceded by a
+ // high surrogate and forms a single Unicode character. Instead, just
+ // include the low surrogate.
+ ++cutStart;
+ --cutLength;
+ }
+ if (NS_IS_LOW_SURROGATE(filenameUTF16[cutStart + cutLength])) {
+ // Likewise, don't drop a trailing low surrogate here. We want to
+ // increase cutLength, since it might be 0 already so we can't very well
+ // decrease it.
+ ++cutLength;
+ }
+
+ // Insert U+2026 HORIZONTAL ELLIPSIS
+ filenameUTF16.ReplaceLiteral(cutStart, cutLength, u"\x2026");
+ }
+ rv = nsContentUtils::FormatLocalizedString(
+ scriptLocation, nsContentUtils::eDOM_PROPERTIES, "KillScriptLocation",
+ filenameUTF16);
+
+ if (NS_SUCCEEDED(rv)) {
+ msg.AppendLiteral("\n\n");
+ msg.Append(scriptLocation);
+ msg.Append(':');
+ msg.AppendInt(lineno);
+ }
+ }
+
+ uint32_t buttonFlags = nsIPrompt::BUTTON_POS_1_DEFAULT +
+ (nsIPrompt::BUTTON_TITLE_IS_STRING *
+ (nsIPrompt::BUTTON_POS_0 + nsIPrompt::BUTTON_POS_1));
+
+ // Add a third button if necessary.
+ if (showDebugButton)
+ buttonFlags += nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_2;
+
+ bool checkboxValue = false;
+ int32_t buttonPressed = 0; // In case the user exits dialog by clicking X.
+ {
+ // Null out the operation callback while we're re-entering JS here.
+ AutoDisableJSInterruptCallback disabler(aCx);
+
+ // Open the dialog.
+ rv = prompt->ConfirmEx(
+ title.get(), msg.get(), buttonFlags, waitButton.get(), stopButton.get(),
+ debugButton.get(), checkboxMsg.get(), &checkboxValue, &buttonPressed);
+ }
+
+ if (buttonPressed == 0) {
+ if (checkboxValue && !isAddonScript && NS_SUCCEEDED(rv))
+ return AlwaysContinueSlowScript;
+ return ContinueSlowScript;
+ }
+
+ if (buttonPressed == 2) {
+ MOZ_RELEASE_ASSERT(debugCallback);
+
+ rv = debugCallback->HandleSlowScriptDebug(this);
+ return NS_SUCCEEDED(rv) ? ContinueSlowScript : KillSlowScript;
+ }
+
+ JS_ClearPendingException(aCx);
+
+ return KillSlowScript;
+}
+
+nsresult nsGlobalWindowInner::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData) {
+ if (!nsCRT::strcmp(aTopic, NS_IOSERVICE_OFFLINE_STATUS_TOPIC)) {
+ if (!IsFrozen()) {
+ // Fires an offline status event if the offline status has changed
+ FireOfflineStatusEventIfChanged();
+ }
+ return NS_OK;
+ }
+
+ if (!nsCRT::strcmp(aTopic, MEMORY_PRESSURE_OBSERVER_TOPIC)) {
+ if (mPerformance) {
+ mPerformance->MemoryPressure();
+ }
+ RemoveReportRecords();
+ return NS_OK;
+ }
+
+ if (!nsCRT::strcmp(aTopic, PERMISSION_CHANGED_TOPIC)) {
+ nsCOMPtr<nsIPermission> perm(do_QueryInterface(aSubject));
+ if (!perm) {
+ // A null permission indicates that the entire permission list
+ // was cleared.
+ MOZ_ASSERT(!nsCRT::strcmp(aData, u"cleared"));
+ UpdatePermissions();
+ return NS_OK;
+ }
+
+ nsAutoCString type;
+ perm->GetType(type);
+ if (type == "autoplay-media"_ns) {
+ UpdateAutoplayPermission();
+ } else if (type == "shortcuts"_ns) {
+ UpdateShortcutsPermission();
+ } else if (type == "popup"_ns) {
+ UpdatePopupPermission();
+ }
+
+ if (!mDoc) {
+ return NS_OK;
+ }
+
+ RefPtr<PermissionDelegateHandler> permDelegateHandler =
+ mDoc->GetPermissionDelegateHandler();
+
+ if (permDelegateHandler) {
+ permDelegateHandler->UpdateDelegatedPermission(type);
+ }
+
+ return NS_OK;
+ }
+
+ if (!nsCRT::strcmp(aTopic, "screen-information-changed")) {
+ if (mScreen) {
+ if (RefPtr<ScreenOrientation> orientation =
+ mScreen->GetOrientationIfExists()) {
+ orientation->MaybeChanged();
+ }
+ }
+ if (mHasOrientationChangeListeners) {
+ int32_t oldAngle = mOrientationAngle;
+ mOrientationAngle = Orientation(CallerType::System);
+ if (mOrientationAngle != oldAngle && IsCurrentInnerWindow()) {
+ nsCOMPtr<nsPIDOMWindowOuter> outer = GetOuterWindow();
+ outer->DispatchCustomEvent(u"orientationchange"_ns);
+ }
+ }
+ return NS_OK;
+ }
+
+ if (!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
+ MOZ_ASSERT(!NS_strcmp(aData, u"intl.accept_languages"));
+
+ // The user preferred languages have changed, we need to fire an event on
+ // Window object and invalidate the cache for navigator.languages. It is
+ // done for every change which can be a waste of cycles but those should be
+ // fairly rare.
+ // We MUST invalidate navigator.languages before sending the event in the
+ // very likely situation where an event handler will try to read its value.
+
+ if (mNavigator) {
+ Navigator_Binding::ClearCachedLanguageValue(mNavigator);
+ Navigator_Binding::ClearCachedLanguagesValue(mNavigator);
+ }
+
+ // The event has to be dispatched only to the current inner window.
+ if (!IsCurrentInnerWindow()) {
+ return NS_OK;
+ }
+
+ RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
+ event->InitEvent(u"languagechange"_ns, false, false);
+ event->SetTrusted(true);
+
+ ErrorResult rv;
+ DispatchEvent(*event, rv);
+ return rv.StealNSResult();
+ }
+
+ NS_WARNING("unrecognized topic in nsGlobalWindowInner::Observe");
+ return NS_ERROR_FAILURE;
+}
+
+void nsGlobalWindowInner::ObserveStorageNotification(
+ StorageEvent* aEvent, const char16_t* aStorageType, bool aPrivateBrowsing) {
+ MOZ_ASSERT(aEvent);
+
+ // The private browsing check must be done here again because this window
+ // could have changed its state before the notification check and now. This
+ // happens in case this window did have a docShell at that time.
+ if (aPrivateBrowsing != IsPrivateBrowsing()) {
+ return;
+ }
+
+ // LocalStorage can only exist on an inner window, and we don't want to
+ // generate events on frozen or otherwise-navigated-away from windows.
+ // (Actually, this code used to try and buffer events for frozen windows,
+ // but it never worked, so we've removed it. See bug 1285898.)
+ if (!IsCurrentInnerWindow() || IsFrozen()) {
+ return;
+ }
+
+ nsIPrincipal* principal = GetPrincipal();
+ if (!principal) {
+ return;
+ }
+
+ bool fireMozStorageChanged = false;
+ nsAutoString eventType;
+ eventType.AssignLiteral("storage");
+
+ if (!NS_strcmp(aStorageType, u"sessionStorage")) {
+ RefPtr<Storage> changingStorage = aEvent->GetStorageArea();
+ MOZ_ASSERT(changingStorage);
+
+ bool check = false;
+
+ if (const RefPtr<SessionStorageManager> storageManager =
+ GetBrowsingContext()->GetSessionStorageManager()) {
+ nsresult rv = storageManager->CheckStorage(GetEffectiveStoragePrincipal(),
+ changingStorage, &check);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ }
+
+ if (!check) {
+ // This storage event is not coming from our storage or is coming
+ // from a different docshell, i.e. it is a clone, ignore this event.
+ return;
+ }
+
+ MOZ_LOG(
+ gDOMLeakPRLogInner, LogLevel::Debug,
+ ("nsGlobalWindowInner %p with sessionStorage %p passing event from %p",
+ this, mSessionStorage.get(), changingStorage.get()));
+
+ fireMozStorageChanged = mSessionStorage == changingStorage;
+ if (fireMozStorageChanged) {
+ eventType.AssignLiteral("MozSessionStorageChanged");
+ }
+ }
+
+ else {
+ MOZ_ASSERT(!NS_strcmp(aStorageType, u"localStorage"));
+
+ nsIPrincipal* storagePrincipal = GetEffectiveStoragePrincipal();
+ if (!storagePrincipal) {
+ return;
+ }
+
+ MOZ_DIAGNOSTIC_ASSERT(StorageUtils::PrincipalsEqual(aEvent->GetPrincipal(),
+ storagePrincipal));
+
+ fireMozStorageChanged =
+ mLocalStorage && mLocalStorage == aEvent->GetStorageArea();
+
+ if (fireMozStorageChanged) {
+ eventType.AssignLiteral("MozLocalStorageChanged");
+ }
+ }
+
+ // Clone the storage event included in the observer notification. We want
+ // to dispatch clones rather than the original event.
+ IgnoredErrorResult error;
+ RefPtr<StorageEvent> clonedEvent =
+ CloneStorageEvent(eventType, aEvent, error);
+ if (error.Failed() || !clonedEvent) {
+ return;
+ }
+
+ clonedEvent->SetTrusted(true);
+
+ if (fireMozStorageChanged) {
+ WidgetEvent* internalEvent = clonedEvent->WidgetEventPtr();
+ internalEvent->mFlags.mOnlyChromeDispatch = true;
+ }
+
+ DispatchEvent(*clonedEvent);
+}
+
+already_AddRefed<StorageEvent> nsGlobalWindowInner::CloneStorageEvent(
+ const nsAString& aType, const RefPtr<StorageEvent>& aEvent,
+ ErrorResult& aRv) {
+ StorageEventInit dict;
+
+ dict.mBubbles = aEvent->Bubbles();
+ dict.mCancelable = aEvent->Cancelable();
+ aEvent->GetKey(dict.mKey);
+ aEvent->GetOldValue(dict.mOldValue);
+ aEvent->GetNewValue(dict.mNewValue);
+ aEvent->GetUrl(dict.mUrl);
+
+ RefPtr<Storage> storageArea = aEvent->GetStorageArea();
+
+ RefPtr<Storage> storage;
+
+ // If null, this is a localStorage event received by IPC.
+ if (!storageArea) {
+ storage = GetLocalStorage(aRv);
+ if (!NextGenLocalStorageEnabled()) {
+ if (aRv.Failed() || !storage) {
+ return nullptr;
+ }
+
+ if (storage->Type() == Storage::eLocalStorage) {
+ RefPtr<LocalStorage> localStorage =
+ static_cast<LocalStorage*>(storage.get());
+
+ // We must apply the current change to the 'local' localStorage.
+ localStorage->ApplyEvent(aEvent);
+ }
+ }
+ } else if (storageArea->Type() == Storage::eSessionStorage) {
+ storage = GetSessionStorage(aRv);
+ } else {
+ MOZ_ASSERT(storageArea->Type() == Storage::eLocalStorage);
+ storage = GetLocalStorage(aRv);
+ }
+
+ if (aRv.Failed() || !storage) {
+ return nullptr;
+ }
+
+ if (storage->Type() == Storage::ePartitionedLocalStorage) {
+ // This error message is not exposed.
+ aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+ return nullptr;
+ }
+
+ MOZ_ASSERT(storage);
+ MOZ_ASSERT_IF(storageArea, storage->IsForkOf(storageArea));
+
+ dict.mStorageArea = storage;
+
+ RefPtr<StorageEvent> event = StorageEvent::Constructor(this, aType, dict);
+ return event.forget();
+}
+
+void nsGlobalWindowInner::Suspend(bool aIncludeSubWindows) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // We can only safely suspend windows that are the current inner window. If
+ // its not the current inner, then we are in one of two different cases.
+ // Either we are in the bfcache or we are doomed window that is going away.
+ // When a window becomes inactive we purposely avoid placing already suspended
+ // windows into the bfcache. It only expects windows suspended due to the
+ // Freeze() method which occurs while the window is still the current inner.
+ // So we must not call Suspend() on bfcache windows at this point or this
+ // invariant will be broken. If the window is doomed there is no point in
+ // suspending it since it will soon be gone.
+ if (!IsCurrentInnerWindow()) {
+ return;
+ }
+
+ // All in-process descendants are also suspended. This ensure mSuspendDepth
+ // is set properly and the timers are properly canceled for each in-process
+ // descendant.
+ if (aIncludeSubWindows) {
+ CallOnInProcessDescendants(&nsGlobalWindowInner::Suspend, false);
+ }
+
+ mSuspendDepth += 1;
+ if (mSuspendDepth != 1) {
+ return;
+ }
+
+ if (mWindowGlobalChild) {
+ mWindowGlobalChild->BlockBFCacheFor(BFCacheStatus::SUSPENDED);
+ }
+
+ nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
+ if (ac) {
+ for (uint32_t i = 0; i < mEnabledSensors.Length(); i++)
+ ac->RemoveWindowListener(mEnabledSensors[i], this);
+ }
+ DisableGamepadUpdates();
+ DisableVRUpdates();
+
+ SuspendWorkersForWindow(*this);
+
+ for (RefPtr<mozilla::dom::SharedWorker> pinnedWorker :
+ mSharedWorkers.ForwardRange()) {
+ pinnedWorker->Suspend();
+ }
+
+ SuspendIdleRequests();
+
+ mTimeoutManager->Suspend();
+
+ // Suspend all of the AudioContexts for this window
+ for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
+ mAudioContexts[i]->SuspendFromChrome();
+ }
+}
+
+void nsGlobalWindowInner::Resume(bool aIncludeSubWindows) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // We can only safely resume a window if its the current inner window. If
+ // its not the current inner, then we are in one of two different cases.
+ // Either we are in the bfcache or we are doomed window that is going away.
+ // If a window is suspended when it becomes inactive we purposely do not
+ // put it in the bfcache, so Resume should never be needed in that case.
+ // If the window is doomed then there is no point in resuming it.
+ if (!IsCurrentInnerWindow()) {
+ return;
+ }
+
+ // Resume all in-process descendants. This restores timers recursively
+ // canceled in Suspend() and ensures all in-process descendants have the
+ // correct mSuspendDepth.
+ if (aIncludeSubWindows) {
+ CallOnInProcessDescendants(&nsGlobalWindowInner::Resume, false);
+ }
+
+ if (mSuspendDepth == 0) {
+ // Ignore if the window is not suspended.
+ return;
+ }
+
+ mSuspendDepth -= 1;
+
+ if (mSuspendDepth != 0) {
+ return;
+ }
+
+ // We should not be able to resume a frozen window. It must be Thaw()'d
+ // first.
+ MOZ_ASSERT(mFreezeDepth == 0);
+
+ nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
+ if (ac) {
+ for (uint32_t i = 0; i < mEnabledSensors.Length(); i++)
+ ac->AddWindowListener(mEnabledSensors[i], this);
+ }
+ EnableGamepadUpdates();
+ EnableVRUpdates();
+
+ // Resume all of the AudioContexts for this window
+ for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
+ mAudioContexts[i]->ResumeFromChrome();
+ }
+
+ if (RefPtr<MediaDevices> devices = GetExtantMediaDevices()) {
+ devices->WindowResumed();
+ }
+
+ mTimeoutManager->Resume();
+
+ ResumeIdleRequests();
+
+ // Resume all of the workers for this window. We must do this
+ // after timeouts since workers may have queued events that can trigger
+ // a setTimeout().
+ ResumeWorkersForWindow(*this);
+
+ for (RefPtr<mozilla::dom::SharedWorker> pinnedWorker :
+ mSharedWorkers.ForwardRange()) {
+ pinnedWorker->Resume();
+ }
+
+ if (mWindowGlobalChild) {
+ mWindowGlobalChild->UnblockBFCacheFor(BFCacheStatus::SUSPENDED);
+ }
+}
+
+bool nsGlobalWindowInner::IsSuspended() const {
+ MOZ_ASSERT(NS_IsMainThread());
+ return mSuspendDepth != 0;
+}
+
+void nsGlobalWindowInner::Freeze(bool aIncludeSubWindows) {
+ MOZ_ASSERT(NS_IsMainThread());
+ Suspend(aIncludeSubWindows);
+ FreezeInternal(aIncludeSubWindows);
+}
+
+void nsGlobalWindowInner::FreezeInternal(bool aIncludeSubWindows) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_DIAGNOSTIC_ASSERT(IsCurrentInnerWindow());
+ MOZ_DIAGNOSTIC_ASSERT(IsSuspended());
+
+ HintIsLoading(false);
+
+ if (aIncludeSubWindows) {
+ CallOnInProcessChildren(&nsGlobalWindowInner::FreezeInternal,
+ aIncludeSubWindows);
+ }
+
+ mFreezeDepth += 1;
+ MOZ_ASSERT(mSuspendDepth >= mFreezeDepth);
+ if (mFreezeDepth != 1) {
+ return;
+ }
+
+ FreezeWorkersForWindow(*this);
+
+ for (RefPtr<mozilla::dom::SharedWorker> pinnedWorker :
+ mSharedWorkers.ForwardRange()) {
+ pinnedWorker->Freeze();
+ }
+
+ mTimeoutManager->Freeze();
+ if (mClientSource) {
+ mClientSource->Freeze();
+ }
+
+ NotifyDOMWindowFrozen(this);
+}
+
+void nsGlobalWindowInner::Thaw(bool aIncludeSubWindows) {
+ MOZ_ASSERT(NS_IsMainThread());
+ ThawInternal(aIncludeSubWindows);
+ Resume(aIncludeSubWindows);
+}
+
+void nsGlobalWindowInner::ThawInternal(bool aIncludeSubWindows) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_DIAGNOSTIC_ASSERT(IsCurrentInnerWindow());
+ MOZ_DIAGNOSTIC_ASSERT(IsSuspended());
+
+ if (aIncludeSubWindows) {
+ CallOnInProcessChildren(&nsGlobalWindowInner::ThawInternal,
+ aIncludeSubWindows);
+ }
+
+ MOZ_ASSERT(mFreezeDepth != 0);
+ mFreezeDepth -= 1;
+ MOZ_ASSERT(mSuspendDepth >= mFreezeDepth);
+ if (mFreezeDepth != 0) {
+ return;
+ }
+
+ if (mClientSource) {
+ mClientSource->Thaw();
+ }
+ mTimeoutManager->Thaw();
+
+ ThawWorkersForWindow(*this);
+
+ for (RefPtr<mozilla::dom::SharedWorker> pinnedWorker :
+ mSharedWorkers.ForwardRange()) {
+ pinnedWorker->Thaw();
+ }
+
+ NotifyDOMWindowThawed(this);
+}
+
+bool nsGlobalWindowInner::IsFrozen() const {
+ MOZ_ASSERT(NS_IsMainThread());
+ bool frozen = mFreezeDepth != 0;
+ MOZ_ASSERT_IF(frozen, IsSuspended());
+ return frozen;
+}
+
+void nsGlobalWindowInner::SyncStateFromParentWindow() {
+ // This method should only be called on an inner window that has been
+ // assigned to an outer window already.
+ MOZ_ASSERT(IsCurrentInnerWindow());
+ nsPIDOMWindowOuter* outer = GetOuterWindow();
+ MOZ_ASSERT(outer);
+
+ // Attempt to find our parent windows.
+ nsCOMPtr<Element> frame = outer->GetFrameElementInternal();
+ nsPIDOMWindowOuter* parentOuter =
+ frame ? frame->OwnerDoc()->GetWindow() : nullptr;
+ nsGlobalWindowInner* parentInner =
+ parentOuter
+ ? nsGlobalWindowInner::Cast(parentOuter->GetCurrentInnerWindow())
+ : nullptr;
+
+ // If our outer is in a modal state, but our parent is not in a modal
+ // state, then we must apply the suspend directly. If our parent is
+ // in a modal state then we should get the suspend automatically
+ // via the parentSuspendDepth application below.
+ if ((!parentInner || !parentInner->IsInModalState()) && IsInModalState()) {
+ Suspend();
+ }
+
+ uint32_t parentFreezeDepth = parentInner ? parentInner->mFreezeDepth : 0;
+ uint32_t parentSuspendDepth = parentInner ? parentInner->mSuspendDepth : 0;
+
+ // Since every Freeze() calls Suspend(), the suspend count must
+ // be equal or greater to the freeze count.
+ MOZ_ASSERT(parentFreezeDepth <= parentSuspendDepth);
+
+ // First apply the Freeze() calls.
+ for (uint32_t i = 0; i < parentFreezeDepth; ++i) {
+ Freeze();
+ }
+
+ // Now apply only the number of Suspend() calls to reach the target
+ // suspend count after applying the Freeze() calls.
+ for (uint32_t i = 0; i < (parentSuspendDepth - parentFreezeDepth); ++i) {
+ Suspend();
+ }
+}
+
+void nsGlobalWindowInner::UpdateBackgroundState() {
+ if (RefPtr<MediaDevices> devices = GetExtantMediaDevices()) {
+ devices->BackgroundStateChanged();
+ }
+ mTimeoutManager->UpdateBackgroundState();
+}
+
+template <typename Method, typename... Args>
+CallState nsGlobalWindowInner::CallOnInProcessDescendantsInternal(
+ BrowsingContext* aBrowsingContext, bool aChildOnly, Method aMethod,
+ Args&&... aArgs) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aBrowsingContext);
+
+ CallState state = CallState::Continue;
+ for (const RefPtr<BrowsingContext>& bc : aBrowsingContext->Children()) {
+ if (nsCOMPtr<nsPIDOMWindowOuter> pWin = bc->GetDOMWindow()) {
+ auto* win = nsGlobalWindowOuter::Cast(pWin);
+ if (nsGlobalWindowInner* inner =
+ nsGlobalWindowInner::Cast(win->GetCurrentInnerWindow())) {
+ // Call the descendant method using our helper CallDescendant() template
+ // method. This allows us to handle both void returning methods and
+ // methods that return CallState explicitly. For void returning methods
+ // we assume CallState::Continue.
+ using returnType = decltype((inner->*aMethod)(aArgs...));
+ state = CallDescendant<returnType>(inner, aMethod, aArgs...);
+
+ if (state == CallState::Stop) {
+ return state;
+ }
+ }
+ }
+
+ if (!aChildOnly) {
+ state = CallOnInProcessDescendantsInternal(bc.get(), aChildOnly, aMethod,
+ aArgs...);
+ if (state == CallState::Stop) {
+ return state;
+ }
+ }
+ }
+
+ return state;
+}
+
+Maybe<ClientInfo> nsGlobalWindowInner::GetClientInfo() const {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (mDoc && mDoc->IsStaticDocument()) {
+ if (Maybe<ClientInfo> info = mDoc->GetOriginalDocument()->GetClientInfo()) {
+ return info;
+ }
+ }
+
+ Maybe<ClientInfo> clientInfo;
+ if (mClientSource) {
+ clientInfo.emplace(mClientSource->Info());
+ }
+ return clientInfo;
+}
+
+Maybe<ClientState> nsGlobalWindowInner::GetClientState() const {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (mDoc && mDoc->IsStaticDocument()) {
+ if (Maybe<ClientState> state =
+ mDoc->GetOriginalDocument()->GetClientState()) {
+ return state;
+ }
+ }
+
+ Maybe<ClientState> clientState;
+ if (mClientSource) {
+ Result<ClientState, ErrorResult> res = mClientSource->SnapshotState();
+ if (res.isOk()) {
+ clientState.emplace(res.unwrap());
+ } else {
+ res.unwrapErr().SuppressException();
+ }
+ }
+ return clientState;
+}
+
+Maybe<ServiceWorkerDescriptor> nsGlobalWindowInner::GetController() const {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (mDoc && mDoc->IsStaticDocument()) {
+ if (Maybe<ServiceWorkerDescriptor> controller =
+ mDoc->GetOriginalDocument()->GetController()) {
+ return controller;
+ }
+ }
+
+ Maybe<ServiceWorkerDescriptor> controller;
+ if (mClientSource) {
+ controller = mClientSource->GetController();
+ }
+ return controller;
+}
+
+void nsGlobalWindowInner::SetCsp(nsIContentSecurityPolicy* aCsp) {
+ if (!mClientSource) {
+ return;
+ }
+ mClientSource->SetCsp(aCsp);
+ // Also cache the CSP within the document
+ mDoc->SetCsp(aCsp);
+
+ if (mWindowGlobalChild) {
+ mWindowGlobalChild->SendSetClientInfo(mClientSource->Info().ToIPC());
+ }
+}
+
+void nsGlobalWindowInner::SetPreloadCsp(nsIContentSecurityPolicy* aPreloadCsp) {
+ if (!mClientSource) {
+ return;
+ }
+ mClientSource->SetPreloadCsp(aPreloadCsp);
+ // Also cache the preload CSP within the document
+ mDoc->SetPreloadCsp(aPreloadCsp);
+
+ if (mWindowGlobalChild) {
+ mWindowGlobalChild->SendSetClientInfo(mClientSource->Info().ToIPC());
+ }
+}
+
+nsIContentSecurityPolicy* nsGlobalWindowInner::GetCsp() {
+ if (mDoc) {
+ return mDoc->GetCsp();
+ }
+
+ // If the window is partially torn down and has its document nulled out,
+ // we query the CSP we snapshot in FreeInnerObjects.
+ if (mDocumentCsp) {
+ return mDocumentCsp;
+ }
+ return nullptr;
+}
+
+RefPtr<ServiceWorker> nsGlobalWindowInner::GetOrCreateServiceWorker(
+ const ServiceWorkerDescriptor& aDescriptor) {
+ MOZ_ASSERT(NS_IsMainThread());
+ RefPtr<ServiceWorker> ref;
+ ForEachGlobalTeardownObserver(
+ [&](GlobalTeardownObserver* aObserver, bool* aDoneOut) {
+ RefPtr<ServiceWorker> sw = do_QueryObject(aObserver);
+ if (!sw || !sw->Descriptor().Matches(aDescriptor)) {
+ return;
+ }
+
+ ref = std::move(sw);
+ *aDoneOut = true;
+ });
+
+ if (!ref) {
+ ref = ServiceWorker::Create(this, aDescriptor);
+ }
+
+ return ref;
+}
+
+RefPtr<mozilla::dom::ServiceWorkerRegistration>
+nsGlobalWindowInner::GetServiceWorkerRegistration(
+ const mozilla::dom::ServiceWorkerRegistrationDescriptor& aDescriptor)
+ const {
+ MOZ_ASSERT(NS_IsMainThread());
+ RefPtr<ServiceWorkerRegistration> ref;
+ ForEachGlobalTeardownObserver(
+ [&](GlobalTeardownObserver* aObserver, bool* aDoneOut) {
+ RefPtr<ServiceWorkerRegistration> swr = do_QueryObject(aObserver);
+ if (!swr || !swr->MatchesDescriptor(aDescriptor)) {
+ return;
+ }
+
+ ref = std::move(swr);
+ *aDoneOut = true;
+ });
+ return ref;
+}
+
+RefPtr<ServiceWorkerRegistration>
+nsGlobalWindowInner::GetOrCreateServiceWorkerRegistration(
+ const ServiceWorkerRegistrationDescriptor& aDescriptor) {
+ MOZ_ASSERT(NS_IsMainThread());
+ RefPtr<ServiceWorkerRegistration> ref =
+ GetServiceWorkerRegistration(aDescriptor);
+ if (!ref) {
+ ref = ServiceWorkerRegistration::CreateForMainThread(this, aDescriptor);
+ }
+ return ref;
+}
+
+StorageAccess nsGlobalWindowInner::GetStorageAccess() {
+ return StorageAllowedForWindow(this);
+}
+
+nsresult nsGlobalWindowInner::FireDelayedDOMEvents(bool aIncludeSubWindows) {
+ // Fires an offline status event if the offline status has changed
+ FireOfflineStatusEventIfChanged();
+
+ if (!aIncludeSubWindows) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIDocShell> docShell = GetDocShell();
+ if (docShell) {
+ int32_t childCount = 0;
+ docShell->GetInProcessChildCount(&childCount);
+
+ // Take a copy of the current children so that modifications to
+ // the child list don't affect to the iteration.
+ AutoTArray<nsCOMPtr<nsIDocShellTreeItem>, 8> children;
+ for (int32_t i = 0; i < childCount; ++i) {
+ nsCOMPtr<nsIDocShellTreeItem> childShell;
+ docShell->GetInProcessChildAt(i, getter_AddRefs(childShell));
+ if (childShell) {
+ children.AppendElement(childShell);
+ }
+ }
+
+ for (nsCOMPtr<nsIDocShellTreeItem> childShell : children) {
+ if (nsCOMPtr<nsPIDOMWindowOuter> pWin = childShell->GetWindow()) {
+ auto* win = nsGlobalWindowOuter::Cast(pWin);
+ win->FireDelayedDOMEvents(true);
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+//*****************************************************************************
+// nsGlobalWindowInner: Window Control Functions
+//*****************************************************************************
+
+nsPIDOMWindowOuter* nsGlobalWindowInner::GetInProcessParentInternal() {
+ nsGlobalWindowOuter* outer = GetOuterWindowInternal();
+ if (!outer) {
+ // No outer window available!
+ return nullptr;
+ }
+ return outer->GetInProcessParentInternal();
+}
+
+nsIPrincipal* nsGlobalWindowInner::GetTopLevelAntiTrackingPrincipal() {
+ nsPIDOMWindowOuter* outerWindow = GetOuterWindowInternal();
+ if (!outerWindow) {
+ return nullptr;
+ }
+
+ nsPIDOMWindowOuter* topLevelOuterWindow =
+ GetBrowsingContext()->Top()->GetDOMWindow();
+ if (!topLevelOuterWindow) {
+ return nullptr;
+ }
+
+ bool stopAtOurLevel =
+ mDoc && mDoc->CookieJarSettings()->GetCookieBehavior() ==
+ nsICookieService::BEHAVIOR_REJECT_TRACKER;
+
+ if (stopAtOurLevel && topLevelOuterWindow == outerWindow) {
+ return nullptr;
+ }
+
+ nsPIDOMWindowInner* topLevelInnerWindow =
+ topLevelOuterWindow->GetCurrentInnerWindow();
+ if (NS_WARN_IF(!topLevelInnerWindow)) {
+ return nullptr;
+ }
+
+ nsIPrincipal* topLevelPrincipal =
+ nsGlobalWindowInner::Cast(topLevelInnerWindow)->GetPrincipal();
+ if (NS_WARN_IF(!topLevelPrincipal)) {
+ return nullptr;
+ }
+
+ return topLevelPrincipal;
+}
+
+nsIPrincipal* nsGlobalWindowInner::GetClientPrincipal() {
+ return mClientSource ? mClientSource->GetPrincipal() : nullptr;
+}
+
+bool nsGlobalWindowInner::IsInFullScreenTransition() {
+ if (!mIsChrome) {
+ return false;
+ }
+
+ nsGlobalWindowOuter* outerWindow = GetOuterWindowInternal();
+ if (!outerWindow) {
+ return false;
+ }
+
+ return outerWindow->mIsInFullScreenTransition;
+}
+
+//*****************************************************************************
+// nsGlobalWindowInner: Timeout Functions
+//*****************************************************************************
+
+class WindowScriptTimeoutHandler final : public ScriptTimeoutHandler {
+ public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(WindowScriptTimeoutHandler,
+ ScriptTimeoutHandler)
+
+ WindowScriptTimeoutHandler(JSContext* aCx, nsIGlobalObject* aGlobal,
+ const nsAString& aExpression)
+ : ScriptTimeoutHandler(aCx, aGlobal, aExpression),
+ mInitiatingScript(ScriptLoader::GetActiveScript(aCx)) {}
+
+ MOZ_CAN_RUN_SCRIPT virtual bool Call(const char* aExecutionReason) override;
+
+ private:
+ virtual ~WindowScriptTimeoutHandler() = default;
+
+ // Initiating script for use when evaluating mExpr on the main thread.
+ RefPtr<JS::loader::LoadedScript> mInitiatingScript;
+};
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(WindowScriptTimeoutHandler,
+ ScriptTimeoutHandler, mInitiatingScript)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WindowScriptTimeoutHandler)
+NS_INTERFACE_MAP_END_INHERITING(ScriptTimeoutHandler)
+
+NS_IMPL_ADDREF_INHERITED(WindowScriptTimeoutHandler, ScriptTimeoutHandler)
+NS_IMPL_RELEASE_INHERITED(WindowScriptTimeoutHandler, ScriptTimeoutHandler)
+
+bool WindowScriptTimeoutHandler::Call(const char* aExecutionReason) {
+ // New script entry point required, due to the "Create a script" sub-step
+ // of
+ // http://www.whatwg.org/specs/web-apps/current-work/#timer-initialisation-steps
+ nsAutoMicroTask mt;
+ AutoEntryScript aes(mGlobal, aExecutionReason, true);
+ JS::CompileOptions options(aes.cx());
+ options.setFileAndLine(mFileName.get(), mLineNo);
+ options.setNoScriptRval(true);
+ options.setIntroductionType("domTimer");
+ JS::Rooted<JSObject*> global(aes.cx(), mGlobal->GetGlobalJSObject());
+ {
+ JSExecutionContext exec(aes.cx(), global, options);
+ nsresult rv = exec.Compile(mExpr);
+
+ JS::Rooted<JSScript*> script(aes.cx(), exec.MaybeGetScript());
+ if (script) {
+ if (mInitiatingScript) {
+ mInitiatingScript->AssociateWithScript(script);
+ }
+
+ rv = exec.ExecScript();
+ }
+
+ if (rv == NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW_UNCATCHABLE) {
+ return false;
+ }
+ }
+
+ return true;
+};
+
+nsGlobalWindowInner* nsGlobalWindowInner::InnerForSetTimeoutOrInterval(
+ ErrorResult& aError) {
+ nsGlobalWindowOuter* outer = GetOuterWindowInternal();
+ nsPIDOMWindowInner* currentInner =
+ outer ? outer->GetCurrentInnerWindow() : this;
+
+ // If forwardTo is not the window with an active document then we want the
+ // call to setTimeout/Interval to be a noop, so return null but don't set an
+ // error.
+ return HasActiveDocument() ? nsGlobalWindowInner::Cast(currentInner)
+ : nullptr;
+}
+
+int32_t nsGlobalWindowInner::SetTimeout(JSContext* aCx, Function& aFunction,
+ int32_t aTimeout,
+ const Sequence<JS::Value>& aArguments,
+ ErrorResult& aError) {
+ return SetTimeoutOrInterval(aCx, aFunction, aTimeout, aArguments, false,
+ aError);
+}
+
+int32_t nsGlobalWindowInner::SetTimeout(JSContext* aCx,
+ const nsAString& aHandler,
+ int32_t aTimeout,
+ const Sequence<JS::Value>& /* unused */,
+ ErrorResult& aError) {
+ return SetTimeoutOrInterval(aCx, aHandler, aTimeout, false, aError);
+}
+
+int32_t nsGlobalWindowInner::SetInterval(JSContext* aCx, Function& aFunction,
+ const int32_t aTimeout,
+ const Sequence<JS::Value>& aArguments,
+ ErrorResult& aError) {
+ return SetTimeoutOrInterval(aCx, aFunction, aTimeout, aArguments, true,
+ aError);
+}
+
+int32_t nsGlobalWindowInner::SetInterval(
+ JSContext* aCx, const nsAString& aHandler, const int32_t aTimeout,
+ const Sequence<JS::Value>& /* unused */, ErrorResult& aError) {
+ return SetTimeoutOrInterval(aCx, aHandler, aTimeout, true, aError);
+}
+
+int32_t nsGlobalWindowInner::SetTimeoutOrInterval(
+ JSContext* aCx, Function& aFunction, int32_t aTimeout,
+ const Sequence<JS::Value>& aArguments, bool aIsInterval,
+ ErrorResult& aError) {
+ nsGlobalWindowInner* inner = InnerForSetTimeoutOrInterval(aError);
+ if (!inner) {
+ return -1;
+ }
+
+ if (inner != this) {
+ RefPtr<nsGlobalWindowInner> innerRef(inner);
+ return innerRef->SetTimeoutOrInterval(aCx, aFunction, aTimeout, aArguments,
+ aIsInterval, aError);
+ }
+
+ DebuggerNotificationDispatch(
+ this, aIsInterval ? DebuggerNotificationType::SetInterval
+ : DebuggerNotificationType::SetTimeout);
+
+ if (!GetContextInternal() || !HasJSGlobal()) {
+ // This window was already closed, or never properly initialized,
+ // don't let a timer be scheduled on such a window.
+ aError.Throw(NS_ERROR_NOT_INITIALIZED);
+ return 0;
+ }
+
+ nsTArray<JS::Heap<JS::Value>> args;
+ if (!args.AppendElements(aArguments, fallible)) {
+ aError.Throw(NS_ERROR_OUT_OF_MEMORY);
+ return 0;
+ }
+
+ RefPtr<TimeoutHandler> handler =
+ new CallbackTimeoutHandler(aCx, this, &aFunction, std::move(args));
+
+ int32_t result;
+ aError =
+ mTimeoutManager->SetTimeout(handler, aTimeout, aIsInterval,
+ Timeout::Reason::eTimeoutOrInterval, &result);
+ return result;
+}
+
+int32_t nsGlobalWindowInner::SetTimeoutOrInterval(JSContext* aCx,
+ const nsAString& aHandler,
+ int32_t aTimeout,
+ bool aIsInterval,
+ ErrorResult& aError) {
+ nsGlobalWindowInner* inner = InnerForSetTimeoutOrInterval(aError);
+ if (!inner) {
+ return -1;
+ }
+
+ if (inner != this) {
+ RefPtr<nsGlobalWindowInner> innerRef(inner);
+ return innerRef->SetTimeoutOrInterval(aCx, aHandler, aTimeout, aIsInterval,
+ aError);
+ }
+
+ DebuggerNotificationDispatch(
+ this, aIsInterval ? DebuggerNotificationType::SetInterval
+ : DebuggerNotificationType::SetTimeout);
+
+ if (!GetContextInternal() || !HasJSGlobal()) {
+ // This window was already closed, or never properly initialized,
+ // don't let a timer be scheduled on such a window.
+ aError.Throw(NS_ERROR_NOT_INITIALIZED);
+ return 0;
+ }
+
+ bool allowEval = false;
+ aError = CSPEvalChecker::CheckForWindow(aCx, this, aHandler, &allowEval);
+ if (NS_WARN_IF(aError.Failed()) || !allowEval) {
+ return 0;
+ }
+
+ RefPtr<TimeoutHandler> handler =
+ new WindowScriptTimeoutHandler(aCx, this, aHandler);
+
+ int32_t result;
+ aError =
+ mTimeoutManager->SetTimeout(handler, aTimeout, aIsInterval,
+ Timeout::Reason::eTimeoutOrInterval, &result);
+ return result;
+}
+
+static const char* GetTimeoutReasonString(Timeout* aTimeout) {
+ switch (aTimeout->mReason) {
+ case Timeout::Reason::eTimeoutOrInterval:
+ if (aTimeout->mIsInterval) {
+ return "setInterval handler";
+ }
+ return "setTimeout handler";
+ case Timeout::Reason::eIdleCallbackTimeout:
+ return "setIdleCallback handler (timed out)";
+ case Timeout::Reason::eAbortSignalTimeout:
+ return "AbortSignal timeout";
+ case Timeout::Reason::eDelayedWebTaskTimeout:
+ return "delayedWebTaskCallback handler (timed out)";
+ default:
+ MOZ_CRASH("Unexpected enum value");
+ return "";
+ }
+ MOZ_CRASH("Unexpected enum value");
+ return "";
+}
+
+bool nsGlobalWindowInner::RunTimeoutHandler(Timeout* aTimeout,
+ nsIScriptContext* aScx) {
+ // Hold on to the timeout in case mExpr or mFunObj releases its
+ // doc.
+ // XXXbz Our caller guarantees it'll hold on to the timeout (because
+ // we're MOZ_CAN_RUN_SCRIPT), so we can probably stop doing that...
+ RefPtr<Timeout> timeout = aTimeout;
+ Timeout* last_running_timeout = mTimeoutManager->BeginRunningTimeout(timeout);
+ timeout->mRunning = true;
+
+ // Push this timeout's popup control state, which should only be
+ // enabled the first time a timeout fires that was created while
+ // popups were enabled and with a delay less than
+ // "dom.disable_open_click_delay".
+ AutoPopupStatePusher popupStatePusher(timeout->mPopupState);
+
+ // Clear the timeout's popup state, if any, to prevent interval
+ // timeouts from repeatedly opening poups.
+ timeout->mPopupState = PopupBlocker::openAbused;
+
+ uint32_t nestingLevel = TimeoutManager::GetNestingLevel();
+ TimeoutManager::SetNestingLevel(timeout->mNestingLevel);
+
+ const char* reason = GetTimeoutReasonString(timeout);
+
+ nsCString str;
+ if (profiler_thread_is_being_profiled_for_markers()) {
+ TimeDuration originalInterval = timeout->When() - timeout->SubmitTime();
+ str.Append(reason);
+ str.Append(" with interval ");
+ str.AppendInt(int(originalInterval.ToMilliseconds()));
+ str.Append("ms: ");
+ nsCString handlerDescription;
+ timeout->mScriptHandler->GetDescription(handlerDescription);
+ str.Append(handlerDescription);
+ }
+ AUTO_PROFILER_MARKER_TEXT("setTimeout callback", DOM,
+ MarkerOptions(MarkerStack::TakeBacktrace(
+ timeout->TakeProfilerBacktrace()),
+ MarkerInnerWindowId(mWindowID)),
+ str);
+
+ bool abortIntervalHandler;
+ {
+ RefPtr<TimeoutHandler> handler(timeout->mScriptHandler);
+
+ CallbackDebuggerNotificationGuard guard(
+ this, timeout->mIsInterval
+ ? DebuggerNotificationType::SetIntervalCallback
+ : DebuggerNotificationType::SetTimeoutCallback);
+ abortIntervalHandler = !handler->Call(reason);
+ }
+
+ // If we received an uncatchable exception, do not schedule the timeout again.
+ // This allows the slow script dialog to break easy DoS attacks like
+ // setInterval(function() { while(1); }, 100);
+ if (abortIntervalHandler) {
+ // If it wasn't an interval timer to begin with, this does nothing. If it
+ // was, we'll treat it as a timeout that we just ran and discard it when
+ // we return.
+ timeout->mIsInterval = false;
+ }
+
+ // We ignore any failures from calling EvaluateString() on the context or
+ // Call() on a Function here since we're in a loop
+ // where we're likely to be running timeouts whose OS timers
+ // didn't fire in time and we don't want to not fire those timers
+ // now just because execution of one timer failed. We can't
+ // propagate the error to anyone who cares about it from this
+ // point anyway, and the script context should have already reported
+ // the script error in the usual way - so we just drop it.
+
+ TimeoutManager::SetNestingLevel(nestingLevel);
+
+ mTimeoutManager->EndRunningTimeout(last_running_timeout);
+ timeout->mRunning = false;
+
+ return timeout->mCleared;
+}
+
+//*****************************************************************************
+// nsGlobalWindowInner: Helper Functions
+//*****************************************************************************
+
+already_AddRefed<nsIDocShellTreeOwner> nsGlobalWindowInner::GetTreeOwner() {
+ FORWARD_TO_OUTER(GetTreeOwner, (), nullptr);
+}
+
+already_AddRefed<nsIWebBrowserChrome>
+nsGlobalWindowInner::GetWebBrowserChrome() {
+ nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
+
+ nsCOMPtr<nsIWebBrowserChrome> browserChrome = do_GetInterface(treeOwner);
+ return browserChrome.forget();
+}
+
+nsIScrollableFrame* nsGlobalWindowInner::GetScrollFrame() {
+ FORWARD_TO_OUTER(GetScrollFrame, (), nullptr);
+}
+
+bool nsGlobalWindowInner::IsPrivateBrowsing() {
+ nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(GetDocShell());
+ return loadContext && loadContext->UsePrivateBrowsing();
+}
+
+void nsGlobalWindowInner::FlushPendingNotifications(FlushType aType) {
+ if (mDoc) {
+ mDoc->FlushPendingNotifications(aType);
+ }
+}
+
+void nsGlobalWindowInner::EnableDeviceSensor(uint32_t aType) {
+ bool alreadyEnabled = false;
+ for (uint32_t i = 0; i < mEnabledSensors.Length(); i++) {
+ if (mEnabledSensors[i] == aType) {
+ alreadyEnabled = true;
+ break;
+ }
+ }
+
+ mEnabledSensors.AppendElement(aType);
+
+ if (alreadyEnabled) {
+ return;
+ }
+
+ nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
+ if (ac) {
+ ac->AddWindowListener(aType, this);
+ }
+}
+
+void nsGlobalWindowInner::DisableDeviceSensor(uint32_t aType) {
+ int32_t doomedElement = -1;
+ int32_t listenerCount = 0;
+ for (uint32_t i = 0; i < mEnabledSensors.Length(); i++) {
+ if (mEnabledSensors[i] == aType) {
+ doomedElement = i;
+ listenerCount++;
+ }
+ }
+
+ if (doomedElement == -1) {
+ return;
+ }
+
+ mEnabledSensors.RemoveElementAt(doomedElement);
+
+ if (listenerCount > 1) {
+ return;
+ }
+
+ nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
+ if (ac) {
+ ac->RemoveWindowListener(aType, this);
+ }
+}
+
+#if defined(MOZ_WIDGET_ANDROID)
+void nsGlobalWindowInner::EnableOrientationChangeListener() {
+ if (!ShouldResistFingerprinting(RFPTarget::ScreenOrientation)) {
+ mHasOrientationChangeListeners = true;
+ mOrientationAngle = Orientation(CallerType::System);
+ }
+}
+
+void nsGlobalWindowInner::DisableOrientationChangeListener() {
+ mHasOrientationChangeListeners = false;
+}
+#endif
+
+void nsGlobalWindowInner::SetHasGamepadEventListener(
+ bool aHasGamepad /* = true*/) {
+ mHasGamepad = aHasGamepad;
+ if (aHasGamepad) {
+ EnableGamepadUpdates();
+ }
+}
+
+void nsGlobalWindowInner::NotifyDetectXRRuntimesCompleted() {
+ if (!mXRRuntimeDetectionInFlight) {
+ return;
+ }
+ mXRRuntimeDetectionInFlight = false;
+ if (mXRPermissionRequestInFlight) {
+ return;
+ }
+ gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
+ bool supported = vm->RuntimeSupportsVR();
+ if (!supported) {
+ // A VR runtime was not installed; we can suppress
+ // the permission prompt
+ OnXRPermissionRequestCancel();
+ return;
+ }
+ // A VR runtime was found. Display a permission prompt before
+ // allowing it to be accessed.
+ // Connect to the VRManager in order to receive the runtime
+ // detection results.
+ mXRPermissionRequestInFlight = true;
+ RefPtr<XRPermissionRequest> request =
+ new XRPermissionRequest(this, WindowID());
+ Unused << NS_WARN_IF(NS_FAILED(request->Start()));
+}
+
+void nsGlobalWindowInner::RequestXRPermission() {
+ if (IsDying()) {
+ // Do not proceed if the window is dying, as that will result
+ // in leaks of objects that get re-allocated after FreeInnerObjects
+ // has been called, including mVREventObserver.
+ return;
+ }
+ if (mXRPermissionGranted) {
+ // Don't prompt redundantly once permission to
+ // access XR devices has been granted.
+ OnXRPermissionRequestAllow();
+ return;
+ }
+ if (mXRRuntimeDetectionInFlight || mXRPermissionRequestInFlight) {
+ // Don't allow multiple simultaneous permissions requests;
+ return;
+ }
+ // Before displaying a permission prompt, detect
+ // if there is any VR runtime installed.
+ gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
+ mXRRuntimeDetectionInFlight = true;
+ EnableVRUpdates();
+ vm->DetectRuntimes();
+}
+
+void nsGlobalWindowInner::OnXRPermissionRequestAllow() {
+ mXRPermissionRequestInFlight = false;
+ if (IsDying()) {
+ // The window may have started dying while the permission request
+ // is in flight.
+ // Do not proceed if the window is dying, as that will result
+ // in leaks of objects that get re-allocated after FreeInnerObjects
+ // has been called, including mNavigator.
+ return;
+ }
+ mXRPermissionGranted = true;
+
+ NotifyHasXRSession();
+
+ dom::Navigator* nav = Navigator();
+ MOZ_ASSERT(nav != nullptr);
+ nav->OnXRPermissionRequestAllow();
+}
+
+void nsGlobalWindowInner::OnXRPermissionRequestCancel() {
+ mXRPermissionRequestInFlight = false;
+ if (IsDying()) {
+ // The window may have started dying while the permission request
+ // is in flight.
+ // Do not proceed if the window is dying, as that will result
+ // in leaks of objects that get re-allocated after FreeInnerObjects
+ // has been called, including mNavigator.
+ return;
+ }
+ dom::Navigator* nav = Navigator();
+ MOZ_ASSERT(nav != nullptr);
+ nav->OnXRPermissionRequestCancel();
+}
+
+void nsGlobalWindowInner::EventListenerAdded(nsAtom* aType) {
+ if (aType == nsGkAtoms::onvrdisplayactivate ||
+ aType == nsGkAtoms::onvrdisplayconnect ||
+ aType == nsGkAtoms::onvrdisplaydeactivate ||
+ aType == nsGkAtoms::onvrdisplaydisconnect ||
+ aType == nsGkAtoms::onvrdisplaypresentchange) {
+ RequestXRPermission();
+ }
+
+ if (aType == nsGkAtoms::onvrdisplayactivate) {
+ mHasVRDisplayActivateEvents = true;
+ }
+
+ if (aType == nsGkAtoms::onunload && mWindowGlobalChild) {
+ if (++mUnloadOrBeforeUnloadListenerCount == 1) {
+ mWindowGlobalChild->BlockBFCacheFor(BFCacheStatus::UNLOAD_LISTENER);
+ }
+ }
+
+ if (aType == nsGkAtoms::onbeforeunload && mWindowGlobalChild) {
+ if (!mozilla::SessionHistoryInParent() ||
+ !StaticPrefs::
+ docshell_shistory_bfcache_ship_allow_beforeunload_listeners()) {
+ if (++mUnloadOrBeforeUnloadListenerCount == 1) {
+ mWindowGlobalChild->BlockBFCacheFor(
+ BFCacheStatus::BEFOREUNLOAD_LISTENER);
+ }
+ }
+ if (!mDoc || !(mDoc->GetSandboxFlags() & SANDBOXED_MODALS)) {
+ mWindowGlobalChild->BeforeUnloadAdded();
+ MOZ_ASSERT(mWindowGlobalChild->BeforeUnloadListeners() > 0);
+ }
+ }
+
+ // We need to initialize localStorage in order to receive notifications.
+ if (aType == nsGkAtoms::onstorage) {
+ ErrorResult rv;
+ GetLocalStorage(rv);
+ rv.SuppressException();
+
+ if (NextGenLocalStorageEnabled() && mLocalStorage &&
+ mLocalStorage->Type() == Storage::eLocalStorage) {
+ auto object = static_cast<LSObject*>(mLocalStorage.get());
+
+ Unused << NS_WARN_IF(NS_FAILED(object->EnsureObserver()));
+ }
+ }
+}
+
+void nsGlobalWindowInner::EventListenerRemoved(nsAtom* aType) {
+ if (aType == nsGkAtoms::onunload && mWindowGlobalChild) {
+ MOZ_ASSERT(mUnloadOrBeforeUnloadListenerCount > 0);
+ if (--mUnloadOrBeforeUnloadListenerCount == 0) {
+ mWindowGlobalChild->UnblockBFCacheFor(BFCacheStatus::UNLOAD_LISTENER);
+ }
+ }
+
+ if (aType == nsGkAtoms::onbeforeunload && mWindowGlobalChild) {
+ if (!mozilla::SessionHistoryInParent() ||
+ !StaticPrefs::
+ docshell_shistory_bfcache_ship_allow_beforeunload_listeners()) {
+ if (--mUnloadOrBeforeUnloadListenerCount == 0) {
+ mWindowGlobalChild->UnblockBFCacheFor(
+ BFCacheStatus::BEFOREUNLOAD_LISTENER);
+ }
+ }
+ if (!mDoc || !(mDoc->GetSandboxFlags() & SANDBOXED_MODALS)) {
+ mWindowGlobalChild->BeforeUnloadRemoved();
+ MOZ_ASSERT(mWindowGlobalChild->BeforeUnloadListeners() >= 0);
+ }
+ }
+
+ if (aType == nsGkAtoms::onstorage) {
+ if (NextGenLocalStorageEnabled() && mLocalStorage &&
+ mLocalStorage->Type() == Storage::eLocalStorage &&
+ // The remove event is fired even if this isn't the last listener, so
+ // only remove if there are no other listeners left.
+ mListenerManager &&
+ !mListenerManager->HasListenersFor(nsGkAtoms::onstorage)) {
+ auto object = static_cast<LSObject*>(mLocalStorage.get());
+
+ object->DropObserver();
+ }
+ }
+}
+
+void nsGlobalWindowInner::NotifyHasXRSession() {
+ if (IsDying()) {
+ // Do not proceed if the window is dying, as that will result
+ // in leaks of objects that get re-allocated after FreeInnerObjects
+ // has been called, including mVREventObserver.
+ return;
+ }
+ if (mWindowGlobalChild && !mHasXRSession) {
+ mWindowGlobalChild->BlockBFCacheFor(BFCacheStatus::HAS_USED_VR);
+ }
+ mHasXRSession = true;
+ EnableVRUpdates();
+}
+
+bool nsGlobalWindowInner::HasUsedVR() const {
+ // Returns true only if content has enumerated and activated
+ // XR devices. Detection of XR runtimes without activation
+ // will not cause true to be returned.
+ return mHasXRSession;
+}
+
+bool nsGlobalWindowInner::IsVRContentDetected() const {
+ // Returns true only if the content will respond to
+ // the VRDisplayActivate event.
+ return mHasVRDisplayActivateEvents;
+}
+
+bool nsGlobalWindowInner::IsVRContentPresenting() const {
+ for (const auto& display : mVRDisplays) {
+ if (display->IsAnyPresenting(gfx::kVRGroupAll)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void nsGlobalWindowInner::AddSizeOfIncludingThis(
+ nsWindowSizes& aWindowSizes) const {
+ aWindowSizes.mDOMSizes.mDOMOtherSize +=
+ aWindowSizes.mState.mMallocSizeOf(this);
+ aWindowSizes.mDOMSizes.mDOMOtherSize +=
+ nsIGlobalObject::ShallowSizeOfExcludingThis(
+ aWindowSizes.mState.mMallocSizeOf);
+
+ EventListenerManager* elm = GetExistingListenerManager();
+ if (elm) {
+ aWindowSizes.mDOMSizes.mDOMOtherSize +=
+ elm->SizeOfIncludingThis(aWindowSizes.mState.mMallocSizeOf);
+ aWindowSizes.mDOMEventListenersCount += elm->ListenerCount();
+ }
+ if (mDoc) {
+ // Multiple global windows can share a document. So only measure the
+ // document if it (a) doesn't have a global window, or (b) it's the
+ // primary document for the window.
+ if (!mDoc->GetInnerWindow() || mDoc->GetInnerWindow() == this) {
+ mDoc->DocAddSizeOfIncludingThis(aWindowSizes);
+ }
+ }
+
+ if (mNavigator) {
+ aWindowSizes.mDOMSizes.mDOMOtherSize +=
+ mNavigator->SizeOfIncludingThis(aWindowSizes.mState.mMallocSizeOf);
+ }
+
+ ForEachGlobalTeardownObserver([&](GlobalTeardownObserver* et,
+ bool* aDoneOut) {
+ if (nsCOMPtr<nsISizeOfEventTarget> iSizeOf = do_QueryObject(et)) {
+ aWindowSizes.mDOMSizes.mDOMEventTargetsSize +=
+ iSizeOf->SizeOfEventTargetIncludingThis(
+ aWindowSizes.mState.mMallocSizeOf);
+ }
+ if (nsCOMPtr<DOMEventTargetHelper> helper = do_QueryObject(et)) {
+ if (EventListenerManager* elm = helper->GetExistingListenerManager()) {
+ aWindowSizes.mDOMEventListenersCount += elm->ListenerCount();
+ }
+ }
+ ++aWindowSizes.mDOMEventTargetsCount;
+ });
+
+ if (mPerformance) {
+ aWindowSizes.mDOMSizes.mDOMPerformanceUserEntries =
+ mPerformance->SizeOfUserEntries(aWindowSizes.mState.mMallocSizeOf);
+ aWindowSizes.mDOMSizes.mDOMPerformanceResourceEntries =
+ mPerformance->SizeOfResourceEntries(aWindowSizes.mState.mMallocSizeOf);
+ aWindowSizes.mDOMSizes.mDOMPerformanceEventEntries =
+ mPerformance->SizeOfEventEntries(aWindowSizes.mState.mMallocSizeOf);
+ }
+}
+
+void nsGlobalWindowInner::RegisterDataDocumentForMemoryReporting(
+ Document* aDocument) {
+ aDocument->SetAddedToMemoryReportAsDataDocument();
+ mDataDocumentsForMemoryReporting.AppendElement(aDocument);
+}
+
+void nsGlobalWindowInner::UnregisterDataDocumentForMemoryReporting(
+ Document* aDocument) {
+ DebugOnly<bool> found =
+ mDataDocumentsForMemoryReporting.RemoveElement(aDocument);
+ MOZ_ASSERT(found);
+}
+
+void nsGlobalWindowInner::CollectDOMSizesForDataDocuments(
+ nsWindowSizes& aSize) const {
+ for (Document* doc : mDataDocumentsForMemoryReporting) {
+ if (doc) {
+ doc->DocAddSizeOfIncludingThis(aSize);
+ }
+ }
+}
+
+void nsGlobalWindowInner::AddGamepad(GamepadHandle aHandle, Gamepad* aGamepad) {
+ // Create the index we will present to content based on which indices are
+ // already taken, as required by the spec.
+ // https://w3c.github.io/gamepad/gamepad.html#widl-Gamepad-index
+ int index = 0;
+ while (mGamepadIndexSet.Contains(index)) {
+ ++index;
+ }
+ mGamepadIndexSet.Put(index);
+ aGamepad->SetIndex(index);
+ mGamepads.InsertOrUpdate(aHandle, RefPtr{aGamepad});
+}
+
+void nsGlobalWindowInner::RemoveGamepad(GamepadHandle aHandle) {
+ RefPtr<Gamepad> gamepad;
+ if (!mGamepads.Get(aHandle, getter_AddRefs(gamepad))) {
+ return;
+ }
+ // Free up the index we were using so it can be reused
+ mGamepadIndexSet.Remove(gamepad->Index());
+ mGamepads.Remove(aHandle);
+}
+
+void nsGlobalWindowInner::GetGamepads(nsTArray<RefPtr<Gamepad>>& aGamepads) {
+ aGamepads.Clear();
+
+ // navigator.getGamepads() always returns an empty array when
+ // privacy.resistFingerprinting is true.
+ if (ShouldResistFingerprinting(RFPTarget::Gamepad)) {
+ return;
+ }
+
+ // mGamepads.Count() may not be sufficient, but it's not harmful.
+ aGamepads.SetCapacity(mGamepads.Count());
+ for (const auto& entry : mGamepads) {
+ Gamepad* gamepad = entry.GetWeak();
+ aGamepads.EnsureLengthAtLeast(gamepad->Index() + 1);
+ aGamepads[gamepad->Index()] = gamepad;
+ }
+}
+
+already_AddRefed<Gamepad> nsGlobalWindowInner::GetGamepad(
+ GamepadHandle aHandle) {
+ RefPtr<Gamepad> gamepad;
+
+ if (mGamepads.Get(aHandle, getter_AddRefs(gamepad))) {
+ return gamepad.forget();
+ }
+
+ return nullptr;
+}
+
+void nsGlobalWindowInner::SetHasSeenGamepadInput(bool aHasSeen) {
+ mHasSeenGamepadInput = aHasSeen;
+}
+
+bool nsGlobalWindowInner::HasSeenGamepadInput() { return mHasSeenGamepadInput; }
+
+void nsGlobalWindowInner::SyncGamepadState() {
+ if (mHasSeenGamepadInput) {
+ RefPtr<GamepadManager> gamepadManager(GamepadManager::GetService());
+ for (const auto& entry : mGamepads) {
+ gamepadManager->SyncGamepadState(entry.GetKey(), this, entry.GetWeak());
+ }
+ }
+}
+
+void nsGlobalWindowInner::StopGamepadHaptics() {
+ if (mHasSeenGamepadInput) {
+ RefPtr<GamepadManager> gamepadManager(GamepadManager::GetService());
+ gamepadManager->StopHaptics();
+ }
+}
+
+bool nsGlobalWindowInner::UpdateVRDisplays(
+ nsTArray<RefPtr<mozilla::dom::VRDisplay>>& aDevices) {
+ VRDisplay::UpdateVRDisplays(mVRDisplays, this);
+ aDevices = mVRDisplays.Clone();
+ return true;
+}
+
+void nsGlobalWindowInner::NotifyActiveVRDisplaysChanged() {
+ if (mNavigator) {
+ mNavigator->NotifyActiveVRDisplaysChanged();
+ }
+}
+
+void nsGlobalWindowInner::NotifyPresentationGenerationChanged(
+ uint32_t aDisplayID) {
+ for (const auto& display : mVRDisplays) {
+ if (display->DisplayId() == aDisplayID) {
+ display->OnPresentationGenerationChanged();
+ }
+ }
+}
+
+void nsGlobalWindowInner::DispatchVRDisplayActivate(
+ uint32_t aDisplayID, mozilla::dom::VRDisplayEventReason aReason) {
+ // Ensure that our list of displays is up to date
+ VRDisplay::UpdateVRDisplays(mVRDisplays, this);
+
+ // Search for the display identified with aDisplayID and fire the
+ // event if found.
+ for (const auto& display : mVRDisplays) {
+ if (display->DisplayId() == aDisplayID) {
+ if (aReason != VRDisplayEventReason::Navigation &&
+ display->IsAnyPresenting(gfx::kVRGroupContent)) {
+ // We only want to trigger this event if nobody is presenting to the
+ // display already or when a page is loaded by navigating away
+ // from a page with an active VR Presentation.
+ continue;
+ }
+
+ VRDisplayEventInit init;
+ init.mBubbles = false;
+ init.mCancelable = false;
+ init.mDisplay = display;
+ init.mReason.Construct(aReason);
+
+ RefPtr<VRDisplayEvent> event =
+ VRDisplayEvent::Constructor(this, u"vrdisplayactivate"_ns, init);
+ // vrdisplayactivate is a trusted event, allowing VRDisplay.requestPresent
+ // to be used in response to link traversal, user request (chrome UX), and
+ // HMD mounting detection sensors.
+ event->SetTrusted(true);
+ // VRDisplay.requestPresent normally requires a user gesture; however, an
+ // exception is made to allow it to be called in response to
+ // vrdisplayactivate during VR link traversal.
+ display->StartHandlingVRNavigationEvent();
+ DispatchEvent(*event);
+ display->StopHandlingVRNavigationEvent();
+ // Once we dispatch the event, we must not access any members as an event
+ // listener can do anything, including closing windows.
+ return;
+ }
+ }
+}
+
+void nsGlobalWindowInner::DispatchVRDisplayDeactivate(
+ uint32_t aDisplayID, mozilla::dom::VRDisplayEventReason aReason) {
+ // Ensure that our list of displays is up to date
+ VRDisplay::UpdateVRDisplays(mVRDisplays, this);
+
+ // Search for the display identified with aDisplayID and fire the
+ // event if found.
+ for (const auto& display : mVRDisplays) {
+ if (display->DisplayId() == aDisplayID && display->IsPresenting()) {
+ // We only want to trigger this event to content that is presenting to
+ // the display already.
+
+ VRDisplayEventInit init;
+ init.mBubbles = false;
+ init.mCancelable = false;
+ init.mDisplay = display;
+ init.mReason.Construct(aReason);
+
+ RefPtr<VRDisplayEvent> event =
+ VRDisplayEvent::Constructor(this, u"vrdisplaydeactivate"_ns, init);
+ event->SetTrusted(true);
+ DispatchEvent(*event);
+ // Once we dispatch the event, we must not access any members as an event
+ // listener can do anything, including closing windows.
+ return;
+ }
+ }
+}
+
+void nsGlobalWindowInner::DispatchVRDisplayConnect(uint32_t aDisplayID) {
+ // Ensure that our list of displays is up to date
+ VRDisplay::UpdateVRDisplays(mVRDisplays, this);
+
+ // Search for the display identified with aDisplayID and fire the
+ // event if found.
+ for (const auto& display : mVRDisplays) {
+ if (display->DisplayId() == aDisplayID) {
+ // Fire event even if not presenting to the display.
+ VRDisplayEventInit init;
+ init.mBubbles = false;
+ init.mCancelable = false;
+ init.mDisplay = display;
+ // VRDisplayEvent.reason is not set for vrdisplayconnect
+
+ RefPtr<VRDisplayEvent> event =
+ VRDisplayEvent::Constructor(this, u"vrdisplayconnect"_ns, init);
+ event->SetTrusted(true);
+ DispatchEvent(*event);
+ // Once we dispatch the event, we must not access any members as an event
+ // listener can do anything, including closing windows.
+ return;
+ }
+ }
+}
+
+void nsGlobalWindowInner::DispatchVRDisplayDisconnect(uint32_t aDisplayID) {
+ // Ensure that our list of displays is up to date
+ VRDisplay::UpdateVRDisplays(mVRDisplays, this);
+
+ // Search for the display identified with aDisplayID and fire the
+ // event if found.
+ for (const auto& display : mVRDisplays) {
+ if (display->DisplayId() == aDisplayID) {
+ // Fire event even if not presenting to the display.
+ VRDisplayEventInit init;
+ init.mBubbles = false;
+ init.mCancelable = false;
+ init.mDisplay = display;
+ // VRDisplayEvent.reason is not set for vrdisplaydisconnect
+
+ RefPtr<VRDisplayEvent> event =
+ VRDisplayEvent::Constructor(this, u"vrdisplaydisconnect"_ns, init);
+ event->SetTrusted(true);
+ DispatchEvent(*event);
+ // Once we dispatch the event, we must not access any members as an event
+ // listener can do anything, including closing windows.
+ return;
+ }
+ }
+}
+
+void nsGlobalWindowInner::DispatchVRDisplayPresentChange(uint32_t aDisplayID) {
+ // Ensure that our list of displays is up to date
+ VRDisplay::UpdateVRDisplays(mVRDisplays, this);
+
+ // Search for the display identified with aDisplayID and fire the
+ // event if found.
+ for (const auto& display : mVRDisplays) {
+ if (display->DisplayId() == aDisplayID) {
+ // Fire event even if not presenting to the display.
+ VRDisplayEventInit init;
+ init.mBubbles = false;
+ init.mCancelable = false;
+ init.mDisplay = display;
+ // VRDisplayEvent.reason is not set for vrdisplaypresentchange
+ RefPtr<VRDisplayEvent> event =
+ VRDisplayEvent::Constructor(this, u"vrdisplaypresentchange"_ns, init);
+ event->SetTrusted(true);
+ DispatchEvent(*event);
+ // Once we dispatch the event, we must not access any members as an event
+ // listener can do anything, including closing windows.
+ return;
+ }
+ }
+}
+
+enum WindowState {
+ // These constants need to match the constants in Window.webidl
+ STATE_MAXIMIZED = 1,
+ STATE_MINIMIZED = 2,
+ STATE_NORMAL = 3,
+ STATE_FULLSCREEN = 4
+};
+
+uint16_t nsGlobalWindowInner::WindowState() {
+ nsCOMPtr<nsIWidget> widget = GetMainWidget();
+
+ int32_t mode = widget ? widget->SizeMode() : 0;
+
+ switch (mode) {
+ case nsSizeMode_Minimized:
+ return STATE_MINIMIZED;
+ case nsSizeMode_Maximized:
+ return STATE_MAXIMIZED;
+ case nsSizeMode_Fullscreen:
+ return STATE_FULLSCREEN;
+ case nsSizeMode_Normal:
+ return STATE_NORMAL;
+ default:
+ NS_WARNING("Illegal window state for this chrome window");
+ break;
+ }
+
+ return STATE_NORMAL;
+}
+
+bool nsGlobalWindowInner::IsFullyOccluded() {
+ nsCOMPtr<nsIWidget> widget = GetMainWidget();
+ return widget && widget->IsFullyOccluded();
+}
+
+void nsGlobalWindowInner::Maximize() {
+ if (nsCOMPtr<nsIWidget> widget = GetMainWidget()) {
+ widget->SetSizeMode(nsSizeMode_Maximized);
+ }
+}
+
+void nsGlobalWindowInner::Minimize() {
+ if (nsCOMPtr<nsIWidget> widget = GetMainWidget()) {
+ widget->SetSizeMode(nsSizeMode_Minimized);
+ }
+}
+
+void nsGlobalWindowInner::Restore() {
+ if (nsCOMPtr<nsIWidget> widget = GetMainWidget()) {
+ widget->SetSizeMode(nsSizeMode_Normal);
+ }
+}
+
+void nsGlobalWindowInner::GetWorkspaceID(nsAString& workspaceID) {
+ workspaceID.Truncate();
+ if (nsCOMPtr<nsIWidget> widget = GetMainWidget()) {
+ return widget->GetWorkspaceID(workspaceID);
+ }
+}
+
+void nsGlobalWindowInner::MoveToWorkspace(const nsAString& workspaceID) {
+ if (nsCOMPtr<nsIWidget> widget = GetMainWidget()) {
+ widget->MoveToWorkspace(workspaceID);
+ }
+}
+
+void nsGlobalWindowInner::GetAttention(ErrorResult& aResult) {
+ return GetAttentionWithCycleCount(-1, aResult);
+}
+
+void nsGlobalWindowInner::GetAttentionWithCycleCount(int32_t aCycleCount,
+ ErrorResult& aError) {
+ nsCOMPtr<nsIWidget> widget = GetMainWidget();
+
+ if (widget) {
+ aError = widget->GetAttention(aCycleCount);
+ }
+}
+
+already_AddRefed<Promise> nsGlobalWindowInner::PromiseDocumentFlushed(
+ PromiseDocumentFlushedCallback& aCallback, ErrorResult& aError) {
+ MOZ_RELEASE_ASSERT(IsChromeWindow());
+
+ if (!IsCurrentInnerWindow()) {
+ aError.ThrowInvalidStateError("Not the current inner window");
+ return nullptr;
+ }
+ if (!mDoc) {
+ aError.ThrowInvalidStateError("No document");
+ return nullptr;
+ }
+
+ if (mIteratingDocumentFlushedResolvers) {
+ aError.ThrowInvalidStateError("Already iterating through resolvers");
+ return nullptr;
+ }
+
+ PresShell* presShell = mDoc->GetPresShell();
+ if (!presShell) {
+ aError.ThrowInvalidStateError("No pres shell");
+ return nullptr;
+ }
+
+ // We need to associate the lifetime of the Promise to the lifetime
+ // of the caller's global. That way, if the window we're observing
+ // refresh driver ticks on goes away before our observer is fired,
+ // we can still resolve the Promise.
+ nsIGlobalObject* global = GetIncumbentGlobal();
+ if (!global) {
+ aError.ThrowInvalidStateError("No incumbent global");
+ return nullptr;
+ }
+
+ RefPtr<Promise> resultPromise = Promise::Create(global, aError);
+ if (aError.Failed()) {
+ return nullptr;
+ }
+
+ UniquePtr<PromiseDocumentFlushedResolver> flushResolver(
+ new PromiseDocumentFlushedResolver(resultPromise, aCallback));
+
+ if (!presShell->NeedStyleFlush() && !presShell->NeedLayoutFlush()) {
+ flushResolver->Call();
+ return resultPromise.forget();
+ }
+
+ if (!TryToObserveRefresh()) {
+ aError.ThrowInvalidStateError("Couldn't observe refresh");
+ return nullptr;
+ }
+
+ mDocumentFlushedResolvers.AppendElement(std::move(flushResolver));
+ return resultPromise.forget();
+}
+
+bool nsGlobalWindowInner::TryToObserveRefresh() {
+ if (mObservingRefresh) {
+ return true;
+ }
+
+ if (!mDoc) {
+ return false;
+ }
+
+ nsPresContext* pc = mDoc->GetPresContext();
+ if (!pc) {
+ return false;
+ }
+
+ mObservingRefresh = true;
+ auto observer = MakeRefPtr<ManagedPostRefreshObserver>(
+ pc, [win = RefPtr{this}](bool aWasCanceled) {
+ if (win->MaybeCallDocumentFlushedResolvers(
+ /* aUntilExhaustion = */ aWasCanceled)) {
+ return ManagedPostRefreshObserver::Unregister::No;
+ }
+ win->mObservingRefresh = false;
+ return ManagedPostRefreshObserver::Unregister::Yes;
+ });
+ pc->RegisterManagedPostRefreshObserver(observer.get());
+ return mObservingRefresh;
+}
+
+void nsGlobalWindowInner::CallDocumentFlushedResolvers(bool aUntilExhaustion) {
+ while (true) {
+ {
+ // To coalesce MicroTask checkpoints inside callback call, enclose the
+ // inner loop with nsAutoMicroTask, and perform a MicroTask checkpoint
+ // after the loop.
+ nsAutoMicroTask mt;
+
+ mIteratingDocumentFlushedResolvers = true;
+
+ auto resolvers = std::move(mDocumentFlushedResolvers);
+ for (const auto& resolver : resolvers) {
+ resolver->Call();
+ }
+
+ mIteratingDocumentFlushedResolvers = false;
+ }
+
+ // Leaving nsAutoMicroTask above will perform MicroTask checkpoint, and
+ // Promise callbacks there may create mDocumentFlushedResolvers items.
+
+ // If there's no new resolvers, or we're not exhausting the queue, there's
+ // nothing to do (we'll keep observing if there's any new observer).
+ //
+ // Otherwise, keep looping to call all promises. This case can happen while
+ // destroying the window. This violates the constraint that the
+ // promiseDocumentFlushed callback only ever run when no flush is needed,
+ // but it's necessary to resolve the Promise returned by that.
+ if (!aUntilExhaustion || mDocumentFlushedResolvers.IsEmpty()) {
+ break;
+ }
+ }
+}
+
+bool nsGlobalWindowInner::MaybeCallDocumentFlushedResolvers(
+ bool aUntilExhaustion) {
+ MOZ_ASSERT(mDoc);
+
+ PresShell* presShell = mDoc->GetPresShell();
+ if (!presShell || aUntilExhaustion) {
+ CallDocumentFlushedResolvers(/* aUntilExhaustion = */ true);
+ return false;
+ }
+
+ if (presShell->NeedStyleFlush() || presShell->NeedLayoutFlush()) {
+ // By the time our observer fired, something has already invalidated
+ // style or layout - or perhaps we're still in the middle of a flush that
+ // was interrupted. In either case, we'll wait until the next refresh driver
+ // tick instead and try again.
+ return true;
+ }
+
+ CallDocumentFlushedResolvers(/* aUntilExhaustion = */ false);
+ return !mDocumentFlushedResolvers.IsEmpty();
+}
+
+already_AddRefed<nsWindowRoot> nsGlobalWindowInner::GetWindowRoot(
+ mozilla::ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(GetWindowRootOuter, (), aError, nullptr);
+}
+
+void nsGlobalWindowInner::SetCursor(const nsACString& aCursor,
+ ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(SetCursorOuter, (aCursor, aError), aError, );
+}
+
+nsIBrowserDOMWindow* nsGlobalWindowInner::GetBrowserDOMWindow(
+ ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(GetBrowserDOMWindow, (), aError, nullptr);
+}
+
+void nsGlobalWindowInner::SetBrowserDOMWindow(
+ nsIBrowserDOMWindow* aBrowserWindow, ErrorResult& aError) {
+ FORWARD_TO_OUTER_OR_THROW(SetBrowserDOMWindowOuter, (aBrowserWindow),
+ aError, );
+}
+
+void nsGlobalWindowInner::NotifyDefaultButtonLoaded(Element& aDefaultButton,
+ ErrorResult& aError) {
+ // Don't snap to a disabled button.
+ nsCOMPtr<nsIDOMXULControlElement> xulControl = aDefaultButton.AsXULControl();
+ if (!xulControl) {
+ aError.Throw(NS_ERROR_FAILURE);
+ return;
+ }
+ bool disabled;
+ aError = xulControl->GetDisabled(&disabled);
+ if (aError.Failed() || disabled) {
+ return;
+ }
+
+ // Get the button rect in screen coordinates.
+ nsIFrame* frame = aDefaultButton.GetPrimaryFrame();
+ if (!frame) {
+ aError.Throw(NS_ERROR_FAILURE);
+ return;
+ }
+ LayoutDeviceIntRect buttonRect = LayoutDeviceIntRect::FromAppUnitsToNearest(
+ frame->GetScreenRectInAppUnits(),
+ frame->PresContext()->AppUnitsPerDevPixel());
+
+ // Get the widget rect in screen coordinates.
+ nsIWidget* widget = GetNearestWidget();
+ if (!widget) {
+ aError.Throw(NS_ERROR_FAILURE);
+ return;
+ }
+ LayoutDeviceIntRect widgetRect = widget->GetScreenBounds();
+
+ // Convert the buttonRect coordinates from screen to the widget.
+ buttonRect -= widgetRect.TopLeft();
+ nsresult rv = widget->OnDefaultButtonLoaded(buttonRect);
+ if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) {
+ aError.Throw(rv);
+ }
+}
+
+ChromeMessageBroadcaster* nsGlobalWindowInner::MessageManager() {
+ MOZ_ASSERT(IsChromeWindow());
+ if (!mChromeFields.mMessageManager) {
+ RefPtr<ChromeMessageBroadcaster> globalMM =
+ nsFrameMessageManager::GetGlobalMessageManager();
+ mChromeFields.mMessageManager = new ChromeMessageBroadcaster(globalMM);
+ }
+ return mChromeFields.mMessageManager;
+}
+
+ChromeMessageBroadcaster* nsGlobalWindowInner::GetGroupMessageManager(
+ const nsAString& aGroup) {
+ MOZ_ASSERT(IsChromeWindow());
+
+ return mChromeFields.mGroupMessageManagers
+ .LookupOrInsertWith(
+ aGroup,
+ [&] {
+ return MakeAndAddRef<ChromeMessageBroadcaster>(MessageManager());
+ })
+ .get();
+}
+
+void nsGlobalWindowInner::InitWasOffline() { mWasOffline = NS_IsOffline(); }
+
+int16_t nsGlobalWindowInner::Orientation(CallerType aCallerType) {
+ // GetOrientationAngle() returns 0, 90, 180 or 270.
+ // window.orientation returns -90, 0, 90 or 180.
+ if (nsIGlobalObject::ShouldResistFingerprinting(
+ aCallerType, RFPTarget::ScreenOrientation)) {
+ return 0;
+ }
+ int16_t angle = AssertedCast<int16_t>(Screen()->GetOrientationAngle());
+ return angle <= 180 ? angle : angle - 360;
+}
+
+already_AddRefed<Console> nsGlobalWindowInner::GetConsole(JSContext* aCx,
+ ErrorResult& aRv) {
+ if (!mConsole) {
+ mConsole = Console::Create(aCx, this, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+ }
+
+ RefPtr<Console> console = mConsole;
+ return console.forget();
+}
+
+bool nsGlobalWindowInner::IsSecureContext() const {
+ JS::Realm* realm = js::GetNonCCWObjectRealm(GetWrapperPreserveColor());
+ return JS::GetIsSecureContext(realm);
+}
+
+External* nsGlobalWindowInner::External() {
+ if (!mExternal) {
+ mExternal = new dom::External(ToSupports(this));
+ }
+
+ return mExternal;
+}
+
+void nsGlobalWindowInner::ClearDocumentDependentSlots(JSContext* aCx) {
+ // If JSAPI OOMs here, there is basically nothing we can do to recover safely.
+ if (!Window_Binding::ClearCachedDocumentValue(aCx, this) ||
+ !Window_Binding::ClearCachedPerformanceValue(aCx, this)) {
+ MOZ_CRASH("Unhandlable OOM while clearing document dependent slots.");
+ }
+}
+
+/* static */
+JSObject* nsGlobalWindowInner::CreateNamedPropertiesObject(
+ JSContext* aCx, JS::Handle<JSObject*> aProto) {
+ return WindowNamedPropertiesHandler::Create(aCx, aProto);
+}
+
+void nsGlobalWindowInner::RedefineProperty(JSContext* aCx,
+ const char* aPropName,
+ JS::Handle<JS::Value> aValue,
+ ErrorResult& aError) {
+ JS::Rooted<JSObject*> thisObj(aCx, GetWrapperPreserveColor());
+ if (!thisObj) {
+ aError.Throw(NS_ERROR_UNEXPECTED);
+ return;
+ }
+
+ if (!JS_WrapObject(aCx, &thisObj) ||
+ !JS_DefineProperty(aCx, thisObj, aPropName, aValue, JSPROP_ENUMERATE)) {
+ aError.Throw(NS_ERROR_FAILURE);
+ }
+}
+
+void nsGlobalWindowInner::FireOnNewGlobalObject() {
+ // AutoEntryScript required to invoke debugger hook, which is a
+ // Gecko-specific concept at present.
+ AutoEntryScript aes(this, "nsGlobalWindowInner report new global");
+ JS::Rooted<JSObject*> global(aes.cx(), GetWrapper());
+ JS_FireOnNewGlobalObject(aes.cx(), global);
+}
+
+#if defined(_WINDOWS_) && !defined(MOZ_WRAPPED_WINDOWS_H)
+# pragma message( \
+ "wrapper failure reason: " MOZ_WINDOWS_WRAPPER_DISABLED_REASON)
+# error "Never include unwrapped windows.h in this file!"
+#endif
+
+already_AddRefed<Promise> nsGlobalWindowInner::CreateImageBitmap(
+ const ImageBitmapSource& aImage, const ImageBitmapOptions& aOptions,
+ ErrorResult& aRv) {
+ return ImageBitmap::Create(this, aImage, Nothing(), aOptions, aRv);
+}
+
+already_AddRefed<Promise> nsGlobalWindowInner::CreateImageBitmap(
+ const ImageBitmapSource& aImage, int32_t aSx, int32_t aSy, int32_t aSw,
+ int32_t aSh, const ImageBitmapOptions& aOptions, ErrorResult& aRv) {
+ return ImageBitmap::Create(
+ this, aImage, Some(gfx::IntRect(aSx, aSy, aSw, aSh)), aOptions, aRv);
+}
+
+// https://html.spec.whatwg.org/#structured-cloning
+void nsGlobalWindowInner::StructuredClone(
+ JSContext* aCx, JS::Handle<JS::Value> aValue,
+ const StructuredSerializeOptions& aOptions,
+ JS::MutableHandle<JS::Value> aRetval, ErrorResult& aError) {
+ nsContentUtils::StructuredClone(aCx, this, aValue, aOptions, aRetval, aError);
+}
+
+nsresult nsGlobalWindowInner::Dispatch(
+ already_AddRefed<nsIRunnable>&& aRunnable) const {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ return NS_DispatchToCurrentThread(std::move(aRunnable));
+}
+
+nsISerialEventTarget* nsGlobalWindowInner::SerialEventTarget() const {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ return GetMainThreadSerialEventTarget();
+}
+
+Worklet* nsGlobalWindowInner::GetPaintWorklet(ErrorResult& aRv) {
+ if (!mPaintWorklet) {
+ nsIPrincipal* principal = GetPrincipal();
+ if (!principal) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return nullptr;
+ }
+
+ mPaintWorklet = PaintWorkletImpl::CreateWorklet(this, principal);
+ }
+
+ return mPaintWorklet;
+}
+
+void nsGlobalWindowInner::GetRegionalPrefsLocales(
+ nsTArray<nsString>& aLocales) {
+ MOZ_ASSERT(mozilla::intl::LocaleService::GetInstance());
+
+ AutoTArray<nsCString, 10> rpLocales;
+ mozilla::intl::LocaleService::GetInstance()->GetRegionalPrefsLocales(
+ rpLocales);
+
+ for (const auto& loc : rpLocales) {
+ aLocales.AppendElement(NS_ConvertUTF8toUTF16(loc));
+ }
+}
+
+void nsGlobalWindowInner::GetWebExposedLocales(nsTArray<nsString>& aLocales) {
+ MOZ_ASSERT(mozilla::intl::LocaleService::GetInstance());
+
+ AutoTArray<nsCString, 10> rpLocales;
+ mozilla::intl::LocaleService::GetInstance()->GetWebExposedLocales(rpLocales);
+
+ for (const auto& loc : rpLocales) {
+ aLocales.AppendElement(NS_ConvertUTF8toUTF16(loc));
+ }
+}
+
+IntlUtils* nsGlobalWindowInner::GetIntlUtils(ErrorResult& aError) {
+ if (!mIntlUtils) {
+ mIntlUtils = new IntlUtils(this);
+ }
+
+ return mIntlUtils;
+}
+
+void nsGlobalWindowInner::StoreSharedWorker(SharedWorker* aSharedWorker) {
+ MOZ_ASSERT(aSharedWorker);
+ MOZ_ASSERT(!mSharedWorkers.Contains(aSharedWorker));
+
+ mSharedWorkers.AppendElement(aSharedWorker);
+}
+
+void nsGlobalWindowInner::ForgetSharedWorker(SharedWorker* aSharedWorker) {
+ MOZ_ASSERT(aSharedWorker);
+ MOZ_ASSERT(mSharedWorkers.Contains(aSharedWorker));
+
+ mSharedWorkers.RemoveElement(aSharedWorker);
+}
+
+RefPtr<GenericPromise> nsGlobalWindowInner::StorageAccessPermissionChanged(
+ bool aGranted) {
+ // Invalidate cached StorageAllowed field so that calls to GetLocalStorage
+ // give us the updated localStorage object.
+ ClearStorageAllowedCache();
+
+ // If we're always partitioning non-cookie third party storage then
+ // there is no need to clear it when the user accepts requestStorageAccess.
+ if (StaticPrefs::
+ privacy_partition_always_partition_third_party_non_cookie_storage()) {
+ // Just reset the active cookie and storage principals
+ nsCOMPtr<nsICookieJarSettings> cjs;
+ if (mDoc) {
+ cjs = mDoc->CookieJarSettings();
+ }
+ StorageAccess storageAccess = StorageAllowedForWindow(this);
+ if (ShouldPartitionStorage(storageAccess) &&
+ StoragePartitioningEnabled(storageAccess, cjs)) {
+ if (mDoc) {
+ mDoc->ClearActiveCookieAndStoragePrincipals();
+ }
+ // When storage access is granted the content process needs to request the
+ // updated cookie list from the parent process. Otherwise the site won't
+ // have access to unpartitioned cookies via document.cookie without a
+ // reload.
+ if (aGranted) {
+ nsIChannel* channel = mDoc->GetChannel();
+ if (channel) {
+ // The promise resolves when the updated cookie list has been received
+ // from the parent.
+ return ContentChild::UpdateCookieStatus(channel);
+ }
+ }
+ }
+ }
+
+ PropagateStorageAccessPermissionGrantedToWorkers(*this);
+
+ // If we have a partitioned localStorage, it's time to replace it with a real
+ // one in order to receive notifications.
+
+ if (mLocalStorage) {
+ IgnoredErrorResult error;
+ GetLocalStorage(error);
+ if (NS_WARN_IF(error.Failed())) {
+ return MozPromise<bool, nsresult, true>::CreateAndReject(
+ error.StealNSResult(), __func__);
+ }
+
+ MOZ_ASSERT(mLocalStorage &&
+ mLocalStorage->Type() == Storage::eLocalStorage);
+
+ if (NextGenLocalStorageEnabled() && mListenerManager &&
+ mListenerManager->HasListenersFor(nsGkAtoms::onstorage)) {
+ auto object = static_cast<LSObject*>(mLocalStorage.get());
+
+ object->EnsureObserver();
+ }
+ }
+
+ // Reset the IndexedDB factory.
+ mIndexedDB = nullptr;
+
+ // Reset DOM Cache
+ mCacheStorage = nullptr;
+
+ // Reset the active cookie and storage principals
+ if (mDoc) {
+ mDoc->ClearActiveCookieAndStoragePrincipals();
+ if (mWindowGlobalChild) {
+ // XXX(farre): This is a bit backwards, but clearing the cookie
+ // principal might make us end up with a new effective storage
+ // principal on the child side than on the parent side, which
+ // means that we need to sync it. See bug 1705359.
+ mWindowGlobalChild->SetDocumentPrincipal(
+ mDoc->NodePrincipal(), mDoc->EffectiveStoragePrincipal());
+ }
+ }
+
+ // When storage access is granted the content process needs to request the
+ // updated cookie list from the parent process. Otherwise the site won't have
+ // access to unpartitioned cookies via document.cookie without a reload.
+ if (aGranted) {
+ nsIChannel* channel = mDoc->GetChannel();
+ if (channel) {
+ // The promise resolves when the updated cookie list has been received
+ // from the parent.
+ return ContentChild::UpdateCookieStatus(channel);
+ }
+ }
+ return MozPromise<bool, nsresult, true>::CreateAndResolve(true, __func__);
+}
+
+ContentMediaController* nsGlobalWindowInner::GetContentMediaController() {
+ if (mContentMediaController) {
+ return mContentMediaController;
+ }
+ if (!mBrowsingContext) {
+ return nullptr;
+ }
+
+ mContentMediaController = new ContentMediaController(mBrowsingContext->Id());
+ return mContentMediaController;
+}
+
+void nsGlobalWindowInner::SetScrollMarks(const nsTArray<uint32_t>& aScrollMarks,
+ bool aOnHScrollbar) {
+ mScrollMarks.Assign(aScrollMarks);
+ mScrollMarksOnHScrollbar = aOnHScrollbar;
+
+ // Mark the scrollbar for repainting.
+ if (mDoc) {
+ PresShell* presShell = mDoc->GetPresShell();
+ if (presShell) {
+ nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollable();
+ if (sf) {
+ sf->InvalidateScrollbars();
+ }
+ }
+ }
+}
+
+/* static */
+already_AddRefed<nsGlobalWindowInner> nsGlobalWindowInner::Create(
+ nsGlobalWindowOuter* aOuterWindow, bool aIsChrome,
+ WindowGlobalChild* aActor) {
+ RefPtr<nsGlobalWindowInner> window =
+ new nsGlobalWindowInner(aOuterWindow, aActor);
+ if (aIsChrome) {
+ window->mIsChrome = true;
+ window->mCleanMessageManager = true;
+ }
+
+ if (aActor) {
+ aActor->InitWindowGlobal(window);
+ }
+
+ window->InitWasOffline();
+ return window.forget();
+}
+
+JS::loader::ModuleLoaderBase* nsGlobalWindowInner::GetModuleLoader(
+ JSContext* aCx) {
+ Document* document = GetDocument();
+ if (!document) {
+ return nullptr;
+ }
+
+ ScriptLoader* loader = document->ScriptLoader();
+ if (!loader) {
+ return nullptr;
+ }
+
+ return loader->GetModuleLoader();
+}
+
+nsIURI* nsPIDOMWindowInner::GetDocumentURI() const {
+ return mDoc ? mDoc->GetDocumentURI() : mDocumentURI.get();
+}
+
+nsIURI* nsPIDOMWindowInner::GetDocBaseURI() const {
+ return mDoc ? mDoc->GetDocBaseURI() : mDocBaseURI.get();
+}
+
+mozilla::dom::WindowContext* nsPIDOMWindowInner::GetWindowContext() const {
+ return mWindowGlobalChild ? mWindowGlobalChild->WindowContext() : nullptr;
+}
+
+bool nsPIDOMWindowInner::RemoveFromBFCacheSync() {
+ if (Document* doc = GetExtantDoc()) {
+ return doc->RemoveFromBFCacheSync();
+ }
+ return false;
+}
+
+void nsPIDOMWindowInner::MaybeCreateDoc() {
+ // XXX: Forward to outer?
+ MOZ_ASSERT(!mDoc);
+ if (nsIDocShell* docShell = GetDocShell()) {
+ // Note that |document| here is the same thing as our mDoc, but we
+ // don't have to explicitly set the member variable because the docshell
+ // has already called SetNewDocument().
+ nsCOMPtr<Document> document = docShell->GetDocument();
+ Unused << document;
+ }
+}
+
+mozilla::dom::DocGroup* nsPIDOMWindowInner::GetDocGroup() const {
+ Document* doc = GetExtantDoc();
+ if (doc) {
+ return doc->GetDocGroup();
+ }
+ return nullptr;
+}
+
+mozilla::dom::BrowsingContextGroup*
+nsPIDOMWindowInner::GetBrowsingContextGroup() const {
+ return mBrowsingContext ? mBrowsingContext->Group() : nullptr;
+}
+
+nsIGlobalObject* nsPIDOMWindowInner::AsGlobal() {
+ return nsGlobalWindowInner::Cast(this);
+}
+
+const nsIGlobalObject* nsPIDOMWindowInner::AsGlobal() const {
+ return nsGlobalWindowInner::Cast(this);
+}
+
+RefPtr<GenericPromise>
+nsPIDOMWindowInner::SaveStorageAccessPermissionGranted() {
+ WindowContext* wc = GetWindowContext();
+ if (wc) {
+ Unused << wc->SetUsingStorageAccess(true);
+ }
+
+ return nsGlobalWindowInner::Cast(this)->StorageAccessPermissionChanged(true);
+}
+
+RefPtr<GenericPromise>
+nsPIDOMWindowInner::SaveStorageAccessPermissionRevoked() {
+ WindowContext* wc = GetWindowContext();
+ if (wc) {
+ Unused << wc->SetUsingStorageAccess(false);
+ }
+
+ return nsGlobalWindowInner::Cast(this)->StorageAccessPermissionChanged(false);
+}
+
+bool nsPIDOMWindowInner::UsingStorageAccess() {
+ WindowContext* wc = GetWindowContext();
+ if (!wc) {
+ return false;
+ }
+
+ return wc->GetUsingStorageAccess();
+}
+
+nsPIDOMWindowInner::nsPIDOMWindowInner(nsPIDOMWindowOuter* aOuterWindow,
+ WindowGlobalChild* aActor)
+ : mMutationBits(0),
+ mIsDocumentLoaded(false),
+ mIsHandlingResizeEvent(false),
+ mMayHaveDOMActivateEventListeners(false),
+ mMayHavePaintEventListener(false),
+ mMayHaveTouchEventListener(false),
+ mMayHaveSelectionChangeEventListener(false),
+ mMayHaveFormSelectEventListener(false),
+ mMayHaveMouseEnterLeaveEventListener(false),
+ mMayHavePointerEnterLeaveEventListener(false),
+ mMayHaveTransitionEventListener(false),
+ mMayHaveBeforeInputEventListenerForTelemetry(false),
+ mMutationObserverHasObservedNodeForTelemetry(false),
+ mOuterWindow(aOuterWindow),
+ mWindowID(0),
+ mHasNotifiedGlobalCreated(false),
+ mMarkedCCGeneration(0),
+ mHasTriedToCacheTopInnerWindow(false),
+ mNumOfIndexedDBDatabases(0),
+ mNumOfOpenWebSockets(0),
+ mEvent(nullptr),
+ mWindowGlobalChild(aActor),
+ mWasSuspendedByGroup(false) {
+ MOZ_ASSERT(aOuterWindow);
+ mBrowsingContext = aOuterWindow->GetBrowsingContext();
+
+ if (mWindowGlobalChild) {
+ mWindowID = aActor->InnerWindowId();
+
+ MOZ_ASSERT(mWindowGlobalChild->BrowsingContext() == mBrowsingContext);
+ } else {
+ mWindowID = nsContentUtils::GenerateWindowId();
+ }
+}
+
+nsPIDOMWindowInner::~nsPIDOMWindowInner() = default;
+
+#undef FORWARD_TO_OUTER
+#undef FORWARD_TO_OUTER_OR_THROW
+#undef FORWARD_TO_OUTER_VOID