Bug 1852740: add tests for the `fetchpriority` attribute in Link headers. r=necko...
[gecko.git] / dom / base / nsGlobalWindowInner.cpp
blob3e4f34a51a7f9aa6ced92459d66dfd18de614064
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/ContentFrameMessageManager.h"
109 #include "mozilla/dom/ContentMediaController.h"
110 #include "mozilla/dom/CustomElementRegistry.h"
111 #include "mozilla/dom/DebuggerNotification.h"
112 #include "mozilla/dom/DebuggerNotificationBinding.h"
113 #include "mozilla/dom/DebuggerNotificationManager.h"
114 #include "mozilla/dom/DocGroup.h"
115 #include "mozilla/dom/Document.h"
116 #include "mozilla/dom/DocumentInlines.h"
117 #include "mozilla/dom/Element.h"
118 #include "mozilla/dom/Event.h"
119 #include "mozilla/dom/EventTarget.h"
120 #include "mozilla/dom/External.h"
121 #include "mozilla/dom/Fetch.h"
122 #include "mozilla/dom/Gamepad.h"
123 #include "mozilla/dom/GamepadHandle.h"
124 #include "mozilla/dom/GamepadManager.h"
125 #include "mozilla/dom/HashChangeEvent.h"
126 #include "mozilla/dom/HashChangeEventBinding.h"
127 #include "mozilla/dom/IDBFactory.h"
128 #include "mozilla/dom/IdleRequest.h"
129 #include "mozilla/dom/ImageBitmap.h"
130 #include "mozilla/dom/ImageBitmapSource.h"
131 #include "mozilla/dom/InstallTriggerBinding.h"
132 #include "mozilla/dom/IntlUtils.h"
133 #include "mozilla/dom/JSExecutionContext.h"
134 #include "mozilla/dom/LSObject.h"
135 #include "mozilla/dom/LocalStorage.h"
136 #include "mozilla/dom/LocalStorageCommon.h"
137 #include "mozilla/dom/Location.h"
138 #include "mozilla/dom/MediaDevices.h"
139 #include "mozilla/dom/MediaKeys.h"
140 #include "mozilla/dom/NavigatorBinding.h"
141 #include "mozilla/dom/Nullable.h"
142 #include "mozilla/dom/PartitionedLocalStorage.h"
143 #include "mozilla/dom/Performance.h"
144 #include "mozilla/dom/PopStateEvent.h"
145 #include "mozilla/dom/PopStateEventBinding.h"
146 #include "mozilla/dom/PopupBlocker.h"
147 #include "mozilla/dom/PrimitiveConversions.h"
148 #include "mozilla/dom/Promise.h"
149 #include "mozilla/dom/RootedDictionary.h"
150 #include "mozilla/dom/WebTaskSchedulerMainThread.h"
151 #include "mozilla/dom/ScriptLoader.h"
152 #include "mozilla/dom/ScriptSettings.h"
153 #include "mozilla/dom/ServiceWorker.h"
154 #include "mozilla/dom/ServiceWorkerDescriptor.h"
155 #include "mozilla/dom/ServiceWorkerRegistration.h"
156 #include "mozilla/dom/SessionStorageManager.h"
157 #include "mozilla/dom/SharedWorker.h"
158 #include "mozilla/dom/Storage.h"
159 #include "mozilla/dom/StorageEvent.h"
160 #include "mozilla/dom/StorageEventBinding.h"
161 #include "mozilla/dom/StorageNotifierService.h"
162 #include "mozilla/dom/StorageUtils.h"
163 #include "mozilla/dom/TabMessageTypes.h"
164 #include "mozilla/dom/Timeout.h"
165 #include "mozilla/dom/TimeoutHandler.h"
166 #include "mozilla/dom/TimeoutManager.h"
167 #include "mozilla/dom/ToJSValue.h"
168 #include "mozilla/dom/VRDisplay.h"
169 #include "mozilla/dom/VRDisplayEvent.h"
170 #include "mozilla/dom/VRDisplayEventBinding.h"
171 #include "mozilla/dom/VREventObserver.h"
172 #include "mozilla/dom/VisualViewport.h"
173 #include "mozilla/dom/WebIDLGlobalNameHash.h"
174 #include "mozilla/dom/WindowBinding.h"
175 #include "mozilla/dom/WindowContext.h"
176 #include "mozilla/dom/WindowGlobalChild.h"
177 #include "mozilla/dom/WindowProxyHolder.h"
178 #include "mozilla/dom/WorkerCommon.h"
179 #include "mozilla/dom/Worklet.h"
180 #include "mozilla/dom/XRPermissionRequest.h"
181 #include "mozilla/dom/cache/CacheStorage.h"
182 #include "mozilla/dom/cache/Types.h"
183 #include "mozilla/glean/bindings/Glean.h"
184 #include "mozilla/glean/bindings/GleanPings.h"
185 #include "mozilla/extensions/WebExtensionPolicy.h"
186 #include "mozilla/fallible.h"
187 #include "mozilla/gfx/BasePoint.h"
188 #include "mozilla/gfx/BaseRect.h"
189 #include "mozilla/gfx/BaseSize.h"
190 #include "mozilla/gfx/Rect.h"
191 #include "mozilla/gfx/Types.h"
192 #include "mozilla/intl/LocaleService.h"
193 #include "mozilla/ipc/BackgroundUtils.h"
194 #include "mozilla/ipc/PBackgroundSharedTypes.h"
195 #include "mozilla/net/CookieJarSettings.h"
196 #include "nsAtom.h"
197 #include "nsBaseHashtable.h"
198 #include "nsCCUncollectableMarker.h"
199 #include "nsCOMPtr.h"
200 #include "nsCRT.h"
201 #include "nsCRTGlue.h"
202 #include "nsCanvasFrame.h"
203 #include "nsCharTraits.h"
204 #include "nsCheapSets.h"
205 #include "nsContentUtils.h"
206 #include "nsCoord.h"
207 #include "nsCycleCollectionNoteChild.h"
208 #include "nsCycleCollectionTraversalCallback.h"
209 #include "nsDOMNavigationTiming.h"
210 #include "nsDebug.h"
211 #include "nsDeviceContext.h"
212 #include "nsDocShell.h"
213 #include "nsFocusManager.h"
214 #include "nsFrameMessageManager.h"
215 #include "nsGkAtoms.h"
216 #include "nsGlobalWindowOuter.h"
217 #include "nsHashKeys.h"
218 #include "nsHistory.h"
219 #include "nsIAddonPolicyService.h"
220 #include "nsIArray.h"
221 #include "nsIBaseWindow.h"
222 #include "nsIBrowserChild.h"
223 #include "nsICancelableRunnable.h"
224 #include "nsIChannel.h"
225 #include "nsIContentSecurityPolicy.h"
226 #include "nsIControllers.h"
227 #include "nsICookieJarSettings.h"
228 #include "nsICookieService.h"
229 #include "nsID.h"
230 #include "nsIDOMStorageManager.h"
231 #include "nsIDeviceSensors.h"
232 #include "nsIDocShell.h"
233 #include "nsIDocShellTreeItem.h"
234 #include "nsIDocShellTreeOwner.h"
235 #include "nsIDocumentLoader.h"
236 #include "nsIDragService.h"
237 #include "nsIFocusManager.h"
238 #include "nsIFrame.h"
239 #include "nsIGlobalObject.h"
240 #include "nsIIOService.h"
241 #include "nsIIdleRunnable.h"
242 #include "nsIInterfaceRequestorUtils.h"
243 #include "nsILoadContext.h"
244 #include "nsILoadGroup.h"
245 #include "nsILoadInfo.h"
246 #include "nsINamed.h"
247 #include "nsINode.h"
248 #include "nsIObserver.h"
249 #include "nsIObserverService.h"
250 #include "nsIPermission.h"
251 #include "nsIPermissionManager.h"
252 #include "nsIPrefBranch.h"
253 #include "nsIPrincipal.h"
254 #include "nsIPrompt.h"
255 #include "nsIRunnable.h"
256 #include "nsIScreen.h"
257 #include "nsIScreenManager.h"
258 #include "nsIScriptContext.h"
259 #include "nsIScriptGlobalObject.h"
260 #include "nsIScriptObjectPrincipal.h"
261 #include "nsIScrollableFrame.h"
262 #include "nsISerialEventTarget.h"
263 #include "nsISimpleEnumerator.h"
264 #include "nsISizeOfEventTarget.h"
265 #include "nsISlowScriptDebug.h"
266 #include "nsISupportsUtils.h"
267 #include "nsIThread.h"
268 #include "nsITimedChannel.h"
269 #include "nsIURI.h"
270 #include "nsIWeakReference.h"
271 #include "nsIWebBrowserChrome.h"
272 #include "nsIWebNavigation.h"
273 #include "nsIWebProgressListener.h"
274 #include "nsIWidget.h"
275 #include "nsIWidgetListener.h"
276 #include "nsIXULRuntime.h"
277 #include "nsJSPrincipals.h"
278 #include "nsJSUtils.h"
279 #include "nsLayoutStatics.h"
280 #include "nsLiteralString.h"
281 #include "nsNetUtil.h"
282 #include "nsPIDOMWindow.h"
283 #include "nsPIDOMWindowInlines.h"
284 #include "nsPIWindowRoot.h"
285 #include "nsPoint.h"
286 #include "nsPresContext.h"
287 #include "nsQueryObject.h"
288 #include "nsSandboxFlags.h"
289 #include "nsScreen.h"
290 #include "nsServiceManagerUtils.h"
291 #include "nsString.h"
292 #include "nsStringFlags.h"
293 #include "nsStringFwd.h"
294 #include "nsTArray.h"
295 #include "nsTLiteralString.h"
296 #include "nsTObserverArray.h"
297 #include "nsTStringRepr.h"
298 #include "nsThreadUtils.h"
299 #include "nsWeakReference.h"
300 #include "nsWindowMemoryReporter.h"
301 #include "nsWindowSizes.h"
302 #include "nsWrapperCache.h"
303 #include "nsWrapperCacheInlines.h"
304 #include "nsXULAppAPI.h"
305 #include "nsrootidl.h"
306 #include "prclist.h"
307 #include "prtypes.h"
308 #include "xpcprivate.h"
309 #include "xpcpublic.h"
311 #include "nsIDOMXULControlElement.h"
313 #ifdef NS_PRINTING
314 # include "nsIPrintSettings.h"
315 #endif
317 #ifdef MOZ_WEBSPEECH
318 # include "mozilla/dom/SpeechSynthesis.h"
319 #endif
321 #ifdef ANDROID
322 # include <android/log.h>
323 #endif
325 #ifdef XP_WIN
326 # include "mozilla/Debug.h"
327 # include <process.h>
328 # define getpid _getpid
329 #else
330 # include <unistd.h> // for getpid()
331 #endif
333 using namespace mozilla;
334 using namespace mozilla::dom;
335 using namespace mozilla::dom::ipc;
336 using mozilla::TimeDuration;
337 using mozilla::TimeStamp;
338 using mozilla::dom::GamepadHandle;
339 using mozilla::dom::cache::CacheStorage;
341 #define FORWARD_TO_OUTER(method, args, err_rval) \
342 PR_BEGIN_MACRO \
343 RefPtr<nsGlobalWindowOuter> outer = GetOuterWindowInternal(); \
344 if (!HasActiveDocument()) { \
345 NS_WARNING(outer ? "Inner window does not have active document." \
346 : "No outer window available!"); \
347 return err_rval; \
349 return outer->method args; \
350 PR_END_MACRO
352 static nsGlobalWindowOuter* GetOuterWindowForForwarding(
353 nsGlobalWindowInner* aInner, ErrorResult& aError) {
354 nsGlobalWindowOuter* outer = aInner->GetOuterWindowInternal();
355 if (MOZ_LIKELY(aInner->HasActiveDocument())) {
356 return outer;
358 if (!outer) {
359 NS_WARNING("No outer window available!");
360 aError.Throw(NS_ERROR_NOT_INITIALIZED);
361 } else {
362 aError.Throw(NS_ERROR_XPC_SECURITY_MANAGER_VETO);
364 return nullptr;
367 #define FORWARD_TO_OUTER_OR_THROW(method, args, rv, err_rval) \
368 PR_BEGIN_MACRO \
369 RefPtr<nsGlobalWindowOuter> outer = GetOuterWindowForForwarding(this, rv); \
370 if (MOZ_LIKELY(outer)) { \
371 return outer->method args; \
373 return err_rval; \
374 PR_END_MACRO
376 #define FORWARD_TO_OUTER_VOID(method, args) \
377 PR_BEGIN_MACRO \
378 RefPtr<nsGlobalWindowOuter> outer = GetOuterWindowInternal(); \
379 if (!HasActiveDocument()) { \
380 NS_WARNING(outer ? "Inner window does not have active document." \
381 : "No outer window available!"); \
382 return; \
384 outer->method args; \
385 return; \
386 PR_END_MACRO
388 #define ENSURE_ACTIVE_DOCUMENT(errorresult, err_rval) \
389 PR_BEGIN_MACRO \
390 if (MOZ_UNLIKELY(!HasActiveDocument())) { \
391 aError.Throw(NS_ERROR_XPC_SECURITY_MANAGER_VETO); \
392 return err_rval; \
394 PR_END_MACRO
396 #define DOM_TOUCH_LISTENER_ADDED "dom-touch-listener-added"
397 #define MEMORY_PRESSURE_OBSERVER_TOPIC "memory-pressure"
398 #define PERMISSION_CHANGED_TOPIC "perm-changed"
400 static LazyLogModule gDOMLeakPRLogInner("DOMLeakInner");
401 extern mozilla::LazyLogModule gTimeoutLog;
403 #ifdef DEBUG
404 static LazyLogModule gDocShellAndDOMWindowLeakLogging(
405 "DocShellAndDOMWindowLeak");
406 #endif
408 static FILE* gDumpFile = nullptr;
410 nsGlobalWindowInner::InnerWindowByIdTable*
411 nsGlobalWindowInner::sInnerWindowsById = nullptr;
413 bool nsGlobalWindowInner::sDragServiceDisabled = false;
414 bool nsGlobalWindowInner::sMouseDown = false;
417 * An indirect observer object that means we don't have to implement nsIObserver
418 * on nsGlobalWindow, where any script could see it.
420 class nsGlobalWindowObserver final : public nsIObserver,
421 public nsIInterfaceRequestor,
422 public StorageNotificationObserver {
423 public:
424 explicit nsGlobalWindowObserver(nsGlobalWindowInner* aWindow)
425 : mWindow(aWindow) {}
426 NS_DECL_ISUPPORTS
427 NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
428 const char16_t* aData) override {
429 if (!mWindow) return NS_OK;
430 return mWindow->Observe(aSubject, aTopic, aData);
432 void Forget() { mWindow = nullptr; }
433 NS_IMETHOD GetInterface(const nsIID& aIID, void** aResult) override {
434 if (mWindow && aIID.Equals(NS_GET_IID(nsIDOMWindow)) && mWindow) {
435 return mWindow->QueryInterface(aIID, aResult);
437 return NS_NOINTERFACE;
440 void ObserveStorageNotification(StorageEvent* aEvent,
441 const char16_t* aStorageType,
442 bool aPrivateBrowsing) override {
443 if (mWindow) {
444 mWindow->ObserveStorageNotification(aEvent, aStorageType,
445 aPrivateBrowsing);
449 nsIPrincipal* GetEffectiveCookiePrincipal() const override {
450 return mWindow ? mWindow->GetEffectiveCookiePrincipal() : nullptr;
453 nsIPrincipal* GetEffectiveStoragePrincipal() const override {
454 return mWindow ? mWindow->GetEffectiveStoragePrincipal() : nullptr;
457 bool IsPrivateBrowsing() const override {
458 return mWindow ? mWindow->IsPrivateBrowsing() : false;
461 nsIEventTarget* GetEventTarget() const override {
462 return mWindow ? mWindow->SerialEventTarget() : nullptr;
465 private:
466 ~nsGlobalWindowObserver() = default;
468 // This reference is non-owning and safe because it's cleared by
469 // nsGlobalWindowInner::FreeInnerObjects().
470 nsGlobalWindowInner* MOZ_NON_OWNING_REF mWindow;
473 NS_IMPL_ISUPPORTS(nsGlobalWindowObserver, nsIObserver, nsIInterfaceRequestor)
475 class IdleRequestExecutor;
477 class IdleRequestExecutorTimeoutHandler final : public TimeoutHandler {
478 public:
479 explicit IdleRequestExecutorTimeoutHandler(IdleRequestExecutor* aExecutor)
480 : mExecutor(aExecutor) {}
482 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
483 NS_DECL_CYCLE_COLLECTION_CLASS(IdleRequestExecutorTimeoutHandler)
485 bool Call(const char* /* unused */) override;
487 private:
488 ~IdleRequestExecutorTimeoutHandler() override = default;
489 RefPtr<IdleRequestExecutor> mExecutor;
492 NS_IMPL_CYCLE_COLLECTION(IdleRequestExecutorTimeoutHandler, mExecutor)
494 NS_IMPL_CYCLE_COLLECTING_ADDREF(IdleRequestExecutorTimeoutHandler)
495 NS_IMPL_CYCLE_COLLECTING_RELEASE(IdleRequestExecutorTimeoutHandler)
497 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestExecutorTimeoutHandler)
498 NS_INTERFACE_MAP_ENTRY(nsISupports)
499 NS_INTERFACE_MAP_END
501 class IdleRequestExecutor final : public nsIRunnable,
502 public nsICancelableRunnable,
503 public nsINamed,
504 public nsIIdleRunnable {
505 public:
506 explicit IdleRequestExecutor(nsGlobalWindowInner* aWindow)
507 : mDispatched(false), mDeadline(TimeStamp::Now()), mWindow(aWindow) {
508 MOZ_DIAGNOSTIC_ASSERT(mWindow);
510 mIdlePeriodLimit = {mDeadline, mWindow->LastIdleRequestHandle()};
511 mDelayedExecutorDispatcher = new IdleRequestExecutorTimeoutHandler(this);
514 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
515 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(IdleRequestExecutor, nsIRunnable)
517 NS_DECL_NSIRUNNABLE
518 NS_DECL_NSINAMED
519 nsresult Cancel() override;
520 void SetDeadline(TimeStamp aDeadline) override;
522 bool IsCancelled() const { return !mWindow || mWindow->IsDying(); }
523 // Checks if aRequest shouldn't execute in the current idle period
524 // since it has been queued from a chained call to
525 // requestIdleCallback from within a running idle callback.
526 bool IneligibleForCurrentIdlePeriod(IdleRequest* aRequest) const {
527 return aRequest->Handle() >= mIdlePeriodLimit.mLastRequestIdInIdlePeriod &&
528 TimeStamp::Now() <= mIdlePeriodLimit.mEndOfIdlePeriod;
531 void MaybeUpdateIdlePeriodLimit();
533 // Maybe dispatch the IdleRequestExecutor. MabyeDispatch will
534 // schedule a delayed dispatch if the associated window is in the
535 // background or if given a time to wait until dispatching.
536 void MaybeDispatch(TimeStamp aDelayUntil = TimeStamp());
537 void ScheduleDispatch();
539 private:
540 struct IdlePeriodLimit {
541 TimeStamp mEndOfIdlePeriod;
542 uint32_t mLastRequestIdInIdlePeriod;
545 void DelayedDispatch(uint32_t aDelay);
547 ~IdleRequestExecutor() override = default;
549 bool mDispatched;
550 TimeStamp mDeadline;
551 IdlePeriodLimit mIdlePeriodLimit;
552 RefPtr<nsGlobalWindowInner> mWindow;
553 // The timeout handler responsible for dispatching this executor in
554 // the case of immediate dispatch to the idle queue isn't
555 // desirable. This is used if we've dispatched all idle callbacks
556 // that are allowed to run in the current idle period, or if the
557 // associated window is currently in the background.
558 RefPtr<TimeoutHandler> mDelayedExecutorDispatcher;
559 // If not Nothing() then this value is the handle to the currently
560 // scheduled delayed executor dispatcher. This is needed to be able
561 // to cancel the timeout handler in case of the executor being
562 // cancelled.
563 Maybe<int32_t> mDelayedExecutorHandle;
566 NS_IMPL_CYCLE_COLLECTION(IdleRequestExecutor, mWindow,
567 mDelayedExecutorDispatcher)
569 NS_IMPL_CYCLE_COLLECTING_ADDREF(IdleRequestExecutor)
570 NS_IMPL_CYCLE_COLLECTING_RELEASE(IdleRequestExecutor)
572 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestExecutor)
573 NS_INTERFACE_MAP_ENTRY(nsIRunnable)
574 NS_INTERFACE_MAP_ENTRY(nsICancelableRunnable)
575 NS_INTERFACE_MAP_ENTRY(nsINamed)
576 NS_INTERFACE_MAP_ENTRY(nsIIdleRunnable)
577 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRunnable)
578 NS_INTERFACE_MAP_END
580 NS_IMETHODIMP
581 IdleRequestExecutor::GetName(nsACString& aName) {
582 aName.AssignLiteral("IdleRequestExecutor");
583 return NS_OK;
586 // MOZ_CAN_RUN_SCRIPT_BOUNDARY until nsIRunnable::Run is MOZ_CAN_RUN_SCRIPT.
587 // See bug 1535398.
588 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP IdleRequestExecutor::Run() {
589 MOZ_ASSERT(NS_IsMainThread());
591 mDispatched = false;
592 if (mWindow) {
593 RefPtr<nsGlobalWindowInner> window(mWindow);
594 window->ExecuteIdleRequest(mDeadline);
597 return NS_OK;
600 nsresult IdleRequestExecutor::Cancel() {
601 MOZ_ASSERT(NS_IsMainThread());
603 if (mDelayedExecutorHandle && mWindow) {
604 mWindow->TimeoutManager().ClearTimeout(
605 mDelayedExecutorHandle.value(), Timeout::Reason::eIdleCallbackTimeout);
608 mWindow = nullptr;
609 return NS_OK;
612 void IdleRequestExecutor::SetDeadline(TimeStamp aDeadline) {
613 MOZ_ASSERT(NS_IsMainThread());
615 if (!mWindow) {
616 return;
619 mDeadline = aDeadline;
622 void IdleRequestExecutor::MaybeUpdateIdlePeriodLimit() {
623 if (TimeStamp::Now() > mIdlePeriodLimit.mEndOfIdlePeriod) {
624 mIdlePeriodLimit = {mDeadline, mWindow->LastIdleRequestHandle()};
628 void IdleRequestExecutor::MaybeDispatch(TimeStamp aDelayUntil) {
629 // If we've already dispatched the executor we don't want to do it
630 // again. Also, if we've called IdleRequestExecutor::Cancel mWindow
631 // will be null, which indicates that we shouldn't dispatch this
632 // executor either.
633 if (mDispatched || IsCancelled()) {
634 return;
637 mDispatched = true;
639 nsPIDOMWindowOuter* outer = mWindow->GetOuterWindow();
640 if (outer && outer->IsBackground()) {
641 // Set a timeout handler with a timeout of 0 ms to throttle idle
642 // callback requests coming from a backround window using
643 // background timeout throttling.
644 DelayedDispatch(0);
645 return;
648 TimeStamp now = TimeStamp::Now();
649 if (!aDelayUntil || aDelayUntil < now) {
650 ScheduleDispatch();
651 return;
654 TimeDuration delay = aDelayUntil - now;
655 DelayedDispatch(static_cast<uint32_t>(delay.ToMilliseconds()));
658 void IdleRequestExecutor::ScheduleDispatch() {
659 MOZ_ASSERT(mWindow);
660 mDelayedExecutorHandle = Nothing();
661 RefPtr<IdleRequestExecutor> request = this;
662 NS_DispatchToCurrentThreadQueue(request.forget(), EventQueuePriority::Idle);
665 void IdleRequestExecutor::DelayedDispatch(uint32_t aDelay) {
666 MOZ_ASSERT(mWindow);
667 MOZ_ASSERT(mDelayedExecutorHandle.isNothing());
668 int32_t handle;
669 mWindow->TimeoutManager().SetTimeout(
670 mDelayedExecutorDispatcher, aDelay, false,
671 Timeout::Reason::eIdleCallbackTimeout, &handle);
672 mDelayedExecutorHandle = Some(handle);
675 bool IdleRequestExecutorTimeoutHandler::Call(const char* /* unused */) {
676 if (!mExecutor->IsCancelled()) {
677 mExecutor->ScheduleDispatch();
679 return true;
682 void nsGlobalWindowInner::ScheduleIdleRequestDispatch() {
683 AssertIsOnMainThread();
685 if (!mIdleRequestExecutor) {
686 mIdleRequestExecutor = new IdleRequestExecutor(this);
689 mIdleRequestExecutor->MaybeDispatch();
692 void nsGlobalWindowInner::SuspendIdleRequests() {
693 if (mIdleRequestExecutor) {
694 mIdleRequestExecutor->Cancel();
695 mIdleRequestExecutor = nullptr;
699 void nsGlobalWindowInner::ResumeIdleRequests() {
700 MOZ_ASSERT(!mIdleRequestExecutor);
702 ScheduleIdleRequestDispatch();
705 void nsGlobalWindowInner::RemoveIdleCallback(
706 mozilla::dom::IdleRequest* aRequest) {
707 AssertIsOnMainThread();
709 if (aRequest->HasTimeout()) {
710 mTimeoutManager->ClearTimeout(aRequest->GetTimeoutHandle(),
711 Timeout::Reason::eIdleCallbackTimeout);
714 aRequest->removeFrom(mIdleRequestCallbacks);
717 void nsGlobalWindowInner::RunIdleRequest(IdleRequest* aRequest,
718 DOMHighResTimeStamp aDeadline,
719 bool aDidTimeout) {
720 AssertIsOnMainThread();
721 // XXXbz Do we still need this RefPtr? MOZ_CAN_RUN_SCRIPT should
722 // guarantee that caller is holding a strong ref on the stack.
723 RefPtr<IdleRequest> request(aRequest);
724 RemoveIdleCallback(request);
725 request->IdleRun(this, aDeadline, aDidTimeout);
728 void nsGlobalWindowInner::ExecuteIdleRequest(TimeStamp aDeadline) {
729 AssertIsOnMainThread();
730 RefPtr<IdleRequest> request = mIdleRequestCallbacks.getFirst();
732 if (!request) {
733 // There are no more idle requests, so stop scheduling idle
734 // request callbacks.
735 return;
738 // If the request that we're trying to execute has been queued
739 // during the current idle period, then dispatch it again at the end
740 // of the idle period.
741 if (mIdleRequestExecutor->IneligibleForCurrentIdlePeriod(request)) {
742 mIdleRequestExecutor->MaybeDispatch(aDeadline);
743 return;
746 DOMHighResTimeStamp deadline = 0.0;
748 if (Performance* perf = GetPerformance()) {
749 deadline = perf->GetDOMTiming()->TimeStampToDOMHighRes(aDeadline);
752 mIdleRequestExecutor->MaybeUpdateIdlePeriodLimit();
753 RunIdleRequest(request, deadline, false);
755 // Running the idle callback could've suspended the window, in which
756 // case mIdleRequestExecutor will be null.
757 if (mIdleRequestExecutor) {
758 mIdleRequestExecutor->MaybeDispatch();
762 class IdleRequestTimeoutHandler final : public TimeoutHandler {
763 public:
764 IdleRequestTimeoutHandler(JSContext* aCx, IdleRequest* aIdleRequest,
765 nsPIDOMWindowInner* aWindow)
766 : TimeoutHandler(aCx), mIdleRequest(aIdleRequest), mWindow(aWindow) {}
768 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
769 NS_DECL_CYCLE_COLLECTION_CLASS(IdleRequestTimeoutHandler)
771 MOZ_CAN_RUN_SCRIPT bool Call(const char* /* unused */) override {
772 RefPtr<nsGlobalWindowInner> window(nsGlobalWindowInner::Cast(mWindow));
773 RefPtr<IdleRequest> request(mIdleRequest);
774 window->RunIdleRequest(request, 0.0, true);
775 return true;
778 private:
779 ~IdleRequestTimeoutHandler() override = default;
781 RefPtr<IdleRequest> mIdleRequest;
782 nsCOMPtr<nsPIDOMWindowInner> mWindow;
785 NS_IMPL_CYCLE_COLLECTION(IdleRequestTimeoutHandler, mIdleRequest, mWindow)
787 NS_IMPL_CYCLE_COLLECTING_ADDREF(IdleRequestTimeoutHandler)
788 NS_IMPL_CYCLE_COLLECTING_RELEASE(IdleRequestTimeoutHandler)
790 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestTimeoutHandler)
791 NS_INTERFACE_MAP_ENTRY(nsISupports)
792 NS_INTERFACE_MAP_END
794 uint32_t nsGlobalWindowInner::RequestIdleCallback(
795 JSContext* aCx, IdleRequestCallback& aCallback,
796 const IdleRequestOptions& aOptions, ErrorResult& aError) {
797 AssertIsOnMainThread();
799 if (IsDying()) {
800 return 0;
803 uint32_t handle = mIdleRequestCallbackCounter++;
805 RefPtr<IdleRequest> request = new IdleRequest(&aCallback, handle);
807 if (aOptions.mTimeout.WasPassed()) {
808 int32_t timeoutHandle;
809 RefPtr<TimeoutHandler> handler(
810 new IdleRequestTimeoutHandler(aCx, request, this));
812 nsresult rv = mTimeoutManager->SetTimeout(
813 handler, aOptions.mTimeout.Value(), false,
814 Timeout::Reason::eIdleCallbackTimeout, &timeoutHandle);
816 if (NS_WARN_IF(NS_FAILED(rv))) {
817 return 0;
820 request->SetTimeoutHandle(timeoutHandle);
823 mIdleRequestCallbacks.insertBack(request);
825 if (!IsSuspended()) {
826 ScheduleIdleRequestDispatch();
829 return handle;
832 void nsGlobalWindowInner::CancelIdleCallback(uint32_t aHandle) {
833 for (IdleRequest* r : mIdleRequestCallbacks) {
834 if (r->Handle() == aHandle) {
835 RemoveIdleCallback(r);
836 break;
841 void nsGlobalWindowInner::DisableIdleCallbackRequests() {
842 if (mIdleRequestExecutor) {
843 mIdleRequestExecutor->Cancel();
844 mIdleRequestExecutor = nullptr;
847 while (!mIdleRequestCallbacks.isEmpty()) {
848 RefPtr<IdleRequest> request = mIdleRequestCallbacks.getFirst();
849 RemoveIdleCallback(request);
853 bool nsGlobalWindowInner::IsBackgroundInternal() const {
854 return !mOuterWindow || mOuterWindow->IsBackground();
857 class PromiseDocumentFlushedResolver final {
858 public:
859 PromiseDocumentFlushedResolver(Promise* aPromise,
860 PromiseDocumentFlushedCallback& aCallback)
861 : mPromise(aPromise), mCallback(&aCallback) {}
863 virtual ~PromiseDocumentFlushedResolver() = default;
865 void Call() {
866 nsMutationGuard guard;
867 ErrorResult error;
868 JS::Rooted<JS::Value> returnVal(RootingCx());
869 mCallback->Call(&returnVal, error);
871 if (error.Failed()) {
872 mPromise->MaybeReject(std::move(error));
873 } else if (guard.Mutated(0)) {
874 // Something within the callback mutated the DOM.
875 mPromise->MaybeRejectWithNoModificationAllowedError(
876 "DOM mutated from promiseDocumentFlushed callbacks");
877 } else {
878 mPromise->MaybeResolve(returnVal);
882 RefPtr<Promise> mPromise;
883 RefPtr<PromiseDocumentFlushedCallback> mCallback;
886 //*****************************************************************************
887 //*** nsGlobalWindowInner: Object Management
888 //*****************************************************************************
890 nsGlobalWindowInner::nsGlobalWindowInner(nsGlobalWindowOuter* aOuterWindow,
891 WindowGlobalChild* aActor)
892 : nsPIDOMWindowInner(aOuterWindow, aActor),
893 mHasOrientationChangeListeners(false),
894 mWasOffline(false),
895 mHasHadSlowScript(false),
896 mIsChrome(false),
897 mCleanMessageManager(false),
898 mNeedsFocus(true),
899 mHasFocus(false),
900 mFocusByKeyOccurred(false),
901 mDidFireDocElemInserted(false),
902 mHasGamepad(false),
903 mHasXRSession(false),
904 mHasVRDisplayActivateEvents(false),
905 mXRRuntimeDetectionInFlight(false),
906 mXRPermissionRequestInFlight(false),
907 mXRPermissionGranted(false),
908 mWasCurrentInnerWindow(false),
909 mHasSeenGamepadInput(false),
910 mHintedWasLoading(false),
911 mHasOpenedExternalProtocolFrame(false),
912 mScrollMarksOnHScrollbar(false),
913 mStorageAllowedReasonCache(0),
914 mSuspendDepth(0),
915 mFreezeDepth(0),
916 #ifdef DEBUG
917 mSerial(0),
918 #endif
919 mFocusMethod(0),
920 mIdleRequestCallbackCounter(1),
921 mIdleRequestExecutor(nullptr),
922 mObservingRefresh(false),
923 mIteratingDocumentFlushedResolvers(false),
924 mCanSkipCCGeneration(0) {
925 mIsInnerWindow = true;
927 AssertIsOnMainThread();
928 SetIsOnMainThread();
929 nsLayoutStatics::AddRef();
931 // Initialize the PRCList (this).
932 PR_INIT_CLIST(this);
934 // add this inner window to the outer window list of inners.
935 PR_INSERT_AFTER(this, aOuterWindow);
937 mTimeoutManager = MakeUnique<dom::TimeoutManager>(
938 *this, StaticPrefs::dom_timeout_max_idle_defer_ms());
940 mObserver = new nsGlobalWindowObserver(this);
941 if (nsCOMPtr<nsIObserverService> os = services::GetObserverService()) {
942 // Watch for online/offline status changes so we can fire events. Use
943 // a strong reference.
944 os->AddObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC, false);
945 os->AddObserver(mObserver, MEMORY_PRESSURE_OBSERVER_TOPIC, false);
946 os->AddObserver(mObserver, PERMISSION_CHANGED_TOPIC, false);
947 os->AddObserver(mObserver, "screen-information-changed", false);
950 Preferences::AddStrongObserver(mObserver, "intl.accept_languages");
952 // Watch for storage notifications so we can fire storage events.
953 RefPtr<StorageNotifierService> sns = StorageNotifierService::GetOrCreate();
954 if (sns) {
955 sns->Register(mObserver);
958 if (XRE_IsContentProcess()) {
959 nsCOMPtr<nsIDocShell> docShell = GetDocShell();
960 if (docShell) {
961 mBrowserChild = docShell->GetBrowserChild();
965 if (gDumpFile == nullptr) {
966 nsAutoCString fname;
967 Preferences::GetCString("browser.dom.window.dump.file", fname);
968 if (!fname.IsEmpty()) {
969 // If this fails to open, Dump() knows to just go to stdout on null.
970 gDumpFile = fopen(fname.get(), "wb+");
971 } else {
972 gDumpFile = stdout;
976 #ifdef DEBUG
977 mSerial = nsContentUtils::InnerOrOuterWindowCreated();
979 MOZ_LOG(gDocShellAndDOMWindowLeakLogging, LogLevel::Info,
980 ("++DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p]\n",
981 nsContentUtils::GetCurrentInnerOrOuterWindowCount(),
982 static_cast<void*>(ToCanonicalSupports(this)), getpid(), mSerial,
983 static_cast<void*>(ToCanonicalSupports(aOuterWindow))));
984 #endif
986 MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
987 ("DOMWINDOW %p created outer=%p", this, aOuterWindow));
989 // Add ourselves to the inner windows list.
990 MOZ_ASSERT(sInnerWindowsById, "Inner Windows hash table must be created!");
991 MOZ_ASSERT(!sInnerWindowsById->Contains(mWindowID),
992 "This window shouldn't be in the hash table yet!");
993 // We seem to see crashes in release builds because of null
994 // |sInnerWindowsById|.
995 if (sInnerWindowsById) {
996 sInnerWindowsById->InsertOrUpdate(mWindowID, this);
1000 #ifdef DEBUG
1002 /* static */
1003 void nsGlobalWindowInner::AssertIsOnMainThread() {
1004 MOZ_ASSERT(NS_IsMainThread());
1007 #endif // DEBUG
1009 /* static */
1010 void nsGlobalWindowInner::Init() {
1011 AssertIsOnMainThread();
1013 NS_ASSERTION(gDOMLeakPRLogInner,
1014 "gDOMLeakPRLogInner should have been initialized!");
1016 sInnerWindowsById = new InnerWindowByIdTable();
1019 nsGlobalWindowInner::~nsGlobalWindowInner() {
1020 AssertIsOnMainThread();
1021 MOZ_ASSERT(!mHintedWasLoading);
1023 if (IsChromeWindow()) {
1024 MOZ_ASSERT(mCleanMessageManager,
1025 "chrome windows may always disconnect the msg manager");
1027 DisconnectAndClearGroupMessageManagers();
1029 if (mChromeFields.mMessageManager) {
1030 static_cast<nsFrameMessageManager*>(mChromeFields.mMessageManager.get())
1031 ->Disconnect();
1034 mCleanMessageManager = false;
1037 // In most cases this should already have been called, but call it again
1038 // here to catch any corner cases.
1039 FreeInnerObjects();
1041 if (sInnerWindowsById) {
1042 sInnerWindowsById->Remove(mWindowID);
1045 nsContentUtils::InnerOrOuterWindowDestroyed();
1047 #ifdef DEBUG
1048 if (MOZ_LOG_TEST(gDocShellAndDOMWindowLeakLogging, LogLevel::Info)) {
1049 nsAutoCString url;
1050 if (mLastOpenedURI) {
1051 url = mLastOpenedURI->GetSpecOrDefault();
1053 // Data URLs can be very long, so truncate to avoid flooding the log.
1054 const uint32_t maxURLLength = 1000;
1055 if (url.Length() > maxURLLength) {
1056 url.Truncate(maxURLLength);
1060 nsGlobalWindowOuter* outer = nsGlobalWindowOuter::Cast(mOuterWindow);
1061 MOZ_LOG(
1062 gDocShellAndDOMWindowLeakLogging, LogLevel::Info,
1063 ("--DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p] [url = "
1064 "%s]\n",
1065 nsContentUtils::GetCurrentInnerOrOuterWindowCount(),
1066 static_cast<void*>(ToCanonicalSupports(this)), getpid(), mSerial,
1067 static_cast<void*>(ToCanonicalSupports(outer)), url.get()));
1069 #endif
1070 MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
1071 ("DOMWINDOW %p destroyed", this));
1073 Telemetry::Accumulate(Telemetry::INNERWINDOWS_WITH_MUTATION_LISTENERS,
1074 mMutationBits ? 1 : 0);
1076 // An inner window is destroyed, pull it out of the outer window's
1077 // list if inner windows.
1079 PR_REMOVE_LINK(this);
1081 // If our outer window's inner window is this window, null out the
1082 // outer window's reference to this window that's being deleted.
1083 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
1084 if (outer) {
1085 outer->MaybeClearInnerWindow(this);
1088 // We don't have to leave the tab group if we are an inner window.
1090 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
1091 if (ac) ac->RemoveWindowAsListener(this);
1093 nsLayoutStatics::Release();
1096 // static
1097 void nsGlobalWindowInner::ShutDown() {
1098 AssertIsOnMainThread();
1100 if (gDumpFile && gDumpFile != stdout) {
1101 fclose(gDumpFile);
1103 gDumpFile = nullptr;
1105 delete sInnerWindowsById;
1106 sInnerWindowsById = nullptr;
1109 void nsGlobalWindowInner::FreeInnerObjects() {
1110 if (IsDying()) {
1111 return;
1113 StartDying();
1115 if (mDoc && mDoc->GetWindowContext()) {
1116 // The document is about to lose its window, so this is a good time to send
1117 // our page use counters.
1119 // (We also do this in Document::SetScriptGlobalObject(nullptr), which
1120 // catches most cases of documents losing their window, but not all.)
1121 mDoc->SendPageUseCounters();
1124 // Make sure that this is called before we null out the document and
1125 // other members that the window destroyed observers could
1126 // re-create.
1127 NotifyDOMWindowDestroyed(this);
1128 if (auto* reporter = nsWindowMemoryReporter::Get()) {
1129 reporter->ObserveDOMWindowDetached(this);
1132 // Kill all of the workers for this window.
1133 CancelWorkersForWindow(*this);
1135 for (RefPtr<mozilla::dom::SharedWorker> pinnedWorker :
1136 mSharedWorkers.ForwardRange()) {
1137 pinnedWorker->Close();
1140 if (mTimeoutManager) {
1141 mTimeoutManager->ClearAllTimeouts();
1144 DisableIdleCallbackRequests();
1146 mChromeEventHandler = nullptr;
1148 if (mListenerManager) {
1149 mListenerManager->RemoveAllListeners();
1150 mListenerManager->Disconnect();
1151 mListenerManager = nullptr;
1154 mHistory = nullptr;
1156 if (mNavigator) {
1157 mNavigator->OnNavigation();
1158 mNavigator->Invalidate();
1159 mNavigator = nullptr;
1162 mScreen = nullptr;
1164 if (mDoc) {
1165 // Remember the document's principal, URI, and CSP.
1166 mDocumentPrincipal = mDoc->NodePrincipal();
1167 mDocumentCookiePrincipal = mDoc->EffectiveCookiePrincipal();
1168 mDocumentStoragePrincipal = mDoc->EffectiveStoragePrincipal();
1169 mDocumentPartitionedPrincipal = mDoc->PartitionedPrincipal();
1170 mDocumentURI = mDoc->GetDocumentURI();
1171 mDocBaseURI = mDoc->GetDocBaseURI();
1172 mDocumentCsp = mDoc->GetCsp();
1174 while (mDoc->EventHandlingSuppressed()) {
1175 mDoc->UnsuppressEventHandlingAndFireEvents(false);
1179 // Remove our reference to the document and the document principal.
1180 mFocusedElement = nullptr;
1182 if (mIndexedDB) {
1183 mIndexedDB->DisconnectFromGlobal(this);
1184 mIndexedDB = nullptr;
1187 nsIGlobalObject::UnlinkObjectsInGlobal();
1189 NotifyWindowIDDestroyed("inner-window-destroyed");
1191 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
1192 mAudioContexts[i]->OnWindowDestroy();
1194 mAudioContexts.Clear();
1196 for (MediaKeys* mediaKeys : mMediaKeysInstances) {
1197 mediaKeys->OnInnerWindowDestroy();
1199 mMediaKeysInstances.Clear();
1201 DisableGamepadUpdates();
1202 mHasGamepad = false;
1203 mGamepads.Clear();
1204 DisableVRUpdates();
1205 mHasXRSession = false;
1206 mHasVRDisplayActivateEvents = false;
1207 mXRRuntimeDetectionInFlight = false;
1208 mXRPermissionRequestInFlight = false;
1209 mXRPermissionGranted = false;
1210 mVRDisplays.Clear();
1212 // This breaks a cycle between the window and the ClientSource object.
1213 mClientSource.reset();
1215 if (mWindowGlobalChild) {
1216 // Remove any remaining listeners.
1217 int64_t nListeners = mWindowGlobalChild->BeforeUnloadListeners();
1218 for (int64_t i = 0; i < nListeners; ++i) {
1219 mWindowGlobalChild->BeforeUnloadRemoved();
1221 MOZ_ASSERT(mWindowGlobalChild->BeforeUnloadListeners() == 0);
1224 // If we have any promiseDocumentFlushed callbacks, fire them now so
1225 // that the Promises can resolve.
1226 CallDocumentFlushedResolvers(/* aUntilExhaustion = */ true);
1228 DisconnectEventTargetObjects();
1230 #ifdef MOZ_WIDGET_ANDROID
1231 DisableOrientationChangeListener();
1232 #endif
1234 if (mObserver) {
1235 if (nsCOMPtr<nsIObserverService> os = services::GetObserverService()) {
1236 os->RemoveObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC);
1237 os->RemoveObserver(mObserver, MEMORY_PRESSURE_OBSERVER_TOPIC);
1238 os->RemoveObserver(mObserver, PERMISSION_CHANGED_TOPIC);
1239 os->RemoveObserver(mObserver, "screen-information-changed");
1242 RefPtr<StorageNotifierService> sns = StorageNotifierService::GetOrCreate();
1243 if (sns) {
1244 sns->Unregister(mObserver);
1247 Preferences::RemoveObserver(mObserver, "intl.accept_languages");
1249 // Drop its reference to this dying window, in case for some bogus reason
1250 // the object stays around.
1251 mObserver->Forget();
1254 mMenubar = nullptr;
1255 mToolbar = nullptr;
1256 mLocationbar = nullptr;
1257 mPersonalbar = nullptr;
1258 mStatusbar = nullptr;
1259 mScrollbars = nullptr;
1261 mConsole = nullptr;
1263 mPaintWorklet = nullptr;
1265 mExternal = nullptr;
1266 mInstallTrigger = nullptr;
1268 if (mLocalStorage) {
1269 mLocalStorage->Disconnect();
1270 mLocalStorage = nullptr;
1272 mSessionStorage = nullptr;
1273 mPerformance = nullptr;
1275 mContentMediaController = nullptr;
1277 if (mWebTaskScheduler) {
1278 mWebTaskScheduler->Disconnect();
1279 mWebTaskScheduler = nullptr;
1282 mSharedWorkers.Clear();
1284 #ifdef MOZ_WEBSPEECH
1285 mSpeechSynthesis = nullptr;
1286 #endif
1288 mGlean = nullptr;
1289 mGleanPings = nullptr;
1291 mParentTarget = nullptr;
1293 if (mCleanMessageManager) {
1294 MOZ_ASSERT(mIsChrome, "only chrome should have msg manager cleaned");
1295 if (mChromeFields.mMessageManager) {
1296 mChromeFields.mMessageManager->Disconnect();
1300 if (mWindowGlobalChild && !mWindowGlobalChild->IsClosed()) {
1301 mWindowGlobalChild->Destroy();
1304 mIntlUtils = nullptr;
1306 HintIsLoading(false);
1309 //*****************************************************************************
1310 // nsGlobalWindowInner::nsISupports
1311 //*****************************************************************************
1313 // QueryInterface implementation for nsGlobalWindowInner
1314 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGlobalWindowInner)
1315 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
1316 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, EventTarget)
1317 NS_INTERFACE_MAP_ENTRY(nsIDOMWindow)
1318 NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
1319 NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObject)
1320 NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
1321 NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget)
1322 NS_INTERFACE_MAP_ENTRY(nsPIDOMWindowInner)
1323 NS_INTERFACE_MAP_ENTRY(mozIDOMWindow)
1324 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
1325 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
1326 NS_INTERFACE_MAP_END
1328 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGlobalWindowInner)
1329 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGlobalWindowInner)
1331 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGlobalWindowInner)
1332 if (tmp->IsBlackForCC(false)) {
1333 if (nsCCUncollectableMarker::InGeneration(tmp->mCanSkipCCGeneration)) {
1334 return true;
1336 tmp->mCanSkipCCGeneration = nsCCUncollectableMarker::sGeneration;
1337 if (EventListenerManager* elm = tmp->GetExistingListenerManager()) {
1338 elm->MarkForCC();
1340 if (tmp->mTimeoutManager) {
1341 tmp->mTimeoutManager->UnmarkGrayTimers();
1343 return true;
1345 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
1347 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsGlobalWindowInner)
1348 return tmp->IsBlackForCC(true);
1349 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
1351 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsGlobalWindowInner)
1352 return tmp->IsBlackForCC(false);
1353 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
1355 NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalWindowInner)
1357 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindowInner)
1358 if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
1359 char name[512];
1360 nsAutoCString uri;
1361 if (tmp->mDoc && tmp->mDoc->GetDocumentURI()) {
1362 uri = tmp->mDoc->GetDocumentURI()->GetSpecOrDefault();
1364 SprintfLiteral(name, "nsGlobalWindowInner # %" PRIu64 " inner %s",
1365 tmp->mWindowID, uri.get());
1366 cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
1367 } else {
1368 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsGlobalWindowInner, tmp->mRefCnt.get())
1371 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNavigator)
1373 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance)
1375 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWebTaskScheduler)
1377 #ifdef MOZ_WEBSPEECH
1378 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSpeechSynthesis)
1379 #endif
1381 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlean)
1382 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGleanPings)
1384 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOuterWindow)
1386 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTopInnerWindow)
1388 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager)
1390 if (tmp->mTimeoutManager) {
1391 tmp->mTimeoutManager->ForEachUnorderedTimeout([&cb](Timeout* timeout) {
1392 cb.NoteNativeChild(timeout, NS_CYCLE_COLLECTION_PARTICIPANT(Timeout));
1396 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocation)
1397 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHistory)
1398 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCustomElements)
1399 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSharedWorkers)
1400 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStorage)
1401 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSessionStorage)
1402 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndexedDB)
1403 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPrincipal)
1404 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentCookiePrincipal)
1405 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentStoragePrincipal)
1406 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPartitionedPrincipal)
1407 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentCsp)
1408 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowserChild)
1409 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDoc)
1411 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleRequestExecutor)
1412 for (IdleRequest* request : tmp->mIdleRequestCallbacks) {
1413 cb.NoteNativeChild(request, NS_CYCLE_COLLECTION_PARTICIPANT(IdleRequest));
1416 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mClientSource)
1418 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGamepads)
1420 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCacheStorage)
1421 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVRDisplays)
1423 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDebuggerNotificationManager)
1425 // Traverse stuff from nsPIDOMWindow
1426 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeEventHandler)
1427 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentTarget)
1428 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFocusedElement)
1429 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowsingContext)
1430 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindowGlobalChild)
1432 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMenubar)
1433 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mToolbar)
1434 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocationbar)
1435 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPersonalbar)
1436 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStatusbar)
1437 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScrollbars)
1438 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCrypto)
1439 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsole)
1440 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPaintWorklet)
1441 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExternal)
1442 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInstallTrigger)
1443 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIntlUtils)
1444 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVisualViewport)
1446 tmp->TraverseObjectsInGlobal(cb);
1448 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeFields.mMessageManager)
1449 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeFields.mGroupMessageManagers)
1451 for (size_t i = 0; i < tmp->mDocumentFlushedResolvers.Length(); i++) {
1452 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentFlushedResolvers[i]->mPromise);
1453 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentFlushedResolvers[i]->mCallback);
1456 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1458 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindowInner)
1459 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
1460 if (sInnerWindowsById) {
1461 sInnerWindowsById->Remove(tmp->mWindowID);
1464 JSObject* wrapper = tmp->GetWrapperPreserveColor();
1465 if (wrapper) {
1466 // Mark our realm as dead, so the JS engine won't hand out our
1467 // global after this point.
1468 JS::SetRealmNonLive(js::GetNonCCWObjectRealm(wrapper));
1471 NS_IMPL_CYCLE_COLLECTION_UNLINK(mNavigator)
1473 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance)
1475 if (tmp->mWebTaskScheduler) {
1476 tmp->mWebTaskScheduler->Disconnect();
1477 NS_IMPL_CYCLE_COLLECTION_UNLINK(mWebTaskScheduler)
1480 #ifdef MOZ_WEBSPEECH
1481 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSpeechSynthesis)
1482 #endif
1484 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlean)
1485 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGleanPings)
1487 if (tmp->mOuterWindow) {
1488 nsGlobalWindowOuter::Cast(tmp->mOuterWindow)->MaybeClearInnerWindow(tmp);
1489 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOuterWindow)
1492 if (tmp->mListenerManager) {
1493 tmp->mListenerManager->Disconnect();
1494 NS_IMPL_CYCLE_COLLECTION_UNLINK(mListenerManager)
1497 // Here the Timeouts list would've been unlinked, but we rely on
1498 // that Timeout objects have been traced and will remove themselves
1499 // while unlinking.
1501 tmp->UpdateTopInnerWindow();
1502 NS_IMPL_CYCLE_COLLECTION_UNLINK(mTopInnerWindow)
1504 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocation)
1505 NS_IMPL_CYCLE_COLLECTION_UNLINK(mHistory)
1506 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCustomElements)
1507 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSharedWorkers)
1508 if (tmp->mLocalStorage) {
1509 tmp->mLocalStorage->Disconnect();
1510 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalStorage)
1512 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSessionStorage)
1513 if (tmp->mIndexedDB) {
1514 tmp->mIndexedDB->DisconnectFromGlobal(tmp);
1515 NS_IMPL_CYCLE_COLLECTION_UNLINK(mIndexedDB)
1517 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPrincipal)
1518 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentCookiePrincipal)
1519 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentStoragePrincipal)
1520 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPartitionedPrincipal)
1521 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentCsp)
1522 NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowserChild)
1523 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDoc)
1525 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGamepads)
1527 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCacheStorage)
1528 NS_IMPL_CYCLE_COLLECTION_UNLINK(mVRDisplays)
1530 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDebuggerNotificationManager)
1532 // Unlink stuff from nsPIDOMWindow
1533 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeEventHandler)
1534 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParentTarget)
1535 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFocusedElement)
1536 NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowsingContext)
1538 MOZ_DIAGNOSTIC_ASSERT(
1539 !tmp->mWindowGlobalChild || tmp->mWindowGlobalChild->IsClosed(),
1540 "How are we unlinking a window before its actor has been destroyed?");
1541 NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindowGlobalChild)
1543 NS_IMPL_CYCLE_COLLECTION_UNLINK(mMenubar)
1544 NS_IMPL_CYCLE_COLLECTION_UNLINK(mToolbar)
1545 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocationbar)
1546 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPersonalbar)
1547 NS_IMPL_CYCLE_COLLECTION_UNLINK(mStatusbar)
1548 NS_IMPL_CYCLE_COLLECTION_UNLINK(mScrollbars)
1549 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCrypto)
1550 NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole)
1551 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPaintWorklet)
1552 NS_IMPL_CYCLE_COLLECTION_UNLINK(mExternal)
1553 NS_IMPL_CYCLE_COLLECTION_UNLINK(mInstallTrigger)
1554 NS_IMPL_CYCLE_COLLECTION_UNLINK(mIntlUtils)
1555 NS_IMPL_CYCLE_COLLECTION_UNLINK(mVisualViewport)
1557 tmp->UnlinkObjectsInGlobal();
1559 NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleRequestExecutor)
1561 // Here the IdleRequest list would've been unlinked, but we rely on
1562 // that IdleRequest objects have been traced and will remove
1563 // themselves while unlinking.
1565 NS_IMPL_CYCLE_COLLECTION_UNLINK(mClientSource)
1567 if (tmp->IsChromeWindow()) {
1568 if (tmp->mChromeFields.mMessageManager) {
1569 static_cast<nsFrameMessageManager*>(
1570 tmp->mChromeFields.mMessageManager.get())
1571 ->Disconnect();
1572 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeFields.mMessageManager)
1574 tmp->DisconnectAndClearGroupMessageManagers();
1575 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeFields.mGroupMessageManagers)
1578 for (size_t i = 0; i < tmp->mDocumentFlushedResolvers.Length(); i++) {
1579 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentFlushedResolvers[i]->mPromise);
1580 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentFlushedResolvers[i]->mCallback);
1582 tmp->mDocumentFlushedResolvers.Clear();
1584 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
1585 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1587 #ifdef DEBUG
1588 void nsGlobalWindowInner::RiskyUnlink() {
1589 NS_CYCLE_COLLECTION_INNERNAME.Unlink(this);
1591 #endif
1593 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGlobalWindowInner)
1594 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
1595 NS_IMPL_CYCLE_COLLECTION_TRACE_END
1597 bool nsGlobalWindowInner::IsBlackForCC(bool aTracingNeeded) {
1598 if (!nsCCUncollectableMarker::sGeneration) {
1599 return false;
1602 return (nsCCUncollectableMarker::InGeneration(GetMarkedCCGeneration()) ||
1603 HasKnownLiveWrapper()) &&
1604 (!aTracingNeeded || HasNothingToTrace(ToSupports(this)));
1607 //*****************************************************************************
1608 // nsGlobalWindowInner::nsIScriptGlobalObject
1609 //*****************************************************************************
1611 bool nsGlobalWindowInner::ShouldResistFingerprinting(RFPTarget aTarget) const {
1612 if (mDoc) {
1613 return mDoc->ShouldResistFingerprinting(aTarget);
1615 return nsContentUtils::ShouldResistFingerprinting(
1616 "If we do not have a document then we do not have any context"
1617 "to make an informed RFP choice, so we fall back to the global pref",
1618 aTarget);
1621 OriginTrials nsGlobalWindowInner::Trials() const {
1622 return OriginTrials::FromWindow(this);
1625 FontFaceSet* nsGlobalWindowInner::GetFonts() {
1626 if (mDoc) {
1627 return mDoc->Fonts();
1629 return nullptr;
1632 mozilla::Result<mozilla::ipc::PrincipalInfo, nsresult>
1633 nsGlobalWindowInner::GetStorageKey() {
1634 MOZ_ASSERT(NS_IsMainThread());
1636 nsIPrincipal* principal = GetEffectiveStoragePrincipal();
1637 if (!principal) {
1638 return mozilla::Err(NS_ERROR_FAILURE);
1641 mozilla::ipc::PrincipalInfo principalInfo;
1642 nsresult rv = PrincipalToPrincipalInfo(principal, &principalInfo);
1643 if (NS_FAILED(rv)) {
1644 return mozilla::Err(rv);
1647 // Block expanded and null principals, let content and system through.
1648 if (principalInfo.type() !=
1649 mozilla::ipc::PrincipalInfo::TContentPrincipalInfo &&
1650 principalInfo.type() !=
1651 mozilla::ipc::PrincipalInfo::TSystemPrincipalInfo) {
1652 return Err(NS_ERROR_DOM_SECURITY_ERR);
1655 return std::move(principalInfo);
1658 mozilla::dom::StorageManager* nsGlobalWindowInner::GetStorageManager() {
1659 return Navigator()->Storage();
1662 // https://html.spec.whatwg.org/multipage/web-messaging.html#eligible-for-messaging
1663 // * a Window object whose associated Document is fully active
1664 bool nsGlobalWindowInner::IsEligibleForMessaging() { return IsFullyActive(); }
1666 nsresult nsGlobalWindowInner::EnsureScriptEnvironment() {
1667 // NOTE: We can't use FORWARD_TO_OUTER here because we don't want to fail if
1668 // we're called on an inactive inner window.
1669 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
1670 if (!outer) {
1671 NS_WARNING("No outer window available!");
1672 return NS_ERROR_FAILURE;
1674 return outer->EnsureScriptEnvironment();
1677 nsIScriptContext* nsGlobalWindowInner::GetScriptContext() {
1678 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
1679 if (!outer) {
1680 return nullptr;
1682 return outer->GetScriptContext();
1685 void nsGlobalWindowInner::TraceGlobalJSObject(JSTracer* aTrc) {
1686 TraceWrapper(aTrc, "active window global");
1689 void nsGlobalWindowInner::UpdateAutoplayPermission() {
1690 if (!GetWindowContext()) {
1691 return;
1693 uint32_t perm =
1694 media::AutoplayPolicy::GetSiteAutoplayPermission(GetPrincipal());
1695 if (GetWindowContext()->GetAutoplayPermission() == perm) {
1696 return;
1699 // Setting autoplay permission on a discarded context has no effect.
1700 Unused << GetWindowContext()->SetAutoplayPermission(perm);
1703 void nsGlobalWindowInner::UpdateShortcutsPermission() {
1704 if (!GetWindowContext() ||
1705 !GetWindowContext()->GetBrowsingContext()->IsTop()) {
1706 // We only cache the shortcuts permission on top-level WindowContexts
1707 // since we always check the top-level principal for the permission.
1708 return;
1711 uint32_t perm = GetShortcutsPermission(GetPrincipal());
1713 if (GetWindowContext()->GetShortcutsPermission() == perm) {
1714 return;
1717 // If the WindowContext is discarded this has no effect.
1718 Unused << GetWindowContext()->SetShortcutsPermission(perm);
1721 /* static */
1722 uint32_t nsGlobalWindowInner::GetShortcutsPermission(nsIPrincipal* aPrincipal) {
1723 uint32_t perm = nsIPermissionManager::DENY_ACTION;
1724 nsCOMPtr<nsIPermissionManager> permMgr =
1725 mozilla::components::PermissionManager::Service();
1726 if (aPrincipal && permMgr) {
1727 permMgr->TestExactPermissionFromPrincipal(aPrincipal, "shortcuts"_ns,
1728 &perm);
1730 return perm;
1733 void nsGlobalWindowInner::UpdatePopupPermission() {
1734 if (!GetWindowContext()) {
1735 return;
1738 uint32_t perm = PopupBlocker::GetPopupPermission(GetPrincipal());
1739 if (GetWindowContext()->GetPopupPermission() == perm) {
1740 return;
1743 // If the WindowContext is discarded this has no effect.
1744 Unused << GetWindowContext()->SetPopupPermission(perm);
1747 void nsGlobalWindowInner::UpdatePermissions() {
1748 if (!GetWindowContext()) {
1749 return;
1752 nsCOMPtr<nsIPrincipal> principal = GetPrincipal();
1753 RefPtr<WindowContext> windowContext = GetWindowContext();
1755 WindowContext::Transaction txn;
1756 txn.SetAutoplayPermission(
1757 media::AutoplayPolicy::GetSiteAutoplayPermission(principal));
1758 txn.SetPopupPermission(PopupBlocker::GetPopupPermission(principal));
1760 if (windowContext->IsTop()) {
1761 txn.SetShortcutsPermission(GetShortcutsPermission(principal));
1764 // Setting permissions on a discarded WindowContext has no effect
1765 Unused << txn.Commit(windowContext);
1768 void nsGlobalWindowInner::InitDocumentDependentState(JSContext* aCx) {
1769 MOZ_ASSERT(mDoc);
1771 if (MOZ_LOG_TEST(gDOMLeakPRLogInner, LogLevel::Debug)) {
1772 nsIURI* uri = mDoc->GetDocumentURI();
1773 MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
1774 ("DOMWINDOW %p SetNewDocument %s", this,
1775 uri ? uri->GetSpecOrDefault().get() : ""));
1778 mFocusedElement = nullptr;
1779 mLocalStorage = nullptr;
1780 mSessionStorage = nullptr;
1781 mPerformance = nullptr;
1782 if (mWebTaskScheduler) {
1783 mWebTaskScheduler->Disconnect();
1784 mWebTaskScheduler = nullptr;
1787 // This must be called after nullifying the internal objects because here we
1788 // could recreate them, calling the getter methods, and store them into the JS
1789 // slots. If we nullify them after, the slot values and the objects will be
1790 // out of sync.
1791 ClearDocumentDependentSlots(aCx);
1793 if (!mWindowGlobalChild) {
1794 mWindowGlobalChild = WindowGlobalChild::Create(this);
1796 MOZ_ASSERT(!GetWindowContext()->HasBeenUserGestureActivated(),
1797 "WindowContext should always not have user gesture activation at "
1798 "this point.");
1800 UpdatePermissions();
1802 RefPtr<PermissionDelegateHandler> permDelegateHandler =
1803 mDoc->GetPermissionDelegateHandler();
1805 if (permDelegateHandler) {
1806 permDelegateHandler->PopulateAllDelegatedPermissions();
1809 #if defined(MOZ_WIDGET_ANDROID)
1810 // When we insert the new document to the window in the top-level browsing
1811 // context, we should reset the status of the request which is used for the
1812 // previous document.
1813 if (mWindowGlobalChild && GetBrowsingContext() &&
1814 !GetBrowsingContext()->GetParent()) {
1815 // Return value of setting synced field should be checked. See bug 1656492.
1816 Unused << GetBrowsingContext()->ResetGVAutoplayRequestStatus();
1818 #endif
1820 #ifdef DEBUG
1821 mLastOpenedURI = mDoc->GetDocumentURI();
1822 #endif
1824 Telemetry::Accumulate(Telemetry::INNERWINDOWS_WITH_MUTATION_LISTENERS,
1825 mMutationBits ? 1 : 0);
1827 // Clear our mutation bitfield.
1828 mMutationBits = 0;
1831 nsresult nsGlobalWindowInner::EnsureClientSource() {
1832 MOZ_DIAGNOSTIC_ASSERT(mDoc);
1834 bool newClientSource = false;
1836 // Get the load info for the document if we performed a load. Be careful not
1837 // to look at local URLs, though. Local URLs are those that have a scheme of:
1838 // * about:
1839 // * data:
1840 // * blob:
1841 // We also do an additional check here so that we only treat about:blank
1842 // and about:srcdoc as local URLs. Other internal firefox about: URLs should
1843 // not be treated this way.
1844 nsCOMPtr<nsILoadInfo> loadInfo;
1845 nsCOMPtr<nsIChannel> channel = mDoc->GetChannel();
1846 if (channel) {
1847 nsCOMPtr<nsIURI> uri;
1848 Unused << channel->GetURI(getter_AddRefs(uri));
1850 bool ignoreLoadInfo = false;
1852 // Note, this is mostly copied from NS_IsAboutBlank(). Its duplicated
1853 // here so we can efficiently check about:srcdoc as well.
1854 if (uri->SchemeIs("about")) {
1855 nsCString spec = uri->GetSpecOrDefault();
1856 ignoreLoadInfo = spec.EqualsLiteral("about:blank") ||
1857 spec.EqualsLiteral("about:srcdoc");
1858 } else {
1859 // Its not an about: URL, so now check for our other URL types.
1860 ignoreLoadInfo = uri->SchemeIs("data") || uri->SchemeIs("blob");
1863 if (!ignoreLoadInfo) {
1864 loadInfo = channel->LoadInfo();
1868 // Take the initial client source from the docshell immediately. Even if we
1869 // don't end up using it here we should consume it.
1870 UniquePtr<ClientSource> initialClientSource;
1871 nsIDocShell* docshell = GetDocShell();
1872 if (docshell) {
1873 initialClientSource = docshell->TakeInitialClientSource();
1876 // Try to get the reserved client from the LoadInfo. A Client is
1877 // reserved at the start of the channel load if there is not an
1878 // initial about:blank document that will be reused. It is also
1879 // created if the channel load encounters a cross-origin redirect.
1880 if (loadInfo) {
1881 UniquePtr<ClientSource> reservedClient =
1882 loadInfo->TakeReservedClientSource();
1883 if (reservedClient) {
1884 mClientSource.reset();
1885 mClientSource = std::move(reservedClient);
1886 newClientSource = true;
1890 // We don't have a LoadInfo reserved client, but maybe we should
1891 // be inheriting an initial one from the docshell. This means
1892 // that the docshell started the channel load before creating the
1893 // initial about:blank document. This is an optimization, though,
1894 // and it created an initial Client as a placeholder for the document.
1895 // In this case we want to inherit this placeholder Client here.
1896 if (!mClientSource) {
1897 mClientSource = std::move(initialClientSource);
1898 if (mClientSource) {
1899 newClientSource = true;
1903 nsCOMPtr<nsIPrincipal> foreignPartitionedPrincipal;
1905 nsresult rv = StoragePrincipalHelper::GetPrincipal(
1906 this,
1907 StaticPrefs::privacy_partition_serviceWorkers()
1908 ? StoragePrincipalHelper::eForeignPartitionedPrincipal
1909 : StoragePrincipalHelper::eRegularPrincipal,
1910 getter_AddRefs(foreignPartitionedPrincipal));
1911 NS_ENSURE_SUCCESS(rv, rv);
1913 // Verify the final ClientSource principal matches the final document
1914 // principal. The ClientChannelHelper handles things like network
1915 // redirects, but there are other ways the document principal can change.
1916 // For example, if something sets the nsIChannel.owner property, then
1917 // the final channel principal can be anything. Unfortunately there is
1918 // no good way to detect this until after the channel completes loading.
1920 // For now we handle this just by reseting the ClientSource. This will
1921 // result in a new ClientSource with the correct principal being created.
1922 // To APIs like ServiceWorker and Clients API it will look like there was
1923 // an initial content page created that was then immediately replaced.
1924 // This is pretty close to what we are actually doing.
1925 if (mClientSource) {
1926 auto principalOrErr = mClientSource->Info().GetPrincipal();
1927 nsCOMPtr<nsIPrincipal> clientPrincipal =
1928 principalOrErr.isOk() ? principalOrErr.unwrap() : nullptr;
1929 if (!clientPrincipal ||
1930 !clientPrincipal->Equals(foreignPartitionedPrincipal)) {
1931 mClientSource.reset();
1935 // If we don't have a reserved client or an initial client, then create
1936 // one now. This can happen in certain cases where we avoid preallocating
1937 // the client in the docshell. This mainly occurs in situations where
1938 // the principal is not clearly inherited from the parent; e.g. sandboxed
1939 // iframes, window.open(), etc.
1941 // We also do this late ClientSource creation if the final document ended
1942 // up with a different principal.
1944 // TODO: We may not be marking initial about:blank documents created
1945 // this way as controlled by a service worker properly. The
1946 // controller should be coming from the same place as the inheritted
1947 // principal. We do this in docshell, but as mentioned we aren't
1948 // smart enough to handle all cases yet. For example, a
1949 // window.open() with new URL should inherit the controller from
1950 // the opener, but we probably don't handle that yet.
1951 if (!mClientSource) {
1952 mClientSource = ClientManager::CreateSource(
1953 ClientType::Window, SerialEventTarget(), foreignPartitionedPrincipal);
1954 MOZ_DIAGNOSTIC_ASSERT(mClientSource);
1955 newClientSource = true;
1957 // Note, we don't apply the loadinfo controller below if we create
1958 // the ClientSource here.
1961 // The load may have started controlling the Client as well. If
1962 // so, mark it as controlled immediately here. The actor may
1963 // or may not have been notified by the parent side about being
1964 // controlled yet.
1966 // Note: We should be careful not to control a client that was created late.
1967 // These clients were not seen by the ServiceWorkerManager when it
1968 // marked the LoadInfo controlled and it won't know about them. Its
1969 // also possible we are creating the client late due to the final
1970 // principal changing and these clients should definitely not be
1971 // controlled by a service worker with a different principal.
1972 else if (loadInfo) {
1973 const Maybe<ServiceWorkerDescriptor> controller = loadInfo->GetController();
1974 if (controller.isSome()) {
1975 mClientSource->SetController(controller.ref());
1978 // We also have to handle the case where te initial about:blank is
1979 // controlled due to inheritting the service worker from its parent,
1980 // but the actual nsIChannel load is not covered by any service worker.
1981 // In this case we want the final page to be uncontrolled. There is
1982 // an open spec issue about how exactly this should be handled, but for
1983 // now we just force creation of a new ClientSource to clear the
1984 // controller.
1986 // https://github.com/w3c/ServiceWorker/issues/1232
1988 else if (mClientSource->GetController().isSome()) {
1989 mClientSource.reset();
1990 mClientSource = ClientManager::CreateSource(
1991 ClientType::Window, SerialEventTarget(), foreignPartitionedPrincipal);
1992 MOZ_DIAGNOSTIC_ASSERT(mClientSource);
1993 newClientSource = true;
1997 if (mClientSource) {
1998 // Generally the CSP is stored within the Client and cached on the document.
1999 // At the time of CSP parsing however, the Client has not been created yet,
2000 // hence we store the CSP on the document and propagate/sync the CSP with
2001 // Client here when we create the Client.
2002 mClientSource->SetCsp(mDoc->GetCsp());
2004 DocGroup* docGroup = GetDocGroup();
2005 MOZ_DIAGNOSTIC_ASSERT(docGroup);
2006 mClientSource->SetAgentClusterId(docGroup->AgentClusterId());
2008 if (mWindowGlobalChild) {
2009 mWindowGlobalChild->SendSetClientInfo(mClientSource->Info().ToIPC());
2013 // Its possible that we got a client just after being frozen in
2014 // the bfcache. In that case freeze the client immediately.
2015 if (newClientSource && IsFrozen()) {
2016 mClientSource->Freeze();
2019 return NS_OK;
2022 nsresult nsGlobalWindowInner::ExecutionReady() {
2023 nsresult rv = EnsureClientSource();
2024 NS_ENSURE_SUCCESS(rv, rv);
2026 rv = mClientSource->WindowExecutionReady(this);
2027 NS_ENSURE_SUCCESS(rv, rv);
2029 return NS_OK;
2032 void nsGlobalWindowInner::UpdateParentTarget() {
2033 // NOTE: This method is identical to
2034 // nsGlobalWindowOuter::UpdateParentTarget(). IF YOU UPDATE THIS METHOD,
2035 // UPDATE THE OTHER ONE TOO!
2037 // Try to get our frame element's tab child global (its in-process message
2038 // manager). If that fails, fall back to the chrome event handler's tab
2039 // child global, and if it doesn't have one, just use the chrome event
2040 // handler itself.
2042 nsPIDOMWindowOuter* outer = GetOuterWindow();
2043 if (!outer) {
2044 return;
2046 nsCOMPtr<Element> frameElement = outer->GetFrameElementInternal();
2047 nsCOMPtr<EventTarget> eventTarget =
2048 nsContentUtils::TryGetBrowserChildGlobal(frameElement);
2050 if (!eventTarget) {
2051 nsGlobalWindowOuter* topWin = GetInProcessScriptableTopInternal();
2052 if (topWin) {
2053 frameElement = topWin->GetFrameElementInternal();
2054 eventTarget = nsContentUtils::TryGetBrowserChildGlobal(frameElement);
2058 if (!eventTarget) {
2059 eventTarget = nsContentUtils::TryGetBrowserChildGlobal(mChromeEventHandler);
2062 if (!eventTarget) {
2063 eventTarget = mChromeEventHandler;
2066 mParentTarget = eventTarget;
2069 EventTarget* nsGlobalWindowInner::GetTargetForDOMEvent() {
2070 return GetOuterWindowInternal();
2073 void nsGlobalWindowInner::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
2074 EventMessage msg = aVisitor.mEvent->mMessage;
2076 aVisitor.mCanHandle = true;
2077 aVisitor.mForceContentDispatch = true; // FIXME! Bug 329119
2078 if (msg == eResize && aVisitor.mEvent->IsTrusted()) {
2079 // Checking whether the event target is an inner window or not, so we can
2080 // keep the old behavior also in case a child window is handling resize.
2081 if (aVisitor.mEvent->mOriginalTarget &&
2082 aVisitor.mEvent->mOriginalTarget->IsInnerWindow()) {
2083 mIsHandlingResizeEvent = true;
2085 } else if (msg == eMouseDown && aVisitor.mEvent->IsTrusted()) {
2086 sMouseDown = true;
2087 } else if ((msg == eMouseUp || msg == eDragEnd) &&
2088 aVisitor.mEvent->IsTrusted()) {
2089 sMouseDown = false;
2090 if (sDragServiceDisabled) {
2091 nsCOMPtr<nsIDragService> ds =
2092 do_GetService("@mozilla.org/widget/dragservice;1");
2093 if (ds) {
2094 sDragServiceDisabled = false;
2095 ds->Unsuppress();
2100 aVisitor.SetParentTarget(GetParentTarget(), true);
2103 void nsGlobalWindowInner::FireFrameLoadEvent() {
2104 // If we're not in a content frame, or are at a BrowsingContext tree boundary,
2105 // such as the content-chrome boundary, don't fire the "load" event.
2106 if (GetBrowsingContext()->IsTopContent() ||
2107 GetBrowsingContext()->IsChrome()) {
2108 return;
2111 // If embedder is same-process, fire the event on our embedder element.
2113 // XXX: Bug 1440212 is looking into potentially changing this behaviour to act
2114 // more like the remote case when in-process.
2115 RefPtr<Element> element = GetBrowsingContext()->GetEmbedderElement();
2116 if (element) {
2117 nsEventStatus status = nsEventStatus_eIgnore;
2118 WidgetEvent event(/* aIsTrusted = */ true, eLoad);
2119 event.mFlags.mBubbles = false;
2120 event.mFlags.mCancelable = false;
2122 if (mozilla::dom::DocGroup::TryToLoadIframesInBackground()) {
2123 nsDocShell* ds = nsDocShell::Cast(GetDocShell());
2125 if (ds && !ds->HasFakeOnLoadDispatched()) {
2126 EventDispatcher::Dispatch(element, nullptr, &event, nullptr, &status);
2128 } else {
2129 // Most of the time we could get a pres context to pass in here,
2130 // but not always (i.e. if this window is not shown there won't
2131 // be a pres context available). Since we're not firing a GUI
2132 // event we don't need a pres context anyway so we just pass
2133 // null as the pres context all the time here.
2134 EventDispatcher::Dispatch(element, nullptr, &event, nullptr, &status);
2136 return;
2139 // We don't have an in-process embedder. Try to get our `BrowserChild` actor
2140 // to send a message to that embedder. We want to double-check that our outer
2141 // window is actually the one at the root of this browserChild though, just in
2142 // case.
2143 RefPtr<BrowserChild> browserChild =
2144 BrowserChild::GetFrom(static_cast<nsPIDOMWindowInner*>(this));
2145 if (browserChild) {
2146 // Double-check that our outer window is actually at the root of this
2147 // `BrowserChild`, in case we're in an odd maybe-unhosted situation like a
2148 // print preview dialog.
2149 nsCOMPtr<nsPIDOMWindowOuter> rootOuter =
2150 do_GetInterface(browserChild->WebNavigation());
2151 if (!rootOuter || rootOuter != GetOuterWindow()) {
2152 return;
2155 mozilla::Unused << browserChild->SendMaybeFireEmbedderLoadEvents(
2156 EmbedderElementEventType::LoadEvent);
2160 nsresult nsGlobalWindowInner::PostHandleEvent(EventChainPostVisitor& aVisitor) {
2161 // Return early if there is nothing to do.
2162 switch (aVisitor.mEvent->mMessage) {
2163 case eResize:
2164 case eUnload:
2165 case eLoad:
2166 break;
2167 default:
2168 return NS_OK;
2171 /* mChromeEventHandler and mContext go dangling in the middle of this
2172 function under some circumstances (events that destroy the window)
2173 without this addref. */
2174 RefPtr<EventTarget> kungFuDeathGrip1(mChromeEventHandler);
2175 mozilla::Unused
2176 << kungFuDeathGrip1; // These aren't referred to through the function
2177 nsCOMPtr<nsIScriptContext> kungFuDeathGrip2(GetContextInternal());
2178 mozilla::Unused
2179 << kungFuDeathGrip2; // These aren't referred to through the function
2181 if (aVisitor.mEvent->mMessage == eResize) {
2182 mIsHandlingResizeEvent = false;
2183 } else if (aVisitor.mEvent->mMessage == eUnload &&
2184 aVisitor.mEvent->IsTrusted()) {
2185 // If any VR display presentation is active at unload, the next page
2186 // will receive a vrdisplayactive event to indicate that it should
2187 // immediately begin vr presentation. This should occur when navigating
2188 // forwards, navigating backwards, and on page reload.
2189 for (const auto& display : mVRDisplays) {
2190 if (display->IsPresenting()) {
2191 display->StartVRNavigation();
2192 // Save this VR display ID to trigger vrdisplayactivate event
2193 // after the next load event.
2194 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
2195 if (outer) {
2196 outer->SetAutoActivateVRDisplayID(display->DisplayId());
2199 // XXX The WebVR 1.1 spec does not define which of multiple VR
2200 // presenting VR displays will be chosen during navigation.
2201 // As the underlying platform VR API's currently only allow a single
2202 // VR display, it is safe to choose the first VR display for now.
2203 break;
2206 mIsDocumentLoaded = false;
2207 // Tell the parent process that the document is not loaded.
2208 if (mWindowGlobalChild) {
2209 mWindowGlobalChild->SendUpdateDocumentHasLoaded(mIsDocumentLoaded);
2211 } else if (aVisitor.mEvent->mMessage == eLoad &&
2212 aVisitor.mEvent->IsTrusted()) {
2213 // This is page load event since load events don't propagate to |window|.
2214 // @see Document::GetEventTargetParent.
2215 mIsDocumentLoaded = true;
2216 // Tell the parent process that the document is loaded.
2217 if (mWindowGlobalChild) {
2218 mWindowGlobalChild->SendUpdateDocumentHasLoaded(mIsDocumentLoaded);
2221 mTimeoutManager->OnDocumentLoaded();
2223 MOZ_ASSERT(aVisitor.mEvent->IsTrusted());
2224 FireFrameLoadEvent();
2226 if (mVREventObserver) {
2227 mVREventObserver->NotifyAfterLoad();
2230 uint32_t autoActivateVRDisplayID = 0;
2231 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
2232 if (outer) {
2233 autoActivateVRDisplayID = outer->GetAutoActivateVRDisplayID();
2235 if (autoActivateVRDisplayID) {
2236 DispatchVRDisplayActivate(autoActivateVRDisplayID,
2237 VRDisplayEventReason::Navigation);
2241 return NS_OK;
2244 nsresult nsGlobalWindowInner::DefineArgumentsProperty(nsIArray* aArguments) {
2245 nsIScriptContext* ctx = GetOuterWindowInternal()->mContext;
2246 NS_ENSURE_TRUE(aArguments && ctx, NS_ERROR_NOT_INITIALIZED);
2248 JS::Rooted<JSObject*> obj(RootingCx(), GetWrapperPreserveColor());
2249 return ctx->SetProperty(obj, "arguments", aArguments);
2252 //*****************************************************************************
2253 // nsGlobalWindowInner::nsIScriptObjectPrincipal
2254 //*****************************************************************************
2256 nsIPrincipal* nsGlobalWindowInner::GetPrincipal() {
2257 if (mDoc) {
2258 // If we have a document, get the principal from the document
2259 return mDoc->NodePrincipal();
2262 if (mDocumentPrincipal) {
2263 return mDocumentPrincipal;
2266 // If we don't have a principal and we don't have a document we
2267 // ask the parent window for the principal. This can happen when
2268 // loading a frameset that has a <frame src="javascript:xxx">, in
2269 // that case the global window is used in JS before we've loaded
2270 // a document into the window.
2272 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
2273 do_QueryInterface(GetInProcessParentInternal());
2275 if (objPrincipal) {
2276 return objPrincipal->GetPrincipal();
2279 return nullptr;
2282 nsIPrincipal* nsGlobalWindowInner::GetEffectiveCookiePrincipal() {
2283 if (mDoc) {
2284 // If we have a document, get the principal from the document
2285 return mDoc->EffectiveCookiePrincipal();
2288 if (mDocumentCookiePrincipal) {
2289 return mDocumentCookiePrincipal;
2292 // If we don't have a cookie principal and we don't have a document we ask
2293 // the parent window for the cookie principal.
2295 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
2296 do_QueryInterface(GetInProcessParentInternal());
2298 if (objPrincipal) {
2299 return objPrincipal->GetEffectiveCookiePrincipal();
2302 return nullptr;
2305 nsIPrincipal* nsGlobalWindowInner::GetEffectiveStoragePrincipal() {
2306 if (mDoc) {
2307 // If we have a document, get the principal from the document
2308 return mDoc->EffectiveStoragePrincipal();
2311 if (mDocumentStoragePrincipal) {
2312 return mDocumentStoragePrincipal;
2315 // If we don't have a cookie principal and we don't have a document we ask
2316 // the parent window for the cookie principal.
2318 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
2319 do_QueryInterface(GetInProcessParentInternal());
2321 if (objPrincipal) {
2322 return objPrincipal->GetEffectiveStoragePrincipal();
2325 return nullptr;
2328 nsIPrincipal* nsGlobalWindowInner::PartitionedPrincipal() {
2329 if (mDoc) {
2330 // If we have a document, get the principal from the document
2331 return mDoc->PartitionedPrincipal();
2334 if (mDocumentPartitionedPrincipal) {
2335 return mDocumentPartitionedPrincipal;
2338 // If we don't have a partitioned principal and we don't have a document we
2339 // ask the parent window for the partitioned principal.
2341 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
2342 do_QueryInterface(GetInProcessParentInternal());
2344 if (objPrincipal) {
2345 return objPrincipal->PartitionedPrincipal();
2348 return nullptr;
2351 //*****************************************************************************
2352 // nsGlobalWindowInner::nsIDOMWindow
2353 //*****************************************************************************
2355 bool nsPIDOMWindowInner::AddAudioContext(AudioContext* aAudioContext) {
2356 mAudioContexts.AppendElement(aAudioContext);
2358 // Return true if the context should be muted and false if not.
2359 nsIDocShell* docShell = GetDocShell();
2360 return docShell && !docShell->GetAllowMedia() && !aAudioContext->IsOffline();
2363 void nsPIDOMWindowInner::RemoveAudioContext(AudioContext* aAudioContext) {
2364 mAudioContexts.RemoveElement(aAudioContext);
2367 void nsPIDOMWindowInner::MuteAudioContexts() {
2368 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
2369 if (!mAudioContexts[i]->IsOffline()) {
2370 mAudioContexts[i]->Mute();
2375 void nsPIDOMWindowInner::UnmuteAudioContexts() {
2376 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
2377 if (!mAudioContexts[i]->IsOffline()) {
2378 mAudioContexts[i]->Unmute();
2383 WindowProxyHolder nsGlobalWindowInner::Window() {
2384 return WindowProxyHolder(GetBrowsingContext());
2387 Navigator* nsPIDOMWindowInner::Navigator() {
2388 if (!mNavigator) {
2389 mNavigator = new mozilla::dom::Navigator(this);
2392 return mNavigator;
2395 MediaDevices* nsPIDOMWindowInner::GetExtantMediaDevices() const {
2396 return mNavigator ? mNavigator->GetExtantMediaDevices() : nullptr;
2399 VisualViewport* nsGlobalWindowInner::VisualViewport() {
2400 if (!mVisualViewport) {
2401 mVisualViewport = new mozilla::dom::VisualViewport(this);
2404 return mVisualViewport;
2407 nsScreen* nsGlobalWindowInner::GetScreen(ErrorResult& aError) {
2408 if (!mScreen) {
2409 mScreen = nsScreen::Create(this);
2410 if (!mScreen) {
2411 aError.Throw(NS_ERROR_UNEXPECTED);
2412 return nullptr;
2416 return mScreen;
2419 nsHistory* nsGlobalWindowInner::GetHistory(ErrorResult& aError) {
2420 if (!mHistory) {
2421 mHistory = new nsHistory(this);
2424 return mHistory;
2427 CustomElementRegistry* nsGlobalWindowInner::CustomElements() {
2428 if (!mCustomElements) {
2429 mCustomElements = new CustomElementRegistry(this);
2432 return mCustomElements;
2435 CustomElementRegistry* nsGlobalWindowInner::GetExistingCustomElements() {
2436 return mCustomElements;
2439 Performance* nsPIDOMWindowInner::GetPerformance() {
2440 CreatePerformanceObjectIfNeeded();
2441 return mPerformance;
2444 void nsPIDOMWindowInner::QueuePerformanceNavigationTiming() {
2445 CreatePerformanceObjectIfNeeded();
2446 if (mPerformance) {
2447 mPerformance->QueueNavigationTimingEntry();
2451 void nsPIDOMWindowInner::CreatePerformanceObjectIfNeeded() {
2452 if (mPerformance || !mDoc) {
2453 return;
2455 RefPtr<nsDOMNavigationTiming> timing = mDoc->GetNavigationTiming();
2456 nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(mDoc->GetChannel()));
2457 bool timingEnabled = false;
2458 if (!timedChannel ||
2459 !NS_SUCCEEDED(timedChannel->GetTimingEnabled(&timingEnabled)) ||
2460 !timingEnabled) {
2461 timedChannel = nullptr;
2463 if (timing) {
2464 mPerformance = Performance::CreateForMainThread(this, mDoc->NodePrincipal(),
2465 timing, timedChannel);
2469 bool nsPIDOMWindowInner::IsSecureContext() const {
2470 return nsGlobalWindowInner::Cast(this)->IsSecureContext();
2473 void nsPIDOMWindowInner::Suspend(bool aIncludeSubWindows) {
2474 nsGlobalWindowInner::Cast(this)->Suspend(aIncludeSubWindows);
2477 void nsPIDOMWindowInner::Resume(bool aIncludeSubWindows) {
2478 nsGlobalWindowInner::Cast(this)->Resume(aIncludeSubWindows);
2481 void nsPIDOMWindowInner::SyncStateFromParentWindow() {
2482 nsGlobalWindowInner::Cast(this)->SyncStateFromParentWindow();
2485 Maybe<ClientInfo> nsPIDOMWindowInner::GetClientInfo() const {
2486 return nsGlobalWindowInner::Cast(this)->GetClientInfo();
2489 Maybe<ClientState> nsPIDOMWindowInner::GetClientState() const {
2490 return nsGlobalWindowInner::Cast(this)->GetClientState();
2493 Maybe<ServiceWorkerDescriptor> nsPIDOMWindowInner::GetController() const {
2494 return nsGlobalWindowInner::Cast(this)->GetController();
2497 void nsPIDOMWindowInner::SetCsp(nsIContentSecurityPolicy* aCsp) {
2498 return nsGlobalWindowInner::Cast(this)->SetCsp(aCsp);
2501 void nsPIDOMWindowInner::SetPreloadCsp(nsIContentSecurityPolicy* aPreloadCsp) {
2502 return nsGlobalWindowInner::Cast(this)->SetPreloadCsp(aPreloadCsp);
2505 nsIContentSecurityPolicy* nsPIDOMWindowInner::GetCsp() {
2506 return nsGlobalWindowInner::Cast(this)->GetCsp();
2509 void nsPIDOMWindowInner::NoteCalledRegisterForServiceWorkerScope(
2510 const nsACString& aScope) {
2511 nsGlobalWindowInner::Cast(this)->NoteCalledRegisterForServiceWorkerScope(
2512 aScope);
2515 void nsPIDOMWindowInner::NoteDOMContentLoaded() {
2516 nsGlobalWindowInner::Cast(this)->NoteDOMContentLoaded();
2519 bool nsGlobalWindowInner::ShouldReportForServiceWorkerScope(
2520 const nsAString& aScope) {
2521 bool result = false;
2523 nsPIDOMWindowOuter* topOuter = GetInProcessScriptableTop();
2524 NS_ENSURE_TRUE(topOuter, false);
2526 nsGlobalWindowInner* topInner =
2527 nsGlobalWindowInner::Cast(topOuter->GetCurrentInnerWindow());
2528 NS_ENSURE_TRUE(topInner, false);
2530 topInner->ShouldReportForServiceWorkerScopeInternal(
2531 NS_ConvertUTF16toUTF8(aScope), &result);
2532 return result;
2535 InstallTriggerImpl* nsGlobalWindowInner::GetInstallTrigger() {
2536 if (!mInstallTrigger &&
2537 !StaticPrefs::extensions_InstallTriggerImpl_enabled()) {
2538 // Return nullptr when InstallTriggerImpl is disabled by pref,
2539 // which does not yet break the "typeof InstallTrigger !== 'undefined"
2540 // "UA detection" use case, but prevents access to the InstallTriggerImpl
2541 // methods and properties.
2543 // NOTE: a separate pref ("extensions.InstallTrigger.enabled"), associated
2544 // to this property using the [Pref] extended attribute in Window.webidl,
2545 // does instead hide the entire InstallTrigger property.
2547 // See Bug 1754441 for more details about this deprecation.
2548 return nullptr;
2550 if (!mInstallTrigger) {
2551 ErrorResult rv;
2552 mInstallTrigger = ConstructJSImplementation<InstallTriggerImpl>(
2553 "@mozilla.org/addons/installtrigger;1", this, rv);
2554 if (rv.Failed()) {
2555 rv.SuppressException();
2556 return nullptr;
2560 return mInstallTrigger;
2563 nsIDOMWindowUtils* nsGlobalWindowInner::GetWindowUtils(ErrorResult& aRv) {
2564 FORWARD_TO_OUTER_OR_THROW(WindowUtils, (), aRv, nullptr);
2567 CallState nsGlobalWindowInner::ShouldReportForServiceWorkerScopeInternal(
2568 const nsACString& aScope, bool* aResultOut) {
2569 MOZ_DIAGNOSTIC_ASSERT(aResultOut);
2571 // First check to see if this window is controlled. If so, then we have
2572 // found a match and are done.
2573 const Maybe<ServiceWorkerDescriptor> swd = GetController();
2574 if (swd.isSome() && swd.ref().Scope() == aScope) {
2575 *aResultOut = true;
2576 return CallState::Stop;
2579 // Next, check to see if this window has called
2580 // navigator.serviceWorker.register() for this scope. If so, then treat this
2581 // as a match so console reports appear in the devtools console.
2582 if (mClientSource &&
2583 mClientSource->CalledRegisterForServiceWorkerScope(aScope)) {
2584 *aResultOut = true;
2585 return CallState::Stop;
2588 // Finally check the current docshell nsILoadGroup to see if there are any
2589 // outstanding navigation requests. If so, match the scope against the
2590 // channel's URL. We want to show console reports during the FetchEvent
2591 // intercepting the navigation itself.
2592 nsCOMPtr<nsIDocumentLoader> loader(do_QueryInterface(GetDocShell()));
2593 if (loader) {
2594 nsCOMPtr<nsILoadGroup> loadgroup;
2595 Unused << loader->GetLoadGroup(getter_AddRefs(loadgroup));
2596 if (loadgroup) {
2597 nsCOMPtr<nsISimpleEnumerator> iter;
2598 Unused << loadgroup->GetRequests(getter_AddRefs(iter));
2599 if (iter) {
2600 nsCOMPtr<nsISupports> tmp;
2601 bool hasMore = true;
2602 // Check each network request in the load group.
2603 while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) {
2604 iter->GetNext(getter_AddRefs(tmp));
2605 nsCOMPtr<nsIChannel> loadingChannel(do_QueryInterface(tmp));
2606 // Ignore subresource requests. Logging for a subresource
2607 // FetchEvent should be handled above since the client is
2608 // already controlled.
2609 if (!loadingChannel ||
2610 !nsContentUtils::IsNonSubresourceRequest(loadingChannel)) {
2611 continue;
2613 nsCOMPtr<nsIURI> loadingURL;
2614 Unused << loadingChannel->GetURI(getter_AddRefs(loadingURL));
2615 if (!loadingURL) {
2616 continue;
2618 nsAutoCString loadingSpec;
2619 Unused << loadingURL->GetSpec(loadingSpec);
2620 // Perform a simple substring comparison to match the scope
2621 // against the channel URL.
2622 if (StringBeginsWith(loadingSpec, aScope)) {
2623 *aResultOut = true;
2624 return CallState::Stop;
2631 // The current window doesn't care about this service worker, but maybe
2632 // one of our child frames does.
2633 return CallOnInProcessChildren(
2634 &nsGlobalWindowInner::ShouldReportForServiceWorkerScopeInternal, aScope,
2635 aResultOut);
2638 void nsGlobalWindowInner::NoteCalledRegisterForServiceWorkerScope(
2639 const nsACString& aScope) {
2640 if (!mClientSource) {
2641 return;
2644 mClientSource->NoteCalledRegisterForServiceWorkerScope(aScope);
2647 void nsGlobalWindowInner::NoteDOMContentLoaded() {
2648 if (!mClientSource) {
2649 return;
2652 mClientSource->NoteDOMContentLoaded();
2655 void nsGlobalWindowInner::UpdateTopInnerWindow() {
2656 if (IsTopInnerWindow() || !mTopInnerWindow) {
2657 return;
2660 mTopInnerWindow->UpdateWebSocketCount(-(int32_t)mNumOfOpenWebSockets);
2663 bool nsGlobalWindowInner::IsInSyncOperation() {
2664 return GetExtantDoc() && GetExtantDoc()->IsInSyncOperation();
2667 bool nsGlobalWindowInner::IsSharedMemoryAllowedInternal(
2668 nsIPrincipal* aPrincipal) const {
2669 MOZ_ASSERT(NS_IsMainThread());
2671 if (StaticPrefs::
2672 dom_postMessage_sharedArrayBuffer_bypassCOOP_COEP_insecure_enabled()) {
2673 return true;
2676 if (ExtensionPolicyService::GetSingleton().IsExtensionProcess()) {
2677 if (auto* basePrincipal = BasePrincipal::Cast(aPrincipal)) {
2678 if (auto* policy = basePrincipal->AddonPolicy()) {
2679 return policy->IsPrivileged();
2684 return CrossOriginIsolated();
2687 bool nsGlobalWindowInner::CrossOriginIsolated() const {
2688 MOZ_ASSERT(NS_IsMainThread());
2690 RefPtr<BrowsingContext> bc = GetBrowsingContext();
2691 MOZ_DIAGNOSTIC_ASSERT(bc);
2692 return bc->CrossOriginIsolated();
2695 WindowContext* TopWindowContext(nsPIDOMWindowInner& aWindow) {
2696 WindowContext* wc = aWindow.GetWindowContext();
2697 if (!wc) {
2698 return nullptr;
2701 return wc->TopWindowContext();
2704 void nsPIDOMWindowInner::AddPeerConnection() {
2705 MOZ_ASSERT(NS_IsMainThread());
2706 ++mActivePeerConnections;
2707 if (mActivePeerConnections == 1 && mWindowGlobalChild) {
2708 mWindowGlobalChild->SendUpdateActivePeerConnectionStatus(
2709 /*aIsAdded*/ true);
2711 // We need to present having active peer connections immediately. If we need
2712 // to wait for the parent process to come back with this information we
2713 // might start throttling.
2714 if (WindowContext* top = TopWindowContext(*this)) {
2715 top->TransientSetHasActivePeerConnections();
2720 void nsPIDOMWindowInner::RemovePeerConnection() {
2721 MOZ_ASSERT(NS_IsMainThread());
2722 MOZ_ASSERT(mActivePeerConnections > 0);
2723 --mActivePeerConnections;
2724 if (mActivePeerConnections == 0 && mWindowGlobalChild) {
2725 mWindowGlobalChild->SendUpdateActivePeerConnectionStatus(
2726 /*aIsAdded*/ false);
2730 bool nsPIDOMWindowInner::HasActivePeerConnections() {
2731 MOZ_ASSERT(NS_IsMainThread());
2733 WindowContext* topWindowContext = TopWindowContext(*this);
2734 return topWindowContext && topWindowContext->GetHasActivePeerConnections();
2737 void nsPIDOMWindowInner::AddMediaKeysInstance(MediaKeys* aMediaKeys) {
2738 MOZ_ASSERT(NS_IsMainThread());
2739 mMediaKeysInstances.AppendElement(aMediaKeys);
2740 if (mWindowGlobalChild && mMediaKeysInstances.Length() == 1) {
2741 mWindowGlobalChild->BlockBFCacheFor(BFCacheStatus::CONTAINS_EME_CONTENT);
2745 void nsPIDOMWindowInner::RemoveMediaKeysInstance(MediaKeys* aMediaKeys) {
2746 MOZ_ASSERT(NS_IsMainThread());
2747 mMediaKeysInstances.RemoveElement(aMediaKeys);
2748 if (mWindowGlobalChild && mMediaKeysInstances.IsEmpty()) {
2749 mWindowGlobalChild->UnblockBFCacheFor(BFCacheStatus::CONTAINS_EME_CONTENT);
2753 bool nsPIDOMWindowInner::HasActiveMediaKeysInstance() {
2754 MOZ_ASSERT(NS_IsMainThread());
2755 return !mMediaKeysInstances.IsEmpty();
2758 bool nsPIDOMWindowInner::IsPlayingAudio() {
2759 for (uint32_t i = 0; i < mAudioContexts.Length(); i++) {
2760 if (mAudioContexts[i]->IsRunning()) {
2761 return true;
2764 RefPtr<AudioChannelService> acs = AudioChannelService::Get();
2765 if (!acs) {
2766 return false;
2768 auto outer = GetOuterWindow();
2769 if (!outer) {
2770 // We've been unlinked and are about to die. Not a good time to pretend to
2771 // be playing audio.
2772 return false;
2774 return acs->IsWindowActive(outer);
2777 bool nsPIDOMWindowInner::IsDocumentLoaded() const { return mIsDocumentLoaded; }
2779 mozilla::dom::TimeoutManager& nsPIDOMWindowInner::TimeoutManager() {
2780 return *mTimeoutManager;
2783 bool nsPIDOMWindowInner::IsRunningTimeout() {
2784 return TimeoutManager().IsRunningTimeout();
2787 void nsPIDOMWindowInner::TryToCacheTopInnerWindow() {
2788 if (mHasTriedToCacheTopInnerWindow) {
2789 return;
2792 nsGlobalWindowInner* window = nsGlobalWindowInner::Cast(this);
2794 MOZ_ASSERT(!window->IsDying());
2796 mHasTriedToCacheTopInnerWindow = true;
2798 MOZ_ASSERT(window);
2800 if (nsCOMPtr<nsPIDOMWindowOuter> topOutter =
2801 window->GetInProcessScriptableTop()) {
2802 mTopInnerWindow = topOutter->GetCurrentInnerWindow();
2806 void nsPIDOMWindowInner::UpdateActiveIndexedDBDatabaseCount(int32_t aDelta) {
2807 MOZ_ASSERT(NS_IsMainThread());
2809 if (aDelta == 0) {
2810 return;
2813 // We count databases but not transactions because only active databases
2814 // could block throttling.
2815 uint32_t& counter = mTopInnerWindow
2816 ? mTopInnerWindow->mNumOfIndexedDBDatabases
2817 : mNumOfIndexedDBDatabases;
2819 counter += aDelta;
2822 bool nsPIDOMWindowInner::HasActiveIndexedDBDatabases() {
2823 MOZ_ASSERT(NS_IsMainThread());
2825 return mTopInnerWindow ? mTopInnerWindow->mNumOfIndexedDBDatabases > 0
2826 : mNumOfIndexedDBDatabases > 0;
2829 void nsPIDOMWindowInner::UpdateWebSocketCount(int32_t aDelta) {
2830 MOZ_ASSERT(NS_IsMainThread());
2832 if (aDelta == 0) {
2833 return;
2836 if (mTopInnerWindow && !IsTopInnerWindow()) {
2837 mTopInnerWindow->UpdateWebSocketCount(aDelta);
2840 MOZ_DIAGNOSTIC_ASSERT(
2841 aDelta > 0 || ((aDelta + mNumOfOpenWebSockets) < mNumOfOpenWebSockets));
2843 mNumOfOpenWebSockets += aDelta;
2846 bool nsPIDOMWindowInner::HasOpenWebSockets() const {
2847 MOZ_ASSERT(NS_IsMainThread());
2849 return mNumOfOpenWebSockets ||
2850 (mTopInnerWindow && mTopInnerWindow->mNumOfOpenWebSockets);
2853 bool nsPIDOMWindowInner::IsCurrentInnerWindow() const {
2854 if (mozilla::SessionHistoryInParent() && mBrowsingContext &&
2855 mBrowsingContext->IsInBFCache()) {
2856 return false;
2859 if (!mBrowsingContext || mBrowsingContext->IsDiscarded()) {
2860 // If our BrowsingContext has been discarded, we consider ourselves
2861 // still-current if we were current at the time it was discarded.
2862 return mOuterWindow && WasCurrentInnerWindow();
2865 nsPIDOMWindowOuter* outer = mBrowsingContext->GetDOMWindow();
2866 return outer && outer->GetCurrentInnerWindow() == this;
2869 bool nsPIDOMWindowInner::IsFullyActive() const {
2870 WindowContext* wc = GetWindowContext();
2871 if (!wc || wc->IsDiscarded() || !wc->IsCurrent()) {
2872 return false;
2874 return GetBrowsingContext()->AncestorsAreCurrent();
2877 void nsPIDOMWindowInner::SetAudioCapture(bool aCapture) {
2878 RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
2879 if (service) {
2880 service->SetWindowAudioCaptured(GetOuterWindow(), mWindowID, aCapture);
2884 void nsGlobalWindowInner::SetActiveLoadingState(bool aIsLoading) {
2885 MOZ_LOG(
2886 gTimeoutLog, mozilla::LogLevel::Debug,
2887 ("SetActiveLoadingState innerwindow %p: %d", (void*)this, aIsLoading));
2888 if (GetBrowsingContext()) {
2889 // Setting loading on a discarded context has no effect.
2890 Unused << GetBrowsingContext()->SetLoading(aIsLoading);
2893 if (!nsGlobalWindowInner::Cast(this)->IsChromeWindow()) {
2894 mTimeoutManager->SetLoading(aIsLoading);
2897 HintIsLoading(aIsLoading);
2900 void nsGlobalWindowInner::HintIsLoading(bool aIsLoading) {
2901 // Hint to tell the JS GC to use modified triggers during pageload.
2902 if (mHintedWasLoading != aIsLoading) {
2903 using namespace js::gc;
2904 SetPerformanceHint(danger::GetJSContext(), aIsLoading
2905 ? PerformanceHint::InPageLoad
2906 : PerformanceHint::Normal);
2907 mHintedWasLoading = aIsLoading;
2911 // nsISpeechSynthesisGetter
2913 #ifdef MOZ_WEBSPEECH
2914 SpeechSynthesis* nsGlobalWindowInner::GetSpeechSynthesis(ErrorResult& aError) {
2915 if (!mSpeechSynthesis) {
2916 mSpeechSynthesis = new SpeechSynthesis(this);
2919 return mSpeechSynthesis;
2922 bool nsGlobalWindowInner::HasActiveSpeechSynthesis() {
2923 if (mSpeechSynthesis) {
2924 return !mSpeechSynthesis->HasEmptyQueue();
2927 return false;
2930 #endif
2932 mozilla::glean::Glean* nsGlobalWindowInner::Glean() {
2933 if (!mGlean) {
2934 mGlean = new mozilla::glean::Glean(this);
2937 return mGlean;
2940 mozilla::glean::GleanPings* nsGlobalWindowInner::GleanPings() {
2941 if (!mGleanPings) {
2942 mGleanPings = new mozilla::glean::GleanPings();
2945 return mGleanPings;
2948 Nullable<WindowProxyHolder> nsGlobalWindowInner::GetParent(
2949 ErrorResult& aError) {
2950 FORWARD_TO_OUTER_OR_THROW(GetParentOuter, (), aError, nullptr);
2954 * GetInProcessScriptableParent used to be called when a script read
2955 * window.parent. Under Fission, that is now handled by
2956 * BrowsingContext::GetParent, and the result is a WindowProxyHolder rather than
2957 * an actual global window. This method still exists for legacy callers which
2958 * relied on the old logic, and require in-process windows. However, it only
2959 * works correctly when no out-of-process frames exist between this window and
2960 * the top-level window, so it should not be used in new code.
2962 * In contrast to GetRealParent, GetInProcessScriptableParent respects <iframe
2963 * mozbrowser> boundaries, so if |this| is contained by an <iframe
2964 * mozbrowser>, we will return |this| as its own parent.
2966 nsPIDOMWindowOuter* nsGlobalWindowInner::GetInProcessScriptableParent() {
2967 FORWARD_TO_OUTER(GetInProcessScriptableParent, (), nullptr);
2971 * GetInProcessScriptableTop used to be called when a script read window.top.
2972 * Under Fission, that is now handled by BrowsingContext::Top, and the result is
2973 * a WindowProxyHolder rather than an actual global window. This method still
2974 * exists for legacy callers which relied on the old logic, and require
2975 * in-process windows. However, it only works correctly when no out-of-process
2976 * frames exist between this window and the top-level window, so it should not
2977 * be used in new code.
2979 * In contrast to GetRealTop, GetInProcessScriptableTop respects <iframe
2980 * mozbrowser> boundaries. If we encounter a window owned by an <iframe
2981 * mozbrowser> while walking up the window hierarchy, we'll stop and return that
2982 * window.
2984 nsPIDOMWindowOuter* nsGlobalWindowInner::GetInProcessScriptableTop() {
2985 FORWARD_TO_OUTER(GetInProcessScriptableTop, (), nullptr);
2988 void nsGlobalWindowInner::GetContent(JSContext* aCx,
2989 JS::MutableHandle<JSObject*> aRetval,
2990 CallerType aCallerType,
2991 ErrorResult& aError) {
2992 FORWARD_TO_OUTER_OR_THROW(GetContentOuter,
2993 (aCx, aRetval, aCallerType, aError), aError, );
2996 BarProp* nsGlobalWindowInner::GetMenubar(ErrorResult& aError) {
2997 if (!mMenubar) {
2998 mMenubar = new MenubarProp(this);
3001 return mMenubar;
3004 BarProp* nsGlobalWindowInner::GetToolbar(ErrorResult& aError) {
3005 if (!mToolbar) {
3006 mToolbar = new ToolbarProp(this);
3009 return mToolbar;
3012 BarProp* nsGlobalWindowInner::GetLocationbar(ErrorResult& aError) {
3013 if (!mLocationbar) {
3014 mLocationbar = new LocationbarProp(this);
3016 return mLocationbar;
3019 BarProp* nsGlobalWindowInner::GetPersonalbar(ErrorResult& aError) {
3020 if (!mPersonalbar) {
3021 mPersonalbar = new PersonalbarProp(this);
3023 return mPersonalbar;
3026 BarProp* nsGlobalWindowInner::GetStatusbar(ErrorResult& aError) {
3027 if (!mStatusbar) {
3028 mStatusbar = new StatusbarProp(this);
3030 return mStatusbar;
3033 BarProp* nsGlobalWindowInner::GetScrollbars(ErrorResult& aError) {
3034 if (!mScrollbars) {
3035 mScrollbars = new ScrollbarsProp(this);
3038 return mScrollbars;
3041 bool nsGlobalWindowInner::GetClosed(ErrorResult& aError) {
3042 // If we're called from JS (which is the only way we should be getting called
3043 // here) and we reach this point, that means our JS global is the current
3044 // target of the WindowProxy, which means that we are the "current inner"
3045 // of our outer. So if FORWARD_TO_OUTER fails to forward, that means the
3046 // outer is already torn down, which corresponds to the closed state.
3047 FORWARD_TO_OUTER(GetClosedOuter, (), true);
3050 Nullable<WindowProxyHolder> nsGlobalWindowInner::IndexedGetter(
3051 uint32_t aIndex) {
3052 FORWARD_TO_OUTER(IndexedGetterOuter, (aIndex), nullptr);
3055 namespace {
3057 struct InterfaceShimEntry {
3058 const char* geckoName;
3059 const char* domName;
3062 } // anonymous namespace
3064 // We add shims from Components.interfaces.nsIDOMFoo to window.Foo for each
3065 // interface that has interface constants that sites might be getting off
3066 // of Ci.
3067 const InterfaceShimEntry kInterfaceShimMap[] = {
3068 {"nsIXMLHttpRequest", "XMLHttpRequest"},
3069 {"nsIDOMDOMException", "DOMException"},
3070 {"nsIDOMNode", "Node"},
3071 {"nsIDOMCSSRule", "CSSRule"},
3072 {"nsIDOMEvent", "Event"},
3073 {"nsIDOMNSEvent", "Event"},
3074 {"nsIDOMKeyEvent", "KeyEvent"},
3075 {"nsIDOMMouseEvent", "MouseEvent"},
3076 {"nsIDOMMouseScrollEvent", "MouseScrollEvent"},
3077 {"nsIDOMMutationEvent", "MutationEvent"},
3078 {"nsIDOMUIEvent", "UIEvent"},
3079 {"nsIDOMHTMLMediaElement", "HTMLMediaElement"},
3080 {"nsIDOMRange", "Range"},
3081 // Think about whether Ci.nsINodeFilter can just go away for websites!
3082 {"nsIDOMNodeFilter", "NodeFilter"},
3083 {"nsIDOMXPathResult", "XPathResult"}};
3085 bool nsGlobalWindowInner::ResolveComponentsShim(
3086 JSContext* aCx, JS::Handle<JSObject*> aGlobal,
3087 JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> aDesc) {
3088 // Keep track of how often this happens.
3089 Telemetry::Accumulate(Telemetry::COMPONENTS_SHIM_ACCESSED_BY_CONTENT, true);
3091 // Warn once.
3092 nsCOMPtr<Document> doc = GetExtantDoc();
3093 if (doc) {
3094 doc->WarnOnceAbout(DeprecatedOperations::eComponents, /* asError = */ true);
3097 // Create a fake Components object.
3098 AssertSameCompartment(aCx, aGlobal);
3099 JS::Rooted<JSObject*> components(aCx, JS_NewPlainObject(aCx));
3100 if (NS_WARN_IF(!components)) {
3101 return false;
3104 // Create a fake interfaces object.
3105 JS::Rooted<JSObject*> interfaces(aCx, JS_NewPlainObject(aCx));
3106 if (NS_WARN_IF(!interfaces)) {
3107 return false;
3109 bool ok =
3110 JS_DefineProperty(aCx, components, "interfaces", interfaces,
3111 JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY);
3112 if (NS_WARN_IF(!ok)) {
3113 return false;
3116 // Define a bunch of shims from the Ci.nsIDOMFoo to window.Foo for DOM
3117 // interfaces with constants.
3118 for (uint32_t i = 0; i < ArrayLength(kInterfaceShimMap); ++i) {
3119 // Grab the names from the table.
3120 const char* geckoName = kInterfaceShimMap[i].geckoName;
3121 const char* domName = kInterfaceShimMap[i].domName;
3123 // Look up the appopriate interface object on the global.
3124 JS::Rooted<JS::Value> v(aCx, JS::UndefinedValue());
3125 ok = JS_GetProperty(aCx, aGlobal, domName, &v);
3126 if (NS_WARN_IF(!ok)) {
3127 return false;
3129 if (!v.isObject()) {
3130 NS_WARNING("Unable to find interface object on global");
3131 continue;
3134 // Define the shim on the interfaces object.
3135 ok = JS_DefineProperty(
3136 aCx, interfaces, geckoName, v,
3137 JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY);
3138 if (NS_WARN_IF(!ok)) {
3139 return false;
3143 aDesc.set(mozilla::Some(JS::PropertyDescriptor::Data(
3144 JS::ObjectValue(*components),
3145 {JS::PropertyAttribute::Configurable, JS::PropertyAttribute::Enumerable,
3146 JS::PropertyAttribute::Writable})));
3147 return true;
3150 #ifdef RELEASE_OR_BETA
3151 # define USE_CONTROLLERS_SHIM
3152 #endif
3154 #ifdef USE_CONTROLLERS_SHIM
3155 static const JSClass ControllersShimClass = {"Controllers", 0};
3156 static const JSClass XULControllersShimClass = {"XULControllers", 0};
3157 #endif
3159 bool nsGlobalWindowInner::DoResolve(
3160 JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aId,
3161 JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> aDesc) {
3162 // Note: Keep this in sync with MayResolve.
3164 // Note: The infallibleInit call in GlobalResolve depends on this check.
3165 if (!aId.isString()) {
3166 return true;
3169 bool found;
3170 if (!WebIDLGlobalNameHash::DefineIfEnabled(aCx, aObj, aId, aDesc, &found)) {
3171 return false;
3174 if (found) {
3175 return true;
3178 // We support a cut-down Components.interfaces in case websites are
3179 // using Components.interfaces.nsIFoo.CONSTANT_NAME for the ones
3180 // that have constants.
3181 if (StaticPrefs::dom_use_components_shim() &&
3182 aId == XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_COMPONENTS)) {
3183 return ResolveComponentsShim(aCx, aObj, aDesc);
3186 // We also support a "window.controllers" thing; apparently some
3187 // sites use it for browser-sniffing. See bug 1010577.
3188 #ifdef USE_CONTROLLERS_SHIM
3189 // Note: We use |aObj| rather than |this| to get the principal here, because
3190 // this is called during Window setup when the Document isn't necessarily
3191 // hooked up yet.
3192 if ((aId == XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_CONTROLLERS) ||
3193 aId == XPCJSRuntime::Get()->GetStringID(
3194 XPCJSContext::IDX_CONTROLLERS_CLASS)) &&
3195 !xpc::IsXrayWrapper(aObj) &&
3196 !nsContentUtils::ObjectPrincipal(aObj)->IsSystemPrincipal()) {
3197 if (GetExtantDoc()) {
3198 GetExtantDoc()->WarnOnceAbout(
3199 DeprecatedOperations::eWindow_Cc_ontrollers);
3201 const JSClass* clazz;
3202 if (aId ==
3203 XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_CONTROLLERS)) {
3204 clazz = &XULControllersShimClass;
3205 } else {
3206 clazz = &ControllersShimClass;
3208 MOZ_ASSERT(JS_IsGlobalObject(aObj));
3209 JS::Rooted<JSObject*> shim(aCx, JS_NewObject(aCx, clazz));
3210 if (NS_WARN_IF(!shim)) {
3211 return false;
3214 aDesc.set(mozilla::Some(JS::PropertyDescriptor::Data(
3215 JS::ObjectValue(*shim),
3216 {JS::PropertyAttribute::Configurable, JS::PropertyAttribute::Enumerable,
3217 JS::PropertyAttribute::Writable})));
3218 return true;
3220 #endif
3222 return true;
3225 /* static */
3226 bool nsGlobalWindowInner::MayResolve(jsid aId) {
3227 // Note: This function does not fail and may not have any side-effects.
3228 // Note: Keep this in sync with DoResolve.
3229 if (!aId.isString()) {
3230 return false;
3233 if (aId == XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_COMPONENTS)) {
3234 return true;
3237 if (aId == XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_CONTROLLERS) ||
3238 aId == XPCJSRuntime::Get()->GetStringID(
3239 XPCJSContext::IDX_CONTROLLERS_CLASS)) {
3240 // We only resolve .controllers/.Controllers in release builds and on
3241 // non-chrome windows, but let's not worry about any of that stuff.
3242 return true;
3245 return WebIDLGlobalNameHash::MayResolve(aId);
3248 void nsGlobalWindowInner::GetOwnPropertyNames(
3249 JSContext* aCx, JS::MutableHandleVector<jsid> aNames, bool aEnumerableOnly,
3250 ErrorResult& aRv) {
3251 if (aEnumerableOnly) {
3252 // The names we would return from here get defined on the window via one of
3253 // two codepaths. The ones coming from the WebIDLGlobalNameHash will end up
3254 // in the DefineConstructor function in BindingUtils, which always defines
3255 // things as non-enumerable. The ones coming from the script namespace
3256 // manager get defined by our resolve hook using FillPropertyDescriptor with
3257 // 0 for the property attributes, so non-enumerable as well.
3259 // So in the aEnumerableOnly case we have nothing to do.
3260 return;
3263 // "Components" is marked as enumerable but only resolved on demand :-/.
3264 // aNames.AppendElement(u"Components"_ns);
3266 JS::Rooted<JSObject*> wrapper(aCx, GetWrapper());
3268 // There are actually two ways we can get called here: For normal
3269 // enumeration or for Xray enumeration. In the latter case, we want to
3270 // return all possible WebIDL names, because we don't really support
3271 // deleting these names off our Xray; trying to resolve them will just make
3272 // them come back. In the former case, we want to avoid returning deleted
3273 // names. But the JS engine already knows about the non-deleted
3274 // already-resolved names, so we can just return the so-far-unresolved ones.
3276 // We can tell which case we're in by whether aCx is in our wrapper's
3277 // compartment. If not, we're in the Xray case.
3278 WebIDLGlobalNameHash::NameType nameType =
3279 js::IsObjectInContextCompartment(wrapper, aCx)
3280 ? WebIDLGlobalNameHash::UnresolvedNamesOnly
3281 : WebIDLGlobalNameHash::AllNames;
3282 if (!WebIDLGlobalNameHash::GetNames(aCx, wrapper, nameType, aNames)) {
3283 aRv.NoteJSContextException(aCx);
3287 /* static */
3288 bool nsGlobalWindowInner::IsPrivilegedChromeWindow(JSContext*, JSObject* aObj) {
3289 // For now, have to deal with XPConnect objects here.
3290 nsGlobalWindowInner* win = xpc::WindowOrNull(aObj);
3291 return win && win->IsChromeWindow() &&
3292 nsContentUtils::ObjectPrincipal(aObj) ==
3293 nsContentUtils::GetSystemPrincipal();
3296 /* static */
3297 bool nsGlobalWindowInner::DeviceSensorsEnabled(JSContext*, JSObject*) {
3298 return Preferences::GetBool("device.sensors.enabled");
3301 /* static */
3302 bool nsGlobalWindowInner::CachesEnabled(JSContext* aCx, JSObject*) {
3303 if (!JS::GetIsSecureContext(js::GetContextRealm(aCx))) {
3304 return StaticPrefs::dom_caches_testing_enabled() ||
3305 StaticPrefs::dom_serviceWorkers_testing_enabled();
3307 return true;
3310 /* static */
3311 bool nsGlobalWindowInner::IsSizeToContentEnabled(JSContext* aCx, JSObject*) {
3312 return StaticPrefs::dom_window_sizeToContent_enabled() ||
3313 nsContentUtils::IsSystemCaller(aCx);
3316 /* static */
3317 bool nsGlobalWindowInner::IsGleanNeeded(JSContext* aCx, JSObject* aObj) {
3318 // Glean is needed in ChromeOnly contexts and also in privileged about pages.
3319 nsIPrincipal* principal = nsContentUtils::SubjectPrincipal(aCx);
3320 if (principal->IsSystemPrincipal()) {
3321 return true;
3324 uint32_t flags = 0;
3325 if (NS_FAILED(principal->GetAboutModuleFlags(&flags))) {
3326 return false;
3328 return flags & nsIAboutModule::IS_SECURE_CHROME_UI;
3331 Crypto* nsGlobalWindowInner::GetCrypto(ErrorResult& aError) {
3332 if (!mCrypto) {
3333 mCrypto = new Crypto(this);
3335 return mCrypto;
3338 nsIControllers* nsGlobalWindowInner::GetControllers(ErrorResult& aError) {
3339 FORWARD_TO_OUTER_OR_THROW(GetControllersOuter, (aError), aError, nullptr);
3342 nsresult nsGlobalWindowInner::GetControllers(nsIControllers** aResult) {
3343 ErrorResult rv;
3344 nsCOMPtr<nsIControllers> controllers = GetControllers(rv);
3345 controllers.forget(aResult);
3347 return rv.StealNSResult();
3350 Nullable<WindowProxyHolder> nsGlobalWindowInner::GetOpenerWindow(
3351 ErrorResult& aError) {
3352 FORWARD_TO_OUTER_OR_THROW(GetOpenerWindowOuter, (), aError, nullptr);
3355 void nsGlobalWindowInner::GetOpener(JSContext* aCx,
3356 JS::MutableHandle<JS::Value> aRetval,
3357 ErrorResult& aError) {
3358 Nullable<WindowProxyHolder> opener = GetOpenerWindow(aError);
3359 if (aError.Failed() || opener.IsNull()) {
3360 aRetval.setNull();
3361 return;
3364 if (!ToJSValue(aCx, opener.Value(), aRetval)) {
3365 aError.NoteJSContextException(aCx);
3369 void nsGlobalWindowInner::SetOpener(JSContext* aCx,
3370 JS::Handle<JS::Value> aOpener,
3371 ErrorResult& aError) {
3372 if (aOpener.isNull()) {
3373 RefPtr<BrowsingContext> bc(GetBrowsingContext());
3374 if (!bc->IsDiscarded()) {
3375 bc->SetOpener(nullptr);
3377 return;
3380 // If something other than null is passed, just define aOpener on our inner
3381 // window's JS object, wrapped into the current compartment so that for Xrays
3382 // we define on the Xray expando object, but don't set it on the outer window,
3383 // so that it'll get reset on navigation. This is just like replaceable
3384 // properties, but we're not quite readonly.
3385 RedefineProperty(aCx, "opener", aOpener, aError);
3388 void nsGlobalWindowInner::GetEvent(OwningEventOrUndefined& aRetval) {
3389 if (mEvent) {
3390 aRetval.SetAsEvent() = mEvent;
3391 } else {
3392 aRetval.SetUndefined();
3396 void nsGlobalWindowInner::GetStatus(nsAString& aStatus, ErrorResult& aError) {
3397 FORWARD_TO_OUTER_OR_THROW(GetStatusOuter, (aStatus), aError, );
3400 void nsGlobalWindowInner::SetStatus(const nsAString& aStatus,
3401 ErrorResult& aError) {
3402 FORWARD_TO_OUTER_OR_THROW(SetStatusOuter, (aStatus), aError, );
3405 void nsGlobalWindowInner::GetName(nsAString& aName, ErrorResult& aError) {
3406 FORWARD_TO_OUTER_OR_THROW(GetNameOuter, (aName), aError, );
3409 void nsGlobalWindowInner::SetName(const nsAString& aName,
3410 mozilla::ErrorResult& aError) {
3411 FORWARD_TO_OUTER_OR_THROW(SetNameOuter, (aName, aError), aError, );
3414 double nsGlobalWindowInner::GetInnerWidth(ErrorResult& aError) {
3415 FORWARD_TO_OUTER_OR_THROW(GetInnerWidthOuter, (aError), aError, 0);
3418 nsresult nsGlobalWindowInner::GetInnerWidth(double* aWidth) {
3419 ErrorResult rv;
3420 // Callee doesn't care about the caller type, but play it safe.
3421 *aWidth = GetInnerWidth(rv);
3422 return rv.StealNSResult();
3425 double nsGlobalWindowInner::GetInnerHeight(ErrorResult& aError) {
3426 // We ignore aCallerType; we only have that argument because some other things
3427 // called by GetReplaceableWindowCoord need it. If this ever changes, fix
3428 // nsresult nsGlobalWindowInner::GetInnerHeight(double* aInnerWidth)
3429 // to actually take a useful CallerType and pass it in here.
3430 FORWARD_TO_OUTER_OR_THROW(GetInnerHeightOuter, (aError), aError, 0);
3433 nsresult nsGlobalWindowInner::GetInnerHeight(double* aHeight) {
3434 ErrorResult rv;
3435 // Callee doesn't care about the caller type, but play it safe.
3436 *aHeight = GetInnerHeight(rv);
3437 return rv.StealNSResult();
3440 int32_t nsGlobalWindowInner::GetOuterWidth(CallerType aCallerType,
3441 ErrorResult& aError) {
3442 FORWARD_TO_OUTER_OR_THROW(GetOuterWidthOuter, (aCallerType, aError), aError,
3446 int32_t nsGlobalWindowInner::GetOuterHeight(CallerType aCallerType,
3447 ErrorResult& aError) {
3448 FORWARD_TO_OUTER_OR_THROW(GetOuterHeightOuter, (aCallerType, aError), aError,
3452 double nsGlobalWindowInner::ScreenEdgeSlopX() const {
3453 FORWARD_TO_OUTER(ScreenEdgeSlopX, (), 0);
3456 double nsGlobalWindowInner::ScreenEdgeSlopY() const {
3457 FORWARD_TO_OUTER(ScreenEdgeSlopY, (), 0);
3460 int32_t nsGlobalWindowInner::GetScreenX(CallerType aCallerType,
3461 ErrorResult& aError) {
3462 FORWARD_TO_OUTER_OR_THROW(GetScreenXOuter, (aCallerType, aError), aError, 0);
3465 int32_t nsGlobalWindowInner::GetScreenY(CallerType aCallerType,
3466 ErrorResult& aError) {
3467 FORWARD_TO_OUTER_OR_THROW(GetScreenYOuter, (aCallerType, aError), aError, 0);
3470 float nsGlobalWindowInner::GetMozInnerScreenX(CallerType aCallerType,
3471 ErrorResult& aError) {
3472 FORWARD_TO_OUTER_OR_THROW(GetMozInnerScreenXOuter, (aCallerType), aError, 0);
3475 float nsGlobalWindowInner::GetMozInnerScreenY(CallerType aCallerType,
3476 ErrorResult& aError) {
3477 FORWARD_TO_OUTER_OR_THROW(GetMozInnerScreenYOuter, (aCallerType), aError, 0);
3480 static nsPresContext* GetPresContextForRatio(Document* aDoc) {
3481 if (nsPresContext* presContext = aDoc->GetPresContext()) {
3482 return presContext;
3484 // We're in an undisplayed subdocument... There's not really an awesome way
3485 // to tell what the right DPI is from here, so we try to walk up our parent
3486 // document chain to the extent that the docs can observe each other.
3487 Document* doc = aDoc;
3488 while (doc->StyleOrLayoutObservablyDependsOnParentDocumentLayout()) {
3489 doc = doc->GetInProcessParentDocument();
3490 if (nsPresContext* presContext = doc->GetPresContext()) {
3491 return presContext;
3494 return nullptr;
3497 double nsGlobalWindowInner::GetDevicePixelRatio(CallerType aCallerType,
3498 ErrorResult& aError) {
3499 ENSURE_ACTIVE_DOCUMENT(aError, 0.0);
3501 RefPtr<nsPresContext> presContext = GetPresContextForRatio(mDoc);
3502 if (NS_WARN_IF(!presContext)) {
3503 // Still nothing, oh well.
3504 return 1.0;
3507 if (nsIGlobalObject::ShouldResistFingerprinting(
3508 aCallerType, RFPTarget::WindowDevicePixelRatio)) {
3509 // Spoofing the DevicePixelRatio causes blurriness in some situations
3510 // on HiDPI displays. pdf.js is a non-system caller; but it can't
3511 // expose the fingerprintable information, so we can safely disable
3512 // spoofing in this situation. It doesn't address the issue for
3513 // web-rendered content (including pdf.js instances on the web.)
3514 // In the future we hope to have a better solution to fix all HiDPI
3515 // blurriness...
3516 nsAutoCString origin;
3517 nsresult rv = this->GetPrincipal()->GetOrigin(origin);
3518 if (NS_FAILED(rv) || origin != "resource://pdf.js"_ns) {
3519 return 1.0;
3523 if (aCallerType == CallerType::NonSystem) {
3524 float overrideDPPX = presContext->GetOverrideDPPX();
3525 if (overrideDPPX > 0.0f) {
3526 return overrideDPPX;
3530 return double(AppUnitsPerCSSPixel()) /
3531 double(presContext->AppUnitsPerDevPixel());
3534 double nsGlobalWindowInner::GetDesktopToDeviceScale(ErrorResult& aError) {
3535 ENSURE_ACTIVE_DOCUMENT(aError, 0.0);
3536 nsPresContext* presContext = GetPresContextForRatio(mDoc);
3537 if (!presContext) {
3538 return 1.0;
3540 return presContext->DeviceContext()->GetDesktopToDeviceScale().scale;
3543 int32_t nsGlobalWindowInner::RequestAnimationFrame(
3544 FrameRequestCallback& aCallback, ErrorResult& aError) {
3545 if (!mDoc) {
3546 return 0;
3549 if (GetWrapperPreserveColor()) {
3550 js::NotifyAnimationActivity(GetWrapperPreserveColor());
3553 DebuggerNotificationDispatch(this,
3554 DebuggerNotificationType::RequestAnimationFrame);
3556 int32_t handle;
3557 aError = mDoc->ScheduleFrameRequestCallback(aCallback, &handle);
3558 return handle;
3561 void nsGlobalWindowInner::CancelAnimationFrame(int32_t aHandle,
3562 ErrorResult& aError) {
3563 if (!mDoc) {
3564 return;
3567 DebuggerNotificationDispatch(this,
3568 DebuggerNotificationType::CancelAnimationFrame);
3570 mDoc->CancelFrameRequestCallback(aHandle);
3573 already_AddRefed<MediaQueryList> nsGlobalWindowInner::MatchMedia(
3574 const nsACString& aMediaQueryList, CallerType aCallerType,
3575 ErrorResult& aError) {
3576 ENSURE_ACTIVE_DOCUMENT(aError, nullptr);
3577 return mDoc->MatchMedia(aMediaQueryList, aCallerType);
3580 int32_t nsGlobalWindowInner::GetScrollMinX(ErrorResult& aError) {
3581 FORWARD_TO_OUTER_OR_THROW(GetScrollBoundaryOuter, (eSideLeft), aError, 0);
3584 int32_t nsGlobalWindowInner::GetScrollMinY(ErrorResult& aError) {
3585 FORWARD_TO_OUTER_OR_THROW(GetScrollBoundaryOuter, (eSideTop), aError, 0);
3588 int32_t nsGlobalWindowInner::GetScrollMaxX(ErrorResult& aError) {
3589 FORWARD_TO_OUTER_OR_THROW(GetScrollBoundaryOuter, (eSideRight), aError, 0);
3592 int32_t nsGlobalWindowInner::GetScrollMaxY(ErrorResult& aError) {
3593 FORWARD_TO_OUTER_OR_THROW(GetScrollBoundaryOuter, (eSideBottom), aError, 0);
3596 double nsGlobalWindowInner::GetScrollX(ErrorResult& aError) {
3597 FORWARD_TO_OUTER_OR_THROW(GetScrollXOuter, (), aError, 0);
3600 double nsGlobalWindowInner::GetScrollY(ErrorResult& aError) {
3601 FORWARD_TO_OUTER_OR_THROW(GetScrollYOuter, (), aError, 0);
3604 uint32_t nsGlobalWindowInner::Length() { FORWARD_TO_OUTER(Length, (), 0); }
3606 Nullable<WindowProxyHolder> nsGlobalWindowInner::GetTop(
3607 mozilla::ErrorResult& aError) {
3608 FORWARD_TO_OUTER_OR_THROW(GetTopOuter, (), aError, nullptr);
3611 already_AddRefed<BrowsingContext> nsGlobalWindowInner::GetChildWindow(
3612 const nsAString& aName) {
3613 if (GetOuterWindowInternal()) {
3614 return GetOuterWindowInternal()->GetChildWindow(aName);
3616 return nullptr;
3619 void nsGlobalWindowInner::RefreshRealmPrincipal() {
3620 JS::SetRealmPrincipals(js::GetNonCCWObjectRealm(GetWrapperPreserveColor()),
3621 nsJSPrincipals::get(mDoc->NodePrincipal()));
3624 already_AddRefed<nsIWidget> nsGlobalWindowInner::GetMainWidget() {
3625 FORWARD_TO_OUTER(GetMainWidget, (), nullptr);
3628 nsIWidget* nsGlobalWindowInner::GetNearestWidget() const {
3629 if (GetOuterWindowInternal()) {
3630 return GetOuterWindowInternal()->GetNearestWidget();
3632 return nullptr;
3635 void nsGlobalWindowInner::SetFullScreen(bool aFullscreen,
3636 mozilla::ErrorResult& aError) {
3637 FORWARD_TO_OUTER_OR_THROW(SetFullscreenOuter, (aFullscreen, aError), aError,
3638 /* void */);
3641 bool nsGlobalWindowInner::GetFullScreen(ErrorResult& aError) {
3642 FORWARD_TO_OUTER_OR_THROW(GetFullscreenOuter, (), aError, false);
3645 bool nsGlobalWindowInner::GetFullScreen() {
3646 ErrorResult dummy;
3647 bool fullscreen = GetFullScreen(dummy);
3648 dummy.SuppressException();
3649 return fullscreen;
3652 void nsGlobalWindowInner::Dump(const nsAString& aStr) {
3653 if (!nsJSUtils::DumpEnabled()) {
3654 return;
3657 char* cstr = ToNewUTF8String(aStr);
3659 #if defined(XP_MACOSX)
3660 // have to convert \r to \n so that printing to the console works
3661 char *c = cstr, *cEnd = cstr + strlen(cstr);
3662 while (c < cEnd) {
3663 if (*c == '\r') *c = '\n';
3664 c++;
3666 #endif
3668 if (cstr) {
3669 MOZ_LOG(nsContentUtils::DOMDumpLog(), LogLevel::Debug,
3670 ("[Window.Dump] %s", cstr));
3671 #ifdef XP_WIN
3672 PrintToDebugger(cstr);
3673 #endif
3674 #ifdef ANDROID
3675 __android_log_write(ANDROID_LOG_INFO, "GeckoDump", cstr);
3676 #endif
3677 FILE* fp = gDumpFile ? gDumpFile : stdout;
3678 fputs(cstr, fp);
3679 fflush(fp);
3680 free(cstr);
3684 void nsGlobalWindowInner::Alert(nsIPrincipal& aSubjectPrincipal,
3685 ErrorResult& aError) {
3686 Alert(u""_ns, aSubjectPrincipal, aError);
3689 void nsGlobalWindowInner::Alert(const nsAString& aMessage,
3690 nsIPrincipal& aSubjectPrincipal,
3691 ErrorResult& aError) {
3692 FORWARD_TO_OUTER_OR_THROW(AlertOuter, (aMessage, aSubjectPrincipal, aError),
3693 aError, );
3696 bool nsGlobalWindowInner::Confirm(const nsAString& aMessage,
3697 nsIPrincipal& aSubjectPrincipal,
3698 ErrorResult& aError) {
3699 FORWARD_TO_OUTER_OR_THROW(ConfirmOuter, (aMessage, aSubjectPrincipal, aError),
3700 aError, false);
3703 already_AddRefed<Promise> nsGlobalWindowInner::Fetch(
3704 const RequestOrUSVString& aInput, const RequestInit& aInit,
3705 CallerType aCallerType, ErrorResult& aRv) {
3706 return FetchRequest(this, aInput, aInit, aCallerType, aRv);
3709 void nsGlobalWindowInner::Prompt(const nsAString& aMessage,
3710 const nsAString& aInitial, nsAString& aReturn,
3711 nsIPrincipal& aSubjectPrincipal,
3712 ErrorResult& aError) {
3713 FORWARD_TO_OUTER_OR_THROW(
3714 PromptOuter, (aMessage, aInitial, aReturn, aSubjectPrincipal, aError),
3715 aError, );
3718 void nsGlobalWindowInner::Focus(CallerType aCallerType, ErrorResult& aError) {
3719 FORWARD_TO_OUTER_OR_THROW(FocusOuter,
3720 (aCallerType, /* aFromOtherProcess */ false,
3721 nsFocusManager::GenerateFocusActionId()),
3722 aError, );
3725 nsresult nsGlobalWindowInner::Focus(CallerType aCallerType) {
3726 ErrorResult rv;
3727 Focus(aCallerType, rv);
3729 return rv.StealNSResult();
3732 void nsGlobalWindowInner::Blur(CallerType aCallerType, ErrorResult& aError) {
3733 FORWARD_TO_OUTER_OR_THROW(BlurOuter, (aCallerType), aError, );
3736 void nsGlobalWindowInner::Stop(ErrorResult& aError) {
3737 FORWARD_TO_OUTER_OR_THROW(StopOuter, (aError), aError, );
3740 void nsGlobalWindowInner::Print(ErrorResult& aError) {
3741 FORWARD_TO_OUTER_OR_THROW(PrintOuter, (aError), aError, );
3744 Nullable<WindowProxyHolder> nsGlobalWindowInner::PrintPreview(
3745 nsIPrintSettings* aSettings, nsIWebProgressListener* aListener,
3746 nsIDocShell* aDocShellToCloneInto, ErrorResult& aError) {
3747 FORWARD_TO_OUTER_OR_THROW(
3748 Print,
3749 (aSettings,
3750 /* aRemotePrintJob = */ nullptr, aListener, aDocShellToCloneInto,
3751 nsGlobalWindowOuter::IsPreview::Yes,
3752 nsGlobalWindowOuter::IsForWindowDotPrint::No,
3753 /* aPrintPreviewCallback = */ nullptr, aError),
3754 aError, nullptr);
3757 void nsGlobalWindowInner::MoveTo(int32_t aXPos, int32_t aYPos,
3758 CallerType aCallerType, ErrorResult& aError) {
3759 FORWARD_TO_OUTER_OR_THROW(MoveToOuter, (aXPos, aYPos, aCallerType, aError),
3760 aError, );
3763 void nsGlobalWindowInner::MoveBy(int32_t aXDif, int32_t aYDif,
3764 CallerType aCallerType, ErrorResult& aError) {
3765 FORWARD_TO_OUTER_OR_THROW(MoveByOuter, (aXDif, aYDif, aCallerType, aError),
3766 aError, );
3769 void nsGlobalWindowInner::ResizeTo(int32_t aWidth, int32_t aHeight,
3770 CallerType aCallerType,
3771 ErrorResult& aError) {
3772 FORWARD_TO_OUTER_OR_THROW(ResizeToOuter,
3773 (aWidth, aHeight, aCallerType, aError), aError, );
3776 void nsGlobalWindowInner::ResizeBy(int32_t aWidthDif, int32_t aHeightDif,
3777 CallerType aCallerType,
3778 ErrorResult& aError) {
3779 FORWARD_TO_OUTER_OR_THROW(
3780 ResizeByOuter, (aWidthDif, aHeightDif, aCallerType, aError), aError, );
3783 void nsGlobalWindowInner::SizeToContent(CallerType aCallerType,
3784 ErrorResult& aError) {
3785 FORWARD_TO_OUTER_OR_THROW(SizeToContentOuter, (aCallerType, {}, aError),
3786 aError, );
3789 void nsGlobalWindowInner::SizeToContentConstrained(
3790 const SizeToContentConstraints& aConstraints, ErrorResult& aError) {
3791 FORWARD_TO_OUTER_OR_THROW(
3792 SizeToContentOuter, (CallerType::System, aConstraints, aError), aError, );
3795 already_AddRefed<nsPIWindowRoot> nsGlobalWindowInner::GetTopWindowRoot() {
3796 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
3797 if (!outer) {
3798 return nullptr;
3800 return outer->GetTopWindowRoot();
3803 void nsGlobalWindowInner::Scroll(double aXScroll, double aYScroll) {
3804 // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
3805 auto scrollPos = CSSIntPoint::Truncate(mozilla::ToZeroIfNonfinite(aXScroll),
3806 mozilla::ToZeroIfNonfinite(aYScroll));
3807 ScrollTo(scrollPos, ScrollOptions());
3810 void nsGlobalWindowInner::ScrollTo(double aXScroll, double aYScroll) {
3811 // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
3812 auto scrollPos = CSSIntPoint::Truncate(mozilla::ToZeroIfNonfinite(aXScroll),
3813 mozilla::ToZeroIfNonfinite(aYScroll));
3814 ScrollTo(scrollPos, ScrollOptions());
3817 void nsGlobalWindowInner::ScrollTo(const ScrollToOptions& aOptions) {
3818 // When scrolling to a non-zero offset, we need to determine whether that
3819 // position is within our scrollable range, so we need updated layout
3820 // information which requires a layout flush, otherwise all we need is to
3821 // flush frames to be able to access our scrollable frame here.
3822 FlushType flushType =
3823 ((aOptions.mLeft.WasPassed() && aOptions.mLeft.Value() > 0) ||
3824 (aOptions.mTop.WasPassed() && aOptions.mTop.Value() > 0))
3825 ? FlushType::Layout
3826 : FlushType::Frames;
3827 FlushPendingNotifications(flushType);
3828 nsIScrollableFrame* sf = GetScrollFrame();
3830 if (sf) {
3831 CSSIntPoint scrollPos = sf->GetScrollPositionCSSPixels();
3832 if (aOptions.mLeft.WasPassed()) {
3833 scrollPos.x = static_cast<int32_t>(
3834 mozilla::ToZeroIfNonfinite(aOptions.mLeft.Value()));
3836 if (aOptions.mTop.WasPassed()) {
3837 scrollPos.y = static_cast<int32_t>(
3838 mozilla::ToZeroIfNonfinite(aOptions.mTop.Value()));
3841 ScrollTo(scrollPos, aOptions);
3845 void nsGlobalWindowInner::Scroll(const ScrollToOptions& aOptions) {
3846 ScrollTo(aOptions);
3849 void nsGlobalWindowInner::ScrollTo(const CSSIntPoint& aScroll,
3850 const ScrollOptions& aOptions) {
3851 // When scrolling to a non-zero offset, we need to determine whether that
3852 // position is within our scrollable range, so we need updated layout
3853 // information which requires a layout flush, otherwise all we need is to
3854 // flush frames to be able to access our scrollable frame here.
3855 FlushType flushType =
3856 (aScroll.x || aScroll.y) ? FlushType::Layout : FlushType::Frames;
3857 FlushPendingNotifications(flushType);
3858 nsIScrollableFrame* sf = GetScrollFrame();
3860 if (sf) {
3861 // Here we calculate what the max pixel value is that we can
3862 // scroll to, we do this by dividing maxint with the pixel to
3863 // twips conversion factor, and subtracting 4, the 4 comes from
3864 // experimenting with this value, anything less makes the view
3865 // code not scroll correctly, I have no idea why. -- jst
3866 const int32_t maxpx = nsPresContext::AppUnitsToIntCSSPixels(0x7fffffff) - 4;
3868 CSSIntPoint scroll(aScroll);
3869 if (scroll.x > maxpx) {
3870 scroll.x = maxpx;
3873 if (scroll.y > maxpx) {
3874 scroll.y = maxpx;
3877 ScrollMode scrollMode = sf->IsSmoothScroll(aOptions.mBehavior)
3878 ? ScrollMode::SmoothMsd
3879 : ScrollMode::Instant;
3881 sf->ScrollToCSSPixels(scroll, scrollMode);
3885 void nsGlobalWindowInner::ScrollBy(double aXScrollDif, double aYScrollDif) {
3886 FlushPendingNotifications(FlushType::Layout);
3887 nsIScrollableFrame* sf = GetScrollFrame();
3889 if (sf) {
3890 // It seems like it would make more sense for ScrollBy to use
3891 // SMOOTH mode, but tests seem to depend on the synchronous behaviour.
3892 // Perhaps Web content does too.
3893 ScrollToOptions options;
3894 options.mLeft.Construct(aXScrollDif);
3895 options.mTop.Construct(aYScrollDif);
3896 ScrollBy(options);
3900 void nsGlobalWindowInner::ScrollBy(const ScrollToOptions& aOptions) {
3901 FlushPendingNotifications(FlushType::Layout);
3902 nsIScrollableFrame* sf = GetScrollFrame();
3904 if (sf) {
3905 CSSIntPoint scrollDelta;
3906 if (aOptions.mLeft.WasPassed()) {
3907 scrollDelta.x = static_cast<int32_t>(
3908 mozilla::ToZeroIfNonfinite(aOptions.mLeft.Value()));
3910 if (aOptions.mTop.WasPassed()) {
3911 scrollDelta.y = static_cast<int32_t>(
3912 mozilla::ToZeroIfNonfinite(aOptions.mTop.Value()));
3915 ScrollMode scrollMode = sf->IsSmoothScroll(aOptions.mBehavior)
3916 ? ScrollMode::SmoothMsd
3917 : ScrollMode::Instant;
3919 sf->ScrollByCSSPixels(scrollDelta, scrollMode);
3923 void nsGlobalWindowInner::ScrollByLines(int32_t numLines,
3924 const ScrollOptions& aOptions) {
3925 FlushPendingNotifications(FlushType::Layout);
3926 nsIScrollableFrame* sf = GetScrollFrame();
3927 if (sf) {
3928 // It seems like it would make more sense for ScrollByLines to use
3929 // SMOOTH mode, but tests seem to depend on the synchronous behaviour.
3930 // Perhaps Web content does too.
3931 ScrollMode scrollMode = sf->IsSmoothScroll(aOptions.mBehavior)
3932 ? ScrollMode::SmoothMsd
3933 : ScrollMode::Instant;
3935 sf->ScrollBy(nsIntPoint(0, numLines), ScrollUnit::LINES, scrollMode);
3939 void nsGlobalWindowInner::ScrollByPages(int32_t numPages,
3940 const ScrollOptions& aOptions) {
3941 FlushPendingNotifications(FlushType::Layout);
3942 nsIScrollableFrame* sf = GetScrollFrame();
3943 if (sf) {
3944 // It seems like it would make more sense for ScrollByPages to use
3945 // SMOOTH mode, but tests seem to depend on the synchronous behaviour.
3946 // Perhaps Web content does too.
3947 ScrollMode scrollMode = sf->IsSmoothScroll(aOptions.mBehavior)
3948 ? ScrollMode::SmoothMsd
3949 : ScrollMode::Instant;
3951 sf->ScrollBy(nsIntPoint(0, numPages), ScrollUnit::PAGES, scrollMode);
3955 void nsGlobalWindowInner::MozScrollSnap() {
3956 FlushPendingNotifications(FlushType::Layout);
3957 nsIScrollableFrame* sf = GetScrollFrame();
3958 if (sf) {
3959 sf->ScrollSnap();
3963 void nsGlobalWindowInner::ClearTimeout(int32_t aHandle) {
3964 DebuggerNotificationDispatch(this, DebuggerNotificationType::ClearTimeout);
3966 if (aHandle > 0) {
3967 mTimeoutManager->ClearTimeout(aHandle, Timeout::Reason::eTimeoutOrInterval);
3971 void nsGlobalWindowInner::ClearInterval(int32_t aHandle) {
3972 DebuggerNotificationDispatch(this, DebuggerNotificationType::ClearInterval);
3974 if (aHandle > 0) {
3975 mTimeoutManager->ClearTimeout(aHandle, Timeout::Reason::eTimeoutOrInterval);
3979 void nsGlobalWindowInner::SetResizable(bool aResizable) const {
3980 // nop
3983 void nsGlobalWindowInner::CaptureEvents() {
3984 if (mDoc) {
3985 mDoc->WarnOnceAbout(DeprecatedOperations::eUseOfCaptureEvents);
3989 void nsGlobalWindowInner::ReleaseEvents() {
3990 if (mDoc) {
3991 mDoc->WarnOnceAbout(DeprecatedOperations::eUseOfReleaseEvents);
3995 Nullable<WindowProxyHolder> nsGlobalWindowInner::Open(const nsAString& aUrl,
3996 const nsAString& aName,
3997 const nsAString& aOptions,
3998 ErrorResult& aError) {
3999 FORWARD_TO_OUTER_OR_THROW(OpenOuter, (aUrl, aName, aOptions, aError), aError,
4000 nullptr);
4003 Nullable<WindowProxyHolder> nsGlobalWindowInner::OpenDialog(
4004 JSContext* aCx, const nsAString& aUrl, const nsAString& aName,
4005 const nsAString& aOptions, const Sequence<JS::Value>& aExtraArgument,
4006 ErrorResult& aError) {
4007 FORWARD_TO_OUTER_OR_THROW(
4008 OpenDialogOuter, (aCx, aUrl, aName, aOptions, aExtraArgument, aError),
4009 aError, nullptr);
4012 WindowProxyHolder nsGlobalWindowInner::GetFrames(ErrorResult& aError) {
4013 FORWARD_TO_OUTER_OR_THROW(GetFramesOuter, (), aError, Window());
4016 void nsGlobalWindowInner::PostMessageMoz(JSContext* aCx,
4017 JS::Handle<JS::Value> aMessage,
4018 const nsAString& aTargetOrigin,
4019 JS::Handle<JS::Value> aTransfer,
4020 nsIPrincipal& aSubjectPrincipal,
4021 ErrorResult& aError) {
4022 FORWARD_TO_OUTER_OR_THROW(
4023 PostMessageMozOuter,
4024 (aCx, aMessage, aTargetOrigin, aTransfer, aSubjectPrincipal, aError),
4025 aError, );
4028 void nsGlobalWindowInner::PostMessageMoz(JSContext* aCx,
4029 JS::Handle<JS::Value> aMessage,
4030 const nsAString& aTargetOrigin,
4031 const Sequence<JSObject*>& aTransfer,
4032 nsIPrincipal& aSubjectPrincipal,
4033 ErrorResult& aRv) {
4034 JS::Rooted<JS::Value> transferArray(aCx, JS::UndefinedValue());
4036 aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransfer,
4037 &transferArray);
4038 if (NS_WARN_IF(aRv.Failed())) {
4039 return;
4042 PostMessageMoz(aCx, aMessage, aTargetOrigin, transferArray, aSubjectPrincipal,
4043 aRv);
4046 void nsGlobalWindowInner::PostMessageMoz(
4047 JSContext* aCx, JS::Handle<JS::Value> aMessage,
4048 const WindowPostMessageOptions& aOptions, nsIPrincipal& aSubjectPrincipal,
4049 ErrorResult& aRv) {
4050 JS::Rooted<JS::Value> transferArray(aCx, JS::UndefinedValue());
4052 aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(
4053 aCx, aOptions.mTransfer, &transferArray);
4054 if (NS_WARN_IF(aRv.Failed())) {
4055 return;
4058 PostMessageMoz(aCx, aMessage, aOptions.mTargetOrigin, transferArray,
4059 aSubjectPrincipal, aRv);
4062 void nsGlobalWindowInner::Close(CallerType aCallerType, ErrorResult& aError) {
4063 FORWARD_TO_OUTER_OR_THROW(CloseOuter, (aCallerType == CallerType::System),
4064 aError, );
4067 nsresult nsGlobalWindowInner::Close() {
4068 FORWARD_TO_OUTER(Close, (), NS_ERROR_UNEXPECTED);
4071 bool nsGlobalWindowInner::IsInModalState() {
4072 FORWARD_TO_OUTER(IsInModalState, (), false);
4075 // static
4076 void nsGlobalWindowInner::NotifyDOMWindowDestroyed(
4077 nsGlobalWindowInner* aWindow) {
4078 nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
4079 if (observerService) {
4080 observerService->NotifyObservers(ToSupports(aWindow),
4081 DOM_WINDOW_DESTROYED_TOPIC, nullptr);
4085 void nsGlobalWindowInner::NotifyWindowIDDestroyed(const char* aTopic) {
4086 nsCOMPtr<nsIRunnable> runnable =
4087 new WindowDestroyedEvent(this, mWindowID, aTopic);
4088 Dispatch(runnable.forget());
4091 // static
4092 void nsGlobalWindowInner::NotifyDOMWindowFrozen(nsGlobalWindowInner* aWindow) {
4093 if (aWindow) {
4094 nsCOMPtr<nsIObserverService> observerService =
4095 services::GetObserverService();
4096 if (observerService) {
4097 observerService->NotifyObservers(ToSupports(aWindow),
4098 DOM_WINDOW_FROZEN_TOPIC, nullptr);
4103 // static
4104 void nsGlobalWindowInner::NotifyDOMWindowThawed(nsGlobalWindowInner* aWindow) {
4105 if (aWindow) {
4106 nsCOMPtr<nsIObserverService> observerService =
4107 services::GetObserverService();
4108 if (observerService) {
4109 observerService->NotifyObservers(ToSupports(aWindow),
4110 DOM_WINDOW_THAWED_TOPIC, nullptr);
4115 Element* nsGlobalWindowInner::GetFrameElement(nsIPrincipal& aSubjectPrincipal,
4116 ErrorResult& aError) {
4117 FORWARD_TO_OUTER_OR_THROW(GetFrameElement, (aSubjectPrincipal), aError,
4118 nullptr);
4121 Element* nsGlobalWindowInner::GetRealFrameElement(ErrorResult& aError) {
4122 FORWARD_TO_OUTER_OR_THROW(GetFrameElement, (), aError, nullptr);
4125 void nsGlobalWindowInner::UpdateCommands(const nsAString& anAction) {
4126 if (GetOuterWindowInternal()) {
4127 GetOuterWindowInternal()->UpdateCommands(anAction);
4131 Selection* nsGlobalWindowInner::GetSelection(ErrorResult& aError) {
4132 FORWARD_TO_OUTER_OR_THROW(GetSelectionOuter, (), aError, nullptr);
4135 WebTaskScheduler* nsGlobalWindowInner::Scheduler() {
4136 if (!mWebTaskScheduler) {
4137 mWebTaskScheduler = WebTaskScheduler::CreateForMainThread(this);
4139 MOZ_ASSERT(mWebTaskScheduler);
4140 return mWebTaskScheduler;
4143 bool nsGlobalWindowInner::Find(const nsAString& aString, bool aCaseSensitive,
4144 bool aBackwards, bool aWrapAround,
4145 bool aWholeWord, bool aSearchInFrames,
4146 bool aShowDialog, ErrorResult& aError) {
4147 FORWARD_TO_OUTER_OR_THROW(FindOuter,
4148 (aString, aCaseSensitive, aBackwards, aWrapAround,
4149 aWholeWord, aSearchInFrames, aShowDialog, aError),
4150 aError, false);
4153 void nsGlobalWindowInner::GetOrigin(nsAString& aOrigin) {
4154 nsContentUtils::GetWebExposedOriginSerialization(GetPrincipal(), aOrigin);
4157 // See also AutoJSAPI::ReportException
4158 void nsGlobalWindowInner::ReportError(JSContext* aCx,
4159 JS::Handle<JS::Value> aError,
4160 CallerType aCallerType,
4161 ErrorResult& aRv) {
4162 if (MOZ_UNLIKELY(!HasActiveDocument())) {
4163 return aRv.Throw(NS_ERROR_XPC_SECURITY_MANAGER_VETO);
4166 JS::ErrorReportBuilder jsReport(aCx);
4167 JS::ExceptionStack exnStack(aCx, aError, nullptr);
4168 if (!jsReport.init(aCx, exnStack, JS::ErrorReportBuilder::NoSideEffects)) {
4169 return aRv.NoteJSContextException(aCx);
4172 RefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport();
4173 bool isChrome = aCallerType == CallerType::System;
4174 xpcReport->Init(jsReport.report(), jsReport.toStringResult().c_str(),
4175 isChrome, WindowID());
4177 JS::RootingContext* rcx = JS::RootingContext::get(aCx);
4178 DispatchScriptErrorEvent(this, rcx, xpcReport, exnStack.exception(),
4179 exnStack.stack());
4182 void nsGlobalWindowInner::Atob(const nsAString& aAsciiBase64String,
4183 nsAString& aBinaryData, ErrorResult& aError) {
4184 aError = nsContentUtils::Atob(aAsciiBase64String, aBinaryData);
4187 void nsGlobalWindowInner::Btoa(const nsAString& aBinaryData,
4188 nsAString& aAsciiBase64String,
4189 ErrorResult& aError) {
4190 aError = nsContentUtils::Btoa(aBinaryData, aAsciiBase64String);
4193 //*****************************************************************************
4194 // EventTarget
4195 //*****************************************************************************
4197 nsPIDOMWindowOuter* nsGlobalWindowInner::GetOwnerGlobalForBindingsInternal() {
4198 return nsPIDOMWindowOuter::GetFromCurrentInner(this);
4201 bool nsGlobalWindowInner::DispatchEvent(Event& aEvent, CallerType aCallerType,
4202 ErrorResult& aRv) {
4203 if (!IsCurrentInnerWindow()) {
4204 NS_WARNING(
4205 "DispatchEvent called on non-current inner window, dropping. "
4206 "Please check the window in the caller instead.");
4207 aRv.Throw(NS_ERROR_FAILURE);
4208 return false;
4211 if (!mDoc) {
4212 aRv.Throw(NS_ERROR_FAILURE);
4213 return false;
4216 // Obtain a presentation shell
4217 RefPtr<nsPresContext> presContext = mDoc->GetPresContext();
4219 nsEventStatus status = nsEventStatus_eIgnore;
4220 nsresult rv = EventDispatcher::DispatchDOMEvent(this, nullptr, &aEvent,
4221 presContext, &status);
4222 bool retval = !aEvent.DefaultPrevented(aCallerType);
4223 if (NS_FAILED(rv)) {
4224 aRv.Throw(rv);
4226 return retval;
4229 mozilla::Maybe<mozilla::dom::EventCallbackDebuggerNotificationType>
4230 nsGlobalWindowInner::GetDebuggerNotificationType() const {
4231 return mozilla::Some(
4232 mozilla::dom::EventCallbackDebuggerNotificationType::Global);
4235 bool nsGlobalWindowInner::ComputeDefaultWantsUntrusted(ErrorResult& aRv) {
4236 return !nsContentUtils::IsChromeDoc(mDoc);
4239 EventListenerManager* nsGlobalWindowInner::GetOrCreateListenerManager() {
4240 if (!mListenerManager) {
4241 mListenerManager =
4242 new EventListenerManager(static_cast<EventTarget*>(this));
4245 return mListenerManager;
4248 EventListenerManager* nsGlobalWindowInner::GetExistingListenerManager() const {
4249 return mListenerManager;
4252 mozilla::dom::DebuggerNotificationManager*
4253 nsGlobalWindowInner::GetOrCreateDebuggerNotificationManager() {
4254 if (!mDebuggerNotificationManager) {
4255 mDebuggerNotificationManager = new DebuggerNotificationManager(this);
4258 return mDebuggerNotificationManager;
4261 mozilla::dom::DebuggerNotificationManager*
4262 nsGlobalWindowInner::GetExistingDebuggerNotificationManager() {
4263 return mDebuggerNotificationManager;
4266 //*****************************************************************************
4267 // nsGlobalWindowInner::nsPIDOMWindow
4268 //*****************************************************************************
4270 Location* nsGlobalWindowInner::Location() {
4271 if (!mLocation) {
4272 mLocation = new dom::Location(this);
4275 return mLocation;
4278 void nsGlobalWindowInner::MaybeUpdateTouchState() {
4279 if (mMayHaveTouchEventListener) {
4280 nsCOMPtr<nsIObserverService> observerService =
4281 services::GetObserverService();
4283 if (observerService) {
4284 observerService->NotifyObservers(static_cast<nsIDOMWindow*>(this),
4285 DOM_TOUCH_LISTENER_ADDED, nullptr);
4290 void nsGlobalWindowInner::EnableGamepadUpdates() {
4291 if (mHasGamepad) {
4292 RefPtr<GamepadManager> gamepadManager(GamepadManager::GetService());
4293 if (gamepadManager) {
4294 gamepadManager->AddListener(this);
4299 void nsGlobalWindowInner::DisableGamepadUpdates() {
4300 if (mHasGamepad) {
4301 RefPtr<GamepadManager> gamepadManager(GamepadManager::GetService());
4302 if (gamepadManager) {
4303 gamepadManager->RemoveListener(this);
4308 void nsGlobalWindowInner::EnableVRUpdates() {
4309 // We need to create a VREventObserver before we can either detect XR runtimes
4310 // or start an XR session
4311 if (!mVREventObserver && (mHasXRSession || mXRRuntimeDetectionInFlight)) {
4312 // Assert that we are not creating the observer while IsDying() as
4313 // that would result in a leak. VREventObserver holds a RefPtr to
4314 // this nsGlobalWindowInner and would prevent it from being deallocated.
4315 MOZ_ASSERT(!IsDying(),
4316 "Creating a VREventObserver for an nsGlobalWindow that is "
4317 "dying would cause it to leak.");
4318 mVREventObserver = new VREventObserver(this);
4320 // If the content has an XR session, then we need to tell
4321 // VREventObserver that there is VR activity.
4322 if (mHasXRSession) {
4323 nsPIDOMWindowOuter* outer = GetOuterWindow();
4324 if (outer && !outer->IsBackground()) {
4325 StartVRActivity();
4330 void nsGlobalWindowInner::DisableVRUpdates() {
4331 if (mVREventObserver) {
4332 mVREventObserver->DisconnectFromOwner();
4333 mVREventObserver = nullptr;
4337 void nsGlobalWindowInner::ResetVRTelemetry(bool aUpdate) {
4338 if (mVREventObserver) {
4339 mVREventObserver->UpdateSpentTimeIn2DTelemetry(aUpdate);
4343 void nsGlobalWindowInner::StartVRActivity() {
4345 * If the content has an XR session, tell
4346 * the VREventObserver that the window is accessing
4347 * VR devices.
4349 * It's possible to have a VREventObserver without
4350 * and XR session, if we are using it to get updates
4351 * about XR runtime enumeration. In this case,
4352 * we would not tell the VREventObserver that
4353 * we are accessing VR devices.
4355 if (mVREventObserver && mHasXRSession) {
4356 mVREventObserver->StartActivity();
4360 void nsGlobalWindowInner::StopVRActivity() {
4362 * If the content has an XR session, tell
4363 * the VReventObserver that the window is no longer
4364 * accessing VR devices. This does not stop the
4365 * XR session itself, which may be resumed with
4366 * EnableVRUpdates.
4367 * It's possible to have a VREventObserver without
4368 * and XR session, if we are using it to get updates
4369 * about XR runtime enumeration. In this case,
4370 * we would not tell the VREventObserver that
4371 * we ending an activity that accesses VR devices.
4373 if (mVREventObserver && mHasXRSession) {
4374 mVREventObserver->StopActivity();
4378 void nsGlobalWindowInner::SetFocusedElement(Element* aElement,
4379 uint32_t aFocusMethod,
4380 bool aNeedsFocus) {
4381 if (aElement && aElement->GetComposedDoc() != mDoc) {
4382 NS_WARNING("Trying to set focus to a node from a wrong document");
4383 return;
4386 if (IsDying()) {
4387 NS_ASSERTION(!aElement, "Trying to focus cleaned up window!");
4388 aElement = nullptr;
4389 aNeedsFocus = false;
4391 if (mFocusedElement != aElement) {
4392 UpdateCanvasFocus(false, aElement);
4393 mFocusedElement = aElement;
4394 // TODO: Maybe this should be set on refocus too?
4395 mFocusMethod = aFocusMethod & nsIFocusManager::METHOD_MASK;
4398 if (mFocusedElement) {
4399 // if a node was focused by a keypress, turn on focus rings for the
4400 // window.
4401 if (mFocusMethod & nsIFocusManager::FLAG_BYKEY) {
4402 mUnknownFocusMethodShouldShowOutline = true;
4403 mFocusByKeyOccurred = true;
4404 } else if (nsFocusManager::GetFocusMoveActionCause(mFocusMethod) !=
4405 widget::InputContextAction::CAUSE_UNKNOWN) {
4406 mUnknownFocusMethodShouldShowOutline = false;
4407 } else if (aFocusMethod & nsIFocusManager::FLAG_NOSHOWRING) {
4408 // If we get focused via script, and script has explicitly opted out of
4409 // outlines via FLAG_NOSHOWRING, we don't want to make a refocus start
4410 // showing outlines.
4411 mUnknownFocusMethodShouldShowOutline = false;
4415 if (aNeedsFocus) {
4416 mNeedsFocus = aNeedsFocus;
4420 uint32_t nsGlobalWindowInner::GetFocusMethod() { return mFocusMethod; }
4422 bool nsGlobalWindowInner::ShouldShowFocusRing() {
4423 if (mFocusByKeyOccurred &&
4424 StaticPrefs::browser_display_always_show_rings_after_key_focus()) {
4425 return true;
4427 return StaticPrefs::browser_display_show_focus_rings();
4430 bool nsGlobalWindowInner::TakeFocus(bool aFocus, uint32_t aFocusMethod) {
4431 if (IsDying()) {
4432 return false;
4435 if (aFocus) {
4436 mFocusMethod = aFocusMethod & nsIFocusManager::METHOD_MASK;
4439 if (mHasFocus != aFocus) {
4440 mHasFocus = aFocus;
4441 UpdateCanvasFocus(true, mFocusedElement);
4444 // if mNeedsFocus is true, then the document has not yet received a
4445 // document-level focus event. If there is a root content node, then return
4446 // true to tell the calling focus manager that a focus event is expected. If
4447 // there is no root content node, the document hasn't loaded enough yet, or
4448 // there isn't one and there is no point in firing a focus event.
4449 if (aFocus && mNeedsFocus && mDoc && mDoc->GetRootElement() != nullptr) {
4450 mNeedsFocus = false;
4451 return true;
4454 mNeedsFocus = false;
4455 return false;
4458 void nsGlobalWindowInner::SetReadyForFocus() {
4459 bool oldNeedsFocus = mNeedsFocus;
4460 mNeedsFocus = false;
4462 if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
4463 nsCOMPtr<nsPIDOMWindowOuter> outerWindow = GetOuterWindow();
4464 fm->WindowShown(outerWindow, oldNeedsFocus);
4468 void nsGlobalWindowInner::PageHidden() {
4469 // the window is being hidden, so tell the focus manager that the frame is
4470 // no longer valid. Use the persisted field to determine if the document
4471 // is being destroyed.
4473 if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
4474 nsCOMPtr<nsPIDOMWindowOuter> outerWindow = GetOuterWindow();
4475 fm->WindowHidden(outerWindow, nsFocusManager::GenerateFocusActionId());
4478 mNeedsFocus = true;
4481 class HashchangeCallback : public Runnable {
4482 public:
4483 HashchangeCallback(const nsAString& aOldURL, const nsAString& aNewURL,
4484 nsGlobalWindowInner* aWindow)
4485 : mozilla::Runnable("HashchangeCallback"), mWindow(aWindow) {
4486 MOZ_ASSERT(mWindow);
4487 mOldURL.Assign(aOldURL);
4488 mNewURL.Assign(aNewURL);
4491 NS_IMETHOD Run() override {
4492 MOZ_ASSERT(NS_IsMainThread(), "Should be called on the main thread.");
4493 return mWindow->FireHashchange(mOldURL, mNewURL);
4496 private:
4497 nsString mOldURL;
4498 nsString mNewURL;
4499 RefPtr<nsGlobalWindowInner> mWindow;
4502 nsresult nsGlobalWindowInner::DispatchAsyncHashchange(nsIURI* aOldURI,
4503 nsIURI* aNewURI) {
4504 // Make sure that aOldURI and aNewURI are identical up to the '#', and that
4505 // their hashes are different.
4506 bool equal = false;
4507 NS_ENSURE_STATE(NS_SUCCEEDED(aOldURI->EqualsExceptRef(aNewURI, &equal)) &&
4508 equal);
4509 nsAutoCString oldHash, newHash;
4510 bool oldHasHash, newHasHash;
4511 NS_ENSURE_STATE(NS_SUCCEEDED(aOldURI->GetRef(oldHash)) &&
4512 NS_SUCCEEDED(aNewURI->GetRef(newHash)) &&
4513 NS_SUCCEEDED(aOldURI->GetHasRef(&oldHasHash)) &&
4514 NS_SUCCEEDED(aNewURI->GetHasRef(&newHasHash)) &&
4515 (oldHasHash != newHasHash || !oldHash.Equals(newHash)));
4517 nsAutoCString oldSpec, newSpec;
4518 nsresult rv = aOldURI->GetSpec(oldSpec);
4519 NS_ENSURE_SUCCESS(rv, rv);
4520 rv = aNewURI->GetSpec(newSpec);
4521 NS_ENSURE_SUCCESS(rv, rv);
4523 NS_ConvertUTF8toUTF16 oldWideSpec(oldSpec);
4524 NS_ConvertUTF8toUTF16 newWideSpec(newSpec);
4526 nsCOMPtr<nsIRunnable> callback =
4527 new HashchangeCallback(oldWideSpec, newWideSpec, this);
4528 return Dispatch(callback.forget());
4531 nsresult nsGlobalWindowInner::FireHashchange(const nsAString& aOldURL,
4532 const nsAString& aNewURL) {
4533 // Don't do anything if the window is frozen.
4534 if (IsFrozen()) {
4535 return NS_OK;
4538 // Get a presentation shell for use in creating the hashchange event.
4539 NS_ENSURE_STATE(IsCurrentInnerWindow());
4541 HashChangeEventInit init;
4542 init.mNewURL = aNewURL;
4543 init.mOldURL = aOldURL;
4545 RefPtr<HashChangeEvent> event =
4546 HashChangeEvent::Constructor(this, u"hashchange"_ns, init);
4548 event->SetTrusted(true);
4550 ErrorResult rv;
4551 DispatchEvent(*event, rv);
4552 return rv.StealNSResult();
4555 nsresult nsGlobalWindowInner::DispatchSyncPopState() {
4556 NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
4557 "Must be safe to run script here.");
4559 // Bail if the window is frozen.
4560 if (IsFrozen()) {
4561 return NS_OK;
4564 AutoJSAPI jsapi;
4565 bool result = jsapi.Init(this);
4566 NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
4568 JSContext* cx = jsapi.cx();
4570 // Get the document's pending state object -- it contains the data we're
4571 // going to send along with the popstate event. The object is serialized
4572 // using structured clone.
4573 JS::Rooted<JS::Value> stateJSValue(cx);
4574 nsresult rv = mDoc->GetStateObject(&stateJSValue);
4575 NS_ENSURE_SUCCESS(rv, rv);
4577 if (!JS_WrapValue(cx, &stateJSValue)) {
4578 return NS_ERROR_OUT_OF_MEMORY;
4581 RootedDictionary<PopStateEventInit> init(cx);
4582 init.mState = stateJSValue;
4584 RefPtr<PopStateEvent> event =
4585 PopStateEvent::Constructor(this, u"popstate"_ns, init);
4586 event->SetTrusted(true);
4587 event->SetTarget(this);
4589 ErrorResult err;
4590 DispatchEvent(*event, err);
4591 return err.StealNSResult();
4594 //-------------------------------------------------------
4595 // Tells the HTMLFrame/CanvasFrame that is now has focus
4596 void nsGlobalWindowInner::UpdateCanvasFocus(bool aFocusChanged,
4597 nsIContent* aNewContent) {
4598 // this is called from the inner window so use GetDocShell
4599 nsIDocShell* docShell = GetDocShell();
4600 if (!docShell) return;
4602 bool editable;
4603 docShell->GetEditable(&editable);
4604 if (editable) return;
4606 PresShell* presShell = docShell->GetPresShell();
4607 if (!presShell || !mDoc) {
4608 return;
4611 Element* rootElement = mDoc->GetRootElement();
4612 if (rootElement) {
4613 if ((mHasFocus || aFocusChanged) &&
4614 (mFocusedElement == rootElement || aNewContent == rootElement)) {
4615 nsCanvasFrame* canvasFrame = presShell->GetCanvasFrame();
4616 if (canvasFrame) {
4617 canvasFrame->SetHasFocus(mHasFocus && rootElement == aNewContent);
4620 } else {
4621 // XXXbz I would expect that there is never a canvasFrame in this case...
4622 nsCanvasFrame* canvasFrame = presShell->GetCanvasFrame();
4623 if (canvasFrame) {
4624 canvasFrame->SetHasFocus(false);
4629 already_AddRefed<nsICSSDeclaration> nsGlobalWindowInner::GetComputedStyle(
4630 Element& aElt, const nsAString& aPseudoElt, ErrorResult& aError) {
4631 return GetComputedStyleHelper(aElt, aPseudoElt, false, aError);
4634 already_AddRefed<nsICSSDeclaration>
4635 nsGlobalWindowInner::GetDefaultComputedStyle(Element& aElt,
4636 const nsAString& aPseudoElt,
4637 ErrorResult& aError) {
4638 return GetComputedStyleHelper(aElt, aPseudoElt, true, aError);
4641 already_AddRefed<nsICSSDeclaration> nsGlobalWindowInner::GetComputedStyleHelper(
4642 Element& aElt, const nsAString& aPseudoElt, bool aDefaultStylesOnly,
4643 ErrorResult& aError) {
4644 FORWARD_TO_OUTER_OR_THROW(GetComputedStyleHelperOuter,
4645 (aElt, aPseudoElt, aDefaultStylesOnly, aError),
4646 aError, nullptr);
4649 Storage* nsGlobalWindowInner::GetSessionStorage(ErrorResult& aError) {
4650 nsIPrincipal* principal = GetPrincipal();
4651 nsIPrincipal* storagePrincipal;
4652 if (StaticPrefs::
4653 privacy_partition_always_partition_third_party_non_cookie_storage_exempt_sessionstorage()) {
4654 storagePrincipal = GetEffectiveCookiePrincipal();
4655 } else {
4656 storagePrincipal = GetEffectiveStoragePrincipal();
4658 BrowsingContext* browsingContext = GetBrowsingContext();
4660 if (!principal || !storagePrincipal || !browsingContext ||
4661 !Storage::StoragePrefIsEnabled()) {
4662 return nullptr;
4665 if (mSessionStorage) {
4666 MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
4667 ("nsGlobalWindowInner %p has %p sessionStorage", this,
4668 mSessionStorage.get()));
4669 bool canAccess =
4670 principal->Subsumes(mSessionStorage->Principal()) &&
4671 storagePrincipal->Subsumes(mSessionStorage->StoragePrincipal());
4672 if (!canAccess) {
4673 mSessionStorage = nullptr;
4677 if (!mSessionStorage) {
4678 nsString documentURI;
4679 if (mDoc) {
4680 aError = mDoc->GetDocumentURI(documentURI);
4681 if (NS_WARN_IF(aError.Failed())) {
4682 return nullptr;
4686 if (!mDoc) {
4687 aError.Throw(NS_ERROR_FAILURE);
4688 return nullptr;
4691 // If the document's sandboxed origin flag is set, then accessing
4692 // sessionStorage is prohibited.
4693 if (mDoc->GetSandboxFlags() & SANDBOXED_ORIGIN) {
4694 aError.ThrowSecurityError(
4695 "Forbidden in a sandboxed document without the 'allow-same-origin' "
4696 "flag.");
4697 return nullptr;
4700 uint32_t rejectedReason = 0;
4701 StorageAccess access = StorageAllowedForWindow(this, &rejectedReason);
4703 // SessionStorage is an ephemeral per-tab per-origin storage that only lives
4704 // as long as the tab is open, although it may survive browser restarts
4705 // thanks to the session store. So we interpret storage access differently
4706 // than we would for persistent per-origin storage like LocalStorage and so
4707 // it may be okay to provide SessionStorage even when we receive a value of
4708 // eDeny.
4710 // ContentBlocking::ShouldAllowAccessFor will return false for 3 main
4711 // reasons.
4713 // 1. Cookies are entirely blocked due to a per-origin permission
4714 // (nsICookiePermission::ACCESS_DENY for the top-level principal or this
4715 // window's principal) or the very broad BEHAVIOR_REJECT. This will return
4716 // eDeny with a reason of STATE_COOKIES_BLOCKED_BY_PERMISSION or
4717 // STATE_COOKIES_BLOCKED_ALL.
4719 // 2. Third-party cookies are limited via BEHAVIOR_REJECT_FOREIGN and
4720 // BEHAVIOR_LIMIT_FOREIGN and this is a third-party window. This will return
4721 // eDeny with a reason of STATE_COOKIES_BLOCKED_FOREIGN.
4723 // 3. Tracking protection (BEHAVIOR_REJECT_TRACKER and
4724 // BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN) is in effect and
4725 // IsThirdPartyTrackingResourceWindow() returned true and there wasn't a
4726 // permission that allows it. This will return ePartitionTrackersOrDeny with
4727 // a reason of STATE_COOKIES_BLOCKED_TRACKER or
4728 // STATE_COOKIES_BLOCKED_SOCIALTRACKER.
4730 // In the 1st case, the user has explicitly indicated that they don't want
4731 // to allow any storage to the origin or all origins and so we throw an
4732 // error and deny access to SessionStorage. In the 2nd case, a legacy
4733 // decision reasoned that there's no harm in providing SessionStorage
4734 // because the information is not durable and cannot escape the current tab.
4735 // The rationale is similar for the 3rd case.
4736 if (access == StorageAccess::eDeny &&
4737 rejectedReason !=
4738 nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN) {
4739 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
4740 return nullptr;
4743 const RefPtr<SessionStorageManager> storageManager =
4744 browsingContext->GetSessionStorageManager();
4745 if (!storageManager) {
4746 aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
4747 return nullptr;
4750 RefPtr<Storage> storage;
4751 aError = storageManager->CreateStorage(this, principal, storagePrincipal,
4752 documentURI, IsPrivateBrowsing(),
4753 getter_AddRefs(storage));
4754 if (aError.Failed()) {
4755 return nullptr;
4758 mSessionStorage = storage;
4759 MOZ_ASSERT(mSessionStorage);
4761 MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
4762 ("nsGlobalWindowInner %p tried to get a new sessionStorage %p",
4763 this, mSessionStorage.get()));
4765 if (!mSessionStorage) {
4766 aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
4767 return nullptr;
4771 MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
4772 ("nsGlobalWindowInner %p returns %p sessionStorage", this,
4773 mSessionStorage.get()));
4775 return mSessionStorage;
4778 Storage* nsGlobalWindowInner::GetLocalStorage(ErrorResult& aError) {
4779 if (!Storage::StoragePrefIsEnabled()) {
4780 return nullptr;
4783 // If the document's sandboxed origin flag is set, then accessing localStorage
4784 // is prohibited.
4785 if (mDoc && mDoc->GetSandboxFlags() & SANDBOXED_ORIGIN) {
4786 aError.ThrowSecurityError(
4787 "Forbidden in a sandboxed document without the 'allow-same-origin' "
4788 "flag.");
4789 return nullptr;
4792 // LocalStorage needs to be exposed in every context except for sandboxes and
4793 // NullPrincipals (data: URLs, for instance). But we need to keep data
4794 // separate in some scenarios: private-browsing and partitioned trackers.
4795 // In private-browsing, LocalStorage keeps data in memory, and it shares
4796 // StorageEvents just with other origins in the same private-browsing
4797 // environment.
4798 // For Partitioned Trackers, we expose a partitioned LocalStorage, which
4799 // doesn't share data with other contexts, and it's just in memory.
4800 // Partitioned localStorage is available only for trackers listed in the
4801 // privacy.restrict3rdpartystorage.partitionedHosts pref. See
4802 // nsContentUtils::IsURIInPrefList to know the syntax for the pref value.
4803 // This is a temporary web-compatibility hack.
4805 StorageAccess access = StorageAllowedForWindow(this);
4807 // We allow partitioned localStorage only to some hosts.
4808 bool isolated = false;
4809 if (ShouldPartitionStorage(access)) {
4810 if (!mDoc) {
4811 access = StorageAccess::eDeny;
4812 } else if (!StoragePartitioningEnabled(access, mDoc->CookieJarSettings())) {
4813 static const char* kPrefName =
4814 "privacy.restrict3rdpartystorage.partitionedHosts";
4816 bool isInList = false;
4817 mDoc->NodePrincipal()->IsURIInPrefList(kPrefName, &isInList);
4818 if (!isInList) {
4819 access = StorageAccess::eDeny;
4820 } else {
4821 isolated = true;
4826 if (access == StorageAccess::eDeny) {
4827 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
4828 return nullptr;
4831 nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
4832 if (mDoc) {
4833 cookieJarSettings = mDoc->CookieJarSettings();
4834 } else {
4835 cookieJarSettings = net::CookieJarSettings::GetBlockingAll(
4836 ShouldResistFingerprinting(RFPTarget::IsAlwaysEnabledForPrecompute));
4839 // Note that this behavior is observable: if we grant storage permission to a
4840 // tracker, we pass from the partitioned LocalStorage (or a partitioned cookie
4841 // jar) to the 'normal' one. The previous data is lost and the 2
4842 // window.localStorage objects, before and after the permission granted, will
4843 // be different.
4844 if (mLocalStorage) {
4845 if ((mLocalStorage->Type() == (isolated ? Storage::ePartitionedLocalStorage
4846 : Storage::eLocalStorage)) &&
4847 (mLocalStorage->StoragePrincipal() == GetEffectiveStoragePrincipal())) {
4848 return mLocalStorage;
4851 // storage needs change
4852 mLocalStorage = nullptr;
4855 MOZ_ASSERT(!mLocalStorage);
4857 if (!isolated) {
4858 RefPtr<Storage> storage;
4860 if (NextGenLocalStorageEnabled()) {
4861 aError = LSObject::CreateForWindow(this, getter_AddRefs(storage));
4862 } else {
4863 nsresult rv;
4864 nsCOMPtr<nsIDOMStorageManager> storageManager =
4865 do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv);
4866 if (NS_FAILED(rv)) {
4867 aError.Throw(rv);
4868 return nullptr;
4871 nsString documentURI;
4872 if (mDoc) {
4873 aError = mDoc->GetDocumentURI(documentURI);
4874 if (NS_WARN_IF(aError.Failed())) {
4875 return nullptr;
4879 nsIPrincipal* principal = GetPrincipal();
4880 if (!principal) {
4881 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
4882 return nullptr;
4885 nsIPrincipal* storagePrincipal = GetEffectiveStoragePrincipal();
4886 if (!storagePrincipal) {
4887 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
4888 return nullptr;
4891 aError = storageManager->CreateStorage(this, principal, storagePrincipal,
4892 documentURI, IsPrivateBrowsing(),
4893 getter_AddRefs(storage));
4896 if (aError.Failed()) {
4897 return nullptr;
4900 mLocalStorage = storage;
4901 } else {
4902 nsresult rv;
4903 nsCOMPtr<nsIDOMSessionStorageManager> storageManager =
4904 do_GetService("@mozilla.org/dom/sessionStorage-manager;1", &rv);
4905 if (NS_FAILED(rv)) {
4906 aError.Throw(rv);
4907 return nullptr;
4910 nsIPrincipal* principal = GetPrincipal();
4911 if (!principal) {
4912 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
4913 return nullptr;
4916 nsIPrincipal* storagePrincipal = GetEffectiveStoragePrincipal();
4917 if (!storagePrincipal) {
4918 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
4919 return nullptr;
4922 RefPtr<SessionStorageCache> cache;
4923 if (isolated) {
4924 cache = new SessionStorageCache();
4925 } else {
4926 // This will clone the session storage if it exists.
4927 rv = storageManager->GetSessionStorageCache(principal, storagePrincipal,
4928 &cache);
4929 if (NS_FAILED(rv)) {
4930 aError.Throw(rv);
4931 return nullptr;
4935 mLocalStorage =
4936 new PartitionedLocalStorage(this, principal, storagePrincipal, cache);
4939 MOZ_ASSERT(mLocalStorage);
4940 MOZ_ASSERT(
4941 mLocalStorage->Type() ==
4942 (isolated ? Storage::ePartitionedLocalStorage : Storage::eLocalStorage));
4943 return mLocalStorage;
4946 IDBFactory* nsGlobalWindowInner::GetIndexedDB(JSContext* aCx,
4947 ErrorResult& aError) {
4948 if (!mIndexedDB) {
4949 // This may keep mIndexedDB null without setting an error.
4950 auto res = IDBFactory::CreateForWindow(this);
4951 if (res.isErr()) {
4952 aError = res.unwrapErr();
4953 } else {
4954 mIndexedDB = res.unwrap();
4958 return mIndexedDB;
4961 //*****************************************************************************
4962 // nsGlobalWindowInner::nsIInterfaceRequestor
4963 //*****************************************************************************
4965 NS_IMETHODIMP
4966 nsGlobalWindowInner::GetInterface(const nsIID& aIID, void** aSink) {
4967 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
4968 NS_ENSURE_TRUE(outer, NS_ERROR_NOT_INITIALIZED);
4970 nsresult rv = outer->GetInterfaceInternal(aIID, aSink);
4971 if (rv == NS_ERROR_NO_INTERFACE) {
4972 return QueryInterface(aIID, aSink);
4974 return rv;
4977 void nsGlobalWindowInner::GetInterface(JSContext* aCx,
4978 JS::Handle<JS::Value> aIID,
4979 JS::MutableHandle<JS::Value> aRetval,
4980 ErrorResult& aError) {
4981 dom::GetInterface(aCx, this, aIID, aRetval, aError);
4984 already_AddRefed<CacheStorage> nsGlobalWindowInner::GetCaches(
4985 ErrorResult& aRv) {
4986 if (!mCacheStorage) {
4987 bool forceTrustedOrigin =
4988 GetBrowsingContext() &&
4989 GetBrowsingContext()->Top()->GetServiceWorkersTestingEnabled();
4990 mCacheStorage = CacheStorage::CreateOnMainThread(
4991 cache::DEFAULT_NAMESPACE, this, GetEffectiveStoragePrincipal(),
4992 forceTrustedOrigin, aRv);
4995 RefPtr<CacheStorage> ref = mCacheStorage;
4996 return ref.forget();
4999 void nsGlobalWindowInner::FireOfflineStatusEventIfChanged() {
5000 if (!IsCurrentInnerWindow()) return;
5002 // Don't fire an event if the status hasn't changed
5003 if (mWasOffline == NS_IsOffline()) {
5004 return;
5007 mWasOffline = !mWasOffline;
5009 nsAutoString name;
5010 if (mWasOffline) {
5011 name.AssignLiteral("offline");
5012 } else {
5013 name.AssignLiteral("online");
5015 nsContentUtils::DispatchTrustedEvent(mDoc, this, name, CanBubble::eNo,
5016 Cancelable::eNo);
5019 nsGlobalWindowInner::SlowScriptResponse
5020 nsGlobalWindowInner::ShowSlowScriptDialog(JSContext* aCx,
5021 const nsString& aAddonId,
5022 const double aDuration) {
5023 nsresult rv;
5025 if (Preferences::GetBool("dom.always_stop_slow_scripts")) {
5026 return KillSlowScript;
5029 // If it isn't safe to run script, then it isn't safe to bring up the prompt
5030 // (since that spins the event loop). In that (rare) case, we just kill the
5031 // script and report a warning.
5032 if (!nsContentUtils::IsSafeToRunScript()) {
5033 JS::WarnASCII(aCx, "A long running script was terminated");
5034 return KillSlowScript;
5037 // If our document is not active, just kill the script: we've been unloaded
5038 if (!HasActiveDocument()) {
5039 return KillSlowScript;
5042 // Check if we should offer the option to debug
5043 JS::AutoFilename filename;
5044 uint32_t lineno;
5045 // Computing the line number can be very expensive (see bug 1330231 for
5046 // example), and we don't use the line number anywhere except than in the
5047 // parent process, so we avoid computing it elsewhere. This gives us most of
5048 // the wins we are interested in, since the source of the slowness here is
5049 // minified scripts which is more common in Web content that is loaded in the
5050 // content process.
5051 uint32_t* linenop = XRE_IsParentProcess() ? &lineno : nullptr;
5052 bool hasFrame = JS::DescribeScriptedCaller(aCx, &filename, linenop);
5054 // Record the slow script event if we haven't done so already for this inner
5055 // window (which represents a particular page to the user).
5056 if (!mHasHadSlowScript) {
5057 Telemetry::Accumulate(Telemetry::SLOW_SCRIPT_PAGE_COUNT, 1);
5059 mHasHadSlowScript = true;
5061 // Override the cursor to something that we're sure the user can see.
5062 SetCursor("auto"_ns, IgnoreErrors());
5064 if (XRE_IsContentProcess() && ProcessHangMonitor::Get()) {
5065 ProcessHangMonitor::SlowScriptAction action;
5066 RefPtr<ProcessHangMonitor> monitor = ProcessHangMonitor::Get();
5067 nsIDocShell* docShell = GetDocShell();
5068 nsCOMPtr<nsIBrowserChild> child =
5069 docShell ? docShell->GetBrowserChild() : nullptr;
5070 action =
5071 monitor->NotifySlowScript(child, filename.get(), aAddonId, aDuration);
5072 if (action == ProcessHangMonitor::Terminate) {
5073 return KillSlowScript;
5076 if (action == ProcessHangMonitor::StartDebugger) {
5077 // Spin a nested event loop so that the debugger in the parent can fetch
5078 // any information it needs. Once the debugger has started, return to the
5079 // script.
5080 RefPtr<nsGlobalWindowOuter> outer = GetOuterWindowInternal();
5081 outer->EnterModalState();
5082 SpinEventLoopUntil("nsGlobalWindowInner::ShowSlowScriptDialog"_ns, [&]() {
5083 return monitor->IsDebuggerStartupComplete();
5085 outer->LeaveModalState();
5086 return ContinueSlowScript;
5089 return ContinueSlowScriptAndKeepNotifying;
5092 // Reached only on non-e10s - once per slow script dialog.
5093 // On e10s - we probe once at ProcessHangsMonitor.jsm
5094 Telemetry::Accumulate(Telemetry::SLOW_SCRIPT_NOTICE_COUNT, 1);
5096 // Get the nsIPrompt interface from the docshell
5097 nsCOMPtr<nsIDocShell> ds = GetDocShell();
5098 NS_ENSURE_TRUE(ds, KillSlowScript);
5099 nsCOMPtr<nsIPrompt> prompt = do_GetInterface(ds);
5100 NS_ENSURE_TRUE(prompt, KillSlowScript);
5102 // Prioritize the SlowScriptDebug interface over JSD1.
5103 nsCOMPtr<nsISlowScriptDebugCallback> debugCallback;
5105 if (hasFrame) {
5106 const char* debugCID = "@mozilla.org/dom/slow-script-debug;1";
5107 nsCOMPtr<nsISlowScriptDebug> debugService = do_GetService(debugCID, &rv);
5108 if (NS_SUCCEEDED(rv)) {
5109 debugService->GetActivationHandler(getter_AddRefs(debugCallback));
5113 bool failed = false;
5114 auto getString = [&](const char* name,
5115 nsContentUtils::PropertiesFile propFile =
5116 nsContentUtils::eDOM_PROPERTIES) {
5117 nsAutoString result;
5118 nsresult rv = nsContentUtils::GetLocalizedString(propFile, name, result);
5120 // GetStringFromName can return NS_OK and still give nullptr string
5121 failed = failed || NS_FAILED(rv) || result.IsEmpty();
5122 return result;
5125 bool isAddonScript = !aAddonId.IsEmpty();
5126 bool showDebugButton = debugCallback && !isAddonScript;
5128 // Get localizable strings
5130 nsAutoString title, checkboxMsg, debugButton, msg;
5131 if (isAddonScript) {
5132 title = getString("KillAddonScriptTitle");
5133 checkboxMsg = getString("KillAddonScriptGlobalMessage");
5135 auto appName =
5136 getString("brandShortName", nsContentUtils::eBRAND_PROPERTIES);
5138 nsCOMPtr<nsIAddonPolicyService> aps =
5139 do_GetService("@mozilla.org/addons/policy-service;1");
5140 nsString addonName;
5141 if (!aps || NS_FAILED(aps->GetExtensionName(aAddonId, addonName))) {
5142 addonName = aAddonId;
5145 rv = nsContentUtils::FormatLocalizedString(
5146 msg, nsContentUtils::eDOM_PROPERTIES, "KillAddonScriptMessage",
5147 addonName, appName);
5149 failed = failed || NS_FAILED(rv);
5150 } else {
5151 title = getString("KillScriptTitle");
5152 checkboxMsg = getString("DontAskAgain");
5154 if (showDebugButton) {
5155 debugButton = getString("DebugScriptButton");
5156 msg = getString("KillScriptWithDebugMessage");
5157 } else {
5158 msg = getString("KillScriptMessage");
5162 auto stopButton = getString("StopScriptButton");
5163 auto waitButton = getString("WaitForScriptButton");
5165 if (failed) {
5166 NS_ERROR("Failed to get localized strings.");
5167 return ContinueSlowScript;
5170 // Append file and line number information, if available
5171 if (filename.get()) {
5172 nsAutoString scriptLocation;
5173 // We want to drop the middle part of too-long locations. We'll
5174 // define "too-long" as longer than 60 UTF-16 code units. Just
5175 // have to be a bit careful about unpaired surrogates.
5176 NS_ConvertUTF8toUTF16 filenameUTF16(filename.get());
5177 if (filenameUTF16.Length() > 60) {
5178 // XXXbz Do we need to insert any bidi overrides here?
5179 size_t cutStart = 30;
5180 size_t cutLength = filenameUTF16.Length() - 60;
5181 MOZ_ASSERT(cutLength > 0);
5182 if (NS_IS_LOW_SURROGATE(filenameUTF16[cutStart])) {
5183 // Don't truncate before the low surrogate, in case it's preceded by a
5184 // high surrogate and forms a single Unicode character. Instead, just
5185 // include the low surrogate.
5186 ++cutStart;
5187 --cutLength;
5189 if (NS_IS_LOW_SURROGATE(filenameUTF16[cutStart + cutLength])) {
5190 // Likewise, don't drop a trailing low surrogate here. We want to
5191 // increase cutLength, since it might be 0 already so we can't very well
5192 // decrease it.
5193 ++cutLength;
5196 // Insert U+2026 HORIZONTAL ELLIPSIS
5197 filenameUTF16.ReplaceLiteral(cutStart, cutLength, u"\x2026");
5199 rv = nsContentUtils::FormatLocalizedString(
5200 scriptLocation, nsContentUtils::eDOM_PROPERTIES, "KillScriptLocation",
5201 filenameUTF16);
5203 if (NS_SUCCEEDED(rv)) {
5204 msg.AppendLiteral("\n\n");
5205 msg.Append(scriptLocation);
5206 msg.Append(':');
5207 msg.AppendInt(lineno);
5211 uint32_t buttonFlags = nsIPrompt::BUTTON_POS_1_DEFAULT +
5212 (nsIPrompt::BUTTON_TITLE_IS_STRING *
5213 (nsIPrompt::BUTTON_POS_0 + nsIPrompt::BUTTON_POS_1));
5215 // Add a third button if necessary.
5216 if (showDebugButton)
5217 buttonFlags += nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_2;
5219 bool checkboxValue = false;
5220 int32_t buttonPressed = 0; // In case the user exits dialog by clicking X.
5222 // Null out the operation callback while we're re-entering JS here.
5223 AutoDisableJSInterruptCallback disabler(aCx);
5225 // Open the dialog.
5226 rv = prompt->ConfirmEx(
5227 title.get(), msg.get(), buttonFlags, waitButton.get(), stopButton.get(),
5228 debugButton.get(), checkboxMsg.get(), &checkboxValue, &buttonPressed);
5231 if (buttonPressed == 0) {
5232 if (checkboxValue && !isAddonScript && NS_SUCCEEDED(rv))
5233 return AlwaysContinueSlowScript;
5234 return ContinueSlowScript;
5237 if (buttonPressed == 2) {
5238 MOZ_RELEASE_ASSERT(debugCallback);
5240 rv = debugCallback->HandleSlowScriptDebug(this);
5241 return NS_SUCCEEDED(rv) ? ContinueSlowScript : KillSlowScript;
5244 JS_ClearPendingException(aCx);
5246 return KillSlowScript;
5249 nsresult nsGlobalWindowInner::Observe(nsISupports* aSubject, const char* aTopic,
5250 const char16_t* aData) {
5251 if (!nsCRT::strcmp(aTopic, NS_IOSERVICE_OFFLINE_STATUS_TOPIC)) {
5252 if (!IsFrozen()) {
5253 // Fires an offline status event if the offline status has changed
5254 FireOfflineStatusEventIfChanged();
5256 return NS_OK;
5259 if (!nsCRT::strcmp(aTopic, MEMORY_PRESSURE_OBSERVER_TOPIC)) {
5260 if (mPerformance) {
5261 mPerformance->MemoryPressure();
5263 RemoveReportRecords();
5264 return NS_OK;
5267 if (!nsCRT::strcmp(aTopic, PERMISSION_CHANGED_TOPIC)) {
5268 nsCOMPtr<nsIPermission> perm(do_QueryInterface(aSubject));
5269 if (!perm) {
5270 // A null permission indicates that the entire permission list
5271 // was cleared.
5272 MOZ_ASSERT(!nsCRT::strcmp(aData, u"cleared"));
5273 UpdatePermissions();
5274 return NS_OK;
5277 nsAutoCString type;
5278 perm->GetType(type);
5279 if (type == "autoplay-media"_ns) {
5280 UpdateAutoplayPermission();
5281 } else if (type == "shortcuts"_ns) {
5282 UpdateShortcutsPermission();
5283 } else if (type == "popup"_ns) {
5284 UpdatePopupPermission();
5287 if (!mDoc) {
5288 return NS_OK;
5291 RefPtr<PermissionDelegateHandler> permDelegateHandler =
5292 mDoc->GetPermissionDelegateHandler();
5294 if (permDelegateHandler) {
5295 permDelegateHandler->UpdateDelegatedPermission(type);
5298 return NS_OK;
5301 if (!nsCRT::strcmp(aTopic, "screen-information-changed")) {
5302 if (mScreen) {
5303 if (RefPtr<ScreenOrientation> orientation =
5304 mScreen->GetOrientationIfExists()) {
5305 orientation->MaybeChanged();
5308 if (mHasOrientationChangeListeners) {
5309 int32_t oldAngle = mOrientationAngle;
5310 mOrientationAngle = Orientation(CallerType::System);
5311 if (mOrientationAngle != oldAngle && IsCurrentInnerWindow()) {
5312 nsCOMPtr<nsPIDOMWindowOuter> outer = GetOuterWindow();
5313 outer->DispatchCustomEvent(u"orientationchange"_ns);
5316 return NS_OK;
5319 if (!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
5320 MOZ_ASSERT(!NS_strcmp(aData, u"intl.accept_languages"));
5322 // The user preferred languages have changed, we need to fire an event on
5323 // Window object and invalidate the cache for navigator.languages. It is
5324 // done for every change which can be a waste of cycles but those should be
5325 // fairly rare.
5326 // We MUST invalidate navigator.languages before sending the event in the
5327 // very likely situation where an event handler will try to read its value.
5329 if (mNavigator) {
5330 Navigator_Binding::ClearCachedLanguageValue(mNavigator);
5331 Navigator_Binding::ClearCachedLanguagesValue(mNavigator);
5334 // The event has to be dispatched only to the current inner window.
5335 if (!IsCurrentInnerWindow()) {
5336 return NS_OK;
5339 RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
5340 event->InitEvent(u"languagechange"_ns, false, false);
5341 event->SetTrusted(true);
5343 ErrorResult rv;
5344 DispatchEvent(*event, rv);
5345 return rv.StealNSResult();
5348 NS_WARNING("unrecognized topic in nsGlobalWindowInner::Observe");
5349 return NS_ERROR_FAILURE;
5352 void nsGlobalWindowInner::ObserveStorageNotification(
5353 StorageEvent* aEvent, const char16_t* aStorageType, bool aPrivateBrowsing) {
5354 MOZ_ASSERT(aEvent);
5356 // The private browsing check must be done here again because this window
5357 // could have changed its state before the notification check and now. This
5358 // happens in case this window did have a docShell at that time.
5359 if (aPrivateBrowsing != IsPrivateBrowsing()) {
5360 return;
5363 // LocalStorage can only exist on an inner window, and we don't want to
5364 // generate events on frozen or otherwise-navigated-away from windows.
5365 // (Actually, this code used to try and buffer events for frozen windows,
5366 // but it never worked, so we've removed it. See bug 1285898.)
5367 if (!IsCurrentInnerWindow() || IsFrozen()) {
5368 return;
5371 nsIPrincipal* principal = GetPrincipal();
5372 if (!principal) {
5373 return;
5376 bool fireMozStorageChanged = false;
5377 nsAutoString eventType;
5378 eventType.AssignLiteral("storage");
5380 if (!NS_strcmp(aStorageType, u"sessionStorage")) {
5381 RefPtr<Storage> changingStorage = aEvent->GetStorageArea();
5382 MOZ_ASSERT(changingStorage);
5384 bool check = false;
5386 if (const RefPtr<SessionStorageManager> storageManager =
5387 GetBrowsingContext()->GetSessionStorageManager()) {
5388 nsresult rv = storageManager->CheckStorage(GetEffectiveStoragePrincipal(),
5389 changingStorage, &check);
5390 if (NS_FAILED(rv)) {
5391 return;
5395 if (!check) {
5396 // This storage event is not coming from our storage or is coming
5397 // from a different docshell, i.e. it is a clone, ignore this event.
5398 return;
5401 MOZ_LOG(
5402 gDOMLeakPRLogInner, LogLevel::Debug,
5403 ("nsGlobalWindowInner %p with sessionStorage %p passing event from %p",
5404 this, mSessionStorage.get(), changingStorage.get()));
5406 fireMozStorageChanged = mSessionStorage == changingStorage;
5407 if (fireMozStorageChanged) {
5408 eventType.AssignLiteral("MozSessionStorageChanged");
5412 else {
5413 MOZ_ASSERT(!NS_strcmp(aStorageType, u"localStorage"));
5415 nsIPrincipal* storagePrincipal = GetEffectiveStoragePrincipal();
5416 if (!storagePrincipal) {
5417 return;
5420 MOZ_DIAGNOSTIC_ASSERT(StorageUtils::PrincipalsEqual(aEvent->GetPrincipal(),
5421 storagePrincipal));
5423 fireMozStorageChanged =
5424 mLocalStorage && mLocalStorage == aEvent->GetStorageArea();
5426 if (fireMozStorageChanged) {
5427 eventType.AssignLiteral("MozLocalStorageChanged");
5431 // Clone the storage event included in the observer notification. We want
5432 // to dispatch clones rather than the original event.
5433 IgnoredErrorResult error;
5434 RefPtr<StorageEvent> clonedEvent =
5435 CloneStorageEvent(eventType, aEvent, error);
5436 if (error.Failed() || !clonedEvent) {
5437 return;
5440 clonedEvent->SetTrusted(true);
5442 if (fireMozStorageChanged) {
5443 WidgetEvent* internalEvent = clonedEvent->WidgetEventPtr();
5444 internalEvent->mFlags.mOnlyChromeDispatch = true;
5447 DispatchEvent(*clonedEvent);
5450 already_AddRefed<StorageEvent> nsGlobalWindowInner::CloneStorageEvent(
5451 const nsAString& aType, const RefPtr<StorageEvent>& aEvent,
5452 ErrorResult& aRv) {
5453 StorageEventInit dict;
5455 dict.mBubbles = aEvent->Bubbles();
5456 dict.mCancelable = aEvent->Cancelable();
5457 aEvent->GetKey(dict.mKey);
5458 aEvent->GetOldValue(dict.mOldValue);
5459 aEvent->GetNewValue(dict.mNewValue);
5460 aEvent->GetUrl(dict.mUrl);
5462 RefPtr<Storage> storageArea = aEvent->GetStorageArea();
5464 RefPtr<Storage> storage;
5466 // If null, this is a localStorage event received by IPC.
5467 if (!storageArea) {
5468 storage = GetLocalStorage(aRv);
5469 if (!NextGenLocalStorageEnabled()) {
5470 if (aRv.Failed() || !storage) {
5471 return nullptr;
5474 if (storage->Type() == Storage::eLocalStorage) {
5475 RefPtr<LocalStorage> localStorage =
5476 static_cast<LocalStorage*>(storage.get());
5478 // We must apply the current change to the 'local' localStorage.
5479 localStorage->ApplyEvent(aEvent);
5482 } else if (storageArea->Type() == Storage::eSessionStorage) {
5483 storage = GetSessionStorage(aRv);
5484 } else {
5485 MOZ_ASSERT(storageArea->Type() == Storage::eLocalStorage);
5486 storage = GetLocalStorage(aRv);
5489 if (aRv.Failed() || !storage) {
5490 return nullptr;
5493 if (storage->Type() == Storage::ePartitionedLocalStorage) {
5494 // This error message is not exposed.
5495 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
5496 return nullptr;
5499 MOZ_ASSERT(storage);
5500 MOZ_ASSERT_IF(storageArea, storage->IsForkOf(storageArea));
5502 dict.mStorageArea = storage;
5504 RefPtr<StorageEvent> event = StorageEvent::Constructor(this, aType, dict);
5505 return event.forget();
5508 void nsGlobalWindowInner::Suspend(bool aIncludeSubWindows) {
5509 MOZ_ASSERT(NS_IsMainThread());
5511 // We can only safely suspend windows that are the current inner window. If
5512 // its not the current inner, then we are in one of two different cases.
5513 // Either we are in the bfcache or we are doomed window that is going away.
5514 // When a window becomes inactive we purposely avoid placing already suspended
5515 // windows into the bfcache. It only expects windows suspended due to the
5516 // Freeze() method which occurs while the window is still the current inner.
5517 // So we must not call Suspend() on bfcache windows at this point or this
5518 // invariant will be broken. If the window is doomed there is no point in
5519 // suspending it since it will soon be gone.
5520 if (!IsCurrentInnerWindow()) {
5521 return;
5524 // All in-process descendants are also suspended. This ensure mSuspendDepth
5525 // is set properly and the timers are properly canceled for each in-process
5526 // descendant.
5527 if (aIncludeSubWindows) {
5528 CallOnInProcessDescendants(&nsGlobalWindowInner::Suspend, false);
5531 mSuspendDepth += 1;
5532 if (mSuspendDepth != 1) {
5533 return;
5536 if (mWindowGlobalChild) {
5537 mWindowGlobalChild->BlockBFCacheFor(BFCacheStatus::SUSPENDED);
5540 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
5541 if (ac) {
5542 for (uint32_t i = 0; i < mEnabledSensors.Length(); i++)
5543 ac->RemoveWindowListener(mEnabledSensors[i], this);
5545 DisableGamepadUpdates();
5546 DisableVRUpdates();
5548 SuspendWorkersForWindow(*this);
5550 for (RefPtr<mozilla::dom::SharedWorker> pinnedWorker :
5551 mSharedWorkers.ForwardRange()) {
5552 pinnedWorker->Suspend();
5555 SuspendIdleRequests();
5557 mTimeoutManager->Suspend();
5559 // Suspend all of the AudioContexts for this window
5560 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
5561 mAudioContexts[i]->SuspendFromChrome();
5565 void nsGlobalWindowInner::Resume(bool aIncludeSubWindows) {
5566 MOZ_ASSERT(NS_IsMainThread());
5568 // We can only safely resume a window if its the current inner window. If
5569 // its not the current inner, then we are in one of two different cases.
5570 // Either we are in the bfcache or we are doomed window that is going away.
5571 // If a window is suspended when it becomes inactive we purposely do not
5572 // put it in the bfcache, so Resume should never be needed in that case.
5573 // If the window is doomed then there is no point in resuming it.
5574 if (!IsCurrentInnerWindow()) {
5575 return;
5578 // Resume all in-process descendants. This restores timers recursively
5579 // canceled in Suspend() and ensures all in-process descendants have the
5580 // correct mSuspendDepth.
5581 if (aIncludeSubWindows) {
5582 CallOnInProcessDescendants(&nsGlobalWindowInner::Resume, false);
5585 if (mSuspendDepth == 0) {
5586 // Ignore if the window is not suspended.
5587 return;
5590 mSuspendDepth -= 1;
5592 if (mSuspendDepth != 0) {
5593 return;
5596 // We should not be able to resume a frozen window. It must be Thaw()'d
5597 // first.
5598 MOZ_ASSERT(mFreezeDepth == 0);
5600 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
5601 if (ac) {
5602 for (uint32_t i = 0; i < mEnabledSensors.Length(); i++)
5603 ac->AddWindowListener(mEnabledSensors[i], this);
5605 EnableGamepadUpdates();
5606 EnableVRUpdates();
5608 // Resume all of the AudioContexts for this window
5609 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
5610 mAudioContexts[i]->ResumeFromChrome();
5613 if (RefPtr<MediaDevices> devices = GetExtantMediaDevices()) {
5614 devices->WindowResumed();
5617 mTimeoutManager->Resume();
5619 ResumeIdleRequests();
5621 // Resume all of the workers for this window. We must do this
5622 // after timeouts since workers may have queued events that can trigger
5623 // a setTimeout().
5624 ResumeWorkersForWindow(*this);
5626 for (RefPtr<mozilla::dom::SharedWorker> pinnedWorker :
5627 mSharedWorkers.ForwardRange()) {
5628 pinnedWorker->Resume();
5631 if (mWindowGlobalChild) {
5632 mWindowGlobalChild->UnblockBFCacheFor(BFCacheStatus::SUSPENDED);
5636 bool nsGlobalWindowInner::IsSuspended() const {
5637 MOZ_ASSERT(NS_IsMainThread());
5638 return mSuspendDepth != 0;
5641 void nsGlobalWindowInner::Freeze(bool aIncludeSubWindows) {
5642 MOZ_ASSERT(NS_IsMainThread());
5643 Suspend(aIncludeSubWindows);
5644 FreezeInternal(aIncludeSubWindows);
5647 void nsGlobalWindowInner::FreezeInternal(bool aIncludeSubWindows) {
5648 MOZ_ASSERT(NS_IsMainThread());
5649 MOZ_DIAGNOSTIC_ASSERT(IsCurrentInnerWindow());
5650 MOZ_DIAGNOSTIC_ASSERT(IsSuspended());
5652 HintIsLoading(false);
5654 if (aIncludeSubWindows) {
5655 CallOnInProcessChildren(&nsGlobalWindowInner::FreezeInternal,
5656 aIncludeSubWindows);
5659 mFreezeDepth += 1;
5660 MOZ_ASSERT(mSuspendDepth >= mFreezeDepth);
5661 if (mFreezeDepth != 1) {
5662 return;
5665 FreezeWorkersForWindow(*this);
5667 for (RefPtr<mozilla::dom::SharedWorker> pinnedWorker :
5668 mSharedWorkers.ForwardRange()) {
5669 pinnedWorker->Freeze();
5672 mTimeoutManager->Freeze();
5673 if (mClientSource) {
5674 mClientSource->Freeze();
5677 NotifyDOMWindowFrozen(this);
5680 void nsGlobalWindowInner::Thaw(bool aIncludeSubWindows) {
5681 MOZ_ASSERT(NS_IsMainThread());
5682 ThawInternal(aIncludeSubWindows);
5683 Resume(aIncludeSubWindows);
5686 void nsGlobalWindowInner::ThawInternal(bool aIncludeSubWindows) {
5687 MOZ_ASSERT(NS_IsMainThread());
5688 MOZ_DIAGNOSTIC_ASSERT(IsCurrentInnerWindow());
5689 MOZ_DIAGNOSTIC_ASSERT(IsSuspended());
5691 if (aIncludeSubWindows) {
5692 CallOnInProcessChildren(&nsGlobalWindowInner::ThawInternal,
5693 aIncludeSubWindows);
5696 MOZ_ASSERT(mFreezeDepth != 0);
5697 mFreezeDepth -= 1;
5698 MOZ_ASSERT(mSuspendDepth >= mFreezeDepth);
5699 if (mFreezeDepth != 0) {
5700 return;
5703 if (mClientSource) {
5704 mClientSource->Thaw();
5706 mTimeoutManager->Thaw();
5708 ThawWorkersForWindow(*this);
5710 for (RefPtr<mozilla::dom::SharedWorker> pinnedWorker :
5711 mSharedWorkers.ForwardRange()) {
5712 pinnedWorker->Thaw();
5715 NotifyDOMWindowThawed(this);
5718 bool nsGlobalWindowInner::IsFrozen() const {
5719 MOZ_ASSERT(NS_IsMainThread());
5720 bool frozen = mFreezeDepth != 0;
5721 MOZ_ASSERT_IF(frozen, IsSuspended());
5722 return frozen;
5725 void nsGlobalWindowInner::SyncStateFromParentWindow() {
5726 // This method should only be called on an inner window that has been
5727 // assigned to an outer window already.
5728 MOZ_ASSERT(IsCurrentInnerWindow());
5729 nsPIDOMWindowOuter* outer = GetOuterWindow();
5730 MOZ_ASSERT(outer);
5732 // Attempt to find our parent windows.
5733 nsCOMPtr<Element> frame = outer->GetFrameElementInternal();
5734 nsPIDOMWindowOuter* parentOuter =
5735 frame ? frame->OwnerDoc()->GetWindow() : nullptr;
5736 nsGlobalWindowInner* parentInner =
5737 parentOuter
5738 ? nsGlobalWindowInner::Cast(parentOuter->GetCurrentInnerWindow())
5739 : nullptr;
5741 // If our outer is in a modal state, but our parent is not in a modal
5742 // state, then we must apply the suspend directly. If our parent is
5743 // in a modal state then we should get the suspend automatically
5744 // via the parentSuspendDepth application below.
5745 if ((!parentInner || !parentInner->IsInModalState()) && IsInModalState()) {
5746 Suspend();
5749 uint32_t parentFreezeDepth = parentInner ? parentInner->mFreezeDepth : 0;
5750 uint32_t parentSuspendDepth = parentInner ? parentInner->mSuspendDepth : 0;
5752 // Since every Freeze() calls Suspend(), the suspend count must
5753 // be equal or greater to the freeze count.
5754 MOZ_ASSERT(parentFreezeDepth <= parentSuspendDepth);
5756 // First apply the Freeze() calls.
5757 for (uint32_t i = 0; i < parentFreezeDepth; ++i) {
5758 Freeze();
5761 // Now apply only the number of Suspend() calls to reach the target
5762 // suspend count after applying the Freeze() calls.
5763 for (uint32_t i = 0; i < (parentSuspendDepth - parentFreezeDepth); ++i) {
5764 Suspend();
5768 void nsGlobalWindowInner::UpdateBackgroundState() {
5769 if (RefPtr<MediaDevices> devices = GetExtantMediaDevices()) {
5770 devices->BackgroundStateChanged();
5772 mTimeoutManager->UpdateBackgroundState();
5775 template <typename Method, typename... Args>
5776 CallState nsGlobalWindowInner::CallOnInProcessDescendantsInternal(
5777 BrowsingContext* aBrowsingContext, bool aChildOnly, Method aMethod,
5778 Args&&... aArgs) {
5779 MOZ_ASSERT(NS_IsMainThread());
5780 MOZ_ASSERT(aBrowsingContext);
5782 CallState state = CallState::Continue;
5783 for (const RefPtr<BrowsingContext>& bc : aBrowsingContext->Children()) {
5784 if (nsCOMPtr<nsPIDOMWindowOuter> pWin = bc->GetDOMWindow()) {
5785 auto* win = nsGlobalWindowOuter::Cast(pWin);
5786 if (nsGlobalWindowInner* inner =
5787 nsGlobalWindowInner::Cast(win->GetCurrentInnerWindow())) {
5788 // Call the descendant method using our helper CallDescendant() template
5789 // method. This allows us to handle both void returning methods and
5790 // methods that return CallState explicitly. For void returning methods
5791 // we assume CallState::Continue.
5792 using returnType = decltype((inner->*aMethod)(aArgs...));
5793 state = CallDescendant<returnType>(inner, aMethod, aArgs...);
5795 if (state == CallState::Stop) {
5796 return state;
5801 if (!aChildOnly) {
5802 state = CallOnInProcessDescendantsInternal(bc.get(), aChildOnly, aMethod,
5803 aArgs...);
5804 if (state == CallState::Stop) {
5805 return state;
5810 return state;
5813 Maybe<ClientInfo> nsGlobalWindowInner::GetClientInfo() const {
5814 MOZ_ASSERT(NS_IsMainThread());
5815 if (mDoc && mDoc->IsStaticDocument()) {
5816 if (Maybe<ClientInfo> info = mDoc->GetOriginalDocument()->GetClientInfo()) {
5817 return info;
5821 Maybe<ClientInfo> clientInfo;
5822 if (mClientSource) {
5823 clientInfo.emplace(mClientSource->Info());
5825 return clientInfo;
5828 Maybe<ClientState> nsGlobalWindowInner::GetClientState() const {
5829 MOZ_ASSERT(NS_IsMainThread());
5830 if (mDoc && mDoc->IsStaticDocument()) {
5831 if (Maybe<ClientState> state =
5832 mDoc->GetOriginalDocument()->GetClientState()) {
5833 return state;
5837 Maybe<ClientState> clientState;
5838 if (mClientSource) {
5839 Result<ClientState, ErrorResult> res = mClientSource->SnapshotState();
5840 if (res.isOk()) {
5841 clientState.emplace(res.unwrap());
5842 } else {
5843 res.unwrapErr().SuppressException();
5846 return clientState;
5849 Maybe<ServiceWorkerDescriptor> nsGlobalWindowInner::GetController() const {
5850 MOZ_ASSERT(NS_IsMainThread());
5851 if (mDoc && mDoc->IsStaticDocument()) {
5852 if (Maybe<ServiceWorkerDescriptor> controller =
5853 mDoc->GetOriginalDocument()->GetController()) {
5854 return controller;
5858 Maybe<ServiceWorkerDescriptor> controller;
5859 if (mClientSource) {
5860 controller = mClientSource->GetController();
5862 return controller;
5865 void nsGlobalWindowInner::SetCsp(nsIContentSecurityPolicy* aCsp) {
5866 if (!mClientSource) {
5867 return;
5869 mClientSource->SetCsp(aCsp);
5870 // Also cache the CSP within the document
5871 mDoc->SetCsp(aCsp);
5873 if (mWindowGlobalChild) {
5874 mWindowGlobalChild->SendSetClientInfo(mClientSource->Info().ToIPC());
5878 void nsGlobalWindowInner::SetPreloadCsp(nsIContentSecurityPolicy* aPreloadCsp) {
5879 if (!mClientSource) {
5880 return;
5882 mClientSource->SetPreloadCsp(aPreloadCsp);
5883 // Also cache the preload CSP within the document
5884 mDoc->SetPreloadCsp(aPreloadCsp);
5886 if (mWindowGlobalChild) {
5887 mWindowGlobalChild->SendSetClientInfo(mClientSource->Info().ToIPC());
5891 nsIContentSecurityPolicy* nsGlobalWindowInner::GetCsp() {
5892 if (mDoc) {
5893 return mDoc->GetCsp();
5896 // If the window is partially torn down and has its document nulled out,
5897 // we query the CSP we snapshot in FreeInnerObjects.
5898 if (mDocumentCsp) {
5899 return mDocumentCsp;
5901 return nullptr;
5904 RefPtr<ServiceWorker> nsGlobalWindowInner::GetOrCreateServiceWorker(
5905 const ServiceWorkerDescriptor& aDescriptor) {
5906 MOZ_ASSERT(NS_IsMainThread());
5907 RefPtr<ServiceWorker> ref;
5908 ForEachEventTargetObject([&](DOMEventTargetHelper* aTarget, bool* aDoneOut) {
5909 RefPtr<ServiceWorker> sw = do_QueryObject(aTarget);
5910 if (!sw || !sw->Descriptor().Matches(aDescriptor)) {
5911 return;
5914 ref = std::move(sw);
5915 *aDoneOut = true;
5918 if (!ref) {
5919 ref = ServiceWorker::Create(this, aDescriptor);
5922 return ref;
5925 RefPtr<mozilla::dom::ServiceWorkerRegistration>
5926 nsGlobalWindowInner::GetServiceWorkerRegistration(
5927 const mozilla::dom::ServiceWorkerRegistrationDescriptor& aDescriptor)
5928 const {
5929 MOZ_ASSERT(NS_IsMainThread());
5930 RefPtr<ServiceWorkerRegistration> ref;
5931 ForEachEventTargetObject([&](DOMEventTargetHelper* aTarget, bool* aDoneOut) {
5932 RefPtr<ServiceWorkerRegistration> swr = do_QueryObject(aTarget);
5933 if (!swr || !swr->MatchesDescriptor(aDescriptor)) {
5934 return;
5937 ref = std::move(swr);
5938 *aDoneOut = true;
5940 return ref;
5943 RefPtr<ServiceWorkerRegistration>
5944 nsGlobalWindowInner::GetOrCreateServiceWorkerRegistration(
5945 const ServiceWorkerRegistrationDescriptor& aDescriptor) {
5946 MOZ_ASSERT(NS_IsMainThread());
5947 RefPtr<ServiceWorkerRegistration> ref =
5948 GetServiceWorkerRegistration(aDescriptor);
5949 if (!ref) {
5950 ref = ServiceWorkerRegistration::CreateForMainThread(this, aDescriptor);
5952 return ref;
5955 StorageAccess nsGlobalWindowInner::GetStorageAccess() {
5956 return StorageAllowedForWindow(this);
5959 nsresult nsGlobalWindowInner::FireDelayedDOMEvents(bool aIncludeSubWindows) {
5960 // Fires an offline status event if the offline status has changed
5961 FireOfflineStatusEventIfChanged();
5963 if (!aIncludeSubWindows) {
5964 return NS_OK;
5967 nsCOMPtr<nsIDocShell> docShell = GetDocShell();
5968 if (docShell) {
5969 int32_t childCount = 0;
5970 docShell->GetInProcessChildCount(&childCount);
5972 // Take a copy of the current children so that modifications to
5973 // the child list don't affect to the iteration.
5974 AutoTArray<nsCOMPtr<nsIDocShellTreeItem>, 8> children;
5975 for (int32_t i = 0; i < childCount; ++i) {
5976 nsCOMPtr<nsIDocShellTreeItem> childShell;
5977 docShell->GetInProcessChildAt(i, getter_AddRefs(childShell));
5978 if (childShell) {
5979 children.AppendElement(childShell);
5983 for (nsCOMPtr<nsIDocShellTreeItem> childShell : children) {
5984 if (nsCOMPtr<nsPIDOMWindowOuter> pWin = childShell->GetWindow()) {
5985 auto* win = nsGlobalWindowOuter::Cast(pWin);
5986 win->FireDelayedDOMEvents(true);
5991 return NS_OK;
5994 //*****************************************************************************
5995 // nsGlobalWindowInner: Window Control Functions
5996 //*****************************************************************************
5998 nsPIDOMWindowOuter* nsGlobalWindowInner::GetInProcessParentInternal() {
5999 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
6000 if (!outer) {
6001 // No outer window available!
6002 return nullptr;
6004 return outer->GetInProcessParentInternal();
6007 nsIPrincipal* nsGlobalWindowInner::GetTopLevelAntiTrackingPrincipal() {
6008 nsPIDOMWindowOuter* outerWindow = GetOuterWindowInternal();
6009 if (!outerWindow) {
6010 return nullptr;
6013 nsPIDOMWindowOuter* topLevelOuterWindow =
6014 GetBrowsingContext()->Top()->GetDOMWindow();
6015 if (!topLevelOuterWindow) {
6016 return nullptr;
6019 bool stopAtOurLevel =
6020 mDoc && mDoc->CookieJarSettings()->GetCookieBehavior() ==
6021 nsICookieService::BEHAVIOR_REJECT_TRACKER;
6023 if (stopAtOurLevel && topLevelOuterWindow == outerWindow) {
6024 return nullptr;
6027 nsPIDOMWindowInner* topLevelInnerWindow =
6028 topLevelOuterWindow->GetCurrentInnerWindow();
6029 if (NS_WARN_IF(!topLevelInnerWindow)) {
6030 return nullptr;
6033 nsIPrincipal* topLevelPrincipal =
6034 nsGlobalWindowInner::Cast(topLevelInnerWindow)->GetPrincipal();
6035 if (NS_WARN_IF(!topLevelPrincipal)) {
6036 return nullptr;
6039 return topLevelPrincipal;
6042 nsIPrincipal* nsGlobalWindowInner::GetClientPrincipal() {
6043 return mClientSource ? mClientSource->GetPrincipal() : nullptr;
6046 //*****************************************************************************
6047 // nsGlobalWindowInner: Timeout Functions
6048 //*****************************************************************************
6050 class WindowScriptTimeoutHandler final : public ScriptTimeoutHandler {
6051 public:
6052 NS_DECL_ISUPPORTS_INHERITED
6053 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(WindowScriptTimeoutHandler,
6054 ScriptTimeoutHandler)
6056 WindowScriptTimeoutHandler(JSContext* aCx, nsIGlobalObject* aGlobal,
6057 const nsAString& aExpression)
6058 : ScriptTimeoutHandler(aCx, aGlobal, aExpression),
6059 mInitiatingScript(ScriptLoader::GetActiveScript(aCx)) {}
6061 MOZ_CAN_RUN_SCRIPT virtual bool Call(const char* aExecutionReason) override;
6063 private:
6064 virtual ~WindowScriptTimeoutHandler() = default;
6066 // Initiating script for use when evaluating mExpr on the main thread.
6067 RefPtr<JS::loader::LoadedScript> mInitiatingScript;
6070 NS_IMPL_CYCLE_COLLECTION_INHERITED(WindowScriptTimeoutHandler,
6071 ScriptTimeoutHandler, mInitiatingScript)
6073 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WindowScriptTimeoutHandler)
6074 NS_INTERFACE_MAP_END_INHERITING(ScriptTimeoutHandler)
6076 NS_IMPL_ADDREF_INHERITED(WindowScriptTimeoutHandler, ScriptTimeoutHandler)
6077 NS_IMPL_RELEASE_INHERITED(WindowScriptTimeoutHandler, ScriptTimeoutHandler)
6079 bool WindowScriptTimeoutHandler::Call(const char* aExecutionReason) {
6080 // New script entry point required, due to the "Create a script" sub-step
6081 // of
6082 // http://www.whatwg.org/specs/web-apps/current-work/#timer-initialisation-steps
6083 nsAutoMicroTask mt;
6084 AutoEntryScript aes(mGlobal, aExecutionReason, true);
6085 JS::CompileOptions options(aes.cx());
6086 options.setFileAndLine(mFileName.get(), mLineNo);
6087 options.setNoScriptRval(true);
6088 options.setIntroductionType("domTimer");
6089 JS::Rooted<JSObject*> global(aes.cx(), mGlobal->GetGlobalJSObject());
6091 JSExecutionContext exec(aes.cx(), global, options);
6092 nsresult rv = exec.Compile(mExpr);
6094 JS::Rooted<JSScript*> script(aes.cx(), exec.MaybeGetScript());
6095 if (script) {
6096 if (mInitiatingScript) {
6097 mInitiatingScript->AssociateWithScript(script);
6100 rv = exec.ExecScript();
6103 if (rv == NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW_UNCATCHABLE) {
6104 return false;
6108 return true;
6111 nsGlobalWindowInner* nsGlobalWindowInner::InnerForSetTimeoutOrInterval(
6112 ErrorResult& aError) {
6113 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
6114 nsPIDOMWindowInner* currentInner =
6115 outer ? outer->GetCurrentInnerWindow() : this;
6117 // If forwardTo is not the window with an active document then we want the
6118 // call to setTimeout/Interval to be a noop, so return null but don't set an
6119 // error.
6120 return HasActiveDocument() ? nsGlobalWindowInner::Cast(currentInner)
6121 : nullptr;
6124 int32_t nsGlobalWindowInner::SetTimeout(JSContext* aCx, Function& aFunction,
6125 int32_t aTimeout,
6126 const Sequence<JS::Value>& aArguments,
6127 ErrorResult& aError) {
6128 return SetTimeoutOrInterval(aCx, aFunction, aTimeout, aArguments, false,
6129 aError);
6132 int32_t nsGlobalWindowInner::SetTimeout(JSContext* aCx,
6133 const nsAString& aHandler,
6134 int32_t aTimeout,
6135 const Sequence<JS::Value>& /* unused */,
6136 ErrorResult& aError) {
6137 return SetTimeoutOrInterval(aCx, aHandler, aTimeout, false, aError);
6140 int32_t nsGlobalWindowInner::SetInterval(JSContext* aCx, Function& aFunction,
6141 const int32_t aTimeout,
6142 const Sequence<JS::Value>& aArguments,
6143 ErrorResult& aError) {
6144 return SetTimeoutOrInterval(aCx, aFunction, aTimeout, aArguments, true,
6145 aError);
6148 int32_t nsGlobalWindowInner::SetInterval(
6149 JSContext* aCx, const nsAString& aHandler, const int32_t aTimeout,
6150 const Sequence<JS::Value>& /* unused */, ErrorResult& aError) {
6151 return SetTimeoutOrInterval(aCx, aHandler, aTimeout, true, aError);
6154 int32_t nsGlobalWindowInner::SetTimeoutOrInterval(
6155 JSContext* aCx, Function& aFunction, int32_t aTimeout,
6156 const Sequence<JS::Value>& aArguments, bool aIsInterval,
6157 ErrorResult& aError) {
6158 nsGlobalWindowInner* inner = InnerForSetTimeoutOrInterval(aError);
6159 if (!inner) {
6160 return -1;
6163 if (inner != this) {
6164 RefPtr<nsGlobalWindowInner> innerRef(inner);
6165 return innerRef->SetTimeoutOrInterval(aCx, aFunction, aTimeout, aArguments,
6166 aIsInterval, aError);
6169 DebuggerNotificationDispatch(
6170 this, aIsInterval ? DebuggerNotificationType::SetInterval
6171 : DebuggerNotificationType::SetTimeout);
6173 if (!GetContextInternal() || !HasJSGlobal()) {
6174 // This window was already closed, or never properly initialized,
6175 // don't let a timer be scheduled on such a window.
6176 aError.Throw(NS_ERROR_NOT_INITIALIZED);
6177 return 0;
6180 nsTArray<JS::Heap<JS::Value>> args;
6181 if (!args.AppendElements(aArguments, fallible)) {
6182 aError.Throw(NS_ERROR_OUT_OF_MEMORY);
6183 return 0;
6186 RefPtr<TimeoutHandler> handler =
6187 new CallbackTimeoutHandler(aCx, this, &aFunction, std::move(args));
6189 int32_t result;
6190 aError =
6191 mTimeoutManager->SetTimeout(handler, aTimeout, aIsInterval,
6192 Timeout::Reason::eTimeoutOrInterval, &result);
6193 return result;
6196 int32_t nsGlobalWindowInner::SetTimeoutOrInterval(JSContext* aCx,
6197 const nsAString& aHandler,
6198 int32_t aTimeout,
6199 bool aIsInterval,
6200 ErrorResult& aError) {
6201 nsGlobalWindowInner* inner = InnerForSetTimeoutOrInterval(aError);
6202 if (!inner) {
6203 return -1;
6206 if (inner != this) {
6207 RefPtr<nsGlobalWindowInner> innerRef(inner);
6208 return innerRef->SetTimeoutOrInterval(aCx, aHandler, aTimeout, aIsInterval,
6209 aError);
6212 DebuggerNotificationDispatch(
6213 this, aIsInterval ? DebuggerNotificationType::SetInterval
6214 : DebuggerNotificationType::SetTimeout);
6216 if (!GetContextInternal() || !HasJSGlobal()) {
6217 // This window was already closed, or never properly initialized,
6218 // don't let a timer be scheduled on such a window.
6219 aError.Throw(NS_ERROR_NOT_INITIALIZED);
6220 return 0;
6223 bool allowEval = false;
6224 aError = CSPEvalChecker::CheckForWindow(aCx, this, aHandler, &allowEval);
6225 if (NS_WARN_IF(aError.Failed()) || !allowEval) {
6226 return 0;
6229 RefPtr<TimeoutHandler> handler =
6230 new WindowScriptTimeoutHandler(aCx, this, aHandler);
6232 int32_t result;
6233 aError =
6234 mTimeoutManager->SetTimeout(handler, aTimeout, aIsInterval,
6235 Timeout::Reason::eTimeoutOrInterval, &result);
6236 return result;
6239 static const char* GetTimeoutReasonString(Timeout* aTimeout) {
6240 switch (aTimeout->mReason) {
6241 case Timeout::Reason::eTimeoutOrInterval:
6242 if (aTimeout->mIsInterval) {
6243 return "setInterval handler";
6245 return "setTimeout handler";
6246 case Timeout::Reason::eIdleCallbackTimeout:
6247 return "setIdleCallback handler (timed out)";
6248 case Timeout::Reason::eAbortSignalTimeout:
6249 return "AbortSignal timeout";
6250 case Timeout::Reason::eDelayedWebTaskTimeout:
6251 return "delayedWebTaskCallback handler (timed out)";
6252 default:
6253 MOZ_CRASH("Unexpected enum value");
6254 return "";
6256 MOZ_CRASH("Unexpected enum value");
6257 return "";
6260 bool nsGlobalWindowInner::RunTimeoutHandler(Timeout* aTimeout,
6261 nsIScriptContext* aScx) {
6262 // Hold on to the timeout in case mExpr or mFunObj releases its
6263 // doc.
6264 // XXXbz Our caller guarantees it'll hold on to the timeout (because
6265 // we're MOZ_CAN_RUN_SCRIPT), so we can probably stop doing that...
6266 RefPtr<Timeout> timeout = aTimeout;
6267 Timeout* last_running_timeout = mTimeoutManager->BeginRunningTimeout(timeout);
6268 timeout->mRunning = true;
6270 // Push this timeout's popup control state, which should only be
6271 // enabled the first time a timeout fires that was created while
6272 // popups were enabled and with a delay less than
6273 // "dom.disable_open_click_delay".
6274 AutoPopupStatePusher popupStatePusher(timeout->mPopupState);
6276 // Clear the timeout's popup state, if any, to prevent interval
6277 // timeouts from repeatedly opening poups.
6278 timeout->mPopupState = PopupBlocker::openAbused;
6280 uint32_t nestingLevel = TimeoutManager::GetNestingLevel();
6281 TimeoutManager::SetNestingLevel(timeout->mNestingLevel);
6283 const char* reason = GetTimeoutReasonString(timeout);
6285 nsCString str;
6286 if (profiler_thread_is_being_profiled_for_markers()) {
6287 TimeDuration originalInterval = timeout->When() - timeout->SubmitTime();
6288 str.Append(reason);
6289 str.Append(" with interval ");
6290 str.AppendInt(int(originalInterval.ToMilliseconds()));
6291 str.Append("ms: ");
6292 nsCString handlerDescription;
6293 timeout->mScriptHandler->GetDescription(handlerDescription);
6294 str.Append(handlerDescription);
6296 AUTO_PROFILER_MARKER_TEXT("setTimeout callback", DOM,
6297 MarkerOptions(MarkerStack::TakeBacktrace(
6298 timeout->TakeProfilerBacktrace()),
6299 MarkerInnerWindowId(mWindowID)),
6300 str);
6302 bool abortIntervalHandler;
6304 RefPtr<TimeoutHandler> handler(timeout->mScriptHandler);
6306 CallbackDebuggerNotificationGuard guard(
6307 this, timeout->mIsInterval
6308 ? DebuggerNotificationType::SetIntervalCallback
6309 : DebuggerNotificationType::SetTimeoutCallback);
6310 abortIntervalHandler = !handler->Call(reason);
6313 // If we received an uncatchable exception, do not schedule the timeout again.
6314 // This allows the slow script dialog to break easy DoS attacks like
6315 // setInterval(function() { while(1); }, 100);
6316 if (abortIntervalHandler) {
6317 // If it wasn't an interval timer to begin with, this does nothing. If it
6318 // was, we'll treat it as a timeout that we just ran and discard it when
6319 // we return.
6320 timeout->mIsInterval = false;
6323 // We ignore any failures from calling EvaluateString() on the context or
6324 // Call() on a Function here since we're in a loop
6325 // where we're likely to be running timeouts whose OS timers
6326 // didn't fire in time and we don't want to not fire those timers
6327 // now just because execution of one timer failed. We can't
6328 // propagate the error to anyone who cares about it from this
6329 // point anyway, and the script context should have already reported
6330 // the script error in the usual way - so we just drop it.
6332 TimeoutManager::SetNestingLevel(nestingLevel);
6334 mTimeoutManager->EndRunningTimeout(last_running_timeout);
6335 timeout->mRunning = false;
6337 return timeout->mCleared;
6340 //*****************************************************************************
6341 // nsGlobalWindowInner: Helper Functions
6342 //*****************************************************************************
6344 already_AddRefed<nsIDocShellTreeOwner> nsGlobalWindowInner::GetTreeOwner() {
6345 FORWARD_TO_OUTER(GetTreeOwner, (), nullptr);
6348 already_AddRefed<nsIWebBrowserChrome>
6349 nsGlobalWindowInner::GetWebBrowserChrome() {
6350 nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
6352 nsCOMPtr<nsIWebBrowserChrome> browserChrome = do_GetInterface(treeOwner);
6353 return browserChrome.forget();
6356 nsIScrollableFrame* nsGlobalWindowInner::GetScrollFrame() {
6357 FORWARD_TO_OUTER(GetScrollFrame, (), nullptr);
6360 bool nsGlobalWindowInner::IsPrivateBrowsing() {
6361 nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(GetDocShell());
6362 return loadContext && loadContext->UsePrivateBrowsing();
6365 void nsGlobalWindowInner::FlushPendingNotifications(FlushType aType) {
6366 if (mDoc) {
6367 mDoc->FlushPendingNotifications(aType);
6371 void nsGlobalWindowInner::EnableDeviceSensor(uint32_t aType) {
6372 bool alreadyEnabled = false;
6373 for (uint32_t i = 0; i < mEnabledSensors.Length(); i++) {
6374 if (mEnabledSensors[i] == aType) {
6375 alreadyEnabled = true;
6376 break;
6380 mEnabledSensors.AppendElement(aType);
6382 if (alreadyEnabled) {
6383 return;
6386 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
6387 if (ac) {
6388 ac->AddWindowListener(aType, this);
6392 void nsGlobalWindowInner::DisableDeviceSensor(uint32_t aType) {
6393 int32_t doomedElement = -1;
6394 int32_t listenerCount = 0;
6395 for (uint32_t i = 0; i < mEnabledSensors.Length(); i++) {
6396 if (mEnabledSensors[i] == aType) {
6397 doomedElement = i;
6398 listenerCount++;
6402 if (doomedElement == -1) {
6403 return;
6406 mEnabledSensors.RemoveElementAt(doomedElement);
6408 if (listenerCount > 1) {
6409 return;
6412 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
6413 if (ac) {
6414 ac->RemoveWindowListener(aType, this);
6418 #if defined(MOZ_WIDGET_ANDROID)
6419 void nsGlobalWindowInner::EnableOrientationChangeListener() {
6420 if (!ShouldResistFingerprinting(RFPTarget::ScreenOrientation)) {
6421 mHasOrientationChangeListeners = true;
6422 mOrientationAngle = Orientation(CallerType::System);
6426 void nsGlobalWindowInner::DisableOrientationChangeListener() {
6427 mHasOrientationChangeListeners = false;
6429 #endif
6431 void nsGlobalWindowInner::SetHasGamepadEventListener(
6432 bool aHasGamepad /* = true*/) {
6433 mHasGamepad = aHasGamepad;
6434 if (aHasGamepad) {
6435 EnableGamepadUpdates();
6439 void nsGlobalWindowInner::NotifyDetectXRRuntimesCompleted() {
6440 if (!mXRRuntimeDetectionInFlight) {
6441 return;
6443 mXRRuntimeDetectionInFlight = false;
6444 if (mXRPermissionRequestInFlight) {
6445 return;
6447 gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
6448 bool supported = vm->RuntimeSupportsVR();
6449 if (!supported) {
6450 // A VR runtime was not installed; we can suppress
6451 // the permission prompt
6452 OnXRPermissionRequestCancel();
6453 return;
6455 // A VR runtime was found. Display a permission prompt before
6456 // allowing it to be accessed.
6457 // Connect to the VRManager in order to receive the runtime
6458 // detection results.
6459 mXRPermissionRequestInFlight = true;
6460 RefPtr<XRPermissionRequest> request =
6461 new XRPermissionRequest(this, WindowID());
6462 Unused << NS_WARN_IF(NS_FAILED(request->Start()));
6465 void nsGlobalWindowInner::RequestXRPermission() {
6466 if (IsDying()) {
6467 // Do not proceed if the window is dying, as that will result
6468 // in leaks of objects that get re-allocated after FreeInnerObjects
6469 // has been called, including mVREventObserver.
6470 return;
6472 if (mXRPermissionGranted) {
6473 // Don't prompt redundantly once permission to
6474 // access XR devices has been granted.
6475 OnXRPermissionRequestAllow();
6476 return;
6478 if (mXRRuntimeDetectionInFlight || mXRPermissionRequestInFlight) {
6479 // Don't allow multiple simultaneous permissions requests;
6480 return;
6482 // Before displaying a permission prompt, detect
6483 // if there is any VR runtime installed.
6484 gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
6485 mXRRuntimeDetectionInFlight = true;
6486 EnableVRUpdates();
6487 vm->DetectRuntimes();
6490 void nsGlobalWindowInner::OnXRPermissionRequestAllow() {
6491 mXRPermissionRequestInFlight = false;
6492 if (IsDying()) {
6493 // The window may have started dying while the permission request
6494 // is in flight.
6495 // Do not proceed if the window is dying, as that will result
6496 // in leaks of objects that get re-allocated after FreeInnerObjects
6497 // has been called, including mNavigator.
6498 return;
6500 mXRPermissionGranted = true;
6502 NotifyHasXRSession();
6504 dom::Navigator* nav = Navigator();
6505 MOZ_ASSERT(nav != nullptr);
6506 nav->OnXRPermissionRequestAllow();
6509 void nsGlobalWindowInner::OnXRPermissionRequestCancel() {
6510 mXRPermissionRequestInFlight = false;
6511 if (IsDying()) {
6512 // The window may have started dying while the permission request
6513 // is in flight.
6514 // Do not proceed if the window is dying, as that will result
6515 // in leaks of objects that get re-allocated after FreeInnerObjects
6516 // has been called, including mNavigator.
6517 return;
6519 dom::Navigator* nav = Navigator();
6520 MOZ_ASSERT(nav != nullptr);
6521 nav->OnXRPermissionRequestCancel();
6524 void nsGlobalWindowInner::EventListenerAdded(nsAtom* aType) {
6525 if (aType == nsGkAtoms::onvrdisplayactivate ||
6526 aType == nsGkAtoms::onvrdisplayconnect ||
6527 aType == nsGkAtoms::onvrdisplaydeactivate ||
6528 aType == nsGkAtoms::onvrdisplaydisconnect ||
6529 aType == nsGkAtoms::onvrdisplaypresentchange) {
6530 RequestXRPermission();
6533 if (aType == nsGkAtoms::onvrdisplayactivate) {
6534 mHasVRDisplayActivateEvents = true;
6537 if (aType == nsGkAtoms::onunload && mWindowGlobalChild) {
6538 if (++mUnloadOrBeforeUnloadListenerCount == 1) {
6539 mWindowGlobalChild->BlockBFCacheFor(BFCacheStatus::UNLOAD_LISTENER);
6543 if (aType == nsGkAtoms::onbeforeunload && mWindowGlobalChild) {
6544 if (!mozilla::SessionHistoryInParent() ||
6545 !StaticPrefs::
6546 docshell_shistory_bfcache_ship_allow_beforeunload_listeners()) {
6547 if (++mUnloadOrBeforeUnloadListenerCount == 1) {
6548 mWindowGlobalChild->BlockBFCacheFor(
6549 BFCacheStatus::BEFOREUNLOAD_LISTENER);
6552 if (!mDoc || !(mDoc->GetSandboxFlags() & SANDBOXED_MODALS)) {
6553 mWindowGlobalChild->BeforeUnloadAdded();
6554 MOZ_ASSERT(mWindowGlobalChild->BeforeUnloadListeners() > 0);
6558 // We need to initialize localStorage in order to receive notifications.
6559 if (aType == nsGkAtoms::onstorage) {
6560 ErrorResult rv;
6561 GetLocalStorage(rv);
6562 rv.SuppressException();
6564 if (NextGenLocalStorageEnabled() && mLocalStorage &&
6565 mLocalStorage->Type() == Storage::eLocalStorage) {
6566 auto object = static_cast<LSObject*>(mLocalStorage.get());
6568 Unused << NS_WARN_IF(NS_FAILED(object->EnsureObserver()));
6573 void nsGlobalWindowInner::EventListenerRemoved(nsAtom* aType) {
6574 if (aType == nsGkAtoms::onunload && mWindowGlobalChild) {
6575 MOZ_ASSERT(mUnloadOrBeforeUnloadListenerCount > 0);
6576 if (--mUnloadOrBeforeUnloadListenerCount == 0) {
6577 mWindowGlobalChild->UnblockBFCacheFor(BFCacheStatus::UNLOAD_LISTENER);
6581 if (aType == nsGkAtoms::onbeforeunload && mWindowGlobalChild) {
6582 if (!mozilla::SessionHistoryInParent() ||
6583 !StaticPrefs::
6584 docshell_shistory_bfcache_ship_allow_beforeunload_listeners()) {
6585 if (--mUnloadOrBeforeUnloadListenerCount == 0) {
6586 mWindowGlobalChild->UnblockBFCacheFor(
6587 BFCacheStatus::BEFOREUNLOAD_LISTENER);
6590 if (!mDoc || !(mDoc->GetSandboxFlags() & SANDBOXED_MODALS)) {
6591 mWindowGlobalChild->BeforeUnloadRemoved();
6592 MOZ_ASSERT(mWindowGlobalChild->BeforeUnloadListeners() >= 0);
6596 if (aType == nsGkAtoms::onstorage) {
6597 if (NextGenLocalStorageEnabled() && mLocalStorage &&
6598 mLocalStorage->Type() == Storage::eLocalStorage &&
6599 // The remove event is fired even if this isn't the last listener, so
6600 // only remove if there are no other listeners left.
6601 mListenerManager &&
6602 !mListenerManager->HasListenersFor(nsGkAtoms::onstorage)) {
6603 auto object = static_cast<LSObject*>(mLocalStorage.get());
6605 object->DropObserver();
6610 void nsGlobalWindowInner::NotifyHasXRSession() {
6611 if (IsDying()) {
6612 // Do not proceed if the window is dying, as that will result
6613 // in leaks of objects that get re-allocated after FreeInnerObjects
6614 // has been called, including mVREventObserver.
6615 return;
6617 if (mWindowGlobalChild && !mHasXRSession) {
6618 mWindowGlobalChild->BlockBFCacheFor(BFCacheStatus::HAS_USED_VR);
6620 mHasXRSession = true;
6621 EnableVRUpdates();
6624 bool nsGlobalWindowInner::HasUsedVR() const {
6625 // Returns true only if content has enumerated and activated
6626 // XR devices. Detection of XR runtimes without activation
6627 // will not cause true to be returned.
6628 return mHasXRSession;
6631 bool nsGlobalWindowInner::IsVRContentDetected() const {
6632 // Returns true only if the content will respond to
6633 // the VRDisplayActivate event.
6634 return mHasVRDisplayActivateEvents;
6637 bool nsGlobalWindowInner::IsVRContentPresenting() const {
6638 for (const auto& display : mVRDisplays) {
6639 if (display->IsAnyPresenting(gfx::kVRGroupAll)) {
6640 return true;
6643 return false;
6646 void nsGlobalWindowInner::AddSizeOfIncludingThis(
6647 nsWindowSizes& aWindowSizes) const {
6648 aWindowSizes.mDOMSizes.mDOMOtherSize +=
6649 aWindowSizes.mState.mMallocSizeOf(this);
6650 aWindowSizes.mDOMSizes.mDOMOtherSize +=
6651 nsIGlobalObject::ShallowSizeOfExcludingThis(
6652 aWindowSizes.mState.mMallocSizeOf);
6654 EventListenerManager* elm = GetExistingListenerManager();
6655 if (elm) {
6656 aWindowSizes.mDOMSizes.mDOMOtherSize +=
6657 elm->SizeOfIncludingThis(aWindowSizes.mState.mMallocSizeOf);
6658 aWindowSizes.mDOMEventListenersCount += elm->ListenerCount();
6660 if (mDoc) {
6661 // Multiple global windows can share a document. So only measure the
6662 // document if it (a) doesn't have a global window, or (b) it's the
6663 // primary document for the window.
6664 if (!mDoc->GetInnerWindow() || mDoc->GetInnerWindow() == this) {
6665 mDoc->DocAddSizeOfIncludingThis(aWindowSizes);
6669 if (mNavigator) {
6670 aWindowSizes.mDOMSizes.mDOMOtherSize +=
6671 mNavigator->SizeOfIncludingThis(aWindowSizes.mState.mMallocSizeOf);
6674 ForEachEventTargetObject([&](DOMEventTargetHelper* et, bool* aDoneOut) {
6675 if (nsCOMPtr<nsISizeOfEventTarget> iSizeOf = do_QueryObject(et)) {
6676 aWindowSizes.mDOMSizes.mDOMEventTargetsSize +=
6677 iSizeOf->SizeOfEventTargetIncludingThis(
6678 aWindowSizes.mState.mMallocSizeOf);
6680 if (EventListenerManager* elm = et->GetExistingListenerManager()) {
6681 aWindowSizes.mDOMEventListenersCount += elm->ListenerCount();
6683 ++aWindowSizes.mDOMEventTargetsCount;
6686 if (mPerformance) {
6687 aWindowSizes.mDOMSizes.mDOMPerformanceUserEntries =
6688 mPerformance->SizeOfUserEntries(aWindowSizes.mState.mMallocSizeOf);
6689 aWindowSizes.mDOMSizes.mDOMPerformanceResourceEntries =
6690 mPerformance->SizeOfResourceEntries(aWindowSizes.mState.mMallocSizeOf);
6691 aWindowSizes.mDOMSizes.mDOMPerformanceEventEntries =
6692 mPerformance->SizeOfEventEntries(aWindowSizes.mState.mMallocSizeOf);
6696 void nsGlobalWindowInner::RegisterDataDocumentForMemoryReporting(
6697 Document* aDocument) {
6698 aDocument->SetAddedToMemoryReportAsDataDocument();
6699 mDataDocumentsForMemoryReporting.AppendElement(
6700 do_GetWeakReference(aDocument));
6703 void nsGlobalWindowInner::UnregisterDataDocumentForMemoryReporting(
6704 Document* aDocument) {
6705 nsWeakPtr doc = do_GetWeakReference(aDocument);
6706 MOZ_ASSERT(mDataDocumentsForMemoryReporting.Contains(doc));
6707 mDataDocumentsForMemoryReporting.RemoveElement(doc);
6710 void nsGlobalWindowInner::CollectDOMSizesForDataDocuments(
6711 nsWindowSizes& aSize) const {
6712 for (const nsWeakPtr& ptr : mDataDocumentsForMemoryReporting) {
6713 if (nsCOMPtr<Document> doc = do_QueryReferent(ptr)) {
6714 doc->DocAddSizeOfIncludingThis(aSize);
6719 void nsGlobalWindowInner::AddGamepad(GamepadHandle aHandle, Gamepad* aGamepad) {
6720 // Create the index we will present to content based on which indices are
6721 // already taken, as required by the spec.
6722 // https://w3c.github.io/gamepad/gamepad.html#widl-Gamepad-index
6723 int index = 0;
6724 while (mGamepadIndexSet.Contains(index)) {
6725 ++index;
6727 mGamepadIndexSet.Put(index);
6728 aGamepad->SetIndex(index);
6729 mGamepads.InsertOrUpdate(aHandle, RefPtr{aGamepad});
6732 void nsGlobalWindowInner::RemoveGamepad(GamepadHandle aHandle) {
6733 RefPtr<Gamepad> gamepad;
6734 if (!mGamepads.Get(aHandle, getter_AddRefs(gamepad))) {
6735 return;
6737 // Free up the index we were using so it can be reused
6738 mGamepadIndexSet.Remove(gamepad->Index());
6739 mGamepads.Remove(aHandle);
6742 void nsGlobalWindowInner::GetGamepads(nsTArray<RefPtr<Gamepad>>& aGamepads) {
6743 aGamepads.Clear();
6745 // navigator.getGamepads() always returns an empty array when
6746 // privacy.resistFingerprinting is true.
6747 if (ShouldResistFingerprinting(RFPTarget::Gamepad)) {
6748 return;
6751 // mGamepads.Count() may not be sufficient, but it's not harmful.
6752 aGamepads.SetCapacity(mGamepads.Count());
6753 for (const auto& entry : mGamepads) {
6754 Gamepad* gamepad = entry.GetWeak();
6755 aGamepads.EnsureLengthAtLeast(gamepad->Index() + 1);
6756 aGamepads[gamepad->Index()] = gamepad;
6760 already_AddRefed<Gamepad> nsGlobalWindowInner::GetGamepad(
6761 GamepadHandle aHandle) {
6762 RefPtr<Gamepad> gamepad;
6764 if (mGamepads.Get(aHandle, getter_AddRefs(gamepad))) {
6765 return gamepad.forget();
6768 return nullptr;
6771 void nsGlobalWindowInner::SetHasSeenGamepadInput(bool aHasSeen) {
6772 mHasSeenGamepadInput = aHasSeen;
6775 bool nsGlobalWindowInner::HasSeenGamepadInput() { return mHasSeenGamepadInput; }
6777 void nsGlobalWindowInner::SyncGamepadState() {
6778 if (mHasSeenGamepadInput) {
6779 RefPtr<GamepadManager> gamepadManager(GamepadManager::GetService());
6780 for (const auto& entry : mGamepads) {
6781 gamepadManager->SyncGamepadState(entry.GetKey(), this, entry.GetWeak());
6786 void nsGlobalWindowInner::StopGamepadHaptics() {
6787 if (mHasSeenGamepadInput) {
6788 RefPtr<GamepadManager> gamepadManager(GamepadManager::GetService());
6789 gamepadManager->StopHaptics();
6793 bool nsGlobalWindowInner::UpdateVRDisplays(
6794 nsTArray<RefPtr<mozilla::dom::VRDisplay>>& aDevices) {
6795 VRDisplay::UpdateVRDisplays(mVRDisplays, this);
6796 aDevices = mVRDisplays.Clone();
6797 return true;
6800 void nsGlobalWindowInner::NotifyActiveVRDisplaysChanged() {
6801 if (mNavigator) {
6802 mNavigator->NotifyActiveVRDisplaysChanged();
6806 void nsGlobalWindowInner::NotifyPresentationGenerationChanged(
6807 uint32_t aDisplayID) {
6808 for (const auto& display : mVRDisplays) {
6809 if (display->DisplayId() == aDisplayID) {
6810 display->OnPresentationGenerationChanged();
6815 void nsGlobalWindowInner::DispatchVRDisplayActivate(
6816 uint32_t aDisplayID, mozilla::dom::VRDisplayEventReason aReason) {
6817 // Ensure that our list of displays is up to date
6818 VRDisplay::UpdateVRDisplays(mVRDisplays, this);
6820 // Search for the display identified with aDisplayID and fire the
6821 // event if found.
6822 for (const auto& display : mVRDisplays) {
6823 if (display->DisplayId() == aDisplayID) {
6824 if (aReason != VRDisplayEventReason::Navigation &&
6825 display->IsAnyPresenting(gfx::kVRGroupContent)) {
6826 // We only want to trigger this event if nobody is presenting to the
6827 // display already or when a page is loaded by navigating away
6828 // from a page with an active VR Presentation.
6829 continue;
6832 VRDisplayEventInit init;
6833 init.mBubbles = false;
6834 init.mCancelable = false;
6835 init.mDisplay = display;
6836 init.mReason.Construct(aReason);
6838 RefPtr<VRDisplayEvent> event =
6839 VRDisplayEvent::Constructor(this, u"vrdisplayactivate"_ns, init);
6840 // vrdisplayactivate is a trusted event, allowing VRDisplay.requestPresent
6841 // to be used in response to link traversal, user request (chrome UX), and
6842 // HMD mounting detection sensors.
6843 event->SetTrusted(true);
6844 // VRDisplay.requestPresent normally requires a user gesture; however, an
6845 // exception is made to allow it to be called in response to
6846 // vrdisplayactivate during VR link traversal.
6847 display->StartHandlingVRNavigationEvent();
6848 DispatchEvent(*event);
6849 display->StopHandlingVRNavigationEvent();
6850 // Once we dispatch the event, we must not access any members as an event
6851 // listener can do anything, including closing windows.
6852 return;
6857 void nsGlobalWindowInner::DispatchVRDisplayDeactivate(
6858 uint32_t aDisplayID, mozilla::dom::VRDisplayEventReason aReason) {
6859 // Ensure that our list of displays is up to date
6860 VRDisplay::UpdateVRDisplays(mVRDisplays, this);
6862 // Search for the display identified with aDisplayID and fire the
6863 // event if found.
6864 for (const auto& display : mVRDisplays) {
6865 if (display->DisplayId() == aDisplayID && display->IsPresenting()) {
6866 // We only want to trigger this event to content that is presenting to
6867 // the display already.
6869 VRDisplayEventInit init;
6870 init.mBubbles = false;
6871 init.mCancelable = false;
6872 init.mDisplay = display;
6873 init.mReason.Construct(aReason);
6875 RefPtr<VRDisplayEvent> event =
6876 VRDisplayEvent::Constructor(this, u"vrdisplaydeactivate"_ns, init);
6877 event->SetTrusted(true);
6878 DispatchEvent(*event);
6879 // Once we dispatch the event, we must not access any members as an event
6880 // listener can do anything, including closing windows.
6881 return;
6886 void nsGlobalWindowInner::DispatchVRDisplayConnect(uint32_t aDisplayID) {
6887 // Ensure that our list of displays is up to date
6888 VRDisplay::UpdateVRDisplays(mVRDisplays, this);
6890 // Search for the display identified with aDisplayID and fire the
6891 // event if found.
6892 for (const auto& display : mVRDisplays) {
6893 if (display->DisplayId() == aDisplayID) {
6894 // Fire event even if not presenting to the display.
6895 VRDisplayEventInit init;
6896 init.mBubbles = false;
6897 init.mCancelable = false;
6898 init.mDisplay = display;
6899 // VRDisplayEvent.reason is not set for vrdisplayconnect
6901 RefPtr<VRDisplayEvent> event =
6902 VRDisplayEvent::Constructor(this, u"vrdisplayconnect"_ns, init);
6903 event->SetTrusted(true);
6904 DispatchEvent(*event);
6905 // Once we dispatch the event, we must not access any members as an event
6906 // listener can do anything, including closing windows.
6907 return;
6912 void nsGlobalWindowInner::DispatchVRDisplayDisconnect(uint32_t aDisplayID) {
6913 // Ensure that our list of displays is up to date
6914 VRDisplay::UpdateVRDisplays(mVRDisplays, this);
6916 // Search for the display identified with aDisplayID and fire the
6917 // event if found.
6918 for (const auto& display : mVRDisplays) {
6919 if (display->DisplayId() == aDisplayID) {
6920 // Fire event even if not presenting to the display.
6921 VRDisplayEventInit init;
6922 init.mBubbles = false;
6923 init.mCancelable = false;
6924 init.mDisplay = display;
6925 // VRDisplayEvent.reason is not set for vrdisplaydisconnect
6927 RefPtr<VRDisplayEvent> event =
6928 VRDisplayEvent::Constructor(this, u"vrdisplaydisconnect"_ns, init);
6929 event->SetTrusted(true);
6930 DispatchEvent(*event);
6931 // Once we dispatch the event, we must not access any members as an event
6932 // listener can do anything, including closing windows.
6933 return;
6938 void nsGlobalWindowInner::DispatchVRDisplayPresentChange(uint32_t aDisplayID) {
6939 // Ensure that our list of displays is up to date
6940 VRDisplay::UpdateVRDisplays(mVRDisplays, this);
6942 // Search for the display identified with aDisplayID and fire the
6943 // event if found.
6944 for (const auto& display : mVRDisplays) {
6945 if (display->DisplayId() == aDisplayID) {
6946 // Fire event even if not presenting to the display.
6947 VRDisplayEventInit init;
6948 init.mBubbles = false;
6949 init.mCancelable = false;
6950 init.mDisplay = display;
6951 // VRDisplayEvent.reason is not set for vrdisplaypresentchange
6952 RefPtr<VRDisplayEvent> event =
6953 VRDisplayEvent::Constructor(this, u"vrdisplaypresentchange"_ns, init);
6954 event->SetTrusted(true);
6955 DispatchEvent(*event);
6956 // Once we dispatch the event, we must not access any members as an event
6957 // listener can do anything, including closing windows.
6958 return;
6963 enum WindowState {
6964 // These constants need to match the constants in Window.webidl
6965 STATE_MAXIMIZED = 1,
6966 STATE_MINIMIZED = 2,
6967 STATE_NORMAL = 3,
6968 STATE_FULLSCREEN = 4
6971 uint16_t nsGlobalWindowInner::WindowState() {
6972 nsCOMPtr<nsIWidget> widget = GetMainWidget();
6974 int32_t mode = widget ? widget->SizeMode() : 0;
6976 switch (mode) {
6977 case nsSizeMode_Minimized:
6978 return STATE_MINIMIZED;
6979 case nsSizeMode_Maximized:
6980 return STATE_MAXIMIZED;
6981 case nsSizeMode_Fullscreen:
6982 return STATE_FULLSCREEN;
6983 case nsSizeMode_Normal:
6984 return STATE_NORMAL;
6985 default:
6986 NS_WARNING("Illegal window state for this chrome window");
6987 break;
6990 return STATE_NORMAL;
6993 bool nsGlobalWindowInner::IsFullyOccluded() {
6994 nsCOMPtr<nsIWidget> widget = GetMainWidget();
6995 return widget && widget->IsFullyOccluded();
6998 void nsGlobalWindowInner::Maximize() {
6999 if (nsCOMPtr<nsIWidget> widget = GetMainWidget()) {
7000 widget->SetSizeMode(nsSizeMode_Maximized);
7004 void nsGlobalWindowInner::Minimize() {
7005 if (nsCOMPtr<nsIWidget> widget = GetMainWidget()) {
7006 widget->SetSizeMode(nsSizeMode_Minimized);
7010 void nsGlobalWindowInner::Restore() {
7011 if (nsCOMPtr<nsIWidget> widget = GetMainWidget()) {
7012 widget->SetSizeMode(nsSizeMode_Normal);
7016 void nsGlobalWindowInner::GetWorkspaceID(nsAString& workspaceID) {
7017 workspaceID.Truncate();
7018 if (nsCOMPtr<nsIWidget> widget = GetMainWidget()) {
7019 return widget->GetWorkspaceID(workspaceID);
7023 void nsGlobalWindowInner::MoveToWorkspace(const nsAString& workspaceID) {
7024 if (nsCOMPtr<nsIWidget> widget = GetMainWidget()) {
7025 widget->MoveToWorkspace(workspaceID);
7029 void nsGlobalWindowInner::GetAttention(ErrorResult& aResult) {
7030 return GetAttentionWithCycleCount(-1, aResult);
7033 void nsGlobalWindowInner::GetAttentionWithCycleCount(int32_t aCycleCount,
7034 ErrorResult& aError) {
7035 nsCOMPtr<nsIWidget> widget = GetMainWidget();
7037 if (widget) {
7038 aError = widget->GetAttention(aCycleCount);
7042 already_AddRefed<Promise> nsGlobalWindowInner::PromiseDocumentFlushed(
7043 PromiseDocumentFlushedCallback& aCallback, ErrorResult& aError) {
7044 MOZ_RELEASE_ASSERT(IsChromeWindow());
7046 if (!IsCurrentInnerWindow()) {
7047 aError.ThrowInvalidStateError("Not the current inner window");
7048 return nullptr;
7050 if (!mDoc) {
7051 aError.ThrowInvalidStateError("No document");
7052 return nullptr;
7055 if (mIteratingDocumentFlushedResolvers) {
7056 aError.ThrowInvalidStateError("Already iterating through resolvers");
7057 return nullptr;
7060 PresShell* presShell = mDoc->GetPresShell();
7061 if (!presShell) {
7062 aError.ThrowInvalidStateError("No pres shell");
7063 return nullptr;
7066 // We need to associate the lifetime of the Promise to the lifetime
7067 // of the caller's global. That way, if the window we're observing
7068 // refresh driver ticks on goes away before our observer is fired,
7069 // we can still resolve the Promise.
7070 nsIGlobalObject* global = GetIncumbentGlobal();
7071 if (!global) {
7072 aError.ThrowInvalidStateError("No incumbent global");
7073 return nullptr;
7076 RefPtr<Promise> resultPromise = Promise::Create(global, aError);
7077 if (aError.Failed()) {
7078 return nullptr;
7081 UniquePtr<PromiseDocumentFlushedResolver> flushResolver(
7082 new PromiseDocumentFlushedResolver(resultPromise, aCallback));
7084 if (!presShell->NeedStyleFlush() && !presShell->NeedLayoutFlush()) {
7085 flushResolver->Call();
7086 return resultPromise.forget();
7089 if (!TryToObserveRefresh()) {
7090 aError.ThrowInvalidStateError("Couldn't observe refresh");
7091 return nullptr;
7094 mDocumentFlushedResolvers.AppendElement(std::move(flushResolver));
7095 return resultPromise.forget();
7098 bool nsGlobalWindowInner::TryToObserveRefresh() {
7099 if (mObservingRefresh) {
7100 return true;
7103 if (!mDoc) {
7104 return false;
7107 nsPresContext* pc = mDoc->GetPresContext();
7108 if (!pc) {
7109 return false;
7112 mObservingRefresh = true;
7113 auto observer = MakeRefPtr<ManagedPostRefreshObserver>(
7114 pc, [win = RefPtr{this}](bool aWasCanceled) {
7115 if (win->MaybeCallDocumentFlushedResolvers(
7116 /* aUntilExhaustion = */ aWasCanceled)) {
7117 return ManagedPostRefreshObserver::Unregister::No;
7119 win->mObservingRefresh = false;
7120 return ManagedPostRefreshObserver::Unregister::Yes;
7122 pc->RegisterManagedPostRefreshObserver(observer.get());
7123 return mObservingRefresh;
7126 void nsGlobalWindowInner::CallDocumentFlushedResolvers(bool aUntilExhaustion) {
7127 while (true) {
7129 // To coalesce MicroTask checkpoints inside callback call, enclose the
7130 // inner loop with nsAutoMicroTask, and perform a MicroTask checkpoint
7131 // after the loop.
7132 nsAutoMicroTask mt;
7134 mIteratingDocumentFlushedResolvers = true;
7136 auto resolvers = std::move(mDocumentFlushedResolvers);
7137 for (const auto& resolver : resolvers) {
7138 resolver->Call();
7141 mIteratingDocumentFlushedResolvers = false;
7144 // Leaving nsAutoMicroTask above will perform MicroTask checkpoint, and
7145 // Promise callbacks there may create mDocumentFlushedResolvers items.
7147 // If there's no new resolvers, or we're not exhausting the queue, there's
7148 // nothing to do (we'll keep observing if there's any new observer).
7150 // Otherwise, keep looping to call all promises. This case can happen while
7151 // destroying the window. This violates the constraint that the
7152 // promiseDocumentFlushed callback only ever run when no flush is needed,
7153 // but it's necessary to resolve the Promise returned by that.
7154 if (!aUntilExhaustion || mDocumentFlushedResolvers.IsEmpty()) {
7155 break;
7160 bool nsGlobalWindowInner::MaybeCallDocumentFlushedResolvers(
7161 bool aUntilExhaustion) {
7162 MOZ_ASSERT(mDoc);
7164 PresShell* presShell = mDoc->GetPresShell();
7165 if (!presShell || aUntilExhaustion) {
7166 CallDocumentFlushedResolvers(/* aUntilExhaustion = */ true);
7167 return false;
7170 if (presShell->NeedStyleFlush() || presShell->NeedLayoutFlush()) {
7171 // By the time our observer fired, something has already invalidated
7172 // style or layout - or perhaps we're still in the middle of a flush that
7173 // was interrupted. In either case, we'll wait until the next refresh driver
7174 // tick instead and try again.
7175 return true;
7178 CallDocumentFlushedResolvers(/* aUntilExhaustion = */ false);
7179 return !mDocumentFlushedResolvers.IsEmpty();
7182 already_AddRefed<nsWindowRoot> nsGlobalWindowInner::GetWindowRoot(
7183 mozilla::ErrorResult& aError) {
7184 FORWARD_TO_OUTER_OR_THROW(GetWindowRootOuter, (), aError, nullptr);
7187 void nsGlobalWindowInner::SetCursor(const nsACString& aCursor,
7188 ErrorResult& aError) {
7189 FORWARD_TO_OUTER_OR_THROW(SetCursorOuter, (aCursor, aError), aError, );
7192 nsIBrowserDOMWindow* nsGlobalWindowInner::GetBrowserDOMWindow(
7193 ErrorResult& aError) {
7194 FORWARD_TO_OUTER_OR_THROW(GetBrowserDOMWindow, (), aError, nullptr);
7197 void nsGlobalWindowInner::SetBrowserDOMWindow(
7198 nsIBrowserDOMWindow* aBrowserWindow, ErrorResult& aError) {
7199 FORWARD_TO_OUTER_OR_THROW(SetBrowserDOMWindowOuter, (aBrowserWindow),
7200 aError, );
7203 void nsGlobalWindowInner::NotifyDefaultButtonLoaded(Element& aDefaultButton,
7204 ErrorResult& aError) {
7205 // Don't snap to a disabled button.
7206 nsCOMPtr<nsIDOMXULControlElement> xulControl = aDefaultButton.AsXULControl();
7207 if (!xulControl) {
7208 aError.Throw(NS_ERROR_FAILURE);
7209 return;
7211 bool disabled;
7212 aError = xulControl->GetDisabled(&disabled);
7213 if (aError.Failed() || disabled) {
7214 return;
7217 // Get the button rect in screen coordinates.
7218 nsIFrame* frame = aDefaultButton.GetPrimaryFrame();
7219 if (!frame) {
7220 aError.Throw(NS_ERROR_FAILURE);
7221 return;
7223 LayoutDeviceIntRect buttonRect = LayoutDeviceIntRect::FromAppUnitsToNearest(
7224 frame->GetScreenRectInAppUnits(),
7225 frame->PresContext()->AppUnitsPerDevPixel());
7227 // Get the widget rect in screen coordinates.
7228 nsIWidget* widget = GetNearestWidget();
7229 if (!widget) {
7230 aError.Throw(NS_ERROR_FAILURE);
7231 return;
7233 LayoutDeviceIntRect widgetRect = widget->GetScreenBounds();
7235 // Convert the buttonRect coordinates from screen to the widget.
7236 buttonRect -= widgetRect.TopLeft();
7237 nsresult rv = widget->OnDefaultButtonLoaded(buttonRect);
7238 if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) {
7239 aError.Throw(rv);
7243 ChromeMessageBroadcaster* nsGlobalWindowInner::MessageManager() {
7244 MOZ_ASSERT(IsChromeWindow());
7245 if (!mChromeFields.mMessageManager) {
7246 RefPtr<ChromeMessageBroadcaster> globalMM =
7247 nsFrameMessageManager::GetGlobalMessageManager();
7248 mChromeFields.mMessageManager = new ChromeMessageBroadcaster(globalMM);
7250 return mChromeFields.mMessageManager;
7253 ChromeMessageBroadcaster* nsGlobalWindowInner::GetGroupMessageManager(
7254 const nsAString& aGroup) {
7255 MOZ_ASSERT(IsChromeWindow());
7257 return mChromeFields.mGroupMessageManagers
7258 .LookupOrInsertWith(
7259 aGroup,
7260 [&] {
7261 return MakeAndAddRef<ChromeMessageBroadcaster>(MessageManager());
7263 .get();
7266 void nsGlobalWindowInner::InitWasOffline() { mWasOffline = NS_IsOffline(); }
7268 int16_t nsGlobalWindowInner::Orientation(CallerType aCallerType) {
7269 // GetOrientationAngle() returns 0, 90, 180 or 270.
7270 // window.orientation returns -90, 0, 90 or 180.
7271 if (nsIGlobalObject::ShouldResistFingerprinting(
7272 aCallerType, RFPTarget::ScreenOrientation)) {
7273 return 0;
7275 nsScreen* s = GetScreen(IgnoreErrors());
7276 if (!s) {
7277 return 0;
7279 int16_t angle = AssertedCast<int16_t>(s->GetOrientationAngle());
7280 return angle <= 180 ? angle : angle - 360;
7283 already_AddRefed<Console> nsGlobalWindowInner::GetConsole(JSContext* aCx,
7284 ErrorResult& aRv) {
7285 if (!mConsole) {
7286 mConsole = Console::Create(aCx, this, aRv);
7287 if (NS_WARN_IF(aRv.Failed())) {
7288 return nullptr;
7292 RefPtr<Console> console = mConsole;
7293 return console.forget();
7296 bool nsGlobalWindowInner::IsSecureContext() const {
7297 JS::Realm* realm = js::GetNonCCWObjectRealm(GetWrapperPreserveColor());
7298 return JS::GetIsSecureContext(realm);
7301 External* nsGlobalWindowInner::External() {
7302 if (!mExternal) {
7303 mExternal = new dom::External(ToSupports(this));
7306 return mExternal;
7309 void nsGlobalWindowInner::ClearDocumentDependentSlots(JSContext* aCx) {
7310 // If JSAPI OOMs here, there is basically nothing we can do to recover safely.
7311 if (!Window_Binding::ClearCachedDocumentValue(aCx, this) ||
7312 !Window_Binding::ClearCachedPerformanceValue(aCx, this)) {
7313 MOZ_CRASH("Unhandlable OOM while clearing document dependent slots.");
7317 /* static */
7318 JSObject* nsGlobalWindowInner::CreateNamedPropertiesObject(
7319 JSContext* aCx, JS::Handle<JSObject*> aProto) {
7320 return WindowNamedPropertiesHandler::Create(aCx, aProto);
7323 void nsGlobalWindowInner::RedefineProperty(JSContext* aCx,
7324 const char* aPropName,
7325 JS::Handle<JS::Value> aValue,
7326 ErrorResult& aError) {
7327 JS::Rooted<JSObject*> thisObj(aCx, GetWrapperPreserveColor());
7328 if (!thisObj) {
7329 aError.Throw(NS_ERROR_UNEXPECTED);
7330 return;
7333 if (!JS_WrapObject(aCx, &thisObj) ||
7334 !JS_DefineProperty(aCx, thisObj, aPropName, aValue, JSPROP_ENUMERATE)) {
7335 aError.Throw(NS_ERROR_FAILURE);
7339 void nsGlobalWindowInner::FireOnNewGlobalObject() {
7340 // AutoEntryScript required to invoke debugger hook, which is a
7341 // Gecko-specific concept at present.
7342 AutoEntryScript aes(this, "nsGlobalWindowInner report new global");
7343 JS::Rooted<JSObject*> global(aes.cx(), GetWrapper());
7344 JS_FireOnNewGlobalObject(aes.cx(), global);
7347 #if defined(_WINDOWS_) && !defined(MOZ_WRAPPED_WINDOWS_H)
7348 # pragma message( \
7349 "wrapper failure reason: " MOZ_WINDOWS_WRAPPER_DISABLED_REASON)
7350 # error "Never include unwrapped windows.h in this file!"
7351 #endif
7353 already_AddRefed<Promise> nsGlobalWindowInner::CreateImageBitmap(
7354 const ImageBitmapSource& aImage, const ImageBitmapOptions& aOptions,
7355 ErrorResult& aRv) {
7356 return ImageBitmap::Create(this, aImage, Nothing(), aOptions, aRv);
7359 already_AddRefed<Promise> nsGlobalWindowInner::CreateImageBitmap(
7360 const ImageBitmapSource& aImage, int32_t aSx, int32_t aSy, int32_t aSw,
7361 int32_t aSh, const ImageBitmapOptions& aOptions, ErrorResult& aRv) {
7362 return ImageBitmap::Create(
7363 this, aImage, Some(gfx::IntRect(aSx, aSy, aSw, aSh)), aOptions, aRv);
7366 // https://html.spec.whatwg.org/#structured-cloning
7367 void nsGlobalWindowInner::StructuredClone(
7368 JSContext* aCx, JS::Handle<JS::Value> aValue,
7369 const StructuredSerializeOptions& aOptions,
7370 JS::MutableHandle<JS::Value> aRetval, ErrorResult& aError) {
7371 nsContentUtils::StructuredClone(aCx, this, aValue, aOptions, aRetval, aError);
7374 nsresult nsGlobalWindowInner::Dispatch(
7375 already_AddRefed<nsIRunnable>&& aRunnable) const {
7376 MOZ_RELEASE_ASSERT(NS_IsMainThread());
7377 return NS_DispatchToCurrentThread(std::move(aRunnable));
7380 nsISerialEventTarget* nsGlobalWindowInner::SerialEventTarget() const {
7381 MOZ_RELEASE_ASSERT(NS_IsMainThread());
7382 return GetMainThreadSerialEventTarget();
7385 Worklet* nsGlobalWindowInner::GetPaintWorklet(ErrorResult& aRv) {
7386 if (!mPaintWorklet) {
7387 nsIPrincipal* principal = GetPrincipal();
7388 if (!principal) {
7389 aRv.Throw(NS_ERROR_FAILURE);
7390 return nullptr;
7393 mPaintWorklet = PaintWorkletImpl::CreateWorklet(this, principal);
7396 return mPaintWorklet;
7399 void nsGlobalWindowInner::GetRegionalPrefsLocales(
7400 nsTArray<nsString>& aLocales) {
7401 MOZ_ASSERT(mozilla::intl::LocaleService::GetInstance());
7403 AutoTArray<nsCString, 10> rpLocales;
7404 mozilla::intl::LocaleService::GetInstance()->GetRegionalPrefsLocales(
7405 rpLocales);
7407 for (const auto& loc : rpLocales) {
7408 aLocales.AppendElement(NS_ConvertUTF8toUTF16(loc));
7412 void nsGlobalWindowInner::GetWebExposedLocales(nsTArray<nsString>& aLocales) {
7413 MOZ_ASSERT(mozilla::intl::LocaleService::GetInstance());
7415 AutoTArray<nsCString, 10> rpLocales;
7416 mozilla::intl::LocaleService::GetInstance()->GetWebExposedLocales(rpLocales);
7418 for (const auto& loc : rpLocales) {
7419 aLocales.AppendElement(NS_ConvertUTF8toUTF16(loc));
7423 IntlUtils* nsGlobalWindowInner::GetIntlUtils(ErrorResult& aError) {
7424 if (!mIntlUtils) {
7425 mIntlUtils = new IntlUtils(this);
7428 return mIntlUtils;
7431 void nsGlobalWindowInner::StoreSharedWorker(SharedWorker* aSharedWorker) {
7432 MOZ_ASSERT(aSharedWorker);
7433 MOZ_ASSERT(!mSharedWorkers.Contains(aSharedWorker));
7435 mSharedWorkers.AppendElement(aSharedWorker);
7438 void nsGlobalWindowInner::ForgetSharedWorker(SharedWorker* aSharedWorker) {
7439 MOZ_ASSERT(aSharedWorker);
7440 MOZ_ASSERT(mSharedWorkers.Contains(aSharedWorker));
7442 mSharedWorkers.RemoveElement(aSharedWorker);
7445 void nsGlobalWindowInner::StorageAccessPermissionChanged() {
7446 // Invalidate cached StorageAllowed field so that calls to GetLocalStorage
7447 // give us the updated localStorage object.
7448 ClearStorageAllowedCache();
7450 // If we're always partitioning non-cookie third party storage then
7451 // there is no need to clear it when the user accepts requestStorageAccess.
7452 if (StaticPrefs::
7453 privacy_partition_always_partition_third_party_non_cookie_storage()) {
7454 // Just reset the active cookie and storage principals
7455 nsCOMPtr<nsICookieJarSettings> cjs;
7456 if (mDoc) {
7457 cjs = mDoc->CookieJarSettings();
7459 StorageAccess storageAccess = StorageAllowedForWindow(this);
7460 if (ShouldPartitionStorage(storageAccess) &&
7461 StoragePartitioningEnabled(storageAccess, cjs)) {
7462 if (mDoc) {
7463 mDoc->ClearActiveCookieAndStoragePrincipals();
7465 return;
7469 PropagateStorageAccessPermissionGrantedToWorkers(*this);
7471 // If we have a partitioned localStorage, it's time to replace it with a real
7472 // one in order to receive notifications.
7474 if (mLocalStorage) {
7475 IgnoredErrorResult error;
7476 GetLocalStorage(error);
7477 if (NS_WARN_IF(error.Failed())) {
7478 return;
7481 MOZ_ASSERT(mLocalStorage &&
7482 mLocalStorage->Type() == Storage::eLocalStorage);
7484 if (NextGenLocalStorageEnabled() && mListenerManager &&
7485 mListenerManager->HasListenersFor(nsGkAtoms::onstorage)) {
7486 auto object = static_cast<LSObject*>(mLocalStorage.get());
7488 object->EnsureObserver();
7492 // Reset the IndexedDB factory.
7493 mIndexedDB = nullptr;
7495 // Reset DOM Cache
7496 mCacheStorage = nullptr;
7498 // Reset the active cookie and storage principals
7499 if (mDoc) {
7500 mDoc->ClearActiveCookieAndStoragePrincipals();
7501 if (mWindowGlobalChild) {
7502 // XXX(farre): This is a bit backwards, but clearing the cookie
7503 // principal might make us end up with a new effective storage
7504 // principal on the child side than on the parent side, which
7505 // means that we need to sync it. See bug 1705359.
7506 mWindowGlobalChild->SetDocumentPrincipal(
7507 mDoc->NodePrincipal(), mDoc->EffectiveStoragePrincipal());
7512 ContentMediaController* nsGlobalWindowInner::GetContentMediaController() {
7513 if (mContentMediaController) {
7514 return mContentMediaController;
7516 if (!mBrowsingContext) {
7517 return nullptr;
7520 mContentMediaController = new ContentMediaController(mBrowsingContext->Id());
7521 return mContentMediaController;
7524 void nsGlobalWindowInner::SetScrollMarks(const nsTArray<uint32_t>& aScrollMarks,
7525 bool aOnHScrollbar) {
7526 mScrollMarks.Assign(aScrollMarks);
7527 mScrollMarksOnHScrollbar = aOnHScrollbar;
7529 // Mark the scrollbar for repainting.
7530 if (mDoc) {
7531 PresShell* presShell = mDoc->GetPresShell();
7532 if (presShell) {
7533 nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollable();
7534 if (sf) {
7535 sf->InvalidateScrollbars();
7541 /* static */
7542 already_AddRefed<nsGlobalWindowInner> nsGlobalWindowInner::Create(
7543 nsGlobalWindowOuter* aOuterWindow, bool aIsChrome,
7544 WindowGlobalChild* aActor) {
7545 RefPtr<nsGlobalWindowInner> window =
7546 new nsGlobalWindowInner(aOuterWindow, aActor);
7547 if (aIsChrome) {
7548 window->mIsChrome = true;
7549 window->mCleanMessageManager = true;
7552 if (aActor) {
7553 aActor->InitWindowGlobal(window);
7556 window->InitWasOffline();
7557 return window.forget();
7560 JS::loader::ModuleLoaderBase* nsGlobalWindowInner::GetModuleLoader(
7561 JSContext* aCx) {
7562 Document* document = GetDocument();
7563 if (!document) {
7564 return nullptr;
7567 ScriptLoader* loader = document->ScriptLoader();
7568 if (!loader) {
7569 return nullptr;
7572 return loader->GetModuleLoader();
7575 nsIURI* nsPIDOMWindowInner::GetDocumentURI() const {
7576 return mDoc ? mDoc->GetDocumentURI() : mDocumentURI.get();
7579 nsIURI* nsPIDOMWindowInner::GetDocBaseURI() const {
7580 return mDoc ? mDoc->GetDocBaseURI() : mDocBaseURI.get();
7583 mozilla::dom::WindowContext* nsPIDOMWindowInner::GetWindowContext() const {
7584 return mWindowGlobalChild ? mWindowGlobalChild->WindowContext() : nullptr;
7587 bool nsPIDOMWindowInner::RemoveFromBFCacheSync() {
7588 if (Document* doc = GetExtantDoc()) {
7589 return doc->RemoveFromBFCacheSync();
7591 return false;
7594 void nsPIDOMWindowInner::MaybeCreateDoc() {
7595 // XXX: Forward to outer?
7596 MOZ_ASSERT(!mDoc);
7597 if (nsIDocShell* docShell = GetDocShell()) {
7598 // Note that |document| here is the same thing as our mDoc, but we
7599 // don't have to explicitly set the member variable because the docshell
7600 // has already called SetNewDocument().
7601 nsCOMPtr<Document> document = docShell->GetDocument();
7602 Unused << document;
7606 mozilla::dom::DocGroup* nsPIDOMWindowInner::GetDocGroup() const {
7607 Document* doc = GetExtantDoc();
7608 if (doc) {
7609 return doc->GetDocGroup();
7611 return nullptr;
7614 mozilla::dom::BrowsingContextGroup*
7615 nsPIDOMWindowInner::GetBrowsingContextGroup() const {
7616 return mBrowsingContext ? mBrowsingContext->Group() : nullptr;
7619 nsIGlobalObject* nsPIDOMWindowInner::AsGlobal() {
7620 return nsGlobalWindowInner::Cast(this);
7623 const nsIGlobalObject* nsPIDOMWindowInner::AsGlobal() const {
7624 return nsGlobalWindowInner::Cast(this);
7627 void nsPIDOMWindowInner::SaveStorageAccessPermissionGranted() {
7628 WindowContext* wc = GetWindowContext();
7629 if (wc) {
7630 Unused << wc->SetUsingStorageAccess(true);
7633 nsGlobalWindowInner::Cast(this)->StorageAccessPermissionChanged();
7636 void nsPIDOMWindowInner::SaveStorageAccessPermissionRevoked() {
7637 WindowContext* wc = GetWindowContext();
7638 if (wc) {
7639 Unused << wc->SetUsingStorageAccess(false);
7642 nsGlobalWindowInner::Cast(this)->StorageAccessPermissionChanged();
7645 bool nsPIDOMWindowInner::UsingStorageAccess() {
7646 WindowContext* wc = GetWindowContext();
7647 if (!wc) {
7648 return false;
7651 return wc->GetUsingStorageAccess();
7654 nsPIDOMWindowInner::nsPIDOMWindowInner(nsPIDOMWindowOuter* aOuterWindow,
7655 WindowGlobalChild* aActor)
7656 : mMutationBits(0),
7657 mIsDocumentLoaded(false),
7658 mIsHandlingResizeEvent(false),
7659 mMayHaveDOMActivateEventListeners(false),
7660 mMayHavePaintEventListener(false),
7661 mMayHaveTouchEventListener(false),
7662 mMayHaveSelectionChangeEventListener(false),
7663 mMayHaveFormSelectEventListener(false),
7664 mMayHaveMouseEnterLeaveEventListener(false),
7665 mMayHavePointerEnterLeaveEventListener(false),
7666 mMayHaveTransitionEventListener(false),
7667 mMayHaveBeforeInputEventListenerForTelemetry(false),
7668 mMutationObserverHasObservedNodeForTelemetry(false),
7669 mOuterWindow(aOuterWindow),
7670 mWindowID(0),
7671 mHasNotifiedGlobalCreated(false),
7672 mMarkedCCGeneration(0),
7673 mHasTriedToCacheTopInnerWindow(false),
7674 mNumOfIndexedDBDatabases(0),
7675 mNumOfOpenWebSockets(0),
7676 mEvent(nullptr),
7677 mWindowGlobalChild(aActor),
7678 mWasSuspendedByGroup(false) {
7679 MOZ_ASSERT(aOuterWindow);
7680 mBrowsingContext = aOuterWindow->GetBrowsingContext();
7682 if (mWindowGlobalChild) {
7683 mWindowID = aActor->InnerWindowId();
7685 MOZ_ASSERT(mWindowGlobalChild->BrowsingContext() == mBrowsingContext);
7686 } else {
7687 mWindowID = nsContentUtils::GenerateWindowId();
7691 nsPIDOMWindowInner::~nsPIDOMWindowInner() = default;
7693 #undef FORWARD_TO_OUTER
7694 #undef FORWARD_TO_OUTER_OR_THROW
7695 #undef FORWARD_TO_OUTER_VOID