Bug 1892041 - Part 1: Update test262 features. r=spidermonkey-reviewers,dminor
[gecko.git] / dom / base / nsGlobalWindowInner.cpp
blob7dcd265ca4a78ccd77daac6050f9b6159dd33bf8
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsGlobalWindowInner.h"
9 #include <inttypes.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <cstdint>
14 #include <new>
15 #include <type_traits>
16 #include <utility>
17 #include "AudioChannelService.h"
18 #include "AutoplayPolicy.h"
19 #include "Crypto.h"
20 #include "MainThreadUtils.h"
21 #include "Navigator.h"
22 #include "PaintWorkletImpl.h"
23 #include "SessionStorageCache.h"
24 #include "Units.h"
25 #include "VRManagerChild.h"
26 #include "WindowDestroyedEvent.h"
27 #include "WindowNamedPropertiesHandler.h"
28 #include "js/ComparisonOperators.h"
29 #include "js/CompileOptions.h"
30 #include "js/friend/PerformanceHint.h"
31 #include "js/Id.h"
32 #include "js/loader/LoadedScript.h"
33 #include "js/PropertyAndElement.h" // JS_DefineProperty, JS_GetProperty
34 #include "js/PropertyDescriptor.h"
35 #include "js/RealmOptions.h"
36 #include "js/RootingAPI.h"
37 #include "js/TypeDecls.h"
38 #include "js/Value.h"
39 #include "js/Warnings.h"
40 #include "js/shadow/String.h"
41 #include "jsapi.h"
42 #include "jsfriendapi.h"
43 #include "mozIDOMWindow.h"
44 #include "moz_external_vr.h"
45 #include "mozilla/AlreadyAddRefed.h"
46 #include "mozilla/ArrayIterator.h"
47 #include "mozilla/ArrayUtils.h"
48 #include "mozilla/Attributes.h"
49 #include "mozilla/BaseProfilerMarkersPrerequisites.h"
50 #include "mozilla/BasicEvents.h"
51 #include "mozilla/CallState.h"
52 #include "mozilla/CycleCollectedJSContext.h"
53 #include "mozilla/DOMEventTargetHelper.h"
54 #include "mozilla/ErrorResult.h"
55 #include "mozilla/EventDispatcher.h"
56 #include "mozilla/EventListenerManager.h"
57 #include "mozilla/EventQueue.h"
58 #include "mozilla/ExtensionPolicyService.h"
59 #include "mozilla/FloatingPoint.h"
60 #include "mozilla/FlushType.h"
61 #include "mozilla/Likely.h"
62 #include "mozilla/LinkedList.h"
63 #include "mozilla/LookAndFeel.h"
64 #include "mozilla/Logging.h"
65 #include "mozilla/MacroForEach.h"
66 #include "mozilla/Maybe.h"
67 #include "mozilla/OwningNonNull.h"
68 #include "mozilla/PermissionDelegateHandler.h"
69 #include "mozilla/Preferences.h"
70 #include "mozilla/PresShell.h"
71 #include "mozilla/ProcessHangMonitor.h"
72 #include "mozilla/RefPtr.h"
73 #include "mozilla/Result.h"
74 #include "mozilla/ScrollTypes.h"
75 #include "mozilla/Components.h"
76 #include "mozilla/SizeOfState.h"
77 #include "mozilla/Span.h"
78 #include "mozilla/SpinEventLoopUntil.h"
79 #include "mozilla/Sprintf.h"
80 #include "mozilla/StaticPrefs_browser.h"
81 #include "mozilla/StaticPrefs_docshell.h"
82 #include "mozilla/StaticPrefs_dom.h"
83 #include "mozilla/StaticPrefs_extensions.h"
84 #include "mozilla/StaticPrefs_privacy.h"
85 #include "mozilla/StorageAccess.h"
86 #include "mozilla/StoragePrincipalHelper.h"
87 #include "mozilla/Telemetry.h"
88 #include "mozilla/TelemetryHistogramEnums.h"
89 #include "mozilla/TimeStamp.h"
90 #include "mozilla/UniquePtr.h"
91 #include "mozilla/Unused.h"
92 #include "mozilla/dom/AudioContext.h"
93 #include "mozilla/dom/AutoEntryScript.h"
94 #include "mozilla/dom/BarProps.h"
95 #include "mozilla/dom/BindingDeclarations.h"
96 #include "mozilla/dom/BindingUtils.h"
97 #include "mozilla/dom/BrowserChild.h"
98 #include "mozilla/dom/BrowsingContext.h"
99 #include "mozilla/dom/CSPEvalChecker.h"
100 #include "mozilla/dom/CallbackDebuggerNotification.h"
101 #include "mozilla/dom/ChromeMessageBroadcaster.h"
102 #include "mozilla/dom/ClientInfo.h"
103 #include "mozilla/dom/ClientManager.h"
104 #include "mozilla/dom/ClientSource.h"
105 #include "mozilla/dom/ClientState.h"
106 #include "mozilla/dom/ClientsBinding.h"
107 #include "mozilla/dom/Console.h"
108 #include "mozilla/dom/ContentChild.h"
109 #include "mozilla/dom/ContentFrameMessageManager.h"
110 #include "mozilla/dom/ContentMediaController.h"
111 #include "mozilla/dom/CustomElementRegistry.h"
112 #include "mozilla/dom/DebuggerNotification.h"
113 #include "mozilla/dom/DebuggerNotificationBinding.h"
114 #include "mozilla/dom/DebuggerNotificationManager.h"
115 #include "mozilla/dom/DocGroup.h"
116 #include "mozilla/dom/Document.h"
117 #include "mozilla/dom/DocumentInlines.h"
118 #include "mozilla/dom/Element.h"
119 #include "mozilla/dom/Event.h"
120 #include "mozilla/dom/EventTarget.h"
121 #include "mozilla/dom/External.h"
122 #include "mozilla/dom/Fetch.h"
123 #include "mozilla/dom/Gamepad.h"
124 #include "mozilla/dom/GamepadHandle.h"
125 #include "mozilla/dom/GamepadManager.h"
126 #include "mozilla/dom/HashChangeEvent.h"
127 #include "mozilla/dom/HashChangeEventBinding.h"
128 #include "mozilla/dom/IDBFactory.h"
129 #include "mozilla/dom/IdleRequest.h"
130 #include "mozilla/dom/ImageBitmap.h"
131 #include "mozilla/dom/ImageBitmapSource.h"
132 #include "mozilla/dom/InstallTriggerBinding.h"
133 #include "mozilla/dom/IntlUtils.h"
134 #include "mozilla/dom/JSExecutionContext.h"
135 #include "mozilla/dom/LSObject.h"
136 #include "mozilla/dom/LocalStorage.h"
137 #include "mozilla/dom/LocalStorageCommon.h"
138 #include "mozilla/dom/Location.h"
139 #include "mozilla/dom/MediaDevices.h"
140 #include "mozilla/dom/MediaKeys.h"
141 #include "mozilla/dom/NavigatorBinding.h"
142 #include "mozilla/dom/Nullable.h"
143 #include "mozilla/dom/PartitionedLocalStorage.h"
144 #include "mozilla/dom/Performance.h"
145 #include "mozilla/dom/PerformanceMainThread.h"
146 #include "mozilla/dom/PopStateEvent.h"
147 #include "mozilla/dom/PopStateEventBinding.h"
148 #include "mozilla/dom/PopupBlocker.h"
149 #include "mozilla/dom/PrimitiveConversions.h"
150 #include "mozilla/dom/Promise.h"
151 #include "mozilla/dom/RootedDictionary.h"
152 #include "mozilla/dom/WebTaskSchedulerMainThread.h"
153 #include "mozilla/dom/ScriptLoader.h"
154 #include "mozilla/dom/ScriptSettings.h"
155 #include "mozilla/dom/ServiceWorker.h"
156 #include "mozilla/dom/ServiceWorkerDescriptor.h"
157 #include "mozilla/dom/ServiceWorkerRegistration.h"
158 #include "mozilla/dom/SessionStorageManager.h"
159 #include "mozilla/dom/SharedWorker.h"
160 #include "mozilla/dom/Storage.h"
161 #include "mozilla/dom/StorageEvent.h"
162 #include "mozilla/dom/StorageEventBinding.h"
163 #include "mozilla/dom/StorageNotifierService.h"
164 #include "mozilla/dom/StorageUtils.h"
165 #include "mozilla/dom/TabMessageTypes.h"
166 #include "mozilla/dom/Timeout.h"
167 #include "mozilla/dom/TimeoutHandler.h"
168 #include "mozilla/dom/TimeoutManager.h"
169 #include "mozilla/dom/ToJSValue.h"
170 #include "mozilla/dom/TrustedTypePolicyFactory.h"
171 #include "mozilla/dom/VRDisplay.h"
172 #include "mozilla/dom/VRDisplayEvent.h"
173 #include "mozilla/dom/VRDisplayEventBinding.h"
174 #include "mozilla/dom/VREventObserver.h"
175 #include "mozilla/dom/VisualViewport.h"
176 #include "mozilla/dom/WebIDLGlobalNameHash.h"
177 #include "mozilla/dom/WindowBinding.h"
178 #include "mozilla/dom/WindowContext.h"
179 #include "mozilla/dom/WindowGlobalChild.h"
180 #include "mozilla/dom/WindowProxyHolder.h"
181 #include "mozilla/dom/WorkerCommon.h"
182 #include "mozilla/dom/Worklet.h"
183 #include "mozilla/dom/XRPermissionRequest.h"
184 #include "mozilla/dom/cache/CacheStorage.h"
185 #include "mozilla/dom/cache/Types.h"
186 #include "mozilla/glean/bindings/Glean.h"
187 #include "mozilla/glean/bindings/GleanPings.h"
188 #include "mozilla/extensions/WebExtensionPolicy.h"
189 #include "mozilla/fallible.h"
190 #include "mozilla/gfx/BasePoint.h"
191 #include "mozilla/gfx/BaseRect.h"
192 #include "mozilla/gfx/BaseSize.h"
193 #include "mozilla/gfx/Rect.h"
194 #include "mozilla/gfx/Types.h"
195 #include "mozilla/intl/LocaleService.h"
196 #include "mozilla/ipc/BackgroundUtils.h"
197 #include "mozilla/ipc/PBackgroundSharedTypes.h"
198 #include "mozilla/net/CookieJarSettings.h"
199 #include "nsAtom.h"
200 #include "nsBaseHashtable.h"
201 #include "nsCCUncollectableMarker.h"
202 #include "nsCOMPtr.h"
203 #include "nsCRT.h"
204 #include "nsCRTGlue.h"
205 #include "nsCanvasFrame.h"
206 #include "nsCharTraits.h"
207 #include "nsCheapSets.h"
208 #include "nsContentUtils.h"
209 #include "nsCoord.h"
210 #include "nsCycleCollectionNoteChild.h"
211 #include "nsCycleCollectionTraversalCallback.h"
212 #include "nsDOMNavigationTiming.h"
213 #include "nsDebug.h"
214 #include "nsDeviceContext.h"
215 #include "nsDocShell.h"
216 #include "nsFocusManager.h"
217 #include "nsFrameMessageManager.h"
218 #include "nsGkAtoms.h"
219 #include "nsGlobalWindowOuter.h"
220 #include "nsHashKeys.h"
221 #include "nsHistory.h"
222 #include "nsIAddonPolicyService.h"
223 #include "nsIArray.h"
224 #include "nsIBaseWindow.h"
225 #include "nsIBrowserChild.h"
226 #include "nsICancelableRunnable.h"
227 #include "nsIChannel.h"
228 #include "nsIClipboard.h"
229 #include "nsIContentSecurityPolicy.h"
230 #include "nsIControllers.h"
231 #include "nsICookieJarSettings.h"
232 #include "nsICookieService.h"
233 #include "nsID.h"
234 #include "nsIDOMStorageManager.h"
235 #include "nsIDeviceSensors.h"
236 #include "nsIDocShell.h"
237 #include "nsIDocShellTreeItem.h"
238 #include "nsIDocShellTreeOwner.h"
239 #include "nsIDocumentLoader.h"
240 #include "nsIDragService.h"
241 #include "nsIFocusManager.h"
242 #include "nsIFrame.h"
243 #include "nsIGlobalObject.h"
244 #include "nsIIOService.h"
245 #include "nsIIdleRunnable.h"
246 #include "nsIInterfaceRequestorUtils.h"
247 #include "nsILoadContext.h"
248 #include "nsILoadGroup.h"
249 #include "nsILoadInfo.h"
250 #include "nsINamed.h"
251 #include "nsINode.h"
252 #include "nsIObserver.h"
253 #include "nsIObserverService.h"
254 #include "nsIPermission.h"
255 #include "nsIPermissionManager.h"
256 #include "nsIPrefBranch.h"
257 #include "nsIPrincipal.h"
258 #include "nsIPrompt.h"
259 #include "nsIRunnable.h"
260 #include "nsIScreen.h"
261 #include "nsIScreenManager.h"
262 #include "nsIScriptContext.h"
263 #include "nsIScriptGlobalObject.h"
264 #include "nsIScriptObjectPrincipal.h"
265 #include "nsIScrollableFrame.h"
266 #include "nsISerialEventTarget.h"
267 #include "nsISimpleEnumerator.h"
268 #include "nsISizeOfEventTarget.h"
269 #include "nsISlowScriptDebug.h"
270 #include "nsISupportsUtils.h"
271 #include "nsIThread.h"
272 #include "nsITimedChannel.h"
273 #include "nsIURI.h"
274 #include "nsIWeakReference.h"
275 #include "nsIWebBrowserChrome.h"
276 #include "nsIWebNavigation.h"
277 #include "nsIWebProgressListener.h"
278 #include "nsIWidget.h"
279 #include "nsIWidgetListener.h"
280 #include "nsIXULRuntime.h"
281 #include "nsJSPrincipals.h"
282 #include "nsJSUtils.h"
283 #include "nsLayoutStatics.h"
284 #include "nsLiteralString.h"
285 #include "nsNetUtil.h"
286 #include "nsPIDOMWindow.h"
287 #include "nsPIDOMWindowInlines.h"
288 #include "nsPIWindowRoot.h"
289 #include "nsPoint.h"
290 #include "nsPresContext.h"
291 #include "nsQueryObject.h"
292 #include "nsSandboxFlags.h"
293 #include "nsScreen.h"
294 #include "nsServiceManagerUtils.h"
295 #include "nsString.h"
296 #include "nsStringFlags.h"
297 #include "nsStringFwd.h"
298 #include "nsTArray.h"
299 #include "nsTLiteralString.h"
300 #include "nsTObserverArray.h"
301 #include "nsTStringRepr.h"
302 #include "nsThreadUtils.h"
303 #include "nsWeakReference.h"
304 #include "nsWindowMemoryReporter.h"
305 #include "nsWindowSizes.h"
306 #include "nsWrapperCache.h"
307 #include "nsWrapperCacheInlines.h"
308 #include "nsXULAppAPI.h"
309 #include "nsrootidl.h"
310 #include "prclist.h"
311 #include "prtypes.h"
312 #include "xpcprivate.h"
313 #include "xpcpublic.h"
315 #include "nsIDOMXULControlElement.h"
317 #ifdef NS_PRINTING
318 # include "nsIPrintSettings.h"
319 #endif
321 #ifdef MOZ_WEBSPEECH
322 # include "mozilla/dom/SpeechSynthesis.h"
323 #endif
325 #ifdef ANDROID
326 # include <android/log.h>
327 #endif
329 #ifdef XP_WIN
330 # include "mozilla/Debug.h"
331 # include <process.h>
332 # define getpid _getpid
333 #else
334 # include <unistd.h> // for getpid()
335 #endif
337 using namespace mozilla;
338 using namespace mozilla::dom;
339 using namespace mozilla::dom::ipc;
340 using mozilla::TimeDuration;
341 using mozilla::TimeStamp;
342 using mozilla::dom::GamepadHandle;
343 using mozilla::dom::cache::CacheStorage;
345 #define FORWARD_TO_OUTER(method, args, err_rval) \
346 PR_BEGIN_MACRO \
347 RefPtr<nsGlobalWindowOuter> outer = GetOuterWindowInternal(); \
348 if (!HasActiveDocument()) { \
349 NS_WARNING(outer ? "Inner window does not have active document." \
350 : "No outer window available!"); \
351 return err_rval; \
353 return outer->method args; \
354 PR_END_MACRO
356 static nsGlobalWindowOuter* GetOuterWindowForForwarding(
357 nsGlobalWindowInner* aInner, ErrorResult& aError) {
358 nsGlobalWindowOuter* outer = aInner->GetOuterWindowInternal();
359 if (MOZ_LIKELY(aInner->HasActiveDocument())) {
360 return outer;
362 if (!outer) {
363 NS_WARNING("No outer window available!");
364 aError.Throw(NS_ERROR_NOT_INITIALIZED);
365 } else {
366 aError.Throw(NS_ERROR_XPC_SECURITY_MANAGER_VETO);
368 return nullptr;
371 #define FORWARD_TO_OUTER_OR_THROW(method, args, rv, err_rval) \
372 PR_BEGIN_MACRO \
373 RefPtr<nsGlobalWindowOuter> outer = GetOuterWindowForForwarding(this, rv); \
374 if (MOZ_LIKELY(outer)) { \
375 return outer->method args; \
377 return err_rval; \
378 PR_END_MACRO
380 #define FORWARD_TO_OUTER_VOID(method, args) \
381 PR_BEGIN_MACRO \
382 RefPtr<nsGlobalWindowOuter> outer = GetOuterWindowInternal(); \
383 if (!HasActiveDocument()) { \
384 NS_WARNING(outer ? "Inner window does not have active document." \
385 : "No outer window available!"); \
386 return; \
388 outer->method args; \
389 return; \
390 PR_END_MACRO
392 #define ENSURE_ACTIVE_DOCUMENT(errorresult, err_rval) \
393 PR_BEGIN_MACRO \
394 if (MOZ_UNLIKELY(!HasActiveDocument())) { \
395 aError.Throw(NS_ERROR_XPC_SECURITY_MANAGER_VETO); \
396 return err_rval; \
398 PR_END_MACRO
400 #define DOM_TOUCH_LISTENER_ADDED "dom-touch-listener-added"
401 #define MEMORY_PRESSURE_OBSERVER_TOPIC "memory-pressure"
402 #define PERMISSION_CHANGED_TOPIC "perm-changed"
404 static LazyLogModule gDOMLeakPRLogInner("DOMLeakInner");
405 extern mozilla::LazyLogModule gTimeoutLog;
407 #ifdef DEBUG
408 static LazyLogModule gDocShellAndDOMWindowLeakLogging(
409 "DocShellAndDOMWindowLeak");
410 #endif
412 static FILE* gDumpFile = nullptr;
414 nsGlobalWindowInner::InnerWindowByIdTable*
415 nsGlobalWindowInner::sInnerWindowsById = nullptr;
417 bool nsGlobalWindowInner::sDragServiceDisabled = false;
418 bool nsGlobalWindowInner::sMouseDown = false;
421 * An indirect observer object that means we don't have to implement nsIObserver
422 * on nsGlobalWindow, where any script could see it.
424 class nsGlobalWindowObserver final : public nsIObserver,
425 public nsIInterfaceRequestor,
426 public StorageNotificationObserver {
427 public:
428 explicit nsGlobalWindowObserver(nsGlobalWindowInner* aWindow)
429 : mWindow(aWindow) {}
430 NS_DECL_ISUPPORTS
431 NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
432 const char16_t* aData) override {
433 if (!mWindow) return NS_OK;
434 return mWindow->Observe(aSubject, aTopic, aData);
436 void Forget() { mWindow = nullptr; }
437 NS_IMETHOD GetInterface(const nsIID& aIID, void** aResult) override {
438 if (mWindow && aIID.Equals(NS_GET_IID(nsIDOMWindow)) && mWindow) {
439 return mWindow->QueryInterface(aIID, aResult);
441 return NS_NOINTERFACE;
444 void ObserveStorageNotification(StorageEvent* aEvent,
445 const char16_t* aStorageType,
446 bool aPrivateBrowsing) override {
447 if (mWindow) {
448 mWindow->ObserveStorageNotification(aEvent, aStorageType,
449 aPrivateBrowsing);
453 nsIPrincipal* GetEffectiveCookiePrincipal() const override {
454 return mWindow ? mWindow->GetEffectiveCookiePrincipal() : nullptr;
457 nsIPrincipal* GetEffectiveStoragePrincipal() const override {
458 return mWindow ? mWindow->GetEffectiveStoragePrincipal() : nullptr;
461 bool IsPrivateBrowsing() const override {
462 return mWindow ? mWindow->IsPrivateBrowsing() : false;
465 nsIEventTarget* GetEventTarget() const override {
466 return mWindow ? mWindow->SerialEventTarget() : nullptr;
469 private:
470 ~nsGlobalWindowObserver() = default;
472 // This reference is non-owning and safe because it's cleared by
473 // nsGlobalWindowInner::FreeInnerObjects().
474 nsGlobalWindowInner* MOZ_NON_OWNING_REF mWindow;
477 NS_IMPL_ISUPPORTS(nsGlobalWindowObserver, nsIObserver, nsIInterfaceRequestor)
479 class IdleRequestExecutor;
481 class IdleRequestExecutorTimeoutHandler final : public TimeoutHandler {
482 public:
483 explicit IdleRequestExecutorTimeoutHandler(IdleRequestExecutor* aExecutor)
484 : mExecutor(aExecutor) {}
486 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
487 NS_DECL_CYCLE_COLLECTION_CLASS(IdleRequestExecutorTimeoutHandler)
489 bool Call(const char* /* unused */) override;
491 private:
492 ~IdleRequestExecutorTimeoutHandler() override = default;
493 RefPtr<IdleRequestExecutor> mExecutor;
496 NS_IMPL_CYCLE_COLLECTION(IdleRequestExecutorTimeoutHandler, mExecutor)
498 NS_IMPL_CYCLE_COLLECTING_ADDREF(IdleRequestExecutorTimeoutHandler)
499 NS_IMPL_CYCLE_COLLECTING_RELEASE(IdleRequestExecutorTimeoutHandler)
501 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestExecutorTimeoutHandler)
502 NS_INTERFACE_MAP_ENTRY(nsISupports)
503 NS_INTERFACE_MAP_END
505 class IdleRequestExecutor final : public nsIRunnable,
506 public nsICancelableRunnable,
507 public nsINamed,
508 public nsIIdleRunnable {
509 public:
510 explicit IdleRequestExecutor(nsGlobalWindowInner* aWindow)
511 : mDispatched(false), mDeadline(TimeStamp::Now()), mWindow(aWindow) {
512 MOZ_DIAGNOSTIC_ASSERT(mWindow);
514 mIdlePeriodLimit = {mDeadline, mWindow->LastIdleRequestHandle()};
515 mDelayedExecutorDispatcher = new IdleRequestExecutorTimeoutHandler(this);
518 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
519 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(IdleRequestExecutor, nsIRunnable)
521 NS_DECL_NSIRUNNABLE
522 NS_DECL_NSINAMED
523 nsresult Cancel() override;
524 void SetDeadline(TimeStamp aDeadline) override;
526 bool IsCancelled() const { return !mWindow || mWindow->IsDying(); }
527 // Checks if aRequest shouldn't execute in the current idle period
528 // since it has been queued from a chained call to
529 // requestIdleCallback from within a running idle callback.
530 bool IneligibleForCurrentIdlePeriod(IdleRequest* aRequest) const {
531 return aRequest->Handle() >= mIdlePeriodLimit.mLastRequestIdInIdlePeriod &&
532 TimeStamp::Now() <= mIdlePeriodLimit.mEndOfIdlePeriod;
535 void MaybeUpdateIdlePeriodLimit();
537 // Maybe dispatch the IdleRequestExecutor. MabyeDispatch will
538 // schedule a delayed dispatch if the associated window is in the
539 // background or if given a time to wait until dispatching.
540 void MaybeDispatch(TimeStamp aDelayUntil = TimeStamp());
541 void ScheduleDispatch();
543 private:
544 struct IdlePeriodLimit {
545 TimeStamp mEndOfIdlePeriod;
546 uint32_t mLastRequestIdInIdlePeriod;
549 void DelayedDispatch(uint32_t aDelay);
551 ~IdleRequestExecutor() override = default;
553 bool mDispatched;
554 TimeStamp mDeadline;
555 IdlePeriodLimit mIdlePeriodLimit;
556 RefPtr<nsGlobalWindowInner> mWindow;
557 // The timeout handler responsible for dispatching this executor in
558 // the case of immediate dispatch to the idle queue isn't
559 // desirable. This is used if we've dispatched all idle callbacks
560 // that are allowed to run in the current idle period, or if the
561 // associated window is currently in the background.
562 RefPtr<TimeoutHandler> mDelayedExecutorDispatcher;
563 // If not Nothing() then this value is the handle to the currently
564 // scheduled delayed executor dispatcher. This is needed to be able
565 // to cancel the timeout handler in case of the executor being
566 // cancelled.
567 Maybe<int32_t> mDelayedExecutorHandle;
570 NS_IMPL_CYCLE_COLLECTION(IdleRequestExecutor, mWindow,
571 mDelayedExecutorDispatcher)
573 NS_IMPL_CYCLE_COLLECTING_ADDREF(IdleRequestExecutor)
574 NS_IMPL_CYCLE_COLLECTING_RELEASE(IdleRequestExecutor)
576 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestExecutor)
577 NS_INTERFACE_MAP_ENTRY(nsIRunnable)
578 NS_INTERFACE_MAP_ENTRY(nsICancelableRunnable)
579 NS_INTERFACE_MAP_ENTRY(nsINamed)
580 NS_INTERFACE_MAP_ENTRY(nsIIdleRunnable)
581 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRunnable)
582 NS_INTERFACE_MAP_END
584 NS_IMETHODIMP
585 IdleRequestExecutor::GetName(nsACString& aName) {
586 aName.AssignLiteral("IdleRequestExecutor");
587 return NS_OK;
590 // MOZ_CAN_RUN_SCRIPT_BOUNDARY until nsIRunnable::Run is MOZ_CAN_RUN_SCRIPT.
591 // See bug 1535398.
592 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP IdleRequestExecutor::Run() {
593 MOZ_ASSERT(NS_IsMainThread());
595 mDispatched = false;
596 if (mWindow) {
597 RefPtr<nsGlobalWindowInner> window(mWindow);
598 window->ExecuteIdleRequest(mDeadline);
601 return NS_OK;
604 nsresult IdleRequestExecutor::Cancel() {
605 MOZ_ASSERT(NS_IsMainThread());
607 if (mDelayedExecutorHandle && mWindow) {
608 mWindow->TimeoutManager().ClearTimeout(
609 mDelayedExecutorHandle.value(), Timeout::Reason::eIdleCallbackTimeout);
612 mWindow = nullptr;
613 return NS_OK;
616 void IdleRequestExecutor::SetDeadline(TimeStamp aDeadline) {
617 MOZ_ASSERT(NS_IsMainThread());
619 if (!mWindow) {
620 return;
623 mDeadline = aDeadline;
626 void IdleRequestExecutor::MaybeUpdateIdlePeriodLimit() {
627 if (TimeStamp::Now() > mIdlePeriodLimit.mEndOfIdlePeriod) {
628 mIdlePeriodLimit = {mDeadline, mWindow->LastIdleRequestHandle()};
632 void IdleRequestExecutor::MaybeDispatch(TimeStamp aDelayUntil) {
633 // If we've already dispatched the executor we don't want to do it
634 // again. Also, if we've called IdleRequestExecutor::Cancel mWindow
635 // will be null, which indicates that we shouldn't dispatch this
636 // executor either.
637 if (mDispatched || IsCancelled()) {
638 return;
641 mDispatched = true;
643 nsPIDOMWindowOuter* outer = mWindow->GetOuterWindow();
644 if (outer && outer->IsBackground()) {
645 // Set a timeout handler with a timeout of 0 ms to throttle idle
646 // callback requests coming from a backround window using
647 // background timeout throttling.
648 DelayedDispatch(0);
649 return;
652 TimeStamp now = TimeStamp::Now();
653 if (!aDelayUntil || aDelayUntil < now) {
654 ScheduleDispatch();
655 return;
658 TimeDuration delay = aDelayUntil - now;
659 DelayedDispatch(static_cast<uint32_t>(delay.ToMilliseconds()));
662 void IdleRequestExecutor::ScheduleDispatch() {
663 MOZ_ASSERT(mWindow);
664 mDelayedExecutorHandle = Nothing();
665 RefPtr<IdleRequestExecutor> request = this;
666 NS_DispatchToCurrentThreadQueue(request.forget(), EventQueuePriority::Idle);
669 void IdleRequestExecutor::DelayedDispatch(uint32_t aDelay) {
670 MOZ_ASSERT(mWindow);
671 MOZ_ASSERT(mDelayedExecutorHandle.isNothing());
672 int32_t handle;
673 mWindow->TimeoutManager().SetTimeout(
674 mDelayedExecutorDispatcher, aDelay, false,
675 Timeout::Reason::eIdleCallbackTimeout, &handle);
676 mDelayedExecutorHandle = Some(handle);
679 bool IdleRequestExecutorTimeoutHandler::Call(const char* /* unused */) {
680 if (!mExecutor->IsCancelled()) {
681 mExecutor->ScheduleDispatch();
683 return true;
686 void nsGlobalWindowInner::ScheduleIdleRequestDispatch() {
687 AssertIsOnMainThread();
689 if (!mIdleRequestExecutor) {
690 mIdleRequestExecutor = new IdleRequestExecutor(this);
693 mIdleRequestExecutor->MaybeDispatch();
696 void nsGlobalWindowInner::SuspendIdleRequests() {
697 if (mIdleRequestExecutor) {
698 mIdleRequestExecutor->Cancel();
699 mIdleRequestExecutor = nullptr;
703 void nsGlobalWindowInner::ResumeIdleRequests() {
704 MOZ_ASSERT(!mIdleRequestExecutor);
706 ScheduleIdleRequestDispatch();
709 void nsGlobalWindowInner::RemoveIdleCallback(
710 mozilla::dom::IdleRequest* aRequest) {
711 AssertIsOnMainThread();
713 if (aRequest->HasTimeout()) {
714 mTimeoutManager->ClearTimeout(aRequest->GetTimeoutHandle(),
715 Timeout::Reason::eIdleCallbackTimeout);
718 aRequest->removeFrom(mIdleRequestCallbacks);
721 void nsGlobalWindowInner::RunIdleRequest(IdleRequest* aRequest,
722 DOMHighResTimeStamp aDeadline,
723 bool aDidTimeout) {
724 AssertIsOnMainThread();
725 // XXXbz Do we still need this RefPtr? MOZ_CAN_RUN_SCRIPT should
726 // guarantee that caller is holding a strong ref on the stack.
727 RefPtr<IdleRequest> request(aRequest);
728 RemoveIdleCallback(request);
729 request->IdleRun(this, aDeadline, aDidTimeout);
732 void nsGlobalWindowInner::ExecuteIdleRequest(TimeStamp aDeadline) {
733 AssertIsOnMainThread();
734 RefPtr<IdleRequest> request = mIdleRequestCallbacks.getFirst();
736 if (!request) {
737 // There are no more idle requests, so stop scheduling idle
738 // request callbacks.
739 return;
742 // If the request that we're trying to execute has been queued
743 // during the current idle period, then dispatch it again at the end
744 // of the idle period.
745 if (mIdleRequestExecutor->IneligibleForCurrentIdlePeriod(request)) {
746 mIdleRequestExecutor->MaybeDispatch(aDeadline);
747 return;
750 DOMHighResTimeStamp deadline = 0.0;
752 if (Performance* perf = GetPerformance()) {
753 deadline = perf->GetDOMTiming()->TimeStampToDOMHighRes(aDeadline);
756 mIdleRequestExecutor->MaybeUpdateIdlePeriodLimit();
757 RunIdleRequest(request, deadline, false);
759 // Running the idle callback could've suspended the window, in which
760 // case mIdleRequestExecutor will be null.
761 if (mIdleRequestExecutor) {
762 mIdleRequestExecutor->MaybeDispatch();
766 class IdleRequestTimeoutHandler final : public TimeoutHandler {
767 public:
768 IdleRequestTimeoutHandler(JSContext* aCx, IdleRequest* aIdleRequest,
769 nsPIDOMWindowInner* aWindow)
770 : TimeoutHandler(aCx), mIdleRequest(aIdleRequest), mWindow(aWindow) {}
772 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
773 NS_DECL_CYCLE_COLLECTION_CLASS(IdleRequestTimeoutHandler)
775 MOZ_CAN_RUN_SCRIPT bool Call(const char* /* unused */) override {
776 RefPtr<nsGlobalWindowInner> window(nsGlobalWindowInner::Cast(mWindow));
777 RefPtr<IdleRequest> request(mIdleRequest);
778 window->RunIdleRequest(request, 0.0, true);
779 return true;
782 private:
783 ~IdleRequestTimeoutHandler() override = default;
785 RefPtr<IdleRequest> mIdleRequest;
786 nsCOMPtr<nsPIDOMWindowInner> mWindow;
789 NS_IMPL_CYCLE_COLLECTION(IdleRequestTimeoutHandler, mIdleRequest, mWindow)
791 NS_IMPL_CYCLE_COLLECTING_ADDREF(IdleRequestTimeoutHandler)
792 NS_IMPL_CYCLE_COLLECTING_RELEASE(IdleRequestTimeoutHandler)
794 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestTimeoutHandler)
795 NS_INTERFACE_MAP_ENTRY(nsISupports)
796 NS_INTERFACE_MAP_END
798 uint32_t nsGlobalWindowInner::RequestIdleCallback(
799 JSContext* aCx, IdleRequestCallback& aCallback,
800 const IdleRequestOptions& aOptions, ErrorResult& aError) {
801 AssertIsOnMainThread();
803 if (IsDying()) {
804 return 0;
807 uint32_t handle = mIdleRequestCallbackCounter++;
809 RefPtr<IdleRequest> request = new IdleRequest(&aCallback, handle);
811 if (aOptions.mTimeout.WasPassed()) {
812 int32_t timeoutHandle;
813 RefPtr<TimeoutHandler> handler(
814 new IdleRequestTimeoutHandler(aCx, request, this));
816 nsresult rv = mTimeoutManager->SetTimeout(
817 handler, aOptions.mTimeout.Value(), false,
818 Timeout::Reason::eIdleCallbackTimeout, &timeoutHandle);
820 if (NS_WARN_IF(NS_FAILED(rv))) {
821 return 0;
824 request->SetTimeoutHandle(timeoutHandle);
827 mIdleRequestCallbacks.insertBack(request);
829 if (!IsSuspended()) {
830 ScheduleIdleRequestDispatch();
833 return handle;
836 void nsGlobalWindowInner::CancelIdleCallback(uint32_t aHandle) {
837 for (IdleRequest* r : mIdleRequestCallbacks) {
838 if (r->Handle() == aHandle) {
839 RemoveIdleCallback(r);
840 break;
845 void nsGlobalWindowInner::DisableIdleCallbackRequests() {
846 if (mIdleRequestExecutor) {
847 mIdleRequestExecutor->Cancel();
848 mIdleRequestExecutor = nullptr;
851 while (!mIdleRequestCallbacks.isEmpty()) {
852 RefPtr<IdleRequest> request = mIdleRequestCallbacks.getFirst();
853 RemoveIdleCallback(request);
857 bool nsGlobalWindowInner::IsBackgroundInternal() const {
858 return !mOuterWindow || mOuterWindow->IsBackground();
861 class PromiseDocumentFlushedResolver final {
862 public:
863 PromiseDocumentFlushedResolver(Promise* aPromise,
864 PromiseDocumentFlushedCallback& aCallback)
865 : mPromise(aPromise), mCallback(&aCallback) {}
867 virtual ~PromiseDocumentFlushedResolver() = default;
869 void Call() {
870 nsMutationGuard guard;
871 ErrorResult error;
872 JS::Rooted<JS::Value> returnVal(RootingCx());
873 mCallback->Call(&returnVal, error);
875 if (error.Failed()) {
876 mPromise->MaybeReject(std::move(error));
877 } else if (guard.Mutated(0)) {
878 // Something within the callback mutated the DOM.
879 mPromise->MaybeRejectWithNoModificationAllowedError(
880 "DOM mutated from promiseDocumentFlushed callbacks");
881 } else {
882 mPromise->MaybeResolve(returnVal);
886 RefPtr<Promise> mPromise;
887 RefPtr<PromiseDocumentFlushedCallback> mCallback;
890 //*****************************************************************************
891 //*** nsGlobalWindowInner: Object Management
892 //*****************************************************************************
894 nsGlobalWindowInner::nsGlobalWindowInner(nsGlobalWindowOuter* aOuterWindow,
895 WindowGlobalChild* aActor)
896 : nsPIDOMWindowInner(aOuterWindow, aActor),
897 mHasOrientationChangeListeners(false),
898 mWasOffline(false),
899 mHasHadSlowScript(false),
900 mIsChrome(false),
901 mCleanMessageManager(false),
902 mNeedsFocus(true),
903 mHasFocus(false),
904 mFocusByKeyOccurred(false),
905 mDidFireDocElemInserted(false),
906 mHasGamepad(false),
907 mHasXRSession(false),
908 mHasVRDisplayActivateEvents(false),
909 mXRRuntimeDetectionInFlight(false),
910 mXRPermissionRequestInFlight(false),
911 mXRPermissionGranted(false),
912 mWasCurrentInnerWindow(false),
913 mHasSeenGamepadInput(false),
914 mHintedWasLoading(false),
915 mHasOpenedExternalProtocolFrame(false),
916 mScrollMarksOnHScrollbar(false),
917 mStorageAllowedReasonCache(0),
918 mSuspendDepth(0),
919 mFreezeDepth(0),
920 #ifdef DEBUG
921 mSerial(0),
922 #endif
923 mFocusMethod(0),
924 mIdleRequestCallbackCounter(1),
925 mIdleRequestExecutor(nullptr),
926 mObservingRefresh(false),
927 mIteratingDocumentFlushedResolvers(false),
928 mCanSkipCCGeneration(0) {
929 mIsInnerWindow = true;
931 AssertIsOnMainThread();
932 SetIsOnMainThread();
933 nsLayoutStatics::AddRef();
935 // Initialize the PRCList (this).
936 PR_INIT_CLIST(this);
938 // add this inner window to the outer window list of inners.
939 PR_INSERT_AFTER(this, aOuterWindow);
941 mTimeoutManager = MakeUnique<dom::TimeoutManager>(
942 *this, StaticPrefs::dom_timeout_max_idle_defer_ms());
944 mObserver = new nsGlobalWindowObserver(this);
945 if (nsCOMPtr<nsIObserverService> os = services::GetObserverService()) {
946 // Watch for online/offline status changes so we can fire events. Use
947 // a strong reference.
948 os->AddObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC, false);
949 os->AddObserver(mObserver, MEMORY_PRESSURE_OBSERVER_TOPIC, false);
950 os->AddObserver(mObserver, PERMISSION_CHANGED_TOPIC, false);
951 os->AddObserver(mObserver, "screen-information-changed", false);
954 Preferences::AddStrongObserver(mObserver, "intl.accept_languages");
956 // Watch for storage notifications so we can fire storage events.
957 RefPtr<StorageNotifierService> sns = StorageNotifierService::GetOrCreate();
958 if (sns) {
959 sns->Register(mObserver);
962 if (XRE_IsContentProcess()) {
963 nsCOMPtr<nsIDocShell> docShell = GetDocShell();
964 if (docShell) {
965 mBrowserChild = docShell->GetBrowserChild();
969 if (gDumpFile == nullptr) {
970 nsAutoCString fname;
971 Preferences::GetCString("browser.dom.window.dump.file", fname);
972 if (!fname.IsEmpty()) {
973 // If this fails to open, Dump() knows to just go to stdout on null.
974 gDumpFile = fopen(fname.get(), "wb+");
975 } else {
976 gDumpFile = stdout;
980 #ifdef DEBUG
981 mSerial = nsContentUtils::InnerOrOuterWindowCreated();
983 MOZ_LOG(gDocShellAndDOMWindowLeakLogging, LogLevel::Info,
984 ("++DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p]\n",
985 nsContentUtils::GetCurrentInnerOrOuterWindowCount(),
986 static_cast<void*>(ToCanonicalSupports(this)), getpid(), mSerial,
987 static_cast<void*>(ToCanonicalSupports(aOuterWindow))));
988 #endif
990 MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
991 ("DOMWINDOW %p created outer=%p", this, aOuterWindow));
993 // Add ourselves to the inner windows list.
994 MOZ_ASSERT(sInnerWindowsById, "Inner Windows hash table must be created!");
995 MOZ_ASSERT(!sInnerWindowsById->Contains(mWindowID),
996 "This window shouldn't be in the hash table yet!");
997 // We seem to see crashes in release builds because of null
998 // |sInnerWindowsById|.
999 if (sInnerWindowsById) {
1000 sInnerWindowsById->InsertOrUpdate(mWindowID, this);
1004 #ifdef DEBUG
1006 /* static */
1007 void nsGlobalWindowInner::AssertIsOnMainThread() {
1008 MOZ_ASSERT(NS_IsMainThread());
1011 #endif // DEBUG
1013 /* static */
1014 void nsGlobalWindowInner::Init() {
1015 AssertIsOnMainThread();
1017 NS_ASSERTION(gDOMLeakPRLogInner,
1018 "gDOMLeakPRLogInner should have been initialized!");
1020 sInnerWindowsById = new InnerWindowByIdTable();
1023 nsGlobalWindowInner::~nsGlobalWindowInner() {
1024 AssertIsOnMainThread();
1025 MOZ_ASSERT(!mHintedWasLoading);
1027 if (IsChromeWindow()) {
1028 MOZ_ASSERT(mCleanMessageManager,
1029 "chrome windows may always disconnect the msg manager");
1031 DisconnectAndClearGroupMessageManagers();
1033 if (mChromeFields.mMessageManager) {
1034 static_cast<nsFrameMessageManager*>(mChromeFields.mMessageManager.get())
1035 ->Disconnect();
1038 mCleanMessageManager = false;
1041 // In most cases this should already have been called, but call it again
1042 // here to catch any corner cases.
1043 FreeInnerObjects();
1045 if (sInnerWindowsById) {
1046 sInnerWindowsById->Remove(mWindowID);
1049 nsContentUtils::InnerOrOuterWindowDestroyed();
1051 #ifdef DEBUG
1052 if (MOZ_LOG_TEST(gDocShellAndDOMWindowLeakLogging, LogLevel::Info)) {
1053 nsAutoCString url;
1054 if (mLastOpenedURI) {
1055 url = mLastOpenedURI->GetSpecOrDefault();
1057 // Data URLs can be very long, so truncate to avoid flooding the log.
1058 const uint32_t maxURLLength = 1000;
1059 if (url.Length() > maxURLLength) {
1060 url.Truncate(maxURLLength);
1064 nsGlobalWindowOuter* outer = nsGlobalWindowOuter::Cast(mOuterWindow);
1065 MOZ_LOG(
1066 gDocShellAndDOMWindowLeakLogging, LogLevel::Info,
1067 ("--DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p] [url = "
1068 "%s]\n",
1069 nsContentUtils::GetCurrentInnerOrOuterWindowCount(),
1070 static_cast<void*>(ToCanonicalSupports(this)), getpid(), mSerial,
1071 static_cast<void*>(ToCanonicalSupports(outer)), url.get()));
1073 #endif
1074 MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
1075 ("DOMWINDOW %p destroyed", this));
1077 Telemetry::Accumulate(Telemetry::INNERWINDOWS_WITH_MUTATION_LISTENERS,
1078 mMutationBits ? 1 : 0);
1080 // An inner window is destroyed, pull it out of the outer window's
1081 // list if inner windows.
1083 PR_REMOVE_LINK(this);
1085 // If our outer window's inner window is this window, null out the
1086 // outer window's reference to this window that's being deleted.
1087 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
1088 if (outer) {
1089 outer->MaybeClearInnerWindow(this);
1092 // We don't have to leave the tab group if we are an inner window.
1094 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
1095 if (ac) ac->RemoveWindowAsListener(this);
1097 nsLayoutStatics::Release();
1100 // static
1101 void nsGlobalWindowInner::ShutDown() {
1102 AssertIsOnMainThread();
1104 if (gDumpFile && gDumpFile != stdout) {
1105 fclose(gDumpFile);
1107 gDumpFile = nullptr;
1109 delete sInnerWindowsById;
1110 sInnerWindowsById = nullptr;
1113 void nsGlobalWindowInner::FreeInnerObjects() {
1114 if (IsDying()) {
1115 return;
1117 StartDying();
1119 if (mDoc && mDoc->GetWindowContext()) {
1120 // The document is about to lose its window, so this is a good time to send
1121 // our page use counters.
1123 // (We also do this in Document::SetScriptGlobalObject(nullptr), which
1124 // catches most cases of documents losing their window, but not all.)
1125 mDoc->SendPageUseCounters();
1128 // Make sure that this is called before we null out the document and
1129 // other members that the window destroyed observers could
1130 // re-create.
1131 NotifyDOMWindowDestroyed(this);
1132 if (auto* reporter = nsWindowMemoryReporter::Get()) {
1133 reporter->ObserveDOMWindowDetached(this);
1136 // Kill all of the workers for this window.
1137 CancelWorkersForWindow(*this);
1139 for (RefPtr<mozilla::dom::SharedWorker> pinnedWorker :
1140 mSharedWorkers.ForwardRange()) {
1141 pinnedWorker->Close();
1144 if (mTimeoutManager) {
1145 mTimeoutManager->ClearAllTimeouts();
1148 DisableIdleCallbackRequests();
1150 mChromeEventHandler = nullptr;
1152 if (mListenerManager) {
1153 mListenerManager->RemoveAllListeners();
1154 mListenerManager->Disconnect();
1155 mListenerManager = nullptr;
1158 mHistory = nullptr;
1160 if (mNavigator) {
1161 mNavigator->OnNavigation();
1162 mNavigator->Invalidate();
1163 mNavigator = nullptr;
1166 mScreen = nullptr;
1168 if (mDoc) {
1169 // Remember the document's principal, URI, and CSP.
1170 mDocumentPrincipal = mDoc->NodePrincipal();
1171 mDocumentCookiePrincipal = mDoc->EffectiveCookiePrincipal();
1172 mDocumentStoragePrincipal = mDoc->EffectiveStoragePrincipal();
1173 mDocumentPartitionedPrincipal = mDoc->PartitionedPrincipal();
1174 mDocumentURI = mDoc->GetDocumentURI();
1175 mDocBaseURI = mDoc->GetDocBaseURI();
1176 mDocumentCsp = mDoc->GetCsp();
1178 while (mDoc->EventHandlingSuppressed()) {
1179 mDoc->UnsuppressEventHandlingAndFireEvents(false);
1183 // Remove our reference to the document and the document principal.
1184 mFocusedElement = nullptr;
1186 nsIGlobalObject::UnlinkObjectsInGlobal();
1188 NotifyWindowIDDestroyed("inner-window-destroyed");
1190 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
1191 mAudioContexts[i]->OnWindowDestroy();
1193 mAudioContexts.Clear();
1195 for (MediaKeys* mediaKeys : mMediaKeysInstances) {
1196 mediaKeys->OnInnerWindowDestroy();
1198 mMediaKeysInstances.Clear();
1200 DisableGamepadUpdates();
1201 mHasGamepad = false;
1202 mGamepads.Clear();
1203 DisableVRUpdates();
1204 mHasXRSession = false;
1205 mHasVRDisplayActivateEvents = false;
1206 mXRRuntimeDetectionInFlight = false;
1207 mXRPermissionRequestInFlight = false;
1208 mXRPermissionGranted = false;
1209 mVRDisplays.Clear();
1211 // This breaks a cycle between the window and the ClientSource object.
1212 mClientSource.reset();
1214 if (mWindowGlobalChild) {
1215 // Remove any remaining listeners.
1216 int64_t nListeners = mWindowGlobalChild->BeforeUnloadListeners();
1217 for (int64_t i = 0; i < nListeners; ++i) {
1218 mWindowGlobalChild->BeforeUnloadRemoved();
1220 MOZ_ASSERT(mWindowGlobalChild->BeforeUnloadListeners() == 0);
1223 // If we have any promiseDocumentFlushed callbacks, fire them now so
1224 // that the Promises can resolve.
1225 CallDocumentFlushedResolvers(/* aUntilExhaustion = */ true);
1227 DisconnectGlobalTeardownObservers();
1229 #ifdef MOZ_WIDGET_ANDROID
1230 DisableOrientationChangeListener();
1231 #endif
1233 if (mObserver) {
1234 if (nsCOMPtr<nsIObserverService> os = services::GetObserverService()) {
1235 os->RemoveObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC);
1236 os->RemoveObserver(mObserver, MEMORY_PRESSURE_OBSERVER_TOPIC);
1237 os->RemoveObserver(mObserver, PERMISSION_CHANGED_TOPIC);
1238 os->RemoveObserver(mObserver, "screen-information-changed");
1241 RefPtr<StorageNotifierService> sns = StorageNotifierService::GetOrCreate();
1242 if (sns) {
1243 sns->Unregister(mObserver);
1246 Preferences::RemoveObserver(mObserver, "intl.accept_languages");
1248 // Drop its reference to this dying window, in case for some bogus reason
1249 // the object stays around.
1250 mObserver->Forget();
1253 mMenubar = nullptr;
1254 mToolbar = nullptr;
1255 mLocationbar = nullptr;
1256 mPersonalbar = nullptr;
1257 mStatusbar = nullptr;
1258 mScrollbars = nullptr;
1260 mConsole = nullptr;
1262 mPaintWorklet = nullptr;
1264 mExternal = nullptr;
1265 mInstallTrigger = nullptr;
1267 if (mLocalStorage) {
1268 mLocalStorage->Disconnect();
1269 mLocalStorage = nullptr;
1271 mSessionStorage = nullptr;
1272 if (mPerformance) {
1273 // Since window is dying, nothing is going to be painted
1274 // with meaningful sizes, so these temp data for LCP is
1275 // no longer needed.
1276 static_cast<PerformanceMainThread*>(mPerformance.get())
1277 ->ClearGeneratedTempDataForLCP();
1279 mPerformance = nullptr;
1281 mContentMediaController = nullptr;
1283 if (mWebTaskScheduler) {
1284 mWebTaskScheduler->Disconnect();
1285 mWebTaskScheduler = nullptr;
1288 mTrustedTypePolicyFactory = nullptr;
1290 mSharedWorkers.Clear();
1292 #ifdef MOZ_WEBSPEECH
1293 mSpeechSynthesis = nullptr;
1294 #endif
1296 mGlean = nullptr;
1297 mGleanPings = nullptr;
1299 mParentTarget = nullptr;
1301 if (mCleanMessageManager) {
1302 MOZ_ASSERT(mIsChrome, "only chrome should have msg manager cleaned");
1303 if (mChromeFields.mMessageManager) {
1304 mChromeFields.mMessageManager->Disconnect();
1308 if (mWindowGlobalChild && !mWindowGlobalChild->IsClosed()) {
1309 mWindowGlobalChild->Destroy();
1312 mIntlUtils = nullptr;
1314 HintIsLoading(false);
1317 //*****************************************************************************
1318 // nsGlobalWindowInner::nsISupports
1319 //*****************************************************************************
1321 // QueryInterface implementation for nsGlobalWindowInner
1322 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGlobalWindowInner)
1323 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
1324 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, EventTarget)
1325 NS_INTERFACE_MAP_ENTRY(nsIDOMWindow)
1326 NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
1327 NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObject)
1328 NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
1329 NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget)
1330 NS_INTERFACE_MAP_ENTRY(nsPIDOMWindowInner)
1331 NS_INTERFACE_MAP_ENTRY(mozIDOMWindow)
1332 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
1333 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
1334 NS_INTERFACE_MAP_END
1336 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGlobalWindowInner)
1337 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGlobalWindowInner)
1339 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGlobalWindowInner)
1340 if (tmp->IsBlackForCC(false)) {
1341 if (nsCCUncollectableMarker::InGeneration(tmp->mCanSkipCCGeneration)) {
1342 return true;
1344 tmp->mCanSkipCCGeneration = nsCCUncollectableMarker::sGeneration;
1345 if (EventListenerManager* elm = tmp->GetExistingListenerManager()) {
1346 elm->MarkForCC();
1348 if (tmp->mTimeoutManager) {
1349 tmp->mTimeoutManager->UnmarkGrayTimers();
1351 return true;
1353 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
1355 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsGlobalWindowInner)
1356 return tmp->IsBlackForCC(true);
1357 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
1359 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsGlobalWindowInner)
1360 return tmp->IsBlackForCC(false);
1361 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
1363 NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalWindowInner)
1365 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindowInner)
1366 if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
1367 char name[512];
1368 nsAutoCString uri;
1369 if (tmp->mDoc && tmp->mDoc->GetDocumentURI()) {
1370 uri = tmp->mDoc->GetDocumentURI()->GetSpecOrDefault();
1372 SprintfLiteral(name, "nsGlobalWindowInner # %" PRIu64 " inner %s",
1373 tmp->mWindowID, uri.get());
1374 cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
1375 } else {
1376 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsGlobalWindowInner, tmp->mRefCnt.get())
1379 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNavigator)
1381 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance)
1383 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWebTaskScheduler)
1385 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTrustedTypePolicyFactory)
1387 #ifdef MOZ_WEBSPEECH
1388 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSpeechSynthesis)
1389 #endif
1391 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlean)
1392 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGleanPings)
1394 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOuterWindow)
1396 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTopInnerWindow)
1398 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager)
1400 if (tmp->mTimeoutManager) {
1401 tmp->mTimeoutManager->ForEachUnorderedTimeout([&cb](Timeout* timeout) {
1402 cb.NoteNativeChild(timeout, NS_CYCLE_COLLECTION_PARTICIPANT(Timeout));
1406 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocation)
1407 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHistory)
1408 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCustomElements)
1409 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSharedWorkers)
1410 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStorage)
1411 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSessionStorage)
1412 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndexedDB)
1413 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPrincipal)
1414 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentCookiePrincipal)
1415 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentStoragePrincipal)
1416 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPartitionedPrincipal)
1417 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentCsp)
1418 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowserChild)
1419 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDoc)
1421 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleRequestExecutor)
1422 for (IdleRequest* request : tmp->mIdleRequestCallbacks) {
1423 cb.NoteNativeChild(request, NS_CYCLE_COLLECTION_PARTICIPANT(IdleRequest));
1426 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mClientSource)
1428 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGamepads)
1430 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCacheStorage)
1431 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVRDisplays)
1433 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDebuggerNotificationManager)
1435 // Traverse stuff from nsPIDOMWindow
1436 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeEventHandler)
1437 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentTarget)
1438 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFocusedElement)
1439 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowsingContext)
1440 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindowGlobalChild)
1442 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMenubar)
1443 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mToolbar)
1444 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocationbar)
1445 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPersonalbar)
1446 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStatusbar)
1447 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScrollbars)
1448 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCrypto)
1449 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsole)
1450 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPaintWorklet)
1451 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExternal)
1452 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInstallTrigger)
1453 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIntlUtils)
1454 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVisualViewport)
1455 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCurrentPasteDataTransfer)
1457 tmp->TraverseObjectsInGlobal(cb);
1459 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeFields.mMessageManager)
1460 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeFields.mGroupMessageManagers)
1462 for (size_t i = 0; i < tmp->mDocumentFlushedResolvers.Length(); i++) {
1463 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentFlushedResolvers[i]->mPromise);
1464 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentFlushedResolvers[i]->mCallback);
1467 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1469 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindowInner)
1470 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
1471 if (sInnerWindowsById) {
1472 sInnerWindowsById->Remove(tmp->mWindowID);
1475 JSObject* wrapper = tmp->GetWrapperPreserveColor();
1476 if (wrapper) {
1477 // Mark our realm as dead, so the JS engine won't hand out our
1478 // global after this point.
1479 JS::SetRealmNonLive(js::GetNonCCWObjectRealm(wrapper));
1482 NS_IMPL_CYCLE_COLLECTION_UNLINK(mNavigator)
1484 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance)
1486 if (tmp->mWebTaskScheduler) {
1487 tmp->mWebTaskScheduler->Disconnect();
1488 NS_IMPL_CYCLE_COLLECTION_UNLINK(mWebTaskScheduler)
1491 NS_IMPL_CYCLE_COLLECTION_UNLINK(mTrustedTypePolicyFactory)
1493 #ifdef MOZ_WEBSPEECH
1494 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSpeechSynthesis)
1495 #endif
1497 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlean)
1498 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGleanPings)
1500 if (tmp->mOuterWindow) {
1501 nsGlobalWindowOuter::Cast(tmp->mOuterWindow)->MaybeClearInnerWindow(tmp);
1502 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOuterWindow)
1505 if (tmp->mListenerManager) {
1506 tmp->mListenerManager->Disconnect();
1507 NS_IMPL_CYCLE_COLLECTION_UNLINK(mListenerManager)
1510 // Here the Timeouts list would've been unlinked, but we rely on
1511 // that Timeout objects have been traced and will remove themselves
1512 // while unlinking.
1514 tmp->UpdateTopInnerWindow();
1515 NS_IMPL_CYCLE_COLLECTION_UNLINK(mTopInnerWindow)
1517 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocation)
1518 NS_IMPL_CYCLE_COLLECTION_UNLINK(mHistory)
1519 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCustomElements)
1520 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSharedWorkers)
1521 if (tmp->mLocalStorage) {
1522 tmp->mLocalStorage->Disconnect();
1523 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalStorage)
1525 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSessionStorage)
1526 NS_IMPL_CYCLE_COLLECTION_UNLINK(mIndexedDB)
1527 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPrincipal)
1528 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentCookiePrincipal)
1529 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentStoragePrincipal)
1530 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPartitionedPrincipal)
1531 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentCsp)
1532 NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowserChild)
1533 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDoc)
1535 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGamepads)
1537 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCacheStorage)
1538 NS_IMPL_CYCLE_COLLECTION_UNLINK(mVRDisplays)
1540 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDebuggerNotificationManager)
1542 // Unlink stuff from nsPIDOMWindow
1543 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeEventHandler)
1544 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParentTarget)
1545 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFocusedElement)
1546 NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowsingContext)
1548 MOZ_DIAGNOSTIC_ASSERT(
1549 !tmp->mWindowGlobalChild || tmp->mWindowGlobalChild->IsClosed(),
1550 "How are we unlinking a window before its actor has been destroyed?");
1551 NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindowGlobalChild)
1553 NS_IMPL_CYCLE_COLLECTION_UNLINK(mMenubar)
1554 NS_IMPL_CYCLE_COLLECTION_UNLINK(mToolbar)
1555 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocationbar)
1556 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPersonalbar)
1557 NS_IMPL_CYCLE_COLLECTION_UNLINK(mStatusbar)
1558 NS_IMPL_CYCLE_COLLECTION_UNLINK(mScrollbars)
1559 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCrypto)
1560 NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole)
1561 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPaintWorklet)
1562 NS_IMPL_CYCLE_COLLECTION_UNLINK(mExternal)
1563 NS_IMPL_CYCLE_COLLECTION_UNLINK(mInstallTrigger)
1564 NS_IMPL_CYCLE_COLLECTION_UNLINK(mIntlUtils)
1565 NS_IMPL_CYCLE_COLLECTION_UNLINK(mVisualViewport)
1566 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCurrentPasteDataTransfer)
1568 tmp->UnlinkObjectsInGlobal();
1570 NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleRequestExecutor)
1572 // Here the IdleRequest list would've been unlinked, but we rely on
1573 // that IdleRequest objects have been traced and will remove
1574 // themselves while unlinking.
1576 NS_IMPL_CYCLE_COLLECTION_UNLINK(mClientSource)
1578 if (tmp->IsChromeWindow()) {
1579 if (tmp->mChromeFields.mMessageManager) {
1580 static_cast<nsFrameMessageManager*>(
1581 tmp->mChromeFields.mMessageManager.get())
1582 ->Disconnect();
1583 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeFields.mMessageManager)
1585 tmp->DisconnectAndClearGroupMessageManagers();
1586 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeFields.mGroupMessageManagers)
1589 for (size_t i = 0; i < tmp->mDocumentFlushedResolvers.Length(); i++) {
1590 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentFlushedResolvers[i]->mPromise);
1591 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentFlushedResolvers[i]->mCallback);
1593 tmp->mDocumentFlushedResolvers.Clear();
1595 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
1596 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1598 #ifdef DEBUG
1599 void nsGlobalWindowInner::RiskyUnlink() {
1600 NS_CYCLE_COLLECTION_INNERNAME.Unlink(this);
1602 #endif
1604 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGlobalWindowInner)
1605 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
1606 NS_IMPL_CYCLE_COLLECTION_TRACE_END
1608 bool nsGlobalWindowInner::IsBlackForCC(bool aTracingNeeded) {
1609 if (!nsCCUncollectableMarker::sGeneration) {
1610 return false;
1613 return (nsCCUncollectableMarker::InGeneration(GetMarkedCCGeneration()) ||
1614 HasKnownLiveWrapper()) &&
1615 (!aTracingNeeded || HasNothingToTrace(ToSupports(this)));
1618 //*****************************************************************************
1619 // nsGlobalWindowInner::nsIScriptGlobalObject
1620 //*****************************************************************************
1622 bool nsGlobalWindowInner::ShouldResistFingerprinting(RFPTarget aTarget) const {
1623 if (mDoc) {
1624 return mDoc->ShouldResistFingerprinting(aTarget);
1626 return nsContentUtils::ShouldResistFingerprinting(
1627 "If we do not have a document then we do not have any context"
1628 "to make an informed RFP choice, so we fall back to the global pref",
1629 aTarget);
1632 OriginTrials nsGlobalWindowInner::Trials() const {
1633 return OriginTrials::FromWindow(this);
1636 FontFaceSet* nsGlobalWindowInner::GetFonts() {
1637 if (mDoc) {
1638 return mDoc->Fonts();
1640 return nullptr;
1643 mozilla::Result<mozilla::ipc::PrincipalInfo, nsresult>
1644 nsGlobalWindowInner::GetStorageKey() {
1645 MOZ_ASSERT(NS_IsMainThread());
1647 nsIPrincipal* principal = GetEffectiveStoragePrincipal();
1648 if (!principal) {
1649 return mozilla::Err(NS_ERROR_FAILURE);
1652 mozilla::ipc::PrincipalInfo principalInfo;
1653 nsresult rv = PrincipalToPrincipalInfo(principal, &principalInfo);
1654 if (NS_FAILED(rv)) {
1655 return mozilla::Err(rv);
1658 // Block expanded and null principals, let content and system through.
1659 if (principalInfo.type() !=
1660 mozilla::ipc::PrincipalInfo::TContentPrincipalInfo &&
1661 principalInfo.type() !=
1662 mozilla::ipc::PrincipalInfo::TSystemPrincipalInfo) {
1663 return Err(NS_ERROR_DOM_SECURITY_ERR);
1666 return std::move(principalInfo);
1669 mozilla::dom::StorageManager* nsGlobalWindowInner::GetStorageManager() {
1670 return Navigator()->Storage();
1673 // https://html.spec.whatwg.org/multipage/web-messaging.html#eligible-for-messaging
1674 // * a Window object whose associated Document is fully active
1675 bool nsGlobalWindowInner::IsEligibleForMessaging() { return IsFullyActive(); }
1677 nsresult nsGlobalWindowInner::EnsureScriptEnvironment() {
1678 // NOTE: We can't use FORWARD_TO_OUTER here because we don't want to fail if
1679 // we're called on an inactive inner window.
1680 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
1681 if (!outer) {
1682 NS_WARNING("No outer window available!");
1683 return NS_ERROR_FAILURE;
1685 return outer->EnsureScriptEnvironment();
1688 nsIScriptContext* nsGlobalWindowInner::GetScriptContext() {
1689 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
1690 if (!outer) {
1691 return nullptr;
1693 return outer->GetScriptContext();
1696 void nsGlobalWindowInner::TraceGlobalJSObject(JSTracer* aTrc) {
1697 TraceWrapper(aTrc, "active window global");
1700 void nsGlobalWindowInner::UpdateAutoplayPermission() {
1701 if (!GetWindowContext()) {
1702 return;
1704 uint32_t perm =
1705 media::AutoplayPolicy::GetSiteAutoplayPermission(GetPrincipal());
1706 if (GetWindowContext()->GetAutoplayPermission() == perm) {
1707 return;
1710 // Setting autoplay permission on a discarded context has no effect.
1711 Unused << GetWindowContext()->SetAutoplayPermission(perm);
1714 void nsGlobalWindowInner::UpdateShortcutsPermission() {
1715 if (!GetWindowContext() ||
1716 !GetWindowContext()->GetBrowsingContext()->IsTop()) {
1717 // We only cache the shortcuts permission on top-level WindowContexts
1718 // since we always check the top-level principal for the permission.
1719 return;
1722 uint32_t perm = GetShortcutsPermission(GetPrincipal());
1724 if (GetWindowContext()->GetShortcutsPermission() == perm) {
1725 return;
1728 // If the WindowContext is discarded this has no effect.
1729 Unused << GetWindowContext()->SetShortcutsPermission(perm);
1732 /* static */
1733 uint32_t nsGlobalWindowInner::GetShortcutsPermission(nsIPrincipal* aPrincipal) {
1734 uint32_t perm = nsIPermissionManager::DENY_ACTION;
1735 nsCOMPtr<nsIPermissionManager> permMgr =
1736 mozilla::components::PermissionManager::Service();
1737 if (aPrincipal && permMgr) {
1738 permMgr->TestExactPermissionFromPrincipal(aPrincipal, "shortcuts"_ns,
1739 &perm);
1741 return perm;
1744 void nsGlobalWindowInner::UpdatePopupPermission() {
1745 if (!GetWindowContext()) {
1746 return;
1749 uint32_t perm = PopupBlocker::GetPopupPermission(GetPrincipal());
1750 if (GetWindowContext()->GetPopupPermission() == perm) {
1751 return;
1754 // If the WindowContext is discarded this has no effect.
1755 Unused << GetWindowContext()->SetPopupPermission(perm);
1758 void nsGlobalWindowInner::UpdatePermissions() {
1759 if (!GetWindowContext()) {
1760 return;
1763 nsCOMPtr<nsIPrincipal> principal = GetPrincipal();
1764 RefPtr<WindowContext> windowContext = GetWindowContext();
1766 WindowContext::Transaction txn;
1767 txn.SetAutoplayPermission(
1768 media::AutoplayPolicy::GetSiteAutoplayPermission(principal));
1769 txn.SetPopupPermission(PopupBlocker::GetPopupPermission(principal));
1771 if (windowContext->IsTop()) {
1772 txn.SetShortcutsPermission(GetShortcutsPermission(principal));
1775 // Setting permissions on a discarded WindowContext has no effect
1776 Unused << txn.Commit(windowContext);
1779 void nsGlobalWindowInner::InitDocumentDependentState(JSContext* aCx) {
1780 MOZ_ASSERT(mDoc);
1782 if (MOZ_LOG_TEST(gDOMLeakPRLogInner, LogLevel::Debug)) {
1783 nsIURI* uri = mDoc->GetDocumentURI();
1784 MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
1785 ("DOMWINDOW %p SetNewDocument %s", this,
1786 uri ? uri->GetSpecOrDefault().get() : ""));
1789 mFocusedElement = nullptr;
1790 mLocalStorage = nullptr;
1791 mSessionStorage = nullptr;
1792 mPerformance = nullptr;
1793 if (mWebTaskScheduler) {
1794 mWebTaskScheduler->Disconnect();
1795 mWebTaskScheduler = nullptr;
1798 // This must be called after nullifying the internal objects because here we
1799 // could recreate them, calling the getter methods, and store them into the JS
1800 // slots. If we nullify them after, the slot values and the objects will be
1801 // out of sync.
1802 ClearDocumentDependentSlots(aCx);
1804 if (!mWindowGlobalChild) {
1805 mWindowGlobalChild = WindowGlobalChild::Create(this);
1807 MOZ_ASSERT(!GetWindowContext()->HasBeenUserGestureActivated(),
1808 "WindowContext should always not have user gesture activation at "
1809 "this point.");
1811 UpdatePermissions();
1813 RefPtr<PermissionDelegateHandler> permDelegateHandler =
1814 mDoc->GetPermissionDelegateHandler();
1816 if (permDelegateHandler) {
1817 permDelegateHandler->PopulateAllDelegatedPermissions();
1820 #if defined(MOZ_WIDGET_ANDROID)
1821 // When we insert the new document to the window in the top-level browsing
1822 // context, we should reset the status of the request which is used for the
1823 // previous document.
1824 if (mWindowGlobalChild && GetBrowsingContext() &&
1825 !GetBrowsingContext()->GetParent()) {
1826 // Return value of setting synced field should be checked. See bug 1656492.
1827 Unused << GetBrowsingContext()->ResetGVAutoplayRequestStatus();
1829 #endif
1831 #ifdef DEBUG
1832 mLastOpenedURI = mDoc->GetDocumentURI();
1833 #endif
1835 Telemetry::Accumulate(Telemetry::INNERWINDOWS_WITH_MUTATION_LISTENERS,
1836 mMutationBits ? 1 : 0);
1838 // Clear our mutation bitfield.
1839 mMutationBits = 0;
1842 nsresult nsGlobalWindowInner::EnsureClientSource() {
1843 MOZ_DIAGNOSTIC_ASSERT(mDoc);
1845 bool newClientSource = false;
1847 // Get the load info for the document if we performed a load. Be careful not
1848 // to look at local URLs, though. Local URLs are those that have a scheme of:
1849 // * about:
1850 // * data:
1851 // * blob:
1852 // We also do an additional check here so that we only treat about:blank
1853 // and about:srcdoc as local URLs. Other internal firefox about: URLs should
1854 // not be treated this way.
1855 nsCOMPtr<nsILoadInfo> loadInfo;
1856 nsCOMPtr<nsIChannel> channel = mDoc->GetChannel();
1857 if (channel) {
1858 nsCOMPtr<nsIURI> uri;
1859 Unused << channel->GetURI(getter_AddRefs(uri));
1861 bool ignoreLoadInfo = false;
1863 // Note, this is mostly copied from NS_IsAboutBlank(). Its duplicated
1864 // here so we can efficiently check about:srcdoc as well.
1865 if (uri->SchemeIs("about")) {
1866 nsCString spec = uri->GetSpecOrDefault();
1867 ignoreLoadInfo = spec.EqualsLiteral("about:blank") ||
1868 spec.EqualsLiteral("about:srcdoc");
1869 } else {
1870 // Its not an about: URL, so now check for our other URL types.
1871 ignoreLoadInfo = uri->SchemeIs("data") || uri->SchemeIs("blob");
1874 if (!ignoreLoadInfo) {
1875 loadInfo = channel->LoadInfo();
1879 // Take the initial client source from the docshell immediately. Even if we
1880 // don't end up using it here we should consume it.
1881 UniquePtr<ClientSource> initialClientSource;
1882 nsIDocShell* docshell = GetDocShell();
1883 if (docshell) {
1884 initialClientSource = docshell->TakeInitialClientSource();
1887 // Try to get the reserved client from the LoadInfo. A Client is
1888 // reserved at the start of the channel load if there is not an
1889 // initial about:blank document that will be reused. It is also
1890 // created if the channel load encounters a cross-origin redirect.
1891 if (loadInfo) {
1892 UniquePtr<ClientSource> reservedClient =
1893 loadInfo->TakeReservedClientSource();
1894 if (reservedClient) {
1895 mClientSource.reset();
1896 mClientSource = std::move(reservedClient);
1897 newClientSource = true;
1901 // We don't have a LoadInfo reserved client, but maybe we should
1902 // be inheriting an initial one from the docshell. This means
1903 // that the docshell started the channel load before creating the
1904 // initial about:blank document. This is an optimization, though,
1905 // and it created an initial Client as a placeholder for the document.
1906 // In this case we want to inherit this placeholder Client here.
1907 if (!mClientSource) {
1908 mClientSource = std::move(initialClientSource);
1909 if (mClientSource) {
1910 newClientSource = true;
1914 nsCOMPtr<nsIPrincipal> foreignPartitionedPrincipal;
1916 nsresult rv = StoragePrincipalHelper::GetPrincipal(
1917 this,
1918 StaticPrefs::privacy_partition_serviceWorkers()
1919 ? StoragePrincipalHelper::eForeignPartitionedPrincipal
1920 : StoragePrincipalHelper::eRegularPrincipal,
1921 getter_AddRefs(foreignPartitionedPrincipal));
1922 NS_ENSURE_SUCCESS(rv, rv);
1924 // Verify the final ClientSource principal matches the final document
1925 // principal. The ClientChannelHelper handles things like network
1926 // redirects, but there are other ways the document principal can change.
1927 // For example, if something sets the nsIChannel.owner property, then
1928 // the final channel principal can be anything. Unfortunately there is
1929 // no good way to detect this until after the channel completes loading.
1931 // For now we handle this just by reseting the ClientSource. This will
1932 // result in a new ClientSource with the correct principal being created.
1933 // To APIs like ServiceWorker and Clients API it will look like there was
1934 // an initial content page created that was then immediately replaced.
1935 // This is pretty close to what we are actually doing.
1936 if (mClientSource) {
1937 auto principalOrErr = mClientSource->Info().GetPrincipal();
1938 nsCOMPtr<nsIPrincipal> clientPrincipal =
1939 principalOrErr.isOk() ? principalOrErr.unwrap() : nullptr;
1940 if (!clientPrincipal ||
1941 !clientPrincipal->Equals(foreignPartitionedPrincipal)) {
1942 mClientSource.reset();
1946 // If we don't have a reserved client or an initial client, then create
1947 // one now. This can happen in certain cases where we avoid preallocating
1948 // the client in the docshell. This mainly occurs in situations where
1949 // the principal is not clearly inherited from the parent; e.g. sandboxed
1950 // iframes, window.open(), etc.
1952 // We also do this late ClientSource creation if the final document ended
1953 // up with a different principal.
1955 // TODO: We may not be marking initial about:blank documents created
1956 // this way as controlled by a service worker properly. The
1957 // controller should be coming from the same place as the inheritted
1958 // principal. We do this in docshell, but as mentioned we aren't
1959 // smart enough to handle all cases yet. For example, a
1960 // window.open() with new URL should inherit the controller from
1961 // the opener, but we probably don't handle that yet.
1962 if (!mClientSource) {
1963 mClientSource = ClientManager::CreateSource(
1964 ClientType::Window, SerialEventTarget(), foreignPartitionedPrincipal);
1965 MOZ_DIAGNOSTIC_ASSERT(mClientSource);
1966 newClientSource = true;
1968 // Note, we don't apply the loadinfo controller below if we create
1969 // the ClientSource here.
1972 // The load may have started controlling the Client as well. If
1973 // so, mark it as controlled immediately here. The actor may
1974 // or may not have been notified by the parent side about being
1975 // controlled yet.
1977 // Note: We should be careful not to control a client that was created late.
1978 // These clients were not seen by the ServiceWorkerManager when it
1979 // marked the LoadInfo controlled and it won't know about them. Its
1980 // also possible we are creating the client late due to the final
1981 // principal changing and these clients should definitely not be
1982 // controlled by a service worker with a different principal.
1983 else if (loadInfo) {
1984 const Maybe<ServiceWorkerDescriptor> controller = loadInfo->GetController();
1985 if (controller.isSome()) {
1986 mClientSource->SetController(controller.ref());
1989 // We also have to handle the case where te initial about:blank is
1990 // controlled due to inheritting the service worker from its parent,
1991 // but the actual nsIChannel load is not covered by any service worker.
1992 // In this case we want the final page to be uncontrolled. There is
1993 // an open spec issue about how exactly this should be handled, but for
1994 // now we just force creation of a new ClientSource to clear the
1995 // controller.
1997 // https://github.com/w3c/ServiceWorker/issues/1232
1999 else if (mClientSource->GetController().isSome()) {
2000 mClientSource.reset();
2001 mClientSource = ClientManager::CreateSource(
2002 ClientType::Window, SerialEventTarget(), foreignPartitionedPrincipal);
2003 MOZ_DIAGNOSTIC_ASSERT(mClientSource);
2004 newClientSource = true;
2008 if (mClientSource) {
2009 // Generally the CSP is stored within the Client and cached on the document.
2010 // At the time of CSP parsing however, the Client has not been created yet,
2011 // hence we store the CSP on the document and propagate/sync the CSP with
2012 // Client here when we create the Client.
2013 mClientSource->SetCsp(mDoc->GetCsp());
2015 DocGroup* docGroup = GetDocGroup();
2016 MOZ_DIAGNOSTIC_ASSERT(docGroup);
2017 mClientSource->SetAgentClusterId(docGroup->AgentClusterId());
2019 if (mWindowGlobalChild) {
2020 mWindowGlobalChild->SendSetClientInfo(mClientSource->Info().ToIPC());
2024 // Its possible that we got a client just after being frozen in
2025 // the bfcache. In that case freeze the client immediately.
2026 if (newClientSource && IsFrozen()) {
2027 mClientSource->Freeze();
2030 return NS_OK;
2033 nsresult nsGlobalWindowInner::ExecutionReady() {
2034 nsresult rv = EnsureClientSource();
2035 NS_ENSURE_SUCCESS(rv, rv);
2037 rv = mClientSource->WindowExecutionReady(this);
2038 NS_ENSURE_SUCCESS(rv, rv);
2040 return NS_OK;
2043 void nsGlobalWindowInner::UpdateParentTarget() {
2044 // NOTE: This method is identical to
2045 // nsGlobalWindowOuter::UpdateParentTarget(). IF YOU UPDATE THIS METHOD,
2046 // UPDATE THE OTHER ONE TOO!
2048 // Try to get our frame element's tab child global (its in-process message
2049 // manager). If that fails, fall back to the chrome event handler's tab
2050 // child global, and if it doesn't have one, just use the chrome event
2051 // handler itself.
2053 nsPIDOMWindowOuter* outer = GetOuterWindow();
2054 if (!outer) {
2055 return;
2057 nsCOMPtr<Element> frameElement = outer->GetFrameElementInternal();
2058 nsCOMPtr<EventTarget> eventTarget =
2059 nsContentUtils::TryGetBrowserChildGlobal(frameElement);
2061 if (!eventTarget) {
2062 nsGlobalWindowOuter* topWin = GetInProcessScriptableTopInternal();
2063 if (topWin) {
2064 frameElement = topWin->GetFrameElementInternal();
2065 eventTarget = nsContentUtils::TryGetBrowserChildGlobal(frameElement);
2069 if (!eventTarget) {
2070 eventTarget = nsContentUtils::TryGetBrowserChildGlobal(mChromeEventHandler);
2073 if (!eventTarget) {
2074 eventTarget = mChromeEventHandler;
2077 mParentTarget = eventTarget;
2080 EventTarget* nsGlobalWindowInner::GetTargetForDOMEvent() {
2081 return GetOuterWindowInternal();
2084 void nsGlobalWindowInner::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
2085 EventMessage msg = aVisitor.mEvent->mMessage;
2087 aVisitor.mCanHandle = true;
2088 aVisitor.mForceContentDispatch = true; // FIXME! Bug 329119
2089 if (msg == eResize && aVisitor.mEvent->IsTrusted()) {
2090 // Checking whether the event target is an inner window or not, so we can
2091 // keep the old behavior also in case a child window is handling resize.
2092 if (aVisitor.mEvent->mOriginalTarget &&
2093 aVisitor.mEvent->mOriginalTarget->IsInnerWindow()) {
2094 mIsHandlingResizeEvent = true;
2096 } else if (msg == eMouseDown && aVisitor.mEvent->IsTrusted()) {
2097 sMouseDown = true;
2098 } else if ((msg == eMouseUp || msg == eDragEnd) &&
2099 aVisitor.mEvent->IsTrusted()) {
2100 sMouseDown = false;
2101 if (sDragServiceDisabled) {
2102 nsCOMPtr<nsIDragService> ds =
2103 do_GetService("@mozilla.org/widget/dragservice;1");
2104 if (ds) {
2105 sDragServiceDisabled = false;
2106 ds->Unsuppress();
2111 aVisitor.SetParentTarget(GetParentTarget(), true);
2114 void nsGlobalWindowInner::FireFrameLoadEvent() {
2115 // If we're not in a content frame, or are at a BrowsingContext tree boundary,
2116 // such as the content-chrome boundary, don't fire the "load" event.
2117 if (GetBrowsingContext()->IsTopContent() ||
2118 GetBrowsingContext()->IsChrome()) {
2119 return;
2122 // If embedder is same-process, fire the event on our embedder element.
2124 // XXX: Bug 1440212 is looking into potentially changing this behaviour to act
2125 // more like the remote case when in-process.
2126 RefPtr<Element> element = GetBrowsingContext()->GetEmbedderElement();
2127 if (element) {
2128 nsEventStatus status = nsEventStatus_eIgnore;
2129 WidgetEvent event(/* aIsTrusted = */ true, eLoad);
2130 event.mFlags.mBubbles = false;
2131 event.mFlags.mCancelable = false;
2133 // Most of the time we could get a pres context to pass in here, but not
2134 // always (i.e. if this window is not shown there won't be a pres context
2135 // available). Since we're not firing a GUI event we don't need a pres
2136 // context anyway so we just pass null as the pres context all the time.
2137 EventDispatcher::Dispatch(element, nullptr, &event, nullptr, &status);
2138 return;
2141 // We don't have an in-process embedder. Try to get our `BrowserChild` actor
2142 // to send a message to that embedder. We want to double-check that our outer
2143 // window is actually the one at the root of this browserChild though, just in
2144 // case.
2145 RefPtr<BrowserChild> browserChild =
2146 BrowserChild::GetFrom(static_cast<nsPIDOMWindowInner*>(this));
2147 if (browserChild &&
2148 !GetBrowsingContext()->GetParentWindowContext()->IsInProcess()) {
2149 // Double-check that our outer window is actually at the root of this
2150 // `BrowserChild`, in case we're in an odd maybe-unhosted situation like a
2151 // print preview dialog.
2152 nsCOMPtr<nsPIDOMWindowOuter> rootOuter =
2153 do_GetInterface(browserChild->WebNavigation());
2154 if (!rootOuter || rootOuter != GetOuterWindow()) {
2155 return;
2158 mozilla::Unused << browserChild->SendMaybeFireEmbedderLoadEvents(
2159 EmbedderElementEventType::LoadEvent);
2163 nsresult nsGlobalWindowInner::PostHandleEvent(EventChainPostVisitor& aVisitor) {
2164 // Return early if there is nothing to do.
2165 switch (aVisitor.mEvent->mMessage) {
2166 case eResize:
2167 case eUnload:
2168 case eLoad:
2169 break;
2170 default:
2171 return NS_OK;
2174 /* mChromeEventHandler and mContext go dangling in the middle of this
2175 function under some circumstances (events that destroy the window)
2176 without this addref. */
2177 RefPtr<EventTarget> kungFuDeathGrip1(mChromeEventHandler);
2178 mozilla::Unused
2179 << kungFuDeathGrip1; // These aren't referred to through the function
2180 nsCOMPtr<nsIScriptContext> kungFuDeathGrip2(GetContextInternal());
2181 mozilla::Unused
2182 << kungFuDeathGrip2; // These aren't referred to through the function
2184 if (aVisitor.mEvent->mMessage == eResize) {
2185 mIsHandlingResizeEvent = false;
2186 } else if (aVisitor.mEvent->mMessage == eUnload &&
2187 aVisitor.mEvent->IsTrusted()) {
2188 // If any VR display presentation is active at unload, the next page
2189 // will receive a vrdisplayactive event to indicate that it should
2190 // immediately begin vr presentation. This should occur when navigating
2191 // forwards, navigating backwards, and on page reload.
2192 for (const auto& display : mVRDisplays) {
2193 if (display->IsPresenting()) {
2194 display->StartVRNavigation();
2195 // Save this VR display ID to trigger vrdisplayactivate event
2196 // after the next load event.
2197 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
2198 if (outer) {
2199 outer->SetAutoActivateVRDisplayID(display->DisplayId());
2202 // XXX The WebVR 1.1 spec does not define which of multiple VR
2203 // presenting VR displays will be chosen during navigation.
2204 // As the underlying platform VR API's currently only allow a single
2205 // VR display, it is safe to choose the first VR display for now.
2206 break;
2209 mIsDocumentLoaded = false;
2210 // Tell the parent process that the document is not loaded.
2211 if (mWindowGlobalChild) {
2212 mWindowGlobalChild->SendUpdateDocumentHasLoaded(mIsDocumentLoaded);
2214 } else if (aVisitor.mEvent->mMessage == eLoad &&
2215 aVisitor.mEvent->IsTrusted()) {
2216 // This is page load event since load events don't propagate to |window|.
2217 // @see Document::GetEventTargetParent.
2218 mIsDocumentLoaded = true;
2219 // Tell the parent process that the document is loaded.
2220 if (mWindowGlobalChild) {
2221 mWindowGlobalChild->SendUpdateDocumentHasLoaded(mIsDocumentLoaded);
2224 mTimeoutManager->OnDocumentLoaded();
2226 MOZ_ASSERT(aVisitor.mEvent->IsTrusted());
2227 FireFrameLoadEvent();
2229 if (mVREventObserver) {
2230 mVREventObserver->NotifyAfterLoad();
2233 uint32_t autoActivateVRDisplayID = 0;
2234 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
2235 if (outer) {
2236 autoActivateVRDisplayID = outer->GetAutoActivateVRDisplayID();
2238 if (autoActivateVRDisplayID) {
2239 DispatchVRDisplayActivate(autoActivateVRDisplayID,
2240 VRDisplayEventReason::Navigation);
2244 return NS_OK;
2247 nsresult nsGlobalWindowInner::DefineArgumentsProperty(nsIArray* aArguments) {
2248 nsIScriptContext* ctx = GetOuterWindowInternal()->mContext;
2249 NS_ENSURE_TRUE(aArguments && ctx, NS_ERROR_NOT_INITIALIZED);
2251 JS::Rooted<JSObject*> obj(RootingCx(), GetWrapperPreserveColor());
2252 return ctx->SetProperty(obj, "arguments", aArguments);
2255 //*****************************************************************************
2256 // nsGlobalWindowInner::nsIScriptObjectPrincipal
2257 //*****************************************************************************
2259 nsIPrincipal* nsGlobalWindowInner::GetPrincipal() {
2260 if (mDoc) {
2261 // If we have a document, get the principal from the document
2262 return mDoc->NodePrincipal();
2265 if (mDocumentPrincipal) {
2266 return mDocumentPrincipal;
2269 // If we don't have a principal and we don't have a document we
2270 // ask the parent window for the principal. This can happen when
2271 // loading a frameset that has a <frame src="javascript:xxx">, in
2272 // that case the global window is used in JS before we've loaded
2273 // a document into the window.
2275 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
2276 do_QueryInterface(GetInProcessParentInternal());
2278 if (objPrincipal) {
2279 return objPrincipal->GetPrincipal();
2282 return nullptr;
2285 nsIPrincipal* nsGlobalWindowInner::GetEffectiveCookiePrincipal() {
2286 if (mDoc) {
2287 // If we have a document, get the principal from the document
2288 return mDoc->EffectiveCookiePrincipal();
2291 if (mDocumentCookiePrincipal) {
2292 return mDocumentCookiePrincipal;
2295 // If we don't have a cookie principal and we don't have a document we ask
2296 // the parent window for the cookie principal.
2298 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
2299 do_QueryInterface(GetInProcessParentInternal());
2301 if (objPrincipal) {
2302 return objPrincipal->GetEffectiveCookiePrincipal();
2305 return nullptr;
2308 nsIPrincipal* nsGlobalWindowInner::GetEffectiveStoragePrincipal() {
2309 if (mDoc) {
2310 // If we have a document, get the principal from the document
2311 return mDoc->EffectiveStoragePrincipal();
2314 if (mDocumentStoragePrincipal) {
2315 return mDocumentStoragePrincipal;
2318 // If we don't have a cookie principal and we don't have a document we ask
2319 // the parent window for the cookie principal.
2321 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
2322 do_QueryInterface(GetInProcessParentInternal());
2324 if (objPrincipal) {
2325 return objPrincipal->GetEffectiveStoragePrincipal();
2328 return nullptr;
2331 nsIPrincipal* nsGlobalWindowInner::PartitionedPrincipal() {
2332 if (mDoc) {
2333 // If we have a document, get the principal from the document
2334 return mDoc->PartitionedPrincipal();
2337 if (mDocumentPartitionedPrincipal) {
2338 return mDocumentPartitionedPrincipal;
2341 // If we don't have a partitioned principal and we don't have a document we
2342 // ask the parent window for the partitioned principal.
2344 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
2345 do_QueryInterface(GetInProcessParentInternal());
2347 if (objPrincipal) {
2348 return objPrincipal->PartitionedPrincipal();
2351 return nullptr;
2354 //*****************************************************************************
2355 // nsGlobalWindowInner::nsIDOMWindow
2356 //*****************************************************************************
2358 bool nsPIDOMWindowInner::AddAudioContext(AudioContext* aAudioContext) {
2359 mAudioContexts.AppendElement(aAudioContext);
2361 // Return true if the context should be muted and false if not.
2362 nsIDocShell* docShell = GetDocShell();
2363 return docShell && !docShell->GetAllowMedia() && !aAudioContext->IsOffline();
2366 void nsPIDOMWindowInner::RemoveAudioContext(AudioContext* aAudioContext) {
2367 mAudioContexts.RemoveElement(aAudioContext);
2370 void nsPIDOMWindowInner::MuteAudioContexts() {
2371 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
2372 if (!mAudioContexts[i]->IsOffline()) {
2373 mAudioContexts[i]->Mute();
2378 void nsPIDOMWindowInner::UnmuteAudioContexts() {
2379 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
2380 if (!mAudioContexts[i]->IsOffline()) {
2381 mAudioContexts[i]->Unmute();
2386 WindowProxyHolder nsGlobalWindowInner::Window() {
2387 return WindowProxyHolder(GetBrowsingContext());
2390 Navigator* nsPIDOMWindowInner::Navigator() {
2391 if (!mNavigator) {
2392 mNavigator = new mozilla::dom::Navigator(this);
2395 return mNavigator;
2398 MediaDevices* nsPIDOMWindowInner::GetExtantMediaDevices() const {
2399 return mNavigator ? mNavigator->GetExtantMediaDevices() : nullptr;
2402 VisualViewport* nsGlobalWindowInner::VisualViewport() {
2403 if (!mVisualViewport) {
2404 mVisualViewport = new mozilla::dom::VisualViewport(this);
2406 return mVisualViewport;
2409 nsScreen* nsGlobalWindowInner::Screen() {
2410 if (!mScreen) {
2411 mScreen = new nsScreen(this);
2413 return mScreen;
2416 nsHistory* nsGlobalWindowInner::GetHistory(ErrorResult& aError) {
2417 if (!mHistory) {
2418 mHistory = new nsHistory(this);
2420 return mHistory;
2423 CustomElementRegistry* nsGlobalWindowInner::CustomElements() {
2424 if (!mCustomElements) {
2425 mCustomElements = new CustomElementRegistry(this);
2428 return mCustomElements;
2431 CustomElementRegistry* nsGlobalWindowInner::GetExistingCustomElements() {
2432 return mCustomElements;
2435 Performance* nsPIDOMWindowInner::GetPerformance() {
2436 CreatePerformanceObjectIfNeeded();
2437 return mPerformance;
2440 void nsPIDOMWindowInner::QueuePerformanceNavigationTiming() {
2441 CreatePerformanceObjectIfNeeded();
2442 if (mPerformance) {
2443 mPerformance->QueueNavigationTimingEntry();
2447 void nsPIDOMWindowInner::CreatePerformanceObjectIfNeeded() {
2448 if (mPerformance || !mDoc) {
2449 return;
2451 RefPtr<nsDOMNavigationTiming> timing = mDoc->GetNavigationTiming();
2452 nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(mDoc->GetChannel()));
2453 bool timingEnabled = false;
2454 if (!timedChannel ||
2455 !NS_SUCCEEDED(timedChannel->GetTimingEnabled(&timingEnabled)) ||
2456 !timingEnabled) {
2457 timedChannel = nullptr;
2459 if (timing) {
2460 mPerformance = Performance::CreateForMainThread(this, mDoc->NodePrincipal(),
2461 timing, timedChannel);
2465 bool nsPIDOMWindowInner::IsSecureContext() const {
2466 return nsGlobalWindowInner::Cast(this)->IsSecureContext();
2469 void nsPIDOMWindowInner::Suspend(bool aIncludeSubWindows) {
2470 nsGlobalWindowInner::Cast(this)->Suspend(aIncludeSubWindows);
2473 void nsPIDOMWindowInner::Resume(bool aIncludeSubWindows) {
2474 nsGlobalWindowInner::Cast(this)->Resume(aIncludeSubWindows);
2477 void nsPIDOMWindowInner::SyncStateFromParentWindow() {
2478 nsGlobalWindowInner::Cast(this)->SyncStateFromParentWindow();
2481 Maybe<ClientInfo> nsPIDOMWindowInner::GetClientInfo() const {
2482 return nsGlobalWindowInner::Cast(this)->GetClientInfo();
2485 Maybe<ClientState> nsPIDOMWindowInner::GetClientState() const {
2486 return nsGlobalWindowInner::Cast(this)->GetClientState();
2489 Maybe<ServiceWorkerDescriptor> nsPIDOMWindowInner::GetController() const {
2490 return nsGlobalWindowInner::Cast(this)->GetController();
2493 void nsPIDOMWindowInner::SetCsp(nsIContentSecurityPolicy* aCsp) {
2494 return nsGlobalWindowInner::Cast(this)->SetCsp(aCsp);
2497 void nsPIDOMWindowInner::SetPreloadCsp(nsIContentSecurityPolicy* aPreloadCsp) {
2498 return nsGlobalWindowInner::Cast(this)->SetPreloadCsp(aPreloadCsp);
2501 nsIContentSecurityPolicy* nsPIDOMWindowInner::GetCsp() {
2502 return nsGlobalWindowInner::Cast(this)->GetCsp();
2505 void nsPIDOMWindowInner::NoteCalledRegisterForServiceWorkerScope(
2506 const nsACString& aScope) {
2507 nsGlobalWindowInner::Cast(this)->NoteCalledRegisterForServiceWorkerScope(
2508 aScope);
2511 void nsPIDOMWindowInner::NoteDOMContentLoaded() {
2512 nsGlobalWindowInner::Cast(this)->NoteDOMContentLoaded();
2515 bool nsGlobalWindowInner::ShouldReportForServiceWorkerScope(
2516 const nsAString& aScope) {
2517 bool result = false;
2519 nsPIDOMWindowOuter* topOuter = GetInProcessScriptableTop();
2520 NS_ENSURE_TRUE(topOuter, false);
2522 nsGlobalWindowInner* topInner =
2523 nsGlobalWindowInner::Cast(topOuter->GetCurrentInnerWindow());
2524 NS_ENSURE_TRUE(topInner, false);
2526 topInner->ShouldReportForServiceWorkerScopeInternal(
2527 NS_ConvertUTF16toUTF8(aScope), &result);
2528 return result;
2531 InstallTriggerImpl* nsGlobalWindowInner::GetInstallTrigger() {
2532 if (!mInstallTrigger &&
2533 !StaticPrefs::extensions_InstallTriggerImpl_enabled()) {
2534 // Return nullptr when InstallTriggerImpl is disabled by pref,
2535 // which does not yet break the "typeof InstallTrigger !== 'undefined"
2536 // "UA detection" use case, but prevents access to the InstallTriggerImpl
2537 // methods and properties.
2539 // NOTE: a separate pref ("extensions.InstallTrigger.enabled"), associated
2540 // to this property using the [Pref] extended attribute in Window.webidl,
2541 // does instead hide the entire InstallTrigger property.
2543 // See Bug 1754441 for more details about this deprecation.
2544 return nullptr;
2546 if (!mInstallTrigger) {
2547 ErrorResult rv;
2548 mInstallTrigger = ConstructJSImplementation<InstallTriggerImpl>(
2549 "@mozilla.org/addons/installtrigger;1", this, rv);
2550 if (rv.Failed()) {
2551 rv.SuppressException();
2552 return nullptr;
2556 return mInstallTrigger;
2559 nsIDOMWindowUtils* nsGlobalWindowInner::GetWindowUtils(ErrorResult& aRv) {
2560 FORWARD_TO_OUTER_OR_THROW(WindowUtils, (), aRv, nullptr);
2563 CallState nsGlobalWindowInner::ShouldReportForServiceWorkerScopeInternal(
2564 const nsACString& aScope, bool* aResultOut) {
2565 MOZ_DIAGNOSTIC_ASSERT(aResultOut);
2567 // First check to see if this window is controlled. If so, then we have
2568 // found a match and are done.
2569 const Maybe<ServiceWorkerDescriptor> swd = GetController();
2570 if (swd.isSome() && swd.ref().Scope() == aScope) {
2571 *aResultOut = true;
2572 return CallState::Stop;
2575 // Next, check to see if this window has called
2576 // navigator.serviceWorker.register() for this scope. If so, then treat this
2577 // as a match so console reports appear in the devtools console.
2578 if (mClientSource &&
2579 mClientSource->CalledRegisterForServiceWorkerScope(aScope)) {
2580 *aResultOut = true;
2581 return CallState::Stop;
2584 // Finally check the current docshell nsILoadGroup to see if there are any
2585 // outstanding navigation requests. If so, match the scope against the
2586 // channel's URL. We want to show console reports during the FetchEvent
2587 // intercepting the navigation itself.
2588 nsCOMPtr<nsIDocumentLoader> loader(do_QueryInterface(GetDocShell()));
2589 if (loader) {
2590 nsCOMPtr<nsILoadGroup> loadgroup;
2591 Unused << loader->GetLoadGroup(getter_AddRefs(loadgroup));
2592 if (loadgroup) {
2593 nsCOMPtr<nsISimpleEnumerator> iter;
2594 Unused << loadgroup->GetRequests(getter_AddRefs(iter));
2595 if (iter) {
2596 nsCOMPtr<nsISupports> tmp;
2597 bool hasMore = true;
2598 // Check each network request in the load group.
2599 while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) {
2600 iter->GetNext(getter_AddRefs(tmp));
2601 nsCOMPtr<nsIChannel> loadingChannel(do_QueryInterface(tmp));
2602 // Ignore subresource requests. Logging for a subresource
2603 // FetchEvent should be handled above since the client is
2604 // already controlled.
2605 if (!loadingChannel ||
2606 !nsContentUtils::IsNonSubresourceRequest(loadingChannel)) {
2607 continue;
2609 nsCOMPtr<nsIURI> loadingURL;
2610 Unused << loadingChannel->GetURI(getter_AddRefs(loadingURL));
2611 if (!loadingURL) {
2612 continue;
2614 nsAutoCString loadingSpec;
2615 Unused << loadingURL->GetSpec(loadingSpec);
2616 // Perform a simple substring comparison to match the scope
2617 // against the channel URL.
2618 if (StringBeginsWith(loadingSpec, aScope)) {
2619 *aResultOut = true;
2620 return CallState::Stop;
2627 // The current window doesn't care about this service worker, but maybe
2628 // one of our child frames does.
2629 return CallOnInProcessChildren(
2630 &nsGlobalWindowInner::ShouldReportForServiceWorkerScopeInternal, aScope,
2631 aResultOut);
2634 void nsGlobalWindowInner::NoteCalledRegisterForServiceWorkerScope(
2635 const nsACString& aScope) {
2636 if (!mClientSource) {
2637 return;
2640 mClientSource->NoteCalledRegisterForServiceWorkerScope(aScope);
2643 void nsGlobalWindowInner::NoteDOMContentLoaded() {
2644 if (!mClientSource) {
2645 return;
2648 mClientSource->NoteDOMContentLoaded();
2651 void nsGlobalWindowInner::UpdateTopInnerWindow() {
2652 if (IsTopInnerWindow() || !mTopInnerWindow) {
2653 return;
2656 mTopInnerWindow->UpdateWebSocketCount(-(int32_t)mNumOfOpenWebSockets);
2659 bool nsGlobalWindowInner::IsInSyncOperation() {
2660 return GetExtantDoc() && GetExtantDoc()->IsInSyncOperation();
2663 bool nsGlobalWindowInner::IsSharedMemoryAllowedInternal(
2664 nsIPrincipal* aPrincipal) const {
2665 MOZ_ASSERT(NS_IsMainThread());
2667 if (StaticPrefs::
2668 dom_postMessage_sharedArrayBuffer_bypassCOOP_COEP_insecure_enabled()) {
2669 return true;
2672 if (ExtensionPolicyService::GetSingleton().IsExtensionProcess()) {
2673 if (auto* basePrincipal = BasePrincipal::Cast(aPrincipal)) {
2674 if (auto* policy = basePrincipal->AddonPolicy()) {
2675 return policy->IsPrivileged();
2680 return CrossOriginIsolated();
2683 bool nsGlobalWindowInner::CrossOriginIsolated() const {
2684 MOZ_ASSERT(NS_IsMainThread());
2686 RefPtr<BrowsingContext> bc = GetBrowsingContext();
2687 MOZ_DIAGNOSTIC_ASSERT(bc);
2688 return bc->CrossOriginIsolated();
2691 WindowContext* TopWindowContext(nsPIDOMWindowInner& aWindow) {
2692 WindowContext* wc = aWindow.GetWindowContext();
2693 if (!wc) {
2694 return nullptr;
2697 return wc->TopWindowContext();
2700 void nsPIDOMWindowInner::AddPeerConnection() {
2701 MOZ_ASSERT(NS_IsMainThread());
2702 ++mActivePeerConnections;
2703 if (mActivePeerConnections == 1 && mWindowGlobalChild) {
2704 mWindowGlobalChild->SendUpdateActivePeerConnectionStatus(
2705 /*aIsAdded*/ true);
2707 // We need to present having active peer connections immediately. If we need
2708 // to wait for the parent process to come back with this information we
2709 // might start throttling.
2710 if (WindowContext* top = TopWindowContext(*this)) {
2711 top->TransientSetHasActivePeerConnections();
2716 void nsPIDOMWindowInner::RemovePeerConnection() {
2717 MOZ_ASSERT(NS_IsMainThread());
2718 MOZ_ASSERT(mActivePeerConnections > 0);
2719 --mActivePeerConnections;
2720 if (mActivePeerConnections == 0 && mWindowGlobalChild) {
2721 mWindowGlobalChild->SendUpdateActivePeerConnectionStatus(
2722 /*aIsAdded*/ false);
2726 bool nsPIDOMWindowInner::HasActivePeerConnections() {
2727 MOZ_ASSERT(NS_IsMainThread());
2729 WindowContext* topWindowContext = TopWindowContext(*this);
2730 return topWindowContext && topWindowContext->GetHasActivePeerConnections();
2733 void nsPIDOMWindowInner::AddMediaKeysInstance(MediaKeys* aMediaKeys) {
2734 MOZ_ASSERT(NS_IsMainThread());
2735 mMediaKeysInstances.AppendElement(aMediaKeys);
2736 if (mWindowGlobalChild && mMediaKeysInstances.Length() == 1) {
2737 mWindowGlobalChild->BlockBFCacheFor(BFCacheStatus::CONTAINS_EME_CONTENT);
2741 void nsPIDOMWindowInner::RemoveMediaKeysInstance(MediaKeys* aMediaKeys) {
2742 MOZ_ASSERT(NS_IsMainThread());
2743 mMediaKeysInstances.RemoveElement(aMediaKeys);
2744 if (mWindowGlobalChild && mMediaKeysInstances.IsEmpty()) {
2745 mWindowGlobalChild->UnblockBFCacheFor(BFCacheStatus::CONTAINS_EME_CONTENT);
2749 bool nsPIDOMWindowInner::HasActiveMediaKeysInstance() {
2750 MOZ_ASSERT(NS_IsMainThread());
2751 return !mMediaKeysInstances.IsEmpty();
2754 bool nsPIDOMWindowInner::IsPlayingAudio() {
2755 for (uint32_t i = 0; i < mAudioContexts.Length(); i++) {
2756 if (mAudioContexts[i]->IsRunning()) {
2757 return true;
2760 RefPtr<AudioChannelService> acs = AudioChannelService::Get();
2761 if (!acs) {
2762 return false;
2764 auto outer = GetOuterWindow();
2765 if (!outer) {
2766 // We've been unlinked and are about to die. Not a good time to pretend to
2767 // be playing audio.
2768 return false;
2770 return acs->IsWindowActive(outer);
2773 bool nsPIDOMWindowInner::IsDocumentLoaded() const { return mIsDocumentLoaded; }
2775 mozilla::dom::TimeoutManager& nsPIDOMWindowInner::TimeoutManager() {
2776 return *mTimeoutManager;
2779 bool nsPIDOMWindowInner::IsRunningTimeout() {
2780 return TimeoutManager().IsRunningTimeout();
2783 void nsPIDOMWindowInner::TryToCacheTopInnerWindow() {
2784 if (mHasTriedToCacheTopInnerWindow) {
2785 return;
2788 nsGlobalWindowInner* window = nsGlobalWindowInner::Cast(this);
2790 MOZ_ASSERT(!window->IsDying());
2792 mHasTriedToCacheTopInnerWindow = true;
2794 MOZ_ASSERT(window);
2796 if (nsCOMPtr<nsPIDOMWindowOuter> topOutter =
2797 window->GetInProcessScriptableTop()) {
2798 mTopInnerWindow = topOutter->GetCurrentInnerWindow();
2802 void nsPIDOMWindowInner::UpdateActiveIndexedDBDatabaseCount(int32_t aDelta) {
2803 MOZ_ASSERT(NS_IsMainThread());
2805 if (aDelta == 0) {
2806 return;
2809 // We count databases but not transactions because only active databases
2810 // could block throttling.
2811 uint32_t& counter = mTopInnerWindow
2812 ? mTopInnerWindow->mNumOfIndexedDBDatabases
2813 : mNumOfIndexedDBDatabases;
2815 counter += aDelta;
2818 bool nsPIDOMWindowInner::HasActiveIndexedDBDatabases() {
2819 MOZ_ASSERT(NS_IsMainThread());
2821 return mTopInnerWindow ? mTopInnerWindow->mNumOfIndexedDBDatabases > 0
2822 : mNumOfIndexedDBDatabases > 0;
2825 void nsPIDOMWindowInner::UpdateWebSocketCount(int32_t aDelta) {
2826 MOZ_ASSERT(NS_IsMainThread());
2828 if (aDelta == 0) {
2829 return;
2832 if (mTopInnerWindow && !IsTopInnerWindow()) {
2833 mTopInnerWindow->UpdateWebSocketCount(aDelta);
2836 MOZ_DIAGNOSTIC_ASSERT(
2837 aDelta > 0 || ((aDelta + mNumOfOpenWebSockets) < mNumOfOpenWebSockets));
2839 mNumOfOpenWebSockets += aDelta;
2842 bool nsPIDOMWindowInner::HasOpenWebSockets() const {
2843 MOZ_ASSERT(NS_IsMainThread());
2845 return mNumOfOpenWebSockets ||
2846 (mTopInnerWindow && mTopInnerWindow->mNumOfOpenWebSockets);
2849 bool nsPIDOMWindowInner::IsCurrentInnerWindow() const {
2850 if (mozilla::SessionHistoryInParent() && mBrowsingContext &&
2851 mBrowsingContext->IsInBFCache()) {
2852 return false;
2855 if (!mBrowsingContext || mBrowsingContext->IsDiscarded()) {
2856 // If our BrowsingContext has been discarded, we consider ourselves
2857 // still-current if we were current at the time it was discarded.
2858 return mOuterWindow && WasCurrentInnerWindow();
2861 nsPIDOMWindowOuter* outer = mBrowsingContext->GetDOMWindow();
2862 return outer && outer->GetCurrentInnerWindow() == this;
2865 bool nsPIDOMWindowInner::IsFullyActive() const {
2866 WindowContext* wc = GetWindowContext();
2867 if (!wc || wc->IsDiscarded() || !wc->IsCurrent()) {
2868 return false;
2870 return GetBrowsingContext()->AncestorsAreCurrent();
2873 void nsPIDOMWindowInner::SetAudioCapture(bool aCapture) {
2874 RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
2875 if (service) {
2876 service->SetWindowAudioCaptured(GetOuterWindow(), mWindowID, aCapture);
2880 void nsGlobalWindowInner::SetActiveLoadingState(bool aIsLoading) {
2881 MOZ_LOG(
2882 gTimeoutLog, mozilla::LogLevel::Debug,
2883 ("SetActiveLoadingState innerwindow %p: %d", (void*)this, aIsLoading));
2884 if (GetBrowsingContext()) {
2885 // Setting loading on a discarded context has no effect.
2886 Unused << GetBrowsingContext()->SetLoading(aIsLoading);
2889 if (!nsGlobalWindowInner::Cast(this)->IsChromeWindow()) {
2890 mTimeoutManager->SetLoading(aIsLoading);
2893 HintIsLoading(aIsLoading);
2896 void nsGlobalWindowInner::HintIsLoading(bool aIsLoading) {
2897 // Hint to tell the JS GC to use modified triggers during pageload.
2898 if (mHintedWasLoading != aIsLoading) {
2899 using namespace js::gc;
2900 SetPerformanceHint(danger::GetJSContext(), aIsLoading
2901 ? PerformanceHint::InPageLoad
2902 : PerformanceHint::Normal);
2903 mHintedWasLoading = aIsLoading;
2907 // nsISpeechSynthesisGetter
2909 #ifdef MOZ_WEBSPEECH
2910 SpeechSynthesis* nsGlobalWindowInner::GetSpeechSynthesis(ErrorResult& aError) {
2911 if (!mSpeechSynthesis) {
2912 mSpeechSynthesis = new SpeechSynthesis(this);
2915 return mSpeechSynthesis;
2918 bool nsGlobalWindowInner::HasActiveSpeechSynthesis() {
2919 if (mSpeechSynthesis) {
2920 return !mSpeechSynthesis->HasEmptyQueue();
2923 return false;
2926 #endif
2928 mozilla::glean::Glean* nsGlobalWindowInner::Glean() {
2929 if (!mGlean) {
2930 mGlean = new mozilla::glean::Glean(this);
2933 return mGlean;
2936 mozilla::glean::GleanPings* nsGlobalWindowInner::GleanPings() {
2937 if (!mGleanPings) {
2938 mGleanPings = new mozilla::glean::GleanPings();
2941 return mGleanPings;
2944 Nullable<WindowProxyHolder> nsGlobalWindowInner::GetParent(
2945 ErrorResult& aError) {
2946 FORWARD_TO_OUTER_OR_THROW(GetParentOuter, (), aError, nullptr);
2950 * GetInProcessScriptableParent used to be called when a script read
2951 * window.parent. Under Fission, that is now handled by
2952 * BrowsingContext::GetParent, and the result is a WindowProxyHolder rather than
2953 * an actual global window. This method still exists for legacy callers which
2954 * relied on the old logic, and require in-process windows. However, it only
2955 * works correctly when no out-of-process frames exist between this window and
2956 * the top-level window, so it should not be used in new code.
2958 * In contrast to GetRealParent, GetInProcessScriptableParent respects <iframe
2959 * mozbrowser> boundaries, so if |this| is contained by an <iframe
2960 * mozbrowser>, we will return |this| as its own parent.
2962 nsPIDOMWindowOuter* nsGlobalWindowInner::GetInProcessScriptableParent() {
2963 FORWARD_TO_OUTER(GetInProcessScriptableParent, (), nullptr);
2967 * GetInProcessScriptableTop used to be called when a script read window.top.
2968 * Under Fission, that is now handled by BrowsingContext::Top, and the result is
2969 * a WindowProxyHolder rather than an actual global window. This method still
2970 * exists for legacy callers which relied on the old logic, and require
2971 * in-process windows. However, it only works correctly when no out-of-process
2972 * frames exist between this window and the top-level window, so it should not
2973 * be used in new code.
2975 * In contrast to GetRealTop, GetInProcessScriptableTop respects <iframe
2976 * mozbrowser> boundaries. If we encounter a window owned by an <iframe
2977 * mozbrowser> while walking up the window hierarchy, we'll stop and return that
2978 * window.
2980 nsPIDOMWindowOuter* nsGlobalWindowInner::GetInProcessScriptableTop() {
2981 FORWARD_TO_OUTER(GetInProcessScriptableTop, (), nullptr);
2984 void nsGlobalWindowInner::GetContent(JSContext* aCx,
2985 JS::MutableHandle<JSObject*> aRetval,
2986 CallerType aCallerType,
2987 ErrorResult& aError) {
2988 FORWARD_TO_OUTER_OR_THROW(GetContentOuter,
2989 (aCx, aRetval, aCallerType, aError), aError, );
2992 BarProp* nsGlobalWindowInner::GetMenubar(ErrorResult& aError) {
2993 if (!mMenubar) {
2994 mMenubar = new MenubarProp(this);
2997 return mMenubar;
3000 BarProp* nsGlobalWindowInner::GetToolbar(ErrorResult& aError) {
3001 if (!mToolbar) {
3002 mToolbar = new ToolbarProp(this);
3005 return mToolbar;
3008 BarProp* nsGlobalWindowInner::GetLocationbar(ErrorResult& aError) {
3009 if (!mLocationbar) {
3010 mLocationbar = new LocationbarProp(this);
3012 return mLocationbar;
3015 BarProp* nsGlobalWindowInner::GetPersonalbar(ErrorResult& aError) {
3016 if (!mPersonalbar) {
3017 mPersonalbar = new PersonalbarProp(this);
3019 return mPersonalbar;
3022 BarProp* nsGlobalWindowInner::GetStatusbar(ErrorResult& aError) {
3023 if (!mStatusbar) {
3024 mStatusbar = new StatusbarProp(this);
3026 return mStatusbar;
3029 BarProp* nsGlobalWindowInner::GetScrollbars(ErrorResult& aError) {
3030 if (!mScrollbars) {
3031 mScrollbars = new ScrollbarsProp(this);
3034 return mScrollbars;
3037 bool nsGlobalWindowInner::GetClosed(ErrorResult& aError) {
3038 // If we're called from JS (which is the only way we should be getting called
3039 // here) and we reach this point, that means our JS global is the current
3040 // target of the WindowProxy, which means that we are the "current inner"
3041 // of our outer. So if FORWARD_TO_OUTER fails to forward, that means the
3042 // outer is already torn down, which corresponds to the closed state.
3043 FORWARD_TO_OUTER(GetClosedOuter, (), true);
3046 Nullable<WindowProxyHolder> nsGlobalWindowInner::IndexedGetter(
3047 uint32_t aIndex) {
3048 FORWARD_TO_OUTER(IndexedGetterOuter, (aIndex), nullptr);
3051 namespace {
3053 struct InterfaceShimEntry {
3054 const char* geckoName;
3055 const char* domName;
3058 } // anonymous namespace
3060 // We add shims from Components.interfaces.nsIDOMFoo to window.Foo for each
3061 // interface that has interface constants that sites might be getting off
3062 // of Ci.
3063 const InterfaceShimEntry kInterfaceShimMap[] = {
3064 {"nsIXMLHttpRequest", "XMLHttpRequest"},
3065 {"nsIDOMDOMException", "DOMException"},
3066 {"nsIDOMNode", "Node"},
3067 {"nsIDOMCSSRule", "CSSRule"},
3068 {"nsIDOMEvent", "Event"},
3069 {"nsIDOMNSEvent", "Event"},
3070 {"nsIDOMKeyEvent", "KeyEvent"},
3071 {"nsIDOMMouseEvent", "MouseEvent"},
3072 {"nsIDOMMouseScrollEvent", "MouseScrollEvent"},
3073 {"nsIDOMMutationEvent", "MutationEvent"},
3074 {"nsIDOMUIEvent", "UIEvent"},
3075 {"nsIDOMHTMLMediaElement", "HTMLMediaElement"},
3076 {"nsIDOMRange", "Range"},
3077 // Think about whether Ci.nsINodeFilter can just go away for websites!
3078 {"nsIDOMNodeFilter", "NodeFilter"},
3079 {"nsIDOMXPathResult", "XPathResult"}};
3081 bool nsGlobalWindowInner::ResolveComponentsShim(
3082 JSContext* aCx, JS::Handle<JSObject*> aGlobal,
3083 JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> aDesc) {
3084 // Keep track of how often this happens.
3085 Telemetry::Accumulate(Telemetry::COMPONENTS_SHIM_ACCESSED_BY_CONTENT, true);
3087 // Warn once.
3088 nsCOMPtr<Document> doc = GetExtantDoc();
3089 if (doc) {
3090 doc->WarnOnceAbout(DeprecatedOperations::eComponents, /* asError = */ true);
3093 // Create a fake Components object.
3094 AssertSameCompartment(aCx, aGlobal);
3095 JS::Rooted<JSObject*> components(aCx, JS_NewPlainObject(aCx));
3096 if (NS_WARN_IF(!components)) {
3097 return false;
3100 // Create a fake interfaces object.
3101 JS::Rooted<JSObject*> interfaces(aCx, JS_NewPlainObject(aCx));
3102 if (NS_WARN_IF(!interfaces)) {
3103 return false;
3105 bool ok =
3106 JS_DefineProperty(aCx, components, "interfaces", interfaces,
3107 JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY);
3108 if (NS_WARN_IF(!ok)) {
3109 return false;
3112 // Define a bunch of shims from the Ci.nsIDOMFoo to window.Foo for DOM
3113 // interfaces with constants.
3114 for (uint32_t i = 0; i < ArrayLength(kInterfaceShimMap); ++i) {
3115 // Grab the names from the table.
3116 const char* geckoName = kInterfaceShimMap[i].geckoName;
3117 const char* domName = kInterfaceShimMap[i].domName;
3119 // Look up the appopriate interface object on the global.
3120 JS::Rooted<JS::Value> v(aCx, JS::UndefinedValue());
3121 ok = JS_GetProperty(aCx, aGlobal, domName, &v);
3122 if (NS_WARN_IF(!ok)) {
3123 return false;
3125 if (!v.isObject()) {
3126 NS_WARNING("Unable to find interface object on global");
3127 continue;
3130 // Define the shim on the interfaces object.
3131 ok = JS_DefineProperty(
3132 aCx, interfaces, geckoName, v,
3133 JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY);
3134 if (NS_WARN_IF(!ok)) {
3135 return false;
3139 aDesc.set(mozilla::Some(JS::PropertyDescriptor::Data(
3140 JS::ObjectValue(*components),
3141 {JS::PropertyAttribute::Configurable, JS::PropertyAttribute::Enumerable,
3142 JS::PropertyAttribute::Writable})));
3143 return true;
3146 #ifdef RELEASE_OR_BETA
3147 # define USE_CONTROLLERS_SHIM
3148 #endif
3150 #ifdef USE_CONTROLLERS_SHIM
3151 static const JSClass ControllersShimClass = {"Controllers", 0};
3152 static const JSClass XULControllersShimClass = {"XULControllers", 0};
3153 #endif
3155 bool nsGlobalWindowInner::DoResolve(
3156 JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aId,
3157 JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> aDesc) {
3158 // Note: Keep this in sync with MayResolve.
3160 // Note: The infallibleInit call in GlobalResolve depends on this check.
3161 if (!aId.isString()) {
3162 return true;
3165 bool found;
3166 if (!WebIDLGlobalNameHash::DefineIfEnabled(aCx, aObj, aId, aDesc, &found)) {
3167 return false;
3170 if (found) {
3171 return true;
3174 // We support a cut-down Components.interfaces in case websites are
3175 // using Components.interfaces.nsIFoo.CONSTANT_NAME for the ones
3176 // that have constants.
3177 if (StaticPrefs::dom_use_components_shim() &&
3178 aId == XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_COMPONENTS)) {
3179 return ResolveComponentsShim(aCx, aObj, aDesc);
3182 // We also support a "window.controllers" thing; apparently some
3183 // sites use it for browser-sniffing. See bug 1010577.
3184 #ifdef USE_CONTROLLERS_SHIM
3185 // Note: We use |aObj| rather than |this| to get the principal here, because
3186 // this is called during Window setup when the Document isn't necessarily
3187 // hooked up yet.
3188 if ((aId == XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_CONTROLLERS) ||
3189 aId == XPCJSRuntime::Get()->GetStringID(
3190 XPCJSContext::IDX_CONTROLLERS_CLASS)) &&
3191 !xpc::IsXrayWrapper(aObj) &&
3192 !nsContentUtils::ObjectPrincipal(aObj)->IsSystemPrincipal()) {
3193 if (GetExtantDoc()) {
3194 GetExtantDoc()->WarnOnceAbout(
3195 DeprecatedOperations::eWindow_Cc_ontrollers);
3197 const JSClass* clazz;
3198 if (aId ==
3199 XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_CONTROLLERS)) {
3200 clazz = &XULControllersShimClass;
3201 } else {
3202 clazz = &ControllersShimClass;
3204 MOZ_ASSERT(JS_IsGlobalObject(aObj));
3205 JS::Rooted<JSObject*> shim(aCx, JS_NewObject(aCx, clazz));
3206 if (NS_WARN_IF(!shim)) {
3207 return false;
3210 aDesc.set(mozilla::Some(JS::PropertyDescriptor::Data(
3211 JS::ObjectValue(*shim),
3212 {JS::PropertyAttribute::Configurable, JS::PropertyAttribute::Enumerable,
3213 JS::PropertyAttribute::Writable})));
3214 return true;
3216 #endif
3218 return true;
3221 /* static */
3222 bool nsGlobalWindowInner::MayResolve(jsid aId) {
3223 // Note: This function does not fail and may not have any side-effects.
3224 // Note: Keep this in sync with DoResolve.
3225 if (!aId.isString()) {
3226 return false;
3229 if (aId == XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_COMPONENTS)) {
3230 return true;
3233 if (aId == XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_CONTROLLERS) ||
3234 aId == XPCJSRuntime::Get()->GetStringID(
3235 XPCJSContext::IDX_CONTROLLERS_CLASS)) {
3236 // We only resolve .controllers/.Controllers in release builds and on
3237 // non-chrome windows, but let's not worry about any of that stuff.
3238 return true;
3241 return WebIDLGlobalNameHash::MayResolve(aId);
3244 void nsGlobalWindowInner::GetOwnPropertyNames(
3245 JSContext* aCx, JS::MutableHandleVector<jsid> aNames, bool aEnumerableOnly,
3246 ErrorResult& aRv) {
3247 if (aEnumerableOnly) {
3248 // The names we would return from here get defined on the window via one of
3249 // two codepaths. The ones coming from the WebIDLGlobalNameHash will end up
3250 // in the DefineConstructor function in BindingUtils, which always defines
3251 // things as non-enumerable. The ones coming from the script namespace
3252 // manager get defined by our resolve hook using FillPropertyDescriptor with
3253 // 0 for the property attributes, so non-enumerable as well.
3255 // So in the aEnumerableOnly case we have nothing to do.
3256 return;
3259 // "Components" is marked as enumerable but only resolved on demand :-/.
3260 // aNames.AppendElement(u"Components"_ns);
3262 JS::Rooted<JSObject*> wrapper(aCx, GetWrapper());
3264 // There are actually two ways we can get called here: For normal
3265 // enumeration or for Xray enumeration. In the latter case, we want to
3266 // return all possible WebIDL names, because we don't really support
3267 // deleting these names off our Xray; trying to resolve them will just make
3268 // them come back. In the former case, we want to avoid returning deleted
3269 // names. But the JS engine already knows about the non-deleted
3270 // already-resolved names, so we can just return the so-far-unresolved ones.
3272 // We can tell which case we're in by whether aCx is in our wrapper's
3273 // compartment. If not, we're in the Xray case.
3274 WebIDLGlobalNameHash::NameType nameType =
3275 js::IsObjectInContextCompartment(wrapper, aCx)
3276 ? WebIDLGlobalNameHash::UnresolvedNamesOnly
3277 : WebIDLGlobalNameHash::AllNames;
3278 if (!WebIDLGlobalNameHash::GetNames(aCx, wrapper, nameType, aNames)) {
3279 aRv.NoteJSContextException(aCx);
3283 /* static */
3284 bool nsGlobalWindowInner::IsPrivilegedChromeWindow(JSContext*, JSObject* aObj) {
3285 // For now, have to deal with XPConnect objects here.
3286 nsGlobalWindowInner* win = xpc::WindowOrNull(aObj);
3287 return win && win->IsChromeWindow() &&
3288 nsContentUtils::ObjectPrincipal(aObj) ==
3289 nsContentUtils::GetSystemPrincipal();
3292 /* static */
3293 bool nsGlobalWindowInner::DeviceSensorsEnabled(JSContext*, JSObject*) {
3294 return Preferences::GetBool("device.sensors.enabled");
3297 /* static */
3298 bool nsGlobalWindowInner::CachesEnabled(JSContext* aCx, JSObject* aObj) {
3299 if (!IsSecureContextOrObjectIsFromSecureContext(aCx, aObj)) {
3300 return StaticPrefs::dom_caches_testing_enabled() ||
3301 StaticPrefs::dom_serviceWorkers_testing_enabled();
3303 return true;
3306 /* static */
3307 bool nsGlobalWindowInner::IsSizeToContentEnabled(JSContext* aCx, JSObject*) {
3308 return StaticPrefs::dom_window_sizeToContent_enabled() ||
3309 nsContentUtils::IsSystemCaller(aCx);
3312 /* static */
3313 bool nsGlobalWindowInner::IsGleanNeeded(JSContext* aCx, JSObject* aObj) {
3314 // Glean is needed in ChromeOnly contexts and also in privileged about pages.
3315 nsIPrincipal* principal = nsContentUtils::SubjectPrincipal(aCx);
3316 if (principal->IsSystemPrincipal()) {
3317 return true;
3320 uint32_t flags = 0;
3321 if (NS_FAILED(principal->GetAboutModuleFlags(&flags))) {
3322 return false;
3324 return flags & nsIAboutModule::IS_SECURE_CHROME_UI;
3327 Crypto* nsGlobalWindowInner::GetCrypto(ErrorResult& aError) {
3328 if (!mCrypto) {
3329 mCrypto = new Crypto(this);
3331 return mCrypto;
3334 nsIControllers* nsGlobalWindowInner::GetControllers(ErrorResult& aError) {
3335 FORWARD_TO_OUTER_OR_THROW(GetControllersOuter, (aError), aError, nullptr);
3338 nsresult nsGlobalWindowInner::GetControllers(nsIControllers** aResult) {
3339 ErrorResult rv;
3340 nsCOMPtr<nsIControllers> controllers = GetControllers(rv);
3341 controllers.forget(aResult);
3343 return rv.StealNSResult();
3346 Nullable<WindowProxyHolder> nsGlobalWindowInner::GetOpenerWindow(
3347 ErrorResult& aError) {
3348 FORWARD_TO_OUTER_OR_THROW(GetOpenerWindowOuter, (), aError, nullptr);
3351 void nsGlobalWindowInner::GetOpener(JSContext* aCx,
3352 JS::MutableHandle<JS::Value> aRetval,
3353 ErrorResult& aError) {
3354 Nullable<WindowProxyHolder> opener = GetOpenerWindow(aError);
3355 if (aError.Failed() || opener.IsNull()) {
3356 aRetval.setNull();
3357 return;
3360 if (!ToJSValue(aCx, opener.Value(), aRetval)) {
3361 aError.NoteJSContextException(aCx);
3365 void nsGlobalWindowInner::SetOpener(JSContext* aCx,
3366 JS::Handle<JS::Value> aOpener,
3367 ErrorResult& aError) {
3368 if (aOpener.isNull()) {
3369 RefPtr<BrowsingContext> bc(GetBrowsingContext());
3370 if (!bc->IsDiscarded()) {
3371 bc->SetOpener(nullptr);
3373 return;
3376 // If something other than null is passed, just define aOpener on our inner
3377 // window's JS object, wrapped into the current compartment so that for Xrays
3378 // we define on the Xray expando object, but don't set it on the outer window,
3379 // so that it'll get reset on navigation. This is just like replaceable
3380 // properties, but we're not quite readonly.
3381 RedefineProperty(aCx, "opener", aOpener, aError);
3384 void nsGlobalWindowInner::GetEvent(OwningEventOrUndefined& aRetval) {
3385 if (mEvent) {
3386 aRetval.SetAsEvent() = mEvent;
3387 } else {
3388 aRetval.SetUndefined();
3392 void nsGlobalWindowInner::GetStatus(nsAString& aStatus, ErrorResult& aError) {
3393 FORWARD_TO_OUTER_OR_THROW(GetStatusOuter, (aStatus), aError, );
3396 void nsGlobalWindowInner::SetStatus(const nsAString& aStatus,
3397 ErrorResult& aError) {
3398 FORWARD_TO_OUTER_OR_THROW(SetStatusOuter, (aStatus), aError, );
3401 void nsGlobalWindowInner::GetName(nsAString& aName, ErrorResult& aError) {
3402 FORWARD_TO_OUTER_OR_THROW(GetNameOuter, (aName), aError, );
3405 void nsGlobalWindowInner::SetName(const nsAString& aName,
3406 mozilla::ErrorResult& aError) {
3407 FORWARD_TO_OUTER_OR_THROW(SetNameOuter, (aName, aError), aError, );
3410 double nsGlobalWindowInner::GetInnerWidth(ErrorResult& aError) {
3411 FORWARD_TO_OUTER_OR_THROW(GetInnerWidthOuter, (aError), aError, 0);
3414 nsresult nsGlobalWindowInner::GetInnerWidth(double* aWidth) {
3415 ErrorResult rv;
3416 // Callee doesn't care about the caller type, but play it safe.
3417 *aWidth = GetInnerWidth(rv);
3418 return rv.StealNSResult();
3421 double nsGlobalWindowInner::GetInnerHeight(ErrorResult& aError) {
3422 // We ignore aCallerType; we only have that argument because some other things
3423 // called by GetReplaceableWindowCoord need it. If this ever changes, fix
3424 // nsresult nsGlobalWindowInner::GetInnerHeight(double* aInnerWidth)
3425 // to actually take a useful CallerType and pass it in here.
3426 FORWARD_TO_OUTER_OR_THROW(GetInnerHeightOuter, (aError), aError, 0);
3429 nsresult nsGlobalWindowInner::GetInnerHeight(double* aHeight) {
3430 ErrorResult rv;
3431 // Callee doesn't care about the caller type, but play it safe.
3432 *aHeight = GetInnerHeight(rv);
3433 return rv.StealNSResult();
3436 int32_t nsGlobalWindowInner::GetOuterWidth(CallerType aCallerType,
3437 ErrorResult& aError) {
3438 FORWARD_TO_OUTER_OR_THROW(GetOuterWidthOuter, (aCallerType, aError), aError,
3442 int32_t nsGlobalWindowInner::GetOuterHeight(CallerType aCallerType,
3443 ErrorResult& aError) {
3444 FORWARD_TO_OUTER_OR_THROW(GetOuterHeightOuter, (aCallerType, aError), aError,
3448 double nsGlobalWindowInner::ScreenEdgeSlopX() const {
3449 FORWARD_TO_OUTER(ScreenEdgeSlopX, (), 0);
3452 double nsGlobalWindowInner::ScreenEdgeSlopY() const {
3453 FORWARD_TO_OUTER(ScreenEdgeSlopY, (), 0);
3456 int32_t nsGlobalWindowInner::GetScreenX(CallerType aCallerType,
3457 ErrorResult& aError) {
3458 FORWARD_TO_OUTER_OR_THROW(GetScreenXOuter, (aCallerType, aError), aError, 0);
3461 int32_t nsGlobalWindowInner::GetScreenY(CallerType aCallerType,
3462 ErrorResult& aError) {
3463 FORWARD_TO_OUTER_OR_THROW(GetScreenYOuter, (aCallerType, aError), aError, 0);
3466 float nsGlobalWindowInner::GetMozInnerScreenX(CallerType aCallerType,
3467 ErrorResult& aError) {
3468 FORWARD_TO_OUTER_OR_THROW(GetMozInnerScreenXOuter, (aCallerType), aError, 0);
3471 float nsGlobalWindowInner::GetMozInnerScreenY(CallerType aCallerType,
3472 ErrorResult& aError) {
3473 FORWARD_TO_OUTER_OR_THROW(GetMozInnerScreenYOuter, (aCallerType), aError, 0);
3476 static nsPresContext* GetPresContextForRatio(Document* aDoc) {
3477 if (nsPresContext* presContext = aDoc->GetPresContext()) {
3478 return presContext;
3480 // We're in an undisplayed subdocument... There's not really an awesome way
3481 // to tell what the right DPI is from here, so we try to walk up our parent
3482 // document chain to the extent that the docs can observe each other.
3483 Document* doc = aDoc;
3484 while (doc->StyleOrLayoutObservablyDependsOnParentDocumentLayout()) {
3485 doc = doc->GetInProcessParentDocument();
3486 if (nsPresContext* presContext = doc->GetPresContext()) {
3487 return presContext;
3490 return nullptr;
3493 double nsGlobalWindowInner::GetDevicePixelRatio(CallerType aCallerType,
3494 ErrorResult& aError) {
3495 ENSURE_ACTIVE_DOCUMENT(aError, 0.0);
3497 RefPtr<nsPresContext> presContext = GetPresContextForRatio(mDoc);
3498 if (NS_WARN_IF(!presContext)) {
3499 // Still nothing, oh well.
3500 return 1.0;
3503 if (nsIGlobalObject::ShouldResistFingerprinting(
3504 aCallerType, RFPTarget::WindowDevicePixelRatio)) {
3505 // Spoofing the DevicePixelRatio causes blurriness in some situations
3506 // on HiDPI displays. pdf.js is a non-system caller; but it can't
3507 // expose the fingerprintable information, so we can safely disable
3508 // spoofing in this situation. It doesn't address the issue for
3509 // web-rendered content (including pdf.js instances on the web.)
3510 // In the future we hope to have a better solution to fix all HiDPI
3511 // blurriness...
3512 nsAutoCString origin;
3513 nsresult rv = this->GetPrincipal()->GetOrigin(origin);
3514 if (NS_FAILED(rv) || origin != "resource://pdf.js"_ns) {
3515 return 1.0;
3519 if (aCallerType == CallerType::NonSystem) {
3520 float overrideDPPX = presContext->GetOverrideDPPX();
3521 if (overrideDPPX > 0.0f) {
3522 return overrideDPPX;
3526 return double(AppUnitsPerCSSPixel()) /
3527 double(presContext->AppUnitsPerDevPixel());
3530 double nsGlobalWindowInner::GetDesktopToDeviceScale(ErrorResult& aError) {
3531 ENSURE_ACTIVE_DOCUMENT(aError, 0.0);
3532 nsPresContext* presContext = GetPresContextForRatio(mDoc);
3533 if (!presContext) {
3534 return 1.0;
3536 return presContext->DeviceContext()->GetDesktopToDeviceScale().scale;
3539 int32_t nsGlobalWindowInner::RequestAnimationFrame(
3540 FrameRequestCallback& aCallback, ErrorResult& aError) {
3541 if (!mDoc) {
3542 return 0;
3545 if (GetWrapperPreserveColor()) {
3546 js::NotifyAnimationActivity(GetWrapperPreserveColor());
3549 DebuggerNotificationDispatch(this,
3550 DebuggerNotificationType::RequestAnimationFrame);
3552 int32_t handle;
3553 aError = mDoc->ScheduleFrameRequestCallback(aCallback, &handle);
3554 return handle;
3557 void nsGlobalWindowInner::CancelAnimationFrame(int32_t aHandle,
3558 ErrorResult& aError) {
3559 if (!mDoc) {
3560 return;
3563 DebuggerNotificationDispatch(this,
3564 DebuggerNotificationType::CancelAnimationFrame);
3566 mDoc->CancelFrameRequestCallback(aHandle);
3569 already_AddRefed<MediaQueryList> nsGlobalWindowInner::MatchMedia(
3570 const nsACString& aMediaQueryList, CallerType aCallerType,
3571 ErrorResult& aError) {
3572 ENSURE_ACTIVE_DOCUMENT(aError, nullptr);
3573 return mDoc->MatchMedia(aMediaQueryList, aCallerType);
3576 int32_t nsGlobalWindowInner::GetScrollMinX(ErrorResult& aError) {
3577 FORWARD_TO_OUTER_OR_THROW(GetScrollBoundaryOuter, (eSideLeft), aError, 0);
3580 int32_t nsGlobalWindowInner::GetScrollMinY(ErrorResult& aError) {
3581 FORWARD_TO_OUTER_OR_THROW(GetScrollBoundaryOuter, (eSideTop), aError, 0);
3584 int32_t nsGlobalWindowInner::GetScrollMaxX(ErrorResult& aError) {
3585 FORWARD_TO_OUTER_OR_THROW(GetScrollBoundaryOuter, (eSideRight), aError, 0);
3588 int32_t nsGlobalWindowInner::GetScrollMaxY(ErrorResult& aError) {
3589 FORWARD_TO_OUTER_OR_THROW(GetScrollBoundaryOuter, (eSideBottom), aError, 0);
3592 double nsGlobalWindowInner::GetScrollX(ErrorResult& aError) {
3593 FORWARD_TO_OUTER_OR_THROW(GetScrollXOuter, (), aError, 0);
3596 double nsGlobalWindowInner::GetScrollY(ErrorResult& aError) {
3597 FORWARD_TO_OUTER_OR_THROW(GetScrollYOuter, (), aError, 0);
3600 uint32_t nsGlobalWindowInner::Length() { FORWARD_TO_OUTER(Length, (), 0); }
3602 Nullable<WindowProxyHolder> nsGlobalWindowInner::GetTop(
3603 mozilla::ErrorResult& aError) {
3604 FORWARD_TO_OUTER_OR_THROW(GetTopOuter, (), aError, nullptr);
3607 already_AddRefed<BrowsingContext> nsGlobalWindowInner::GetChildWindow(
3608 const nsAString& aName) {
3609 if (GetOuterWindowInternal()) {
3610 return GetOuterWindowInternal()->GetChildWindow(aName);
3612 return nullptr;
3615 void nsGlobalWindowInner::RefreshRealmPrincipal() {
3616 JS::SetRealmPrincipals(js::GetNonCCWObjectRealm(GetWrapperPreserveColor()),
3617 nsJSPrincipals::get(mDoc->NodePrincipal()));
3620 void nsGlobalWindowInner::RefreshReduceTimerPrecisionCallerType() {
3621 JS::SetRealmReduceTimerPrecisionCallerType(
3622 js::GetNonCCWObjectRealm(GetWrapperPreserveColor()),
3623 RTPCallerTypeToToken(GetRTPCallerType()));
3626 already_AddRefed<nsIWidget> nsGlobalWindowInner::GetMainWidget() {
3627 FORWARD_TO_OUTER(GetMainWidget, (), nullptr);
3630 nsIWidget* nsGlobalWindowInner::GetNearestWidget() const {
3631 if (GetOuterWindowInternal()) {
3632 return GetOuterWindowInternal()->GetNearestWidget();
3634 return nullptr;
3637 void nsGlobalWindowInner::SetFullScreen(bool aFullscreen,
3638 mozilla::ErrorResult& aError) {
3639 FORWARD_TO_OUTER_OR_THROW(SetFullscreenOuter, (aFullscreen, aError), aError,
3640 /* void */);
3643 bool nsGlobalWindowInner::GetFullScreen(ErrorResult& aError) {
3644 FORWARD_TO_OUTER_OR_THROW(GetFullscreenOuter, (), aError, false);
3647 bool nsGlobalWindowInner::GetFullScreen() {
3648 ErrorResult dummy;
3649 bool fullscreen = GetFullScreen(dummy);
3650 dummy.SuppressException();
3651 return fullscreen;
3654 void nsGlobalWindowInner::Dump(const nsAString& aStr) {
3655 if (!nsJSUtils::DumpEnabled()) {
3656 return;
3659 char* cstr = ToNewUTF8String(aStr);
3661 #if defined(XP_MACOSX)
3662 // have to convert \r to \n so that printing to the console works
3663 char *c = cstr, *cEnd = cstr + strlen(cstr);
3664 while (c < cEnd) {
3665 if (*c == '\r') *c = '\n';
3666 c++;
3668 #endif
3670 if (cstr) {
3671 MOZ_LOG(nsContentUtils::DOMDumpLog(), LogLevel::Debug,
3672 ("[Window.Dump] %s", cstr));
3673 #ifdef XP_WIN
3674 PrintToDebugger(cstr);
3675 #endif
3676 #ifdef ANDROID
3677 __android_log_write(ANDROID_LOG_INFO, "GeckoDump", cstr);
3678 #endif
3679 FILE* fp = gDumpFile ? gDumpFile : stdout;
3680 fputs(cstr, fp);
3681 fflush(fp);
3682 free(cstr);
3686 void nsGlobalWindowInner::Alert(nsIPrincipal& aSubjectPrincipal,
3687 ErrorResult& aError) {
3688 Alert(u""_ns, aSubjectPrincipal, aError);
3691 void nsGlobalWindowInner::Alert(const nsAString& aMessage,
3692 nsIPrincipal& aSubjectPrincipal,
3693 ErrorResult& aError) {
3694 FORWARD_TO_OUTER_OR_THROW(AlertOuter, (aMessage, aSubjectPrincipal, aError),
3695 aError, );
3698 bool nsGlobalWindowInner::Confirm(const nsAString& aMessage,
3699 nsIPrincipal& aSubjectPrincipal,
3700 ErrorResult& aError) {
3701 FORWARD_TO_OUTER_OR_THROW(ConfirmOuter, (aMessage, aSubjectPrincipal, aError),
3702 aError, false);
3705 already_AddRefed<Promise> nsGlobalWindowInner::Fetch(
3706 const RequestOrUTF8String& aInput, const RequestInit& aInit,
3707 CallerType aCallerType, ErrorResult& aRv) {
3708 return FetchRequest(this, aInput, aInit, aCallerType, aRv);
3711 void nsGlobalWindowInner::Prompt(const nsAString& aMessage,
3712 const nsAString& aInitial, nsAString& aReturn,
3713 nsIPrincipal& aSubjectPrincipal,
3714 ErrorResult& aError) {
3715 FORWARD_TO_OUTER_OR_THROW(
3716 PromptOuter, (aMessage, aInitial, aReturn, aSubjectPrincipal, aError),
3717 aError, );
3720 void nsGlobalWindowInner::Focus(CallerType aCallerType, ErrorResult& aError) {
3721 FORWARD_TO_OUTER_OR_THROW(FocusOuter,
3722 (aCallerType, /* aFromOtherProcess */ false,
3723 nsFocusManager::GenerateFocusActionId()),
3724 aError, );
3727 nsresult nsGlobalWindowInner::Focus(CallerType aCallerType) {
3728 ErrorResult rv;
3729 Focus(aCallerType, rv);
3731 return rv.StealNSResult();
3734 void nsGlobalWindowInner::Blur(CallerType aCallerType, ErrorResult& aError) {
3735 FORWARD_TO_OUTER_OR_THROW(BlurOuter, (aCallerType), aError, );
3738 void nsGlobalWindowInner::Stop(ErrorResult& aError) {
3739 FORWARD_TO_OUTER_OR_THROW(StopOuter, (aError), aError, );
3742 void nsGlobalWindowInner::Print(ErrorResult& aError) {
3743 FORWARD_TO_OUTER_OR_THROW(PrintOuter, (aError), aError, );
3746 Nullable<WindowProxyHolder> nsGlobalWindowInner::PrintPreview(
3747 nsIPrintSettings* aSettings, nsIWebProgressListener* aListener,
3748 nsIDocShell* aDocShellToCloneInto, ErrorResult& aError) {
3749 FORWARD_TO_OUTER_OR_THROW(
3750 Print,
3751 (aSettings,
3752 /* aRemotePrintJob = */ nullptr, aListener, aDocShellToCloneInto,
3753 nsGlobalWindowOuter::IsPreview::Yes,
3754 nsGlobalWindowOuter::IsForWindowDotPrint::No,
3755 /* aPrintPreviewCallback = */ nullptr, nullptr, aError),
3756 aError, nullptr);
3759 void nsGlobalWindowInner::MoveTo(int32_t aXPos, int32_t aYPos,
3760 CallerType aCallerType, ErrorResult& aError) {
3761 FORWARD_TO_OUTER_OR_THROW(MoveToOuter, (aXPos, aYPos, aCallerType, aError),
3762 aError, );
3765 void nsGlobalWindowInner::MoveBy(int32_t aXDif, int32_t aYDif,
3766 CallerType aCallerType, ErrorResult& aError) {
3767 FORWARD_TO_OUTER_OR_THROW(MoveByOuter, (aXDif, aYDif, aCallerType, aError),
3768 aError, );
3771 void nsGlobalWindowInner::ResizeTo(int32_t aWidth, int32_t aHeight,
3772 CallerType aCallerType,
3773 ErrorResult& aError) {
3774 FORWARD_TO_OUTER_OR_THROW(ResizeToOuter,
3775 (aWidth, aHeight, aCallerType, aError), aError, );
3778 void nsGlobalWindowInner::ResizeBy(int32_t aWidthDif, int32_t aHeightDif,
3779 CallerType aCallerType,
3780 ErrorResult& aError) {
3781 FORWARD_TO_OUTER_OR_THROW(
3782 ResizeByOuter, (aWidthDif, aHeightDif, aCallerType, aError), aError, );
3785 void nsGlobalWindowInner::SizeToContent(CallerType aCallerType,
3786 ErrorResult& aError) {
3787 FORWARD_TO_OUTER_OR_THROW(SizeToContentOuter, (aCallerType, {}, aError),
3788 aError, );
3791 void nsGlobalWindowInner::SizeToContentConstrained(
3792 const SizeToContentConstraints& aConstraints, ErrorResult& aError) {
3793 FORWARD_TO_OUTER_OR_THROW(
3794 SizeToContentOuter, (CallerType::System, aConstraints, aError), aError, );
3797 already_AddRefed<nsPIWindowRoot> nsGlobalWindowInner::GetTopWindowRoot() {
3798 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
3799 if (!outer) {
3800 return nullptr;
3802 return outer->GetTopWindowRoot();
3805 void nsGlobalWindowInner::Scroll(double aXScroll, double aYScroll) {
3806 // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
3807 auto scrollPos = CSSIntPoint::Truncate(mozilla::ToZeroIfNonfinite(aXScroll),
3808 mozilla::ToZeroIfNonfinite(aYScroll));
3809 ScrollTo(scrollPos, ScrollOptions());
3812 void nsGlobalWindowInner::ScrollTo(double aXScroll, double aYScroll) {
3813 // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
3814 auto scrollPos = CSSIntPoint::Truncate(mozilla::ToZeroIfNonfinite(aXScroll),
3815 mozilla::ToZeroIfNonfinite(aYScroll));
3816 ScrollTo(scrollPos, ScrollOptions());
3819 void nsGlobalWindowInner::ScrollTo(const ScrollToOptions& aOptions) {
3820 // When scrolling to a non-zero offset, we need to determine whether that
3821 // position is within our scrollable range, so we need updated layout
3822 // information which requires a layout flush, otherwise all we need is to
3823 // flush frames to be able to access our scrollable frame here.
3824 FlushType flushType =
3825 ((aOptions.mLeft.WasPassed() && aOptions.mLeft.Value() > 0) ||
3826 (aOptions.mTop.WasPassed() && aOptions.mTop.Value() > 0))
3827 ? FlushType::Layout
3828 : FlushType::Frames;
3829 FlushPendingNotifications(flushType);
3830 nsIScrollableFrame* sf = GetScrollFrame();
3832 if (sf) {
3833 CSSIntPoint scrollPos = sf->GetRoundedScrollPositionCSSPixels();
3834 if (aOptions.mLeft.WasPassed()) {
3835 scrollPos.x = static_cast<int32_t>(
3836 mozilla::ToZeroIfNonfinite(aOptions.mLeft.Value()));
3838 if (aOptions.mTop.WasPassed()) {
3839 scrollPos.y = static_cast<int32_t>(
3840 mozilla::ToZeroIfNonfinite(aOptions.mTop.Value()));
3843 ScrollTo(scrollPos, aOptions);
3847 void nsGlobalWindowInner::Scroll(const ScrollToOptions& aOptions) {
3848 ScrollTo(aOptions);
3851 void nsGlobalWindowInner::ScrollTo(const CSSIntPoint& aScroll,
3852 const ScrollOptions& aOptions) {
3853 // When scrolling to a non-zero offset, we need to determine whether that
3854 // position is within our scrollable range, so we need updated layout
3855 // information which requires a layout flush, otherwise all we need is to
3856 // flush frames to be able to access our scrollable frame here.
3857 FlushType flushType =
3858 (aScroll.x || aScroll.y) ? FlushType::Layout : FlushType::Frames;
3859 FlushPendingNotifications(flushType);
3860 nsIScrollableFrame* sf = GetScrollFrame();
3862 if (sf) {
3863 // Here we calculate what the max pixel value is that we can
3864 // scroll to, we do this by dividing maxint with the pixel to
3865 // twips conversion factor, and subtracting 4, the 4 comes from
3866 // experimenting with this value, anything less makes the view
3867 // code not scroll correctly, I have no idea why. -- jst
3868 const int32_t maxpx = nsPresContext::AppUnitsToIntCSSPixels(0x7fffffff) - 4;
3870 CSSIntPoint scroll(aScroll);
3871 if (scroll.x > maxpx) {
3872 scroll.x = maxpx;
3875 if (scroll.y > maxpx) {
3876 scroll.y = maxpx;
3879 ScrollMode scrollMode = sf->IsSmoothScroll(aOptions.mBehavior)
3880 ? ScrollMode::SmoothMsd
3881 : ScrollMode::Instant;
3883 sf->ScrollToCSSPixels(scroll, scrollMode);
3887 void nsGlobalWindowInner::ScrollBy(double aXScrollDif, double aYScrollDif) {
3888 FlushPendingNotifications(FlushType::Layout);
3889 nsIScrollableFrame* sf = GetScrollFrame();
3891 if (sf) {
3892 // It seems like it would make more sense for ScrollBy to use
3893 // SMOOTH mode, but tests seem to depend on the synchronous behaviour.
3894 // Perhaps Web content does too.
3895 ScrollToOptions options;
3896 options.mLeft.Construct(aXScrollDif);
3897 options.mTop.Construct(aYScrollDif);
3898 ScrollBy(options);
3902 void nsGlobalWindowInner::ScrollBy(const ScrollToOptions& aOptions) {
3903 FlushPendingNotifications(FlushType::Layout);
3904 nsIScrollableFrame* sf = GetScrollFrame();
3906 if (sf) {
3907 CSSIntPoint scrollDelta;
3908 if (aOptions.mLeft.WasPassed()) {
3909 scrollDelta.x = static_cast<int32_t>(
3910 mozilla::ToZeroIfNonfinite(aOptions.mLeft.Value()));
3912 if (aOptions.mTop.WasPassed()) {
3913 scrollDelta.y = static_cast<int32_t>(
3914 mozilla::ToZeroIfNonfinite(aOptions.mTop.Value()));
3917 ScrollMode scrollMode = sf->IsSmoothScroll(aOptions.mBehavior)
3918 ? ScrollMode::SmoothMsd
3919 : ScrollMode::Instant;
3921 sf->ScrollByCSSPixels(scrollDelta, scrollMode);
3925 void nsGlobalWindowInner::ScrollByLines(int32_t numLines,
3926 const ScrollOptions& aOptions) {
3927 FlushPendingNotifications(FlushType::Layout);
3928 nsIScrollableFrame* sf = GetScrollFrame();
3929 if (sf) {
3930 // It seems like it would make more sense for ScrollByLines to use
3931 // SMOOTH mode, but tests seem to depend on the synchronous behaviour.
3932 // Perhaps Web content does too.
3933 ScrollMode scrollMode = sf->IsSmoothScroll(aOptions.mBehavior)
3934 ? ScrollMode::SmoothMsd
3935 : ScrollMode::Instant;
3937 sf->ScrollBy(nsIntPoint(0, numLines), ScrollUnit::LINES, scrollMode);
3941 void nsGlobalWindowInner::ScrollByPages(int32_t numPages,
3942 const ScrollOptions& aOptions) {
3943 FlushPendingNotifications(FlushType::Layout);
3944 nsIScrollableFrame* sf = GetScrollFrame();
3945 if (sf) {
3946 // It seems like it would make more sense for ScrollByPages to use
3947 // SMOOTH mode, but tests seem to depend on the synchronous behaviour.
3948 // Perhaps Web content does too.
3949 ScrollMode scrollMode = sf->IsSmoothScroll(aOptions.mBehavior)
3950 ? ScrollMode::SmoothMsd
3951 : ScrollMode::Instant;
3953 sf->ScrollBy(nsIntPoint(0, numPages), ScrollUnit::PAGES, scrollMode);
3957 void nsGlobalWindowInner::MozScrollSnap() {
3958 FlushPendingNotifications(FlushType::Layout);
3959 nsIScrollableFrame* sf = GetScrollFrame();
3960 if (sf) {
3961 sf->ScrollSnap();
3965 void nsGlobalWindowInner::ClearTimeout(int32_t aHandle) {
3966 DebuggerNotificationDispatch(this, DebuggerNotificationType::ClearTimeout);
3968 if (aHandle > 0) {
3969 mTimeoutManager->ClearTimeout(aHandle, Timeout::Reason::eTimeoutOrInterval);
3973 void nsGlobalWindowInner::ClearInterval(int32_t aHandle) {
3974 DebuggerNotificationDispatch(this, DebuggerNotificationType::ClearInterval);
3976 if (aHandle > 0) {
3977 mTimeoutManager->ClearTimeout(aHandle, Timeout::Reason::eTimeoutOrInterval);
3981 void nsGlobalWindowInner::SetResizable(bool aResizable) const {
3982 // nop
3985 void nsGlobalWindowInner::CaptureEvents() {
3986 if (mDoc) {
3987 mDoc->WarnOnceAbout(DeprecatedOperations::eUseOfCaptureEvents);
3991 void nsGlobalWindowInner::ReleaseEvents() {
3992 if (mDoc) {
3993 mDoc->WarnOnceAbout(DeprecatedOperations::eUseOfReleaseEvents);
3997 Nullable<WindowProxyHolder> nsGlobalWindowInner::Open(const nsAString& aUrl,
3998 const nsAString& aName,
3999 const nsAString& aOptions,
4000 ErrorResult& aError) {
4001 FORWARD_TO_OUTER_OR_THROW(OpenOuter, (aUrl, aName, aOptions, aError), aError,
4002 nullptr);
4005 Nullable<WindowProxyHolder> nsGlobalWindowInner::OpenDialog(
4006 JSContext* aCx, const nsAString& aUrl, const nsAString& aName,
4007 const nsAString& aOptions, const Sequence<JS::Value>& aExtraArgument,
4008 ErrorResult& aError) {
4009 FORWARD_TO_OUTER_OR_THROW(
4010 OpenDialogOuter, (aCx, aUrl, aName, aOptions, aExtraArgument, aError),
4011 aError, nullptr);
4014 WindowProxyHolder nsGlobalWindowInner::GetFrames(ErrorResult& aError) {
4015 FORWARD_TO_OUTER_OR_THROW(GetFramesOuter, (), aError, Window());
4018 void nsGlobalWindowInner::PostMessageMoz(JSContext* aCx,
4019 JS::Handle<JS::Value> aMessage,
4020 const nsAString& aTargetOrigin,
4021 JS::Handle<JS::Value> aTransfer,
4022 nsIPrincipal& aSubjectPrincipal,
4023 ErrorResult& aError) {
4024 FORWARD_TO_OUTER_OR_THROW(
4025 PostMessageMozOuter,
4026 (aCx, aMessage, aTargetOrigin, aTransfer, aSubjectPrincipal, aError),
4027 aError, );
4030 void nsGlobalWindowInner::PostMessageMoz(JSContext* aCx,
4031 JS::Handle<JS::Value> aMessage,
4032 const nsAString& aTargetOrigin,
4033 const Sequence<JSObject*>& aTransfer,
4034 nsIPrincipal& aSubjectPrincipal,
4035 ErrorResult& aRv) {
4036 JS::Rooted<JS::Value> transferArray(aCx, JS::UndefinedValue());
4038 aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransfer,
4039 &transferArray);
4040 if (NS_WARN_IF(aRv.Failed())) {
4041 return;
4044 PostMessageMoz(aCx, aMessage, aTargetOrigin, transferArray, aSubjectPrincipal,
4045 aRv);
4048 void nsGlobalWindowInner::PostMessageMoz(
4049 JSContext* aCx, JS::Handle<JS::Value> aMessage,
4050 const WindowPostMessageOptions& aOptions, nsIPrincipal& aSubjectPrincipal,
4051 ErrorResult& aRv) {
4052 JS::Rooted<JS::Value> transferArray(aCx, JS::UndefinedValue());
4054 aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(
4055 aCx, aOptions.mTransfer, &transferArray);
4056 if (NS_WARN_IF(aRv.Failed())) {
4057 return;
4060 PostMessageMoz(aCx, aMessage, aOptions.mTargetOrigin, transferArray,
4061 aSubjectPrincipal, aRv);
4064 void nsGlobalWindowInner::Close(CallerType aCallerType, ErrorResult& aError) {
4065 FORWARD_TO_OUTER_OR_THROW(CloseOuter, (aCallerType == CallerType::System),
4066 aError, );
4069 nsresult nsGlobalWindowInner::Close() {
4070 FORWARD_TO_OUTER(Close, (), NS_ERROR_UNEXPECTED);
4073 bool nsGlobalWindowInner::IsInModalState() {
4074 FORWARD_TO_OUTER(IsInModalState, (), false);
4077 // static
4078 void nsGlobalWindowInner::NotifyDOMWindowDestroyed(
4079 nsGlobalWindowInner* aWindow) {
4080 nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
4081 if (observerService) {
4082 observerService->NotifyObservers(ToSupports(aWindow),
4083 DOM_WINDOW_DESTROYED_TOPIC, nullptr);
4087 void nsGlobalWindowInner::NotifyWindowIDDestroyed(const char* aTopic) {
4088 nsCOMPtr<nsIRunnable> runnable =
4089 new WindowDestroyedEvent(this, mWindowID, aTopic);
4090 Dispatch(runnable.forget());
4093 // static
4094 void nsGlobalWindowInner::NotifyDOMWindowFrozen(nsGlobalWindowInner* aWindow) {
4095 if (aWindow) {
4096 nsCOMPtr<nsIObserverService> observerService =
4097 services::GetObserverService();
4098 if (observerService) {
4099 observerService->NotifyObservers(ToSupports(aWindow),
4100 DOM_WINDOW_FROZEN_TOPIC, nullptr);
4105 // static
4106 void nsGlobalWindowInner::NotifyDOMWindowThawed(nsGlobalWindowInner* aWindow) {
4107 if (aWindow) {
4108 nsCOMPtr<nsIObserverService> observerService =
4109 services::GetObserverService();
4110 if (observerService) {
4111 observerService->NotifyObservers(ToSupports(aWindow),
4112 DOM_WINDOW_THAWED_TOPIC, nullptr);
4117 Element* nsGlobalWindowInner::GetFrameElement(nsIPrincipal& aSubjectPrincipal,
4118 ErrorResult& aError) {
4119 FORWARD_TO_OUTER_OR_THROW(GetFrameElement, (aSubjectPrincipal), aError,
4120 nullptr);
4123 Element* nsGlobalWindowInner::GetRealFrameElement(ErrorResult& aError) {
4124 FORWARD_TO_OUTER_OR_THROW(GetFrameElement, (), aError, nullptr);
4127 void nsGlobalWindowInner::UpdateCommands(const nsAString& anAction) {
4128 if (GetOuterWindowInternal()) {
4129 GetOuterWindowInternal()->UpdateCommands(anAction);
4133 Selection* nsGlobalWindowInner::GetSelection(ErrorResult& aError) {
4134 FORWARD_TO_OUTER_OR_THROW(GetSelectionOuter, (), aError, nullptr);
4137 WebTaskScheduler* nsGlobalWindowInner::Scheduler() {
4138 if (!mWebTaskScheduler) {
4139 mWebTaskScheduler = WebTaskScheduler::CreateForMainThread(this);
4141 MOZ_ASSERT(mWebTaskScheduler);
4142 return mWebTaskScheduler;
4145 bool nsGlobalWindowInner::Find(const nsAString& aString, bool aCaseSensitive,
4146 bool aBackwards, bool aWrapAround,
4147 bool aWholeWord, bool aSearchInFrames,
4148 bool aShowDialog, ErrorResult& aError) {
4149 FORWARD_TO_OUTER_OR_THROW(FindOuter,
4150 (aString, aCaseSensitive, aBackwards, aWrapAround,
4151 aWholeWord, aSearchInFrames, aShowDialog, aError),
4152 aError, false);
4155 void nsGlobalWindowInner::GetOrigin(nsAString& aOrigin) {
4156 nsContentUtils::GetWebExposedOriginSerialization(GetPrincipal(), aOrigin);
4159 // See also AutoJSAPI::ReportException
4160 void nsGlobalWindowInner::ReportError(JSContext* aCx,
4161 JS::Handle<JS::Value> aError,
4162 CallerType aCallerType,
4163 ErrorResult& aRv) {
4164 if (MOZ_UNLIKELY(!HasActiveDocument())) {
4165 return aRv.Throw(NS_ERROR_XPC_SECURITY_MANAGER_VETO);
4168 JS::ErrorReportBuilder jsReport(aCx);
4169 JS::ExceptionStack exnStack(aCx, aError, nullptr);
4170 if (!jsReport.init(aCx, exnStack, JS::ErrorReportBuilder::NoSideEffects)) {
4171 return aRv.NoteJSContextException(aCx);
4174 RefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport();
4175 bool isChrome = aCallerType == CallerType::System;
4176 xpcReport->Init(jsReport.report(), jsReport.toStringResult().c_str(),
4177 isChrome, WindowID());
4179 JS::RootingContext* rcx = JS::RootingContext::get(aCx);
4180 DispatchScriptErrorEvent(this, rcx, xpcReport, exnStack.exception(),
4181 exnStack.stack());
4184 void nsGlobalWindowInner::Atob(const nsAString& aAsciiBase64String,
4185 nsAString& aBinaryData, ErrorResult& aError) {
4186 aError = nsContentUtils::Atob(aAsciiBase64String, aBinaryData);
4189 void nsGlobalWindowInner::Btoa(const nsAString& aBinaryData,
4190 nsAString& aAsciiBase64String,
4191 ErrorResult& aError) {
4192 aError = nsContentUtils::Btoa(aBinaryData, aAsciiBase64String);
4195 //*****************************************************************************
4196 // EventTarget
4197 //*****************************************************************************
4199 nsPIDOMWindowOuter* nsGlobalWindowInner::GetOwnerGlobalForBindingsInternal() {
4200 return nsPIDOMWindowOuter::GetFromCurrentInner(this);
4203 bool nsGlobalWindowInner::DispatchEvent(Event& aEvent, CallerType aCallerType,
4204 ErrorResult& aRv) {
4205 if (!IsCurrentInnerWindow()) {
4206 NS_WARNING(
4207 "DispatchEvent called on non-current inner window, dropping. "
4208 "Please check the window in the caller instead.");
4209 aRv.Throw(NS_ERROR_FAILURE);
4210 return false;
4213 if (!mDoc) {
4214 aRv.Throw(NS_ERROR_FAILURE);
4215 return false;
4218 // Obtain a presentation shell
4219 RefPtr<nsPresContext> presContext = mDoc->GetPresContext();
4221 nsEventStatus status = nsEventStatus_eIgnore;
4222 nsresult rv = EventDispatcher::DispatchDOMEvent(this, nullptr, &aEvent,
4223 presContext, &status);
4224 bool retval = !aEvent.DefaultPrevented(aCallerType);
4225 if (NS_FAILED(rv)) {
4226 aRv.Throw(rv);
4228 return retval;
4231 mozilla::Maybe<mozilla::dom::EventCallbackDebuggerNotificationType>
4232 nsGlobalWindowInner::GetDebuggerNotificationType() const {
4233 return mozilla::Some(
4234 mozilla::dom::EventCallbackDebuggerNotificationType::Global);
4237 bool nsGlobalWindowInner::ComputeDefaultWantsUntrusted(ErrorResult& aRv) {
4238 return !nsContentUtils::IsChromeDoc(mDoc);
4241 EventListenerManager* nsGlobalWindowInner::GetOrCreateListenerManager() {
4242 if (!mListenerManager) {
4243 mListenerManager =
4244 new EventListenerManager(static_cast<EventTarget*>(this));
4247 return mListenerManager;
4250 EventListenerManager* nsGlobalWindowInner::GetExistingListenerManager() const {
4251 return mListenerManager;
4254 mozilla::dom::DebuggerNotificationManager*
4255 nsGlobalWindowInner::GetOrCreateDebuggerNotificationManager() {
4256 if (!mDebuggerNotificationManager) {
4257 mDebuggerNotificationManager = new DebuggerNotificationManager(this);
4260 return mDebuggerNotificationManager;
4263 mozilla::dom::DebuggerNotificationManager*
4264 nsGlobalWindowInner::GetExistingDebuggerNotificationManager() {
4265 return mDebuggerNotificationManager;
4268 //*****************************************************************************
4269 // nsGlobalWindowInner::nsPIDOMWindow
4270 //*****************************************************************************
4272 Location* nsGlobalWindowInner::Location() {
4273 if (!mLocation) {
4274 mLocation = new dom::Location(this);
4277 return mLocation;
4280 void nsGlobalWindowInner::MaybeUpdateTouchState() {
4281 if (mMayHaveTouchEventListener) {
4282 nsCOMPtr<nsIObserverService> observerService =
4283 services::GetObserverService();
4285 if (observerService) {
4286 observerService->NotifyObservers(static_cast<nsIDOMWindow*>(this),
4287 DOM_TOUCH_LISTENER_ADDED, nullptr);
4292 void nsGlobalWindowInner::EnableGamepadUpdates() {
4293 if (mHasGamepad) {
4294 RefPtr<GamepadManager> gamepadManager(GamepadManager::GetService());
4295 if (gamepadManager) {
4296 gamepadManager->AddListener(this);
4301 void nsGlobalWindowInner::DisableGamepadUpdates() {
4302 if (mHasGamepad) {
4303 RefPtr<GamepadManager> gamepadManager(GamepadManager::GetService());
4304 if (gamepadManager) {
4305 gamepadManager->RemoveListener(this);
4310 void nsGlobalWindowInner::EnableVRUpdates() {
4311 // We need to create a VREventObserver before we can either detect XR runtimes
4312 // or start an XR session
4313 if (!mVREventObserver && (mHasXRSession || mXRRuntimeDetectionInFlight)) {
4314 // Assert that we are not creating the observer while IsDying() as
4315 // that would result in a leak. VREventObserver holds a RefPtr to
4316 // this nsGlobalWindowInner and would prevent it from being deallocated.
4317 MOZ_ASSERT(!IsDying(),
4318 "Creating a VREventObserver for an nsGlobalWindow that is "
4319 "dying would cause it to leak.");
4320 mVREventObserver = new VREventObserver(this);
4322 // If the content has an XR session, then we need to tell
4323 // VREventObserver that there is VR activity.
4324 if (mHasXRSession) {
4325 nsPIDOMWindowOuter* outer = GetOuterWindow();
4326 if (outer && !outer->IsBackground()) {
4327 StartVRActivity();
4332 void nsGlobalWindowInner::DisableVRUpdates() {
4333 if (mVREventObserver) {
4334 mVREventObserver->DisconnectFromOwner();
4335 mVREventObserver = nullptr;
4339 void nsGlobalWindowInner::ResetVRTelemetry(bool aUpdate) {
4340 if (mVREventObserver) {
4341 mVREventObserver->UpdateSpentTimeIn2DTelemetry(aUpdate);
4345 void nsGlobalWindowInner::StartVRActivity() {
4347 * If the content has an XR session, tell
4348 * the VREventObserver that the window is accessing
4349 * VR devices.
4351 * It's possible to have a VREventObserver without
4352 * and XR session, if we are using it to get updates
4353 * about XR runtime enumeration. In this case,
4354 * we would not tell the VREventObserver that
4355 * we are accessing VR devices.
4357 if (mVREventObserver && mHasXRSession) {
4358 mVREventObserver->StartActivity();
4362 void nsGlobalWindowInner::StopVRActivity() {
4364 * If the content has an XR session, tell
4365 * the VReventObserver that the window is no longer
4366 * accessing VR devices. This does not stop the
4367 * XR session itself, which may be resumed with
4368 * EnableVRUpdates.
4369 * It's possible to have a VREventObserver without
4370 * and XR session, if we are using it to get updates
4371 * about XR runtime enumeration. In this case,
4372 * we would not tell the VREventObserver that
4373 * we ending an activity that accesses VR devices.
4375 if (mVREventObserver && mHasXRSession) {
4376 mVREventObserver->StopActivity();
4380 void nsGlobalWindowInner::SetFocusedElement(Element* aElement,
4381 uint32_t aFocusMethod,
4382 bool aNeedsFocus) {
4383 if (aElement && aElement->GetComposedDoc() != mDoc) {
4384 NS_WARNING("Trying to set focus to a node from a wrong document");
4385 return;
4388 if (IsDying()) {
4389 NS_ASSERTION(!aElement, "Trying to focus cleaned up window!");
4390 aElement = nullptr;
4391 aNeedsFocus = false;
4393 if (mFocusedElement != aElement) {
4394 UpdateCanvasFocus(false, aElement);
4395 mFocusedElement = aElement;
4396 // TODO: Maybe this should be set on refocus too?
4397 mFocusMethod = aFocusMethod & nsIFocusManager::METHOD_MASK;
4400 if (mFocusedElement) {
4401 // if a node was focused by a keypress, turn on focus rings for the
4402 // window.
4403 if (mFocusMethod & nsIFocusManager::FLAG_BYKEY) {
4404 mUnknownFocusMethodShouldShowOutline = true;
4405 mFocusByKeyOccurred = true;
4406 } else if (nsFocusManager::GetFocusMoveActionCause(mFocusMethod) !=
4407 widget::InputContextAction::CAUSE_UNKNOWN) {
4408 mUnknownFocusMethodShouldShowOutline = false;
4409 } else if (aFocusMethod & nsIFocusManager::FLAG_NOSHOWRING) {
4410 // If we get focused via script, and script has explicitly opted out of
4411 // outlines via FLAG_NOSHOWRING, we don't want to make a refocus start
4412 // showing outlines.
4413 mUnknownFocusMethodShouldShowOutline = false;
4417 if (aNeedsFocus) {
4418 mNeedsFocus = aNeedsFocus;
4422 uint32_t nsGlobalWindowInner::GetFocusMethod() { return mFocusMethod; }
4424 bool nsGlobalWindowInner::ShouldShowFocusRing() {
4425 if (mFocusByKeyOccurred &&
4426 StaticPrefs::browser_display_always_show_rings_after_key_focus()) {
4427 return true;
4429 return StaticPrefs::browser_display_show_focus_rings();
4432 bool nsGlobalWindowInner::TakeFocus(bool aFocus, uint32_t aFocusMethod) {
4433 if (IsDying()) {
4434 return false;
4437 if (aFocus) {
4438 mFocusMethod = aFocusMethod & nsIFocusManager::METHOD_MASK;
4441 if (mHasFocus != aFocus) {
4442 mHasFocus = aFocus;
4443 UpdateCanvasFocus(true, mFocusedElement);
4446 // if mNeedsFocus is true, then the document has not yet received a
4447 // document-level focus event. If there is a root content node, then return
4448 // true to tell the calling focus manager that a focus event is expected. If
4449 // there is no root content node, the document hasn't loaded enough yet, or
4450 // there isn't one and there is no point in firing a focus event.
4451 if (aFocus && mNeedsFocus && mDoc && mDoc->GetRootElement() != nullptr) {
4452 mNeedsFocus = false;
4453 return true;
4456 mNeedsFocus = false;
4457 return false;
4460 void nsGlobalWindowInner::SetReadyForFocus() {
4461 bool oldNeedsFocus = mNeedsFocus;
4462 mNeedsFocus = false;
4464 if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
4465 nsCOMPtr<nsPIDOMWindowOuter> outerWindow = GetOuterWindow();
4466 fm->WindowShown(outerWindow, oldNeedsFocus);
4470 void nsGlobalWindowInner::PageHidden() {
4471 // the window is being hidden, so tell the focus manager that the frame is
4472 // no longer valid. Use the persisted field to determine if the document
4473 // is being destroyed.
4475 if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
4476 nsCOMPtr<nsPIDOMWindowOuter> outerWindow = GetOuterWindow();
4477 fm->WindowHidden(outerWindow, nsFocusManager::GenerateFocusActionId());
4480 mNeedsFocus = true;
4483 class HashchangeCallback : public Runnable {
4484 public:
4485 HashchangeCallback(const nsAString& aOldURL, const nsAString& aNewURL,
4486 nsGlobalWindowInner* aWindow)
4487 : mozilla::Runnable("HashchangeCallback"), mWindow(aWindow) {
4488 MOZ_ASSERT(mWindow);
4489 mOldURL.Assign(aOldURL);
4490 mNewURL.Assign(aNewURL);
4493 NS_IMETHOD Run() override {
4494 MOZ_ASSERT(NS_IsMainThread(), "Should be called on the main thread.");
4495 return mWindow->FireHashchange(mOldURL, mNewURL);
4498 private:
4499 nsString mOldURL;
4500 nsString mNewURL;
4501 RefPtr<nsGlobalWindowInner> mWindow;
4504 nsresult nsGlobalWindowInner::DispatchAsyncHashchange(nsIURI* aOldURI,
4505 nsIURI* aNewURI) {
4506 // Make sure that aOldURI and aNewURI are identical up to the '#', and that
4507 // their hashes are different.
4508 bool equal = false;
4509 NS_ENSURE_STATE(NS_SUCCEEDED(aOldURI->EqualsExceptRef(aNewURI, &equal)) &&
4510 equal);
4511 nsAutoCString oldHash, newHash;
4512 bool oldHasHash, newHasHash;
4513 NS_ENSURE_STATE(NS_SUCCEEDED(aOldURI->GetRef(oldHash)) &&
4514 NS_SUCCEEDED(aNewURI->GetRef(newHash)) &&
4515 NS_SUCCEEDED(aOldURI->GetHasRef(&oldHasHash)) &&
4516 NS_SUCCEEDED(aNewURI->GetHasRef(&newHasHash)) &&
4517 (oldHasHash != newHasHash || !oldHash.Equals(newHash)));
4519 nsAutoCString oldSpec, newSpec;
4520 nsresult rv = aOldURI->GetSpec(oldSpec);
4521 NS_ENSURE_SUCCESS(rv, rv);
4522 rv = aNewURI->GetSpec(newSpec);
4523 NS_ENSURE_SUCCESS(rv, rv);
4525 NS_ConvertUTF8toUTF16 oldWideSpec(oldSpec);
4526 NS_ConvertUTF8toUTF16 newWideSpec(newSpec);
4528 nsCOMPtr<nsIRunnable> callback =
4529 new HashchangeCallback(oldWideSpec, newWideSpec, this);
4530 return Dispatch(callback.forget());
4533 nsresult nsGlobalWindowInner::FireHashchange(const nsAString& aOldURL,
4534 const nsAString& aNewURL) {
4535 // Don't do anything if the window is frozen.
4536 if (IsFrozen()) {
4537 return NS_OK;
4540 // Get a presentation shell for use in creating the hashchange event.
4541 NS_ENSURE_STATE(IsCurrentInnerWindow());
4543 HashChangeEventInit init;
4544 init.mNewURL = aNewURL;
4545 init.mOldURL = aOldURL;
4547 RefPtr<HashChangeEvent> event =
4548 HashChangeEvent::Constructor(this, u"hashchange"_ns, init);
4550 event->SetTrusted(true);
4552 ErrorResult rv;
4553 DispatchEvent(*event, rv);
4554 return rv.StealNSResult();
4557 nsresult nsGlobalWindowInner::DispatchSyncPopState() {
4558 NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
4559 "Must be safe to run script here.");
4561 // Bail if the window is frozen.
4562 if (IsFrozen()) {
4563 return NS_OK;
4566 AutoJSAPI jsapi;
4567 bool result = jsapi.Init(this);
4568 NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
4570 JSContext* cx = jsapi.cx();
4572 // Get the document's pending state object -- it contains the data we're
4573 // going to send along with the popstate event. The object is serialized
4574 // using structured clone.
4575 JS::Rooted<JS::Value> stateJSValue(cx);
4576 nsresult rv = mDoc->GetStateObject(&stateJSValue);
4577 NS_ENSURE_SUCCESS(rv, rv);
4579 if (!JS_WrapValue(cx, &stateJSValue)) {
4580 return NS_ERROR_OUT_OF_MEMORY;
4583 RootedDictionary<PopStateEventInit> init(cx);
4584 init.mState = stateJSValue;
4586 RefPtr<PopStateEvent> event =
4587 PopStateEvent::Constructor(this, u"popstate"_ns, init);
4588 event->SetTrusted(true);
4589 event->SetTarget(this);
4591 ErrorResult err;
4592 DispatchEvent(*event, err);
4593 return err.StealNSResult();
4596 //-------------------------------------------------------
4597 // Tells the HTMLFrame/CanvasFrame that is now has focus
4598 void nsGlobalWindowInner::UpdateCanvasFocus(bool aFocusChanged,
4599 nsIContent* aNewContent) {
4600 // this is called from the inner window so use GetDocShell
4601 nsIDocShell* docShell = GetDocShell();
4602 if (!docShell) return;
4604 bool editable;
4605 docShell->GetEditable(&editable);
4606 if (editable) return;
4608 PresShell* presShell = docShell->GetPresShell();
4609 if (!presShell || !mDoc) {
4610 return;
4613 Element* rootElement = mDoc->GetRootElement();
4614 if (rootElement) {
4615 if ((mHasFocus || aFocusChanged) &&
4616 (mFocusedElement == rootElement || aNewContent == rootElement)) {
4617 nsCanvasFrame* canvasFrame = presShell->GetCanvasFrame();
4618 if (canvasFrame) {
4619 canvasFrame->SetHasFocus(mHasFocus && rootElement == aNewContent);
4622 } else {
4623 // XXXbz I would expect that there is never a canvasFrame in this case...
4624 nsCanvasFrame* canvasFrame = presShell->GetCanvasFrame();
4625 if (canvasFrame) {
4626 canvasFrame->SetHasFocus(false);
4631 already_AddRefed<nsICSSDeclaration> nsGlobalWindowInner::GetComputedStyle(
4632 Element& aElt, const nsAString& aPseudoElt, ErrorResult& aError) {
4633 return GetComputedStyleHelper(aElt, aPseudoElt, false, aError);
4636 already_AddRefed<nsICSSDeclaration>
4637 nsGlobalWindowInner::GetDefaultComputedStyle(Element& aElt,
4638 const nsAString& aPseudoElt,
4639 ErrorResult& aError) {
4640 return GetComputedStyleHelper(aElt, aPseudoElt, true, aError);
4643 already_AddRefed<nsICSSDeclaration> nsGlobalWindowInner::GetComputedStyleHelper(
4644 Element& aElt, const nsAString& aPseudoElt, bool aDefaultStylesOnly,
4645 ErrorResult& aError) {
4646 FORWARD_TO_OUTER_OR_THROW(GetComputedStyleHelperOuter,
4647 (aElt, aPseudoElt, aDefaultStylesOnly, aError),
4648 aError, nullptr);
4651 Storage* nsGlobalWindowInner::GetSessionStorage(ErrorResult& aError) {
4652 nsIPrincipal* principal = GetPrincipal();
4653 nsIPrincipal* storagePrincipal;
4654 if (StaticPrefs::
4655 privacy_partition_always_partition_third_party_non_cookie_storage_exempt_sessionstorage()) {
4656 storagePrincipal = GetEffectiveCookiePrincipal();
4657 } else {
4658 storagePrincipal = GetEffectiveStoragePrincipal();
4660 BrowsingContext* browsingContext = GetBrowsingContext();
4662 if (!principal || !storagePrincipal || !browsingContext ||
4663 !Storage::StoragePrefIsEnabled()) {
4664 return nullptr;
4667 if (mSessionStorage) {
4668 MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
4669 ("nsGlobalWindowInner %p has %p sessionStorage", this,
4670 mSessionStorage.get()));
4671 bool canAccess =
4672 principal->Subsumes(mSessionStorage->Principal()) &&
4673 storagePrincipal->Subsumes(mSessionStorage->StoragePrincipal());
4674 if (!canAccess) {
4675 mSessionStorage = nullptr;
4679 if (!mSessionStorage) {
4680 nsString documentURI;
4681 if (mDoc) {
4682 aError = mDoc->GetDocumentURI(documentURI);
4683 if (NS_WARN_IF(aError.Failed())) {
4684 return nullptr;
4688 if (!mDoc) {
4689 aError.Throw(NS_ERROR_FAILURE);
4690 return nullptr;
4693 // If the document's sandboxed origin flag is set, then accessing
4694 // sessionStorage is prohibited.
4695 if (mDoc->GetSandboxFlags() & SANDBOXED_ORIGIN) {
4696 aError.ThrowSecurityError(
4697 "Forbidden in a sandboxed document without the 'allow-same-origin' "
4698 "flag.");
4699 return nullptr;
4702 uint32_t rejectedReason = 0;
4703 StorageAccess access = StorageAllowedForWindow(this, &rejectedReason);
4705 // SessionStorage is an ephemeral per-tab per-origin storage that only lives
4706 // as long as the tab is open, although it may survive browser restarts
4707 // thanks to the session store. So we interpret storage access differently
4708 // than we would for persistent per-origin storage like LocalStorage and so
4709 // it may be okay to provide SessionStorage even when we receive a value of
4710 // eDeny.
4712 // ContentBlocking::ShouldAllowAccessFor will return false for 3 main
4713 // reasons.
4715 // 1. Cookies are entirely blocked due to a per-origin permission
4716 // (nsICookiePermission::ACCESS_DENY for the top-level principal or this
4717 // window's principal) or the very broad BEHAVIOR_REJECT. This will return
4718 // eDeny with a reason of STATE_COOKIES_BLOCKED_BY_PERMISSION or
4719 // STATE_COOKIES_BLOCKED_ALL.
4721 // 2. Third-party cookies are limited via BEHAVIOR_REJECT_FOREIGN and
4722 // BEHAVIOR_LIMIT_FOREIGN and this is a third-party window. This will return
4723 // eDeny with a reason of STATE_COOKIES_BLOCKED_FOREIGN.
4725 // 3. Tracking protection (BEHAVIOR_REJECT_TRACKER and
4726 // BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN) is in effect and
4727 // IsThirdPartyTrackingResourceWindow() returned true and there wasn't a
4728 // permission that allows it. This will return ePartitionTrackersOrDeny with
4729 // a reason of STATE_COOKIES_BLOCKED_TRACKER or
4730 // STATE_COOKIES_BLOCKED_SOCIALTRACKER.
4732 // In the 1st case, the user has explicitly indicated that they don't want
4733 // to allow any storage to the origin or all origins and so we throw an
4734 // error and deny access to SessionStorage. In the 2nd case, a legacy
4735 // decision reasoned that there's no harm in providing SessionStorage
4736 // because the information is not durable and cannot escape the current tab.
4737 // The rationale is similar for the 3rd case.
4738 if (access == StorageAccess::eDeny &&
4739 rejectedReason !=
4740 nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN) {
4741 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
4742 return nullptr;
4745 const RefPtr<SessionStorageManager> storageManager =
4746 browsingContext->GetSessionStorageManager();
4747 if (!storageManager) {
4748 aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
4749 return nullptr;
4752 RefPtr<Storage> storage;
4753 aError = storageManager->CreateStorage(this, principal, storagePrincipal,
4754 documentURI, IsPrivateBrowsing(),
4755 getter_AddRefs(storage));
4756 if (aError.Failed()) {
4757 return nullptr;
4760 mSessionStorage = storage;
4761 MOZ_ASSERT(mSessionStorage);
4763 MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
4764 ("nsGlobalWindowInner %p tried to get a new sessionStorage %p",
4765 this, mSessionStorage.get()));
4767 if (!mSessionStorage) {
4768 aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
4769 return nullptr;
4773 MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
4774 ("nsGlobalWindowInner %p returns %p sessionStorage", this,
4775 mSessionStorage.get()));
4777 return mSessionStorage;
4780 Storage* nsGlobalWindowInner::GetLocalStorage(ErrorResult& aError) {
4781 if (!Storage::StoragePrefIsEnabled()) {
4782 return nullptr;
4785 // If the document's sandboxed origin flag is set, then accessing localStorage
4786 // is prohibited.
4787 if (mDoc && mDoc->GetSandboxFlags() & SANDBOXED_ORIGIN) {
4788 aError.ThrowSecurityError(
4789 "Forbidden in a sandboxed document without the 'allow-same-origin' "
4790 "flag.");
4791 return nullptr;
4794 // LocalStorage needs to be exposed in every context except for sandboxes and
4795 // NullPrincipals (data: URLs, for instance). But we need to keep data
4796 // separate in some scenarios: private-browsing and partitioned trackers.
4797 // In private-browsing, LocalStorage keeps data in memory, and it shares
4798 // StorageEvents just with other origins in the same private-browsing
4799 // environment.
4800 // For Partitioned Trackers, we expose a partitioned LocalStorage, which
4801 // doesn't share data with other contexts, and it's just in memory.
4802 // Partitioned localStorage is available only for trackers listed in the
4803 // privacy.restrict3rdpartystorage.partitionedHosts pref. See
4804 // nsContentUtils::IsURIInPrefList to know the syntax for the pref value.
4805 // This is a temporary web-compatibility hack.
4807 StorageAccess access = StorageAllowedForWindow(this);
4809 // We allow partitioned localStorage only to some hosts.
4810 bool isolated = false;
4811 if (ShouldPartitionStorage(access)) {
4812 if (!mDoc) {
4813 access = StorageAccess::eDeny;
4814 } else if (!StoragePartitioningEnabled(access, mDoc->CookieJarSettings())) {
4815 static const char* kPrefName =
4816 "privacy.restrict3rdpartystorage.partitionedHosts";
4818 bool isInList = false;
4819 mDoc->NodePrincipal()->IsURIInPrefList(kPrefName, &isInList);
4820 if (!isInList) {
4821 access = StorageAccess::eDeny;
4822 } else {
4823 isolated = true;
4828 if (access == StorageAccess::eDeny) {
4829 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
4830 return nullptr;
4833 nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
4834 if (mDoc) {
4835 cookieJarSettings = mDoc->CookieJarSettings();
4836 } else {
4837 cookieJarSettings = net::CookieJarSettings::GetBlockingAll(
4838 ShouldResistFingerprinting(RFPTarget::IsAlwaysEnabledForPrecompute));
4841 // Note that this behavior is observable: if we grant storage permission to a
4842 // tracker, we pass from the partitioned LocalStorage (or a partitioned cookie
4843 // jar) to the 'normal' one. The previous data is lost and the 2
4844 // window.localStorage objects, before and after the permission granted, will
4845 // be different.
4846 if (mLocalStorage) {
4847 if ((mLocalStorage->Type() == (isolated ? Storage::ePartitionedLocalStorage
4848 : Storage::eLocalStorage)) &&
4849 (mLocalStorage->StoragePrincipal() == GetEffectiveStoragePrincipal())) {
4850 return mLocalStorage;
4853 // storage needs change
4854 mLocalStorage = nullptr;
4857 MOZ_ASSERT(!mLocalStorage);
4859 if (!isolated) {
4860 RefPtr<Storage> storage;
4862 if (NextGenLocalStorageEnabled()) {
4863 aError = LSObject::CreateForWindow(this, getter_AddRefs(storage));
4864 } else {
4865 nsresult rv;
4866 nsCOMPtr<nsIDOMStorageManager> storageManager =
4867 do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv);
4868 if (NS_FAILED(rv)) {
4869 aError.Throw(rv);
4870 return nullptr;
4873 nsString documentURI;
4874 if (mDoc) {
4875 aError = mDoc->GetDocumentURI(documentURI);
4876 if (NS_WARN_IF(aError.Failed())) {
4877 return nullptr;
4881 nsIPrincipal* principal = GetPrincipal();
4882 if (!principal) {
4883 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
4884 return nullptr;
4887 nsIPrincipal* storagePrincipal = GetEffectiveStoragePrincipal();
4888 if (!storagePrincipal) {
4889 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
4890 return nullptr;
4893 aError = storageManager->CreateStorage(this, principal, storagePrincipal,
4894 documentURI, IsPrivateBrowsing(),
4895 getter_AddRefs(storage));
4898 if (aError.Failed()) {
4899 return nullptr;
4902 mLocalStorage = storage;
4903 } else {
4904 nsresult rv;
4905 nsCOMPtr<nsIDOMSessionStorageManager> storageManager =
4906 do_GetService("@mozilla.org/dom/sessionStorage-manager;1", &rv);
4907 if (NS_FAILED(rv)) {
4908 aError.Throw(rv);
4909 return nullptr;
4912 nsIPrincipal* principal = GetPrincipal();
4913 if (!principal) {
4914 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
4915 return nullptr;
4918 nsIPrincipal* storagePrincipal = GetEffectiveStoragePrincipal();
4919 if (!storagePrincipal) {
4920 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
4921 return nullptr;
4924 RefPtr<SessionStorageCache> cache;
4925 if (isolated) {
4926 cache = new SessionStorageCache();
4927 } else {
4928 // This will clone the session storage if it exists.
4929 rv = storageManager->GetSessionStorageCache(principal, storagePrincipal,
4930 &cache);
4931 if (NS_FAILED(rv)) {
4932 aError.Throw(rv);
4933 return nullptr;
4937 mLocalStorage =
4938 new PartitionedLocalStorage(this, principal, storagePrincipal, cache);
4941 MOZ_ASSERT(mLocalStorage);
4942 MOZ_ASSERT(
4943 mLocalStorage->Type() ==
4944 (isolated ? Storage::ePartitionedLocalStorage : Storage::eLocalStorage));
4945 return mLocalStorage;
4948 IDBFactory* nsGlobalWindowInner::GetIndexedDB(JSContext* aCx,
4949 ErrorResult& aError) {
4950 if (!mIndexedDB) {
4951 // This may keep mIndexedDB null without setting an error.
4952 auto res = IDBFactory::CreateForWindow(this);
4953 if (res.isErr()) {
4954 aError = res.unwrapErr();
4955 } else {
4956 mIndexedDB = res.unwrap();
4960 return mIndexedDB;
4963 //*****************************************************************************
4964 // nsGlobalWindowInner::nsIInterfaceRequestor
4965 //*****************************************************************************
4967 NS_IMETHODIMP
4968 nsGlobalWindowInner::GetInterface(const nsIID& aIID, void** aSink) {
4969 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
4970 NS_ENSURE_TRUE(outer, NS_ERROR_NOT_INITIALIZED);
4972 nsresult rv = outer->GetInterfaceInternal(aIID, aSink);
4973 if (rv == NS_ERROR_NO_INTERFACE) {
4974 return QueryInterface(aIID, aSink);
4976 return rv;
4979 void nsGlobalWindowInner::GetInterface(JSContext* aCx,
4980 JS::Handle<JS::Value> aIID,
4981 JS::MutableHandle<JS::Value> aRetval,
4982 ErrorResult& aError) {
4983 dom::GetInterface(aCx, this, aIID, aRetval, aError);
4986 already_AddRefed<CacheStorage> nsGlobalWindowInner::GetCaches(
4987 ErrorResult& aRv) {
4988 if (!mCacheStorage) {
4989 bool forceTrustedOrigin =
4990 GetBrowsingContext() &&
4991 GetBrowsingContext()->Top()->GetServiceWorkersTestingEnabled();
4992 mCacheStorage = CacheStorage::CreateOnMainThread(
4993 cache::DEFAULT_NAMESPACE, this, GetEffectiveStoragePrincipal(),
4994 forceTrustedOrigin, aRv);
4997 RefPtr<CacheStorage> ref = mCacheStorage;
4998 return ref.forget();
5001 void nsGlobalWindowInner::FireOfflineStatusEventIfChanged() {
5002 if (!IsCurrentInnerWindow()) return;
5004 // Don't fire an event if the status hasn't changed
5005 if (mWasOffline == NS_IsOffline()) {
5006 return;
5009 mWasOffline = !mWasOffline;
5011 nsAutoString name;
5012 if (mWasOffline) {
5013 name.AssignLiteral("offline");
5014 } else {
5015 name.AssignLiteral("online");
5017 nsContentUtils::DispatchTrustedEvent(mDoc, this, name, CanBubble::eNo,
5018 Cancelable::eNo);
5021 nsGlobalWindowInner::SlowScriptResponse
5022 nsGlobalWindowInner::ShowSlowScriptDialog(JSContext* aCx,
5023 const nsString& aAddonId,
5024 const double aDuration) {
5025 nsresult rv;
5027 if (Preferences::GetBool("dom.always_stop_slow_scripts")) {
5028 return KillSlowScript;
5031 // If it isn't safe to run script, then it isn't safe to bring up the prompt
5032 // (since that spins the event loop). In that (rare) case, we just kill the
5033 // script and report a warning.
5034 if (!nsContentUtils::IsSafeToRunScript()) {
5035 JS::WarnASCII(aCx, "A long running script was terminated");
5036 return KillSlowScript;
5039 // If our document is not active, just kill the script: we've been unloaded
5040 if (!HasActiveDocument()) {
5041 return KillSlowScript;
5044 // Check if we should offer the option to debug
5045 JS::AutoFilename filename;
5046 uint32_t lineno;
5047 // Computing the line number can be very expensive (see bug 1330231 for
5048 // example), and we don't use the line number anywhere except than in the
5049 // parent process, so we avoid computing it elsewhere. This gives us most of
5050 // the wins we are interested in, since the source of the slowness here is
5051 // minified scripts which is more common in Web content that is loaded in the
5052 // content process.
5053 uint32_t* linenop = XRE_IsParentProcess() ? &lineno : nullptr;
5054 bool hasFrame = JS::DescribeScriptedCaller(aCx, &filename, linenop);
5056 // Record the slow script event if we haven't done so already for this inner
5057 // window (which represents a particular page to the user).
5058 if (!mHasHadSlowScript) {
5059 Telemetry::Accumulate(Telemetry::SLOW_SCRIPT_PAGE_COUNT, 1);
5061 mHasHadSlowScript = true;
5063 // Override the cursor to something that we're sure the user can see.
5064 SetCursor("auto"_ns, IgnoreErrors());
5066 if (XRE_IsContentProcess() && ProcessHangMonitor::Get()) {
5067 ProcessHangMonitor::SlowScriptAction action;
5068 RefPtr<ProcessHangMonitor> monitor = ProcessHangMonitor::Get();
5069 nsIDocShell* docShell = GetDocShell();
5070 nsCOMPtr<nsIBrowserChild> child =
5071 docShell ? docShell->GetBrowserChild() : nullptr;
5072 action =
5073 monitor->NotifySlowScript(child, filename.get(), aAddonId, aDuration);
5074 if (action == ProcessHangMonitor::Terminate) {
5075 return KillSlowScript;
5078 if (action == ProcessHangMonitor::StartDebugger) {
5079 // Spin a nested event loop so that the debugger in the parent can fetch
5080 // any information it needs. Once the debugger has started, return to the
5081 // script.
5082 RefPtr<nsGlobalWindowOuter> outer = GetOuterWindowInternal();
5083 outer->EnterModalState();
5084 SpinEventLoopUntil("nsGlobalWindowInner::ShowSlowScriptDialog"_ns, [&]() {
5085 return monitor->IsDebuggerStartupComplete();
5087 outer->LeaveModalState();
5088 return ContinueSlowScript;
5091 return ContinueSlowScriptAndKeepNotifying;
5094 // Reached only on non-e10s - once per slow script dialog.
5095 // On e10s - we probe once at ProcessHangsMonitor.sys.mjs
5096 Telemetry::Accumulate(Telemetry::SLOW_SCRIPT_NOTICE_COUNT, 1);
5098 // Get the nsIPrompt interface from the docshell
5099 nsCOMPtr<nsIDocShell> ds = GetDocShell();
5100 NS_ENSURE_TRUE(ds, KillSlowScript);
5101 nsCOMPtr<nsIPrompt> prompt = do_GetInterface(ds);
5102 NS_ENSURE_TRUE(prompt, KillSlowScript);
5104 // Prioritize the SlowScriptDebug interface over JSD1.
5105 nsCOMPtr<nsISlowScriptDebugCallback> debugCallback;
5107 if (hasFrame) {
5108 const char* debugCID = "@mozilla.org/dom/slow-script-debug;1";
5109 nsCOMPtr<nsISlowScriptDebug> debugService = do_GetService(debugCID, &rv);
5110 if (NS_SUCCEEDED(rv)) {
5111 debugService->GetActivationHandler(getter_AddRefs(debugCallback));
5115 bool failed = false;
5116 auto getString = [&](const char* name,
5117 nsContentUtils::PropertiesFile propFile =
5118 nsContentUtils::eDOM_PROPERTIES) {
5119 nsAutoString result;
5120 nsresult rv = nsContentUtils::GetLocalizedString(propFile, name, result);
5122 // GetStringFromName can return NS_OK and still give nullptr string
5123 failed = failed || NS_FAILED(rv) || result.IsEmpty();
5124 return result;
5127 bool isAddonScript = !aAddonId.IsEmpty();
5128 bool showDebugButton = debugCallback && !isAddonScript;
5130 // Get localizable strings
5132 nsAutoString title, checkboxMsg, debugButton, msg;
5133 if (isAddonScript) {
5134 title = getString("KillAddonScriptTitle");
5135 checkboxMsg = getString("KillAddonScriptGlobalMessage");
5137 auto appName =
5138 getString("brandShortName", nsContentUtils::eBRAND_PROPERTIES);
5140 nsCOMPtr<nsIAddonPolicyService> aps =
5141 do_GetService("@mozilla.org/addons/policy-service;1");
5142 nsString addonName;
5143 if (!aps || NS_FAILED(aps->GetExtensionName(aAddonId, addonName))) {
5144 addonName = aAddonId;
5147 rv = nsContentUtils::FormatLocalizedString(
5148 msg, nsContentUtils::eDOM_PROPERTIES, "KillAddonScriptMessage",
5149 addonName, appName);
5151 failed = failed || NS_FAILED(rv);
5152 } else {
5153 title = getString("KillScriptTitle");
5154 checkboxMsg = getString("DontAskAgain");
5156 if (showDebugButton) {
5157 debugButton = getString("DebugScriptButton");
5158 msg = getString("KillScriptWithDebugMessage");
5159 } else {
5160 msg = getString("KillScriptMessage");
5164 auto stopButton = getString("StopScriptButton");
5165 auto waitButton = getString("WaitForScriptButton");
5167 if (failed) {
5168 NS_ERROR("Failed to get localized strings.");
5169 return ContinueSlowScript;
5172 // Append file and line number information, if available
5173 if (filename.get()) {
5174 nsAutoString scriptLocation;
5175 // We want to drop the middle part of too-long locations. We'll
5176 // define "too-long" as longer than 60 UTF-16 code units. Just
5177 // have to be a bit careful about unpaired surrogates.
5178 NS_ConvertUTF8toUTF16 filenameUTF16(filename.get());
5179 if (filenameUTF16.Length() > 60) {
5180 // XXXbz Do we need to insert any bidi overrides here?
5181 size_t cutStart = 30;
5182 size_t cutLength = filenameUTF16.Length() - 60;
5183 MOZ_ASSERT(cutLength > 0);
5184 if (NS_IS_LOW_SURROGATE(filenameUTF16[cutStart])) {
5185 // Don't truncate before the low surrogate, in case it's preceded by a
5186 // high surrogate and forms a single Unicode character. Instead, just
5187 // include the low surrogate.
5188 ++cutStart;
5189 --cutLength;
5191 if (NS_IS_LOW_SURROGATE(filenameUTF16[cutStart + cutLength])) {
5192 // Likewise, don't drop a trailing low surrogate here. We want to
5193 // increase cutLength, since it might be 0 already so we can't very well
5194 // decrease it.
5195 ++cutLength;
5198 // Insert U+2026 HORIZONTAL ELLIPSIS
5199 filenameUTF16.ReplaceLiteral(cutStart, cutLength, u"\x2026");
5201 rv = nsContentUtils::FormatLocalizedString(
5202 scriptLocation, nsContentUtils::eDOM_PROPERTIES, "KillScriptLocation",
5203 filenameUTF16);
5205 if (NS_SUCCEEDED(rv)) {
5206 msg.AppendLiteral("\n\n");
5207 msg.Append(scriptLocation);
5208 msg.Append(':');
5209 msg.AppendInt(lineno);
5213 uint32_t buttonFlags = nsIPrompt::BUTTON_POS_1_DEFAULT +
5214 (nsIPrompt::BUTTON_TITLE_IS_STRING *
5215 (nsIPrompt::BUTTON_POS_0 + nsIPrompt::BUTTON_POS_1));
5217 // Add a third button if necessary.
5218 if (showDebugButton)
5219 buttonFlags += nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_2;
5221 bool checkboxValue = false;
5222 int32_t buttonPressed = 0; // In case the user exits dialog by clicking X.
5224 // Null out the operation callback while we're re-entering JS here.
5225 AutoDisableJSInterruptCallback disabler(aCx);
5227 // Open the dialog.
5228 rv = prompt->ConfirmEx(
5229 title.get(), msg.get(), buttonFlags, waitButton.get(), stopButton.get(),
5230 debugButton.get(), checkboxMsg.get(), &checkboxValue, &buttonPressed);
5233 if (buttonPressed == 0) {
5234 if (checkboxValue && !isAddonScript && NS_SUCCEEDED(rv))
5235 return AlwaysContinueSlowScript;
5236 return ContinueSlowScript;
5239 if (buttonPressed == 2) {
5240 MOZ_RELEASE_ASSERT(debugCallback);
5242 rv = debugCallback->HandleSlowScriptDebug(this);
5243 return NS_SUCCEEDED(rv) ? ContinueSlowScript : KillSlowScript;
5246 JS_ClearPendingException(aCx);
5248 return KillSlowScript;
5251 nsresult nsGlobalWindowInner::Observe(nsISupports* aSubject, const char* aTopic,
5252 const char16_t* aData) {
5253 if (!nsCRT::strcmp(aTopic, NS_IOSERVICE_OFFLINE_STATUS_TOPIC)) {
5254 if (!IsFrozen()) {
5255 // Fires an offline status event if the offline status has changed
5256 FireOfflineStatusEventIfChanged();
5258 return NS_OK;
5261 if (!nsCRT::strcmp(aTopic, MEMORY_PRESSURE_OBSERVER_TOPIC)) {
5262 if (mPerformance) {
5263 mPerformance->MemoryPressure();
5265 RemoveReportRecords();
5266 return NS_OK;
5269 if (!nsCRT::strcmp(aTopic, PERMISSION_CHANGED_TOPIC)) {
5270 nsCOMPtr<nsIPermission> perm(do_QueryInterface(aSubject));
5271 if (!perm) {
5272 // A null permission indicates that the entire permission list
5273 // was cleared.
5274 MOZ_ASSERT(!nsCRT::strcmp(aData, u"cleared"));
5275 UpdatePermissions();
5276 return NS_OK;
5279 nsAutoCString type;
5280 perm->GetType(type);
5281 if (type == "autoplay-media"_ns) {
5282 UpdateAutoplayPermission();
5283 } else if (type == "shortcuts"_ns) {
5284 UpdateShortcutsPermission();
5285 } else if (type == "popup"_ns) {
5286 UpdatePopupPermission();
5289 if (!mDoc) {
5290 return NS_OK;
5293 RefPtr<PermissionDelegateHandler> permDelegateHandler =
5294 mDoc->GetPermissionDelegateHandler();
5296 if (permDelegateHandler) {
5297 permDelegateHandler->UpdateDelegatedPermission(type);
5300 return NS_OK;
5303 if (!nsCRT::strcmp(aTopic, "screen-information-changed")) {
5304 if (mScreen) {
5305 if (RefPtr<ScreenOrientation> orientation =
5306 mScreen->GetOrientationIfExists()) {
5307 orientation->MaybeChanged();
5310 if (mHasOrientationChangeListeners) {
5311 int32_t oldAngle = mOrientationAngle;
5312 mOrientationAngle = Orientation(CallerType::System);
5313 if (mOrientationAngle != oldAngle && IsCurrentInnerWindow()) {
5314 nsCOMPtr<nsPIDOMWindowOuter> outer = GetOuterWindow();
5315 outer->DispatchCustomEvent(u"orientationchange"_ns);
5318 return NS_OK;
5321 if (!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
5322 MOZ_ASSERT(!NS_strcmp(aData, u"intl.accept_languages"));
5324 // The user preferred languages have changed, we need to fire an event on
5325 // Window object and invalidate the cache for navigator.languages. It is
5326 // done for every change which can be a waste of cycles but those should be
5327 // fairly rare.
5328 // We MUST invalidate navigator.languages before sending the event in the
5329 // very likely situation where an event handler will try to read its value.
5331 if (mNavigator) {
5332 Navigator_Binding::ClearCachedLanguageValue(mNavigator);
5333 Navigator_Binding::ClearCachedLanguagesValue(mNavigator);
5336 // The event has to be dispatched only to the current inner window.
5337 if (!IsCurrentInnerWindow()) {
5338 return NS_OK;
5341 RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
5342 event->InitEvent(u"languagechange"_ns, false, false);
5343 event->SetTrusted(true);
5345 ErrorResult rv;
5346 DispatchEvent(*event, rv);
5347 return rv.StealNSResult();
5350 NS_WARNING("unrecognized topic in nsGlobalWindowInner::Observe");
5351 return NS_ERROR_FAILURE;
5354 void nsGlobalWindowInner::ObserveStorageNotification(
5355 StorageEvent* aEvent, const char16_t* aStorageType, bool aPrivateBrowsing) {
5356 MOZ_ASSERT(aEvent);
5358 // The private browsing check must be done here again because this window
5359 // could have changed its state before the notification check and now. This
5360 // happens in case this window did have a docShell at that time.
5361 if (aPrivateBrowsing != IsPrivateBrowsing()) {
5362 return;
5365 // LocalStorage can only exist on an inner window, and we don't want to
5366 // generate events on frozen or otherwise-navigated-away from windows.
5367 // (Actually, this code used to try and buffer events for frozen windows,
5368 // but it never worked, so we've removed it. See bug 1285898.)
5369 if (!IsCurrentInnerWindow() || IsFrozen()) {
5370 return;
5373 nsIPrincipal* principal = GetPrincipal();
5374 if (!principal) {
5375 return;
5378 bool fireMozStorageChanged = false;
5379 nsAutoString eventType;
5380 eventType.AssignLiteral("storage");
5382 if (!NS_strcmp(aStorageType, u"sessionStorage")) {
5383 RefPtr<Storage> changingStorage = aEvent->GetStorageArea();
5384 MOZ_ASSERT(changingStorage);
5386 bool check = false;
5388 if (const RefPtr<SessionStorageManager> storageManager =
5389 GetBrowsingContext()->GetSessionStorageManager()) {
5390 nsresult rv = storageManager->CheckStorage(GetEffectiveStoragePrincipal(),
5391 changingStorage, &check);
5392 if (NS_FAILED(rv)) {
5393 return;
5397 if (!check) {
5398 // This storage event is not coming from our storage or is coming
5399 // from a different docshell, i.e. it is a clone, ignore this event.
5400 return;
5403 MOZ_LOG(
5404 gDOMLeakPRLogInner, LogLevel::Debug,
5405 ("nsGlobalWindowInner %p with sessionStorage %p passing event from %p",
5406 this, mSessionStorage.get(), changingStorage.get()));
5408 fireMozStorageChanged = mSessionStorage == changingStorage;
5409 if (fireMozStorageChanged) {
5410 eventType.AssignLiteral("MozSessionStorageChanged");
5414 else {
5415 MOZ_ASSERT(!NS_strcmp(aStorageType, u"localStorage"));
5417 nsIPrincipal* storagePrincipal = GetEffectiveStoragePrincipal();
5418 if (!storagePrincipal) {
5419 return;
5422 MOZ_DIAGNOSTIC_ASSERT(StorageUtils::PrincipalsEqual(aEvent->GetPrincipal(),
5423 storagePrincipal));
5425 fireMozStorageChanged =
5426 mLocalStorage && mLocalStorage == aEvent->GetStorageArea();
5428 if (fireMozStorageChanged) {
5429 eventType.AssignLiteral("MozLocalStorageChanged");
5433 // Clone the storage event included in the observer notification. We want
5434 // to dispatch clones rather than the original event.
5435 IgnoredErrorResult error;
5436 RefPtr<StorageEvent> clonedEvent =
5437 CloneStorageEvent(eventType, aEvent, error);
5438 if (error.Failed() || !clonedEvent) {
5439 return;
5442 clonedEvent->SetTrusted(true);
5444 if (fireMozStorageChanged) {
5445 WidgetEvent* internalEvent = clonedEvent->WidgetEventPtr();
5446 internalEvent->mFlags.mOnlyChromeDispatch = true;
5449 DispatchEvent(*clonedEvent);
5452 already_AddRefed<StorageEvent> nsGlobalWindowInner::CloneStorageEvent(
5453 const nsAString& aType, const RefPtr<StorageEvent>& aEvent,
5454 ErrorResult& aRv) {
5455 StorageEventInit dict;
5457 dict.mBubbles = aEvent->Bubbles();
5458 dict.mCancelable = aEvent->Cancelable();
5459 aEvent->GetKey(dict.mKey);
5460 aEvent->GetOldValue(dict.mOldValue);
5461 aEvent->GetNewValue(dict.mNewValue);
5462 aEvent->GetUrl(dict.mUrl);
5464 RefPtr<Storage> storageArea = aEvent->GetStorageArea();
5466 RefPtr<Storage> storage;
5468 // If null, this is a localStorage event received by IPC.
5469 if (!storageArea) {
5470 storage = GetLocalStorage(aRv);
5471 if (!NextGenLocalStorageEnabled()) {
5472 if (aRv.Failed() || !storage) {
5473 return nullptr;
5476 if (storage->Type() == Storage::eLocalStorage) {
5477 RefPtr<LocalStorage> localStorage =
5478 static_cast<LocalStorage*>(storage.get());
5480 // We must apply the current change to the 'local' localStorage.
5481 localStorage->ApplyEvent(aEvent);
5484 } else if (storageArea->Type() == Storage::eSessionStorage) {
5485 storage = GetSessionStorage(aRv);
5486 } else {
5487 MOZ_ASSERT(storageArea->Type() == Storage::eLocalStorage);
5488 storage = GetLocalStorage(aRv);
5491 if (aRv.Failed() || !storage) {
5492 return nullptr;
5495 if (storage->Type() == Storage::ePartitionedLocalStorage) {
5496 // This error message is not exposed.
5497 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
5498 return nullptr;
5501 MOZ_ASSERT(storage);
5502 MOZ_ASSERT_IF(storageArea, storage->IsForkOf(storageArea));
5504 dict.mStorageArea = storage;
5506 RefPtr<StorageEvent> event = StorageEvent::Constructor(this, aType, dict);
5507 return event.forget();
5510 void nsGlobalWindowInner::Suspend(bool aIncludeSubWindows) {
5511 MOZ_ASSERT(NS_IsMainThread());
5513 // We can only safely suspend windows that are the current inner window. If
5514 // its not the current inner, then we are in one of two different cases.
5515 // Either we are in the bfcache or we are doomed window that is going away.
5516 // When a window becomes inactive we purposely avoid placing already suspended
5517 // windows into the bfcache. It only expects windows suspended due to the
5518 // Freeze() method which occurs while the window is still the current inner.
5519 // So we must not call Suspend() on bfcache windows at this point or this
5520 // invariant will be broken. If the window is doomed there is no point in
5521 // suspending it since it will soon be gone.
5522 if (!IsCurrentInnerWindow()) {
5523 return;
5526 // All in-process descendants are also suspended. This ensure mSuspendDepth
5527 // is set properly and the timers are properly canceled for each in-process
5528 // descendant.
5529 if (aIncludeSubWindows) {
5530 CallOnInProcessDescendants(&nsGlobalWindowInner::Suspend, false);
5533 mSuspendDepth += 1;
5534 if (mSuspendDepth != 1) {
5535 return;
5538 if (mWindowGlobalChild) {
5539 mWindowGlobalChild->BlockBFCacheFor(BFCacheStatus::SUSPENDED);
5542 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
5543 if (ac) {
5544 for (uint32_t i = 0; i < mEnabledSensors.Length(); i++)
5545 ac->RemoveWindowListener(mEnabledSensors[i], this);
5547 DisableGamepadUpdates();
5548 DisableVRUpdates();
5550 SuspendWorkersForWindow(*this);
5552 for (RefPtr<mozilla::dom::SharedWorker> pinnedWorker :
5553 mSharedWorkers.ForwardRange()) {
5554 pinnedWorker->Suspend();
5557 SuspendIdleRequests();
5559 mTimeoutManager->Suspend();
5561 // Suspend all of the AudioContexts for this window
5562 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
5563 mAudioContexts[i]->SuspendFromChrome();
5567 void nsGlobalWindowInner::Resume(bool aIncludeSubWindows) {
5568 MOZ_ASSERT(NS_IsMainThread());
5570 // We can only safely resume a window if its the current inner window. If
5571 // its not the current inner, then we are in one of two different cases.
5572 // Either we are in the bfcache or we are doomed window that is going away.
5573 // If a window is suspended when it becomes inactive we purposely do not
5574 // put it in the bfcache, so Resume should never be needed in that case.
5575 // If the window is doomed then there is no point in resuming it.
5576 if (!IsCurrentInnerWindow()) {
5577 return;
5580 // Resume all in-process descendants. This restores timers recursively
5581 // canceled in Suspend() and ensures all in-process descendants have the
5582 // correct mSuspendDepth.
5583 if (aIncludeSubWindows) {
5584 CallOnInProcessDescendants(&nsGlobalWindowInner::Resume, false);
5587 if (mSuspendDepth == 0) {
5588 // Ignore if the window is not suspended.
5589 return;
5592 mSuspendDepth -= 1;
5594 if (mSuspendDepth != 0) {
5595 return;
5598 // We should not be able to resume a frozen window. It must be Thaw()'d
5599 // first.
5600 MOZ_ASSERT(mFreezeDepth == 0);
5602 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
5603 if (ac) {
5604 for (uint32_t i = 0; i < mEnabledSensors.Length(); i++)
5605 ac->AddWindowListener(mEnabledSensors[i], this);
5607 EnableGamepadUpdates();
5608 EnableVRUpdates();
5610 // Resume all of the AudioContexts for this window
5611 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
5612 mAudioContexts[i]->ResumeFromChrome();
5615 if (RefPtr<MediaDevices> devices = GetExtantMediaDevices()) {
5616 devices->WindowResumed();
5619 mTimeoutManager->Resume();
5621 ResumeIdleRequests();
5623 // Resume all of the workers for this window. We must do this
5624 // after timeouts since workers may have queued events that can trigger
5625 // a setTimeout().
5626 ResumeWorkersForWindow(*this);
5628 for (RefPtr<mozilla::dom::SharedWorker> pinnedWorker :
5629 mSharedWorkers.ForwardRange()) {
5630 pinnedWorker->Resume();
5633 if (mWindowGlobalChild) {
5634 mWindowGlobalChild->UnblockBFCacheFor(BFCacheStatus::SUSPENDED);
5638 bool nsGlobalWindowInner::IsSuspended() const {
5639 MOZ_ASSERT(NS_IsMainThread());
5640 return mSuspendDepth != 0;
5643 void nsGlobalWindowInner::Freeze(bool aIncludeSubWindows) {
5644 MOZ_ASSERT(NS_IsMainThread());
5645 Suspend(aIncludeSubWindows);
5646 FreezeInternal(aIncludeSubWindows);
5649 void nsGlobalWindowInner::FreezeInternal(bool aIncludeSubWindows) {
5650 MOZ_ASSERT(NS_IsMainThread());
5651 MOZ_DIAGNOSTIC_ASSERT(IsCurrentInnerWindow());
5652 MOZ_DIAGNOSTIC_ASSERT(IsSuspended());
5654 HintIsLoading(false);
5656 if (aIncludeSubWindows) {
5657 CallOnInProcessChildren(&nsGlobalWindowInner::FreezeInternal,
5658 aIncludeSubWindows);
5661 mFreezeDepth += 1;
5662 MOZ_ASSERT(mSuspendDepth >= mFreezeDepth);
5663 if (mFreezeDepth != 1) {
5664 return;
5667 FreezeWorkersForWindow(*this);
5669 for (RefPtr<mozilla::dom::SharedWorker> pinnedWorker :
5670 mSharedWorkers.ForwardRange()) {
5671 pinnedWorker->Freeze();
5674 mTimeoutManager->Freeze();
5675 if (mClientSource) {
5676 mClientSource->Freeze();
5679 NotifyDOMWindowFrozen(this);
5682 void nsGlobalWindowInner::Thaw(bool aIncludeSubWindows) {
5683 MOZ_ASSERT(NS_IsMainThread());
5684 ThawInternal(aIncludeSubWindows);
5685 Resume(aIncludeSubWindows);
5688 void nsGlobalWindowInner::ThawInternal(bool aIncludeSubWindows) {
5689 MOZ_ASSERT(NS_IsMainThread());
5690 MOZ_DIAGNOSTIC_ASSERT(IsCurrentInnerWindow());
5691 MOZ_DIAGNOSTIC_ASSERT(IsSuspended());
5693 if (aIncludeSubWindows) {
5694 CallOnInProcessChildren(&nsGlobalWindowInner::ThawInternal,
5695 aIncludeSubWindows);
5698 MOZ_ASSERT(mFreezeDepth != 0);
5699 mFreezeDepth -= 1;
5700 MOZ_ASSERT(mSuspendDepth >= mFreezeDepth);
5701 if (mFreezeDepth != 0) {
5702 return;
5705 if (mClientSource) {
5706 mClientSource->Thaw();
5708 mTimeoutManager->Thaw();
5710 ThawWorkersForWindow(*this);
5712 for (RefPtr<mozilla::dom::SharedWorker> pinnedWorker :
5713 mSharedWorkers.ForwardRange()) {
5714 pinnedWorker->Thaw();
5717 NotifyDOMWindowThawed(this);
5720 bool nsGlobalWindowInner::IsFrozen() const {
5721 MOZ_ASSERT(NS_IsMainThread());
5722 bool frozen = mFreezeDepth != 0;
5723 MOZ_ASSERT_IF(frozen, IsSuspended());
5724 return frozen;
5727 void nsGlobalWindowInner::SyncStateFromParentWindow() {
5728 // This method should only be called on an inner window that has been
5729 // assigned to an outer window already.
5730 MOZ_ASSERT(IsCurrentInnerWindow());
5731 nsPIDOMWindowOuter* outer = GetOuterWindow();
5732 MOZ_ASSERT(outer);
5734 // Attempt to find our parent windows.
5735 nsCOMPtr<Element> frame = outer->GetFrameElementInternal();
5736 nsPIDOMWindowOuter* parentOuter =
5737 frame ? frame->OwnerDoc()->GetWindow() : nullptr;
5738 nsGlobalWindowInner* parentInner =
5739 parentOuter
5740 ? nsGlobalWindowInner::Cast(parentOuter->GetCurrentInnerWindow())
5741 : nullptr;
5743 // If our outer is in a modal state, but our parent is not in a modal
5744 // state, then we must apply the suspend directly. If our parent is
5745 // in a modal state then we should get the suspend automatically
5746 // via the parentSuspendDepth application below.
5747 if ((!parentInner || !parentInner->IsInModalState()) && IsInModalState()) {
5748 Suspend();
5751 uint32_t parentFreezeDepth = parentInner ? parentInner->mFreezeDepth : 0;
5752 uint32_t parentSuspendDepth = parentInner ? parentInner->mSuspendDepth : 0;
5754 // Since every Freeze() calls Suspend(), the suspend count must
5755 // be equal or greater to the freeze count.
5756 MOZ_ASSERT(parentFreezeDepth <= parentSuspendDepth);
5758 // First apply the Freeze() calls.
5759 for (uint32_t i = 0; i < parentFreezeDepth; ++i) {
5760 Freeze();
5763 // Now apply only the number of Suspend() calls to reach the target
5764 // suspend count after applying the Freeze() calls.
5765 for (uint32_t i = 0; i < (parentSuspendDepth - parentFreezeDepth); ++i) {
5766 Suspend();
5770 void nsGlobalWindowInner::UpdateBackgroundState() {
5771 if (RefPtr<MediaDevices> devices = GetExtantMediaDevices()) {
5772 devices->BackgroundStateChanged();
5774 mTimeoutManager->UpdateBackgroundState();
5777 template <typename Method, typename... Args>
5778 CallState nsGlobalWindowInner::CallOnInProcessDescendantsInternal(
5779 BrowsingContext* aBrowsingContext, bool aChildOnly, Method aMethod,
5780 Args&&... aArgs) {
5781 MOZ_ASSERT(NS_IsMainThread());
5782 MOZ_ASSERT(aBrowsingContext);
5784 CallState state = CallState::Continue;
5785 for (const RefPtr<BrowsingContext>& bc : aBrowsingContext->Children()) {
5786 if (nsCOMPtr<nsPIDOMWindowOuter> pWin = bc->GetDOMWindow()) {
5787 auto* win = nsGlobalWindowOuter::Cast(pWin);
5788 if (nsGlobalWindowInner* inner =
5789 nsGlobalWindowInner::Cast(win->GetCurrentInnerWindow())) {
5790 // Call the descendant method using our helper CallDescendant() template
5791 // method. This allows us to handle both void returning methods and
5792 // methods that return CallState explicitly. For void returning methods
5793 // we assume CallState::Continue.
5794 using returnType = decltype((inner->*aMethod)(aArgs...));
5795 state = CallDescendant<returnType>(inner, aMethod, aArgs...);
5797 if (state == CallState::Stop) {
5798 return state;
5803 if (!aChildOnly) {
5804 state = CallOnInProcessDescendantsInternal(bc.get(), aChildOnly, aMethod,
5805 aArgs...);
5806 if (state == CallState::Stop) {
5807 return state;
5812 return state;
5815 Maybe<ClientInfo> nsGlobalWindowInner::GetClientInfo() const {
5816 MOZ_ASSERT(NS_IsMainThread());
5817 if (mDoc && mDoc->IsStaticDocument()) {
5818 if (Maybe<ClientInfo> info = mDoc->GetOriginalDocument()->GetClientInfo()) {
5819 return info;
5823 Maybe<ClientInfo> clientInfo;
5824 if (mClientSource) {
5825 clientInfo.emplace(mClientSource->Info());
5827 return clientInfo;
5830 Maybe<ClientState> nsGlobalWindowInner::GetClientState() const {
5831 MOZ_ASSERT(NS_IsMainThread());
5832 if (mDoc && mDoc->IsStaticDocument()) {
5833 if (Maybe<ClientState> state =
5834 mDoc->GetOriginalDocument()->GetClientState()) {
5835 return state;
5839 Maybe<ClientState> clientState;
5840 if (mClientSource) {
5841 Result<ClientState, ErrorResult> res = mClientSource->SnapshotState();
5842 if (res.isOk()) {
5843 clientState.emplace(res.unwrap());
5844 } else {
5845 res.unwrapErr().SuppressException();
5848 return clientState;
5851 Maybe<ServiceWorkerDescriptor> nsGlobalWindowInner::GetController() const {
5852 MOZ_ASSERT(NS_IsMainThread());
5853 if (mDoc && mDoc->IsStaticDocument()) {
5854 if (Maybe<ServiceWorkerDescriptor> controller =
5855 mDoc->GetOriginalDocument()->GetController()) {
5856 return controller;
5860 Maybe<ServiceWorkerDescriptor> controller;
5861 if (mClientSource) {
5862 controller = mClientSource->GetController();
5864 return controller;
5867 void nsGlobalWindowInner::SetCsp(nsIContentSecurityPolicy* aCsp) {
5868 if (!mClientSource) {
5869 return;
5871 mClientSource->SetCsp(aCsp);
5872 // Also cache the CSP within the document
5873 mDoc->SetCsp(aCsp);
5875 if (mWindowGlobalChild) {
5876 mWindowGlobalChild->SendSetClientInfo(mClientSource->Info().ToIPC());
5880 void nsGlobalWindowInner::SetPreloadCsp(nsIContentSecurityPolicy* aPreloadCsp) {
5881 if (!mClientSource) {
5882 return;
5884 mClientSource->SetPreloadCsp(aPreloadCsp);
5885 // Also cache the preload CSP within the document
5886 mDoc->SetPreloadCsp(aPreloadCsp);
5888 if (mWindowGlobalChild) {
5889 mWindowGlobalChild->SendSetClientInfo(mClientSource->Info().ToIPC());
5893 nsIContentSecurityPolicy* nsGlobalWindowInner::GetCsp() {
5894 if (mDoc) {
5895 return mDoc->GetCsp();
5898 // If the window is partially torn down and has its document nulled out,
5899 // we query the CSP we snapshot in FreeInnerObjects.
5900 if (mDocumentCsp) {
5901 return mDocumentCsp;
5903 return nullptr;
5906 RefPtr<ServiceWorker> nsGlobalWindowInner::GetOrCreateServiceWorker(
5907 const ServiceWorkerDescriptor& aDescriptor) {
5908 MOZ_ASSERT(NS_IsMainThread());
5909 RefPtr<ServiceWorker> ref;
5910 ForEachGlobalTeardownObserver(
5911 [&](GlobalTeardownObserver* aObserver, bool* aDoneOut) {
5912 RefPtr<ServiceWorker> sw = do_QueryObject(aObserver);
5913 if (!sw || !sw->Descriptor().Matches(aDescriptor)) {
5914 return;
5917 ref = std::move(sw);
5918 *aDoneOut = true;
5921 if (!ref) {
5922 ref = ServiceWorker::Create(this, aDescriptor);
5925 return ref;
5928 RefPtr<mozilla::dom::ServiceWorkerRegistration>
5929 nsGlobalWindowInner::GetServiceWorkerRegistration(
5930 const mozilla::dom::ServiceWorkerRegistrationDescriptor& aDescriptor)
5931 const {
5932 MOZ_ASSERT(NS_IsMainThread());
5933 RefPtr<ServiceWorkerRegistration> ref;
5934 ForEachGlobalTeardownObserver(
5935 [&](GlobalTeardownObserver* aObserver, bool* aDoneOut) {
5936 RefPtr<ServiceWorkerRegistration> swr = do_QueryObject(aObserver);
5937 if (!swr || !swr->MatchesDescriptor(aDescriptor)) {
5938 return;
5941 ref = std::move(swr);
5942 *aDoneOut = true;
5944 return ref;
5947 RefPtr<ServiceWorkerRegistration>
5948 nsGlobalWindowInner::GetOrCreateServiceWorkerRegistration(
5949 const ServiceWorkerRegistrationDescriptor& aDescriptor) {
5950 MOZ_ASSERT(NS_IsMainThread());
5951 RefPtr<ServiceWorkerRegistration> ref =
5952 GetServiceWorkerRegistration(aDescriptor);
5953 if (!ref) {
5954 ref = ServiceWorkerRegistration::CreateForMainThread(this, aDescriptor);
5956 return ref;
5959 StorageAccess nsGlobalWindowInner::GetStorageAccess() {
5960 return StorageAllowedForWindow(this);
5963 nsresult nsGlobalWindowInner::FireDelayedDOMEvents(bool aIncludeSubWindows) {
5964 // Fires an offline status event if the offline status has changed
5965 FireOfflineStatusEventIfChanged();
5967 if (!aIncludeSubWindows) {
5968 return NS_OK;
5971 nsCOMPtr<nsIDocShell> docShell = GetDocShell();
5972 if (docShell) {
5973 int32_t childCount = 0;
5974 docShell->GetInProcessChildCount(&childCount);
5976 // Take a copy of the current children so that modifications to
5977 // the child list don't affect to the iteration.
5978 AutoTArray<nsCOMPtr<nsIDocShellTreeItem>, 8> children;
5979 for (int32_t i = 0; i < childCount; ++i) {
5980 nsCOMPtr<nsIDocShellTreeItem> childShell;
5981 docShell->GetInProcessChildAt(i, getter_AddRefs(childShell));
5982 if (childShell) {
5983 children.AppendElement(childShell);
5987 for (nsCOMPtr<nsIDocShellTreeItem> childShell : children) {
5988 if (nsCOMPtr<nsPIDOMWindowOuter> pWin = childShell->GetWindow()) {
5989 auto* win = nsGlobalWindowOuter::Cast(pWin);
5990 win->FireDelayedDOMEvents(true);
5995 return NS_OK;
5998 //*****************************************************************************
5999 // nsGlobalWindowInner: Window Control Functions
6000 //*****************************************************************************
6002 nsPIDOMWindowOuter* nsGlobalWindowInner::GetInProcessParentInternal() {
6003 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
6004 if (!outer) {
6005 // No outer window available!
6006 return nullptr;
6008 return outer->GetInProcessParentInternal();
6011 nsIPrincipal* nsGlobalWindowInner::GetTopLevelAntiTrackingPrincipal() {
6012 nsPIDOMWindowOuter* outerWindow = GetOuterWindowInternal();
6013 if (!outerWindow) {
6014 return nullptr;
6017 nsPIDOMWindowOuter* topLevelOuterWindow =
6018 GetBrowsingContext()->Top()->GetDOMWindow();
6019 if (!topLevelOuterWindow) {
6020 return nullptr;
6023 bool stopAtOurLevel =
6024 mDoc && mDoc->CookieJarSettings()->GetCookieBehavior() ==
6025 nsICookieService::BEHAVIOR_REJECT_TRACKER;
6027 if (stopAtOurLevel && topLevelOuterWindow == outerWindow) {
6028 return nullptr;
6031 nsPIDOMWindowInner* topLevelInnerWindow =
6032 topLevelOuterWindow->GetCurrentInnerWindow();
6033 if (NS_WARN_IF(!topLevelInnerWindow)) {
6034 return nullptr;
6037 nsIPrincipal* topLevelPrincipal =
6038 nsGlobalWindowInner::Cast(topLevelInnerWindow)->GetPrincipal();
6039 if (NS_WARN_IF(!topLevelPrincipal)) {
6040 return nullptr;
6043 return topLevelPrincipal;
6046 nsIPrincipal* nsGlobalWindowInner::GetClientPrincipal() {
6047 return mClientSource ? mClientSource->GetPrincipal() : nullptr;
6050 bool nsGlobalWindowInner::IsInFullScreenTransition() {
6051 if (!mIsChrome) {
6052 return false;
6055 nsGlobalWindowOuter* outerWindow = GetOuterWindowInternal();
6056 if (!outerWindow) {
6057 return false;
6060 return outerWindow->mIsInFullScreenTransition;
6063 //*****************************************************************************
6064 // nsGlobalWindowInner: Timeout Functions
6065 //*****************************************************************************
6067 class WindowScriptTimeoutHandler final : public ScriptTimeoutHandler {
6068 public:
6069 NS_DECL_ISUPPORTS_INHERITED
6070 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(WindowScriptTimeoutHandler,
6071 ScriptTimeoutHandler)
6073 WindowScriptTimeoutHandler(JSContext* aCx, nsIGlobalObject* aGlobal,
6074 const nsAString& aExpression)
6075 : ScriptTimeoutHandler(aCx, aGlobal, aExpression),
6076 mInitiatingScript(ScriptLoader::GetActiveScript(aCx)) {}
6078 MOZ_CAN_RUN_SCRIPT virtual bool Call(const char* aExecutionReason) override;
6080 private:
6081 virtual ~WindowScriptTimeoutHandler() = default;
6083 // Initiating script for use when evaluating mExpr on the main thread.
6084 RefPtr<JS::loader::LoadedScript> mInitiatingScript;
6087 NS_IMPL_CYCLE_COLLECTION_INHERITED(WindowScriptTimeoutHandler,
6088 ScriptTimeoutHandler, mInitiatingScript)
6090 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WindowScriptTimeoutHandler)
6091 NS_INTERFACE_MAP_END_INHERITING(ScriptTimeoutHandler)
6093 NS_IMPL_ADDREF_INHERITED(WindowScriptTimeoutHandler, ScriptTimeoutHandler)
6094 NS_IMPL_RELEASE_INHERITED(WindowScriptTimeoutHandler, ScriptTimeoutHandler)
6096 bool WindowScriptTimeoutHandler::Call(const char* aExecutionReason) {
6097 // New script entry point required, due to the "Create a script" sub-step
6098 // of
6099 // http://www.whatwg.org/specs/web-apps/current-work/#timer-initialisation-steps
6100 nsAutoMicroTask mt;
6101 AutoEntryScript aes(mGlobal, aExecutionReason, true);
6102 JS::CompileOptions options(aes.cx());
6103 options.setFileAndLine(mFileName.get(), mLineNo);
6104 options.setNoScriptRval(true);
6105 options.setIntroductionType("domTimer");
6106 JS::Rooted<JSObject*> global(aes.cx(), mGlobal->GetGlobalJSObject());
6108 JSExecutionContext exec(aes.cx(), global, options);
6109 nsresult rv = exec.Compile(mExpr);
6111 JS::Rooted<JSScript*> script(aes.cx(), exec.MaybeGetScript());
6112 if (script) {
6113 if (mInitiatingScript) {
6114 mInitiatingScript->AssociateWithScript(script);
6117 rv = exec.ExecScript();
6120 if (rv == NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW_UNCATCHABLE) {
6121 return false;
6125 return true;
6128 nsGlobalWindowInner* nsGlobalWindowInner::InnerForSetTimeoutOrInterval(
6129 ErrorResult& aError) {
6130 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
6131 nsPIDOMWindowInner* currentInner =
6132 outer ? outer->GetCurrentInnerWindow() : this;
6134 // If forwardTo is not the window with an active document then we want the
6135 // call to setTimeout/Interval to be a noop, so return null but don't set an
6136 // error.
6137 return HasActiveDocument() ? nsGlobalWindowInner::Cast(currentInner)
6138 : nullptr;
6141 int32_t nsGlobalWindowInner::SetTimeout(JSContext* aCx, Function& aFunction,
6142 int32_t aTimeout,
6143 const Sequence<JS::Value>& aArguments,
6144 ErrorResult& aError) {
6145 return SetTimeoutOrInterval(aCx, aFunction, aTimeout, aArguments, false,
6146 aError);
6149 int32_t nsGlobalWindowInner::SetTimeout(JSContext* aCx,
6150 const nsAString& aHandler,
6151 int32_t aTimeout,
6152 const Sequence<JS::Value>& /* unused */,
6153 ErrorResult& aError) {
6154 return SetTimeoutOrInterval(aCx, aHandler, aTimeout, false, aError);
6157 int32_t nsGlobalWindowInner::SetInterval(JSContext* aCx, Function& aFunction,
6158 const int32_t aTimeout,
6159 const Sequence<JS::Value>& aArguments,
6160 ErrorResult& aError) {
6161 return SetTimeoutOrInterval(aCx, aFunction, aTimeout, aArguments, true,
6162 aError);
6165 int32_t nsGlobalWindowInner::SetInterval(
6166 JSContext* aCx, const nsAString& aHandler, const int32_t aTimeout,
6167 const Sequence<JS::Value>& /* unused */, ErrorResult& aError) {
6168 return SetTimeoutOrInterval(aCx, aHandler, aTimeout, true, aError);
6171 int32_t nsGlobalWindowInner::SetTimeoutOrInterval(
6172 JSContext* aCx, Function& aFunction, int32_t aTimeout,
6173 const Sequence<JS::Value>& aArguments, bool aIsInterval,
6174 ErrorResult& aError) {
6175 nsGlobalWindowInner* inner = InnerForSetTimeoutOrInterval(aError);
6176 if (!inner) {
6177 return -1;
6180 if (inner != this) {
6181 RefPtr<nsGlobalWindowInner> innerRef(inner);
6182 return innerRef->SetTimeoutOrInterval(aCx, aFunction, aTimeout, aArguments,
6183 aIsInterval, aError);
6186 DebuggerNotificationDispatch(
6187 this, aIsInterval ? DebuggerNotificationType::SetInterval
6188 : DebuggerNotificationType::SetTimeout);
6190 if (!GetContextInternal() || !HasJSGlobal()) {
6191 // This window was already closed, or never properly initialized,
6192 // don't let a timer be scheduled on such a window.
6193 aError.Throw(NS_ERROR_NOT_INITIALIZED);
6194 return 0;
6197 nsTArray<JS::Heap<JS::Value>> args;
6198 if (!args.AppendElements(aArguments, fallible)) {
6199 aError.Throw(NS_ERROR_OUT_OF_MEMORY);
6200 return 0;
6203 RefPtr<TimeoutHandler> handler =
6204 new CallbackTimeoutHandler(aCx, this, &aFunction, std::move(args));
6206 int32_t result;
6207 aError =
6208 mTimeoutManager->SetTimeout(handler, aTimeout, aIsInterval,
6209 Timeout::Reason::eTimeoutOrInterval, &result);
6210 return result;
6213 int32_t nsGlobalWindowInner::SetTimeoutOrInterval(JSContext* aCx,
6214 const nsAString& aHandler,
6215 int32_t aTimeout,
6216 bool aIsInterval,
6217 ErrorResult& aError) {
6218 nsGlobalWindowInner* inner = InnerForSetTimeoutOrInterval(aError);
6219 if (!inner) {
6220 return -1;
6223 if (inner != this) {
6224 RefPtr<nsGlobalWindowInner> innerRef(inner);
6225 return innerRef->SetTimeoutOrInterval(aCx, aHandler, aTimeout, aIsInterval,
6226 aError);
6229 DebuggerNotificationDispatch(
6230 this, aIsInterval ? DebuggerNotificationType::SetInterval
6231 : DebuggerNotificationType::SetTimeout);
6233 if (!GetContextInternal() || !HasJSGlobal()) {
6234 // This window was already closed, or never properly initialized,
6235 // don't let a timer be scheduled on such a window.
6236 aError.Throw(NS_ERROR_NOT_INITIALIZED);
6237 return 0;
6240 bool allowEval = false;
6241 aError = CSPEvalChecker::CheckForWindow(aCx, this, aHandler, &allowEval);
6242 if (NS_WARN_IF(aError.Failed()) || !allowEval) {
6243 return 0;
6246 RefPtr<TimeoutHandler> handler =
6247 new WindowScriptTimeoutHandler(aCx, this, aHandler);
6249 int32_t result;
6250 aError =
6251 mTimeoutManager->SetTimeout(handler, aTimeout, aIsInterval,
6252 Timeout::Reason::eTimeoutOrInterval, &result);
6253 return result;
6256 static const char* GetTimeoutReasonString(Timeout* aTimeout) {
6257 switch (aTimeout->mReason) {
6258 case Timeout::Reason::eTimeoutOrInterval:
6259 if (aTimeout->mIsInterval) {
6260 return "setInterval handler";
6262 return "setTimeout handler";
6263 case Timeout::Reason::eIdleCallbackTimeout:
6264 return "setIdleCallback handler (timed out)";
6265 case Timeout::Reason::eAbortSignalTimeout:
6266 return "AbortSignal timeout";
6267 case Timeout::Reason::eDelayedWebTaskTimeout:
6268 return "delayedWebTaskCallback handler (timed out)";
6269 default:
6270 MOZ_CRASH("Unexpected enum value");
6271 return "";
6273 MOZ_CRASH("Unexpected enum value");
6274 return "";
6277 bool nsGlobalWindowInner::RunTimeoutHandler(Timeout* aTimeout,
6278 nsIScriptContext* aScx) {
6279 // Hold on to the timeout in case mExpr or mFunObj releases its
6280 // doc.
6281 // XXXbz Our caller guarantees it'll hold on to the timeout (because
6282 // we're MOZ_CAN_RUN_SCRIPT), so we can probably stop doing that...
6283 RefPtr<Timeout> timeout = aTimeout;
6284 Timeout* last_running_timeout = mTimeoutManager->BeginRunningTimeout(timeout);
6285 timeout->mRunning = true;
6287 // Push this timeout's popup control state, which should only be
6288 // enabled the first time a timeout fires that was created while
6289 // popups were enabled and with a delay less than
6290 // "dom.disable_open_click_delay".
6291 AutoPopupStatePusher popupStatePusher(timeout->mPopupState);
6293 // Clear the timeout's popup state, if any, to prevent interval
6294 // timeouts from repeatedly opening poups.
6295 timeout->mPopupState = PopupBlocker::openAbused;
6297 uint32_t nestingLevel = TimeoutManager::GetNestingLevel();
6298 TimeoutManager::SetNestingLevel(timeout->mNestingLevel);
6300 const char* reason = GetTimeoutReasonString(timeout);
6302 nsCString str;
6303 if (profiler_thread_is_being_profiled_for_markers()) {
6304 TimeDuration originalInterval = timeout->When() - timeout->SubmitTime();
6305 str.Append(reason);
6306 str.Append(" with interval ");
6307 str.AppendInt(int(originalInterval.ToMilliseconds()));
6308 str.Append("ms: ");
6309 nsCString handlerDescription;
6310 timeout->mScriptHandler->GetDescription(handlerDescription);
6311 str.Append(handlerDescription);
6313 AUTO_PROFILER_MARKER_TEXT("setTimeout callback", DOM,
6314 MarkerOptions(MarkerStack::TakeBacktrace(
6315 timeout->TakeProfilerBacktrace()),
6316 MarkerInnerWindowId(mWindowID)),
6317 str);
6319 bool abortIntervalHandler;
6321 RefPtr<TimeoutHandler> handler(timeout->mScriptHandler);
6323 CallbackDebuggerNotificationGuard guard(
6324 this, timeout->mIsInterval
6325 ? DebuggerNotificationType::SetIntervalCallback
6326 : DebuggerNotificationType::SetTimeoutCallback);
6327 abortIntervalHandler = !handler->Call(reason);
6330 // If we received an uncatchable exception, do not schedule the timeout again.
6331 // This allows the slow script dialog to break easy DoS attacks like
6332 // setInterval(function() { while(1); }, 100);
6333 if (abortIntervalHandler) {
6334 // If it wasn't an interval timer to begin with, this does nothing. If it
6335 // was, we'll treat it as a timeout that we just ran and discard it when
6336 // we return.
6337 timeout->mIsInterval = false;
6340 // We ignore any failures from calling EvaluateString() on the context or
6341 // Call() on a Function here since we're in a loop
6342 // where we're likely to be running timeouts whose OS timers
6343 // didn't fire in time and we don't want to not fire those timers
6344 // now just because execution of one timer failed. We can't
6345 // propagate the error to anyone who cares about it from this
6346 // point anyway, and the script context should have already reported
6347 // the script error in the usual way - so we just drop it.
6349 TimeoutManager::SetNestingLevel(nestingLevel);
6351 mTimeoutManager->EndRunningTimeout(last_running_timeout);
6352 timeout->mRunning = false;
6354 return timeout->mCleared;
6357 //*****************************************************************************
6358 // nsGlobalWindowInner: Helper Functions
6359 //*****************************************************************************
6361 already_AddRefed<nsIDocShellTreeOwner> nsGlobalWindowInner::GetTreeOwner() {
6362 FORWARD_TO_OUTER(GetTreeOwner, (), nullptr);
6365 already_AddRefed<nsIWebBrowserChrome>
6366 nsGlobalWindowInner::GetWebBrowserChrome() {
6367 nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
6369 nsCOMPtr<nsIWebBrowserChrome> browserChrome = do_GetInterface(treeOwner);
6370 return browserChrome.forget();
6373 nsIScrollableFrame* nsGlobalWindowInner::GetScrollFrame() {
6374 FORWARD_TO_OUTER(GetScrollFrame, (), nullptr);
6377 bool nsGlobalWindowInner::IsPrivateBrowsing() {
6378 nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(GetDocShell());
6379 return loadContext && loadContext->UsePrivateBrowsing();
6382 void nsGlobalWindowInner::FlushPendingNotifications(FlushType aType) {
6383 if (mDoc) {
6384 mDoc->FlushPendingNotifications(aType);
6388 void nsGlobalWindowInner::EnableDeviceSensor(uint32_t aType) {
6389 bool alreadyEnabled = false;
6390 for (uint32_t i = 0; i < mEnabledSensors.Length(); i++) {
6391 if (mEnabledSensors[i] == aType) {
6392 alreadyEnabled = true;
6393 break;
6397 mEnabledSensors.AppendElement(aType);
6399 if (alreadyEnabled) {
6400 return;
6403 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
6404 if (ac) {
6405 ac->AddWindowListener(aType, this);
6409 void nsGlobalWindowInner::DisableDeviceSensor(uint32_t aType) {
6410 int32_t doomedElement = -1;
6411 int32_t listenerCount = 0;
6412 for (uint32_t i = 0; i < mEnabledSensors.Length(); i++) {
6413 if (mEnabledSensors[i] == aType) {
6414 doomedElement = i;
6415 listenerCount++;
6419 if (doomedElement == -1) {
6420 return;
6423 mEnabledSensors.RemoveElementAt(doomedElement);
6425 if (listenerCount > 1) {
6426 return;
6429 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
6430 if (ac) {
6431 ac->RemoveWindowListener(aType, this);
6435 #if defined(MOZ_WIDGET_ANDROID)
6436 void nsGlobalWindowInner::EnableOrientationChangeListener() {
6437 if (!ShouldResistFingerprinting(RFPTarget::ScreenOrientation)) {
6438 mHasOrientationChangeListeners = true;
6439 mOrientationAngle = Orientation(CallerType::System);
6443 void nsGlobalWindowInner::DisableOrientationChangeListener() {
6444 mHasOrientationChangeListeners = false;
6446 #endif
6448 void nsGlobalWindowInner::SetHasGamepadEventListener(
6449 bool aHasGamepad /* = true*/) {
6450 mHasGamepad = aHasGamepad;
6451 if (aHasGamepad) {
6452 EnableGamepadUpdates();
6456 void nsGlobalWindowInner::NotifyDetectXRRuntimesCompleted() {
6457 if (!mXRRuntimeDetectionInFlight) {
6458 return;
6460 mXRRuntimeDetectionInFlight = false;
6461 if (mXRPermissionRequestInFlight) {
6462 return;
6464 gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
6465 bool supported = vm->RuntimeSupportsVR();
6466 if (!supported) {
6467 // A VR runtime was not installed; we can suppress
6468 // the permission prompt
6469 OnXRPermissionRequestCancel();
6470 return;
6472 // A VR runtime was found. Display a permission prompt before
6473 // allowing it to be accessed.
6474 // Connect to the VRManager in order to receive the runtime
6475 // detection results.
6476 mXRPermissionRequestInFlight = true;
6477 RefPtr<XRPermissionRequest> request =
6478 new XRPermissionRequest(this, WindowID());
6479 Unused << NS_WARN_IF(NS_FAILED(request->Start()));
6482 void nsGlobalWindowInner::RequestXRPermission() {
6483 if (IsDying()) {
6484 // Do not proceed if the window is dying, as that will result
6485 // in leaks of objects that get re-allocated after FreeInnerObjects
6486 // has been called, including mVREventObserver.
6487 return;
6489 if (mXRPermissionGranted) {
6490 // Don't prompt redundantly once permission to
6491 // access XR devices has been granted.
6492 OnXRPermissionRequestAllow();
6493 return;
6495 if (mXRRuntimeDetectionInFlight || mXRPermissionRequestInFlight) {
6496 // Don't allow multiple simultaneous permissions requests;
6497 return;
6499 // Before displaying a permission prompt, detect
6500 // if there is any VR runtime installed.
6501 gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
6502 mXRRuntimeDetectionInFlight = true;
6503 EnableVRUpdates();
6504 vm->DetectRuntimes();
6507 void nsGlobalWindowInner::OnXRPermissionRequestAllow() {
6508 mXRPermissionRequestInFlight = false;
6509 if (IsDying()) {
6510 // The window may have started dying while the permission request
6511 // is in flight.
6512 // Do not proceed if the window is dying, as that will result
6513 // in leaks of objects that get re-allocated after FreeInnerObjects
6514 // has been called, including mNavigator.
6515 return;
6517 mXRPermissionGranted = true;
6519 NotifyHasXRSession();
6521 dom::Navigator* nav = Navigator();
6522 MOZ_ASSERT(nav != nullptr);
6523 nav->OnXRPermissionRequestAllow();
6526 void nsGlobalWindowInner::OnXRPermissionRequestCancel() {
6527 mXRPermissionRequestInFlight = false;
6528 if (IsDying()) {
6529 // The window may have started dying while the permission request
6530 // is in flight.
6531 // Do not proceed if the window is dying, as that will result
6532 // in leaks of objects that get re-allocated after FreeInnerObjects
6533 // has been called, including mNavigator.
6534 return;
6536 dom::Navigator* nav = Navigator();
6537 MOZ_ASSERT(nav != nullptr);
6538 nav->OnXRPermissionRequestCancel();
6541 void nsGlobalWindowInner::EventListenerAdded(nsAtom* aType) {
6542 if (aType == nsGkAtoms::onvrdisplayactivate ||
6543 aType == nsGkAtoms::onvrdisplayconnect ||
6544 aType == nsGkAtoms::onvrdisplaydeactivate ||
6545 aType == nsGkAtoms::onvrdisplaydisconnect ||
6546 aType == nsGkAtoms::onvrdisplaypresentchange) {
6547 RequestXRPermission();
6550 if (aType == nsGkAtoms::onvrdisplayactivate) {
6551 mHasVRDisplayActivateEvents = true;
6554 if (aType == nsGkAtoms::onunload && mWindowGlobalChild) {
6555 if (++mUnloadOrBeforeUnloadListenerCount == 1) {
6556 mWindowGlobalChild->BlockBFCacheFor(BFCacheStatus::UNLOAD_LISTENER);
6560 if (aType == nsGkAtoms::onbeforeunload && mWindowGlobalChild) {
6561 if (!mozilla::SessionHistoryInParent() ||
6562 !StaticPrefs::
6563 docshell_shistory_bfcache_ship_allow_beforeunload_listeners()) {
6564 if (++mUnloadOrBeforeUnloadListenerCount == 1) {
6565 mWindowGlobalChild->BlockBFCacheFor(
6566 BFCacheStatus::BEFOREUNLOAD_LISTENER);
6569 if (!mDoc || !(mDoc->GetSandboxFlags() & SANDBOXED_MODALS)) {
6570 mWindowGlobalChild->BeforeUnloadAdded();
6571 MOZ_ASSERT(mWindowGlobalChild->BeforeUnloadListeners() > 0);
6575 // We need to initialize localStorage in order to receive notifications.
6576 if (aType == nsGkAtoms::onstorage) {
6577 ErrorResult rv;
6578 GetLocalStorage(rv);
6579 rv.SuppressException();
6581 if (NextGenLocalStorageEnabled() && mLocalStorage &&
6582 mLocalStorage->Type() == Storage::eLocalStorage) {
6583 auto object = static_cast<LSObject*>(mLocalStorage.get());
6585 Unused << NS_WARN_IF(NS_FAILED(object->EnsureObserver()));
6590 void nsGlobalWindowInner::EventListenerRemoved(nsAtom* aType) {
6591 if (aType == nsGkAtoms::onunload && mWindowGlobalChild) {
6592 MOZ_ASSERT(mUnloadOrBeforeUnloadListenerCount > 0);
6593 if (--mUnloadOrBeforeUnloadListenerCount == 0) {
6594 mWindowGlobalChild->UnblockBFCacheFor(BFCacheStatus::UNLOAD_LISTENER);
6598 if (aType == nsGkAtoms::onbeforeunload && mWindowGlobalChild) {
6599 if (!mozilla::SessionHistoryInParent() ||
6600 !StaticPrefs::
6601 docshell_shistory_bfcache_ship_allow_beforeunload_listeners()) {
6602 if (--mUnloadOrBeforeUnloadListenerCount == 0) {
6603 mWindowGlobalChild->UnblockBFCacheFor(
6604 BFCacheStatus::BEFOREUNLOAD_LISTENER);
6607 if (!mDoc || !(mDoc->GetSandboxFlags() & SANDBOXED_MODALS)) {
6608 mWindowGlobalChild->BeforeUnloadRemoved();
6609 MOZ_ASSERT(mWindowGlobalChild->BeforeUnloadListeners() >= 0);
6613 if (aType == nsGkAtoms::onstorage) {
6614 if (NextGenLocalStorageEnabled() && mLocalStorage &&
6615 mLocalStorage->Type() == Storage::eLocalStorage &&
6616 // The remove event is fired even if this isn't the last listener, so
6617 // only remove if there are no other listeners left.
6618 mListenerManager &&
6619 !mListenerManager->HasListenersFor(nsGkAtoms::onstorage)) {
6620 auto object = static_cast<LSObject*>(mLocalStorage.get());
6622 object->DropObserver();
6627 void nsGlobalWindowInner::NotifyHasXRSession() {
6628 if (IsDying()) {
6629 // Do not proceed if the window is dying, as that will result
6630 // in leaks of objects that get re-allocated after FreeInnerObjects
6631 // has been called, including mVREventObserver.
6632 return;
6634 if (mWindowGlobalChild && !mHasXRSession) {
6635 mWindowGlobalChild->BlockBFCacheFor(BFCacheStatus::HAS_USED_VR);
6637 mHasXRSession = true;
6638 EnableVRUpdates();
6641 bool nsGlobalWindowInner::HasUsedVR() const {
6642 // Returns true only if content has enumerated and activated
6643 // XR devices. Detection of XR runtimes without activation
6644 // will not cause true to be returned.
6645 return mHasXRSession;
6648 bool nsGlobalWindowInner::IsVRContentDetected() const {
6649 // Returns true only if the content will respond to
6650 // the VRDisplayActivate event.
6651 return mHasVRDisplayActivateEvents;
6654 bool nsGlobalWindowInner::IsVRContentPresenting() const {
6655 for (const auto& display : mVRDisplays) {
6656 if (display->IsAnyPresenting(gfx::kVRGroupAll)) {
6657 return true;
6660 return false;
6663 void nsGlobalWindowInner::AddSizeOfIncludingThis(
6664 nsWindowSizes& aWindowSizes) const {
6665 aWindowSizes.mDOMSizes.mDOMOtherSize +=
6666 aWindowSizes.mState.mMallocSizeOf(this);
6667 aWindowSizes.mDOMSizes.mDOMOtherSize +=
6668 nsIGlobalObject::ShallowSizeOfExcludingThis(
6669 aWindowSizes.mState.mMallocSizeOf);
6671 EventListenerManager* elm = GetExistingListenerManager();
6672 if (elm) {
6673 aWindowSizes.mDOMSizes.mDOMOtherSize +=
6674 elm->SizeOfIncludingThis(aWindowSizes.mState.mMallocSizeOf);
6675 aWindowSizes.mDOMEventListenersCount += elm->ListenerCount();
6677 if (mDoc) {
6678 // Multiple global windows can share a document. So only measure the
6679 // document if it (a) doesn't have a global window, or (b) it's the
6680 // primary document for the window.
6681 if (!mDoc->GetInnerWindow() || mDoc->GetInnerWindow() == this) {
6682 mDoc->DocAddSizeOfIncludingThis(aWindowSizes);
6686 if (mNavigator) {
6687 aWindowSizes.mDOMSizes.mDOMOtherSize +=
6688 mNavigator->SizeOfIncludingThis(aWindowSizes.mState.mMallocSizeOf);
6691 ForEachGlobalTeardownObserver([&](GlobalTeardownObserver* et,
6692 bool* aDoneOut) {
6693 if (nsCOMPtr<nsISizeOfEventTarget> iSizeOf = do_QueryObject(et)) {
6694 aWindowSizes.mDOMSizes.mDOMEventTargetsSize +=
6695 iSizeOf->SizeOfEventTargetIncludingThis(
6696 aWindowSizes.mState.mMallocSizeOf);
6698 if (nsCOMPtr<DOMEventTargetHelper> helper = do_QueryObject(et)) {
6699 if (EventListenerManager* elm = helper->GetExistingListenerManager()) {
6700 aWindowSizes.mDOMEventListenersCount += elm->ListenerCount();
6703 ++aWindowSizes.mDOMEventTargetsCount;
6706 if (mPerformance) {
6707 aWindowSizes.mDOMSizes.mDOMPerformanceUserEntries =
6708 mPerformance->SizeOfUserEntries(aWindowSizes.mState.mMallocSizeOf);
6709 aWindowSizes.mDOMSizes.mDOMPerformanceResourceEntries =
6710 mPerformance->SizeOfResourceEntries(aWindowSizes.mState.mMallocSizeOf);
6711 aWindowSizes.mDOMSizes.mDOMPerformanceEventEntries =
6712 mPerformance->SizeOfEventEntries(aWindowSizes.mState.mMallocSizeOf);
6716 void nsGlobalWindowInner::RegisterDataDocumentForMemoryReporting(
6717 Document* aDocument) {
6718 aDocument->SetAddedToMemoryReportAsDataDocument();
6719 mDataDocumentsForMemoryReporting.AppendElement(aDocument);
6722 void nsGlobalWindowInner::UnregisterDataDocumentForMemoryReporting(
6723 Document* aDocument) {
6724 DebugOnly<bool> found =
6725 mDataDocumentsForMemoryReporting.RemoveElement(aDocument);
6726 MOZ_ASSERT(found);
6729 void nsGlobalWindowInner::CollectDOMSizesForDataDocuments(
6730 nsWindowSizes& aSize) const {
6731 for (Document* doc : mDataDocumentsForMemoryReporting) {
6732 if (doc) {
6733 doc->DocAddSizeOfIncludingThis(aSize);
6738 void nsGlobalWindowInner::AddGamepad(GamepadHandle aHandle, Gamepad* aGamepad) {
6739 // Create the index we will present to content based on which indices are
6740 // already taken, as required by the spec.
6741 // https://w3c.github.io/gamepad/gamepad.html#widl-Gamepad-index
6742 int index = 0;
6743 while (mGamepadIndexSet.Contains(index)) {
6744 ++index;
6746 mGamepadIndexSet.Put(index);
6747 aGamepad->SetIndex(index);
6748 mGamepads.InsertOrUpdate(aHandle, RefPtr{aGamepad});
6751 void nsGlobalWindowInner::RemoveGamepad(GamepadHandle aHandle) {
6752 RefPtr<Gamepad> gamepad;
6753 if (!mGamepads.Get(aHandle, getter_AddRefs(gamepad))) {
6754 return;
6756 // Free up the index we were using so it can be reused
6757 mGamepadIndexSet.Remove(gamepad->Index());
6758 mGamepads.Remove(aHandle);
6761 void nsGlobalWindowInner::GetGamepads(nsTArray<RefPtr<Gamepad>>& aGamepads) {
6762 aGamepads.Clear();
6764 // navigator.getGamepads() always returns an empty array when
6765 // privacy.resistFingerprinting is true.
6766 if (ShouldResistFingerprinting(RFPTarget::Gamepad)) {
6767 return;
6770 // mGamepads.Count() may not be sufficient, but it's not harmful.
6771 aGamepads.SetCapacity(mGamepads.Count());
6772 for (const auto& entry : mGamepads) {
6773 Gamepad* gamepad = entry.GetWeak();
6774 aGamepads.EnsureLengthAtLeast(gamepad->Index() + 1);
6775 aGamepads[gamepad->Index()] = gamepad;
6779 already_AddRefed<Gamepad> nsGlobalWindowInner::GetGamepad(
6780 GamepadHandle aHandle) {
6781 RefPtr<Gamepad> gamepad;
6783 if (mGamepads.Get(aHandle, getter_AddRefs(gamepad))) {
6784 return gamepad.forget();
6787 return nullptr;
6790 void nsGlobalWindowInner::SetHasSeenGamepadInput(bool aHasSeen) {
6791 mHasSeenGamepadInput = aHasSeen;
6794 bool nsGlobalWindowInner::HasSeenGamepadInput() { return mHasSeenGamepadInput; }
6796 void nsGlobalWindowInner::SyncGamepadState() {
6797 if (mHasSeenGamepadInput) {
6798 RefPtr<GamepadManager> gamepadManager(GamepadManager::GetService());
6799 for (const auto& entry : mGamepads) {
6800 gamepadManager->SyncGamepadState(entry.GetKey(), this, entry.GetWeak());
6805 void nsGlobalWindowInner::StopGamepadHaptics() {
6806 if (mHasSeenGamepadInput) {
6807 RefPtr<GamepadManager> gamepadManager(GamepadManager::GetService());
6808 gamepadManager->StopHaptics();
6812 bool nsGlobalWindowInner::UpdateVRDisplays(
6813 nsTArray<RefPtr<mozilla::dom::VRDisplay>>& aDevices) {
6814 VRDisplay::UpdateVRDisplays(mVRDisplays, this);
6815 aDevices = mVRDisplays.Clone();
6816 return true;
6819 void nsGlobalWindowInner::NotifyActiveVRDisplaysChanged() {
6820 if (mNavigator) {
6821 mNavigator->NotifyActiveVRDisplaysChanged();
6825 void nsGlobalWindowInner::NotifyPresentationGenerationChanged(
6826 uint32_t aDisplayID) {
6827 for (const auto& display : mVRDisplays) {
6828 if (display->DisplayId() == aDisplayID) {
6829 display->OnPresentationGenerationChanged();
6834 void nsGlobalWindowInner::DispatchVRDisplayActivate(
6835 uint32_t aDisplayID, mozilla::dom::VRDisplayEventReason aReason) {
6836 // Ensure that our list of displays is up to date
6837 VRDisplay::UpdateVRDisplays(mVRDisplays, this);
6839 // Search for the display identified with aDisplayID and fire the
6840 // event if found.
6841 for (const auto& display : mVRDisplays) {
6842 if (display->DisplayId() == aDisplayID) {
6843 if (aReason != VRDisplayEventReason::Navigation &&
6844 display->IsAnyPresenting(gfx::kVRGroupContent)) {
6845 // We only want to trigger this event if nobody is presenting to the
6846 // display already or when a page is loaded by navigating away
6847 // from a page with an active VR Presentation.
6848 continue;
6851 VRDisplayEventInit init;
6852 init.mBubbles = false;
6853 init.mCancelable = false;
6854 init.mDisplay = display;
6855 init.mReason.Construct(aReason);
6857 RefPtr<VRDisplayEvent> event =
6858 VRDisplayEvent::Constructor(this, u"vrdisplayactivate"_ns, init);
6859 // vrdisplayactivate is a trusted event, allowing VRDisplay.requestPresent
6860 // to be used in response to link traversal, user request (chrome UX), and
6861 // HMD mounting detection sensors.
6862 event->SetTrusted(true);
6863 // VRDisplay.requestPresent normally requires a user gesture; however, an
6864 // exception is made to allow it to be called in response to
6865 // vrdisplayactivate during VR link traversal.
6866 display->StartHandlingVRNavigationEvent();
6867 DispatchEvent(*event);
6868 display->StopHandlingVRNavigationEvent();
6869 // Once we dispatch the event, we must not access any members as an event
6870 // listener can do anything, including closing windows.
6871 return;
6876 void nsGlobalWindowInner::DispatchVRDisplayDeactivate(
6877 uint32_t aDisplayID, mozilla::dom::VRDisplayEventReason aReason) {
6878 // Ensure that our list of displays is up to date
6879 VRDisplay::UpdateVRDisplays(mVRDisplays, this);
6881 // Search for the display identified with aDisplayID and fire the
6882 // event if found.
6883 for (const auto& display : mVRDisplays) {
6884 if (display->DisplayId() == aDisplayID && display->IsPresenting()) {
6885 // We only want to trigger this event to content that is presenting to
6886 // the display already.
6888 VRDisplayEventInit init;
6889 init.mBubbles = false;
6890 init.mCancelable = false;
6891 init.mDisplay = display;
6892 init.mReason.Construct(aReason);
6894 RefPtr<VRDisplayEvent> event =
6895 VRDisplayEvent::Constructor(this, u"vrdisplaydeactivate"_ns, init);
6896 event->SetTrusted(true);
6897 DispatchEvent(*event);
6898 // Once we dispatch the event, we must not access any members as an event
6899 // listener can do anything, including closing windows.
6900 return;
6905 void nsGlobalWindowInner::DispatchVRDisplayConnect(uint32_t aDisplayID) {
6906 // Ensure that our list of displays is up to date
6907 VRDisplay::UpdateVRDisplays(mVRDisplays, this);
6909 // Search for the display identified with aDisplayID and fire the
6910 // event if found.
6911 for (const auto& display : mVRDisplays) {
6912 if (display->DisplayId() == aDisplayID) {
6913 // Fire event even if not presenting to the display.
6914 VRDisplayEventInit init;
6915 init.mBubbles = false;
6916 init.mCancelable = false;
6917 init.mDisplay = display;
6918 // VRDisplayEvent.reason is not set for vrdisplayconnect
6920 RefPtr<VRDisplayEvent> event =
6921 VRDisplayEvent::Constructor(this, u"vrdisplayconnect"_ns, init);
6922 event->SetTrusted(true);
6923 DispatchEvent(*event);
6924 // Once we dispatch the event, we must not access any members as an event
6925 // listener can do anything, including closing windows.
6926 return;
6931 void nsGlobalWindowInner::DispatchVRDisplayDisconnect(uint32_t aDisplayID) {
6932 // Ensure that our list of displays is up to date
6933 VRDisplay::UpdateVRDisplays(mVRDisplays, this);
6935 // Search for the display identified with aDisplayID and fire the
6936 // event if found.
6937 for (const auto& display : mVRDisplays) {
6938 if (display->DisplayId() == aDisplayID) {
6939 // Fire event even if not presenting to the display.
6940 VRDisplayEventInit init;
6941 init.mBubbles = false;
6942 init.mCancelable = false;
6943 init.mDisplay = display;
6944 // VRDisplayEvent.reason is not set for vrdisplaydisconnect
6946 RefPtr<VRDisplayEvent> event =
6947 VRDisplayEvent::Constructor(this, u"vrdisplaydisconnect"_ns, init);
6948 event->SetTrusted(true);
6949 DispatchEvent(*event);
6950 // Once we dispatch the event, we must not access any members as an event
6951 // listener can do anything, including closing windows.
6952 return;
6957 void nsGlobalWindowInner::DispatchVRDisplayPresentChange(uint32_t aDisplayID) {
6958 // Ensure that our list of displays is up to date
6959 VRDisplay::UpdateVRDisplays(mVRDisplays, this);
6961 // Search for the display identified with aDisplayID and fire the
6962 // event if found.
6963 for (const auto& display : mVRDisplays) {
6964 if (display->DisplayId() == aDisplayID) {
6965 // Fire event even if not presenting to the display.
6966 VRDisplayEventInit init;
6967 init.mBubbles = false;
6968 init.mCancelable = false;
6969 init.mDisplay = display;
6970 // VRDisplayEvent.reason is not set for vrdisplaypresentchange
6971 RefPtr<VRDisplayEvent> event =
6972 VRDisplayEvent::Constructor(this, u"vrdisplaypresentchange"_ns, init);
6973 event->SetTrusted(true);
6974 DispatchEvent(*event);
6975 // Once we dispatch the event, we must not access any members as an event
6976 // listener can do anything, including closing windows.
6977 return;
6982 enum WindowState {
6983 // These constants need to match the constants in Window.webidl
6984 STATE_MAXIMIZED = 1,
6985 STATE_MINIMIZED = 2,
6986 STATE_NORMAL = 3,
6987 STATE_FULLSCREEN = 4
6990 uint16_t nsGlobalWindowInner::WindowState() {
6991 nsCOMPtr<nsIWidget> widget = GetMainWidget();
6993 int32_t mode = widget ? widget->SizeMode() : 0;
6995 switch (mode) {
6996 case nsSizeMode_Minimized:
6997 return STATE_MINIMIZED;
6998 case nsSizeMode_Maximized:
6999 return STATE_MAXIMIZED;
7000 case nsSizeMode_Fullscreen:
7001 return STATE_FULLSCREEN;
7002 case nsSizeMode_Normal:
7003 return STATE_NORMAL;
7004 default:
7005 NS_WARNING("Illegal window state for this chrome window");
7006 break;
7009 return STATE_NORMAL;
7012 bool nsGlobalWindowInner::IsFullyOccluded() {
7013 nsCOMPtr<nsIWidget> widget = GetMainWidget();
7014 return widget && widget->IsFullyOccluded();
7017 void nsGlobalWindowInner::Maximize() {
7018 if (nsCOMPtr<nsIWidget> widget = GetMainWidget()) {
7019 widget->SetSizeMode(nsSizeMode_Maximized);
7023 void nsGlobalWindowInner::Minimize() {
7024 if (nsCOMPtr<nsIWidget> widget = GetMainWidget()) {
7025 widget->SetSizeMode(nsSizeMode_Minimized);
7029 void nsGlobalWindowInner::Restore() {
7030 if (nsCOMPtr<nsIWidget> widget = GetMainWidget()) {
7031 widget->SetSizeMode(nsSizeMode_Normal);
7035 void nsGlobalWindowInner::GetWorkspaceID(nsAString& workspaceID) {
7036 workspaceID.Truncate();
7037 if (nsCOMPtr<nsIWidget> widget = GetMainWidget()) {
7038 return widget->GetWorkspaceID(workspaceID);
7042 void nsGlobalWindowInner::MoveToWorkspace(const nsAString& workspaceID) {
7043 if (nsCOMPtr<nsIWidget> widget = GetMainWidget()) {
7044 widget->MoveToWorkspace(workspaceID);
7048 void nsGlobalWindowInner::GetAttention(ErrorResult& aResult) {
7049 return GetAttentionWithCycleCount(-1, aResult);
7052 void nsGlobalWindowInner::GetAttentionWithCycleCount(int32_t aCycleCount,
7053 ErrorResult& aError) {
7054 nsCOMPtr<nsIWidget> widget = GetMainWidget();
7056 if (widget) {
7057 aError = widget->GetAttention(aCycleCount);
7061 already_AddRefed<Promise> nsGlobalWindowInner::PromiseDocumentFlushed(
7062 PromiseDocumentFlushedCallback& aCallback, ErrorResult& aError) {
7063 MOZ_RELEASE_ASSERT(IsChromeWindow());
7065 if (!IsCurrentInnerWindow()) {
7066 aError.ThrowInvalidStateError("Not the current inner window");
7067 return nullptr;
7069 if (!mDoc) {
7070 aError.ThrowInvalidStateError("No document");
7071 return nullptr;
7074 if (mIteratingDocumentFlushedResolvers) {
7075 aError.ThrowInvalidStateError("Already iterating through resolvers");
7076 return nullptr;
7079 PresShell* presShell = mDoc->GetPresShell();
7080 if (!presShell) {
7081 aError.ThrowInvalidStateError("No pres shell");
7082 return nullptr;
7085 // We need to associate the lifetime of the Promise to the lifetime
7086 // of the caller's global. That way, if the window we're observing
7087 // refresh driver ticks on goes away before our observer is fired,
7088 // we can still resolve the Promise.
7089 nsIGlobalObject* global = GetIncumbentGlobal();
7090 if (!global) {
7091 aError.ThrowInvalidStateError("No incumbent global");
7092 return nullptr;
7095 RefPtr<Promise> resultPromise = Promise::Create(global, aError);
7096 if (aError.Failed()) {
7097 return nullptr;
7100 UniquePtr<PromiseDocumentFlushedResolver> flushResolver(
7101 new PromiseDocumentFlushedResolver(resultPromise, aCallback));
7103 if (!presShell->NeedStyleFlush() && !presShell->NeedLayoutFlush()) {
7104 flushResolver->Call();
7105 return resultPromise.forget();
7108 if (!TryToObserveRefresh()) {
7109 aError.ThrowInvalidStateError("Couldn't observe refresh");
7110 return nullptr;
7113 mDocumentFlushedResolvers.AppendElement(std::move(flushResolver));
7114 return resultPromise.forget();
7117 bool nsGlobalWindowInner::TryToObserveRefresh() {
7118 if (mObservingRefresh) {
7119 return true;
7122 if (!mDoc) {
7123 return false;
7126 nsPresContext* pc = mDoc->GetPresContext();
7127 if (!pc) {
7128 return false;
7131 mObservingRefresh = true;
7132 auto observer = MakeRefPtr<ManagedPostRefreshObserver>(
7133 pc, [win = RefPtr{this}](bool aWasCanceled) {
7134 if (win->MaybeCallDocumentFlushedResolvers(
7135 /* aUntilExhaustion = */ aWasCanceled)) {
7136 return ManagedPostRefreshObserver::Unregister::No;
7138 win->mObservingRefresh = false;
7139 return ManagedPostRefreshObserver::Unregister::Yes;
7141 pc->RegisterManagedPostRefreshObserver(observer.get());
7142 return mObservingRefresh;
7145 void nsGlobalWindowInner::CallDocumentFlushedResolvers(bool aUntilExhaustion) {
7146 while (true) {
7148 // To coalesce MicroTask checkpoints inside callback call, enclose the
7149 // inner loop with nsAutoMicroTask, and perform a MicroTask checkpoint
7150 // after the loop.
7151 nsAutoMicroTask mt;
7153 mIteratingDocumentFlushedResolvers = true;
7155 auto resolvers = std::move(mDocumentFlushedResolvers);
7156 for (const auto& resolver : resolvers) {
7157 resolver->Call();
7160 mIteratingDocumentFlushedResolvers = false;
7163 // Leaving nsAutoMicroTask above will perform MicroTask checkpoint, and
7164 // Promise callbacks there may create mDocumentFlushedResolvers items.
7166 // If there's no new resolvers, or we're not exhausting the queue, there's
7167 // nothing to do (we'll keep observing if there's any new observer).
7169 // Otherwise, keep looping to call all promises. This case can happen while
7170 // destroying the window. This violates the constraint that the
7171 // promiseDocumentFlushed callback only ever run when no flush is needed,
7172 // but it's necessary to resolve the Promise returned by that.
7173 if (!aUntilExhaustion || mDocumentFlushedResolvers.IsEmpty()) {
7174 break;
7179 bool nsGlobalWindowInner::MaybeCallDocumentFlushedResolvers(
7180 bool aUntilExhaustion) {
7181 MOZ_ASSERT(mDoc);
7183 PresShell* presShell = mDoc->GetPresShell();
7184 if (!presShell || aUntilExhaustion) {
7185 CallDocumentFlushedResolvers(/* aUntilExhaustion = */ true);
7186 return false;
7189 if (presShell->NeedStyleFlush() || presShell->NeedLayoutFlush()) {
7190 // By the time our observer fired, something has already invalidated
7191 // style or layout - or perhaps we're still in the middle of a flush that
7192 // was interrupted. In either case, we'll wait until the next refresh driver
7193 // tick instead and try again.
7194 return true;
7197 CallDocumentFlushedResolvers(/* aUntilExhaustion = */ false);
7198 return !mDocumentFlushedResolvers.IsEmpty();
7201 already_AddRefed<nsWindowRoot> nsGlobalWindowInner::GetWindowRoot(
7202 mozilla::ErrorResult& aError) {
7203 FORWARD_TO_OUTER_OR_THROW(GetWindowRootOuter, (), aError, nullptr);
7206 void nsGlobalWindowInner::SetCursor(const nsACString& aCursor,
7207 ErrorResult& aError) {
7208 FORWARD_TO_OUTER_OR_THROW(SetCursorOuter, (aCursor, aError), aError, );
7211 nsIBrowserDOMWindow* nsGlobalWindowInner::GetBrowserDOMWindow(
7212 ErrorResult& aError) {
7213 FORWARD_TO_OUTER_OR_THROW(GetBrowserDOMWindow, (), aError, nullptr);
7216 void nsGlobalWindowInner::SetBrowserDOMWindow(
7217 nsIBrowserDOMWindow* aBrowserWindow, ErrorResult& aError) {
7218 FORWARD_TO_OUTER_OR_THROW(SetBrowserDOMWindowOuter, (aBrowserWindow),
7219 aError, );
7222 void nsGlobalWindowInner::NotifyDefaultButtonLoaded(Element& aDefaultButton,
7223 ErrorResult& aError) {
7224 // Don't snap to a disabled button.
7225 nsCOMPtr<nsIDOMXULControlElement> xulControl = aDefaultButton.AsXULControl();
7226 if (!xulControl) {
7227 aError.Throw(NS_ERROR_FAILURE);
7228 return;
7230 bool disabled;
7231 aError = xulControl->GetDisabled(&disabled);
7232 if (aError.Failed() || disabled) {
7233 return;
7236 // Get the button rect in screen coordinates.
7237 nsIFrame* frame = aDefaultButton.GetPrimaryFrame();
7238 if (!frame) {
7239 aError.Throw(NS_ERROR_FAILURE);
7240 return;
7242 LayoutDeviceIntRect buttonRect = LayoutDeviceIntRect::FromAppUnitsToNearest(
7243 frame->GetScreenRectInAppUnits(),
7244 frame->PresContext()->AppUnitsPerDevPixel());
7246 // Get the widget rect in screen coordinates.
7247 nsIWidget* widget = GetNearestWidget();
7248 if (!widget) {
7249 aError.Throw(NS_ERROR_FAILURE);
7250 return;
7252 LayoutDeviceIntRect widgetRect = widget->GetScreenBounds();
7254 // Convert the buttonRect coordinates from screen to the widget.
7255 buttonRect -= widgetRect.TopLeft();
7256 nsresult rv = widget->OnDefaultButtonLoaded(buttonRect);
7257 if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) {
7258 aError.Throw(rv);
7262 ChromeMessageBroadcaster* nsGlobalWindowInner::MessageManager() {
7263 MOZ_ASSERT(IsChromeWindow());
7264 if (!mChromeFields.mMessageManager) {
7265 RefPtr<ChromeMessageBroadcaster> globalMM =
7266 nsFrameMessageManager::GetGlobalMessageManager();
7267 mChromeFields.mMessageManager = new ChromeMessageBroadcaster(globalMM);
7269 return mChromeFields.mMessageManager;
7272 ChromeMessageBroadcaster* nsGlobalWindowInner::GetGroupMessageManager(
7273 const nsAString& aGroup) {
7274 MOZ_ASSERT(IsChromeWindow());
7276 return mChromeFields.mGroupMessageManagers
7277 .LookupOrInsertWith(
7278 aGroup,
7279 [&] {
7280 return MakeAndAddRef<ChromeMessageBroadcaster>(MessageManager());
7282 .get();
7285 void nsGlobalWindowInner::InitWasOffline() { mWasOffline = NS_IsOffline(); }
7287 int16_t nsGlobalWindowInner::Orientation(CallerType aCallerType) {
7288 // GetOrientationAngle() returns 0, 90, 180 or 270.
7289 // window.orientation returns -90, 0, 90 or 180.
7290 if (nsIGlobalObject::ShouldResistFingerprinting(
7291 aCallerType, RFPTarget::ScreenOrientation)) {
7292 return 0;
7294 int16_t angle = AssertedCast<int16_t>(Screen()->GetOrientationAngle());
7295 return angle <= 180 ? angle : angle - 360;
7298 already_AddRefed<Console> nsGlobalWindowInner::GetConsole(JSContext* aCx,
7299 ErrorResult& aRv) {
7300 if (!mConsole) {
7301 mConsole = Console::Create(aCx, this, aRv);
7302 if (NS_WARN_IF(aRv.Failed())) {
7303 return nullptr;
7307 RefPtr<Console> console = mConsole;
7308 return console.forget();
7311 bool nsGlobalWindowInner::IsSecureContext() const {
7312 JS::Realm* realm = js::GetNonCCWObjectRealm(GetWrapperPreserveColor());
7313 return JS::GetIsSecureContext(realm);
7316 External* nsGlobalWindowInner::External() {
7317 if (!mExternal) {
7318 mExternal = new dom::External(ToSupports(this));
7321 return mExternal;
7324 void nsGlobalWindowInner::ClearDocumentDependentSlots(JSContext* aCx) {
7325 // If JSAPI OOMs here, there is basically nothing we can do to recover safely.
7326 if (!Window_Binding::ClearCachedDocumentValue(aCx, this) ||
7327 !Window_Binding::ClearCachedPerformanceValue(aCx, this)) {
7328 MOZ_CRASH("Unhandlable OOM while clearing document dependent slots.");
7332 /* static */
7333 JSObject* nsGlobalWindowInner::CreateNamedPropertiesObject(
7334 JSContext* aCx, JS::Handle<JSObject*> aProto) {
7335 return WindowNamedPropertiesHandler::Create(aCx, aProto);
7338 void nsGlobalWindowInner::RedefineProperty(JSContext* aCx,
7339 const char* aPropName,
7340 JS::Handle<JS::Value> aValue,
7341 ErrorResult& aError) {
7342 JS::Rooted<JSObject*> thisObj(aCx, GetWrapperPreserveColor());
7343 if (!thisObj) {
7344 aError.Throw(NS_ERROR_UNEXPECTED);
7345 return;
7348 if (!JS_WrapObject(aCx, &thisObj) ||
7349 !JS_DefineProperty(aCx, thisObj, aPropName, aValue, JSPROP_ENUMERATE)) {
7350 aError.Throw(NS_ERROR_FAILURE);
7354 void nsGlobalWindowInner::FireOnNewGlobalObject() {
7355 // AutoEntryScript required to invoke debugger hook, which is a
7356 // Gecko-specific concept at present.
7357 AutoEntryScript aes(this, "nsGlobalWindowInner report new global");
7358 JS::Rooted<JSObject*> global(aes.cx(), GetWrapper());
7359 JS_FireOnNewGlobalObject(aes.cx(), global);
7362 #if defined(_WINDOWS_) && !defined(MOZ_WRAPPED_WINDOWS_H)
7363 # pragma message( \
7364 "wrapper failure reason: " MOZ_WINDOWS_WRAPPER_DISABLED_REASON)
7365 # error "Never include unwrapped windows.h in this file!"
7366 #endif
7368 already_AddRefed<Promise> nsGlobalWindowInner::CreateImageBitmap(
7369 const ImageBitmapSource& aImage, const ImageBitmapOptions& aOptions,
7370 ErrorResult& aRv) {
7371 return ImageBitmap::Create(this, aImage, Nothing(), aOptions, aRv);
7374 already_AddRefed<Promise> nsGlobalWindowInner::CreateImageBitmap(
7375 const ImageBitmapSource& aImage, int32_t aSx, int32_t aSy, int32_t aSw,
7376 int32_t aSh, const ImageBitmapOptions& aOptions, ErrorResult& aRv) {
7377 return ImageBitmap::Create(
7378 this, aImage, Some(gfx::IntRect(aSx, aSy, aSw, aSh)), aOptions, aRv);
7381 // https://html.spec.whatwg.org/#structured-cloning
7382 void nsGlobalWindowInner::StructuredClone(
7383 JSContext* aCx, JS::Handle<JS::Value> aValue,
7384 const StructuredSerializeOptions& aOptions,
7385 JS::MutableHandle<JS::Value> aRetval, ErrorResult& aError) {
7386 nsContentUtils::StructuredClone(aCx, this, aValue, aOptions, aRetval, aError);
7389 nsresult nsGlobalWindowInner::Dispatch(
7390 already_AddRefed<nsIRunnable>&& aRunnable) const {
7391 MOZ_RELEASE_ASSERT(NS_IsMainThread());
7392 return NS_DispatchToCurrentThread(std::move(aRunnable));
7395 nsISerialEventTarget* nsGlobalWindowInner::SerialEventTarget() const {
7396 MOZ_RELEASE_ASSERT(NS_IsMainThread());
7397 return GetMainThreadSerialEventTarget();
7400 Worklet* nsGlobalWindowInner::GetPaintWorklet(ErrorResult& aRv) {
7401 if (!mPaintWorklet) {
7402 nsIPrincipal* principal = GetPrincipal();
7403 if (!principal) {
7404 aRv.Throw(NS_ERROR_FAILURE);
7405 return nullptr;
7408 mPaintWorklet = PaintWorkletImpl::CreateWorklet(this, principal);
7411 return mPaintWorklet;
7414 void nsGlobalWindowInner::GetRegionalPrefsLocales(
7415 nsTArray<nsString>& aLocales) {
7416 MOZ_ASSERT(mozilla::intl::LocaleService::GetInstance());
7418 AutoTArray<nsCString, 10> rpLocales;
7419 mozilla::intl::LocaleService::GetInstance()->GetRegionalPrefsLocales(
7420 rpLocales);
7422 for (const auto& loc : rpLocales) {
7423 aLocales.AppendElement(NS_ConvertUTF8toUTF16(loc));
7427 void nsGlobalWindowInner::GetWebExposedLocales(nsTArray<nsString>& aLocales) {
7428 MOZ_ASSERT(mozilla::intl::LocaleService::GetInstance());
7430 AutoTArray<nsCString, 10> rpLocales;
7431 mozilla::intl::LocaleService::GetInstance()->GetWebExposedLocales(rpLocales);
7433 for (const auto& loc : rpLocales) {
7434 aLocales.AppendElement(NS_ConvertUTF8toUTF16(loc));
7438 IntlUtils* nsGlobalWindowInner::GetIntlUtils(ErrorResult& aError) {
7439 if (!mIntlUtils) {
7440 mIntlUtils = new IntlUtils(this);
7443 return mIntlUtils;
7446 void nsGlobalWindowInner::StoreSharedWorker(SharedWorker* aSharedWorker) {
7447 MOZ_ASSERT(aSharedWorker);
7448 MOZ_ASSERT(!mSharedWorkers.Contains(aSharedWorker));
7450 mSharedWorkers.AppendElement(aSharedWorker);
7453 void nsGlobalWindowInner::ForgetSharedWorker(SharedWorker* aSharedWorker) {
7454 MOZ_ASSERT(aSharedWorker);
7455 MOZ_ASSERT(mSharedWorkers.Contains(aSharedWorker));
7457 mSharedWorkers.RemoveElement(aSharedWorker);
7460 RefPtr<GenericPromise> nsGlobalWindowInner::StorageAccessPermissionChanged(
7461 bool aGranted) {
7462 // Invalidate cached StorageAllowed field so that calls to GetLocalStorage
7463 // give us the updated localStorage object.
7464 ClearStorageAllowedCache();
7466 // If we're always partitioning non-cookie third party storage then
7467 // there is no need to clear it when the user accepts requestStorageAccess.
7468 if (StaticPrefs::
7469 privacy_partition_always_partition_third_party_non_cookie_storage()) {
7470 // Just reset the active cookie and storage principals
7471 nsCOMPtr<nsICookieJarSettings> cjs;
7472 if (mDoc) {
7473 cjs = mDoc->CookieJarSettings();
7475 StorageAccess storageAccess = StorageAllowedForWindow(this);
7476 if (ShouldPartitionStorage(storageAccess) &&
7477 StoragePartitioningEnabled(storageAccess, cjs)) {
7478 if (mDoc) {
7479 mDoc->ClearActiveCookieAndStoragePrincipals();
7481 // When storage access is granted the content process needs to request the
7482 // updated cookie list from the parent process. Otherwise the site won't
7483 // have access to unpartitioned cookies via document.cookie without a
7484 // reload.
7485 if (aGranted) {
7486 nsIChannel* channel = mDoc->GetChannel();
7487 if (channel) {
7488 // The promise resolves when the updated cookie list has been received
7489 // from the parent.
7490 return ContentChild::UpdateCookieStatus(channel);
7496 PropagateStorageAccessPermissionGrantedToWorkers(*this);
7498 // If we have a partitioned localStorage, it's time to replace it with a real
7499 // one in order to receive notifications.
7501 if (mLocalStorage) {
7502 IgnoredErrorResult error;
7503 GetLocalStorage(error);
7504 if (NS_WARN_IF(error.Failed())) {
7505 return MozPromise<bool, nsresult, true>::CreateAndReject(
7506 error.StealNSResult(), __func__);
7509 MOZ_ASSERT(mLocalStorage &&
7510 mLocalStorage->Type() == Storage::eLocalStorage);
7512 if (NextGenLocalStorageEnabled() && mListenerManager &&
7513 mListenerManager->HasListenersFor(nsGkAtoms::onstorage)) {
7514 auto object = static_cast<LSObject*>(mLocalStorage.get());
7516 object->EnsureObserver();
7520 // Reset the IndexedDB factory.
7521 mIndexedDB = nullptr;
7523 // Reset DOM Cache
7524 mCacheStorage = nullptr;
7526 // Reset the active cookie and storage principals
7527 if (mDoc) {
7528 mDoc->ClearActiveCookieAndStoragePrincipals();
7529 if (mWindowGlobalChild) {
7530 // XXX(farre): This is a bit backwards, but clearing the cookie
7531 // principal might make us end up with a new effective storage
7532 // principal on the child side than on the parent side, which
7533 // means that we need to sync it. See bug 1705359.
7534 mWindowGlobalChild->SetDocumentPrincipal(
7535 mDoc->NodePrincipal(), mDoc->EffectiveStoragePrincipal());
7539 // When storage access is granted the content process needs to request the
7540 // updated cookie list from the parent process. Otherwise the site won't have
7541 // access to unpartitioned cookies via document.cookie without a reload.
7542 if (aGranted) {
7543 nsIChannel* channel = mDoc->GetChannel();
7544 if (channel) {
7545 // The promise resolves when the updated cookie list has been received
7546 // from the parent.
7547 return ContentChild::UpdateCookieStatus(channel);
7550 return MozPromise<bool, nsresult, true>::CreateAndResolve(true, __func__);
7553 ContentMediaController* nsGlobalWindowInner::GetContentMediaController() {
7554 if (mContentMediaController) {
7555 return mContentMediaController;
7557 if (!mBrowsingContext) {
7558 return nullptr;
7561 mContentMediaController = new ContentMediaController(mBrowsingContext->Id());
7562 return mContentMediaController;
7565 void nsGlobalWindowInner::SetScrollMarks(const nsTArray<uint32_t>& aScrollMarks,
7566 bool aOnHScrollbar) {
7567 mScrollMarks.Assign(aScrollMarks);
7568 mScrollMarksOnHScrollbar = aOnHScrollbar;
7570 // Mark the scrollbar for repainting.
7571 if (mDoc) {
7572 PresShell* presShell = mDoc->GetPresShell();
7573 if (presShell) {
7574 nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollable();
7575 if (sf) {
7576 sf->InvalidateScrollbars();
7582 /* static */
7583 already_AddRefed<nsGlobalWindowInner> nsGlobalWindowInner::Create(
7584 nsGlobalWindowOuter* aOuterWindow, bool aIsChrome,
7585 WindowGlobalChild* aActor) {
7586 RefPtr<nsGlobalWindowInner> window =
7587 new nsGlobalWindowInner(aOuterWindow, aActor);
7588 if (aIsChrome) {
7589 window->mIsChrome = true;
7590 window->mCleanMessageManager = true;
7593 if (aActor) {
7594 aActor->InitWindowGlobal(window);
7597 window->InitWasOffline();
7598 return window.forget();
7601 JS::loader::ModuleLoaderBase* nsGlobalWindowInner::GetModuleLoader(
7602 JSContext* aCx) {
7603 Document* document = GetDocument();
7604 if (!document) {
7605 return nullptr;
7608 ScriptLoader* loader = document->ScriptLoader();
7609 if (!loader) {
7610 return nullptr;
7613 return loader->GetModuleLoader();
7616 void nsGlobalWindowInner::SetCurrentPasteDataTransfer(
7617 DataTransfer* aDataTransfer) {
7618 MOZ_ASSERT_IF(aDataTransfer, aDataTransfer->GetEventMessage() == ePaste);
7619 MOZ_ASSERT_IF(aDataTransfer, aDataTransfer->ClipboardType() ==
7620 nsIClipboard::kGlobalClipboard);
7621 MOZ_ASSERT_IF(aDataTransfer, aDataTransfer->GetAsyncGetClipboardData());
7622 mCurrentPasteDataTransfer = aDataTransfer;
7625 DataTransfer* nsGlobalWindowInner::GetCurrentPasteDataTransfer() const {
7626 return mCurrentPasteDataTransfer;
7629 TrustedTypePolicyFactory* nsGlobalWindowInner::TrustedTypes() {
7630 if (!mTrustedTypePolicyFactory) {
7631 mTrustedTypePolicyFactory = MakeRefPtr<TrustedTypePolicyFactory>(this);
7634 return mTrustedTypePolicyFactory;
7637 nsIURI* nsPIDOMWindowInner::GetDocumentURI() const {
7638 return mDoc ? mDoc->GetDocumentURI() : mDocumentURI.get();
7641 nsIURI* nsPIDOMWindowInner::GetDocBaseURI() const {
7642 return mDoc ? mDoc->GetDocBaseURI() : mDocBaseURI.get();
7645 mozilla::dom::WindowContext* nsPIDOMWindowInner::GetWindowContext() const {
7646 return mWindowGlobalChild ? mWindowGlobalChild->WindowContext() : nullptr;
7649 bool nsPIDOMWindowInner::RemoveFromBFCacheSync() {
7650 if (Document* doc = GetExtantDoc()) {
7651 return doc->RemoveFromBFCacheSync();
7653 return false;
7656 void nsPIDOMWindowInner::MaybeCreateDoc() {
7657 // XXX: Forward to outer?
7658 MOZ_ASSERT(!mDoc);
7659 if (nsIDocShell* docShell = GetDocShell()) {
7660 // Note that |document| here is the same thing as our mDoc, but we
7661 // don't have to explicitly set the member variable because the docshell
7662 // has already called SetNewDocument().
7663 nsCOMPtr<Document> document = docShell->GetDocument();
7664 Unused << document;
7668 mozilla::dom::DocGroup* nsPIDOMWindowInner::GetDocGroup() const {
7669 Document* doc = GetExtantDoc();
7670 if (doc) {
7671 return doc->GetDocGroup();
7673 return nullptr;
7676 mozilla::dom::BrowsingContextGroup*
7677 nsPIDOMWindowInner::GetBrowsingContextGroup() const {
7678 return mBrowsingContext ? mBrowsingContext->Group() : nullptr;
7681 nsIGlobalObject* nsPIDOMWindowInner::AsGlobal() {
7682 return nsGlobalWindowInner::Cast(this);
7685 const nsIGlobalObject* nsPIDOMWindowInner::AsGlobal() const {
7686 return nsGlobalWindowInner::Cast(this);
7689 RefPtr<GenericPromise>
7690 nsPIDOMWindowInner::SaveStorageAccessPermissionGranted() {
7691 WindowContext* wc = GetWindowContext();
7692 if (wc) {
7693 Unused << wc->SetUsingStorageAccess(true);
7696 return nsGlobalWindowInner::Cast(this)->StorageAccessPermissionChanged(true);
7699 RefPtr<GenericPromise>
7700 nsPIDOMWindowInner::SaveStorageAccessPermissionRevoked() {
7701 WindowContext* wc = GetWindowContext();
7702 if (wc) {
7703 Unused << wc->SetUsingStorageAccess(false);
7706 return nsGlobalWindowInner::Cast(this)->StorageAccessPermissionChanged(false);
7709 bool nsPIDOMWindowInner::UsingStorageAccess() {
7710 WindowContext* wc = GetWindowContext();
7711 if (!wc) {
7712 return false;
7715 return wc->GetUsingStorageAccess();
7718 nsPIDOMWindowInner::nsPIDOMWindowInner(nsPIDOMWindowOuter* aOuterWindow,
7719 WindowGlobalChild* aActor)
7720 : mMutationBits(0),
7721 mIsDocumentLoaded(false),
7722 mIsHandlingResizeEvent(false),
7723 mMayHaveDOMActivateEventListeners(false),
7724 mMayHavePaintEventListener(false),
7725 mMayHaveTouchEventListener(false),
7726 mMayHaveSelectionChangeEventListener(false),
7727 mMayHaveFormSelectEventListener(false),
7728 mMayHaveMouseEnterLeaveEventListener(false),
7729 mMayHavePointerEnterLeaveEventListener(false),
7730 mMayHaveTransitionEventListener(false),
7731 mMayHaveBeforeInputEventListenerForTelemetry(false),
7732 mMutationObserverHasObservedNodeForTelemetry(false),
7733 mOuterWindow(aOuterWindow),
7734 mWindowID(0),
7735 mHasNotifiedGlobalCreated(false),
7736 mMarkedCCGeneration(0),
7737 mHasTriedToCacheTopInnerWindow(false),
7738 mNumOfIndexedDBDatabases(0),
7739 mNumOfOpenWebSockets(0),
7740 mEvent(nullptr),
7741 mWindowGlobalChild(aActor),
7742 mWasSuspendedByGroup(false) {
7743 MOZ_ASSERT(aOuterWindow);
7744 mBrowsingContext = aOuterWindow->GetBrowsingContext();
7746 if (mWindowGlobalChild) {
7747 mWindowID = aActor->InnerWindowId();
7749 MOZ_ASSERT(mWindowGlobalChild->BrowsingContext() == mBrowsingContext);
7750 } else {
7751 mWindowID = nsContentUtils::GenerateWindowId();
7755 nsPIDOMWindowInner::~nsPIDOMWindowInner() = default;
7757 #undef FORWARD_TO_OUTER
7758 #undef FORWARD_TO_OUTER_OR_THROW
7759 #undef FORWARD_TO_OUTER_VOID