Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / dom / base / nsGlobalWindowInner.cpp
blobe9c040a36d1f6ebc4c8e176ab2bf89d823f295f9
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/BounceTrackingStorageObserver.h"
52 #include "mozilla/CallState.h"
53 #include "mozilla/CycleCollectedJSContext.h"
54 #include "mozilla/DOMEventTargetHelper.h"
55 #include "mozilla/ErrorResult.h"
56 #include "mozilla/EventDispatcher.h"
57 #include "mozilla/EventListenerManager.h"
58 #include "mozilla/EventQueue.h"
59 #include "mozilla/ExtensionPolicyService.h"
60 #include "mozilla/FloatingPoint.h"
61 #include "mozilla/FlushType.h"
62 #include "mozilla/Likely.h"
63 #include "mozilla/LinkedList.h"
64 #include "mozilla/LookAndFeel.h"
65 #include "mozilla/Logging.h"
66 #include "mozilla/MacroForEach.h"
67 #include "mozilla/Maybe.h"
68 #include "mozilla/OwningNonNull.h"
69 #include "mozilla/PermissionDelegateHandler.h"
70 #include "mozilla/Preferences.h"
71 #include "mozilla/PresShell.h"
72 #include "mozilla/ProcessHangMonitor.h"
73 #include "mozilla/RefPtr.h"
74 #include "mozilla/Result.h"
75 #include "mozilla/ScrollTypes.h"
76 #include "mozilla/Components.h"
77 #include "mozilla/SizeOfState.h"
78 #include "mozilla/Span.h"
79 #include "mozilla/SpinEventLoopUntil.h"
80 #include "mozilla/Sprintf.h"
81 #include "mozilla/StaticPrefs_browser.h"
82 #include "mozilla/StaticPrefs_docshell.h"
83 #include "mozilla/StaticPrefs_dom.h"
84 #include "mozilla/StaticPrefs_extensions.h"
85 #include "mozilla/StaticPrefs_privacy.h"
86 #include "mozilla/StorageAccess.h"
87 #include "mozilla/StoragePrincipalHelper.h"
88 #include "mozilla/Telemetry.h"
89 #include "mozilla/TelemetryHistogramEnums.h"
90 #include "mozilla/TimeStamp.h"
91 #include "mozilla/UniquePtr.h"
92 #include "mozilla/Unused.h"
93 #include "mozilla/dom/AudioContext.h"
94 #include "mozilla/dom/AutoEntryScript.h"
95 #include "mozilla/dom/BarProps.h"
96 #include "mozilla/dom/BindingDeclarations.h"
97 #include "mozilla/dom/BindingUtils.h"
98 #include "mozilla/dom/BrowserChild.h"
99 #include "mozilla/dom/BrowsingContext.h"
100 #include "mozilla/dom/CSPEvalChecker.h"
101 #include "mozilla/dom/CallbackDebuggerNotification.h"
102 #include "mozilla/dom/ChromeMessageBroadcaster.h"
103 #include "mozilla/dom/ClientInfo.h"
104 #include "mozilla/dom/ClientManager.h"
105 #include "mozilla/dom/ClientSource.h"
106 #include "mozilla/dom/ClientState.h"
107 #include "mozilla/dom/ClientsBinding.h"
108 #include "mozilla/dom/Console.h"
109 #include "mozilla/dom/ContentChild.h"
110 #include "mozilla/dom/ContentFrameMessageManager.h"
111 #include "mozilla/dom/ContentMediaController.h"
112 #include "mozilla/dom/CustomElementRegistry.h"
113 #include "mozilla/dom/DebuggerNotification.h"
114 #include "mozilla/dom/DebuggerNotificationBinding.h"
115 #include "mozilla/dom/DebuggerNotificationManager.h"
116 #include "mozilla/dom/DocGroup.h"
117 #include "mozilla/dom/Document.h"
118 #include "mozilla/dom/DocumentInlines.h"
119 #include "mozilla/dom/Element.h"
120 #include "mozilla/dom/Event.h"
121 #include "mozilla/dom/EventTarget.h"
122 #include "mozilla/dom/External.h"
123 #include "mozilla/dom/Fetch.h"
124 #include "mozilla/dom/Gamepad.h"
125 #include "mozilla/dom/GamepadHandle.h"
126 #include "mozilla/dom/GamepadManager.h"
127 #include "mozilla/dom/HashChangeEvent.h"
128 #include "mozilla/dom/HashChangeEventBinding.h"
129 #include "mozilla/dom/IDBFactory.h"
130 #include "mozilla/dom/IdleRequest.h"
131 #include "mozilla/dom/ImageBitmap.h"
132 #include "mozilla/dom/ImageBitmapSource.h"
133 #include "mozilla/dom/InstallTriggerBinding.h"
134 #include "mozilla/dom/IntlUtils.h"
135 #include "mozilla/dom/JSExecutionContext.h"
136 #include "mozilla/dom/LSObject.h"
137 #include "mozilla/dom/LocalStorage.h"
138 #include "mozilla/dom/LocalStorageCommon.h"
139 #include "mozilla/dom/Location.h"
140 #include "mozilla/dom/MediaDevices.h"
141 #include "mozilla/dom/MediaKeys.h"
142 #include "mozilla/dom/NavigatorBinding.h"
143 #include "mozilla/dom/Nullable.h"
144 #include "mozilla/dom/PartitionedLocalStorage.h"
145 #include "mozilla/dom/Performance.h"
146 #include "mozilla/dom/PerformanceMainThread.h"
147 #include "mozilla/dom/PopStateEvent.h"
148 #include "mozilla/dom/PopStateEventBinding.h"
149 #include "mozilla/dom/PopupBlocker.h"
150 #include "mozilla/dom/PrimitiveConversions.h"
151 #include "mozilla/dom/Promise.h"
152 #include "mozilla/dom/RootedDictionary.h"
153 #include "mozilla/dom/WebTaskSchedulerMainThread.h"
154 #include "mozilla/dom/ScriptLoader.h"
155 #include "mozilla/dom/ScriptSettings.h"
156 #include "mozilla/dom/ServiceWorker.h"
157 #include "mozilla/dom/ServiceWorkerDescriptor.h"
158 #include "mozilla/dom/ServiceWorkerRegistration.h"
159 #include "mozilla/dom/SessionStorageManager.h"
160 #include "mozilla/dom/SharedWorker.h"
161 #include "mozilla/dom/Storage.h"
162 #include "mozilla/dom/StorageEvent.h"
163 #include "mozilla/dom/StorageEventBinding.h"
164 #include "mozilla/dom/StorageNotifierService.h"
165 #include "mozilla/dom/StorageUtils.h"
166 #include "mozilla/dom/TabMessageTypes.h"
167 #include "mozilla/dom/Timeout.h"
168 #include "mozilla/dom/TimeoutHandler.h"
169 #include "mozilla/dom/TimeoutManager.h"
170 #include "mozilla/dom/ToJSValue.h"
171 #include "mozilla/dom/TrustedTypePolicyFactory.h"
172 #include "mozilla/dom/VRDisplay.h"
173 #include "mozilla/dom/VRDisplayEvent.h"
174 #include "mozilla/dom/VRDisplayEventBinding.h"
175 #include "mozilla/dom/VREventObserver.h"
176 #include "mozilla/dom/VisualViewport.h"
177 #include "mozilla/dom/WebIDLGlobalNameHash.h"
178 #include "mozilla/dom/WindowBinding.h"
179 #include "mozilla/dom/WindowContext.h"
180 #include "mozilla/dom/WindowGlobalChild.h"
181 #include "mozilla/dom/WindowProxyHolder.h"
182 #include "mozilla/dom/WorkerCommon.h"
183 #include "mozilla/dom/Worklet.h"
184 #include "mozilla/dom/XRPermissionRequest.h"
185 #include "mozilla/dom/cache/CacheStorage.h"
186 #include "mozilla/dom/cache/Types.h"
187 #include "mozilla/glean/bindings/Glean.h"
188 #include "mozilla/glean/bindings/GleanPings.h"
189 #include "mozilla/extensions/WebExtensionPolicy.h"
190 #include "mozilla/fallible.h"
191 #include "mozilla/gfx/BasePoint.h"
192 #include "mozilla/gfx/BaseRect.h"
193 #include "mozilla/gfx/BaseSize.h"
194 #include "mozilla/gfx/Rect.h"
195 #include "mozilla/gfx/Types.h"
196 #include "mozilla/intl/LocaleService.h"
197 #include "mozilla/ipc/BackgroundUtils.h"
198 #include "mozilla/ipc/PBackgroundSharedTypes.h"
199 #include "mozilla/net/CookieJarSettings.h"
200 #include "nsAtom.h"
201 #include "nsBaseHashtable.h"
202 #include "nsCCUncollectableMarker.h"
203 #include "nsCOMPtr.h"
204 #include "nsCRT.h"
205 #include "nsCRTGlue.h"
206 #include "nsCanvasFrame.h"
207 #include "nsCharTraits.h"
208 #include "nsCheapSets.h"
209 #include "nsContentUtils.h"
210 #include "nsCoord.h"
211 #include "nsCycleCollectionNoteChild.h"
212 #include "nsCycleCollectionTraversalCallback.h"
213 #include "nsDOMNavigationTiming.h"
214 #include "nsDebug.h"
215 #include "nsDeviceContext.h"
216 #include "nsDocShell.h"
217 #include "nsFocusManager.h"
218 #include "nsFrameMessageManager.h"
219 #include "nsGkAtoms.h"
220 #include "nsGlobalWindowOuter.h"
221 #include "nsHashKeys.h"
222 #include "nsHistory.h"
223 #include "nsIAddonPolicyService.h"
224 #include "nsIArray.h"
225 #include "nsIBaseWindow.h"
226 #include "nsIBrowserChild.h"
227 #include "nsICancelableRunnable.h"
228 #include "nsIChannel.h"
229 #include "nsIClipboard.h"
230 #include "nsIContentSecurityPolicy.h"
231 #include "nsIControllers.h"
232 #include "nsICookieJarSettings.h"
233 #include "nsICookieService.h"
234 #include "nsID.h"
235 #include "nsIDOMStorageManager.h"
236 #include "nsIDeviceSensors.h"
237 #include "nsIDocShell.h"
238 #include "nsIDocShellTreeItem.h"
239 #include "nsIDocShellTreeOwner.h"
240 #include "nsIDocumentLoader.h"
241 #include "nsIDragService.h"
242 #include "nsIFocusManager.h"
243 #include "nsIFrame.h"
244 #include "nsIGlobalObject.h"
245 #include "nsIIOService.h"
246 #include "nsIIdleRunnable.h"
247 #include "nsIInterfaceRequestorUtils.h"
248 #include "nsILoadContext.h"
249 #include "nsILoadGroup.h"
250 #include "nsILoadInfo.h"
251 #include "nsINamed.h"
252 #include "nsINode.h"
253 #include "nsIObserver.h"
254 #include "nsIObserverService.h"
255 #include "nsIPermission.h"
256 #include "nsIPermissionManager.h"
257 #include "nsIPrefBranch.h"
258 #include "nsIPrincipal.h"
259 #include "nsIPrompt.h"
260 #include "nsIRunnable.h"
261 #include "nsIScreen.h"
262 #include "nsIScreenManager.h"
263 #include "nsIScriptContext.h"
264 #include "nsIScriptGlobalObject.h"
265 #include "nsIScriptObjectPrincipal.h"
266 #include "nsIScrollableFrame.h"
267 #include "nsISerialEventTarget.h"
268 #include "nsISimpleEnumerator.h"
269 #include "nsISizeOfEventTarget.h"
270 #include "nsISlowScriptDebug.h"
271 #include "nsISupportsUtils.h"
272 #include "nsIThread.h"
273 #include "nsITimedChannel.h"
274 #include "nsIURI.h"
275 #include "nsIWeakReference.h"
276 #include "nsIWebBrowserChrome.h"
277 #include "nsIWebNavigation.h"
278 #include "nsIWebProgressListener.h"
279 #include "nsIWidget.h"
280 #include "nsIWidgetListener.h"
281 #include "nsIXULRuntime.h"
282 #include "nsJSPrincipals.h"
283 #include "nsJSUtils.h"
284 #include "nsLayoutStatics.h"
285 #include "nsLiteralString.h"
286 #include "nsNetUtil.h"
287 #include "nsPIDOMWindow.h"
288 #include "nsPIDOMWindowInlines.h"
289 #include "nsPIWindowRoot.h"
290 #include "nsPoint.h"
291 #include "nsPresContext.h"
292 #include "nsQueryObject.h"
293 #include "nsSandboxFlags.h"
294 #include "nsScreen.h"
295 #include "nsServiceManagerUtils.h"
296 #include "nsString.h"
297 #include "nsStringFlags.h"
298 #include "nsStringFwd.h"
299 #include "nsTArray.h"
300 #include "nsTLiteralString.h"
301 #include "nsTObserverArray.h"
302 #include "nsTStringRepr.h"
303 #include "nsThreadUtils.h"
304 #include "nsWeakReference.h"
305 #include "nsWindowMemoryReporter.h"
306 #include "nsWindowSizes.h"
307 #include "nsWrapperCache.h"
308 #include "nsWrapperCacheInlines.h"
309 #include "nsXULAppAPI.h"
310 #include "nsrootidl.h"
311 #include "prclist.h"
312 #include "prtypes.h"
313 #include "xpcprivate.h"
314 #include "xpcpublic.h"
316 #include "nsIDOMXULControlElement.h"
318 #ifdef NS_PRINTING
319 # include "nsIPrintSettings.h"
320 #endif
322 #ifdef MOZ_WEBSPEECH
323 # include "mozilla/dom/SpeechSynthesis.h"
324 #endif
326 #ifdef ANDROID
327 # include <android/log.h>
328 #endif
330 #ifdef XP_WIN
331 # include "mozilla/Debug.h"
332 # include <process.h>
333 # define getpid _getpid
334 #else
335 # include <unistd.h> // for getpid()
336 #endif
338 using namespace mozilla;
339 using namespace mozilla::dom;
340 using namespace mozilla::dom::ipc;
341 using mozilla::TimeDuration;
342 using mozilla::TimeStamp;
343 using mozilla::dom::GamepadHandle;
344 using mozilla::dom::cache::CacheStorage;
346 #define FORWARD_TO_OUTER(method, args, err_rval) \
347 PR_BEGIN_MACRO \
348 RefPtr<nsGlobalWindowOuter> outer = GetOuterWindowInternal(); \
349 if (!HasActiveDocument()) { \
350 NS_WARNING(outer ? "Inner window does not have active document." \
351 : "No outer window available!"); \
352 return err_rval; \
354 return outer->method args; \
355 PR_END_MACRO
357 static nsGlobalWindowOuter* GetOuterWindowForForwarding(
358 nsGlobalWindowInner* aInner, ErrorResult& aError) {
359 nsGlobalWindowOuter* outer = aInner->GetOuterWindowInternal();
360 if (MOZ_LIKELY(aInner->HasActiveDocument())) {
361 return outer;
363 if (!outer) {
364 NS_WARNING("No outer window available!");
365 aError.Throw(NS_ERROR_NOT_INITIALIZED);
366 } else {
367 aError.Throw(NS_ERROR_XPC_SECURITY_MANAGER_VETO);
369 return nullptr;
372 #define FORWARD_TO_OUTER_OR_THROW(method, args, rv, err_rval) \
373 PR_BEGIN_MACRO \
374 RefPtr<nsGlobalWindowOuter> outer = GetOuterWindowForForwarding(this, rv); \
375 if (MOZ_LIKELY(outer)) { \
376 return outer->method args; \
378 return err_rval; \
379 PR_END_MACRO
381 #define FORWARD_TO_OUTER_VOID(method, args) \
382 PR_BEGIN_MACRO \
383 RefPtr<nsGlobalWindowOuter> outer = GetOuterWindowInternal(); \
384 if (!HasActiveDocument()) { \
385 NS_WARNING(outer ? "Inner window does not have active document." \
386 : "No outer window available!"); \
387 return; \
389 outer->method args; \
390 return; \
391 PR_END_MACRO
393 #define ENSURE_ACTIVE_DOCUMENT(errorresult, err_rval) \
394 PR_BEGIN_MACRO \
395 if (MOZ_UNLIKELY(!HasActiveDocument())) { \
396 aError.Throw(NS_ERROR_XPC_SECURITY_MANAGER_VETO); \
397 return err_rval; \
399 PR_END_MACRO
401 #define DOM_TOUCH_LISTENER_ADDED "dom-touch-listener-added"
402 #define MEMORY_PRESSURE_OBSERVER_TOPIC "memory-pressure"
403 #define PERMISSION_CHANGED_TOPIC "perm-changed"
405 static LazyLogModule gDOMLeakPRLogInner("DOMLeakInner");
406 extern mozilla::LazyLogModule gTimeoutLog;
408 #ifdef DEBUG
409 static LazyLogModule gDocShellAndDOMWindowLeakLogging(
410 "DocShellAndDOMWindowLeak");
411 #endif
413 static FILE* gDumpFile = nullptr;
415 nsGlobalWindowInner::InnerWindowByIdTable*
416 nsGlobalWindowInner::sInnerWindowsById = nullptr;
418 bool nsGlobalWindowInner::sDragServiceDisabled = false;
419 bool nsGlobalWindowInner::sMouseDown = false;
422 * An indirect observer object that means we don't have to implement nsIObserver
423 * on nsGlobalWindow, where any script could see it.
425 class nsGlobalWindowObserver final : public nsIObserver,
426 public nsIInterfaceRequestor,
427 public StorageNotificationObserver {
428 public:
429 explicit nsGlobalWindowObserver(nsGlobalWindowInner* aWindow)
430 : mWindow(aWindow) {}
431 NS_DECL_ISUPPORTS
432 NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
433 const char16_t* aData) override {
434 if (!mWindow) return NS_OK;
435 return mWindow->Observe(aSubject, aTopic, aData);
437 void Forget() { mWindow = nullptr; }
438 NS_IMETHOD GetInterface(const nsIID& aIID, void** aResult) override {
439 if (mWindow && aIID.Equals(NS_GET_IID(nsIDOMWindow)) && mWindow) {
440 return mWindow->QueryInterface(aIID, aResult);
442 return NS_NOINTERFACE;
445 void ObserveStorageNotification(StorageEvent* aEvent,
446 const char16_t* aStorageType,
447 bool aPrivateBrowsing) override {
448 if (mWindow) {
449 mWindow->ObserveStorageNotification(aEvent, aStorageType,
450 aPrivateBrowsing);
454 nsIPrincipal* GetEffectiveCookiePrincipal() const override {
455 return mWindow ? mWindow->GetEffectiveCookiePrincipal() : nullptr;
458 nsIPrincipal* GetEffectiveStoragePrincipal() const override {
459 return mWindow ? mWindow->GetEffectiveStoragePrincipal() : nullptr;
462 bool IsPrivateBrowsing() const override {
463 return mWindow ? mWindow->IsPrivateBrowsing() : false;
466 nsIEventTarget* GetEventTarget() const override {
467 return mWindow ? mWindow->SerialEventTarget() : nullptr;
470 private:
471 ~nsGlobalWindowObserver() = default;
473 // This reference is non-owning and safe because it's cleared by
474 // nsGlobalWindowInner::FreeInnerObjects().
475 nsGlobalWindowInner* MOZ_NON_OWNING_REF mWindow;
478 NS_IMPL_ISUPPORTS(nsGlobalWindowObserver, nsIObserver, nsIInterfaceRequestor)
480 class IdleRequestExecutor;
482 class IdleRequestExecutorTimeoutHandler final : public TimeoutHandler {
483 public:
484 explicit IdleRequestExecutorTimeoutHandler(IdleRequestExecutor* aExecutor)
485 : mExecutor(aExecutor) {}
487 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
488 NS_DECL_CYCLE_COLLECTION_CLASS(IdleRequestExecutorTimeoutHandler)
490 bool Call(const char* /* unused */) override;
492 private:
493 ~IdleRequestExecutorTimeoutHandler() override = default;
494 RefPtr<IdleRequestExecutor> mExecutor;
497 NS_IMPL_CYCLE_COLLECTION(IdleRequestExecutorTimeoutHandler, mExecutor)
499 NS_IMPL_CYCLE_COLLECTING_ADDREF(IdleRequestExecutorTimeoutHandler)
500 NS_IMPL_CYCLE_COLLECTING_RELEASE(IdleRequestExecutorTimeoutHandler)
502 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestExecutorTimeoutHandler)
503 NS_INTERFACE_MAP_ENTRY(nsISupports)
504 NS_INTERFACE_MAP_END
506 class IdleRequestExecutor final : public nsIRunnable,
507 public nsICancelableRunnable,
508 public nsINamed,
509 public nsIIdleRunnable {
510 public:
511 explicit IdleRequestExecutor(nsGlobalWindowInner* aWindow)
512 : mDispatched(false), mDeadline(TimeStamp::Now()), mWindow(aWindow) {
513 MOZ_DIAGNOSTIC_ASSERT(mWindow);
515 mIdlePeriodLimit = {mDeadline, mWindow->LastIdleRequestHandle()};
516 mDelayedExecutorDispatcher = new IdleRequestExecutorTimeoutHandler(this);
519 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
520 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(IdleRequestExecutor, nsIRunnable)
522 NS_DECL_NSIRUNNABLE
523 NS_DECL_NSINAMED
524 nsresult Cancel() override;
525 void SetDeadline(TimeStamp aDeadline) override;
527 bool IsCancelled() const { return !mWindow || mWindow->IsDying(); }
528 // Checks if aRequest shouldn't execute in the current idle period
529 // since it has been queued from a chained call to
530 // requestIdleCallback from within a running idle callback.
531 bool IneligibleForCurrentIdlePeriod(IdleRequest* aRequest) const {
532 return aRequest->Handle() >= mIdlePeriodLimit.mLastRequestIdInIdlePeriod &&
533 TimeStamp::Now() <= mIdlePeriodLimit.mEndOfIdlePeriod;
536 void MaybeUpdateIdlePeriodLimit();
538 // Maybe dispatch the IdleRequestExecutor. MabyeDispatch will
539 // schedule a delayed dispatch if the associated window is in the
540 // background or if given a time to wait until dispatching.
541 void MaybeDispatch(TimeStamp aDelayUntil = TimeStamp());
542 void ScheduleDispatch();
544 private:
545 struct IdlePeriodLimit {
546 TimeStamp mEndOfIdlePeriod;
547 uint32_t mLastRequestIdInIdlePeriod;
550 void DelayedDispatch(uint32_t aDelay);
552 ~IdleRequestExecutor() override = default;
554 bool mDispatched;
555 TimeStamp mDeadline;
556 IdlePeriodLimit mIdlePeriodLimit;
557 RefPtr<nsGlobalWindowInner> mWindow;
558 // The timeout handler responsible for dispatching this executor in
559 // the case of immediate dispatch to the idle queue isn't
560 // desirable. This is used if we've dispatched all idle callbacks
561 // that are allowed to run in the current idle period, or if the
562 // associated window is currently in the background.
563 RefPtr<TimeoutHandler> mDelayedExecutorDispatcher;
564 // If not Nothing() then this value is the handle to the currently
565 // scheduled delayed executor dispatcher. This is needed to be able
566 // to cancel the timeout handler in case of the executor being
567 // cancelled.
568 Maybe<int32_t> mDelayedExecutorHandle;
571 NS_IMPL_CYCLE_COLLECTION(IdleRequestExecutor, mWindow,
572 mDelayedExecutorDispatcher)
574 NS_IMPL_CYCLE_COLLECTING_ADDREF(IdleRequestExecutor)
575 NS_IMPL_CYCLE_COLLECTING_RELEASE(IdleRequestExecutor)
577 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestExecutor)
578 NS_INTERFACE_MAP_ENTRY(nsIRunnable)
579 NS_INTERFACE_MAP_ENTRY(nsICancelableRunnable)
580 NS_INTERFACE_MAP_ENTRY(nsINamed)
581 NS_INTERFACE_MAP_ENTRY(nsIIdleRunnable)
582 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRunnable)
583 NS_INTERFACE_MAP_END
585 NS_IMETHODIMP
586 IdleRequestExecutor::GetName(nsACString& aName) {
587 aName.AssignLiteral("IdleRequestExecutor");
588 return NS_OK;
591 // MOZ_CAN_RUN_SCRIPT_BOUNDARY until nsIRunnable::Run is MOZ_CAN_RUN_SCRIPT.
592 // See bug 1535398.
593 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP IdleRequestExecutor::Run() {
594 MOZ_ASSERT(NS_IsMainThread());
596 mDispatched = false;
597 if (mWindow) {
598 RefPtr<nsGlobalWindowInner> window(mWindow);
599 window->ExecuteIdleRequest(mDeadline);
602 return NS_OK;
605 nsresult IdleRequestExecutor::Cancel() {
606 MOZ_ASSERT(NS_IsMainThread());
608 if (mDelayedExecutorHandle && mWindow) {
609 mWindow->TimeoutManager().ClearTimeout(
610 mDelayedExecutorHandle.value(), Timeout::Reason::eIdleCallbackTimeout);
613 mWindow = nullptr;
614 return NS_OK;
617 void IdleRequestExecutor::SetDeadline(TimeStamp aDeadline) {
618 MOZ_ASSERT(NS_IsMainThread());
620 if (!mWindow) {
621 return;
624 mDeadline = aDeadline;
627 void IdleRequestExecutor::MaybeUpdateIdlePeriodLimit() {
628 if (TimeStamp::Now() > mIdlePeriodLimit.mEndOfIdlePeriod) {
629 mIdlePeriodLimit = {mDeadline, mWindow->LastIdleRequestHandle()};
633 void IdleRequestExecutor::MaybeDispatch(TimeStamp aDelayUntil) {
634 // If we've already dispatched the executor we don't want to do it
635 // again. Also, if we've called IdleRequestExecutor::Cancel mWindow
636 // will be null, which indicates that we shouldn't dispatch this
637 // executor either.
638 if (mDispatched || IsCancelled()) {
639 return;
642 mDispatched = true;
644 nsPIDOMWindowOuter* outer = mWindow->GetOuterWindow();
645 if (outer && outer->IsBackground()) {
646 // Set a timeout handler with a timeout of 0 ms to throttle idle
647 // callback requests coming from a backround window using
648 // background timeout throttling.
649 DelayedDispatch(0);
650 return;
653 TimeStamp now = TimeStamp::Now();
654 if (!aDelayUntil || aDelayUntil < now) {
655 ScheduleDispatch();
656 return;
659 TimeDuration delay = aDelayUntil - now;
660 DelayedDispatch(static_cast<uint32_t>(delay.ToMilliseconds()));
663 void IdleRequestExecutor::ScheduleDispatch() {
664 MOZ_ASSERT(mWindow);
665 mDelayedExecutorHandle = Nothing();
666 RefPtr<IdleRequestExecutor> request = this;
667 NS_DispatchToCurrentThreadQueue(request.forget(), EventQueuePriority::Idle);
670 void IdleRequestExecutor::DelayedDispatch(uint32_t aDelay) {
671 MOZ_ASSERT(mWindow);
672 MOZ_ASSERT(mDelayedExecutorHandle.isNothing());
673 int32_t handle;
674 mWindow->TimeoutManager().SetTimeout(
675 mDelayedExecutorDispatcher, aDelay, false,
676 Timeout::Reason::eIdleCallbackTimeout, &handle);
677 mDelayedExecutorHandle = Some(handle);
680 bool IdleRequestExecutorTimeoutHandler::Call(const char* /* unused */) {
681 if (!mExecutor->IsCancelled()) {
682 mExecutor->ScheduleDispatch();
684 return true;
687 void nsGlobalWindowInner::ScheduleIdleRequestDispatch() {
688 AssertIsOnMainThread();
690 if (!mIdleRequestExecutor) {
691 mIdleRequestExecutor = new IdleRequestExecutor(this);
694 mIdleRequestExecutor->MaybeDispatch();
697 void nsGlobalWindowInner::SuspendIdleRequests() {
698 if (mIdleRequestExecutor) {
699 mIdleRequestExecutor->Cancel();
700 mIdleRequestExecutor = nullptr;
704 void nsGlobalWindowInner::ResumeIdleRequests() {
705 MOZ_ASSERT(!mIdleRequestExecutor);
707 ScheduleIdleRequestDispatch();
710 void nsGlobalWindowInner::RemoveIdleCallback(
711 mozilla::dom::IdleRequest* aRequest) {
712 AssertIsOnMainThread();
714 if (aRequest->HasTimeout()) {
715 mTimeoutManager->ClearTimeout(aRequest->GetTimeoutHandle(),
716 Timeout::Reason::eIdleCallbackTimeout);
719 aRequest->removeFrom(mIdleRequestCallbacks);
722 void nsGlobalWindowInner::RunIdleRequest(IdleRequest* aRequest,
723 DOMHighResTimeStamp aDeadline,
724 bool aDidTimeout) {
725 AssertIsOnMainThread();
726 // XXXbz Do we still need this RefPtr? MOZ_CAN_RUN_SCRIPT should
727 // guarantee that caller is holding a strong ref on the stack.
728 RefPtr<IdleRequest> request(aRequest);
729 RemoveIdleCallback(request);
730 request->IdleRun(this, aDeadline, aDidTimeout);
733 void nsGlobalWindowInner::ExecuteIdleRequest(TimeStamp aDeadline) {
734 AssertIsOnMainThread();
735 RefPtr<IdleRequest> request = mIdleRequestCallbacks.getFirst();
737 if (!request) {
738 // There are no more idle requests, so stop scheduling idle
739 // request callbacks.
740 return;
743 // If the request that we're trying to execute has been queued
744 // during the current idle period, then dispatch it again at the end
745 // of the idle period.
746 if (mIdleRequestExecutor->IneligibleForCurrentIdlePeriod(request)) {
747 mIdleRequestExecutor->MaybeDispatch(aDeadline);
748 return;
751 DOMHighResTimeStamp deadline = 0.0;
753 if (Performance* perf = GetPerformance()) {
754 deadline = perf->GetDOMTiming()->TimeStampToDOMHighRes(aDeadline);
757 mIdleRequestExecutor->MaybeUpdateIdlePeriodLimit();
758 RunIdleRequest(request, deadline, false);
760 // Running the idle callback could've suspended the window, in which
761 // case mIdleRequestExecutor will be null.
762 if (mIdleRequestExecutor) {
763 mIdleRequestExecutor->MaybeDispatch();
767 class IdleRequestTimeoutHandler final : public TimeoutHandler {
768 public:
769 IdleRequestTimeoutHandler(JSContext* aCx, IdleRequest* aIdleRequest,
770 nsPIDOMWindowInner* aWindow)
771 : TimeoutHandler(aCx), mIdleRequest(aIdleRequest), mWindow(aWindow) {}
773 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
774 NS_DECL_CYCLE_COLLECTION_CLASS(IdleRequestTimeoutHandler)
776 MOZ_CAN_RUN_SCRIPT bool Call(const char* /* unused */) override {
777 RefPtr<nsGlobalWindowInner> window(nsGlobalWindowInner::Cast(mWindow));
778 RefPtr<IdleRequest> request(mIdleRequest);
779 window->RunIdleRequest(request, 0.0, true);
780 return true;
783 private:
784 ~IdleRequestTimeoutHandler() override = default;
786 RefPtr<IdleRequest> mIdleRequest;
787 nsCOMPtr<nsPIDOMWindowInner> mWindow;
790 NS_IMPL_CYCLE_COLLECTION(IdleRequestTimeoutHandler, mIdleRequest, mWindow)
792 NS_IMPL_CYCLE_COLLECTING_ADDREF(IdleRequestTimeoutHandler)
793 NS_IMPL_CYCLE_COLLECTING_RELEASE(IdleRequestTimeoutHandler)
795 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestTimeoutHandler)
796 NS_INTERFACE_MAP_ENTRY(nsISupports)
797 NS_INTERFACE_MAP_END
799 uint32_t nsGlobalWindowInner::RequestIdleCallback(
800 JSContext* aCx, IdleRequestCallback& aCallback,
801 const IdleRequestOptions& aOptions, ErrorResult& aError) {
802 AssertIsOnMainThread();
804 if (IsDying()) {
805 return 0;
808 uint32_t handle = mIdleRequestCallbackCounter++;
810 RefPtr<IdleRequest> request = new IdleRequest(&aCallback, handle);
812 if (aOptions.mTimeout.WasPassed()) {
813 int32_t timeoutHandle;
814 RefPtr<TimeoutHandler> handler(
815 new IdleRequestTimeoutHandler(aCx, request, this));
817 nsresult rv = mTimeoutManager->SetTimeout(
818 handler, aOptions.mTimeout.Value(), false,
819 Timeout::Reason::eIdleCallbackTimeout, &timeoutHandle);
821 if (NS_WARN_IF(NS_FAILED(rv))) {
822 return 0;
825 request->SetTimeoutHandle(timeoutHandle);
828 mIdleRequestCallbacks.insertBack(request);
830 if (!IsSuspended()) {
831 ScheduleIdleRequestDispatch();
834 return handle;
837 void nsGlobalWindowInner::CancelIdleCallback(uint32_t aHandle) {
838 for (IdleRequest* r : mIdleRequestCallbacks) {
839 if (r->Handle() == aHandle) {
840 RemoveIdleCallback(r);
841 break;
846 void nsGlobalWindowInner::DisableIdleCallbackRequests() {
847 if (mIdleRequestExecutor) {
848 mIdleRequestExecutor->Cancel();
849 mIdleRequestExecutor = nullptr;
852 while (!mIdleRequestCallbacks.isEmpty()) {
853 RefPtr<IdleRequest> request = mIdleRequestCallbacks.getFirst();
854 RemoveIdleCallback(request);
858 bool nsGlobalWindowInner::IsBackgroundInternal() const {
859 return !mOuterWindow || mOuterWindow->IsBackground();
862 class PromiseDocumentFlushedResolver final {
863 public:
864 PromiseDocumentFlushedResolver(Promise* aPromise,
865 PromiseDocumentFlushedCallback& aCallback)
866 : mPromise(aPromise), mCallback(&aCallback) {}
868 virtual ~PromiseDocumentFlushedResolver() = default;
870 void Call() {
871 nsMutationGuard guard;
872 ErrorResult error;
873 JS::Rooted<JS::Value> returnVal(RootingCx());
874 mCallback->Call(&returnVal, error);
876 if (error.Failed()) {
877 mPromise->MaybeReject(std::move(error));
878 } else if (guard.Mutated(0)) {
879 // Something within the callback mutated the DOM.
880 mPromise->MaybeRejectWithNoModificationAllowedError(
881 "DOM mutated from promiseDocumentFlushed callbacks");
882 } else {
883 mPromise->MaybeResolve(returnVal);
887 RefPtr<Promise> mPromise;
888 RefPtr<PromiseDocumentFlushedCallback> mCallback;
891 //*****************************************************************************
892 //*** nsGlobalWindowInner: Object Management
893 //*****************************************************************************
895 nsGlobalWindowInner::nsGlobalWindowInner(nsGlobalWindowOuter* aOuterWindow,
896 WindowGlobalChild* aActor)
897 : nsPIDOMWindowInner(aOuterWindow, aActor),
898 mHasOrientationChangeListeners(false),
899 mWasOffline(false),
900 mHasHadSlowScript(false),
901 mIsChrome(false),
902 mCleanMessageManager(false),
903 mNeedsFocus(true),
904 mHasFocus(false),
905 mFocusByKeyOccurred(false),
906 mDidFireDocElemInserted(false),
907 mHasGamepad(false),
908 mHasXRSession(false),
909 mHasVRDisplayActivateEvents(false),
910 mXRRuntimeDetectionInFlight(false),
911 mXRPermissionRequestInFlight(false),
912 mXRPermissionGranted(false),
913 mWasCurrentInnerWindow(false),
914 mHasSeenGamepadInput(false),
915 mHintedWasLoading(false),
916 mHasOpenedExternalProtocolFrame(false),
917 mScrollMarksOnHScrollbar(false),
918 mStorageAllowedReasonCache(0),
919 mSuspendDepth(0),
920 mFreezeDepth(0),
921 #ifdef DEBUG
922 mSerial(0),
923 #endif
924 mFocusMethod(0),
925 mIdleRequestCallbackCounter(1),
926 mIdleRequestExecutor(nullptr),
927 mObservingRefresh(false),
928 mIteratingDocumentFlushedResolvers(false),
929 mCanSkipCCGeneration(0) {
930 mIsInnerWindow = true;
932 AssertIsOnMainThread();
933 SetIsOnMainThread();
934 nsLayoutStatics::AddRef();
936 // Initialize the PRCList (this).
937 PR_INIT_CLIST(this);
939 // add this inner window to the outer window list of inners.
940 PR_INSERT_AFTER(this, aOuterWindow);
942 mTimeoutManager = MakeUnique<dom::TimeoutManager>(
943 *this, StaticPrefs::dom_timeout_max_idle_defer_ms());
945 mObserver = new nsGlobalWindowObserver(this);
946 if (nsCOMPtr<nsIObserverService> os = services::GetObserverService()) {
947 // Watch for online/offline status changes so we can fire events. Use
948 // a strong reference.
949 os->AddObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC, false);
950 os->AddObserver(mObserver, MEMORY_PRESSURE_OBSERVER_TOPIC, false);
951 os->AddObserver(mObserver, PERMISSION_CHANGED_TOPIC, false);
952 os->AddObserver(mObserver, "screen-information-changed", false);
955 Preferences::AddStrongObserver(mObserver, "intl.accept_languages");
957 // Watch for storage notifications so we can fire storage events.
958 RefPtr<StorageNotifierService> sns = StorageNotifierService::GetOrCreate();
959 if (sns) {
960 sns->Register(mObserver);
963 if (XRE_IsContentProcess()) {
964 nsCOMPtr<nsIDocShell> docShell = GetDocShell();
965 if (docShell) {
966 mBrowserChild = docShell->GetBrowserChild();
970 if (gDumpFile == nullptr) {
971 nsAutoCString fname;
972 Preferences::GetCString("browser.dom.window.dump.file", fname);
973 if (!fname.IsEmpty()) {
974 // If this fails to open, Dump() knows to just go to stdout on null.
975 gDumpFile = fopen(fname.get(), "wb+");
976 } else {
977 gDumpFile = stdout;
981 #ifdef DEBUG
982 mSerial = nsContentUtils::InnerOrOuterWindowCreated();
984 MOZ_LOG(gDocShellAndDOMWindowLeakLogging, LogLevel::Info,
985 ("++DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p]\n",
986 nsContentUtils::GetCurrentInnerOrOuterWindowCount(),
987 static_cast<void*>(ToCanonicalSupports(this)), getpid(), mSerial,
988 static_cast<void*>(ToCanonicalSupports(aOuterWindow))));
989 #endif
991 MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
992 ("DOMWINDOW %p created outer=%p", this, aOuterWindow));
994 // Add ourselves to the inner windows list.
995 MOZ_ASSERT(sInnerWindowsById, "Inner Windows hash table must be created!");
996 MOZ_ASSERT(!sInnerWindowsById->Contains(mWindowID),
997 "This window shouldn't be in the hash table yet!");
998 // We seem to see crashes in release builds because of null
999 // |sInnerWindowsById|.
1000 if (sInnerWindowsById) {
1001 sInnerWindowsById->InsertOrUpdate(mWindowID, this);
1005 #ifdef DEBUG
1007 /* static */
1008 void nsGlobalWindowInner::AssertIsOnMainThread() {
1009 MOZ_ASSERT(NS_IsMainThread());
1012 #endif // DEBUG
1014 /* static */
1015 void nsGlobalWindowInner::Init() {
1016 AssertIsOnMainThread();
1018 NS_ASSERTION(gDOMLeakPRLogInner,
1019 "gDOMLeakPRLogInner should have been initialized!");
1021 sInnerWindowsById = new InnerWindowByIdTable();
1024 nsGlobalWindowInner::~nsGlobalWindowInner() {
1025 AssertIsOnMainThread();
1026 MOZ_ASSERT(!mHintedWasLoading);
1028 if (IsChromeWindow()) {
1029 MOZ_ASSERT(mCleanMessageManager,
1030 "chrome windows may always disconnect the msg manager");
1032 DisconnectAndClearGroupMessageManagers();
1034 if (mChromeFields.mMessageManager) {
1035 static_cast<nsFrameMessageManager*>(mChromeFields.mMessageManager.get())
1036 ->Disconnect();
1039 mCleanMessageManager = false;
1042 // In most cases this should already have been called, but call it again
1043 // here to catch any corner cases.
1044 FreeInnerObjects();
1046 if (sInnerWindowsById) {
1047 sInnerWindowsById->Remove(mWindowID);
1050 nsContentUtils::InnerOrOuterWindowDestroyed();
1052 #ifdef DEBUG
1053 if (MOZ_LOG_TEST(gDocShellAndDOMWindowLeakLogging, LogLevel::Info)) {
1054 nsAutoCString url;
1055 if (mLastOpenedURI) {
1056 url = mLastOpenedURI->GetSpecOrDefault();
1058 // Data URLs can be very long, so truncate to avoid flooding the log.
1059 const uint32_t maxURLLength = 1000;
1060 if (url.Length() > maxURLLength) {
1061 url.Truncate(maxURLLength);
1065 nsGlobalWindowOuter* outer = nsGlobalWindowOuter::Cast(mOuterWindow);
1066 MOZ_LOG(
1067 gDocShellAndDOMWindowLeakLogging, LogLevel::Info,
1068 ("--DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p] [url = "
1069 "%s]\n",
1070 nsContentUtils::GetCurrentInnerOrOuterWindowCount(),
1071 static_cast<void*>(ToCanonicalSupports(this)), getpid(), mSerial,
1072 static_cast<void*>(ToCanonicalSupports(outer)), url.get()));
1074 #endif
1075 MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
1076 ("DOMWINDOW %p destroyed", this));
1078 Telemetry::Accumulate(Telemetry::INNERWINDOWS_WITH_MUTATION_LISTENERS,
1079 mMutationBits ? 1 : 0);
1081 // An inner window is destroyed, pull it out of the outer window's
1082 // list if inner windows.
1084 PR_REMOVE_LINK(this);
1086 // If our outer window's inner window is this window, null out the
1087 // outer window's reference to this window that's being deleted.
1088 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
1089 if (outer) {
1090 outer->MaybeClearInnerWindow(this);
1093 // We don't have to leave the tab group if we are an inner window.
1095 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
1096 if (ac) ac->RemoveWindowAsListener(this);
1098 nsLayoutStatics::Release();
1101 // static
1102 void nsGlobalWindowInner::ShutDown() {
1103 AssertIsOnMainThread();
1105 if (gDumpFile && gDumpFile != stdout) {
1106 fclose(gDumpFile);
1108 gDumpFile = nullptr;
1110 delete sInnerWindowsById;
1111 sInnerWindowsById = nullptr;
1114 void nsGlobalWindowInner::FreeInnerObjects() {
1115 if (IsDying()) {
1116 return;
1118 StartDying();
1120 if (mDoc && mDoc->GetWindowContext()) {
1121 // The document is about to lose its window, so this is a good time to send
1122 // our page use counters.
1124 // (We also do this in Document::SetScriptGlobalObject(nullptr), which
1125 // catches most cases of documents losing their window, but not all.)
1126 mDoc->SendPageUseCounters();
1129 // Make sure that this is called before we null out the document and
1130 // other members that the window destroyed observers could
1131 // re-create.
1132 NotifyDOMWindowDestroyed(this);
1133 if (auto* reporter = nsWindowMemoryReporter::Get()) {
1134 reporter->ObserveDOMWindowDetached(this);
1137 // Kill all of the workers for this window.
1138 CancelWorkersForWindow(*this);
1140 for (RefPtr<mozilla::dom::SharedWorker> pinnedWorker :
1141 mSharedWorkers.ForwardRange()) {
1142 pinnedWorker->Close();
1145 if (mTimeoutManager) {
1146 mTimeoutManager->ClearAllTimeouts();
1149 DisableIdleCallbackRequests();
1151 mChromeEventHandler = nullptr;
1153 if (mListenerManager) {
1154 mListenerManager->RemoveAllListeners();
1155 mListenerManager->Disconnect();
1156 mListenerManager = nullptr;
1159 mHistory = nullptr;
1161 if (mNavigator) {
1162 mNavigator->OnNavigation();
1163 mNavigator->Invalidate();
1164 mNavigator = nullptr;
1167 mScreen = nullptr;
1169 if (mDoc) {
1170 // Remember the document's principal, URI, and CSP.
1171 mDocumentPrincipal = mDoc->NodePrincipal();
1172 mDocumentCookiePrincipal = mDoc->EffectiveCookiePrincipal();
1173 mDocumentStoragePrincipal = mDoc->EffectiveStoragePrincipal();
1174 mDocumentPartitionedPrincipal = mDoc->PartitionedPrincipal();
1175 mDocumentURI = mDoc->GetDocumentURI();
1176 mDocBaseURI = mDoc->GetDocBaseURI();
1177 mDocumentCsp = mDoc->GetCsp();
1179 while (mDoc->EventHandlingSuppressed()) {
1180 mDoc->UnsuppressEventHandlingAndFireEvents(false);
1184 // Remove our reference to the document and the document principal.
1185 mFocusedElement = 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 DisconnectGlobalTeardownObservers();
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 if (mPerformance) {
1274 // Since window is dying, nothing is going to be painted
1275 // with meaningful sizes, so these temp data for LCP is
1276 // no longer needed.
1277 static_cast<PerformanceMainThread*>(mPerformance.get())
1278 ->ClearGeneratedTempDataForLCP();
1280 mPerformance = nullptr;
1282 mContentMediaController = nullptr;
1284 if (mWebTaskScheduler) {
1285 mWebTaskScheduler->Disconnect();
1286 mWebTaskScheduler = nullptr;
1289 mTrustedTypePolicyFactory = nullptr;
1291 mSharedWorkers.Clear();
1293 #ifdef MOZ_WEBSPEECH
1294 mSpeechSynthesis = nullptr;
1295 #endif
1297 mGlean = nullptr;
1298 mGleanPings = nullptr;
1300 mParentTarget = nullptr;
1302 if (mCleanMessageManager) {
1303 MOZ_ASSERT(mIsChrome, "only chrome should have msg manager cleaned");
1304 if (mChromeFields.mMessageManager) {
1305 mChromeFields.mMessageManager->Disconnect();
1309 if (mWindowGlobalChild && !mWindowGlobalChild->IsClosed()) {
1310 mWindowGlobalChild->Destroy();
1313 mIntlUtils = nullptr;
1315 HintIsLoading(false);
1318 //*****************************************************************************
1319 // nsGlobalWindowInner::nsISupports
1320 //*****************************************************************************
1322 // QueryInterface implementation for nsGlobalWindowInner
1323 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGlobalWindowInner)
1324 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
1325 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, EventTarget)
1326 NS_INTERFACE_MAP_ENTRY(nsIDOMWindow)
1327 NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
1328 NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObject)
1329 NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
1330 NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget)
1331 NS_INTERFACE_MAP_ENTRY(nsPIDOMWindowInner)
1332 NS_INTERFACE_MAP_ENTRY(mozIDOMWindow)
1333 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
1334 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
1335 NS_INTERFACE_MAP_END
1337 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGlobalWindowInner)
1338 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGlobalWindowInner)
1340 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGlobalWindowInner)
1341 if (tmp->IsBlackForCC(false)) {
1342 if (nsCCUncollectableMarker::InGeneration(tmp->mCanSkipCCGeneration)) {
1343 return true;
1345 tmp->mCanSkipCCGeneration = nsCCUncollectableMarker::sGeneration;
1346 if (EventListenerManager* elm = tmp->GetExistingListenerManager()) {
1347 elm->MarkForCC();
1349 if (tmp->mTimeoutManager) {
1350 tmp->mTimeoutManager->UnmarkGrayTimers();
1352 return true;
1354 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
1356 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsGlobalWindowInner)
1357 return tmp->IsBlackForCC(true);
1358 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
1360 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsGlobalWindowInner)
1361 return tmp->IsBlackForCC(false);
1362 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
1364 NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalWindowInner)
1366 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindowInner)
1367 if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
1368 char name[512];
1369 nsAutoCString uri;
1370 if (tmp->mDoc && tmp->mDoc->GetDocumentURI()) {
1371 uri = tmp->mDoc->GetDocumentURI()->GetSpecOrDefault();
1373 SprintfLiteral(name, "nsGlobalWindowInner # %" PRIu64 " inner %s",
1374 tmp->mWindowID, uri.get());
1375 cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
1376 } else {
1377 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsGlobalWindowInner, tmp->mRefCnt.get())
1380 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNavigator)
1382 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance)
1384 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWebTaskScheduler)
1386 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTrustedTypePolicyFactory)
1388 #ifdef MOZ_WEBSPEECH
1389 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSpeechSynthesis)
1390 #endif
1392 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlean)
1393 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGleanPings)
1395 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOuterWindow)
1397 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTopInnerWindow)
1399 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager)
1401 if (tmp->mTimeoutManager) {
1402 tmp->mTimeoutManager->ForEachUnorderedTimeout([&cb](Timeout* timeout) {
1403 cb.NoteNativeChild(timeout, NS_CYCLE_COLLECTION_PARTICIPANT(Timeout));
1407 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocation)
1408 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHistory)
1409 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCustomElements)
1410 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSharedWorkers)
1411 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStorage)
1412 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSessionStorage)
1413 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndexedDB)
1414 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPrincipal)
1415 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentCookiePrincipal)
1416 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentStoragePrincipal)
1417 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPartitionedPrincipal)
1418 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentCsp)
1419 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowserChild)
1420 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDoc)
1422 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleRequestExecutor)
1423 for (IdleRequest* request : tmp->mIdleRequestCallbacks) {
1424 cb.NoteNativeChild(request, NS_CYCLE_COLLECTION_PARTICIPANT(IdleRequest));
1427 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mClientSource)
1429 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGamepads)
1431 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCacheStorage)
1432 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVRDisplays)
1434 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDebuggerNotificationManager)
1436 // Traverse stuff from nsPIDOMWindow
1437 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeEventHandler)
1438 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentTarget)
1439 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFocusedElement)
1440 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowsingContext)
1441 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindowGlobalChild)
1443 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMenubar)
1444 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mToolbar)
1445 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocationbar)
1446 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPersonalbar)
1447 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStatusbar)
1448 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScrollbars)
1449 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCrypto)
1450 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsole)
1451 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPaintWorklet)
1452 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExternal)
1453 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInstallTrigger)
1454 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIntlUtils)
1455 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVisualViewport)
1456 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCurrentPasteDataTransfer)
1458 tmp->TraverseObjectsInGlobal(cb);
1460 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeFields.mMessageManager)
1461 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeFields.mGroupMessageManagers)
1463 for (size_t i = 0; i < tmp->mDocumentFlushedResolvers.Length(); i++) {
1464 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentFlushedResolvers[i]->mPromise);
1465 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentFlushedResolvers[i]->mCallback);
1468 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1470 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindowInner)
1471 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
1472 if (sInnerWindowsById) {
1473 sInnerWindowsById->Remove(tmp->mWindowID);
1476 JSObject* wrapper = tmp->GetWrapperPreserveColor();
1477 if (wrapper) {
1478 // Mark our realm as dead, so the JS engine won't hand out our
1479 // global after this point.
1480 JS::SetRealmNonLive(js::GetNonCCWObjectRealm(wrapper));
1483 NS_IMPL_CYCLE_COLLECTION_UNLINK(mNavigator)
1485 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance)
1487 if (tmp->mWebTaskScheduler) {
1488 tmp->mWebTaskScheduler->Disconnect();
1489 NS_IMPL_CYCLE_COLLECTION_UNLINK(mWebTaskScheduler)
1492 NS_IMPL_CYCLE_COLLECTION_UNLINK(mTrustedTypePolicyFactory)
1494 #ifdef MOZ_WEBSPEECH
1495 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSpeechSynthesis)
1496 #endif
1498 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlean)
1499 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGleanPings)
1501 if (tmp->mOuterWindow) {
1502 nsGlobalWindowOuter::Cast(tmp->mOuterWindow)->MaybeClearInnerWindow(tmp);
1503 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOuterWindow)
1506 if (tmp->mListenerManager) {
1507 tmp->mListenerManager->Disconnect();
1508 NS_IMPL_CYCLE_COLLECTION_UNLINK(mListenerManager)
1511 // Here the Timeouts list would've been unlinked, but we rely on
1512 // that Timeout objects have been traced and will remove themselves
1513 // while unlinking.
1515 tmp->UpdateTopInnerWindow();
1516 NS_IMPL_CYCLE_COLLECTION_UNLINK(mTopInnerWindow)
1518 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocation)
1519 NS_IMPL_CYCLE_COLLECTION_UNLINK(mHistory)
1520 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCustomElements)
1521 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSharedWorkers)
1522 if (tmp->mLocalStorage) {
1523 tmp->mLocalStorage->Disconnect();
1524 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalStorage)
1526 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSessionStorage)
1527 NS_IMPL_CYCLE_COLLECTION_UNLINK(mIndexedDB)
1528 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPrincipal)
1529 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentCookiePrincipal)
1530 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentStoragePrincipal)
1531 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPartitionedPrincipal)
1532 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentCsp)
1533 NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowserChild)
1534 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDoc)
1536 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGamepads)
1538 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCacheStorage)
1539 NS_IMPL_CYCLE_COLLECTION_UNLINK(mVRDisplays)
1541 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDebuggerNotificationManager)
1543 // Unlink stuff from nsPIDOMWindow
1544 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeEventHandler)
1545 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParentTarget)
1546 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFocusedElement)
1547 NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowsingContext)
1549 MOZ_DIAGNOSTIC_ASSERT(
1550 !tmp->mWindowGlobalChild || tmp->mWindowGlobalChild->IsClosed(),
1551 "How are we unlinking a window before its actor has been destroyed?");
1552 NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindowGlobalChild)
1554 NS_IMPL_CYCLE_COLLECTION_UNLINK(mMenubar)
1555 NS_IMPL_CYCLE_COLLECTION_UNLINK(mToolbar)
1556 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocationbar)
1557 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPersonalbar)
1558 NS_IMPL_CYCLE_COLLECTION_UNLINK(mStatusbar)
1559 NS_IMPL_CYCLE_COLLECTION_UNLINK(mScrollbars)
1560 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCrypto)
1561 NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole)
1562 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPaintWorklet)
1563 NS_IMPL_CYCLE_COLLECTION_UNLINK(mExternal)
1564 NS_IMPL_CYCLE_COLLECTION_UNLINK(mInstallTrigger)
1565 NS_IMPL_CYCLE_COLLECTION_UNLINK(mIntlUtils)
1566 NS_IMPL_CYCLE_COLLECTION_UNLINK(mVisualViewport)
1567 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCurrentPasteDataTransfer)
1569 tmp->UnlinkObjectsInGlobal();
1571 NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleRequestExecutor)
1573 // Here the IdleRequest list would've been unlinked, but we rely on
1574 // that IdleRequest objects have been traced and will remove
1575 // themselves while unlinking.
1577 NS_IMPL_CYCLE_COLLECTION_UNLINK(mClientSource)
1579 if (tmp->IsChromeWindow()) {
1580 if (tmp->mChromeFields.mMessageManager) {
1581 static_cast<nsFrameMessageManager*>(
1582 tmp->mChromeFields.mMessageManager.get())
1583 ->Disconnect();
1584 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeFields.mMessageManager)
1586 tmp->DisconnectAndClearGroupMessageManagers();
1587 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeFields.mGroupMessageManagers)
1590 for (size_t i = 0; i < tmp->mDocumentFlushedResolvers.Length(); i++) {
1591 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentFlushedResolvers[i]->mPromise);
1592 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentFlushedResolvers[i]->mCallback);
1594 tmp->mDocumentFlushedResolvers.Clear();
1596 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
1597 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1599 #ifdef DEBUG
1600 void nsGlobalWindowInner::RiskyUnlink() {
1601 NS_CYCLE_COLLECTION_INNERNAME.Unlink(this);
1603 #endif
1605 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGlobalWindowInner)
1606 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
1607 NS_IMPL_CYCLE_COLLECTION_TRACE_END
1609 bool nsGlobalWindowInner::IsBlackForCC(bool aTracingNeeded) {
1610 if (!nsCCUncollectableMarker::sGeneration) {
1611 return false;
1614 return (nsCCUncollectableMarker::InGeneration(GetMarkedCCGeneration()) ||
1615 HasKnownLiveWrapper()) &&
1616 (!aTracingNeeded || HasNothingToTrace(ToSupports(this)));
1619 //*****************************************************************************
1620 // nsGlobalWindowInner::nsIScriptGlobalObject
1621 //*****************************************************************************
1623 bool nsGlobalWindowInner::ShouldResistFingerprinting(RFPTarget aTarget) const {
1624 if (mDoc) {
1625 return mDoc->ShouldResistFingerprinting(aTarget);
1627 return nsContentUtils::ShouldResistFingerprinting(
1628 "If we do not have a document then we do not have any context"
1629 "to make an informed RFP choice, so we fall back to the global pref",
1630 aTarget);
1633 OriginTrials nsGlobalWindowInner::Trials() const {
1634 return OriginTrials::FromWindow(this);
1637 FontFaceSet* nsGlobalWindowInner::GetFonts() {
1638 if (mDoc) {
1639 return mDoc->Fonts();
1641 return nullptr;
1644 mozilla::Result<mozilla::ipc::PrincipalInfo, nsresult>
1645 nsGlobalWindowInner::GetStorageKey() {
1646 MOZ_ASSERT(NS_IsMainThread());
1648 nsIPrincipal* principal = GetEffectiveStoragePrincipal();
1649 if (!principal) {
1650 return mozilla::Err(NS_ERROR_FAILURE);
1653 mozilla::ipc::PrincipalInfo principalInfo;
1654 nsresult rv = PrincipalToPrincipalInfo(principal, &principalInfo);
1655 if (NS_FAILED(rv)) {
1656 return mozilla::Err(rv);
1659 // Block expanded and null principals, let content and system through.
1660 if (principalInfo.type() !=
1661 mozilla::ipc::PrincipalInfo::TContentPrincipalInfo &&
1662 principalInfo.type() !=
1663 mozilla::ipc::PrincipalInfo::TSystemPrincipalInfo) {
1664 return Err(NS_ERROR_DOM_SECURITY_ERR);
1667 return std::move(principalInfo);
1670 mozilla::dom::StorageManager* nsGlobalWindowInner::GetStorageManager() {
1671 return Navigator()->Storage();
1674 // https://html.spec.whatwg.org/multipage/web-messaging.html#eligible-for-messaging
1675 // * a Window object whose associated Document is fully active
1676 bool nsGlobalWindowInner::IsEligibleForMessaging() { return IsFullyActive(); }
1678 nsresult nsGlobalWindowInner::EnsureScriptEnvironment() {
1679 // NOTE: We can't use FORWARD_TO_OUTER here because we don't want to fail if
1680 // we're called on an inactive inner window.
1681 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
1682 if (!outer) {
1683 NS_WARNING("No outer window available!");
1684 return NS_ERROR_FAILURE;
1686 return outer->EnsureScriptEnvironment();
1689 nsIScriptContext* nsGlobalWindowInner::GetScriptContext() {
1690 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
1691 if (!outer) {
1692 return nullptr;
1694 return outer->GetScriptContext();
1697 void nsGlobalWindowInner::TraceGlobalJSObject(JSTracer* aTrc) {
1698 TraceWrapper(aTrc, "active window global");
1701 void nsGlobalWindowInner::UpdateAutoplayPermission() {
1702 if (!GetWindowContext()) {
1703 return;
1705 uint32_t perm =
1706 media::AutoplayPolicy::GetSiteAutoplayPermission(GetPrincipal());
1707 if (GetWindowContext()->GetAutoplayPermission() == perm) {
1708 return;
1711 // Setting autoplay permission on a discarded context has no effect.
1712 Unused << GetWindowContext()->SetAutoplayPermission(perm);
1715 void nsGlobalWindowInner::UpdateShortcutsPermission() {
1716 if (!GetWindowContext() ||
1717 !GetWindowContext()->GetBrowsingContext()->IsTop()) {
1718 // We only cache the shortcuts permission on top-level WindowContexts
1719 // since we always check the top-level principal for the permission.
1720 return;
1723 uint32_t perm = GetShortcutsPermission(GetPrincipal());
1725 if (GetWindowContext()->GetShortcutsPermission() == perm) {
1726 return;
1729 // If the WindowContext is discarded this has no effect.
1730 Unused << GetWindowContext()->SetShortcutsPermission(perm);
1733 /* static */
1734 uint32_t nsGlobalWindowInner::GetShortcutsPermission(nsIPrincipal* aPrincipal) {
1735 uint32_t perm = nsIPermissionManager::DENY_ACTION;
1736 nsCOMPtr<nsIPermissionManager> permMgr =
1737 mozilla::components::PermissionManager::Service();
1738 if (aPrincipal && permMgr) {
1739 permMgr->TestExactPermissionFromPrincipal(aPrincipal, "shortcuts"_ns,
1740 &perm);
1742 return perm;
1745 void nsGlobalWindowInner::UpdatePopupPermission() {
1746 if (!GetWindowContext()) {
1747 return;
1750 uint32_t perm = PopupBlocker::GetPopupPermission(GetPrincipal());
1751 if (GetWindowContext()->GetPopupPermission() == perm) {
1752 return;
1755 // If the WindowContext is discarded this has no effect.
1756 Unused << GetWindowContext()->SetPopupPermission(perm);
1759 void nsGlobalWindowInner::UpdatePermissions() {
1760 if (!GetWindowContext()) {
1761 return;
1764 nsCOMPtr<nsIPrincipal> principal = GetPrincipal();
1765 RefPtr<WindowContext> windowContext = GetWindowContext();
1767 WindowContext::Transaction txn;
1768 txn.SetAutoplayPermission(
1769 media::AutoplayPolicy::GetSiteAutoplayPermission(principal));
1770 txn.SetPopupPermission(PopupBlocker::GetPopupPermission(principal));
1772 if (windowContext->IsTop()) {
1773 txn.SetShortcutsPermission(GetShortcutsPermission(principal));
1776 // Setting permissions on a discarded WindowContext has no effect
1777 Unused << txn.Commit(windowContext);
1780 void nsGlobalWindowInner::InitDocumentDependentState(JSContext* aCx) {
1781 MOZ_ASSERT(mDoc);
1783 if (MOZ_LOG_TEST(gDOMLeakPRLogInner, LogLevel::Debug)) {
1784 nsIURI* uri = mDoc->GetDocumentURI();
1785 MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
1786 ("DOMWINDOW %p SetNewDocument %s", this,
1787 uri ? uri->GetSpecOrDefault().get() : ""));
1790 mFocusedElement = nullptr;
1791 mLocalStorage = nullptr;
1792 mSessionStorage = nullptr;
1793 mPerformance = nullptr;
1794 if (mWebTaskScheduler) {
1795 mWebTaskScheduler->Disconnect();
1796 mWebTaskScheduler = nullptr;
1799 // This must be called after nullifying the internal objects because here we
1800 // could recreate them, calling the getter methods, and store them into the JS
1801 // slots. If we nullify them after, the slot values and the objects will be
1802 // out of sync.
1803 ClearDocumentDependentSlots(aCx);
1805 if (!mWindowGlobalChild) {
1806 mWindowGlobalChild = WindowGlobalChild::Create(this);
1808 MOZ_ASSERT(!GetWindowContext()->HasBeenUserGestureActivated(),
1809 "WindowContext should always not have user gesture activation at "
1810 "this point.");
1812 UpdatePermissions();
1814 RefPtr<PermissionDelegateHandler> permDelegateHandler =
1815 mDoc->GetPermissionDelegateHandler();
1817 if (permDelegateHandler) {
1818 permDelegateHandler->PopulateAllDelegatedPermissions();
1821 #if defined(MOZ_WIDGET_ANDROID)
1822 // When we insert the new document to the window in the top-level browsing
1823 // context, we should reset the status of the request which is used for the
1824 // previous document.
1825 if (mWindowGlobalChild && GetBrowsingContext() &&
1826 !GetBrowsingContext()->GetParent()) {
1827 // Return value of setting synced field should be checked. See bug 1656492.
1828 Unused << GetBrowsingContext()->ResetGVAutoplayRequestStatus();
1830 #endif
1832 #ifdef DEBUG
1833 mLastOpenedURI = mDoc->GetDocumentURI();
1834 #endif
1836 Telemetry::Accumulate(Telemetry::INNERWINDOWS_WITH_MUTATION_LISTENERS,
1837 mMutationBits ? 1 : 0);
1839 // Clear our mutation bitfield.
1840 mMutationBits = 0;
1843 nsresult nsGlobalWindowInner::EnsureClientSource() {
1844 MOZ_DIAGNOSTIC_ASSERT(mDoc);
1846 bool newClientSource = false;
1848 // Get the load info for the document if we performed a load. Be careful not
1849 // to look at local URLs, though. Local URLs are those that have a scheme of:
1850 // * about:
1851 // * data:
1852 // * blob:
1853 // We also do an additional check here so that we only treat about:blank
1854 // and about:srcdoc as local URLs. Other internal firefox about: URLs should
1855 // not be treated this way.
1856 nsCOMPtr<nsILoadInfo> loadInfo;
1857 nsCOMPtr<nsIChannel> channel = mDoc->GetChannel();
1858 if (channel) {
1859 nsCOMPtr<nsIURI> uri;
1860 Unused << channel->GetURI(getter_AddRefs(uri));
1862 bool ignoreLoadInfo = false;
1864 // Note, this is mostly copied from NS_IsAboutBlank(). Its duplicated
1865 // here so we can efficiently check about:srcdoc as well.
1866 if (uri->SchemeIs("about")) {
1867 nsCString spec = uri->GetSpecOrDefault();
1868 ignoreLoadInfo = spec.EqualsLiteral("about:blank") ||
1869 spec.EqualsLiteral("about:srcdoc");
1870 } else {
1871 // Its not an about: URL, so now check for our other URL types.
1872 ignoreLoadInfo = uri->SchemeIs("data") || uri->SchemeIs("blob");
1875 if (!ignoreLoadInfo) {
1876 loadInfo = channel->LoadInfo();
1880 // Take the initial client source from the docshell immediately. Even if we
1881 // don't end up using it here we should consume it.
1882 UniquePtr<ClientSource> initialClientSource;
1883 nsIDocShell* docshell = GetDocShell();
1884 if (docshell) {
1885 initialClientSource = docshell->TakeInitialClientSource();
1888 // Try to get the reserved client from the LoadInfo. A Client is
1889 // reserved at the start of the channel load if there is not an
1890 // initial about:blank document that will be reused. It is also
1891 // created if the channel load encounters a cross-origin redirect.
1892 if (loadInfo) {
1893 UniquePtr<ClientSource> reservedClient =
1894 loadInfo->TakeReservedClientSource();
1895 if (reservedClient) {
1896 mClientSource.reset();
1897 mClientSource = std::move(reservedClient);
1898 newClientSource = true;
1902 // We don't have a LoadInfo reserved client, but maybe we should
1903 // be inheriting an initial one from the docshell. This means
1904 // that the docshell started the channel load before creating the
1905 // initial about:blank document. This is an optimization, though,
1906 // and it created an initial Client as a placeholder for the document.
1907 // In this case we want to inherit this placeholder Client here.
1908 if (!mClientSource) {
1909 mClientSource = std::move(initialClientSource);
1910 if (mClientSource) {
1911 newClientSource = true;
1915 nsCOMPtr<nsIPrincipal> foreignPartitionedPrincipal;
1917 nsresult rv = StoragePrincipalHelper::GetPrincipal(
1918 this,
1919 StaticPrefs::privacy_partition_serviceWorkers()
1920 ? StoragePrincipalHelper::eForeignPartitionedPrincipal
1921 : StoragePrincipalHelper::eRegularPrincipal,
1922 getter_AddRefs(foreignPartitionedPrincipal));
1923 NS_ENSURE_SUCCESS(rv, rv);
1925 // Verify the final ClientSource principal matches the final document
1926 // principal. The ClientChannelHelper handles things like network
1927 // redirects, but there are other ways the document principal can change.
1928 // For example, if something sets the nsIChannel.owner property, then
1929 // the final channel principal can be anything. Unfortunately there is
1930 // no good way to detect this until after the channel completes loading.
1932 // For now we handle this just by reseting the ClientSource. This will
1933 // result in a new ClientSource with the correct principal being created.
1934 // To APIs like ServiceWorker and Clients API it will look like there was
1935 // an initial content page created that was then immediately replaced.
1936 // This is pretty close to what we are actually doing.
1937 if (mClientSource) {
1938 auto principalOrErr = mClientSource->Info().GetPrincipal();
1939 nsCOMPtr<nsIPrincipal> clientPrincipal =
1940 principalOrErr.isOk() ? principalOrErr.unwrap() : nullptr;
1941 if (!clientPrincipal ||
1942 !clientPrincipal->Equals(foreignPartitionedPrincipal)) {
1943 mClientSource.reset();
1947 // If we don't have a reserved client or an initial client, then create
1948 // one now. This can happen in certain cases where we avoid preallocating
1949 // the client in the docshell. This mainly occurs in situations where
1950 // the principal is not clearly inherited from the parent; e.g. sandboxed
1951 // iframes, window.open(), etc.
1953 // We also do this late ClientSource creation if the final document ended
1954 // up with a different principal.
1956 // TODO: We may not be marking initial about:blank documents created
1957 // this way as controlled by a service worker properly. The
1958 // controller should be coming from the same place as the inheritted
1959 // principal. We do this in docshell, but as mentioned we aren't
1960 // smart enough to handle all cases yet. For example, a
1961 // window.open() with new URL should inherit the controller from
1962 // the opener, but we probably don't handle that yet.
1963 if (!mClientSource) {
1964 mClientSource = ClientManager::CreateSource(
1965 ClientType::Window, SerialEventTarget(), foreignPartitionedPrincipal);
1966 MOZ_DIAGNOSTIC_ASSERT(mClientSource);
1967 newClientSource = true;
1969 // Note, we don't apply the loadinfo controller below if we create
1970 // the ClientSource here.
1973 // The load may have started controlling the Client as well. If
1974 // so, mark it as controlled immediately here. The actor may
1975 // or may not have been notified by the parent side about being
1976 // controlled yet.
1978 // Note: We should be careful not to control a client that was created late.
1979 // These clients were not seen by the ServiceWorkerManager when it
1980 // marked the LoadInfo controlled and it won't know about them. Its
1981 // also possible we are creating the client late due to the final
1982 // principal changing and these clients should definitely not be
1983 // controlled by a service worker with a different principal.
1984 else if (loadInfo) {
1985 const Maybe<ServiceWorkerDescriptor> controller = loadInfo->GetController();
1986 if (controller.isSome()) {
1987 mClientSource->SetController(controller.ref());
1990 // We also have to handle the case where te initial about:blank is
1991 // controlled due to inheritting the service worker from its parent,
1992 // but the actual nsIChannel load is not covered by any service worker.
1993 // In this case we want the final page to be uncontrolled. There is
1994 // an open spec issue about how exactly this should be handled, but for
1995 // now we just force creation of a new ClientSource to clear the
1996 // controller.
1998 // https://github.com/w3c/ServiceWorker/issues/1232
2000 else if (mClientSource->GetController().isSome()) {
2001 mClientSource.reset();
2002 mClientSource = ClientManager::CreateSource(
2003 ClientType::Window, SerialEventTarget(), foreignPartitionedPrincipal);
2004 MOZ_DIAGNOSTIC_ASSERT(mClientSource);
2005 newClientSource = true;
2009 if (mClientSource) {
2010 // Generally the CSP is stored within the Client and cached on the document.
2011 // At the time of CSP parsing however, the Client has not been created yet,
2012 // hence we store the CSP on the document and propagate/sync the CSP with
2013 // Client here when we create the Client.
2014 mClientSource->SetCsp(mDoc->GetCsp());
2016 DocGroup* docGroup = GetDocGroup();
2017 MOZ_DIAGNOSTIC_ASSERT(docGroup);
2018 mClientSource->SetAgentClusterId(docGroup->AgentClusterId());
2020 if (mWindowGlobalChild) {
2021 mWindowGlobalChild->SendSetClientInfo(mClientSource->Info().ToIPC());
2025 // Its possible that we got a client just after being frozen in
2026 // the bfcache. In that case freeze the client immediately.
2027 if (newClientSource && IsFrozen()) {
2028 mClientSource->Freeze();
2031 return NS_OK;
2034 nsresult nsGlobalWindowInner::ExecutionReady() {
2035 nsresult rv = EnsureClientSource();
2036 NS_ENSURE_SUCCESS(rv, rv);
2038 rv = mClientSource->WindowExecutionReady(this);
2039 NS_ENSURE_SUCCESS(rv, rv);
2041 return NS_OK;
2044 void nsGlobalWindowInner::UpdateParentTarget() {
2045 // NOTE: This method is identical to
2046 // nsGlobalWindowOuter::UpdateParentTarget(). IF YOU UPDATE THIS METHOD,
2047 // UPDATE THE OTHER ONE TOO!
2049 // Try to get our frame element's tab child global (its in-process message
2050 // manager). If that fails, fall back to the chrome event handler's tab
2051 // child global, and if it doesn't have one, just use the chrome event
2052 // handler itself.
2054 nsPIDOMWindowOuter* outer = GetOuterWindow();
2055 if (!outer) {
2056 return;
2058 nsCOMPtr<Element> frameElement = outer->GetFrameElementInternal();
2059 nsCOMPtr<EventTarget> eventTarget =
2060 nsContentUtils::TryGetBrowserChildGlobal(frameElement);
2062 if (!eventTarget) {
2063 nsGlobalWindowOuter* topWin = GetInProcessScriptableTopInternal();
2064 if (topWin) {
2065 frameElement = topWin->GetFrameElementInternal();
2066 eventTarget = nsContentUtils::TryGetBrowserChildGlobal(frameElement);
2070 if (!eventTarget) {
2071 eventTarget = nsContentUtils::TryGetBrowserChildGlobal(mChromeEventHandler);
2074 if (!eventTarget) {
2075 eventTarget = mChromeEventHandler;
2078 mParentTarget = eventTarget;
2081 EventTarget* nsGlobalWindowInner::GetTargetForDOMEvent() {
2082 return GetOuterWindowInternal();
2085 void nsGlobalWindowInner::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
2086 EventMessage msg = aVisitor.mEvent->mMessage;
2088 aVisitor.mCanHandle = true;
2089 aVisitor.mForceContentDispatch = true; // FIXME! Bug 329119
2090 if (msg == eResize && aVisitor.mEvent->IsTrusted()) {
2091 // Checking whether the event target is an inner window or not, so we can
2092 // keep the old behavior also in case a child window is handling resize.
2093 if (aVisitor.mEvent->mOriginalTarget &&
2094 aVisitor.mEvent->mOriginalTarget->IsInnerWindow()) {
2095 mIsHandlingResizeEvent = true;
2097 } else if (msg == eMouseDown && aVisitor.mEvent->IsTrusted()) {
2098 sMouseDown = true;
2099 } else if ((msg == eMouseUp || msg == eDragEnd) &&
2100 aVisitor.mEvent->IsTrusted()) {
2101 sMouseDown = false;
2102 if (sDragServiceDisabled) {
2103 nsCOMPtr<nsIDragService> ds =
2104 do_GetService("@mozilla.org/widget/dragservice;1");
2105 if (ds) {
2106 sDragServiceDisabled = false;
2107 ds->Unsuppress();
2112 aVisitor.SetParentTarget(GetParentTarget(), true);
2115 void nsGlobalWindowInner::FireFrameLoadEvent() {
2116 // If we're not in a content frame, or are at a BrowsingContext tree boundary,
2117 // such as the content-chrome boundary, don't fire the "load" event.
2118 if (GetBrowsingContext()->IsTopContent() ||
2119 GetBrowsingContext()->IsChrome()) {
2120 return;
2123 // If embedder is same-process, fire the event on our embedder element.
2125 // XXX: Bug 1440212 is looking into potentially changing this behaviour to act
2126 // more like the remote case when in-process.
2127 RefPtr<Element> element = GetBrowsingContext()->GetEmbedderElement();
2128 if (element) {
2129 nsEventStatus status = nsEventStatus_eIgnore;
2130 WidgetEvent event(/* aIsTrusted = */ true, eLoad);
2131 event.mFlags.mBubbles = false;
2132 event.mFlags.mCancelable = false;
2134 // Most of the time we could get a pres context to pass in here, but not
2135 // always (i.e. if this window is not shown there won't be a pres context
2136 // available). Since we're not firing a GUI event we don't need a pres
2137 // context anyway so we just pass null as the pres context all the time.
2138 EventDispatcher::Dispatch(element, nullptr, &event, nullptr, &status);
2139 return;
2142 // We don't have an in-process embedder. Try to get our `BrowserChild` actor
2143 // to send a message to that embedder. We want to double-check that our outer
2144 // window is actually the one at the root of this browserChild though, just in
2145 // case.
2146 RefPtr<BrowserChild> browserChild =
2147 BrowserChild::GetFrom(static_cast<nsPIDOMWindowInner*>(this));
2148 if (browserChild &&
2149 !GetBrowsingContext()->GetParentWindowContext()->IsInProcess()) {
2150 // Double-check that our outer window is actually at the root of this
2151 // `BrowserChild`, in case we're in an odd maybe-unhosted situation like a
2152 // print preview dialog.
2153 nsCOMPtr<nsPIDOMWindowOuter> rootOuter =
2154 do_GetInterface(browserChild->WebNavigation());
2155 if (!rootOuter || rootOuter != GetOuterWindow()) {
2156 return;
2159 mozilla::Unused << browserChild->SendMaybeFireEmbedderLoadEvents(
2160 EmbedderElementEventType::LoadEvent);
2164 nsresult nsGlobalWindowInner::PostHandleEvent(EventChainPostVisitor& aVisitor) {
2165 // Return early if there is nothing to do.
2166 switch (aVisitor.mEvent->mMessage) {
2167 case eResize:
2168 case eUnload:
2169 case eLoad:
2170 break;
2171 default:
2172 return NS_OK;
2175 /* mChromeEventHandler and mContext go dangling in the middle of this
2176 function under some circumstances (events that destroy the window)
2177 without this addref. */
2178 RefPtr<EventTarget> kungFuDeathGrip1(mChromeEventHandler);
2179 mozilla::Unused
2180 << kungFuDeathGrip1; // These aren't referred to through the function
2181 nsCOMPtr<nsIScriptContext> kungFuDeathGrip2(GetContextInternal());
2182 mozilla::Unused
2183 << kungFuDeathGrip2; // These aren't referred to through the function
2185 if (aVisitor.mEvent->mMessage == eResize) {
2186 mIsHandlingResizeEvent = false;
2187 } else if (aVisitor.mEvent->mMessage == eUnload &&
2188 aVisitor.mEvent->IsTrusted()) {
2189 // If any VR display presentation is active at unload, the next page
2190 // will receive a vrdisplayactive event to indicate that it should
2191 // immediately begin vr presentation. This should occur when navigating
2192 // forwards, navigating backwards, and on page reload.
2193 for (const auto& display : mVRDisplays) {
2194 if (display->IsPresenting()) {
2195 display->StartVRNavigation();
2196 // Save this VR display ID to trigger vrdisplayactivate event
2197 // after the next load event.
2198 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
2199 if (outer) {
2200 outer->SetAutoActivateVRDisplayID(display->DisplayId());
2203 // XXX The WebVR 1.1 spec does not define which of multiple VR
2204 // presenting VR displays will be chosen during navigation.
2205 // As the underlying platform VR API's currently only allow a single
2206 // VR display, it is safe to choose the first VR display for now.
2207 break;
2210 mIsDocumentLoaded = false;
2211 // Tell the parent process that the document is not loaded.
2212 if (mWindowGlobalChild) {
2213 mWindowGlobalChild->SendUpdateDocumentHasLoaded(mIsDocumentLoaded);
2215 } else if (aVisitor.mEvent->mMessage == eLoad &&
2216 aVisitor.mEvent->IsTrusted()) {
2217 // This is page load event since load events don't propagate to |window|.
2218 // @see Document::GetEventTargetParent.
2219 mIsDocumentLoaded = true;
2220 // Tell the parent process that the document is loaded.
2221 if (mWindowGlobalChild) {
2222 mWindowGlobalChild->SendUpdateDocumentHasLoaded(mIsDocumentLoaded);
2225 mTimeoutManager->OnDocumentLoaded();
2227 MOZ_ASSERT(aVisitor.mEvent->IsTrusted());
2228 FireFrameLoadEvent();
2230 if (mVREventObserver) {
2231 mVREventObserver->NotifyAfterLoad();
2234 uint32_t autoActivateVRDisplayID = 0;
2235 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
2236 if (outer) {
2237 autoActivateVRDisplayID = outer->GetAutoActivateVRDisplayID();
2239 if (autoActivateVRDisplayID) {
2240 DispatchVRDisplayActivate(autoActivateVRDisplayID,
2241 VRDisplayEventReason::Navigation);
2245 return NS_OK;
2248 nsresult nsGlobalWindowInner::DefineArgumentsProperty(nsIArray* aArguments) {
2249 nsIScriptContext* ctx = GetOuterWindowInternal()->mContext;
2250 NS_ENSURE_TRUE(aArguments && ctx, NS_ERROR_NOT_INITIALIZED);
2252 JS::Rooted<JSObject*> obj(RootingCx(), GetWrapperPreserveColor());
2253 return ctx->SetProperty(obj, "arguments", aArguments);
2256 //*****************************************************************************
2257 // nsGlobalWindowInner::nsIScriptObjectPrincipal
2258 //*****************************************************************************
2260 nsIPrincipal* nsGlobalWindowInner::GetPrincipal() {
2261 if (mDoc) {
2262 // If we have a document, get the principal from the document
2263 return mDoc->NodePrincipal();
2266 if (mDocumentPrincipal) {
2267 return mDocumentPrincipal;
2270 // If we don't have a principal and we don't have a document we
2271 // ask the parent window for the principal. This can happen when
2272 // loading a frameset that has a <frame src="javascript:xxx">, in
2273 // that case the global window is used in JS before we've loaded
2274 // a document into the window.
2276 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
2277 do_QueryInterface(GetInProcessParentInternal());
2279 if (objPrincipal) {
2280 return objPrincipal->GetPrincipal();
2283 return nullptr;
2286 nsIPrincipal* nsGlobalWindowInner::GetEffectiveCookiePrincipal() {
2287 if (mDoc) {
2288 // If we have a document, get the principal from the document
2289 return mDoc->EffectiveCookiePrincipal();
2292 if (mDocumentCookiePrincipal) {
2293 return mDocumentCookiePrincipal;
2296 // If we don't have a cookie principal and we don't have a document we ask
2297 // the parent window for the cookie principal.
2299 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
2300 do_QueryInterface(GetInProcessParentInternal());
2302 if (objPrincipal) {
2303 return objPrincipal->GetEffectiveCookiePrincipal();
2306 return nullptr;
2309 nsIPrincipal* nsGlobalWindowInner::GetEffectiveStoragePrincipal() {
2310 if (mDoc) {
2311 // If we have a document, get the principal from the document
2312 return mDoc->EffectiveStoragePrincipal();
2315 if (mDocumentStoragePrincipal) {
2316 return mDocumentStoragePrincipal;
2319 // If we don't have a cookie principal and we don't have a document we ask
2320 // the parent window for the cookie principal.
2322 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
2323 do_QueryInterface(GetInProcessParentInternal());
2325 if (objPrincipal) {
2326 return objPrincipal->GetEffectiveStoragePrincipal();
2329 return nullptr;
2332 nsIPrincipal* nsGlobalWindowInner::PartitionedPrincipal() {
2333 if (mDoc) {
2334 // If we have a document, get the principal from the document
2335 return mDoc->PartitionedPrincipal();
2338 if (mDocumentPartitionedPrincipal) {
2339 return mDocumentPartitionedPrincipal;
2342 // If we don't have a partitioned principal and we don't have a document we
2343 // ask the parent window for the partitioned principal.
2345 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
2346 do_QueryInterface(GetInProcessParentInternal());
2348 if (objPrincipal) {
2349 return objPrincipal->PartitionedPrincipal();
2352 return nullptr;
2355 //*****************************************************************************
2356 // nsGlobalWindowInner::nsIDOMWindow
2357 //*****************************************************************************
2359 bool nsPIDOMWindowInner::AddAudioContext(AudioContext* aAudioContext) {
2360 mAudioContexts.AppendElement(aAudioContext);
2362 // Return true if the context should be muted and false if not.
2363 nsIDocShell* docShell = GetDocShell();
2364 return docShell && !docShell->GetAllowMedia() && !aAudioContext->IsOffline();
2367 void nsPIDOMWindowInner::RemoveAudioContext(AudioContext* aAudioContext) {
2368 mAudioContexts.RemoveElement(aAudioContext);
2371 void nsPIDOMWindowInner::MuteAudioContexts() {
2372 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
2373 if (!mAudioContexts[i]->IsOffline()) {
2374 mAudioContexts[i]->Mute();
2379 void nsPIDOMWindowInner::UnmuteAudioContexts() {
2380 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
2381 if (!mAudioContexts[i]->IsOffline()) {
2382 mAudioContexts[i]->Unmute();
2387 WindowProxyHolder nsGlobalWindowInner::Window() {
2388 return WindowProxyHolder(GetBrowsingContext());
2391 Navigator* nsPIDOMWindowInner::Navigator() {
2392 if (!mNavigator) {
2393 mNavigator = new mozilla::dom::Navigator(this);
2396 return mNavigator;
2399 MediaDevices* nsPIDOMWindowInner::GetExtantMediaDevices() const {
2400 return mNavigator ? mNavigator->GetExtantMediaDevices() : nullptr;
2403 VisualViewport* nsGlobalWindowInner::VisualViewport() {
2404 if (!mVisualViewport) {
2405 mVisualViewport = new mozilla::dom::VisualViewport(this);
2407 return mVisualViewport;
2410 nsScreen* nsGlobalWindowInner::Screen() {
2411 if (!mScreen) {
2412 mScreen = new nsScreen(this);
2414 return mScreen;
2417 nsHistory* nsGlobalWindowInner::GetHistory(ErrorResult& aError) {
2418 if (!mHistory) {
2419 mHistory = new nsHistory(this);
2421 return mHistory;
2424 CustomElementRegistry* nsGlobalWindowInner::CustomElements() {
2425 if (!mCustomElements) {
2426 mCustomElements = new CustomElementRegistry(this);
2429 return mCustomElements;
2432 CustomElementRegistry* nsGlobalWindowInner::GetExistingCustomElements() {
2433 return mCustomElements;
2436 Performance* nsPIDOMWindowInner::GetPerformance() {
2437 CreatePerformanceObjectIfNeeded();
2438 return mPerformance;
2441 void nsPIDOMWindowInner::QueuePerformanceNavigationTiming() {
2442 CreatePerformanceObjectIfNeeded();
2443 if (mPerformance) {
2444 mPerformance->QueueNavigationTimingEntry();
2448 void nsPIDOMWindowInner::CreatePerformanceObjectIfNeeded() {
2449 if (mPerformance || !mDoc) {
2450 return;
2452 RefPtr<nsDOMNavigationTiming> timing = mDoc->GetNavigationTiming();
2453 nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(mDoc->GetChannel()));
2454 bool timingEnabled = false;
2455 if (!timedChannel ||
2456 !NS_SUCCEEDED(timedChannel->GetTimingEnabled(&timingEnabled)) ||
2457 !timingEnabled) {
2458 timedChannel = nullptr;
2460 if (timing) {
2461 mPerformance = Performance::CreateForMainThread(this, mDoc->NodePrincipal(),
2462 timing, timedChannel);
2466 bool nsPIDOMWindowInner::IsSecureContext() const {
2467 return nsGlobalWindowInner::Cast(this)->IsSecureContext();
2470 void nsPIDOMWindowInner::Suspend(bool aIncludeSubWindows) {
2471 nsGlobalWindowInner::Cast(this)->Suspend(aIncludeSubWindows);
2474 void nsPIDOMWindowInner::Resume(bool aIncludeSubWindows) {
2475 nsGlobalWindowInner::Cast(this)->Resume(aIncludeSubWindows);
2478 void nsPIDOMWindowInner::SyncStateFromParentWindow() {
2479 nsGlobalWindowInner::Cast(this)->SyncStateFromParentWindow();
2482 Maybe<ClientInfo> nsPIDOMWindowInner::GetClientInfo() const {
2483 return nsGlobalWindowInner::Cast(this)->GetClientInfo();
2486 Maybe<ClientState> nsPIDOMWindowInner::GetClientState() const {
2487 return nsGlobalWindowInner::Cast(this)->GetClientState();
2490 Maybe<ServiceWorkerDescriptor> nsPIDOMWindowInner::GetController() const {
2491 return nsGlobalWindowInner::Cast(this)->GetController();
2494 void nsPIDOMWindowInner::SetCsp(nsIContentSecurityPolicy* aCsp) {
2495 return nsGlobalWindowInner::Cast(this)->SetCsp(aCsp);
2498 void nsPIDOMWindowInner::SetPreloadCsp(nsIContentSecurityPolicy* aPreloadCsp) {
2499 return nsGlobalWindowInner::Cast(this)->SetPreloadCsp(aPreloadCsp);
2502 nsIContentSecurityPolicy* nsPIDOMWindowInner::GetCsp() {
2503 return nsGlobalWindowInner::Cast(this)->GetCsp();
2506 void nsPIDOMWindowInner::NoteCalledRegisterForServiceWorkerScope(
2507 const nsACString& aScope) {
2508 nsGlobalWindowInner::Cast(this)->NoteCalledRegisterForServiceWorkerScope(
2509 aScope);
2512 void nsPIDOMWindowInner::NoteDOMContentLoaded() {
2513 nsGlobalWindowInner::Cast(this)->NoteDOMContentLoaded();
2516 bool nsGlobalWindowInner::ShouldReportForServiceWorkerScope(
2517 const nsAString& aScope) {
2518 bool result = false;
2520 nsPIDOMWindowOuter* topOuter = GetInProcessScriptableTop();
2521 NS_ENSURE_TRUE(topOuter, false);
2523 nsGlobalWindowInner* topInner =
2524 nsGlobalWindowInner::Cast(topOuter->GetCurrentInnerWindow());
2525 NS_ENSURE_TRUE(topInner, false);
2527 topInner->ShouldReportForServiceWorkerScopeInternal(
2528 NS_ConvertUTF16toUTF8(aScope), &result);
2529 return result;
2532 InstallTriggerImpl* nsGlobalWindowInner::GetInstallTrigger() {
2533 if (!mInstallTrigger &&
2534 !StaticPrefs::extensions_InstallTriggerImpl_enabled()) {
2535 // Return nullptr when InstallTriggerImpl is disabled by pref,
2536 // which does not yet break the "typeof InstallTrigger !== 'undefined"
2537 // "UA detection" use case, but prevents access to the InstallTriggerImpl
2538 // methods and properties.
2540 // NOTE: a separate pref ("extensions.InstallTrigger.enabled"), associated
2541 // to this property using the [Pref] extended attribute in Window.webidl,
2542 // does instead hide the entire InstallTrigger property.
2544 // See Bug 1754441 for more details about this deprecation.
2545 return nullptr;
2547 if (!mInstallTrigger) {
2548 ErrorResult rv;
2549 mInstallTrigger = ConstructJSImplementation<InstallTriggerImpl>(
2550 "@mozilla.org/addons/installtrigger;1", this, rv);
2551 if (rv.Failed()) {
2552 rv.SuppressException();
2553 return nullptr;
2557 return mInstallTrigger;
2560 nsIDOMWindowUtils* nsGlobalWindowInner::GetWindowUtils(ErrorResult& aRv) {
2561 FORWARD_TO_OUTER_OR_THROW(WindowUtils, (), aRv, nullptr);
2564 CallState nsGlobalWindowInner::ShouldReportForServiceWorkerScopeInternal(
2565 const nsACString& aScope, bool* aResultOut) {
2566 MOZ_DIAGNOSTIC_ASSERT(aResultOut);
2568 // First check to see if this window is controlled. If so, then we have
2569 // found a match and are done.
2570 const Maybe<ServiceWorkerDescriptor> swd = GetController();
2571 if (swd.isSome() && swd.ref().Scope() == aScope) {
2572 *aResultOut = true;
2573 return CallState::Stop;
2576 // Next, check to see if this window has called
2577 // navigator.serviceWorker.register() for this scope. If so, then treat this
2578 // as a match so console reports appear in the devtools console.
2579 if (mClientSource &&
2580 mClientSource->CalledRegisterForServiceWorkerScope(aScope)) {
2581 *aResultOut = true;
2582 return CallState::Stop;
2585 // Finally check the current docshell nsILoadGroup to see if there are any
2586 // outstanding navigation requests. If so, match the scope against the
2587 // channel's URL. We want to show console reports during the FetchEvent
2588 // intercepting the navigation itself.
2589 nsCOMPtr<nsIDocumentLoader> loader(do_QueryInterface(GetDocShell()));
2590 if (loader) {
2591 nsCOMPtr<nsILoadGroup> loadgroup;
2592 Unused << loader->GetLoadGroup(getter_AddRefs(loadgroup));
2593 if (loadgroup) {
2594 nsCOMPtr<nsISimpleEnumerator> iter;
2595 Unused << loadgroup->GetRequests(getter_AddRefs(iter));
2596 if (iter) {
2597 nsCOMPtr<nsISupports> tmp;
2598 bool hasMore = true;
2599 // Check each network request in the load group.
2600 while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) {
2601 iter->GetNext(getter_AddRefs(tmp));
2602 nsCOMPtr<nsIChannel> loadingChannel(do_QueryInterface(tmp));
2603 // Ignore subresource requests. Logging for a subresource
2604 // FetchEvent should be handled above since the client is
2605 // already controlled.
2606 if (!loadingChannel ||
2607 !nsContentUtils::IsNonSubresourceRequest(loadingChannel)) {
2608 continue;
2610 nsCOMPtr<nsIURI> loadingURL;
2611 Unused << loadingChannel->GetURI(getter_AddRefs(loadingURL));
2612 if (!loadingURL) {
2613 continue;
2615 nsAutoCString loadingSpec;
2616 Unused << loadingURL->GetSpec(loadingSpec);
2617 // Perform a simple substring comparison to match the scope
2618 // against the channel URL.
2619 if (StringBeginsWith(loadingSpec, aScope)) {
2620 *aResultOut = true;
2621 return CallState::Stop;
2628 // The current window doesn't care about this service worker, but maybe
2629 // one of our child frames does.
2630 return CallOnInProcessChildren(
2631 &nsGlobalWindowInner::ShouldReportForServiceWorkerScopeInternal, aScope,
2632 aResultOut);
2635 void nsGlobalWindowInner::NoteCalledRegisterForServiceWorkerScope(
2636 const nsACString& aScope) {
2637 if (!mClientSource) {
2638 return;
2641 mClientSource->NoteCalledRegisterForServiceWorkerScope(aScope);
2644 void nsGlobalWindowInner::NoteDOMContentLoaded() {
2645 if (!mClientSource) {
2646 return;
2649 mClientSource->NoteDOMContentLoaded();
2652 void nsGlobalWindowInner::UpdateTopInnerWindow() {
2653 if (IsTopInnerWindow() || !mTopInnerWindow) {
2654 return;
2657 mTopInnerWindow->UpdateWebSocketCount(-(int32_t)mNumOfOpenWebSockets);
2660 bool nsGlobalWindowInner::IsInSyncOperation() {
2661 return GetExtantDoc() && GetExtantDoc()->IsInSyncOperation();
2664 bool nsGlobalWindowInner::IsSharedMemoryAllowedInternal(
2665 nsIPrincipal* aPrincipal) const {
2666 MOZ_ASSERT(NS_IsMainThread());
2668 if (StaticPrefs::
2669 dom_postMessage_sharedArrayBuffer_bypassCOOP_COEP_insecure_enabled()) {
2670 return true;
2673 if (ExtensionPolicyService::GetSingleton().IsExtensionProcess()) {
2674 if (auto* basePrincipal = BasePrincipal::Cast(aPrincipal)) {
2675 if (auto* policy = basePrincipal->AddonPolicy()) {
2676 return policy->IsPrivileged();
2681 return CrossOriginIsolated();
2684 bool nsGlobalWindowInner::CrossOriginIsolated() const {
2685 MOZ_ASSERT(NS_IsMainThread());
2687 RefPtr<BrowsingContext> bc = GetBrowsingContext();
2688 MOZ_DIAGNOSTIC_ASSERT(bc);
2689 return bc->CrossOriginIsolated();
2692 WindowContext* TopWindowContext(nsPIDOMWindowInner& aWindow) {
2693 WindowContext* wc = aWindow.GetWindowContext();
2694 if (!wc) {
2695 return nullptr;
2698 return wc->TopWindowContext();
2701 void nsPIDOMWindowInner::AddPeerConnection() {
2702 MOZ_ASSERT(NS_IsMainThread());
2703 ++mActivePeerConnections;
2704 if (mActivePeerConnections == 1 && mWindowGlobalChild) {
2705 mWindowGlobalChild->SendUpdateActivePeerConnectionStatus(
2706 /*aIsAdded*/ true);
2708 // We need to present having active peer connections immediately. If we need
2709 // to wait for the parent process to come back with this information we
2710 // might start throttling.
2711 if (WindowContext* top = TopWindowContext(*this)) {
2712 top->TransientSetHasActivePeerConnections();
2717 void nsPIDOMWindowInner::RemovePeerConnection() {
2718 MOZ_ASSERT(NS_IsMainThread());
2719 MOZ_ASSERT(mActivePeerConnections > 0);
2720 --mActivePeerConnections;
2721 if (mActivePeerConnections == 0 && mWindowGlobalChild) {
2722 mWindowGlobalChild->SendUpdateActivePeerConnectionStatus(
2723 /*aIsAdded*/ false);
2727 bool nsPIDOMWindowInner::HasActivePeerConnections() {
2728 MOZ_ASSERT(NS_IsMainThread());
2730 WindowContext* topWindowContext = TopWindowContext(*this);
2731 return topWindowContext && topWindowContext->GetHasActivePeerConnections();
2734 void nsPIDOMWindowInner::AddMediaKeysInstance(MediaKeys* aMediaKeys) {
2735 MOZ_ASSERT(NS_IsMainThread());
2736 mMediaKeysInstances.AppendElement(aMediaKeys);
2737 if (mWindowGlobalChild && mMediaKeysInstances.Length() == 1) {
2738 mWindowGlobalChild->BlockBFCacheFor(BFCacheStatus::CONTAINS_EME_CONTENT);
2742 void nsPIDOMWindowInner::RemoveMediaKeysInstance(MediaKeys* aMediaKeys) {
2743 MOZ_ASSERT(NS_IsMainThread());
2744 mMediaKeysInstances.RemoveElement(aMediaKeys);
2745 if (mWindowGlobalChild && mMediaKeysInstances.IsEmpty()) {
2746 mWindowGlobalChild->UnblockBFCacheFor(BFCacheStatus::CONTAINS_EME_CONTENT);
2750 bool nsPIDOMWindowInner::HasActiveMediaKeysInstance() {
2751 MOZ_ASSERT(NS_IsMainThread());
2752 return !mMediaKeysInstances.IsEmpty();
2755 bool nsPIDOMWindowInner::IsPlayingAudio() {
2756 for (uint32_t i = 0; i < mAudioContexts.Length(); i++) {
2757 if (mAudioContexts[i]->IsRunning()) {
2758 return true;
2761 RefPtr<AudioChannelService> acs = AudioChannelService::Get();
2762 if (!acs) {
2763 return false;
2765 auto outer = GetOuterWindow();
2766 if (!outer) {
2767 // We've been unlinked and are about to die. Not a good time to pretend to
2768 // be playing audio.
2769 return false;
2771 return acs->IsWindowActive(outer);
2774 bool nsPIDOMWindowInner::IsDocumentLoaded() const { return mIsDocumentLoaded; }
2776 mozilla::dom::TimeoutManager& nsPIDOMWindowInner::TimeoutManager() {
2777 return *mTimeoutManager;
2780 bool nsPIDOMWindowInner::IsRunningTimeout() {
2781 return TimeoutManager().IsRunningTimeout();
2784 void nsPIDOMWindowInner::TryToCacheTopInnerWindow() {
2785 if (mHasTriedToCacheTopInnerWindow) {
2786 return;
2789 nsGlobalWindowInner* window = nsGlobalWindowInner::Cast(this);
2791 MOZ_ASSERT(!window->IsDying());
2793 mHasTriedToCacheTopInnerWindow = true;
2795 MOZ_ASSERT(window);
2797 if (nsCOMPtr<nsPIDOMWindowOuter> topOutter =
2798 window->GetInProcessScriptableTop()) {
2799 mTopInnerWindow = topOutter->GetCurrentInnerWindow();
2803 void nsPIDOMWindowInner::UpdateActiveIndexedDBDatabaseCount(int32_t aDelta) {
2804 MOZ_ASSERT(NS_IsMainThread());
2806 if (aDelta == 0) {
2807 return;
2810 // We count databases but not transactions because only active databases
2811 // could block throttling.
2812 uint32_t& counter = mTopInnerWindow
2813 ? mTopInnerWindow->mNumOfIndexedDBDatabases
2814 : mNumOfIndexedDBDatabases;
2816 counter += aDelta;
2819 bool nsPIDOMWindowInner::HasActiveIndexedDBDatabases() {
2820 MOZ_ASSERT(NS_IsMainThread());
2822 return mTopInnerWindow ? mTopInnerWindow->mNumOfIndexedDBDatabases > 0
2823 : mNumOfIndexedDBDatabases > 0;
2826 void nsPIDOMWindowInner::UpdateWebSocketCount(int32_t aDelta) {
2827 MOZ_ASSERT(NS_IsMainThread());
2829 if (aDelta == 0) {
2830 return;
2833 if (mTopInnerWindow && !IsTopInnerWindow()) {
2834 mTopInnerWindow->UpdateWebSocketCount(aDelta);
2837 MOZ_DIAGNOSTIC_ASSERT(
2838 aDelta > 0 || ((aDelta + mNumOfOpenWebSockets) < mNumOfOpenWebSockets));
2840 mNumOfOpenWebSockets += aDelta;
2843 bool nsPIDOMWindowInner::HasOpenWebSockets() const {
2844 MOZ_ASSERT(NS_IsMainThread());
2846 return mNumOfOpenWebSockets ||
2847 (mTopInnerWindow && mTopInnerWindow->mNumOfOpenWebSockets);
2850 bool nsPIDOMWindowInner::IsCurrentInnerWindow() const {
2851 if (mozilla::SessionHistoryInParent() && mBrowsingContext &&
2852 mBrowsingContext->IsInBFCache()) {
2853 return false;
2856 if (!mBrowsingContext || mBrowsingContext->IsDiscarded()) {
2857 // If our BrowsingContext has been discarded, we consider ourselves
2858 // still-current if we were current at the time it was discarded.
2859 return mOuterWindow && WasCurrentInnerWindow();
2862 nsPIDOMWindowOuter* outer = mBrowsingContext->GetDOMWindow();
2863 return outer && outer->GetCurrentInnerWindow() == this;
2866 bool nsPIDOMWindowInner::IsFullyActive() const {
2867 WindowContext* wc = GetWindowContext();
2868 if (!wc || wc->IsDiscarded() || !wc->IsCurrent()) {
2869 return false;
2871 return GetBrowsingContext()->AncestorsAreCurrent();
2874 void nsPIDOMWindowInner::SetAudioCapture(bool aCapture) {
2875 RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
2876 if (service) {
2877 service->SetWindowAudioCaptured(GetOuterWindow(), mWindowID, aCapture);
2881 void nsGlobalWindowInner::SetActiveLoadingState(bool aIsLoading) {
2882 MOZ_LOG(
2883 gTimeoutLog, mozilla::LogLevel::Debug,
2884 ("SetActiveLoadingState innerwindow %p: %d", (void*)this, aIsLoading));
2885 if (GetBrowsingContext()) {
2886 // Setting loading on a discarded context has no effect.
2887 Unused << GetBrowsingContext()->SetLoading(aIsLoading);
2890 if (!nsGlobalWindowInner::Cast(this)->IsChromeWindow()) {
2891 mTimeoutManager->SetLoading(aIsLoading);
2894 HintIsLoading(aIsLoading);
2897 void nsGlobalWindowInner::HintIsLoading(bool aIsLoading) {
2898 // Hint to tell the JS GC to use modified triggers during pageload.
2899 if (mHintedWasLoading != aIsLoading) {
2900 using namespace js::gc;
2901 SetPerformanceHint(danger::GetJSContext(), aIsLoading
2902 ? PerformanceHint::InPageLoad
2903 : PerformanceHint::Normal);
2904 mHintedWasLoading = aIsLoading;
2908 // nsISpeechSynthesisGetter
2910 #ifdef MOZ_WEBSPEECH
2911 SpeechSynthesis* nsGlobalWindowInner::GetSpeechSynthesis(ErrorResult& aError) {
2912 if (!mSpeechSynthesis) {
2913 mSpeechSynthesis = new SpeechSynthesis(this);
2916 return mSpeechSynthesis;
2919 bool nsGlobalWindowInner::HasActiveSpeechSynthesis() {
2920 if (mSpeechSynthesis) {
2921 return !mSpeechSynthesis->HasEmptyQueue();
2924 return false;
2927 #endif
2929 mozilla::glean::Glean* nsGlobalWindowInner::Glean() {
2930 if (!mGlean) {
2931 mGlean = new mozilla::glean::Glean(this);
2934 return mGlean;
2937 mozilla::glean::GleanPings* nsGlobalWindowInner::GleanPings() {
2938 if (!mGleanPings) {
2939 mGleanPings = new mozilla::glean::GleanPings();
2942 return mGleanPings;
2945 Nullable<WindowProxyHolder> nsGlobalWindowInner::GetParent(
2946 ErrorResult& aError) {
2947 FORWARD_TO_OUTER_OR_THROW(GetParentOuter, (), aError, nullptr);
2951 * GetInProcessScriptableParent used to be called when a script read
2952 * window.parent. Under Fission, that is now handled by
2953 * BrowsingContext::GetParent, and the result is a WindowProxyHolder rather than
2954 * an actual global window. This method still exists for legacy callers which
2955 * relied on the old logic, and require in-process windows. However, it only
2956 * works correctly when no out-of-process frames exist between this window and
2957 * the top-level window, so it should not be used in new code.
2959 * In contrast to GetRealParent, GetInProcessScriptableParent respects <iframe
2960 * mozbrowser> boundaries, so if |this| is contained by an <iframe
2961 * mozbrowser>, we will return |this| as its own parent.
2963 nsPIDOMWindowOuter* nsGlobalWindowInner::GetInProcessScriptableParent() {
2964 FORWARD_TO_OUTER(GetInProcessScriptableParent, (), nullptr);
2968 * GetInProcessScriptableTop used to be called when a script read window.top.
2969 * Under Fission, that is now handled by BrowsingContext::Top, and the result is
2970 * a WindowProxyHolder rather than an actual global window. This method still
2971 * exists for legacy callers which relied on the old logic, and require
2972 * in-process windows. However, it only works correctly when no out-of-process
2973 * frames exist between this window and the top-level window, so it should not
2974 * be used in new code.
2976 * In contrast to GetRealTop, GetInProcessScriptableTop respects <iframe
2977 * mozbrowser> boundaries. If we encounter a window owned by an <iframe
2978 * mozbrowser> while walking up the window hierarchy, we'll stop and return that
2979 * window.
2981 nsPIDOMWindowOuter* nsGlobalWindowInner::GetInProcessScriptableTop() {
2982 FORWARD_TO_OUTER(GetInProcessScriptableTop, (), nullptr);
2985 void nsGlobalWindowInner::GetContent(JSContext* aCx,
2986 JS::MutableHandle<JSObject*> aRetval,
2987 CallerType aCallerType,
2988 ErrorResult& aError) {
2989 FORWARD_TO_OUTER_OR_THROW(GetContentOuter,
2990 (aCx, aRetval, aCallerType, aError), aError, );
2993 BarProp* nsGlobalWindowInner::GetMenubar(ErrorResult& aError) {
2994 if (!mMenubar) {
2995 mMenubar = new MenubarProp(this);
2998 return mMenubar;
3001 BarProp* nsGlobalWindowInner::GetToolbar(ErrorResult& aError) {
3002 if (!mToolbar) {
3003 mToolbar = new ToolbarProp(this);
3006 return mToolbar;
3009 BarProp* nsGlobalWindowInner::GetLocationbar(ErrorResult& aError) {
3010 if (!mLocationbar) {
3011 mLocationbar = new LocationbarProp(this);
3013 return mLocationbar;
3016 BarProp* nsGlobalWindowInner::GetPersonalbar(ErrorResult& aError) {
3017 if (!mPersonalbar) {
3018 mPersonalbar = new PersonalbarProp(this);
3020 return mPersonalbar;
3023 BarProp* nsGlobalWindowInner::GetStatusbar(ErrorResult& aError) {
3024 if (!mStatusbar) {
3025 mStatusbar = new StatusbarProp(this);
3027 return mStatusbar;
3030 BarProp* nsGlobalWindowInner::GetScrollbars(ErrorResult& aError) {
3031 if (!mScrollbars) {
3032 mScrollbars = new ScrollbarsProp(this);
3035 return mScrollbars;
3038 bool nsGlobalWindowInner::GetClosed(ErrorResult& aError) {
3039 // If we're called from JS (which is the only way we should be getting called
3040 // here) and we reach this point, that means our JS global is the current
3041 // target of the WindowProxy, which means that we are the "current inner"
3042 // of our outer. So if FORWARD_TO_OUTER fails to forward, that means the
3043 // outer is already torn down, which corresponds to the closed state.
3044 FORWARD_TO_OUTER(GetClosedOuter, (), true);
3047 Nullable<WindowProxyHolder> nsGlobalWindowInner::IndexedGetter(
3048 uint32_t aIndex) {
3049 FORWARD_TO_OUTER(IndexedGetterOuter, (aIndex), nullptr);
3052 namespace {
3054 struct InterfaceShimEntry {
3055 const char* geckoName;
3056 const char* domName;
3059 } // anonymous namespace
3061 // We add shims from Components.interfaces.nsIDOMFoo to window.Foo for each
3062 // interface that has interface constants that sites might be getting off
3063 // of Ci.
3064 const InterfaceShimEntry kInterfaceShimMap[] = {
3065 {"nsIXMLHttpRequest", "XMLHttpRequest"},
3066 {"nsIDOMDOMException", "DOMException"},
3067 {"nsIDOMNode", "Node"},
3068 {"nsIDOMCSSRule", "CSSRule"},
3069 {"nsIDOMEvent", "Event"},
3070 {"nsIDOMNSEvent", "Event"},
3071 {"nsIDOMKeyEvent", "KeyEvent"},
3072 {"nsIDOMMouseEvent", "MouseEvent"},
3073 {"nsIDOMMouseScrollEvent", "MouseScrollEvent"},
3074 {"nsIDOMMutationEvent", "MutationEvent"},
3075 {"nsIDOMUIEvent", "UIEvent"},
3076 {"nsIDOMHTMLMediaElement", "HTMLMediaElement"},
3077 {"nsIDOMRange", "Range"},
3078 // Think about whether Ci.nsINodeFilter can just go away for websites!
3079 {"nsIDOMNodeFilter", "NodeFilter"},
3080 {"nsIDOMXPathResult", "XPathResult"}};
3082 bool nsGlobalWindowInner::ResolveComponentsShim(
3083 JSContext* aCx, JS::Handle<JSObject*> aGlobal,
3084 JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> aDesc) {
3085 // Keep track of how often this happens.
3086 Telemetry::Accumulate(Telemetry::COMPONENTS_SHIM_ACCESSED_BY_CONTENT, true);
3088 // Warn once.
3089 nsCOMPtr<Document> doc = GetExtantDoc();
3090 if (doc) {
3091 doc->WarnOnceAbout(DeprecatedOperations::eComponents, /* asError = */ true);
3094 // Create a fake Components object.
3095 AssertSameCompartment(aCx, aGlobal);
3096 JS::Rooted<JSObject*> components(aCx, JS_NewPlainObject(aCx));
3097 if (NS_WARN_IF(!components)) {
3098 return false;
3101 // Create a fake interfaces object.
3102 JS::Rooted<JSObject*> interfaces(aCx, JS_NewPlainObject(aCx));
3103 if (NS_WARN_IF(!interfaces)) {
3104 return false;
3106 bool ok =
3107 JS_DefineProperty(aCx, components, "interfaces", interfaces,
3108 JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY);
3109 if (NS_WARN_IF(!ok)) {
3110 return false;
3113 // Define a bunch of shims from the Ci.nsIDOMFoo to window.Foo for DOM
3114 // interfaces with constants.
3115 for (uint32_t i = 0; i < ArrayLength(kInterfaceShimMap); ++i) {
3116 // Grab the names from the table.
3117 const char* geckoName = kInterfaceShimMap[i].geckoName;
3118 const char* domName = kInterfaceShimMap[i].domName;
3120 // Look up the appopriate interface object on the global.
3121 JS::Rooted<JS::Value> v(aCx, JS::UndefinedValue());
3122 ok = JS_GetProperty(aCx, aGlobal, domName, &v);
3123 if (NS_WARN_IF(!ok)) {
3124 return false;
3126 if (!v.isObject()) {
3127 NS_WARNING("Unable to find interface object on global");
3128 continue;
3131 // Define the shim on the interfaces object.
3132 ok = JS_DefineProperty(
3133 aCx, interfaces, geckoName, v,
3134 JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY);
3135 if (NS_WARN_IF(!ok)) {
3136 return false;
3140 aDesc.set(mozilla::Some(JS::PropertyDescriptor::Data(
3141 JS::ObjectValue(*components),
3142 {JS::PropertyAttribute::Configurable, JS::PropertyAttribute::Enumerable,
3143 JS::PropertyAttribute::Writable})));
3144 return true;
3147 #ifdef RELEASE_OR_BETA
3148 # define USE_CONTROLLERS_SHIM
3149 #endif
3151 #ifdef USE_CONTROLLERS_SHIM
3152 static const JSClass ControllersShimClass = {"Controllers", 0};
3153 static const JSClass XULControllersShimClass = {"XULControllers", 0};
3154 #endif
3156 bool nsGlobalWindowInner::DoResolve(
3157 JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aId,
3158 JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> aDesc) {
3159 // Note: Keep this in sync with MayResolve.
3161 // Note: The infallibleInit call in GlobalResolve depends on this check.
3162 if (!aId.isString()) {
3163 return true;
3166 bool found;
3167 if (!WebIDLGlobalNameHash::DefineIfEnabled(aCx, aObj, aId, aDesc, &found)) {
3168 return false;
3171 if (found) {
3172 return true;
3175 // We support a cut-down Components.interfaces in case websites are
3176 // using Components.interfaces.nsIFoo.CONSTANT_NAME for the ones
3177 // that have constants.
3178 if (StaticPrefs::dom_use_components_shim() &&
3179 aId == XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_COMPONENTS)) {
3180 return ResolveComponentsShim(aCx, aObj, aDesc);
3183 // We also support a "window.controllers" thing; apparently some
3184 // sites use it for browser-sniffing. See bug 1010577.
3185 #ifdef USE_CONTROLLERS_SHIM
3186 // Note: We use |aObj| rather than |this| to get the principal here, because
3187 // this is called during Window setup when the Document isn't necessarily
3188 // hooked up yet.
3189 if ((aId == XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_CONTROLLERS) ||
3190 aId == XPCJSRuntime::Get()->GetStringID(
3191 XPCJSContext::IDX_CONTROLLERS_CLASS)) &&
3192 !xpc::IsXrayWrapper(aObj) &&
3193 !nsContentUtils::ObjectPrincipal(aObj)->IsSystemPrincipal()) {
3194 if (GetExtantDoc()) {
3195 GetExtantDoc()->WarnOnceAbout(
3196 DeprecatedOperations::eWindow_Cc_ontrollers);
3198 const JSClass* clazz;
3199 if (aId ==
3200 XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_CONTROLLERS)) {
3201 clazz = &XULControllersShimClass;
3202 } else {
3203 clazz = &ControllersShimClass;
3205 MOZ_ASSERT(JS_IsGlobalObject(aObj));
3206 JS::Rooted<JSObject*> shim(aCx, JS_NewObject(aCx, clazz));
3207 if (NS_WARN_IF(!shim)) {
3208 return false;
3211 aDesc.set(mozilla::Some(JS::PropertyDescriptor::Data(
3212 JS::ObjectValue(*shim),
3213 {JS::PropertyAttribute::Configurable, JS::PropertyAttribute::Enumerable,
3214 JS::PropertyAttribute::Writable})));
3215 return true;
3217 #endif
3219 return true;
3222 /* static */
3223 bool nsGlobalWindowInner::MayResolve(jsid aId) {
3224 // Note: This function does not fail and may not have any side-effects.
3225 // Note: Keep this in sync with DoResolve.
3226 if (!aId.isString()) {
3227 return false;
3230 if (aId == XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_COMPONENTS)) {
3231 return true;
3234 if (aId == XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_CONTROLLERS) ||
3235 aId == XPCJSRuntime::Get()->GetStringID(
3236 XPCJSContext::IDX_CONTROLLERS_CLASS)) {
3237 // We only resolve .controllers/.Controllers in release builds and on
3238 // non-chrome windows, but let's not worry about any of that stuff.
3239 return true;
3242 return WebIDLGlobalNameHash::MayResolve(aId);
3245 void nsGlobalWindowInner::GetOwnPropertyNames(
3246 JSContext* aCx, JS::MutableHandleVector<jsid> aNames, bool aEnumerableOnly,
3247 ErrorResult& aRv) {
3248 if (aEnumerableOnly) {
3249 // The names we would return from here get defined on the window via one of
3250 // two codepaths. The ones coming from the WebIDLGlobalNameHash will end up
3251 // in the DefineConstructor function in BindingUtils, which always defines
3252 // things as non-enumerable. The ones coming from the script namespace
3253 // manager get defined by our resolve hook using FillPropertyDescriptor with
3254 // 0 for the property attributes, so non-enumerable as well.
3256 // So in the aEnumerableOnly case we have nothing to do.
3257 return;
3260 // "Components" is marked as enumerable but only resolved on demand :-/.
3261 // aNames.AppendElement(u"Components"_ns);
3263 JS::Rooted<JSObject*> wrapper(aCx, GetWrapper());
3265 // There are actually two ways we can get called here: For normal
3266 // enumeration or for Xray enumeration. In the latter case, we want to
3267 // return all possible WebIDL names, because we don't really support
3268 // deleting these names off our Xray; trying to resolve them will just make
3269 // them come back. In the former case, we want to avoid returning deleted
3270 // names. But the JS engine already knows about the non-deleted
3271 // already-resolved names, so we can just return the so-far-unresolved ones.
3273 // We can tell which case we're in by whether aCx is in our wrapper's
3274 // compartment. If not, we're in the Xray case.
3275 WebIDLGlobalNameHash::NameType nameType =
3276 js::IsObjectInContextCompartment(wrapper, aCx)
3277 ? WebIDLGlobalNameHash::UnresolvedNamesOnly
3278 : WebIDLGlobalNameHash::AllNames;
3279 if (!WebIDLGlobalNameHash::GetNames(aCx, wrapper, nameType, aNames)) {
3280 aRv.NoteJSContextException(aCx);
3284 /* static */
3285 bool nsGlobalWindowInner::IsPrivilegedChromeWindow(JSContext*, JSObject* aObj) {
3286 // For now, have to deal with XPConnect objects here.
3287 nsGlobalWindowInner* win = xpc::WindowOrNull(aObj);
3288 return win && win->IsChromeWindow() &&
3289 nsContentUtils::ObjectPrincipal(aObj) ==
3290 nsContentUtils::GetSystemPrincipal();
3293 /* static */
3294 bool nsGlobalWindowInner::DeviceSensorsEnabled(JSContext*, JSObject*) {
3295 return Preferences::GetBool("device.sensors.enabled");
3298 /* static */
3299 bool nsGlobalWindowInner::CachesEnabled(JSContext* aCx, JSObject* aObj) {
3300 if (!IsSecureContextOrObjectIsFromSecureContext(aCx, aObj)) {
3301 return StaticPrefs::dom_caches_testing_enabled() ||
3302 StaticPrefs::dom_serviceWorkers_testing_enabled();
3304 return true;
3307 /* static */
3308 bool nsGlobalWindowInner::IsSizeToContentEnabled(JSContext* aCx, JSObject*) {
3309 return StaticPrefs::dom_window_sizeToContent_enabled() ||
3310 nsContentUtils::IsSystemCaller(aCx);
3313 /* static */
3314 bool nsGlobalWindowInner::IsGleanNeeded(JSContext* aCx, JSObject* aObj) {
3315 // Glean is needed in ChromeOnly contexts and also in privileged about pages.
3316 nsIPrincipal* principal = nsContentUtils::SubjectPrincipal(aCx);
3317 if (principal->IsSystemPrincipal()) {
3318 return true;
3321 uint32_t flags = 0;
3322 if (NS_FAILED(principal->GetAboutModuleFlags(&flags))) {
3323 return false;
3325 return flags & nsIAboutModule::IS_SECURE_CHROME_UI;
3328 Crypto* nsGlobalWindowInner::GetCrypto(ErrorResult& aError) {
3329 if (!mCrypto) {
3330 mCrypto = new Crypto(this);
3332 return mCrypto;
3335 nsIControllers* nsGlobalWindowInner::GetControllers(ErrorResult& aError) {
3336 FORWARD_TO_OUTER_OR_THROW(GetControllersOuter, (aError), aError, nullptr);
3339 nsresult nsGlobalWindowInner::GetControllers(nsIControllers** aResult) {
3340 ErrorResult rv;
3341 nsCOMPtr<nsIControllers> controllers = GetControllers(rv);
3342 controllers.forget(aResult);
3344 return rv.StealNSResult();
3347 Nullable<WindowProxyHolder> nsGlobalWindowInner::GetOpenerWindow(
3348 ErrorResult& aError) {
3349 FORWARD_TO_OUTER_OR_THROW(GetOpenerWindowOuter, (), aError, nullptr);
3352 void nsGlobalWindowInner::GetOpener(JSContext* aCx,
3353 JS::MutableHandle<JS::Value> aRetval,
3354 ErrorResult& aError) {
3355 Nullable<WindowProxyHolder> opener = GetOpenerWindow(aError);
3356 if (aError.Failed() || opener.IsNull()) {
3357 aRetval.setNull();
3358 return;
3361 if (!ToJSValue(aCx, opener.Value(), aRetval)) {
3362 aError.NoteJSContextException(aCx);
3366 void nsGlobalWindowInner::SetOpener(JSContext* aCx,
3367 JS::Handle<JS::Value> aOpener,
3368 ErrorResult& aError) {
3369 if (aOpener.isNull()) {
3370 RefPtr<BrowsingContext> bc(GetBrowsingContext());
3371 if (!bc->IsDiscarded()) {
3372 bc->SetOpener(nullptr);
3374 return;
3377 // If something other than null is passed, just define aOpener on our inner
3378 // window's JS object, wrapped into the current compartment so that for Xrays
3379 // we define on the Xray expando object, but don't set it on the outer window,
3380 // so that it'll get reset on navigation. This is just like replaceable
3381 // properties, but we're not quite readonly.
3382 RedefineProperty(aCx, "opener", aOpener, aError);
3385 void nsGlobalWindowInner::GetEvent(OwningEventOrUndefined& aRetval) {
3386 if (mEvent) {
3387 aRetval.SetAsEvent() = mEvent;
3388 } else {
3389 aRetval.SetUndefined();
3393 void nsGlobalWindowInner::GetStatus(nsAString& aStatus, ErrorResult& aError) {
3394 FORWARD_TO_OUTER_OR_THROW(GetStatusOuter, (aStatus), aError, );
3397 void nsGlobalWindowInner::SetStatus(const nsAString& aStatus,
3398 ErrorResult& aError) {
3399 FORWARD_TO_OUTER_OR_THROW(SetStatusOuter, (aStatus), aError, );
3402 void nsGlobalWindowInner::GetName(nsAString& aName, ErrorResult& aError) {
3403 FORWARD_TO_OUTER_OR_THROW(GetNameOuter, (aName), aError, );
3406 void nsGlobalWindowInner::SetName(const nsAString& aName,
3407 mozilla::ErrorResult& aError) {
3408 FORWARD_TO_OUTER_OR_THROW(SetNameOuter, (aName, aError), aError, );
3411 double nsGlobalWindowInner::GetInnerWidth(ErrorResult& aError) {
3412 FORWARD_TO_OUTER_OR_THROW(GetInnerWidthOuter, (aError), aError, 0);
3415 nsresult nsGlobalWindowInner::GetInnerWidth(double* aWidth) {
3416 ErrorResult rv;
3417 // Callee doesn't care about the caller type, but play it safe.
3418 *aWidth = GetInnerWidth(rv);
3419 return rv.StealNSResult();
3422 double nsGlobalWindowInner::GetInnerHeight(ErrorResult& aError) {
3423 // We ignore aCallerType; we only have that argument because some other things
3424 // called by GetReplaceableWindowCoord need it. If this ever changes, fix
3425 // nsresult nsGlobalWindowInner::GetInnerHeight(double* aInnerWidth)
3426 // to actually take a useful CallerType and pass it in here.
3427 FORWARD_TO_OUTER_OR_THROW(GetInnerHeightOuter, (aError), aError, 0);
3430 nsresult nsGlobalWindowInner::GetInnerHeight(double* aHeight) {
3431 ErrorResult rv;
3432 // Callee doesn't care about the caller type, but play it safe.
3433 *aHeight = GetInnerHeight(rv);
3434 return rv.StealNSResult();
3437 int32_t nsGlobalWindowInner::GetOuterWidth(CallerType aCallerType,
3438 ErrorResult& aError) {
3439 FORWARD_TO_OUTER_OR_THROW(GetOuterWidthOuter, (aCallerType, aError), aError,
3443 int32_t nsGlobalWindowInner::GetOuterHeight(CallerType aCallerType,
3444 ErrorResult& aError) {
3445 FORWARD_TO_OUTER_OR_THROW(GetOuterHeightOuter, (aCallerType, aError), aError,
3449 double nsGlobalWindowInner::ScreenEdgeSlopX() const {
3450 FORWARD_TO_OUTER(ScreenEdgeSlopX, (), 0);
3453 double nsGlobalWindowInner::ScreenEdgeSlopY() const {
3454 FORWARD_TO_OUTER(ScreenEdgeSlopY, (), 0);
3457 int32_t nsGlobalWindowInner::GetScreenX(CallerType aCallerType,
3458 ErrorResult& aError) {
3459 FORWARD_TO_OUTER_OR_THROW(GetScreenXOuter, (aCallerType, aError), aError, 0);
3462 int32_t nsGlobalWindowInner::GetScreenY(CallerType aCallerType,
3463 ErrorResult& aError) {
3464 FORWARD_TO_OUTER_OR_THROW(GetScreenYOuter, (aCallerType, aError), aError, 0);
3467 float nsGlobalWindowInner::GetMozInnerScreenX(CallerType aCallerType,
3468 ErrorResult& aError) {
3469 FORWARD_TO_OUTER_OR_THROW(GetMozInnerScreenXOuter, (aCallerType), aError, 0);
3472 float nsGlobalWindowInner::GetMozInnerScreenY(CallerType aCallerType,
3473 ErrorResult& aError) {
3474 FORWARD_TO_OUTER_OR_THROW(GetMozInnerScreenYOuter, (aCallerType), aError, 0);
3477 static nsPresContext* GetPresContextForRatio(Document* aDoc) {
3478 if (nsPresContext* presContext = aDoc->GetPresContext()) {
3479 return presContext;
3481 // We're in an undisplayed subdocument... There's not really an awesome way
3482 // to tell what the right DPI is from here, so we try to walk up our parent
3483 // document chain to the extent that the docs can observe each other.
3484 Document* doc = aDoc;
3485 while (doc->StyleOrLayoutObservablyDependsOnParentDocumentLayout()) {
3486 doc = doc->GetInProcessParentDocument();
3487 if (nsPresContext* presContext = doc->GetPresContext()) {
3488 return presContext;
3491 return nullptr;
3494 double nsGlobalWindowInner::GetDevicePixelRatio(CallerType aCallerType,
3495 ErrorResult& aError) {
3496 ENSURE_ACTIVE_DOCUMENT(aError, 0.0);
3498 RefPtr<nsPresContext> presContext = GetPresContextForRatio(mDoc);
3499 if (NS_WARN_IF(!presContext)) {
3500 // Still nothing, oh well.
3501 return 1.0;
3504 if (nsIGlobalObject::ShouldResistFingerprinting(
3505 aCallerType, RFPTarget::WindowDevicePixelRatio)) {
3506 // Spoofing the DevicePixelRatio causes blurriness in some situations
3507 // on HiDPI displays. pdf.js is a non-system caller; but it can't
3508 // expose the fingerprintable information, so we can safely disable
3509 // spoofing in this situation. It doesn't address the issue for
3510 // web-rendered content (including pdf.js instances on the web.)
3511 // In the future we hope to have a better solution to fix all HiDPI
3512 // blurriness...
3513 nsAutoCString origin;
3514 nsresult rv = this->GetPrincipal()->GetOrigin(origin);
3515 if (NS_FAILED(rv) || origin != "resource://pdf.js"_ns) {
3516 return 1.0;
3520 if (aCallerType == CallerType::NonSystem) {
3521 float overrideDPPX = presContext->GetOverrideDPPX();
3522 if (overrideDPPX > 0.0f) {
3523 return overrideDPPX;
3527 return double(AppUnitsPerCSSPixel()) /
3528 double(presContext->AppUnitsPerDevPixel());
3531 double nsGlobalWindowInner::GetDesktopToDeviceScale(ErrorResult& aError) {
3532 ENSURE_ACTIVE_DOCUMENT(aError, 0.0);
3533 nsPresContext* presContext = GetPresContextForRatio(mDoc);
3534 if (!presContext) {
3535 return 1.0;
3537 return presContext->DeviceContext()->GetDesktopToDeviceScale().scale;
3540 int32_t nsGlobalWindowInner::RequestAnimationFrame(
3541 FrameRequestCallback& aCallback, ErrorResult& aError) {
3542 if (!mDoc) {
3543 return 0;
3546 if (GetWrapperPreserveColor()) {
3547 js::NotifyAnimationActivity(GetWrapperPreserveColor());
3550 DebuggerNotificationDispatch(this,
3551 DebuggerNotificationType::RequestAnimationFrame);
3553 int32_t handle;
3554 aError = mDoc->ScheduleFrameRequestCallback(aCallback, &handle);
3555 return handle;
3558 void nsGlobalWindowInner::CancelAnimationFrame(int32_t aHandle,
3559 ErrorResult& aError) {
3560 if (!mDoc) {
3561 return;
3564 DebuggerNotificationDispatch(this,
3565 DebuggerNotificationType::CancelAnimationFrame);
3567 mDoc->CancelFrameRequestCallback(aHandle);
3570 already_AddRefed<MediaQueryList> nsGlobalWindowInner::MatchMedia(
3571 const nsACString& aMediaQueryList, CallerType aCallerType,
3572 ErrorResult& aError) {
3573 ENSURE_ACTIVE_DOCUMENT(aError, nullptr);
3574 return mDoc->MatchMedia(aMediaQueryList, aCallerType);
3577 int32_t nsGlobalWindowInner::GetScrollMinX(ErrorResult& aError) {
3578 FORWARD_TO_OUTER_OR_THROW(GetScrollBoundaryOuter, (eSideLeft), aError, 0);
3581 int32_t nsGlobalWindowInner::GetScrollMinY(ErrorResult& aError) {
3582 FORWARD_TO_OUTER_OR_THROW(GetScrollBoundaryOuter, (eSideTop), aError, 0);
3585 int32_t nsGlobalWindowInner::GetScrollMaxX(ErrorResult& aError) {
3586 FORWARD_TO_OUTER_OR_THROW(GetScrollBoundaryOuter, (eSideRight), aError, 0);
3589 int32_t nsGlobalWindowInner::GetScrollMaxY(ErrorResult& aError) {
3590 FORWARD_TO_OUTER_OR_THROW(GetScrollBoundaryOuter, (eSideBottom), aError, 0);
3593 double nsGlobalWindowInner::GetScrollX(ErrorResult& aError) {
3594 FORWARD_TO_OUTER_OR_THROW(GetScrollXOuter, (), aError, 0);
3597 double nsGlobalWindowInner::GetScrollY(ErrorResult& aError) {
3598 FORWARD_TO_OUTER_OR_THROW(GetScrollYOuter, (), aError, 0);
3601 uint32_t nsGlobalWindowInner::Length() { FORWARD_TO_OUTER(Length, (), 0); }
3603 Nullable<WindowProxyHolder> nsGlobalWindowInner::GetTop(
3604 mozilla::ErrorResult& aError) {
3605 FORWARD_TO_OUTER_OR_THROW(GetTopOuter, (), aError, nullptr);
3608 already_AddRefed<BrowsingContext> nsGlobalWindowInner::GetChildWindow(
3609 const nsAString& aName) {
3610 if (GetOuterWindowInternal()) {
3611 return GetOuterWindowInternal()->GetChildWindow(aName);
3613 return nullptr;
3616 void nsGlobalWindowInner::RefreshRealmPrincipal() {
3617 JS::SetRealmPrincipals(js::GetNonCCWObjectRealm(GetWrapperPreserveColor()),
3618 nsJSPrincipals::get(mDoc->NodePrincipal()));
3621 void nsGlobalWindowInner::RefreshReduceTimerPrecisionCallerType() {
3622 JS::SetRealmReduceTimerPrecisionCallerType(
3623 js::GetNonCCWObjectRealm(GetWrapperPreserveColor()),
3624 RTPCallerTypeToToken(GetRTPCallerType()));
3627 already_AddRefed<nsIWidget> nsGlobalWindowInner::GetMainWidget() {
3628 FORWARD_TO_OUTER(GetMainWidget, (), nullptr);
3631 nsIWidget* nsGlobalWindowInner::GetNearestWidget() const {
3632 if (GetOuterWindowInternal()) {
3633 return GetOuterWindowInternal()->GetNearestWidget();
3635 return nullptr;
3638 void nsGlobalWindowInner::SetFullScreen(bool aFullscreen,
3639 mozilla::ErrorResult& aError) {
3640 FORWARD_TO_OUTER_OR_THROW(SetFullscreenOuter, (aFullscreen, aError), aError,
3641 /* void */);
3644 bool nsGlobalWindowInner::GetFullScreen(ErrorResult& aError) {
3645 FORWARD_TO_OUTER_OR_THROW(GetFullscreenOuter, (), aError, false);
3648 bool nsGlobalWindowInner::GetFullScreen() {
3649 ErrorResult dummy;
3650 bool fullscreen = GetFullScreen(dummy);
3651 dummy.SuppressException();
3652 return fullscreen;
3655 void nsGlobalWindowInner::Dump(const nsAString& aStr) {
3656 if (!nsJSUtils::DumpEnabled()) {
3657 return;
3660 char* cstr = ToNewUTF8String(aStr);
3662 #if defined(XP_MACOSX)
3663 // have to convert \r to \n so that printing to the console works
3664 char *c = cstr, *cEnd = cstr + strlen(cstr);
3665 while (c < cEnd) {
3666 if (*c == '\r') *c = '\n';
3667 c++;
3669 #endif
3671 if (cstr) {
3672 MOZ_LOG(nsContentUtils::DOMDumpLog(), LogLevel::Debug,
3673 ("[Window.Dump] %s", cstr));
3674 #ifdef XP_WIN
3675 PrintToDebugger(cstr);
3676 #endif
3677 #ifdef ANDROID
3678 __android_log_write(ANDROID_LOG_INFO, "GeckoDump", cstr);
3679 #endif
3680 FILE* fp = gDumpFile ? gDumpFile : stdout;
3681 fputs(cstr, fp);
3682 fflush(fp);
3683 free(cstr);
3687 void nsGlobalWindowInner::Alert(nsIPrincipal& aSubjectPrincipal,
3688 ErrorResult& aError) {
3689 Alert(u""_ns, aSubjectPrincipal, aError);
3692 void nsGlobalWindowInner::Alert(const nsAString& aMessage,
3693 nsIPrincipal& aSubjectPrincipal,
3694 ErrorResult& aError) {
3695 FORWARD_TO_OUTER_OR_THROW(AlertOuter, (aMessage, aSubjectPrincipal, aError),
3696 aError, );
3699 bool nsGlobalWindowInner::Confirm(const nsAString& aMessage,
3700 nsIPrincipal& aSubjectPrincipal,
3701 ErrorResult& aError) {
3702 FORWARD_TO_OUTER_OR_THROW(ConfirmOuter, (aMessage, aSubjectPrincipal, aError),
3703 aError, false);
3706 already_AddRefed<Promise> nsGlobalWindowInner::Fetch(
3707 const RequestOrUTF8String& aInput, const RequestInit& aInit,
3708 CallerType aCallerType, ErrorResult& aRv) {
3709 return FetchRequest(this, aInput, aInit, aCallerType, aRv);
3712 void nsGlobalWindowInner::Prompt(const nsAString& aMessage,
3713 const nsAString& aInitial, nsAString& aReturn,
3714 nsIPrincipal& aSubjectPrincipal,
3715 ErrorResult& aError) {
3716 FORWARD_TO_OUTER_OR_THROW(
3717 PromptOuter, (aMessage, aInitial, aReturn, aSubjectPrincipal, aError),
3718 aError, );
3721 void nsGlobalWindowInner::Focus(CallerType aCallerType, ErrorResult& aError) {
3722 FORWARD_TO_OUTER_OR_THROW(FocusOuter,
3723 (aCallerType, /* aFromOtherProcess */ false,
3724 nsFocusManager::GenerateFocusActionId()),
3725 aError, );
3728 nsresult nsGlobalWindowInner::Focus(CallerType aCallerType) {
3729 ErrorResult rv;
3730 Focus(aCallerType, rv);
3732 return rv.StealNSResult();
3735 void nsGlobalWindowInner::Blur(CallerType aCallerType, ErrorResult& aError) {
3736 FORWARD_TO_OUTER_OR_THROW(BlurOuter, (aCallerType), aError, );
3739 void nsGlobalWindowInner::Stop(ErrorResult& aError) {
3740 FORWARD_TO_OUTER_OR_THROW(StopOuter, (aError), aError, );
3743 void nsGlobalWindowInner::Print(ErrorResult& aError) {
3744 FORWARD_TO_OUTER_OR_THROW(PrintOuter, (aError), aError, );
3747 Nullable<WindowProxyHolder> nsGlobalWindowInner::PrintPreview(
3748 nsIPrintSettings* aSettings, nsIWebProgressListener* aListener,
3749 nsIDocShell* aDocShellToCloneInto, ErrorResult& aError) {
3750 FORWARD_TO_OUTER_OR_THROW(
3751 Print,
3752 (aSettings,
3753 /* aRemotePrintJob = */ nullptr, aListener, aDocShellToCloneInto,
3754 nsGlobalWindowOuter::IsPreview::Yes,
3755 nsGlobalWindowOuter::IsForWindowDotPrint::No,
3756 /* aPrintPreviewCallback = */ nullptr, nullptr, aError),
3757 aError, nullptr);
3760 void nsGlobalWindowInner::MoveTo(int32_t aXPos, int32_t aYPos,
3761 CallerType aCallerType, ErrorResult& aError) {
3762 FORWARD_TO_OUTER_OR_THROW(MoveToOuter, (aXPos, aYPos, aCallerType, aError),
3763 aError, );
3766 void nsGlobalWindowInner::MoveBy(int32_t aXDif, int32_t aYDif,
3767 CallerType aCallerType, ErrorResult& aError) {
3768 FORWARD_TO_OUTER_OR_THROW(MoveByOuter, (aXDif, aYDif, aCallerType, aError),
3769 aError, );
3772 void nsGlobalWindowInner::ResizeTo(int32_t aWidth, int32_t aHeight,
3773 CallerType aCallerType,
3774 ErrorResult& aError) {
3775 FORWARD_TO_OUTER_OR_THROW(ResizeToOuter,
3776 (aWidth, aHeight, aCallerType, aError), aError, );
3779 void nsGlobalWindowInner::ResizeBy(int32_t aWidthDif, int32_t aHeightDif,
3780 CallerType aCallerType,
3781 ErrorResult& aError) {
3782 FORWARD_TO_OUTER_OR_THROW(
3783 ResizeByOuter, (aWidthDif, aHeightDif, aCallerType, aError), aError, );
3786 void nsGlobalWindowInner::SizeToContent(CallerType aCallerType,
3787 ErrorResult& aError) {
3788 FORWARD_TO_OUTER_OR_THROW(SizeToContentOuter, (aCallerType, {}, aError),
3789 aError, );
3792 void nsGlobalWindowInner::SizeToContentConstrained(
3793 const SizeToContentConstraints& aConstraints, ErrorResult& aError) {
3794 FORWARD_TO_OUTER_OR_THROW(
3795 SizeToContentOuter, (CallerType::System, aConstraints, aError), aError, );
3798 already_AddRefed<nsPIWindowRoot> nsGlobalWindowInner::GetTopWindowRoot() {
3799 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
3800 if (!outer) {
3801 return nullptr;
3803 return outer->GetTopWindowRoot();
3806 void nsGlobalWindowInner::Scroll(double aXScroll, double aYScroll) {
3807 // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
3808 auto scrollPos = CSSIntPoint::Truncate(mozilla::ToZeroIfNonfinite(aXScroll),
3809 mozilla::ToZeroIfNonfinite(aYScroll));
3810 ScrollTo(scrollPos, ScrollOptions());
3813 void nsGlobalWindowInner::ScrollTo(double aXScroll, double aYScroll) {
3814 // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
3815 auto scrollPos = CSSIntPoint::Truncate(mozilla::ToZeroIfNonfinite(aXScroll),
3816 mozilla::ToZeroIfNonfinite(aYScroll));
3817 ScrollTo(scrollPos, ScrollOptions());
3820 void nsGlobalWindowInner::ScrollTo(const ScrollToOptions& aOptions) {
3821 // When scrolling to a non-zero offset, we need to determine whether that
3822 // position is within our scrollable range, so we need updated layout
3823 // information which requires a layout flush, otherwise all we need is to
3824 // flush frames to be able to access our scrollable frame here.
3825 FlushType flushType =
3826 ((aOptions.mLeft.WasPassed() && aOptions.mLeft.Value() > 0) ||
3827 (aOptions.mTop.WasPassed() && aOptions.mTop.Value() > 0))
3828 ? FlushType::Layout
3829 : FlushType::Frames;
3830 FlushPendingNotifications(flushType);
3831 nsIScrollableFrame* sf = GetScrollFrame();
3833 if (sf) {
3834 CSSIntPoint scrollPos = sf->GetRoundedScrollPositionCSSPixels();
3835 if (aOptions.mLeft.WasPassed()) {
3836 scrollPos.x = static_cast<int32_t>(
3837 mozilla::ToZeroIfNonfinite(aOptions.mLeft.Value()));
3839 if (aOptions.mTop.WasPassed()) {
3840 scrollPos.y = static_cast<int32_t>(
3841 mozilla::ToZeroIfNonfinite(aOptions.mTop.Value()));
3844 ScrollTo(scrollPos, aOptions);
3848 void nsGlobalWindowInner::Scroll(const ScrollToOptions& aOptions) {
3849 ScrollTo(aOptions);
3852 void nsGlobalWindowInner::ScrollTo(const CSSIntPoint& aScroll,
3853 const ScrollOptions& aOptions) {
3854 // When scrolling to a non-zero offset, we need to determine whether that
3855 // position is within our scrollable range, so we need updated layout
3856 // information which requires a layout flush, otherwise all we need is to
3857 // flush frames to be able to access our scrollable frame here.
3858 FlushType flushType =
3859 (aScroll.x || aScroll.y) ? FlushType::Layout : FlushType::Frames;
3860 FlushPendingNotifications(flushType);
3861 nsIScrollableFrame* sf = GetScrollFrame();
3863 if (sf) {
3864 // Here we calculate what the max pixel value is that we can
3865 // scroll to, we do this by dividing maxint with the pixel to
3866 // twips conversion factor, and subtracting 4, the 4 comes from
3867 // experimenting with this value, anything less makes the view
3868 // code not scroll correctly, I have no idea why. -- jst
3869 const int32_t maxpx = nsPresContext::AppUnitsToIntCSSPixels(0x7fffffff) - 4;
3871 CSSIntPoint scroll(aScroll);
3872 if (scroll.x > maxpx) {
3873 scroll.x = maxpx;
3876 if (scroll.y > maxpx) {
3877 scroll.y = maxpx;
3880 ScrollMode scrollMode = sf->IsSmoothScroll(aOptions.mBehavior)
3881 ? ScrollMode::SmoothMsd
3882 : ScrollMode::Instant;
3884 sf->ScrollToCSSPixels(scroll, scrollMode);
3888 void nsGlobalWindowInner::ScrollBy(double aXScrollDif, double aYScrollDif) {
3889 FlushPendingNotifications(FlushType::Layout);
3890 nsIScrollableFrame* sf = GetScrollFrame();
3892 if (sf) {
3893 // It seems like it would make more sense for ScrollBy to use
3894 // SMOOTH mode, but tests seem to depend on the synchronous behaviour.
3895 // Perhaps Web content does too.
3896 ScrollToOptions options;
3897 options.mLeft.Construct(aXScrollDif);
3898 options.mTop.Construct(aYScrollDif);
3899 ScrollBy(options);
3903 void nsGlobalWindowInner::ScrollBy(const ScrollToOptions& aOptions) {
3904 FlushPendingNotifications(FlushType::Layout);
3905 nsIScrollableFrame* sf = GetScrollFrame();
3907 if (sf) {
3908 CSSIntPoint scrollDelta;
3909 if (aOptions.mLeft.WasPassed()) {
3910 scrollDelta.x = static_cast<int32_t>(
3911 mozilla::ToZeroIfNonfinite(aOptions.mLeft.Value()));
3913 if (aOptions.mTop.WasPassed()) {
3914 scrollDelta.y = static_cast<int32_t>(
3915 mozilla::ToZeroIfNonfinite(aOptions.mTop.Value()));
3918 ScrollMode scrollMode = sf->IsSmoothScroll(aOptions.mBehavior)
3919 ? ScrollMode::SmoothMsd
3920 : ScrollMode::Instant;
3922 sf->ScrollByCSSPixels(scrollDelta, scrollMode);
3926 void nsGlobalWindowInner::ScrollByLines(int32_t numLines,
3927 const ScrollOptions& aOptions) {
3928 FlushPendingNotifications(FlushType::Layout);
3929 nsIScrollableFrame* sf = GetScrollFrame();
3930 if (sf) {
3931 // It seems like it would make more sense for ScrollByLines to use
3932 // SMOOTH mode, but tests seem to depend on the synchronous behaviour.
3933 // Perhaps Web content does too.
3934 ScrollMode scrollMode = sf->IsSmoothScroll(aOptions.mBehavior)
3935 ? ScrollMode::SmoothMsd
3936 : ScrollMode::Instant;
3938 sf->ScrollBy(nsIntPoint(0, numLines), ScrollUnit::LINES, scrollMode);
3942 void nsGlobalWindowInner::ScrollByPages(int32_t numPages,
3943 const ScrollOptions& aOptions) {
3944 FlushPendingNotifications(FlushType::Layout);
3945 nsIScrollableFrame* sf = GetScrollFrame();
3946 if (sf) {
3947 // It seems like it would make more sense for ScrollByPages to use
3948 // SMOOTH mode, but tests seem to depend on the synchronous behaviour.
3949 // Perhaps Web content does too.
3950 ScrollMode scrollMode = sf->IsSmoothScroll(aOptions.mBehavior)
3951 ? ScrollMode::SmoothMsd
3952 : ScrollMode::Instant;
3954 sf->ScrollBy(nsIntPoint(0, numPages), ScrollUnit::PAGES, scrollMode);
3958 void nsGlobalWindowInner::MozScrollSnap() {
3959 FlushPendingNotifications(FlushType::Layout);
3960 nsIScrollableFrame* sf = GetScrollFrame();
3961 if (sf) {
3962 sf->ScrollSnap();
3966 void nsGlobalWindowInner::ClearTimeout(int32_t aHandle) {
3967 DebuggerNotificationDispatch(this, DebuggerNotificationType::ClearTimeout);
3969 if (aHandle > 0) {
3970 mTimeoutManager->ClearTimeout(aHandle, Timeout::Reason::eTimeoutOrInterval);
3974 void nsGlobalWindowInner::ClearInterval(int32_t aHandle) {
3975 DebuggerNotificationDispatch(this, DebuggerNotificationType::ClearInterval);
3977 if (aHandle > 0) {
3978 mTimeoutManager->ClearTimeout(aHandle, Timeout::Reason::eTimeoutOrInterval);
3982 void nsGlobalWindowInner::SetResizable(bool aResizable) const {
3983 // nop
3986 void nsGlobalWindowInner::CaptureEvents() {
3987 if (mDoc) {
3988 mDoc->WarnOnceAbout(DeprecatedOperations::eUseOfCaptureEvents);
3992 void nsGlobalWindowInner::ReleaseEvents() {
3993 if (mDoc) {
3994 mDoc->WarnOnceAbout(DeprecatedOperations::eUseOfReleaseEvents);
3998 Nullable<WindowProxyHolder> nsGlobalWindowInner::Open(const nsAString& aUrl,
3999 const nsAString& aName,
4000 const nsAString& aOptions,
4001 ErrorResult& aError) {
4002 FORWARD_TO_OUTER_OR_THROW(OpenOuter, (aUrl, aName, aOptions, aError), aError,
4003 nullptr);
4006 Nullable<WindowProxyHolder> nsGlobalWindowInner::OpenDialog(
4007 JSContext* aCx, const nsAString& aUrl, const nsAString& aName,
4008 const nsAString& aOptions, const Sequence<JS::Value>& aExtraArgument,
4009 ErrorResult& aError) {
4010 FORWARD_TO_OUTER_OR_THROW(
4011 OpenDialogOuter, (aCx, aUrl, aName, aOptions, aExtraArgument, aError),
4012 aError, nullptr);
4015 WindowProxyHolder nsGlobalWindowInner::GetFrames(ErrorResult& aError) {
4016 FORWARD_TO_OUTER_OR_THROW(GetFramesOuter, (), aError, Window());
4019 void nsGlobalWindowInner::PostMessageMoz(JSContext* aCx,
4020 JS::Handle<JS::Value> aMessage,
4021 const nsAString& aTargetOrigin,
4022 JS::Handle<JS::Value> aTransfer,
4023 nsIPrincipal& aSubjectPrincipal,
4024 ErrorResult& aError) {
4025 FORWARD_TO_OUTER_OR_THROW(
4026 PostMessageMozOuter,
4027 (aCx, aMessage, aTargetOrigin, aTransfer, aSubjectPrincipal, aError),
4028 aError, );
4031 void nsGlobalWindowInner::PostMessageMoz(JSContext* aCx,
4032 JS::Handle<JS::Value> aMessage,
4033 const nsAString& aTargetOrigin,
4034 const Sequence<JSObject*>& aTransfer,
4035 nsIPrincipal& aSubjectPrincipal,
4036 ErrorResult& aRv) {
4037 JS::Rooted<JS::Value> transferArray(aCx, JS::UndefinedValue());
4039 aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransfer,
4040 &transferArray);
4041 if (NS_WARN_IF(aRv.Failed())) {
4042 return;
4045 PostMessageMoz(aCx, aMessage, aTargetOrigin, transferArray, aSubjectPrincipal,
4046 aRv);
4049 void nsGlobalWindowInner::PostMessageMoz(
4050 JSContext* aCx, JS::Handle<JS::Value> aMessage,
4051 const WindowPostMessageOptions& aOptions, nsIPrincipal& aSubjectPrincipal,
4052 ErrorResult& aRv) {
4053 JS::Rooted<JS::Value> transferArray(aCx, JS::UndefinedValue());
4055 aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(
4056 aCx, aOptions.mTransfer, &transferArray);
4057 if (NS_WARN_IF(aRv.Failed())) {
4058 return;
4061 PostMessageMoz(aCx, aMessage, aOptions.mTargetOrigin, transferArray,
4062 aSubjectPrincipal, aRv);
4065 void nsGlobalWindowInner::Close(CallerType aCallerType, ErrorResult& aError) {
4066 FORWARD_TO_OUTER_OR_THROW(CloseOuter, (aCallerType == CallerType::System),
4067 aError, );
4070 nsresult nsGlobalWindowInner::Close() {
4071 FORWARD_TO_OUTER(Close, (), NS_ERROR_UNEXPECTED);
4074 bool nsGlobalWindowInner::IsInModalState() {
4075 FORWARD_TO_OUTER(IsInModalState, (), false);
4078 // static
4079 void nsGlobalWindowInner::NotifyDOMWindowDestroyed(
4080 nsGlobalWindowInner* aWindow) {
4081 nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
4082 if (observerService) {
4083 observerService->NotifyObservers(ToSupports(aWindow),
4084 DOM_WINDOW_DESTROYED_TOPIC, nullptr);
4088 void nsGlobalWindowInner::NotifyWindowIDDestroyed(const char* aTopic) {
4089 nsCOMPtr<nsIRunnable> runnable =
4090 new WindowDestroyedEvent(this, mWindowID, aTopic);
4091 Dispatch(runnable.forget());
4094 // static
4095 void nsGlobalWindowInner::NotifyDOMWindowFrozen(nsGlobalWindowInner* aWindow) {
4096 if (aWindow) {
4097 nsCOMPtr<nsIObserverService> observerService =
4098 services::GetObserverService();
4099 if (observerService) {
4100 observerService->NotifyObservers(ToSupports(aWindow),
4101 DOM_WINDOW_FROZEN_TOPIC, nullptr);
4106 // static
4107 void nsGlobalWindowInner::NotifyDOMWindowThawed(nsGlobalWindowInner* aWindow) {
4108 if (aWindow) {
4109 nsCOMPtr<nsIObserverService> observerService =
4110 services::GetObserverService();
4111 if (observerService) {
4112 observerService->NotifyObservers(ToSupports(aWindow),
4113 DOM_WINDOW_THAWED_TOPIC, nullptr);
4118 Element* nsGlobalWindowInner::GetFrameElement(nsIPrincipal& aSubjectPrincipal,
4119 ErrorResult& aError) {
4120 FORWARD_TO_OUTER_OR_THROW(GetFrameElement, (aSubjectPrincipal), aError,
4121 nullptr);
4124 Element* nsGlobalWindowInner::GetRealFrameElement(ErrorResult& aError) {
4125 FORWARD_TO_OUTER_OR_THROW(GetFrameElement, (), aError, nullptr);
4128 void nsGlobalWindowInner::UpdateCommands(const nsAString& anAction) {
4129 if (GetOuterWindowInternal()) {
4130 GetOuterWindowInternal()->UpdateCommands(anAction);
4134 Selection* nsGlobalWindowInner::GetSelection(ErrorResult& aError) {
4135 FORWARD_TO_OUTER_OR_THROW(GetSelectionOuter, (), aError, nullptr);
4138 WebTaskScheduler* nsGlobalWindowInner::Scheduler() {
4139 if (!mWebTaskScheduler) {
4140 mWebTaskScheduler = WebTaskScheduler::CreateForMainThread(this);
4142 MOZ_ASSERT(mWebTaskScheduler);
4143 return mWebTaskScheduler;
4146 bool nsGlobalWindowInner::Find(const nsAString& aString, bool aCaseSensitive,
4147 bool aBackwards, bool aWrapAround,
4148 bool aWholeWord, bool aSearchInFrames,
4149 bool aShowDialog, ErrorResult& aError) {
4150 FORWARD_TO_OUTER_OR_THROW(FindOuter,
4151 (aString, aCaseSensitive, aBackwards, aWrapAround,
4152 aWholeWord, aSearchInFrames, aShowDialog, aError),
4153 aError, false);
4156 void nsGlobalWindowInner::GetOrigin(nsAString& aOrigin) {
4157 nsContentUtils::GetWebExposedOriginSerialization(GetPrincipal(), aOrigin);
4160 // See also AutoJSAPI::ReportException
4161 void nsGlobalWindowInner::ReportError(JSContext* aCx,
4162 JS::Handle<JS::Value> aError,
4163 CallerType aCallerType,
4164 ErrorResult& aRv) {
4165 if (MOZ_UNLIKELY(!HasActiveDocument())) {
4166 return aRv.Throw(NS_ERROR_XPC_SECURITY_MANAGER_VETO);
4169 JS::ErrorReportBuilder jsReport(aCx);
4170 JS::ExceptionStack exnStack(aCx, aError, nullptr);
4171 if (!jsReport.init(aCx, exnStack, JS::ErrorReportBuilder::NoSideEffects)) {
4172 return aRv.NoteJSContextException(aCx);
4175 RefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport();
4176 bool isChrome = aCallerType == CallerType::System;
4177 xpcReport->Init(jsReport.report(), jsReport.toStringResult().c_str(),
4178 isChrome, WindowID());
4180 JS::RootingContext* rcx = JS::RootingContext::get(aCx);
4181 DispatchScriptErrorEvent(this, rcx, xpcReport, exnStack.exception(),
4182 exnStack.stack());
4185 void nsGlobalWindowInner::Atob(const nsAString& aAsciiBase64String,
4186 nsAString& aBinaryData, ErrorResult& aError) {
4187 aError = nsContentUtils::Atob(aAsciiBase64String, aBinaryData);
4190 void nsGlobalWindowInner::Btoa(const nsAString& aBinaryData,
4191 nsAString& aAsciiBase64String,
4192 ErrorResult& aError) {
4193 aError = nsContentUtils::Btoa(aBinaryData, aAsciiBase64String);
4196 //*****************************************************************************
4197 // EventTarget
4198 //*****************************************************************************
4200 nsPIDOMWindowOuter* nsGlobalWindowInner::GetOwnerGlobalForBindingsInternal() {
4201 return nsPIDOMWindowOuter::GetFromCurrentInner(this);
4204 bool nsGlobalWindowInner::DispatchEvent(Event& aEvent, CallerType aCallerType,
4205 ErrorResult& aRv) {
4206 if (!IsCurrentInnerWindow()) {
4207 NS_WARNING(
4208 "DispatchEvent called on non-current inner window, dropping. "
4209 "Please check the window in the caller instead.");
4210 aRv.Throw(NS_ERROR_FAILURE);
4211 return false;
4214 if (!mDoc) {
4215 aRv.Throw(NS_ERROR_FAILURE);
4216 return false;
4219 // Obtain a presentation shell
4220 RefPtr<nsPresContext> presContext = mDoc->GetPresContext();
4222 nsEventStatus status = nsEventStatus_eIgnore;
4223 nsresult rv = EventDispatcher::DispatchDOMEvent(this, nullptr, &aEvent,
4224 presContext, &status);
4225 bool retval = !aEvent.DefaultPrevented(aCallerType);
4226 if (NS_FAILED(rv)) {
4227 aRv.Throw(rv);
4229 return retval;
4232 mozilla::Maybe<mozilla::dom::EventCallbackDebuggerNotificationType>
4233 nsGlobalWindowInner::GetDebuggerNotificationType() const {
4234 return mozilla::Some(
4235 mozilla::dom::EventCallbackDebuggerNotificationType::Global);
4238 bool nsGlobalWindowInner::ComputeDefaultWantsUntrusted(ErrorResult& aRv) {
4239 return !nsContentUtils::IsChromeDoc(mDoc);
4242 EventListenerManager* nsGlobalWindowInner::GetOrCreateListenerManager() {
4243 if (!mListenerManager) {
4244 mListenerManager =
4245 new EventListenerManager(static_cast<EventTarget*>(this));
4248 return mListenerManager;
4251 EventListenerManager* nsGlobalWindowInner::GetExistingListenerManager() const {
4252 return mListenerManager;
4255 mozilla::dom::DebuggerNotificationManager*
4256 nsGlobalWindowInner::GetOrCreateDebuggerNotificationManager() {
4257 if (!mDebuggerNotificationManager) {
4258 mDebuggerNotificationManager = new DebuggerNotificationManager(this);
4261 return mDebuggerNotificationManager;
4264 mozilla::dom::DebuggerNotificationManager*
4265 nsGlobalWindowInner::GetExistingDebuggerNotificationManager() {
4266 return mDebuggerNotificationManager;
4269 //*****************************************************************************
4270 // nsGlobalWindowInner::nsPIDOMWindow
4271 //*****************************************************************************
4273 Location* nsGlobalWindowInner::Location() {
4274 if (!mLocation) {
4275 mLocation = new dom::Location(this);
4278 return mLocation;
4281 void nsGlobalWindowInner::MaybeUpdateTouchState() {
4282 if (mMayHaveTouchEventListener) {
4283 nsCOMPtr<nsIObserverService> observerService =
4284 services::GetObserverService();
4286 if (observerService) {
4287 observerService->NotifyObservers(static_cast<nsIDOMWindow*>(this),
4288 DOM_TOUCH_LISTENER_ADDED, nullptr);
4293 void nsGlobalWindowInner::EnableGamepadUpdates() {
4294 if (mHasGamepad) {
4295 RefPtr<GamepadManager> gamepadManager(GamepadManager::GetService());
4296 if (gamepadManager) {
4297 gamepadManager->AddListener(this);
4302 void nsGlobalWindowInner::DisableGamepadUpdates() {
4303 if (mHasGamepad) {
4304 RefPtr<GamepadManager> gamepadManager(GamepadManager::GetService());
4305 if (gamepadManager) {
4306 gamepadManager->RemoveListener(this);
4311 void nsGlobalWindowInner::EnableVRUpdates() {
4312 // We need to create a VREventObserver before we can either detect XR runtimes
4313 // or start an XR session
4314 if (!mVREventObserver && (mHasXRSession || mXRRuntimeDetectionInFlight)) {
4315 // Assert that we are not creating the observer while IsDying() as
4316 // that would result in a leak. VREventObserver holds a RefPtr to
4317 // this nsGlobalWindowInner and would prevent it from being deallocated.
4318 MOZ_ASSERT(!IsDying(),
4319 "Creating a VREventObserver for an nsGlobalWindow that is "
4320 "dying would cause it to leak.");
4321 mVREventObserver = new VREventObserver(this);
4323 // If the content has an XR session, then we need to tell
4324 // VREventObserver that there is VR activity.
4325 if (mHasXRSession) {
4326 nsPIDOMWindowOuter* outer = GetOuterWindow();
4327 if (outer && !outer->IsBackground()) {
4328 StartVRActivity();
4333 void nsGlobalWindowInner::DisableVRUpdates() {
4334 if (mVREventObserver) {
4335 mVREventObserver->DisconnectFromOwner();
4336 mVREventObserver = nullptr;
4340 void nsGlobalWindowInner::ResetVRTelemetry(bool aUpdate) {
4341 if (mVREventObserver) {
4342 mVREventObserver->UpdateSpentTimeIn2DTelemetry(aUpdate);
4346 void nsGlobalWindowInner::StartVRActivity() {
4348 * If the content has an XR session, tell
4349 * the VREventObserver that the window is accessing
4350 * VR devices.
4352 * It's possible to have a VREventObserver without
4353 * and XR session, if we are using it to get updates
4354 * about XR runtime enumeration. In this case,
4355 * we would not tell the VREventObserver that
4356 * we are accessing VR devices.
4358 if (mVREventObserver && mHasXRSession) {
4359 mVREventObserver->StartActivity();
4363 void nsGlobalWindowInner::StopVRActivity() {
4365 * If the content has an XR session, tell
4366 * the VReventObserver that the window is no longer
4367 * accessing VR devices. This does not stop the
4368 * XR session itself, which may be resumed with
4369 * EnableVRUpdates.
4370 * It's possible to have a VREventObserver without
4371 * and XR session, if we are using it to get updates
4372 * about XR runtime enumeration. In this case,
4373 * we would not tell the VREventObserver that
4374 * we ending an activity that accesses VR devices.
4376 if (mVREventObserver && mHasXRSession) {
4377 mVREventObserver->StopActivity();
4381 void nsGlobalWindowInner::SetFocusedElement(Element* aElement,
4382 uint32_t aFocusMethod,
4383 bool aNeedsFocus) {
4384 if (aElement && aElement->GetComposedDoc() != mDoc) {
4385 NS_WARNING("Trying to set focus to a node from a wrong document");
4386 return;
4389 if (IsDying()) {
4390 NS_ASSERTION(!aElement, "Trying to focus cleaned up window!");
4391 aElement = nullptr;
4392 aNeedsFocus = false;
4394 if (mFocusedElement != aElement) {
4395 UpdateCanvasFocus(false, aElement);
4396 mFocusedElement = aElement;
4397 // TODO: Maybe this should be set on refocus too?
4398 mFocusMethod = aFocusMethod & nsIFocusManager::METHOD_MASK;
4401 if (mFocusedElement) {
4402 // if a node was focused by a keypress, turn on focus rings for the
4403 // window.
4404 if (mFocusMethod & nsIFocusManager::FLAG_BYKEY) {
4405 mUnknownFocusMethodShouldShowOutline = true;
4406 mFocusByKeyOccurred = true;
4407 } else if (nsFocusManager::GetFocusMoveActionCause(mFocusMethod) !=
4408 widget::InputContextAction::CAUSE_UNKNOWN) {
4409 mUnknownFocusMethodShouldShowOutline = false;
4410 } else if (aFocusMethod & nsIFocusManager::FLAG_NOSHOWRING) {
4411 // If we get focused via script, and script has explicitly opted out of
4412 // outlines via FLAG_NOSHOWRING, we don't want to make a refocus start
4413 // showing outlines.
4414 mUnknownFocusMethodShouldShowOutline = false;
4418 if (aNeedsFocus) {
4419 mNeedsFocus = aNeedsFocus;
4423 uint32_t nsGlobalWindowInner::GetFocusMethod() { return mFocusMethod; }
4425 bool nsGlobalWindowInner::ShouldShowFocusRing() {
4426 if (mFocusByKeyOccurred &&
4427 StaticPrefs::browser_display_always_show_rings_after_key_focus()) {
4428 return true;
4430 return StaticPrefs::browser_display_show_focus_rings();
4433 bool nsGlobalWindowInner::TakeFocus(bool aFocus, uint32_t aFocusMethod) {
4434 if (IsDying()) {
4435 return false;
4438 if (aFocus) {
4439 mFocusMethod = aFocusMethod & nsIFocusManager::METHOD_MASK;
4442 if (mHasFocus != aFocus) {
4443 mHasFocus = aFocus;
4444 UpdateCanvasFocus(true, mFocusedElement);
4447 // if mNeedsFocus is true, then the document has not yet received a
4448 // document-level focus event. If there is a root content node, then return
4449 // true to tell the calling focus manager that a focus event is expected. If
4450 // there is no root content node, the document hasn't loaded enough yet, or
4451 // there isn't one and there is no point in firing a focus event.
4452 if (aFocus && mNeedsFocus && mDoc && mDoc->GetRootElement() != nullptr) {
4453 mNeedsFocus = false;
4454 return true;
4457 mNeedsFocus = false;
4458 return false;
4461 void nsGlobalWindowInner::SetReadyForFocus() {
4462 bool oldNeedsFocus = mNeedsFocus;
4463 mNeedsFocus = false;
4465 if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
4466 nsCOMPtr<nsPIDOMWindowOuter> outerWindow = GetOuterWindow();
4467 fm->WindowShown(outerWindow, oldNeedsFocus);
4471 void nsGlobalWindowInner::PageHidden() {
4472 // the window is being hidden, so tell the focus manager that the frame is
4473 // no longer valid. Use the persisted field to determine if the document
4474 // is being destroyed.
4476 if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
4477 nsCOMPtr<nsPIDOMWindowOuter> outerWindow = GetOuterWindow();
4478 fm->WindowHidden(outerWindow, nsFocusManager::GenerateFocusActionId());
4481 mNeedsFocus = true;
4484 class HashchangeCallback : public Runnable {
4485 public:
4486 HashchangeCallback(const nsAString& aOldURL, const nsAString& aNewURL,
4487 nsGlobalWindowInner* aWindow)
4488 : mozilla::Runnable("HashchangeCallback"), mWindow(aWindow) {
4489 MOZ_ASSERT(mWindow);
4490 mOldURL.Assign(aOldURL);
4491 mNewURL.Assign(aNewURL);
4494 NS_IMETHOD Run() override {
4495 MOZ_ASSERT(NS_IsMainThread(), "Should be called on the main thread.");
4496 return mWindow->FireHashchange(mOldURL, mNewURL);
4499 private:
4500 nsString mOldURL;
4501 nsString mNewURL;
4502 RefPtr<nsGlobalWindowInner> mWindow;
4505 nsresult nsGlobalWindowInner::DispatchAsyncHashchange(nsIURI* aOldURI,
4506 nsIURI* aNewURI) {
4507 // Make sure that aOldURI and aNewURI are identical up to the '#', and that
4508 // their hashes are different.
4509 bool equal = false;
4510 NS_ENSURE_STATE(NS_SUCCEEDED(aOldURI->EqualsExceptRef(aNewURI, &equal)) &&
4511 equal);
4512 nsAutoCString oldHash, newHash;
4513 bool oldHasHash, newHasHash;
4514 NS_ENSURE_STATE(NS_SUCCEEDED(aOldURI->GetRef(oldHash)) &&
4515 NS_SUCCEEDED(aNewURI->GetRef(newHash)) &&
4516 NS_SUCCEEDED(aOldURI->GetHasRef(&oldHasHash)) &&
4517 NS_SUCCEEDED(aNewURI->GetHasRef(&newHasHash)) &&
4518 (oldHasHash != newHasHash || !oldHash.Equals(newHash)));
4520 nsAutoCString oldSpec, newSpec;
4521 nsresult rv = aOldURI->GetSpec(oldSpec);
4522 NS_ENSURE_SUCCESS(rv, rv);
4523 rv = aNewURI->GetSpec(newSpec);
4524 NS_ENSURE_SUCCESS(rv, rv);
4526 NS_ConvertUTF8toUTF16 oldWideSpec(oldSpec);
4527 NS_ConvertUTF8toUTF16 newWideSpec(newSpec);
4529 nsCOMPtr<nsIRunnable> callback =
4530 new HashchangeCallback(oldWideSpec, newWideSpec, this);
4531 return Dispatch(callback.forget());
4534 nsresult nsGlobalWindowInner::FireHashchange(const nsAString& aOldURL,
4535 const nsAString& aNewURL) {
4536 // Don't do anything if the window is frozen.
4537 if (IsFrozen()) {
4538 return NS_OK;
4541 // Get a presentation shell for use in creating the hashchange event.
4542 NS_ENSURE_STATE(IsCurrentInnerWindow());
4544 HashChangeEventInit init;
4545 init.mNewURL = aNewURL;
4546 init.mOldURL = aOldURL;
4548 RefPtr<HashChangeEvent> event =
4549 HashChangeEvent::Constructor(this, u"hashchange"_ns, init);
4551 event->SetTrusted(true);
4553 ErrorResult rv;
4554 DispatchEvent(*event, rv);
4555 return rv.StealNSResult();
4558 nsresult nsGlobalWindowInner::DispatchSyncPopState() {
4559 NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
4560 "Must be safe to run script here.");
4562 // Bail if the window is frozen.
4563 if (IsFrozen()) {
4564 return NS_OK;
4567 AutoJSAPI jsapi;
4568 bool result = jsapi.Init(this);
4569 NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
4571 JSContext* cx = jsapi.cx();
4573 // Get the document's pending state object -- it contains the data we're
4574 // going to send along with the popstate event. The object is serialized
4575 // using structured clone.
4576 JS::Rooted<JS::Value> stateJSValue(cx);
4577 nsresult rv = mDoc->GetStateObject(&stateJSValue);
4578 NS_ENSURE_SUCCESS(rv, rv);
4580 if (!JS_WrapValue(cx, &stateJSValue)) {
4581 return NS_ERROR_OUT_OF_MEMORY;
4584 RootedDictionary<PopStateEventInit> init(cx);
4585 init.mState = stateJSValue;
4587 RefPtr<PopStateEvent> event =
4588 PopStateEvent::Constructor(this, u"popstate"_ns, init);
4589 event->SetTrusted(true);
4590 event->SetTarget(this);
4592 ErrorResult err;
4593 DispatchEvent(*event, err);
4594 return err.StealNSResult();
4597 //-------------------------------------------------------
4598 // Tells the HTMLFrame/CanvasFrame that is now has focus
4599 void nsGlobalWindowInner::UpdateCanvasFocus(bool aFocusChanged,
4600 nsIContent* aNewContent) {
4601 // this is called from the inner window so use GetDocShell
4602 nsIDocShell* docShell = GetDocShell();
4603 if (!docShell) return;
4605 bool editable;
4606 docShell->GetEditable(&editable);
4607 if (editable) return;
4609 PresShell* presShell = docShell->GetPresShell();
4610 if (!presShell || !mDoc) {
4611 return;
4614 Element* rootElement = mDoc->GetRootElement();
4615 if (rootElement) {
4616 if ((mHasFocus || aFocusChanged) &&
4617 (mFocusedElement == rootElement || aNewContent == rootElement)) {
4618 nsCanvasFrame* canvasFrame = presShell->GetCanvasFrame();
4619 if (canvasFrame) {
4620 canvasFrame->SetHasFocus(mHasFocus && rootElement == aNewContent);
4623 } else {
4624 // XXXbz I would expect that there is never a canvasFrame in this case...
4625 nsCanvasFrame* canvasFrame = presShell->GetCanvasFrame();
4626 if (canvasFrame) {
4627 canvasFrame->SetHasFocus(false);
4632 already_AddRefed<nsICSSDeclaration> nsGlobalWindowInner::GetComputedStyle(
4633 Element& aElt, const nsAString& aPseudoElt, ErrorResult& aError) {
4634 return GetComputedStyleHelper(aElt, aPseudoElt, false, aError);
4637 already_AddRefed<nsICSSDeclaration>
4638 nsGlobalWindowInner::GetDefaultComputedStyle(Element& aElt,
4639 const nsAString& aPseudoElt,
4640 ErrorResult& aError) {
4641 return GetComputedStyleHelper(aElt, aPseudoElt, true, aError);
4644 already_AddRefed<nsICSSDeclaration> nsGlobalWindowInner::GetComputedStyleHelper(
4645 Element& aElt, const nsAString& aPseudoElt, bool aDefaultStylesOnly,
4646 ErrorResult& aError) {
4647 FORWARD_TO_OUTER_OR_THROW(GetComputedStyleHelperOuter,
4648 (aElt, aPseudoElt, aDefaultStylesOnly, aError),
4649 aError, nullptr);
4652 void nsGlobalWindowInner::MaybeNotifyStorageKeyUsed() {
4653 // Only notify once per window lifetime.
4654 if (hasNotifiedStorageKeyUsed) {
4655 return;
4657 nsresult rv =
4658 BounceTrackingStorageObserver::OnInitialStorageAccess(GetWindowContext());
4659 if (NS_WARN_IF(NS_FAILED(rv))) {
4660 return;
4662 hasNotifiedStorageKeyUsed = true;
4665 Storage* nsGlobalWindowInner::GetSessionStorage(ErrorResult& aError) {
4666 nsIPrincipal* principal = GetPrincipal();
4667 nsIPrincipal* storagePrincipal;
4668 if (StaticPrefs::
4669 privacy_partition_always_partition_third_party_non_cookie_storage_exempt_sessionstorage()) {
4670 storagePrincipal = GetEffectiveCookiePrincipal();
4671 } else {
4672 storagePrincipal = GetEffectiveStoragePrincipal();
4674 BrowsingContext* browsingContext = GetBrowsingContext();
4676 if (!principal || !storagePrincipal || !browsingContext ||
4677 !Storage::StoragePrefIsEnabled()) {
4678 return nullptr;
4681 if (mSessionStorage) {
4682 MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
4683 ("nsGlobalWindowInner %p has %p sessionStorage", this,
4684 mSessionStorage.get()));
4685 bool canAccess =
4686 principal->Subsumes(mSessionStorage->Principal()) &&
4687 storagePrincipal->Subsumes(mSessionStorage->StoragePrincipal());
4688 if (!canAccess) {
4689 mSessionStorage = nullptr;
4693 if (!mSessionStorage) {
4694 nsString documentURI;
4695 if (mDoc) {
4696 aError = mDoc->GetDocumentURI(documentURI);
4697 if (NS_WARN_IF(aError.Failed())) {
4698 return nullptr;
4702 if (!mDoc) {
4703 aError.Throw(NS_ERROR_FAILURE);
4704 return nullptr;
4707 // If the document's sandboxed origin flag is set, then accessing
4708 // sessionStorage is prohibited.
4709 if (mDoc->GetSandboxFlags() & SANDBOXED_ORIGIN) {
4710 aError.ThrowSecurityError(
4711 "Forbidden in a sandboxed document without the 'allow-same-origin' "
4712 "flag.");
4713 return nullptr;
4716 uint32_t rejectedReason = 0;
4717 StorageAccess access = StorageAllowedForWindow(this, &rejectedReason);
4719 // SessionStorage is an ephemeral per-tab per-origin storage that only lives
4720 // as long as the tab is open, although it may survive browser restarts
4721 // thanks to the session store. So we interpret storage access differently
4722 // than we would for persistent per-origin storage like LocalStorage and so
4723 // it may be okay to provide SessionStorage even when we receive a value of
4724 // eDeny.
4726 // ContentBlocking::ShouldAllowAccessFor will return false for 3 main
4727 // reasons.
4729 // 1. Cookies are entirely blocked due to a per-origin permission
4730 // (nsICookiePermission::ACCESS_DENY for the top-level principal or this
4731 // window's principal) or the very broad BEHAVIOR_REJECT. This will return
4732 // eDeny with a reason of STATE_COOKIES_BLOCKED_BY_PERMISSION or
4733 // STATE_COOKIES_BLOCKED_ALL.
4735 // 2. Third-party cookies are limited via BEHAVIOR_REJECT_FOREIGN and
4736 // BEHAVIOR_LIMIT_FOREIGN and this is a third-party window. This will return
4737 // eDeny with a reason of STATE_COOKIES_BLOCKED_FOREIGN.
4739 // 3. Tracking protection (BEHAVIOR_REJECT_TRACKER and
4740 // BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN) is in effect and
4741 // IsThirdPartyTrackingResourceWindow() returned true and there wasn't a
4742 // permission that allows it. This will return ePartitionTrackersOrDeny with
4743 // a reason of STATE_COOKIES_BLOCKED_TRACKER or
4744 // STATE_COOKIES_BLOCKED_SOCIALTRACKER.
4746 // In the 1st case, the user has explicitly indicated that they don't want
4747 // to allow any storage to the origin or all origins and so we throw an
4748 // error and deny access to SessionStorage. In the 2nd case, a legacy
4749 // decision reasoned that there's no harm in providing SessionStorage
4750 // because the information is not durable and cannot escape the current tab.
4751 // The rationale is similar for the 3rd case.
4752 if (access == StorageAccess::eDeny &&
4753 rejectedReason !=
4754 nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN) {
4755 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
4756 return nullptr;
4759 const RefPtr<SessionStorageManager> storageManager =
4760 browsingContext->GetSessionStorageManager();
4761 if (!storageManager) {
4762 aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
4763 return nullptr;
4766 RefPtr<Storage> storage;
4767 aError = storageManager->CreateStorage(this, principal, storagePrincipal,
4768 documentURI, IsPrivateBrowsing(),
4769 getter_AddRefs(storage));
4770 if (aError.Failed()) {
4771 return nullptr;
4774 mSessionStorage = storage;
4775 MOZ_ASSERT(mSessionStorage);
4777 MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
4778 ("nsGlobalWindowInner %p tried to get a new sessionStorage %p",
4779 this, mSessionStorage.get()));
4781 if (!mSessionStorage) {
4782 aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
4783 return nullptr;
4787 MaybeNotifyStorageKeyUsed();
4789 MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
4790 ("nsGlobalWindowInner %p returns %p sessionStorage", this,
4791 mSessionStorage.get()));
4793 return mSessionStorage;
4796 Storage* nsGlobalWindowInner::GetLocalStorage(ErrorResult& aError) {
4797 if (!Storage::StoragePrefIsEnabled()) {
4798 return nullptr;
4801 // If the document's sandboxed origin flag is set, then accessing localStorage
4802 // is prohibited.
4803 if (mDoc && mDoc->GetSandboxFlags() & SANDBOXED_ORIGIN) {
4804 aError.ThrowSecurityError(
4805 "Forbidden in a sandboxed document without the 'allow-same-origin' "
4806 "flag.");
4807 return nullptr;
4810 // LocalStorage needs to be exposed in every context except for sandboxes and
4811 // NullPrincipals (data: URLs, for instance). But we need to keep data
4812 // separate in some scenarios: private-browsing and partitioned trackers.
4813 // In private-browsing, LocalStorage keeps data in memory, and it shares
4814 // StorageEvents just with other origins in the same private-browsing
4815 // environment.
4816 // For Partitioned Trackers, we expose a partitioned LocalStorage, which
4817 // doesn't share data with other contexts, and it's just in memory.
4818 // Partitioned localStorage is available only for trackers listed in the
4819 // privacy.restrict3rdpartystorage.partitionedHosts pref. See
4820 // nsContentUtils::IsURIInPrefList to know the syntax for the pref value.
4821 // This is a temporary web-compatibility hack.
4823 StorageAccess access = StorageAllowedForWindow(this);
4825 // We allow partitioned localStorage only to some hosts.
4826 bool isolated = false;
4827 if (ShouldPartitionStorage(access)) {
4828 if (!mDoc) {
4829 access = StorageAccess::eDeny;
4830 } else if (!StoragePartitioningEnabled(access, mDoc->CookieJarSettings())) {
4831 static const char* kPrefName =
4832 "privacy.restrict3rdpartystorage.partitionedHosts";
4834 bool isInList = false;
4835 mDoc->NodePrincipal()->IsURIInPrefList(kPrefName, &isInList);
4836 if (!isInList) {
4837 access = StorageAccess::eDeny;
4838 } else {
4839 isolated = true;
4844 if (access == StorageAccess::eDeny) {
4845 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
4846 return nullptr;
4849 nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
4850 if (mDoc) {
4851 cookieJarSettings = mDoc->CookieJarSettings();
4852 } else {
4853 cookieJarSettings = net::CookieJarSettings::GetBlockingAll(
4854 ShouldResistFingerprinting(RFPTarget::IsAlwaysEnabledForPrecompute));
4857 // Note that this behavior is observable: if we grant storage permission to a
4858 // tracker, we pass from the partitioned LocalStorage (or a partitioned cookie
4859 // jar) to the 'normal' one. The previous data is lost and the 2
4860 // window.localStorage objects, before and after the permission granted, will
4861 // be different.
4862 if (mLocalStorage) {
4863 if ((mLocalStorage->Type() == (isolated ? Storage::ePartitionedLocalStorage
4864 : Storage::eLocalStorage)) &&
4865 (mLocalStorage->StoragePrincipal() == GetEffectiveStoragePrincipal())) {
4866 return mLocalStorage;
4869 // storage needs change
4870 mLocalStorage = nullptr;
4873 MOZ_ASSERT(!mLocalStorage);
4875 if (!isolated) {
4876 RefPtr<Storage> storage;
4878 if (NextGenLocalStorageEnabled()) {
4879 aError = LSObject::CreateForWindow(this, getter_AddRefs(storage));
4880 } else {
4881 nsresult rv;
4882 nsCOMPtr<nsIDOMStorageManager> storageManager =
4883 do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv);
4884 if (NS_FAILED(rv)) {
4885 aError.Throw(rv);
4886 return nullptr;
4889 nsString documentURI;
4890 if (mDoc) {
4891 aError = mDoc->GetDocumentURI(documentURI);
4892 if (NS_WARN_IF(aError.Failed())) {
4893 return nullptr;
4897 nsIPrincipal* principal = GetPrincipal();
4898 if (!principal) {
4899 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
4900 return nullptr;
4903 nsIPrincipal* storagePrincipal = GetEffectiveStoragePrincipal();
4904 if (!storagePrincipal) {
4905 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
4906 return nullptr;
4909 aError = storageManager->CreateStorage(this, principal, storagePrincipal,
4910 documentURI, IsPrivateBrowsing(),
4911 getter_AddRefs(storage));
4914 if (aError.Failed()) {
4915 return nullptr;
4918 mLocalStorage = storage;
4919 } else {
4920 nsresult rv;
4921 nsCOMPtr<nsIDOMSessionStorageManager> storageManager =
4922 do_GetService("@mozilla.org/dom/sessionStorage-manager;1", &rv);
4923 if (NS_FAILED(rv)) {
4924 aError.Throw(rv);
4925 return nullptr;
4928 nsIPrincipal* principal = GetPrincipal();
4929 if (!principal) {
4930 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
4931 return nullptr;
4934 nsIPrincipal* storagePrincipal = GetEffectiveStoragePrincipal();
4935 if (!storagePrincipal) {
4936 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
4937 return nullptr;
4940 RefPtr<SessionStorageCache> cache;
4941 if (isolated) {
4942 cache = new SessionStorageCache();
4943 } else {
4944 // This will clone the session storage if it exists.
4945 rv = storageManager->GetSessionStorageCache(principal, storagePrincipal,
4946 &cache);
4947 if (NS_FAILED(rv)) {
4948 aError.Throw(rv);
4949 return nullptr;
4953 mLocalStorage =
4954 new PartitionedLocalStorage(this, principal, storagePrincipal, cache);
4957 MaybeNotifyStorageKeyUsed();
4959 MOZ_ASSERT(mLocalStorage);
4960 MOZ_ASSERT(
4961 mLocalStorage->Type() ==
4962 (isolated ? Storage::ePartitionedLocalStorage : Storage::eLocalStorage));
4963 return mLocalStorage;
4966 IDBFactory* nsGlobalWindowInner::GetIndexedDB(JSContext* aCx,
4967 ErrorResult& aError) {
4968 if (!mIndexedDB) {
4969 // This may keep mIndexedDB null without setting an error.
4970 auto res = IDBFactory::CreateForWindow(this);
4971 if (res.isErr()) {
4972 aError = res.unwrapErr();
4973 } else {
4974 mIndexedDB = res.unwrap();
4978 MaybeNotifyStorageKeyUsed();
4980 return mIndexedDB;
4983 //*****************************************************************************
4984 // nsGlobalWindowInner::nsIInterfaceRequestor
4985 //*****************************************************************************
4987 NS_IMETHODIMP
4988 nsGlobalWindowInner::GetInterface(const nsIID& aIID, void** aSink) {
4989 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
4990 NS_ENSURE_TRUE(outer, NS_ERROR_NOT_INITIALIZED);
4992 nsresult rv = outer->GetInterfaceInternal(aIID, aSink);
4993 if (rv == NS_ERROR_NO_INTERFACE) {
4994 return QueryInterface(aIID, aSink);
4996 return rv;
4999 void nsGlobalWindowInner::GetInterface(JSContext* aCx,
5000 JS::Handle<JS::Value> aIID,
5001 JS::MutableHandle<JS::Value> aRetval,
5002 ErrorResult& aError) {
5003 dom::GetInterface(aCx, this, aIID, aRetval, aError);
5006 already_AddRefed<CacheStorage> nsGlobalWindowInner::GetCaches(
5007 ErrorResult& aRv) {
5008 if (!mCacheStorage) {
5009 bool forceTrustedOrigin =
5010 GetBrowsingContext() &&
5011 GetBrowsingContext()->Top()->GetServiceWorkersTestingEnabled();
5012 mCacheStorage = CacheStorage::CreateOnMainThread(
5013 cache::DEFAULT_NAMESPACE, this, GetEffectiveStoragePrincipal(),
5014 forceTrustedOrigin, aRv);
5017 RefPtr<CacheStorage> ref = mCacheStorage;
5018 return ref.forget();
5021 void nsGlobalWindowInner::FireOfflineStatusEventIfChanged() {
5022 if (!IsCurrentInnerWindow()) return;
5024 // Don't fire an event if the status hasn't changed
5025 if (mWasOffline == NS_IsOffline()) {
5026 return;
5029 mWasOffline = !mWasOffline;
5031 nsAutoString name;
5032 if (mWasOffline) {
5033 name.AssignLiteral("offline");
5034 } else {
5035 name.AssignLiteral("online");
5037 nsContentUtils::DispatchTrustedEvent(mDoc, this, name, CanBubble::eNo,
5038 Cancelable::eNo);
5041 nsGlobalWindowInner::SlowScriptResponse
5042 nsGlobalWindowInner::ShowSlowScriptDialog(JSContext* aCx,
5043 const nsString& aAddonId,
5044 const double aDuration) {
5045 nsresult rv;
5047 if (Preferences::GetBool("dom.always_stop_slow_scripts")) {
5048 return KillSlowScript;
5051 // If it isn't safe to run script, then it isn't safe to bring up the prompt
5052 // (since that spins the event loop). In that (rare) case, we just kill the
5053 // script and report a warning.
5054 if (!nsContentUtils::IsSafeToRunScript()) {
5055 JS::WarnASCII(aCx, "A long running script was terminated");
5056 return KillSlowScript;
5059 // If our document is not active, just kill the script: we've been unloaded
5060 if (!HasActiveDocument()) {
5061 return KillSlowScript;
5064 // Check if we should offer the option to debug
5065 JS::AutoFilename filename;
5066 uint32_t lineno;
5067 // Computing the line number can be very expensive (see bug 1330231 for
5068 // example), and we don't use the line number anywhere except than in the
5069 // parent process, so we avoid computing it elsewhere. This gives us most of
5070 // the wins we are interested in, since the source of the slowness here is
5071 // minified scripts which is more common in Web content that is loaded in the
5072 // content process.
5073 uint32_t* linenop = XRE_IsParentProcess() ? &lineno : nullptr;
5074 bool hasFrame = JS::DescribeScriptedCaller(aCx, &filename, linenop);
5076 // Record the slow script event if we haven't done so already for this inner
5077 // window (which represents a particular page to the user).
5078 if (!mHasHadSlowScript) {
5079 Telemetry::Accumulate(Telemetry::SLOW_SCRIPT_PAGE_COUNT, 1);
5081 mHasHadSlowScript = true;
5083 // Override the cursor to something that we're sure the user can see.
5084 SetCursor("auto"_ns, IgnoreErrors());
5086 if (XRE_IsContentProcess() && ProcessHangMonitor::Get()) {
5087 ProcessHangMonitor::SlowScriptAction action;
5088 RefPtr<ProcessHangMonitor> monitor = ProcessHangMonitor::Get();
5089 nsIDocShell* docShell = GetDocShell();
5090 nsCOMPtr<nsIBrowserChild> child =
5091 docShell ? docShell->GetBrowserChild() : nullptr;
5092 action =
5093 monitor->NotifySlowScript(child, filename.get(), aAddonId, aDuration);
5094 if (action == ProcessHangMonitor::Terminate) {
5095 return KillSlowScript;
5098 if (action == ProcessHangMonitor::StartDebugger) {
5099 // Spin a nested event loop so that the debugger in the parent can fetch
5100 // any information it needs. Once the debugger has started, return to the
5101 // script.
5102 RefPtr<nsGlobalWindowOuter> outer = GetOuterWindowInternal();
5103 outer->EnterModalState();
5104 SpinEventLoopUntil("nsGlobalWindowInner::ShowSlowScriptDialog"_ns, [&]() {
5105 return monitor->IsDebuggerStartupComplete();
5107 outer->LeaveModalState();
5108 return ContinueSlowScript;
5111 return ContinueSlowScriptAndKeepNotifying;
5114 // Reached only on non-e10s - once per slow script dialog.
5115 // On e10s - we probe once at ProcessHangsMonitor.sys.mjs
5116 Telemetry::Accumulate(Telemetry::SLOW_SCRIPT_NOTICE_COUNT, 1);
5118 // Get the nsIPrompt interface from the docshell
5119 nsCOMPtr<nsIDocShell> ds = GetDocShell();
5120 NS_ENSURE_TRUE(ds, KillSlowScript);
5121 nsCOMPtr<nsIPrompt> prompt = do_GetInterface(ds);
5122 NS_ENSURE_TRUE(prompt, KillSlowScript);
5124 // Prioritize the SlowScriptDebug interface over JSD1.
5125 nsCOMPtr<nsISlowScriptDebugCallback> debugCallback;
5127 if (hasFrame) {
5128 const char* debugCID = "@mozilla.org/dom/slow-script-debug;1";
5129 nsCOMPtr<nsISlowScriptDebug> debugService = do_GetService(debugCID, &rv);
5130 if (NS_SUCCEEDED(rv)) {
5131 debugService->GetActivationHandler(getter_AddRefs(debugCallback));
5135 bool failed = false;
5136 auto getString = [&](const char* name,
5137 nsContentUtils::PropertiesFile propFile =
5138 nsContentUtils::eDOM_PROPERTIES) {
5139 nsAutoString result;
5140 nsresult rv = nsContentUtils::GetLocalizedString(propFile, name, result);
5142 // GetStringFromName can return NS_OK and still give nullptr string
5143 failed = failed || NS_FAILED(rv) || result.IsEmpty();
5144 return result;
5147 bool isAddonScript = !aAddonId.IsEmpty();
5148 bool showDebugButton = debugCallback && !isAddonScript;
5150 // Get localizable strings
5152 nsAutoString title, checkboxMsg, debugButton, msg;
5153 if (isAddonScript) {
5154 title = getString("KillAddonScriptTitle");
5155 checkboxMsg = getString("KillAddonScriptGlobalMessage");
5157 auto appName =
5158 getString("brandShortName", nsContentUtils::eBRAND_PROPERTIES);
5160 nsCOMPtr<nsIAddonPolicyService> aps =
5161 do_GetService("@mozilla.org/addons/policy-service;1");
5162 nsString addonName;
5163 if (!aps || NS_FAILED(aps->GetExtensionName(aAddonId, addonName))) {
5164 addonName = aAddonId;
5167 rv = nsContentUtils::FormatLocalizedString(
5168 msg, nsContentUtils::eDOM_PROPERTIES, "KillAddonScriptMessage",
5169 addonName, appName);
5171 failed = failed || NS_FAILED(rv);
5172 } else {
5173 title = getString("KillScriptTitle");
5174 checkboxMsg = getString("DontAskAgain");
5176 if (showDebugButton) {
5177 debugButton = getString("DebugScriptButton");
5178 msg = getString("KillScriptWithDebugMessage");
5179 } else {
5180 msg = getString("KillScriptMessage");
5184 auto stopButton = getString("StopScriptButton");
5185 auto waitButton = getString("WaitForScriptButton");
5187 if (failed) {
5188 NS_ERROR("Failed to get localized strings.");
5189 return ContinueSlowScript;
5192 // Append file and line number information, if available
5193 if (filename.get()) {
5194 nsAutoString scriptLocation;
5195 // We want to drop the middle part of too-long locations. We'll
5196 // define "too-long" as longer than 60 UTF-16 code units. Just
5197 // have to be a bit careful about unpaired surrogates.
5198 NS_ConvertUTF8toUTF16 filenameUTF16(filename.get());
5199 if (filenameUTF16.Length() > 60) {
5200 // XXXbz Do we need to insert any bidi overrides here?
5201 size_t cutStart = 30;
5202 size_t cutLength = filenameUTF16.Length() - 60;
5203 MOZ_ASSERT(cutLength > 0);
5204 if (NS_IS_LOW_SURROGATE(filenameUTF16[cutStart])) {
5205 // Don't truncate before the low surrogate, in case it's preceded by a
5206 // high surrogate and forms a single Unicode character. Instead, just
5207 // include the low surrogate.
5208 ++cutStart;
5209 --cutLength;
5211 if (NS_IS_LOW_SURROGATE(filenameUTF16[cutStart + cutLength])) {
5212 // Likewise, don't drop a trailing low surrogate here. We want to
5213 // increase cutLength, since it might be 0 already so we can't very well
5214 // decrease it.
5215 ++cutLength;
5218 // Insert U+2026 HORIZONTAL ELLIPSIS
5219 filenameUTF16.ReplaceLiteral(cutStart, cutLength, u"\x2026");
5221 rv = nsContentUtils::FormatLocalizedString(
5222 scriptLocation, nsContentUtils::eDOM_PROPERTIES, "KillScriptLocation",
5223 filenameUTF16);
5225 if (NS_SUCCEEDED(rv)) {
5226 msg.AppendLiteral("\n\n");
5227 msg.Append(scriptLocation);
5228 msg.Append(':');
5229 msg.AppendInt(lineno);
5233 uint32_t buttonFlags = nsIPrompt::BUTTON_POS_1_DEFAULT +
5234 (nsIPrompt::BUTTON_TITLE_IS_STRING *
5235 (nsIPrompt::BUTTON_POS_0 + nsIPrompt::BUTTON_POS_1));
5237 // Add a third button if necessary.
5238 if (showDebugButton)
5239 buttonFlags += nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_2;
5241 bool checkboxValue = false;
5242 int32_t buttonPressed = 0; // In case the user exits dialog by clicking X.
5244 // Null out the operation callback while we're re-entering JS here.
5245 AutoDisableJSInterruptCallback disabler(aCx);
5247 // Open the dialog.
5248 rv = prompt->ConfirmEx(
5249 title.get(), msg.get(), buttonFlags, waitButton.get(), stopButton.get(),
5250 debugButton.get(), checkboxMsg.get(), &checkboxValue, &buttonPressed);
5253 if (buttonPressed == 0) {
5254 if (checkboxValue && !isAddonScript && NS_SUCCEEDED(rv))
5255 return AlwaysContinueSlowScript;
5256 return ContinueSlowScript;
5259 if (buttonPressed == 2) {
5260 MOZ_RELEASE_ASSERT(debugCallback);
5262 rv = debugCallback->HandleSlowScriptDebug(this);
5263 return NS_SUCCEEDED(rv) ? ContinueSlowScript : KillSlowScript;
5266 JS_ClearPendingException(aCx);
5268 return KillSlowScript;
5271 nsresult nsGlobalWindowInner::Observe(nsISupports* aSubject, const char* aTopic,
5272 const char16_t* aData) {
5273 if (!nsCRT::strcmp(aTopic, NS_IOSERVICE_OFFLINE_STATUS_TOPIC)) {
5274 if (!IsFrozen()) {
5275 // Fires an offline status event if the offline status has changed
5276 FireOfflineStatusEventIfChanged();
5278 return NS_OK;
5281 if (!nsCRT::strcmp(aTopic, MEMORY_PRESSURE_OBSERVER_TOPIC)) {
5282 if (mPerformance) {
5283 mPerformance->MemoryPressure();
5285 RemoveReportRecords();
5286 return NS_OK;
5289 if (!nsCRT::strcmp(aTopic, PERMISSION_CHANGED_TOPIC)) {
5290 nsCOMPtr<nsIPermission> perm(do_QueryInterface(aSubject));
5291 if (!perm) {
5292 // A null permission indicates that the entire permission list
5293 // was cleared.
5294 MOZ_ASSERT(!nsCRT::strcmp(aData, u"cleared"));
5295 UpdatePermissions();
5296 return NS_OK;
5299 nsAutoCString type;
5300 perm->GetType(type);
5301 if (type == "autoplay-media"_ns) {
5302 UpdateAutoplayPermission();
5303 } else if (type == "shortcuts"_ns) {
5304 UpdateShortcutsPermission();
5305 } else if (type == "popup"_ns) {
5306 UpdatePopupPermission();
5309 if (!mDoc) {
5310 return NS_OK;
5313 RefPtr<PermissionDelegateHandler> permDelegateHandler =
5314 mDoc->GetPermissionDelegateHandler();
5316 if (permDelegateHandler) {
5317 permDelegateHandler->UpdateDelegatedPermission(type);
5320 return NS_OK;
5323 if (!nsCRT::strcmp(aTopic, "screen-information-changed")) {
5324 if (mScreen) {
5325 if (RefPtr<ScreenOrientation> orientation =
5326 mScreen->GetOrientationIfExists()) {
5327 orientation->MaybeChanged();
5330 if (mHasOrientationChangeListeners) {
5331 int32_t oldAngle = mOrientationAngle;
5332 mOrientationAngle = Orientation(CallerType::System);
5333 if (mOrientationAngle != oldAngle && IsCurrentInnerWindow()) {
5334 nsCOMPtr<nsPIDOMWindowOuter> outer = GetOuterWindow();
5335 outer->DispatchCustomEvent(u"orientationchange"_ns);
5338 return NS_OK;
5341 if (!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
5342 MOZ_ASSERT(!NS_strcmp(aData, u"intl.accept_languages"));
5344 // The user preferred languages have changed, we need to fire an event on
5345 // Window object and invalidate the cache for navigator.languages. It is
5346 // done for every change which can be a waste of cycles but those should be
5347 // fairly rare.
5348 // We MUST invalidate navigator.languages before sending the event in the
5349 // very likely situation where an event handler will try to read its value.
5351 if (mNavigator) {
5352 Navigator_Binding::ClearCachedLanguageValue(mNavigator);
5353 Navigator_Binding::ClearCachedLanguagesValue(mNavigator);
5356 // The event has to be dispatched only to the current inner window.
5357 if (!IsCurrentInnerWindow()) {
5358 return NS_OK;
5361 RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
5362 event->InitEvent(u"languagechange"_ns, false, false);
5363 event->SetTrusted(true);
5365 ErrorResult rv;
5366 DispatchEvent(*event, rv);
5367 return rv.StealNSResult();
5370 NS_WARNING("unrecognized topic in nsGlobalWindowInner::Observe");
5371 return NS_ERROR_FAILURE;
5374 void nsGlobalWindowInner::ObserveStorageNotification(
5375 StorageEvent* aEvent, const char16_t* aStorageType, bool aPrivateBrowsing) {
5376 MOZ_ASSERT(aEvent);
5378 // The private browsing check must be done here again because this window
5379 // could have changed its state before the notification check and now. This
5380 // happens in case this window did have a docShell at that time.
5381 if (aPrivateBrowsing != IsPrivateBrowsing()) {
5382 return;
5385 // LocalStorage can only exist on an inner window, and we don't want to
5386 // generate events on frozen or otherwise-navigated-away from windows.
5387 // (Actually, this code used to try and buffer events for frozen windows,
5388 // but it never worked, so we've removed it. See bug 1285898.)
5389 if (!IsCurrentInnerWindow() || IsFrozen()) {
5390 return;
5393 nsIPrincipal* principal = GetPrincipal();
5394 if (!principal) {
5395 return;
5398 bool fireMozStorageChanged = false;
5399 nsAutoString eventType;
5400 eventType.AssignLiteral("storage");
5402 if (!NS_strcmp(aStorageType, u"sessionStorage")) {
5403 RefPtr<Storage> changingStorage = aEvent->GetStorageArea();
5404 MOZ_ASSERT(changingStorage);
5406 bool check = false;
5408 if (const RefPtr<SessionStorageManager> storageManager =
5409 GetBrowsingContext()->GetSessionStorageManager()) {
5410 nsresult rv = storageManager->CheckStorage(GetEffectiveStoragePrincipal(),
5411 changingStorage, &check);
5412 if (NS_FAILED(rv)) {
5413 return;
5417 if (!check) {
5418 // This storage event is not coming from our storage or is coming
5419 // from a different docshell, i.e. it is a clone, ignore this event.
5420 return;
5423 MOZ_LOG(
5424 gDOMLeakPRLogInner, LogLevel::Debug,
5425 ("nsGlobalWindowInner %p with sessionStorage %p passing event from %p",
5426 this, mSessionStorage.get(), changingStorage.get()));
5428 fireMozStorageChanged = mSessionStorage == changingStorage;
5429 if (fireMozStorageChanged) {
5430 eventType.AssignLiteral("MozSessionStorageChanged");
5434 else {
5435 MOZ_ASSERT(!NS_strcmp(aStorageType, u"localStorage"));
5437 nsIPrincipal* storagePrincipal = GetEffectiveStoragePrincipal();
5438 if (!storagePrincipal) {
5439 return;
5442 MOZ_DIAGNOSTIC_ASSERT(StorageUtils::PrincipalsEqual(aEvent->GetPrincipal(),
5443 storagePrincipal));
5445 fireMozStorageChanged =
5446 mLocalStorage && mLocalStorage == aEvent->GetStorageArea();
5448 if (fireMozStorageChanged) {
5449 eventType.AssignLiteral("MozLocalStorageChanged");
5453 // Clone the storage event included in the observer notification. We want
5454 // to dispatch clones rather than the original event.
5455 IgnoredErrorResult error;
5456 RefPtr<StorageEvent> clonedEvent =
5457 CloneStorageEvent(eventType, aEvent, error);
5458 if (error.Failed() || !clonedEvent) {
5459 return;
5462 clonedEvent->SetTrusted(true);
5464 if (fireMozStorageChanged) {
5465 WidgetEvent* internalEvent = clonedEvent->WidgetEventPtr();
5466 internalEvent->mFlags.mOnlyChromeDispatch = true;
5469 DispatchEvent(*clonedEvent);
5472 already_AddRefed<StorageEvent> nsGlobalWindowInner::CloneStorageEvent(
5473 const nsAString& aType, const RefPtr<StorageEvent>& aEvent,
5474 ErrorResult& aRv) {
5475 StorageEventInit dict;
5477 dict.mBubbles = aEvent->Bubbles();
5478 dict.mCancelable = aEvent->Cancelable();
5479 aEvent->GetKey(dict.mKey);
5480 aEvent->GetOldValue(dict.mOldValue);
5481 aEvent->GetNewValue(dict.mNewValue);
5482 aEvent->GetUrl(dict.mUrl);
5484 RefPtr<Storage> storageArea = aEvent->GetStorageArea();
5486 RefPtr<Storage> storage;
5488 // If null, this is a localStorage event received by IPC.
5489 if (!storageArea) {
5490 storage = GetLocalStorage(aRv);
5491 if (!NextGenLocalStorageEnabled()) {
5492 if (aRv.Failed() || !storage) {
5493 return nullptr;
5496 if (storage->Type() == Storage::eLocalStorage) {
5497 RefPtr<LocalStorage> localStorage =
5498 static_cast<LocalStorage*>(storage.get());
5500 // We must apply the current change to the 'local' localStorage.
5501 localStorage->ApplyEvent(aEvent);
5504 } else if (storageArea->Type() == Storage::eSessionStorage) {
5505 storage = GetSessionStorage(aRv);
5506 } else {
5507 MOZ_ASSERT(storageArea->Type() == Storage::eLocalStorage);
5508 storage = GetLocalStorage(aRv);
5511 if (aRv.Failed() || !storage) {
5512 return nullptr;
5515 if (storage->Type() == Storage::ePartitionedLocalStorage) {
5516 // This error message is not exposed.
5517 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
5518 return nullptr;
5521 MOZ_ASSERT(storage);
5522 MOZ_ASSERT_IF(storageArea, storage->IsForkOf(storageArea));
5524 dict.mStorageArea = storage;
5526 RefPtr<StorageEvent> event = StorageEvent::Constructor(this, aType, dict);
5527 return event.forget();
5530 void nsGlobalWindowInner::Suspend(bool aIncludeSubWindows) {
5531 MOZ_ASSERT(NS_IsMainThread());
5533 // We can only safely suspend windows that are the current inner window. If
5534 // its not the current inner, then we are in one of two different cases.
5535 // Either we are in the bfcache or we are doomed window that is going away.
5536 // When a window becomes inactive we purposely avoid placing already suspended
5537 // windows into the bfcache. It only expects windows suspended due to the
5538 // Freeze() method which occurs while the window is still the current inner.
5539 // So we must not call Suspend() on bfcache windows at this point or this
5540 // invariant will be broken. If the window is doomed there is no point in
5541 // suspending it since it will soon be gone.
5542 if (!IsCurrentInnerWindow()) {
5543 return;
5546 // All in-process descendants are also suspended. This ensure mSuspendDepth
5547 // is set properly and the timers are properly canceled for each in-process
5548 // descendant.
5549 if (aIncludeSubWindows) {
5550 CallOnInProcessDescendants(&nsGlobalWindowInner::Suspend, false);
5553 mSuspendDepth += 1;
5554 if (mSuspendDepth != 1) {
5555 return;
5558 if (mWindowGlobalChild) {
5559 mWindowGlobalChild->BlockBFCacheFor(BFCacheStatus::SUSPENDED);
5562 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
5563 if (ac) {
5564 for (uint32_t i = 0; i < mEnabledSensors.Length(); i++)
5565 ac->RemoveWindowListener(mEnabledSensors[i], this);
5567 DisableGamepadUpdates();
5568 DisableVRUpdates();
5570 SuspendWorkersForWindow(*this);
5572 for (RefPtr<mozilla::dom::SharedWorker> pinnedWorker :
5573 mSharedWorkers.ForwardRange()) {
5574 pinnedWorker->Suspend();
5577 SuspendIdleRequests();
5579 mTimeoutManager->Suspend();
5581 // Suspend all of the AudioContexts for this window
5582 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
5583 mAudioContexts[i]->SuspendFromChrome();
5587 void nsGlobalWindowInner::Resume(bool aIncludeSubWindows) {
5588 MOZ_ASSERT(NS_IsMainThread());
5590 // We can only safely resume a window if its the current inner window. If
5591 // its not the current inner, then we are in one of two different cases.
5592 // Either we are in the bfcache or we are doomed window that is going away.
5593 // If a window is suspended when it becomes inactive we purposely do not
5594 // put it in the bfcache, so Resume should never be needed in that case.
5595 // If the window is doomed then there is no point in resuming it.
5596 if (!IsCurrentInnerWindow()) {
5597 return;
5600 // Resume all in-process descendants. This restores timers recursively
5601 // canceled in Suspend() and ensures all in-process descendants have the
5602 // correct mSuspendDepth.
5603 if (aIncludeSubWindows) {
5604 CallOnInProcessDescendants(&nsGlobalWindowInner::Resume, false);
5607 if (mSuspendDepth == 0) {
5608 // Ignore if the window is not suspended.
5609 return;
5612 mSuspendDepth -= 1;
5614 if (mSuspendDepth != 0) {
5615 return;
5618 // We should not be able to resume a frozen window. It must be Thaw()'d
5619 // first.
5620 MOZ_ASSERT(mFreezeDepth == 0);
5622 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
5623 if (ac) {
5624 for (uint32_t i = 0; i < mEnabledSensors.Length(); i++)
5625 ac->AddWindowListener(mEnabledSensors[i], this);
5627 EnableGamepadUpdates();
5628 EnableVRUpdates();
5630 // Resume all of the AudioContexts for this window
5631 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
5632 mAudioContexts[i]->ResumeFromChrome();
5635 if (RefPtr<MediaDevices> devices = GetExtantMediaDevices()) {
5636 devices->WindowResumed();
5639 mTimeoutManager->Resume();
5641 ResumeIdleRequests();
5643 // Resume all of the workers for this window. We must do this
5644 // after timeouts since workers may have queued events that can trigger
5645 // a setTimeout().
5646 ResumeWorkersForWindow(*this);
5648 for (RefPtr<mozilla::dom::SharedWorker> pinnedWorker :
5649 mSharedWorkers.ForwardRange()) {
5650 pinnedWorker->Resume();
5653 if (mWindowGlobalChild) {
5654 mWindowGlobalChild->UnblockBFCacheFor(BFCacheStatus::SUSPENDED);
5658 bool nsGlobalWindowInner::IsSuspended() const {
5659 MOZ_ASSERT(NS_IsMainThread());
5660 return mSuspendDepth != 0;
5663 void nsGlobalWindowInner::Freeze(bool aIncludeSubWindows) {
5664 MOZ_ASSERT(NS_IsMainThread());
5665 Suspend(aIncludeSubWindows);
5666 FreezeInternal(aIncludeSubWindows);
5669 void nsGlobalWindowInner::FreezeInternal(bool aIncludeSubWindows) {
5670 MOZ_ASSERT(NS_IsMainThread());
5671 MOZ_DIAGNOSTIC_ASSERT(IsCurrentInnerWindow());
5672 MOZ_DIAGNOSTIC_ASSERT(IsSuspended());
5674 HintIsLoading(false);
5676 if (aIncludeSubWindows) {
5677 CallOnInProcessChildren(&nsGlobalWindowInner::FreezeInternal,
5678 aIncludeSubWindows);
5681 mFreezeDepth += 1;
5682 MOZ_ASSERT(mSuspendDepth >= mFreezeDepth);
5683 if (mFreezeDepth != 1) {
5684 return;
5687 FreezeWorkersForWindow(*this);
5689 for (RefPtr<mozilla::dom::SharedWorker> pinnedWorker :
5690 mSharedWorkers.ForwardRange()) {
5691 pinnedWorker->Freeze();
5694 mTimeoutManager->Freeze();
5695 if (mClientSource) {
5696 mClientSource->Freeze();
5699 NotifyDOMWindowFrozen(this);
5702 void nsGlobalWindowInner::Thaw(bool aIncludeSubWindows) {
5703 MOZ_ASSERT(NS_IsMainThread());
5704 ThawInternal(aIncludeSubWindows);
5705 Resume(aIncludeSubWindows);
5708 void nsGlobalWindowInner::ThawInternal(bool aIncludeSubWindows) {
5709 MOZ_ASSERT(NS_IsMainThread());
5710 MOZ_DIAGNOSTIC_ASSERT(IsCurrentInnerWindow());
5711 MOZ_DIAGNOSTIC_ASSERT(IsSuspended());
5713 if (aIncludeSubWindows) {
5714 CallOnInProcessChildren(&nsGlobalWindowInner::ThawInternal,
5715 aIncludeSubWindows);
5718 MOZ_ASSERT(mFreezeDepth != 0);
5719 mFreezeDepth -= 1;
5720 MOZ_ASSERT(mSuspendDepth >= mFreezeDepth);
5721 if (mFreezeDepth != 0) {
5722 return;
5725 if (mClientSource) {
5726 mClientSource->Thaw();
5728 mTimeoutManager->Thaw();
5730 ThawWorkersForWindow(*this);
5732 for (RefPtr<mozilla::dom::SharedWorker> pinnedWorker :
5733 mSharedWorkers.ForwardRange()) {
5734 pinnedWorker->Thaw();
5737 NotifyDOMWindowThawed(this);
5740 bool nsGlobalWindowInner::IsFrozen() const {
5741 MOZ_ASSERT(NS_IsMainThread());
5742 bool frozen = mFreezeDepth != 0;
5743 MOZ_ASSERT_IF(frozen, IsSuspended());
5744 return frozen;
5747 void nsGlobalWindowInner::SyncStateFromParentWindow() {
5748 // This method should only be called on an inner window that has been
5749 // assigned to an outer window already.
5750 MOZ_ASSERT(IsCurrentInnerWindow());
5751 nsPIDOMWindowOuter* outer = GetOuterWindow();
5752 MOZ_ASSERT(outer);
5754 // Attempt to find our parent windows.
5755 nsCOMPtr<Element> frame = outer->GetFrameElementInternal();
5756 nsPIDOMWindowOuter* parentOuter =
5757 frame ? frame->OwnerDoc()->GetWindow() : nullptr;
5758 nsGlobalWindowInner* parentInner =
5759 parentOuter
5760 ? nsGlobalWindowInner::Cast(parentOuter->GetCurrentInnerWindow())
5761 : nullptr;
5763 // If our outer is in a modal state, but our parent is not in a modal
5764 // state, then we must apply the suspend directly. If our parent is
5765 // in a modal state then we should get the suspend automatically
5766 // via the parentSuspendDepth application below.
5767 if ((!parentInner || !parentInner->IsInModalState()) && IsInModalState()) {
5768 Suspend();
5771 uint32_t parentFreezeDepth = parentInner ? parentInner->mFreezeDepth : 0;
5772 uint32_t parentSuspendDepth = parentInner ? parentInner->mSuspendDepth : 0;
5774 // Since every Freeze() calls Suspend(), the suspend count must
5775 // be equal or greater to the freeze count.
5776 MOZ_ASSERT(parentFreezeDepth <= parentSuspendDepth);
5778 // First apply the Freeze() calls.
5779 for (uint32_t i = 0; i < parentFreezeDepth; ++i) {
5780 Freeze();
5783 // Now apply only the number of Suspend() calls to reach the target
5784 // suspend count after applying the Freeze() calls.
5785 for (uint32_t i = 0; i < (parentSuspendDepth - parentFreezeDepth); ++i) {
5786 Suspend();
5790 void nsGlobalWindowInner::UpdateBackgroundState() {
5791 if (RefPtr<MediaDevices> devices = GetExtantMediaDevices()) {
5792 devices->BackgroundStateChanged();
5794 mTimeoutManager->UpdateBackgroundState();
5797 template <typename Method, typename... Args>
5798 CallState nsGlobalWindowInner::CallOnInProcessDescendantsInternal(
5799 BrowsingContext* aBrowsingContext, bool aChildOnly, Method aMethod,
5800 Args&&... aArgs) {
5801 MOZ_ASSERT(NS_IsMainThread());
5802 MOZ_ASSERT(aBrowsingContext);
5804 CallState state = CallState::Continue;
5805 for (const RefPtr<BrowsingContext>& bc : aBrowsingContext->Children()) {
5806 if (nsCOMPtr<nsPIDOMWindowOuter> pWin = bc->GetDOMWindow()) {
5807 auto* win = nsGlobalWindowOuter::Cast(pWin);
5808 if (nsGlobalWindowInner* inner =
5809 nsGlobalWindowInner::Cast(win->GetCurrentInnerWindow())) {
5810 // Call the descendant method using our helper CallDescendant() template
5811 // method. This allows us to handle both void returning methods and
5812 // methods that return CallState explicitly. For void returning methods
5813 // we assume CallState::Continue.
5814 using returnType = decltype((inner->*aMethod)(aArgs...));
5815 state = CallDescendant<returnType>(inner, aMethod, aArgs...);
5817 if (state == CallState::Stop) {
5818 return state;
5823 if (!aChildOnly) {
5824 state = CallOnInProcessDescendantsInternal(bc.get(), aChildOnly, aMethod,
5825 aArgs...);
5826 if (state == CallState::Stop) {
5827 return state;
5832 return state;
5835 Maybe<ClientInfo> nsGlobalWindowInner::GetClientInfo() const {
5836 MOZ_ASSERT(NS_IsMainThread());
5837 if (mDoc && mDoc->IsStaticDocument()) {
5838 if (Maybe<ClientInfo> info = mDoc->GetOriginalDocument()->GetClientInfo()) {
5839 return info;
5843 Maybe<ClientInfo> clientInfo;
5844 if (mClientSource) {
5845 clientInfo.emplace(mClientSource->Info());
5847 return clientInfo;
5850 Maybe<ClientState> nsGlobalWindowInner::GetClientState() const {
5851 MOZ_ASSERT(NS_IsMainThread());
5852 if (mDoc && mDoc->IsStaticDocument()) {
5853 if (Maybe<ClientState> state =
5854 mDoc->GetOriginalDocument()->GetClientState()) {
5855 return state;
5859 Maybe<ClientState> clientState;
5860 if (mClientSource) {
5861 Result<ClientState, ErrorResult> res = mClientSource->SnapshotState();
5862 if (res.isOk()) {
5863 clientState.emplace(res.unwrap());
5864 } else {
5865 res.unwrapErr().SuppressException();
5868 return clientState;
5871 Maybe<ServiceWorkerDescriptor> nsGlobalWindowInner::GetController() const {
5872 MOZ_ASSERT(NS_IsMainThread());
5873 if (mDoc && mDoc->IsStaticDocument()) {
5874 if (Maybe<ServiceWorkerDescriptor> controller =
5875 mDoc->GetOriginalDocument()->GetController()) {
5876 return controller;
5880 Maybe<ServiceWorkerDescriptor> controller;
5881 if (mClientSource) {
5882 controller = mClientSource->GetController();
5884 return controller;
5887 void nsGlobalWindowInner::SetCsp(nsIContentSecurityPolicy* aCsp) {
5888 if (!mClientSource) {
5889 return;
5891 mClientSource->SetCsp(aCsp);
5892 // Also cache the CSP within the document
5893 mDoc->SetCsp(aCsp);
5895 if (mWindowGlobalChild) {
5896 mWindowGlobalChild->SendSetClientInfo(mClientSource->Info().ToIPC());
5900 void nsGlobalWindowInner::SetPreloadCsp(nsIContentSecurityPolicy* aPreloadCsp) {
5901 if (!mClientSource) {
5902 return;
5904 mClientSource->SetPreloadCsp(aPreloadCsp);
5905 // Also cache the preload CSP within the document
5906 mDoc->SetPreloadCsp(aPreloadCsp);
5908 if (mWindowGlobalChild) {
5909 mWindowGlobalChild->SendSetClientInfo(mClientSource->Info().ToIPC());
5913 nsIContentSecurityPolicy* nsGlobalWindowInner::GetCsp() {
5914 if (mDoc) {
5915 return mDoc->GetCsp();
5918 // If the window is partially torn down and has its document nulled out,
5919 // we query the CSP we snapshot in FreeInnerObjects.
5920 if (mDocumentCsp) {
5921 return mDocumentCsp;
5923 return nullptr;
5926 RefPtr<ServiceWorker> nsGlobalWindowInner::GetOrCreateServiceWorker(
5927 const ServiceWorkerDescriptor& aDescriptor) {
5928 MOZ_ASSERT(NS_IsMainThread());
5929 RefPtr<ServiceWorker> ref;
5930 ForEachGlobalTeardownObserver(
5931 [&](GlobalTeardownObserver* aObserver, bool* aDoneOut) {
5932 RefPtr<ServiceWorker> sw = do_QueryObject(aObserver);
5933 if (!sw || !sw->Descriptor().Matches(aDescriptor)) {
5934 return;
5937 ref = std::move(sw);
5938 *aDoneOut = true;
5941 if (!ref) {
5942 ref = ServiceWorker::Create(this, aDescriptor);
5945 return ref;
5948 RefPtr<mozilla::dom::ServiceWorkerRegistration>
5949 nsGlobalWindowInner::GetServiceWorkerRegistration(
5950 const mozilla::dom::ServiceWorkerRegistrationDescriptor& aDescriptor)
5951 const {
5952 MOZ_ASSERT(NS_IsMainThread());
5953 RefPtr<ServiceWorkerRegistration> ref;
5954 ForEachGlobalTeardownObserver(
5955 [&](GlobalTeardownObserver* aObserver, bool* aDoneOut) {
5956 RefPtr<ServiceWorkerRegistration> swr = do_QueryObject(aObserver);
5957 if (!swr || !swr->MatchesDescriptor(aDescriptor)) {
5958 return;
5961 ref = std::move(swr);
5962 *aDoneOut = true;
5964 return ref;
5967 RefPtr<ServiceWorkerRegistration>
5968 nsGlobalWindowInner::GetOrCreateServiceWorkerRegistration(
5969 const ServiceWorkerRegistrationDescriptor& aDescriptor) {
5970 MOZ_ASSERT(NS_IsMainThread());
5971 RefPtr<ServiceWorkerRegistration> ref =
5972 GetServiceWorkerRegistration(aDescriptor);
5973 if (!ref) {
5974 ref = ServiceWorkerRegistration::CreateForMainThread(this, aDescriptor);
5976 return ref;
5979 StorageAccess nsGlobalWindowInner::GetStorageAccess() {
5980 return StorageAllowedForWindow(this);
5983 nsresult nsGlobalWindowInner::FireDelayedDOMEvents(bool aIncludeSubWindows) {
5984 // Fires an offline status event if the offline status has changed
5985 FireOfflineStatusEventIfChanged();
5987 if (!aIncludeSubWindows) {
5988 return NS_OK;
5991 nsCOMPtr<nsIDocShell> docShell = GetDocShell();
5992 if (docShell) {
5993 int32_t childCount = 0;
5994 docShell->GetInProcessChildCount(&childCount);
5996 // Take a copy of the current children so that modifications to
5997 // the child list don't affect to the iteration.
5998 AutoTArray<nsCOMPtr<nsIDocShellTreeItem>, 8> children;
5999 for (int32_t i = 0; i < childCount; ++i) {
6000 nsCOMPtr<nsIDocShellTreeItem> childShell;
6001 docShell->GetInProcessChildAt(i, getter_AddRefs(childShell));
6002 if (childShell) {
6003 children.AppendElement(childShell);
6007 for (nsCOMPtr<nsIDocShellTreeItem> childShell : children) {
6008 if (nsCOMPtr<nsPIDOMWindowOuter> pWin = childShell->GetWindow()) {
6009 auto* win = nsGlobalWindowOuter::Cast(pWin);
6010 win->FireDelayedDOMEvents(true);
6015 return NS_OK;
6018 //*****************************************************************************
6019 // nsGlobalWindowInner: Window Control Functions
6020 //*****************************************************************************
6022 nsPIDOMWindowOuter* nsGlobalWindowInner::GetInProcessParentInternal() {
6023 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
6024 if (!outer) {
6025 // No outer window available!
6026 return nullptr;
6028 return outer->GetInProcessParentInternal();
6031 nsIPrincipal* nsGlobalWindowInner::GetTopLevelAntiTrackingPrincipal() {
6032 nsPIDOMWindowOuter* outerWindow = GetOuterWindowInternal();
6033 if (!outerWindow) {
6034 return nullptr;
6037 nsPIDOMWindowOuter* topLevelOuterWindow =
6038 GetBrowsingContext()->Top()->GetDOMWindow();
6039 if (!topLevelOuterWindow) {
6040 return nullptr;
6043 bool stopAtOurLevel =
6044 mDoc && mDoc->CookieJarSettings()->GetCookieBehavior() ==
6045 nsICookieService::BEHAVIOR_REJECT_TRACKER;
6047 if (stopAtOurLevel && topLevelOuterWindow == outerWindow) {
6048 return nullptr;
6051 nsPIDOMWindowInner* topLevelInnerWindow =
6052 topLevelOuterWindow->GetCurrentInnerWindow();
6053 if (NS_WARN_IF(!topLevelInnerWindow)) {
6054 return nullptr;
6057 nsIPrincipal* topLevelPrincipal =
6058 nsGlobalWindowInner::Cast(topLevelInnerWindow)->GetPrincipal();
6059 if (NS_WARN_IF(!topLevelPrincipal)) {
6060 return nullptr;
6063 return topLevelPrincipal;
6066 nsIPrincipal* nsGlobalWindowInner::GetClientPrincipal() {
6067 return mClientSource ? mClientSource->GetPrincipal() : nullptr;
6070 bool nsGlobalWindowInner::IsInFullScreenTransition() {
6071 if (!mIsChrome) {
6072 return false;
6075 nsGlobalWindowOuter* outerWindow = GetOuterWindowInternal();
6076 if (!outerWindow) {
6077 return false;
6080 return outerWindow->mIsInFullScreenTransition;
6083 //*****************************************************************************
6084 // nsGlobalWindowInner: Timeout Functions
6085 //*****************************************************************************
6087 class WindowScriptTimeoutHandler final : public ScriptTimeoutHandler {
6088 public:
6089 NS_DECL_ISUPPORTS_INHERITED
6090 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(WindowScriptTimeoutHandler,
6091 ScriptTimeoutHandler)
6093 WindowScriptTimeoutHandler(JSContext* aCx, nsIGlobalObject* aGlobal,
6094 const nsAString& aExpression)
6095 : ScriptTimeoutHandler(aCx, aGlobal, aExpression),
6096 mInitiatingScript(ScriptLoader::GetActiveScript(aCx)) {}
6098 MOZ_CAN_RUN_SCRIPT virtual bool Call(const char* aExecutionReason) override;
6100 private:
6101 virtual ~WindowScriptTimeoutHandler() = default;
6103 // Initiating script for use when evaluating mExpr on the main thread.
6104 RefPtr<JS::loader::LoadedScript> mInitiatingScript;
6107 NS_IMPL_CYCLE_COLLECTION_INHERITED(WindowScriptTimeoutHandler,
6108 ScriptTimeoutHandler, mInitiatingScript)
6110 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WindowScriptTimeoutHandler)
6111 NS_INTERFACE_MAP_END_INHERITING(ScriptTimeoutHandler)
6113 NS_IMPL_ADDREF_INHERITED(WindowScriptTimeoutHandler, ScriptTimeoutHandler)
6114 NS_IMPL_RELEASE_INHERITED(WindowScriptTimeoutHandler, ScriptTimeoutHandler)
6116 bool WindowScriptTimeoutHandler::Call(const char* aExecutionReason) {
6117 // New script entry point required, due to the "Create a script" sub-step
6118 // of
6119 // http://www.whatwg.org/specs/web-apps/current-work/#timer-initialisation-steps
6120 nsAutoMicroTask mt;
6121 AutoEntryScript aes(mGlobal, aExecutionReason, true);
6122 JS::CompileOptions options(aes.cx());
6123 options.setFileAndLine(mFileName.get(), mLineNo);
6124 options.setNoScriptRval(true);
6125 options.setIntroductionType("domTimer");
6126 JS::Rooted<JSObject*> global(aes.cx(), mGlobal->GetGlobalJSObject());
6128 JSExecutionContext exec(aes.cx(), global, options);
6129 nsresult rv = exec.Compile(mExpr);
6131 JS::Rooted<JSScript*> script(aes.cx(), exec.MaybeGetScript());
6132 if (script) {
6133 if (mInitiatingScript) {
6134 mInitiatingScript->AssociateWithScript(script);
6137 rv = exec.ExecScript();
6140 if (rv == NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW_UNCATCHABLE) {
6141 return false;
6145 return true;
6148 nsGlobalWindowInner* nsGlobalWindowInner::InnerForSetTimeoutOrInterval(
6149 ErrorResult& aError) {
6150 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
6151 nsPIDOMWindowInner* currentInner =
6152 outer ? outer->GetCurrentInnerWindow() : this;
6154 // If forwardTo is not the window with an active document then we want the
6155 // call to setTimeout/Interval to be a noop, so return null but don't set an
6156 // error.
6157 return HasActiveDocument() ? nsGlobalWindowInner::Cast(currentInner)
6158 : nullptr;
6161 int32_t nsGlobalWindowInner::SetTimeout(JSContext* aCx, Function& aFunction,
6162 int32_t aTimeout,
6163 const Sequence<JS::Value>& aArguments,
6164 ErrorResult& aError) {
6165 return SetTimeoutOrInterval(aCx, aFunction, aTimeout, aArguments, false,
6166 aError);
6169 int32_t nsGlobalWindowInner::SetTimeout(JSContext* aCx,
6170 const nsAString& aHandler,
6171 int32_t aTimeout,
6172 const Sequence<JS::Value>& /* unused */,
6173 ErrorResult& aError) {
6174 return SetTimeoutOrInterval(aCx, aHandler, aTimeout, false, aError);
6177 int32_t nsGlobalWindowInner::SetInterval(JSContext* aCx, Function& aFunction,
6178 const int32_t aTimeout,
6179 const Sequence<JS::Value>& aArguments,
6180 ErrorResult& aError) {
6181 return SetTimeoutOrInterval(aCx, aFunction, aTimeout, aArguments, true,
6182 aError);
6185 int32_t nsGlobalWindowInner::SetInterval(
6186 JSContext* aCx, const nsAString& aHandler, const int32_t aTimeout,
6187 const Sequence<JS::Value>& /* unused */, ErrorResult& aError) {
6188 return SetTimeoutOrInterval(aCx, aHandler, aTimeout, true, aError);
6191 int32_t nsGlobalWindowInner::SetTimeoutOrInterval(
6192 JSContext* aCx, Function& aFunction, int32_t aTimeout,
6193 const Sequence<JS::Value>& aArguments, bool aIsInterval,
6194 ErrorResult& aError) {
6195 nsGlobalWindowInner* inner = InnerForSetTimeoutOrInterval(aError);
6196 if (!inner) {
6197 return -1;
6200 if (inner != this) {
6201 RefPtr<nsGlobalWindowInner> innerRef(inner);
6202 return innerRef->SetTimeoutOrInterval(aCx, aFunction, aTimeout, aArguments,
6203 aIsInterval, aError);
6206 DebuggerNotificationDispatch(
6207 this, aIsInterval ? DebuggerNotificationType::SetInterval
6208 : DebuggerNotificationType::SetTimeout);
6210 if (!GetContextInternal() || !HasJSGlobal()) {
6211 // This window was already closed, or never properly initialized,
6212 // don't let a timer be scheduled on such a window.
6213 aError.Throw(NS_ERROR_NOT_INITIALIZED);
6214 return 0;
6217 nsTArray<JS::Heap<JS::Value>> args;
6218 if (!args.AppendElements(aArguments, fallible)) {
6219 aError.Throw(NS_ERROR_OUT_OF_MEMORY);
6220 return 0;
6223 RefPtr<TimeoutHandler> handler =
6224 new CallbackTimeoutHandler(aCx, this, &aFunction, std::move(args));
6226 int32_t result;
6227 aError =
6228 mTimeoutManager->SetTimeout(handler, aTimeout, aIsInterval,
6229 Timeout::Reason::eTimeoutOrInterval, &result);
6230 return result;
6233 int32_t nsGlobalWindowInner::SetTimeoutOrInterval(JSContext* aCx,
6234 const nsAString& aHandler,
6235 int32_t aTimeout,
6236 bool aIsInterval,
6237 ErrorResult& aError) {
6238 nsGlobalWindowInner* inner = InnerForSetTimeoutOrInterval(aError);
6239 if (!inner) {
6240 return -1;
6243 if (inner != this) {
6244 RefPtr<nsGlobalWindowInner> innerRef(inner);
6245 return innerRef->SetTimeoutOrInterval(aCx, aHandler, aTimeout, aIsInterval,
6246 aError);
6249 DebuggerNotificationDispatch(
6250 this, aIsInterval ? DebuggerNotificationType::SetInterval
6251 : DebuggerNotificationType::SetTimeout);
6253 if (!GetContextInternal() || !HasJSGlobal()) {
6254 // This window was already closed, or never properly initialized,
6255 // don't let a timer be scheduled on such a window.
6256 aError.Throw(NS_ERROR_NOT_INITIALIZED);
6257 return 0;
6260 bool allowEval = false;
6261 aError = CSPEvalChecker::CheckForWindow(aCx, this, aHandler, &allowEval);
6262 if (NS_WARN_IF(aError.Failed()) || !allowEval) {
6263 return 0;
6266 RefPtr<TimeoutHandler> handler =
6267 new WindowScriptTimeoutHandler(aCx, this, aHandler);
6269 int32_t result;
6270 aError =
6271 mTimeoutManager->SetTimeout(handler, aTimeout, aIsInterval,
6272 Timeout::Reason::eTimeoutOrInterval, &result);
6273 return result;
6276 static const char* GetTimeoutReasonString(Timeout* aTimeout) {
6277 switch (aTimeout->mReason) {
6278 case Timeout::Reason::eTimeoutOrInterval:
6279 if (aTimeout->mIsInterval) {
6280 return "setInterval handler";
6282 return "setTimeout handler";
6283 case Timeout::Reason::eIdleCallbackTimeout:
6284 return "setIdleCallback handler (timed out)";
6285 case Timeout::Reason::eAbortSignalTimeout:
6286 return "AbortSignal timeout";
6287 case Timeout::Reason::eDelayedWebTaskTimeout:
6288 return "delayedWebTaskCallback handler (timed out)";
6289 default:
6290 MOZ_CRASH("Unexpected enum value");
6291 return "";
6293 MOZ_CRASH("Unexpected enum value");
6294 return "";
6297 bool nsGlobalWindowInner::RunTimeoutHandler(Timeout* aTimeout,
6298 nsIScriptContext* aScx) {
6299 // Hold on to the timeout in case mExpr or mFunObj releases its
6300 // doc.
6301 // XXXbz Our caller guarantees it'll hold on to the timeout (because
6302 // we're MOZ_CAN_RUN_SCRIPT), so we can probably stop doing that...
6303 RefPtr<Timeout> timeout = aTimeout;
6304 Timeout* last_running_timeout = mTimeoutManager->BeginRunningTimeout(timeout);
6305 timeout->mRunning = true;
6307 // Push this timeout's popup control state, which should only be
6308 // enabled the first time a timeout fires that was created while
6309 // popups were enabled and with a delay less than
6310 // "dom.disable_open_click_delay".
6311 AutoPopupStatePusher popupStatePusher(timeout->mPopupState);
6313 // Clear the timeout's popup state, if any, to prevent interval
6314 // timeouts from repeatedly opening poups.
6315 timeout->mPopupState = PopupBlocker::openAbused;
6317 uint32_t nestingLevel = TimeoutManager::GetNestingLevel();
6318 TimeoutManager::SetNestingLevel(timeout->mNestingLevel);
6320 const char* reason = GetTimeoutReasonString(timeout);
6322 nsCString str;
6323 if (profiler_thread_is_being_profiled_for_markers()) {
6324 TimeDuration originalInterval = timeout->When() - timeout->SubmitTime();
6325 str.Append(reason);
6326 str.Append(" with interval ");
6327 str.AppendInt(int(originalInterval.ToMilliseconds()));
6328 str.Append("ms: ");
6329 nsCString handlerDescription;
6330 timeout->mScriptHandler->GetDescription(handlerDescription);
6331 str.Append(handlerDescription);
6333 AUTO_PROFILER_MARKER_TEXT("setTimeout callback", DOM,
6334 MarkerOptions(MarkerStack::TakeBacktrace(
6335 timeout->TakeProfilerBacktrace()),
6336 MarkerInnerWindowId(mWindowID)),
6337 str);
6339 bool abortIntervalHandler;
6341 RefPtr<TimeoutHandler> handler(timeout->mScriptHandler);
6343 CallbackDebuggerNotificationGuard guard(
6344 this, timeout->mIsInterval
6345 ? DebuggerNotificationType::SetIntervalCallback
6346 : DebuggerNotificationType::SetTimeoutCallback);
6347 abortIntervalHandler = !handler->Call(reason);
6350 // If we received an uncatchable exception, do not schedule the timeout again.
6351 // This allows the slow script dialog to break easy DoS attacks like
6352 // setInterval(function() { while(1); }, 100);
6353 if (abortIntervalHandler) {
6354 // If it wasn't an interval timer to begin with, this does nothing. If it
6355 // was, we'll treat it as a timeout that we just ran and discard it when
6356 // we return.
6357 timeout->mIsInterval = false;
6360 // We ignore any failures from calling EvaluateString() on the context or
6361 // Call() on a Function here since we're in a loop
6362 // where we're likely to be running timeouts whose OS timers
6363 // didn't fire in time and we don't want to not fire those timers
6364 // now just because execution of one timer failed. We can't
6365 // propagate the error to anyone who cares about it from this
6366 // point anyway, and the script context should have already reported
6367 // the script error in the usual way - so we just drop it.
6369 TimeoutManager::SetNestingLevel(nestingLevel);
6371 mTimeoutManager->EndRunningTimeout(last_running_timeout);
6372 timeout->mRunning = false;
6374 return timeout->mCleared;
6377 //*****************************************************************************
6378 // nsGlobalWindowInner: Helper Functions
6379 //*****************************************************************************
6381 already_AddRefed<nsIDocShellTreeOwner> nsGlobalWindowInner::GetTreeOwner() {
6382 FORWARD_TO_OUTER(GetTreeOwner, (), nullptr);
6385 already_AddRefed<nsIWebBrowserChrome>
6386 nsGlobalWindowInner::GetWebBrowserChrome() {
6387 nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
6389 nsCOMPtr<nsIWebBrowserChrome> browserChrome = do_GetInterface(treeOwner);
6390 return browserChrome.forget();
6393 nsIScrollableFrame* nsGlobalWindowInner::GetScrollFrame() {
6394 FORWARD_TO_OUTER(GetScrollFrame, (), nullptr);
6397 bool nsGlobalWindowInner::IsPrivateBrowsing() {
6398 nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(GetDocShell());
6399 return loadContext && loadContext->UsePrivateBrowsing();
6402 void nsGlobalWindowInner::FlushPendingNotifications(FlushType aType) {
6403 if (mDoc) {
6404 mDoc->FlushPendingNotifications(aType);
6408 void nsGlobalWindowInner::EnableDeviceSensor(uint32_t aType) {
6409 bool alreadyEnabled = false;
6410 for (uint32_t i = 0; i < mEnabledSensors.Length(); i++) {
6411 if (mEnabledSensors[i] == aType) {
6412 alreadyEnabled = true;
6413 break;
6417 mEnabledSensors.AppendElement(aType);
6419 if (alreadyEnabled) {
6420 return;
6423 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
6424 if (ac) {
6425 ac->AddWindowListener(aType, this);
6429 void nsGlobalWindowInner::DisableDeviceSensor(uint32_t aType) {
6430 int32_t doomedElement = -1;
6431 int32_t listenerCount = 0;
6432 for (uint32_t i = 0; i < mEnabledSensors.Length(); i++) {
6433 if (mEnabledSensors[i] == aType) {
6434 doomedElement = i;
6435 listenerCount++;
6439 if (doomedElement == -1) {
6440 return;
6443 mEnabledSensors.RemoveElementAt(doomedElement);
6445 if (listenerCount > 1) {
6446 return;
6449 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
6450 if (ac) {
6451 ac->RemoveWindowListener(aType, this);
6455 #if defined(MOZ_WIDGET_ANDROID)
6456 void nsGlobalWindowInner::EnableOrientationChangeListener() {
6457 if (!ShouldResistFingerprinting(RFPTarget::ScreenOrientation)) {
6458 mHasOrientationChangeListeners = true;
6459 mOrientationAngle = Orientation(CallerType::System);
6463 void nsGlobalWindowInner::DisableOrientationChangeListener() {
6464 mHasOrientationChangeListeners = false;
6466 #endif
6468 void nsGlobalWindowInner::SetHasGamepadEventListener(
6469 bool aHasGamepad /* = true*/) {
6470 mHasGamepad = aHasGamepad;
6471 if (aHasGamepad) {
6472 EnableGamepadUpdates();
6476 void nsGlobalWindowInner::NotifyDetectXRRuntimesCompleted() {
6477 if (!mXRRuntimeDetectionInFlight) {
6478 return;
6480 mXRRuntimeDetectionInFlight = false;
6481 if (mXRPermissionRequestInFlight) {
6482 return;
6484 gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
6485 bool supported = vm->RuntimeSupportsVR();
6486 if (!supported) {
6487 // A VR runtime was not installed; we can suppress
6488 // the permission prompt
6489 OnXRPermissionRequestCancel();
6490 return;
6492 // A VR runtime was found. Display a permission prompt before
6493 // allowing it to be accessed.
6494 // Connect to the VRManager in order to receive the runtime
6495 // detection results.
6496 mXRPermissionRequestInFlight = true;
6497 RefPtr<XRPermissionRequest> request =
6498 new XRPermissionRequest(this, WindowID());
6499 Unused << NS_WARN_IF(NS_FAILED(request->Start()));
6502 void nsGlobalWindowInner::RequestXRPermission() {
6503 if (IsDying()) {
6504 // Do not proceed if the window is dying, as that will result
6505 // in leaks of objects that get re-allocated after FreeInnerObjects
6506 // has been called, including mVREventObserver.
6507 return;
6509 if (mXRPermissionGranted) {
6510 // Don't prompt redundantly once permission to
6511 // access XR devices has been granted.
6512 OnXRPermissionRequestAllow();
6513 return;
6515 if (mXRRuntimeDetectionInFlight || mXRPermissionRequestInFlight) {
6516 // Don't allow multiple simultaneous permissions requests;
6517 return;
6519 // Before displaying a permission prompt, detect
6520 // if there is any VR runtime installed.
6521 gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
6522 mXRRuntimeDetectionInFlight = true;
6523 EnableVRUpdates();
6524 vm->DetectRuntimes();
6527 void nsGlobalWindowInner::OnXRPermissionRequestAllow() {
6528 mXRPermissionRequestInFlight = false;
6529 if (IsDying()) {
6530 // The window may have started dying while the permission request
6531 // is in flight.
6532 // Do not proceed if the window is dying, as that will result
6533 // in leaks of objects that get re-allocated after FreeInnerObjects
6534 // has been called, including mNavigator.
6535 return;
6537 mXRPermissionGranted = true;
6539 NotifyHasXRSession();
6541 dom::Navigator* nav = Navigator();
6542 MOZ_ASSERT(nav != nullptr);
6543 nav->OnXRPermissionRequestAllow();
6546 void nsGlobalWindowInner::OnXRPermissionRequestCancel() {
6547 mXRPermissionRequestInFlight = false;
6548 if (IsDying()) {
6549 // The window may have started dying while the permission request
6550 // is in flight.
6551 // Do not proceed if the window is dying, as that will result
6552 // in leaks of objects that get re-allocated after FreeInnerObjects
6553 // has been called, including mNavigator.
6554 return;
6556 dom::Navigator* nav = Navigator();
6557 MOZ_ASSERT(nav != nullptr);
6558 nav->OnXRPermissionRequestCancel();
6561 void nsGlobalWindowInner::EventListenerAdded(nsAtom* aType) {
6562 if (aType == nsGkAtoms::onvrdisplayactivate ||
6563 aType == nsGkAtoms::onvrdisplayconnect ||
6564 aType == nsGkAtoms::onvrdisplaydeactivate ||
6565 aType == nsGkAtoms::onvrdisplaydisconnect ||
6566 aType == nsGkAtoms::onvrdisplaypresentchange) {
6567 RequestXRPermission();
6570 if (aType == nsGkAtoms::onvrdisplayactivate) {
6571 mHasVRDisplayActivateEvents = true;
6574 if (aType == nsGkAtoms::onunload && mWindowGlobalChild) {
6575 if (++mUnloadOrBeforeUnloadListenerCount == 1) {
6576 mWindowGlobalChild->BlockBFCacheFor(BFCacheStatus::UNLOAD_LISTENER);
6580 if (aType == nsGkAtoms::onbeforeunload && mWindowGlobalChild) {
6581 if (!mozilla::SessionHistoryInParent() ||
6582 !StaticPrefs::
6583 docshell_shistory_bfcache_ship_allow_beforeunload_listeners()) {
6584 if (++mUnloadOrBeforeUnloadListenerCount == 1) {
6585 mWindowGlobalChild->BlockBFCacheFor(
6586 BFCacheStatus::BEFOREUNLOAD_LISTENER);
6589 if (!mDoc || !(mDoc->GetSandboxFlags() & SANDBOXED_MODALS)) {
6590 mWindowGlobalChild->BeforeUnloadAdded();
6591 MOZ_ASSERT(mWindowGlobalChild->BeforeUnloadListeners() > 0);
6595 // We need to initialize localStorage in order to receive notifications.
6596 if (aType == nsGkAtoms::onstorage) {
6597 ErrorResult rv;
6598 GetLocalStorage(rv);
6599 rv.SuppressException();
6601 if (NextGenLocalStorageEnabled() && mLocalStorage &&
6602 mLocalStorage->Type() == Storage::eLocalStorage) {
6603 auto object = static_cast<LSObject*>(mLocalStorage.get());
6605 Unused << NS_WARN_IF(NS_FAILED(object->EnsureObserver()));
6610 void nsGlobalWindowInner::EventListenerRemoved(nsAtom* aType) {
6611 if (aType == nsGkAtoms::onunload && mWindowGlobalChild) {
6612 MOZ_ASSERT(mUnloadOrBeforeUnloadListenerCount > 0);
6613 if (--mUnloadOrBeforeUnloadListenerCount == 0) {
6614 mWindowGlobalChild->UnblockBFCacheFor(BFCacheStatus::UNLOAD_LISTENER);
6618 if (aType == nsGkAtoms::onbeforeunload && mWindowGlobalChild) {
6619 if (!mozilla::SessionHistoryInParent() ||
6620 !StaticPrefs::
6621 docshell_shistory_bfcache_ship_allow_beforeunload_listeners()) {
6622 if (--mUnloadOrBeforeUnloadListenerCount == 0) {
6623 mWindowGlobalChild->UnblockBFCacheFor(
6624 BFCacheStatus::BEFOREUNLOAD_LISTENER);
6627 if (!mDoc || !(mDoc->GetSandboxFlags() & SANDBOXED_MODALS)) {
6628 mWindowGlobalChild->BeforeUnloadRemoved();
6629 MOZ_ASSERT(mWindowGlobalChild->BeforeUnloadListeners() >= 0);
6633 if (aType == nsGkAtoms::onstorage) {
6634 if (NextGenLocalStorageEnabled() && mLocalStorage &&
6635 mLocalStorage->Type() == Storage::eLocalStorage &&
6636 // The remove event is fired even if this isn't the last listener, so
6637 // only remove if there are no other listeners left.
6638 mListenerManager &&
6639 !mListenerManager->HasListenersFor(nsGkAtoms::onstorage)) {
6640 auto object = static_cast<LSObject*>(mLocalStorage.get());
6642 object->DropObserver();
6647 void nsGlobalWindowInner::NotifyHasXRSession() {
6648 if (IsDying()) {
6649 // Do not proceed if the window is dying, as that will result
6650 // in leaks of objects that get re-allocated after FreeInnerObjects
6651 // has been called, including mVREventObserver.
6652 return;
6654 if (mWindowGlobalChild && !mHasXRSession) {
6655 mWindowGlobalChild->BlockBFCacheFor(BFCacheStatus::HAS_USED_VR);
6657 mHasXRSession = true;
6658 EnableVRUpdates();
6661 bool nsGlobalWindowInner::HasUsedVR() const {
6662 // Returns true only if content has enumerated and activated
6663 // XR devices. Detection of XR runtimes without activation
6664 // will not cause true to be returned.
6665 return mHasXRSession;
6668 bool nsGlobalWindowInner::IsVRContentDetected() const {
6669 // Returns true only if the content will respond to
6670 // the VRDisplayActivate event.
6671 return mHasVRDisplayActivateEvents;
6674 bool nsGlobalWindowInner::IsVRContentPresenting() const {
6675 for (const auto& display : mVRDisplays) {
6676 if (display->IsAnyPresenting(gfx::kVRGroupAll)) {
6677 return true;
6680 return false;
6683 void nsGlobalWindowInner::AddSizeOfIncludingThis(
6684 nsWindowSizes& aWindowSizes) const {
6685 aWindowSizes.mDOMSizes.mDOMOtherSize +=
6686 aWindowSizes.mState.mMallocSizeOf(this);
6687 aWindowSizes.mDOMSizes.mDOMOtherSize +=
6688 nsIGlobalObject::ShallowSizeOfExcludingThis(
6689 aWindowSizes.mState.mMallocSizeOf);
6691 EventListenerManager* elm = GetExistingListenerManager();
6692 if (elm) {
6693 aWindowSizes.mDOMSizes.mDOMOtherSize +=
6694 elm->SizeOfIncludingThis(aWindowSizes.mState.mMallocSizeOf);
6695 aWindowSizes.mDOMEventListenersCount += elm->ListenerCount();
6697 if (mDoc) {
6698 // Multiple global windows can share a document. So only measure the
6699 // document if it (a) doesn't have a global window, or (b) it's the
6700 // primary document for the window.
6701 if (!mDoc->GetInnerWindow() || mDoc->GetInnerWindow() == this) {
6702 mDoc->DocAddSizeOfIncludingThis(aWindowSizes);
6706 if (mNavigator) {
6707 aWindowSizes.mDOMSizes.mDOMOtherSize +=
6708 mNavigator->SizeOfIncludingThis(aWindowSizes.mState.mMallocSizeOf);
6711 ForEachGlobalTeardownObserver([&](GlobalTeardownObserver* et,
6712 bool* aDoneOut) {
6713 if (nsCOMPtr<nsISizeOfEventTarget> iSizeOf = do_QueryObject(et)) {
6714 aWindowSizes.mDOMSizes.mDOMEventTargetsSize +=
6715 iSizeOf->SizeOfEventTargetIncludingThis(
6716 aWindowSizes.mState.mMallocSizeOf);
6718 if (nsCOMPtr<DOMEventTargetHelper> helper = do_QueryObject(et)) {
6719 if (EventListenerManager* elm = helper->GetExistingListenerManager()) {
6720 aWindowSizes.mDOMEventListenersCount += elm->ListenerCount();
6723 ++aWindowSizes.mDOMEventTargetsCount;
6726 if (mPerformance) {
6727 aWindowSizes.mDOMSizes.mDOMPerformanceUserEntries =
6728 mPerformance->SizeOfUserEntries(aWindowSizes.mState.mMallocSizeOf);
6729 aWindowSizes.mDOMSizes.mDOMPerformanceResourceEntries =
6730 mPerformance->SizeOfResourceEntries(aWindowSizes.mState.mMallocSizeOf);
6731 aWindowSizes.mDOMSizes.mDOMPerformanceEventEntries =
6732 mPerformance->SizeOfEventEntries(aWindowSizes.mState.mMallocSizeOf);
6736 void nsGlobalWindowInner::RegisterDataDocumentForMemoryReporting(
6737 Document* aDocument) {
6738 aDocument->SetAddedToMemoryReportAsDataDocument();
6739 mDataDocumentsForMemoryReporting.AppendElement(aDocument);
6742 void nsGlobalWindowInner::UnregisterDataDocumentForMemoryReporting(
6743 Document* aDocument) {
6744 DebugOnly<bool> found =
6745 mDataDocumentsForMemoryReporting.RemoveElement(aDocument);
6746 MOZ_ASSERT(found);
6749 void nsGlobalWindowInner::CollectDOMSizesForDataDocuments(
6750 nsWindowSizes& aSize) const {
6751 for (Document* doc : mDataDocumentsForMemoryReporting) {
6752 if (doc) {
6753 doc->DocAddSizeOfIncludingThis(aSize);
6758 void nsGlobalWindowInner::AddGamepad(GamepadHandle aHandle, Gamepad* aGamepad) {
6759 // Create the index we will present to content based on which indices are
6760 // already taken, as required by the spec.
6761 // https://w3c.github.io/gamepad/gamepad.html#widl-Gamepad-index
6762 int index = 0;
6763 while (mGamepadIndexSet.Contains(index)) {
6764 ++index;
6766 mGamepadIndexSet.Put(index);
6767 aGamepad->SetIndex(index);
6768 mGamepads.InsertOrUpdate(aHandle, RefPtr{aGamepad});
6771 void nsGlobalWindowInner::RemoveGamepad(GamepadHandle aHandle) {
6772 RefPtr<Gamepad> gamepad;
6773 if (!mGamepads.Get(aHandle, getter_AddRefs(gamepad))) {
6774 return;
6776 // Free up the index we were using so it can be reused
6777 mGamepadIndexSet.Remove(gamepad->Index());
6778 mGamepads.Remove(aHandle);
6781 void nsGlobalWindowInner::GetGamepads(nsTArray<RefPtr<Gamepad>>& aGamepads) {
6782 aGamepads.Clear();
6784 // navigator.getGamepads() always returns an empty array when
6785 // privacy.resistFingerprinting is true.
6786 if (ShouldResistFingerprinting(RFPTarget::Gamepad)) {
6787 return;
6790 // mGamepads.Count() may not be sufficient, but it's not harmful.
6791 aGamepads.SetCapacity(mGamepads.Count());
6792 for (const auto& entry : mGamepads) {
6793 Gamepad* gamepad = entry.GetWeak();
6794 aGamepads.EnsureLengthAtLeast(gamepad->Index() + 1);
6795 aGamepads[gamepad->Index()] = gamepad;
6799 already_AddRefed<Gamepad> nsGlobalWindowInner::GetGamepad(
6800 GamepadHandle aHandle) {
6801 RefPtr<Gamepad> gamepad;
6803 if (mGamepads.Get(aHandle, getter_AddRefs(gamepad))) {
6804 return gamepad.forget();
6807 return nullptr;
6810 void nsGlobalWindowInner::SetHasSeenGamepadInput(bool aHasSeen) {
6811 mHasSeenGamepadInput = aHasSeen;
6814 bool nsGlobalWindowInner::HasSeenGamepadInput() { return mHasSeenGamepadInput; }
6816 void nsGlobalWindowInner::SyncGamepadState() {
6817 if (mHasSeenGamepadInput) {
6818 RefPtr<GamepadManager> gamepadManager(GamepadManager::GetService());
6819 for (const auto& entry : mGamepads) {
6820 gamepadManager->SyncGamepadState(entry.GetKey(), this, entry.GetWeak());
6825 void nsGlobalWindowInner::StopGamepadHaptics() {
6826 if (mHasSeenGamepadInput) {
6827 RefPtr<GamepadManager> gamepadManager(GamepadManager::GetService());
6828 gamepadManager->StopHaptics();
6832 bool nsGlobalWindowInner::UpdateVRDisplays(
6833 nsTArray<RefPtr<mozilla::dom::VRDisplay>>& aDevices) {
6834 VRDisplay::UpdateVRDisplays(mVRDisplays, this);
6835 aDevices = mVRDisplays.Clone();
6836 return true;
6839 void nsGlobalWindowInner::NotifyActiveVRDisplaysChanged() {
6840 if (mNavigator) {
6841 mNavigator->NotifyActiveVRDisplaysChanged();
6845 void nsGlobalWindowInner::NotifyPresentationGenerationChanged(
6846 uint32_t aDisplayID) {
6847 for (const auto& display : mVRDisplays) {
6848 if (display->DisplayId() == aDisplayID) {
6849 display->OnPresentationGenerationChanged();
6854 void nsGlobalWindowInner::DispatchVRDisplayActivate(
6855 uint32_t aDisplayID, mozilla::dom::VRDisplayEventReason aReason) {
6856 // Ensure that our list of displays is up to date
6857 VRDisplay::UpdateVRDisplays(mVRDisplays, this);
6859 // Search for the display identified with aDisplayID and fire the
6860 // event if found.
6861 for (const auto& display : mVRDisplays) {
6862 if (display->DisplayId() == aDisplayID) {
6863 if (aReason != VRDisplayEventReason::Navigation &&
6864 display->IsAnyPresenting(gfx::kVRGroupContent)) {
6865 // We only want to trigger this event if nobody is presenting to the
6866 // display already or when a page is loaded by navigating away
6867 // from a page with an active VR Presentation.
6868 continue;
6871 VRDisplayEventInit init;
6872 init.mBubbles = false;
6873 init.mCancelable = false;
6874 init.mDisplay = display;
6875 init.mReason.Construct(aReason);
6877 RefPtr<VRDisplayEvent> event =
6878 VRDisplayEvent::Constructor(this, u"vrdisplayactivate"_ns, init);
6879 // vrdisplayactivate is a trusted event, allowing VRDisplay.requestPresent
6880 // to be used in response to link traversal, user request (chrome UX), and
6881 // HMD mounting detection sensors.
6882 event->SetTrusted(true);
6883 // VRDisplay.requestPresent normally requires a user gesture; however, an
6884 // exception is made to allow it to be called in response to
6885 // vrdisplayactivate during VR link traversal.
6886 display->StartHandlingVRNavigationEvent();
6887 DispatchEvent(*event);
6888 display->StopHandlingVRNavigationEvent();
6889 // Once we dispatch the event, we must not access any members as an event
6890 // listener can do anything, including closing windows.
6891 return;
6896 void nsGlobalWindowInner::DispatchVRDisplayDeactivate(
6897 uint32_t aDisplayID, mozilla::dom::VRDisplayEventReason aReason) {
6898 // Ensure that our list of displays is up to date
6899 VRDisplay::UpdateVRDisplays(mVRDisplays, this);
6901 // Search for the display identified with aDisplayID and fire the
6902 // event if found.
6903 for (const auto& display : mVRDisplays) {
6904 if (display->DisplayId() == aDisplayID && display->IsPresenting()) {
6905 // We only want to trigger this event to content that is presenting to
6906 // the display already.
6908 VRDisplayEventInit init;
6909 init.mBubbles = false;
6910 init.mCancelable = false;
6911 init.mDisplay = display;
6912 init.mReason.Construct(aReason);
6914 RefPtr<VRDisplayEvent> event =
6915 VRDisplayEvent::Constructor(this, u"vrdisplaydeactivate"_ns, init);
6916 event->SetTrusted(true);
6917 DispatchEvent(*event);
6918 // Once we dispatch the event, we must not access any members as an event
6919 // listener can do anything, including closing windows.
6920 return;
6925 void nsGlobalWindowInner::DispatchVRDisplayConnect(uint32_t aDisplayID) {
6926 // Ensure that our list of displays is up to date
6927 VRDisplay::UpdateVRDisplays(mVRDisplays, this);
6929 // Search for the display identified with aDisplayID and fire the
6930 // event if found.
6931 for (const auto& display : mVRDisplays) {
6932 if (display->DisplayId() == aDisplayID) {
6933 // Fire event even if not presenting to the display.
6934 VRDisplayEventInit init;
6935 init.mBubbles = false;
6936 init.mCancelable = false;
6937 init.mDisplay = display;
6938 // VRDisplayEvent.reason is not set for vrdisplayconnect
6940 RefPtr<VRDisplayEvent> event =
6941 VRDisplayEvent::Constructor(this, u"vrdisplayconnect"_ns, init);
6942 event->SetTrusted(true);
6943 DispatchEvent(*event);
6944 // Once we dispatch the event, we must not access any members as an event
6945 // listener can do anything, including closing windows.
6946 return;
6951 void nsGlobalWindowInner::DispatchVRDisplayDisconnect(uint32_t aDisplayID) {
6952 // Ensure that our list of displays is up to date
6953 VRDisplay::UpdateVRDisplays(mVRDisplays, this);
6955 // Search for the display identified with aDisplayID and fire the
6956 // event if found.
6957 for (const auto& display : mVRDisplays) {
6958 if (display->DisplayId() == aDisplayID) {
6959 // Fire event even if not presenting to the display.
6960 VRDisplayEventInit init;
6961 init.mBubbles = false;
6962 init.mCancelable = false;
6963 init.mDisplay = display;
6964 // VRDisplayEvent.reason is not set for vrdisplaydisconnect
6966 RefPtr<VRDisplayEvent> event =
6967 VRDisplayEvent::Constructor(this, u"vrdisplaydisconnect"_ns, init);
6968 event->SetTrusted(true);
6969 DispatchEvent(*event);
6970 // Once we dispatch the event, we must not access any members as an event
6971 // listener can do anything, including closing windows.
6972 return;
6977 void nsGlobalWindowInner::DispatchVRDisplayPresentChange(uint32_t aDisplayID) {
6978 // Ensure that our list of displays is up to date
6979 VRDisplay::UpdateVRDisplays(mVRDisplays, this);
6981 // Search for the display identified with aDisplayID and fire the
6982 // event if found.
6983 for (const auto& display : mVRDisplays) {
6984 if (display->DisplayId() == aDisplayID) {
6985 // Fire event even if not presenting to the display.
6986 VRDisplayEventInit init;
6987 init.mBubbles = false;
6988 init.mCancelable = false;
6989 init.mDisplay = display;
6990 // VRDisplayEvent.reason is not set for vrdisplaypresentchange
6991 RefPtr<VRDisplayEvent> event =
6992 VRDisplayEvent::Constructor(this, u"vrdisplaypresentchange"_ns, init);
6993 event->SetTrusted(true);
6994 DispatchEvent(*event);
6995 // Once we dispatch the event, we must not access any members as an event
6996 // listener can do anything, including closing windows.
6997 return;
7002 enum WindowState {
7003 // These constants need to match the constants in Window.webidl
7004 STATE_MAXIMIZED = 1,
7005 STATE_MINIMIZED = 2,
7006 STATE_NORMAL = 3,
7007 STATE_FULLSCREEN = 4
7010 uint16_t nsGlobalWindowInner::WindowState() {
7011 nsCOMPtr<nsIWidget> widget = GetMainWidget();
7013 int32_t mode = widget ? widget->SizeMode() : 0;
7015 switch (mode) {
7016 case nsSizeMode_Minimized:
7017 return STATE_MINIMIZED;
7018 case nsSizeMode_Maximized:
7019 return STATE_MAXIMIZED;
7020 case nsSizeMode_Fullscreen:
7021 return STATE_FULLSCREEN;
7022 case nsSizeMode_Normal:
7023 return STATE_NORMAL;
7024 default:
7025 NS_WARNING("Illegal window state for this chrome window");
7026 break;
7029 return STATE_NORMAL;
7032 bool nsGlobalWindowInner::IsFullyOccluded() {
7033 nsCOMPtr<nsIWidget> widget = GetMainWidget();
7034 return widget && widget->IsFullyOccluded();
7037 void nsGlobalWindowInner::Maximize() {
7038 if (nsCOMPtr<nsIWidget> widget = GetMainWidget()) {
7039 widget->SetSizeMode(nsSizeMode_Maximized);
7043 void nsGlobalWindowInner::Minimize() {
7044 if (nsCOMPtr<nsIWidget> widget = GetMainWidget()) {
7045 widget->SetSizeMode(nsSizeMode_Minimized);
7049 void nsGlobalWindowInner::Restore() {
7050 if (nsCOMPtr<nsIWidget> widget = GetMainWidget()) {
7051 widget->SetSizeMode(nsSizeMode_Normal);
7055 void nsGlobalWindowInner::GetWorkspaceID(nsAString& workspaceID) {
7056 workspaceID.Truncate();
7057 if (nsCOMPtr<nsIWidget> widget = GetMainWidget()) {
7058 return widget->GetWorkspaceID(workspaceID);
7062 void nsGlobalWindowInner::MoveToWorkspace(const nsAString& workspaceID) {
7063 if (nsCOMPtr<nsIWidget> widget = GetMainWidget()) {
7064 widget->MoveToWorkspace(workspaceID);
7068 void nsGlobalWindowInner::GetAttention(ErrorResult& aResult) {
7069 return GetAttentionWithCycleCount(-1, aResult);
7072 void nsGlobalWindowInner::GetAttentionWithCycleCount(int32_t aCycleCount,
7073 ErrorResult& aError) {
7074 nsCOMPtr<nsIWidget> widget = GetMainWidget();
7076 if (widget) {
7077 aError = widget->GetAttention(aCycleCount);
7081 already_AddRefed<Promise> nsGlobalWindowInner::PromiseDocumentFlushed(
7082 PromiseDocumentFlushedCallback& aCallback, ErrorResult& aError) {
7083 MOZ_RELEASE_ASSERT(IsChromeWindow());
7085 if (!IsCurrentInnerWindow()) {
7086 aError.ThrowInvalidStateError("Not the current inner window");
7087 return nullptr;
7089 if (!mDoc) {
7090 aError.ThrowInvalidStateError("No document");
7091 return nullptr;
7094 if (mIteratingDocumentFlushedResolvers) {
7095 aError.ThrowInvalidStateError("Already iterating through resolvers");
7096 return nullptr;
7099 PresShell* presShell = mDoc->GetPresShell();
7100 if (!presShell) {
7101 aError.ThrowInvalidStateError("No pres shell");
7102 return nullptr;
7105 // We need to associate the lifetime of the Promise to the lifetime
7106 // of the caller's global. That way, if the window we're observing
7107 // refresh driver ticks on goes away before our observer is fired,
7108 // we can still resolve the Promise.
7109 nsIGlobalObject* global = GetIncumbentGlobal();
7110 if (!global) {
7111 aError.ThrowInvalidStateError("No incumbent global");
7112 return nullptr;
7115 RefPtr<Promise> resultPromise = Promise::Create(global, aError);
7116 if (aError.Failed()) {
7117 return nullptr;
7120 UniquePtr<PromiseDocumentFlushedResolver> flushResolver(
7121 new PromiseDocumentFlushedResolver(resultPromise, aCallback));
7123 if (!presShell->NeedStyleFlush() && !presShell->NeedLayoutFlush()) {
7124 flushResolver->Call();
7125 return resultPromise.forget();
7128 if (!TryToObserveRefresh()) {
7129 aError.ThrowInvalidStateError("Couldn't observe refresh");
7130 return nullptr;
7133 mDocumentFlushedResolvers.AppendElement(std::move(flushResolver));
7134 return resultPromise.forget();
7137 bool nsGlobalWindowInner::TryToObserveRefresh() {
7138 if (mObservingRefresh) {
7139 return true;
7142 if (!mDoc) {
7143 return false;
7146 nsPresContext* pc = mDoc->GetPresContext();
7147 if (!pc) {
7148 return false;
7151 mObservingRefresh = true;
7152 auto observer = MakeRefPtr<ManagedPostRefreshObserver>(
7153 pc, [win = RefPtr{this}](bool aWasCanceled) {
7154 if (win->MaybeCallDocumentFlushedResolvers(
7155 /* aUntilExhaustion = */ aWasCanceled)) {
7156 return ManagedPostRefreshObserver::Unregister::No;
7158 win->mObservingRefresh = false;
7159 return ManagedPostRefreshObserver::Unregister::Yes;
7161 pc->RegisterManagedPostRefreshObserver(observer.get());
7162 return mObservingRefresh;
7165 void nsGlobalWindowInner::CallDocumentFlushedResolvers(bool aUntilExhaustion) {
7166 while (true) {
7168 // To coalesce MicroTask checkpoints inside callback call, enclose the
7169 // inner loop with nsAutoMicroTask, and perform a MicroTask checkpoint
7170 // after the loop.
7171 nsAutoMicroTask mt;
7173 mIteratingDocumentFlushedResolvers = true;
7175 auto resolvers = std::move(mDocumentFlushedResolvers);
7176 for (const auto& resolver : resolvers) {
7177 resolver->Call();
7180 mIteratingDocumentFlushedResolvers = false;
7183 // Leaving nsAutoMicroTask above will perform MicroTask checkpoint, and
7184 // Promise callbacks there may create mDocumentFlushedResolvers items.
7186 // If there's no new resolvers, or we're not exhausting the queue, there's
7187 // nothing to do (we'll keep observing if there's any new observer).
7189 // Otherwise, keep looping to call all promises. This case can happen while
7190 // destroying the window. This violates the constraint that the
7191 // promiseDocumentFlushed callback only ever run when no flush is needed,
7192 // but it's necessary to resolve the Promise returned by that.
7193 if (!aUntilExhaustion || mDocumentFlushedResolvers.IsEmpty()) {
7194 break;
7199 bool nsGlobalWindowInner::MaybeCallDocumentFlushedResolvers(
7200 bool aUntilExhaustion) {
7201 MOZ_ASSERT(mDoc);
7203 PresShell* presShell = mDoc->GetPresShell();
7204 if (!presShell || aUntilExhaustion) {
7205 CallDocumentFlushedResolvers(/* aUntilExhaustion = */ true);
7206 return false;
7209 if (presShell->NeedStyleFlush() || presShell->NeedLayoutFlush()) {
7210 // By the time our observer fired, something has already invalidated
7211 // style or layout - or perhaps we're still in the middle of a flush that
7212 // was interrupted. In either case, we'll wait until the next refresh driver
7213 // tick instead and try again.
7214 return true;
7217 CallDocumentFlushedResolvers(/* aUntilExhaustion = */ false);
7218 return !mDocumentFlushedResolvers.IsEmpty();
7221 already_AddRefed<nsWindowRoot> nsGlobalWindowInner::GetWindowRoot(
7222 mozilla::ErrorResult& aError) {
7223 FORWARD_TO_OUTER_OR_THROW(GetWindowRootOuter, (), aError, nullptr);
7226 void nsGlobalWindowInner::SetCursor(const nsACString& aCursor,
7227 ErrorResult& aError) {
7228 FORWARD_TO_OUTER_OR_THROW(SetCursorOuter, (aCursor, aError), aError, );
7231 nsIBrowserDOMWindow* nsGlobalWindowInner::GetBrowserDOMWindow(
7232 ErrorResult& aError) {
7233 FORWARD_TO_OUTER_OR_THROW(GetBrowserDOMWindow, (), aError, nullptr);
7236 void nsGlobalWindowInner::SetBrowserDOMWindow(
7237 nsIBrowserDOMWindow* aBrowserWindow, ErrorResult& aError) {
7238 FORWARD_TO_OUTER_OR_THROW(SetBrowserDOMWindowOuter, (aBrowserWindow),
7239 aError, );
7242 void nsGlobalWindowInner::NotifyDefaultButtonLoaded(Element& aDefaultButton,
7243 ErrorResult& aError) {
7244 // Don't snap to a disabled button.
7245 nsCOMPtr<nsIDOMXULControlElement> xulControl = aDefaultButton.AsXULControl();
7246 if (!xulControl) {
7247 aError.Throw(NS_ERROR_FAILURE);
7248 return;
7250 bool disabled;
7251 aError = xulControl->GetDisabled(&disabled);
7252 if (aError.Failed() || disabled) {
7253 return;
7256 // Get the button rect in screen coordinates.
7257 nsIFrame* frame = aDefaultButton.GetPrimaryFrame();
7258 if (!frame) {
7259 aError.Throw(NS_ERROR_FAILURE);
7260 return;
7262 LayoutDeviceIntRect buttonRect = LayoutDeviceIntRect::FromAppUnitsToNearest(
7263 frame->GetScreenRectInAppUnits(),
7264 frame->PresContext()->AppUnitsPerDevPixel());
7266 // Get the widget rect in screen coordinates.
7267 nsIWidget* widget = GetNearestWidget();
7268 if (!widget) {
7269 aError.Throw(NS_ERROR_FAILURE);
7270 return;
7272 LayoutDeviceIntRect widgetRect = widget->GetScreenBounds();
7274 // Convert the buttonRect coordinates from screen to the widget.
7275 buttonRect -= widgetRect.TopLeft();
7276 nsresult rv = widget->OnDefaultButtonLoaded(buttonRect);
7277 if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) {
7278 aError.Throw(rv);
7282 ChromeMessageBroadcaster* nsGlobalWindowInner::MessageManager() {
7283 MOZ_ASSERT(IsChromeWindow());
7284 if (!mChromeFields.mMessageManager) {
7285 RefPtr<ChromeMessageBroadcaster> globalMM =
7286 nsFrameMessageManager::GetGlobalMessageManager();
7287 mChromeFields.mMessageManager = new ChromeMessageBroadcaster(globalMM);
7289 return mChromeFields.mMessageManager;
7292 ChromeMessageBroadcaster* nsGlobalWindowInner::GetGroupMessageManager(
7293 const nsAString& aGroup) {
7294 MOZ_ASSERT(IsChromeWindow());
7296 return mChromeFields.mGroupMessageManagers
7297 .LookupOrInsertWith(
7298 aGroup,
7299 [&] {
7300 return MakeAndAddRef<ChromeMessageBroadcaster>(MessageManager());
7302 .get();
7305 void nsGlobalWindowInner::InitWasOffline() { mWasOffline = NS_IsOffline(); }
7307 int16_t nsGlobalWindowInner::Orientation(CallerType aCallerType) {
7308 // GetOrientationAngle() returns 0, 90, 180 or 270.
7309 // window.orientation returns -90, 0, 90 or 180.
7310 if (nsIGlobalObject::ShouldResistFingerprinting(
7311 aCallerType, RFPTarget::ScreenOrientation)) {
7312 return 0;
7314 int16_t angle = AssertedCast<int16_t>(Screen()->GetOrientationAngle());
7315 return angle <= 180 ? angle : angle - 360;
7318 already_AddRefed<Console> nsGlobalWindowInner::GetConsole(JSContext* aCx,
7319 ErrorResult& aRv) {
7320 if (!mConsole) {
7321 mConsole = Console::Create(aCx, this, aRv);
7322 if (NS_WARN_IF(aRv.Failed())) {
7323 return nullptr;
7327 RefPtr<Console> console = mConsole;
7328 return console.forget();
7331 bool nsGlobalWindowInner::IsSecureContext() const {
7332 JS::Realm* realm = js::GetNonCCWObjectRealm(GetWrapperPreserveColor());
7333 return JS::GetIsSecureContext(realm);
7336 External* nsGlobalWindowInner::External() {
7337 if (!mExternal) {
7338 mExternal = new dom::External(ToSupports(this));
7341 return mExternal;
7344 void nsGlobalWindowInner::ClearDocumentDependentSlots(JSContext* aCx) {
7345 // If JSAPI OOMs here, there is basically nothing we can do to recover safely.
7346 if (!Window_Binding::ClearCachedDocumentValue(aCx, this) ||
7347 !Window_Binding::ClearCachedPerformanceValue(aCx, this)) {
7348 MOZ_CRASH("Unhandlable OOM while clearing document dependent slots.");
7352 /* static */
7353 JSObject* nsGlobalWindowInner::CreateNamedPropertiesObject(
7354 JSContext* aCx, JS::Handle<JSObject*> aProto) {
7355 return WindowNamedPropertiesHandler::Create(aCx, aProto);
7358 void nsGlobalWindowInner::RedefineProperty(JSContext* aCx,
7359 const char* aPropName,
7360 JS::Handle<JS::Value> aValue,
7361 ErrorResult& aError) {
7362 JS::Rooted<JSObject*> thisObj(aCx, GetWrapperPreserveColor());
7363 if (!thisObj) {
7364 aError.Throw(NS_ERROR_UNEXPECTED);
7365 return;
7368 if (!JS_WrapObject(aCx, &thisObj) ||
7369 !JS_DefineProperty(aCx, thisObj, aPropName, aValue, JSPROP_ENUMERATE)) {
7370 aError.Throw(NS_ERROR_FAILURE);
7374 void nsGlobalWindowInner::FireOnNewGlobalObject() {
7375 // AutoEntryScript required to invoke debugger hook, which is a
7376 // Gecko-specific concept at present.
7377 AutoEntryScript aes(this, "nsGlobalWindowInner report new global");
7378 JS::Rooted<JSObject*> global(aes.cx(), GetWrapper());
7379 JS_FireOnNewGlobalObject(aes.cx(), global);
7382 #if defined(_WINDOWS_) && !defined(MOZ_WRAPPED_WINDOWS_H)
7383 # pragma message( \
7384 "wrapper failure reason: " MOZ_WINDOWS_WRAPPER_DISABLED_REASON)
7385 # error "Never include unwrapped windows.h in this file!"
7386 #endif
7388 already_AddRefed<Promise> nsGlobalWindowInner::CreateImageBitmap(
7389 const ImageBitmapSource& aImage, const ImageBitmapOptions& aOptions,
7390 ErrorResult& aRv) {
7391 return ImageBitmap::Create(this, aImage, Nothing(), aOptions, aRv);
7394 already_AddRefed<Promise> nsGlobalWindowInner::CreateImageBitmap(
7395 const ImageBitmapSource& aImage, int32_t aSx, int32_t aSy, int32_t aSw,
7396 int32_t aSh, const ImageBitmapOptions& aOptions, ErrorResult& aRv) {
7397 return ImageBitmap::Create(
7398 this, aImage, Some(gfx::IntRect(aSx, aSy, aSw, aSh)), aOptions, aRv);
7401 // https://html.spec.whatwg.org/#structured-cloning
7402 void nsGlobalWindowInner::StructuredClone(
7403 JSContext* aCx, JS::Handle<JS::Value> aValue,
7404 const StructuredSerializeOptions& aOptions,
7405 JS::MutableHandle<JS::Value> aRetval, ErrorResult& aError) {
7406 nsContentUtils::StructuredClone(aCx, this, aValue, aOptions, aRetval, aError);
7409 nsresult nsGlobalWindowInner::Dispatch(
7410 already_AddRefed<nsIRunnable>&& aRunnable) const {
7411 MOZ_RELEASE_ASSERT(NS_IsMainThread());
7412 return NS_DispatchToCurrentThread(std::move(aRunnable));
7415 nsISerialEventTarget* nsGlobalWindowInner::SerialEventTarget() const {
7416 MOZ_RELEASE_ASSERT(NS_IsMainThread());
7417 return GetMainThreadSerialEventTarget();
7420 Worklet* nsGlobalWindowInner::GetPaintWorklet(ErrorResult& aRv) {
7421 if (!mPaintWorklet) {
7422 nsIPrincipal* principal = GetPrincipal();
7423 if (!principal) {
7424 aRv.Throw(NS_ERROR_FAILURE);
7425 return nullptr;
7428 mPaintWorklet = PaintWorkletImpl::CreateWorklet(this, principal);
7431 return mPaintWorklet;
7434 void nsGlobalWindowInner::GetRegionalPrefsLocales(
7435 nsTArray<nsString>& aLocales) {
7436 MOZ_ASSERT(mozilla::intl::LocaleService::GetInstance());
7438 AutoTArray<nsCString, 10> rpLocales;
7439 mozilla::intl::LocaleService::GetInstance()->GetRegionalPrefsLocales(
7440 rpLocales);
7442 for (const auto& loc : rpLocales) {
7443 aLocales.AppendElement(NS_ConvertUTF8toUTF16(loc));
7447 void nsGlobalWindowInner::GetWebExposedLocales(nsTArray<nsString>& aLocales) {
7448 MOZ_ASSERT(mozilla::intl::LocaleService::GetInstance());
7450 AutoTArray<nsCString, 10> rpLocales;
7451 mozilla::intl::LocaleService::GetInstance()->GetWebExposedLocales(rpLocales);
7453 for (const auto& loc : rpLocales) {
7454 aLocales.AppendElement(NS_ConvertUTF8toUTF16(loc));
7458 IntlUtils* nsGlobalWindowInner::GetIntlUtils(ErrorResult& aError) {
7459 if (!mIntlUtils) {
7460 mIntlUtils = new IntlUtils(this);
7463 return mIntlUtils;
7466 void nsGlobalWindowInner::StoreSharedWorker(SharedWorker* aSharedWorker) {
7467 MOZ_ASSERT(aSharedWorker);
7468 MOZ_ASSERT(!mSharedWorkers.Contains(aSharedWorker));
7470 mSharedWorkers.AppendElement(aSharedWorker);
7473 void nsGlobalWindowInner::ForgetSharedWorker(SharedWorker* aSharedWorker) {
7474 MOZ_ASSERT(aSharedWorker);
7475 MOZ_ASSERT(mSharedWorkers.Contains(aSharedWorker));
7477 mSharedWorkers.RemoveElement(aSharedWorker);
7480 RefPtr<GenericPromise> nsGlobalWindowInner::StorageAccessPermissionChanged(
7481 bool aGranted) {
7482 // Invalidate cached StorageAllowed field so that calls to GetLocalStorage
7483 // give us the updated localStorage object.
7484 ClearStorageAllowedCache();
7486 // If we're always partitioning non-cookie third party storage then
7487 // there is no need to clear it when the user accepts requestStorageAccess.
7488 if (StaticPrefs::
7489 privacy_partition_always_partition_third_party_non_cookie_storage()) {
7490 // Just reset the active cookie and storage principals
7491 nsCOMPtr<nsICookieJarSettings> cjs;
7492 if (mDoc) {
7493 cjs = mDoc->CookieJarSettings();
7495 StorageAccess storageAccess = StorageAllowedForWindow(this);
7496 if (ShouldPartitionStorage(storageAccess) &&
7497 StoragePartitioningEnabled(storageAccess, cjs)) {
7498 if (mDoc) {
7499 mDoc->ClearActiveCookieAndStoragePrincipals();
7501 // When storage access is granted the content process needs to request the
7502 // updated cookie list from the parent process. Otherwise the site won't
7503 // have access to unpartitioned cookies via document.cookie without a
7504 // reload.
7505 if (aGranted) {
7506 nsIChannel* channel = mDoc->GetChannel();
7507 if (channel) {
7508 // The promise resolves when the updated cookie list has been received
7509 // from the parent.
7510 return ContentChild::UpdateCookieStatus(channel);
7516 PropagateStorageAccessPermissionGrantedToWorkers(*this);
7518 // If we have a partitioned localStorage, it's time to replace it with a real
7519 // one in order to receive notifications.
7521 if (mLocalStorage) {
7522 IgnoredErrorResult error;
7523 GetLocalStorage(error);
7524 if (NS_WARN_IF(error.Failed())) {
7525 return MozPromise<bool, nsresult, true>::CreateAndReject(
7526 error.StealNSResult(), __func__);
7529 MOZ_ASSERT(mLocalStorage &&
7530 mLocalStorage->Type() == Storage::eLocalStorage);
7532 if (NextGenLocalStorageEnabled() && mListenerManager &&
7533 mListenerManager->HasListenersFor(nsGkAtoms::onstorage)) {
7534 auto object = static_cast<LSObject*>(mLocalStorage.get());
7536 object->EnsureObserver();
7540 // Reset the IndexedDB factory.
7541 mIndexedDB = nullptr;
7543 // Reset DOM Cache
7544 mCacheStorage = nullptr;
7546 // Reset the active cookie and storage principals
7547 if (mDoc) {
7548 mDoc->ClearActiveCookieAndStoragePrincipals();
7549 if (mWindowGlobalChild) {
7550 // XXX(farre): This is a bit backwards, but clearing the cookie
7551 // principal might make us end up with a new effective storage
7552 // principal on the child side than on the parent side, which
7553 // means that we need to sync it. See bug 1705359.
7554 mWindowGlobalChild->SetDocumentPrincipal(
7555 mDoc->NodePrincipal(), mDoc->EffectiveStoragePrincipal());
7559 // When storage access is granted the content process needs to request the
7560 // updated cookie list from the parent process. Otherwise the site won't have
7561 // access to unpartitioned cookies via document.cookie without a reload.
7562 if (aGranted) {
7563 nsIChannel* channel = mDoc->GetChannel();
7564 if (channel) {
7565 // The promise resolves when the updated cookie list has been received
7566 // from the parent.
7567 return ContentChild::UpdateCookieStatus(channel);
7570 return MozPromise<bool, nsresult, true>::CreateAndResolve(true, __func__);
7573 ContentMediaController* nsGlobalWindowInner::GetContentMediaController() {
7574 if (mContentMediaController) {
7575 return mContentMediaController;
7577 if (!mBrowsingContext) {
7578 return nullptr;
7581 mContentMediaController = new ContentMediaController(mBrowsingContext->Id());
7582 return mContentMediaController;
7585 void nsGlobalWindowInner::SetScrollMarks(const nsTArray<uint32_t>& aScrollMarks,
7586 bool aOnHScrollbar) {
7587 mScrollMarks.Assign(aScrollMarks);
7588 mScrollMarksOnHScrollbar = aOnHScrollbar;
7590 // Mark the scrollbar for repainting.
7591 if (mDoc) {
7592 PresShell* presShell = mDoc->GetPresShell();
7593 if (presShell) {
7594 nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollable();
7595 if (sf) {
7596 sf->InvalidateScrollbars();
7602 /* static */
7603 already_AddRefed<nsGlobalWindowInner> nsGlobalWindowInner::Create(
7604 nsGlobalWindowOuter* aOuterWindow, bool aIsChrome,
7605 WindowGlobalChild* aActor) {
7606 RefPtr<nsGlobalWindowInner> window =
7607 new nsGlobalWindowInner(aOuterWindow, aActor);
7608 if (aIsChrome) {
7609 window->mIsChrome = true;
7610 window->mCleanMessageManager = true;
7613 if (aActor) {
7614 aActor->InitWindowGlobal(window);
7617 window->InitWasOffline();
7618 return window.forget();
7621 JS::loader::ModuleLoaderBase* nsGlobalWindowInner::GetModuleLoader(
7622 JSContext* aCx) {
7623 Document* document = GetDocument();
7624 if (!document) {
7625 return nullptr;
7628 ScriptLoader* loader = document->ScriptLoader();
7629 if (!loader) {
7630 return nullptr;
7633 return loader->GetModuleLoader();
7636 void nsGlobalWindowInner::SetCurrentPasteDataTransfer(
7637 DataTransfer* aDataTransfer) {
7638 MOZ_ASSERT_IF(aDataTransfer, aDataTransfer->GetEventMessage() == ePaste);
7639 MOZ_ASSERT_IF(aDataTransfer, aDataTransfer->ClipboardType() ==
7640 nsIClipboard::kGlobalClipboard);
7641 MOZ_ASSERT_IF(aDataTransfer, aDataTransfer->GetAsyncGetClipboardData());
7642 mCurrentPasteDataTransfer = aDataTransfer;
7645 DataTransfer* nsGlobalWindowInner::GetCurrentPasteDataTransfer() const {
7646 return mCurrentPasteDataTransfer;
7649 TrustedTypePolicyFactory* nsGlobalWindowInner::TrustedTypes() {
7650 if (!mTrustedTypePolicyFactory) {
7651 mTrustedTypePolicyFactory = MakeRefPtr<TrustedTypePolicyFactory>(this);
7654 return mTrustedTypePolicyFactory;
7657 nsIURI* nsPIDOMWindowInner::GetDocumentURI() const {
7658 return mDoc ? mDoc->GetDocumentURI() : mDocumentURI.get();
7661 nsIURI* nsPIDOMWindowInner::GetDocBaseURI() const {
7662 return mDoc ? mDoc->GetDocBaseURI() : mDocBaseURI.get();
7665 mozilla::dom::WindowContext* nsPIDOMWindowInner::GetWindowContext() const {
7666 return mWindowGlobalChild ? mWindowGlobalChild->WindowContext() : nullptr;
7669 bool nsPIDOMWindowInner::RemoveFromBFCacheSync() {
7670 if (Document* doc = GetExtantDoc()) {
7671 return doc->RemoveFromBFCacheSync();
7673 return false;
7676 void nsPIDOMWindowInner::MaybeCreateDoc() {
7677 // XXX: Forward to outer?
7678 MOZ_ASSERT(!mDoc);
7679 if (nsIDocShell* docShell = GetDocShell()) {
7680 // Note that |document| here is the same thing as our mDoc, but we
7681 // don't have to explicitly set the member variable because the docshell
7682 // has already called SetNewDocument().
7683 nsCOMPtr<Document> document = docShell->GetDocument();
7684 Unused << document;
7688 mozilla::dom::DocGroup* nsPIDOMWindowInner::GetDocGroup() const {
7689 Document* doc = GetExtantDoc();
7690 if (doc) {
7691 return doc->GetDocGroup();
7693 return nullptr;
7696 mozilla::dom::BrowsingContextGroup*
7697 nsPIDOMWindowInner::GetBrowsingContextGroup() const {
7698 return mBrowsingContext ? mBrowsingContext->Group() : nullptr;
7701 nsIGlobalObject* nsPIDOMWindowInner::AsGlobal() {
7702 return nsGlobalWindowInner::Cast(this);
7705 const nsIGlobalObject* nsPIDOMWindowInner::AsGlobal() const {
7706 return nsGlobalWindowInner::Cast(this);
7709 RefPtr<GenericPromise>
7710 nsPIDOMWindowInner::SaveStorageAccessPermissionGranted() {
7711 WindowContext* wc = GetWindowContext();
7712 if (wc) {
7713 Unused << wc->SetUsingStorageAccess(true);
7716 return nsGlobalWindowInner::Cast(this)->StorageAccessPermissionChanged(true);
7719 RefPtr<GenericPromise>
7720 nsPIDOMWindowInner::SaveStorageAccessPermissionRevoked() {
7721 WindowContext* wc = GetWindowContext();
7722 if (wc) {
7723 Unused << wc->SetUsingStorageAccess(false);
7726 return nsGlobalWindowInner::Cast(this)->StorageAccessPermissionChanged(false);
7729 bool nsPIDOMWindowInner::UsingStorageAccess() {
7730 WindowContext* wc = GetWindowContext();
7731 if (!wc) {
7732 return false;
7735 return wc->GetUsingStorageAccess();
7738 nsPIDOMWindowInner::nsPIDOMWindowInner(nsPIDOMWindowOuter* aOuterWindow,
7739 WindowGlobalChild* aActor)
7740 : mMutationBits(0),
7741 mIsDocumentLoaded(false),
7742 mIsHandlingResizeEvent(false),
7743 mMayHaveDOMActivateEventListeners(false),
7744 mMayHavePaintEventListener(false),
7745 mMayHaveTouchEventListener(false),
7746 mMayHaveSelectionChangeEventListener(false),
7747 mMayHaveFormSelectEventListener(false),
7748 mMayHaveMouseEnterLeaveEventListener(false),
7749 mMayHavePointerEnterLeaveEventListener(false),
7750 mMayHaveTransitionEventListener(false),
7751 mMayHaveBeforeInputEventListenerForTelemetry(false),
7752 mMutationObserverHasObservedNodeForTelemetry(false),
7753 mOuterWindow(aOuterWindow),
7754 mWindowID(0),
7755 mHasNotifiedGlobalCreated(false),
7756 mMarkedCCGeneration(0),
7757 mHasTriedToCacheTopInnerWindow(false),
7758 mNumOfIndexedDBDatabases(0),
7759 mNumOfOpenWebSockets(0),
7760 mEvent(nullptr),
7761 mWindowGlobalChild(aActor),
7762 mWasSuspendedByGroup(false) {
7763 MOZ_ASSERT(aOuterWindow);
7764 mBrowsingContext = aOuterWindow->GetBrowsingContext();
7766 if (mWindowGlobalChild) {
7767 mWindowID = aActor->InnerWindowId();
7769 MOZ_ASSERT(mWindowGlobalChild->BrowsingContext() == mBrowsingContext);
7770 } else {
7771 mWindowID = nsContentUtils::GenerateWindowId();
7775 nsPIDOMWindowInner::~nsPIDOMWindowInner() = default;
7777 #undef FORWARD_TO_OUTER
7778 #undef FORWARD_TO_OUTER_OR_THROW
7779 #undef FORWARD_TO_OUTER_VOID