Bug 1835710 - Cancel off-thread JIT compilation before changing nursery allocation...
[gecko.git] / dom / base / nsGlobalWindowInner.cpp
blob198a9c4896b27671c016101838cde7ab9a4349eb
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsGlobalWindowInner.h"
9 #include <inttypes.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <cstdint>
14 #include <new>
15 #include <type_traits>
16 #include <utility>
17 #include "AudioChannelService.h"
18 #include "AutoplayPolicy.h"
19 #include "Crypto.h"
20 #include "MainThreadUtils.h"
21 #include "Navigator.h"
22 #include "PaintWorkletImpl.h"
23 #include "SessionStorageCache.h"
24 #include "Units.h"
25 #include "VRManagerChild.h"
26 #include "WindowDestroyedEvent.h"
27 #include "WindowNamedPropertiesHandler.h"
28 #include "js/ComparisonOperators.h"
29 #include "js/CompileOptions.h"
30 #include "js/friend/PerformanceHint.h"
31 #include "js/Id.h"
32 #include "js/loader/LoadedScript.h"
33 #include "js/PropertyAndElement.h" // JS_DefineProperty, JS_GetProperty
34 #include "js/PropertyDescriptor.h"
35 #include "js/RealmOptions.h"
36 #include "js/RootingAPI.h"
37 #include "js/TypeDecls.h"
38 #include "js/Value.h"
39 #include "js/Warnings.h"
40 #include "js/shadow/String.h"
41 #include "jsapi.h"
42 #include "jsfriendapi.h"
43 #include "mozIDOMWindow.h"
44 #include "moz_external_vr.h"
45 #include "mozilla/AlreadyAddRefed.h"
46 #include "mozilla/ArrayIterator.h"
47 #include "mozilla/ArrayUtils.h"
48 #include "mozilla/Attributes.h"
49 #include "mozilla/BaseProfilerMarkersPrerequisites.h"
50 #include "mozilla/BasicEvents.h"
51 #include "mozilla/CallState.h"
52 #include "mozilla/CycleCollectedJSContext.h"
53 #include "mozilla/DOMEventTargetHelper.h"
54 #include "mozilla/ErrorResult.h"
55 #include "mozilla/EventDispatcher.h"
56 #include "mozilla/EventListenerManager.h"
57 #include "mozilla/EventQueue.h"
58 #include "mozilla/ExtensionPolicyService.h"
59 #include "mozilla/FloatingPoint.h"
60 #include "mozilla/FlushType.h"
61 #include "mozilla/Likely.h"
62 #include "mozilla/LinkedList.h"
63 #include "mozilla/LookAndFeel.h"
64 #include "mozilla/Logging.h"
65 #include "mozilla/MacroForEach.h"
66 #include "mozilla/Maybe.h"
67 #include "mozilla/OwningNonNull.h"
68 #include "mozilla/PermissionDelegateHandler.h"
69 #include "mozilla/Preferences.h"
70 #include "mozilla/PresShell.h"
71 #include "mozilla/ProcessHangMonitor.h"
72 #include "mozilla/RefPtr.h"
73 #include "mozilla/Result.h"
74 #include "mozilla/ScrollTypes.h"
75 #include "mozilla/Components.h"
76 #include "mozilla/SizeOfState.h"
77 #include "mozilla/Span.h"
78 #include "mozilla/SpinEventLoopUntil.h"
79 #include "mozilla/Sprintf.h"
80 #include "mozilla/StaticPrefs_browser.h"
81 #include "mozilla/StaticPrefs_docshell.h"
82 #include "mozilla/StaticPrefs_dom.h"
83 #include "mozilla/StaticPrefs_extensions.h"
84 #include "mozilla/StaticPrefs_privacy.h"
85 #include "mozilla/StorageAccess.h"
86 #include "mozilla/StoragePrincipalHelper.h"
87 #include "mozilla/TaskCategory.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/ContentFrameMessageManager.h"
110 #include "mozilla/dom/ContentMediaController.h"
111 #include "mozilla/dom/CustomElementRegistry.h"
112 #include "mozilla/dom/DebuggerNotification.h"
113 #include "mozilla/dom/DebuggerNotificationBinding.h"
114 #include "mozilla/dom/DebuggerNotificationManager.h"
115 #include "mozilla/dom/DispatcherTrait.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/PopStateEvent.h"
147 #include "mozilla/dom/PopStateEventBinding.h"
148 #include "mozilla/dom/PopupBlocker.h"
149 #include "mozilla/dom/PrimitiveConversions.h"
150 #include "mozilla/dom/Promise.h"
151 #include "mozilla/dom/RootedDictionary.h"
152 #include "mozilla/dom/WebTaskSchedulerMainThread.h"
153 #include "mozilla/dom/ScriptLoader.h"
154 #include "mozilla/dom/ScriptSettings.h"
155 #include "mozilla/dom/ServiceWorker.h"
156 #include "mozilla/dom/ServiceWorkerDescriptor.h"
157 #include "mozilla/dom/ServiceWorkerRegistration.h"
158 #include "mozilla/dom/SessionStorageManager.h"
159 #include "mozilla/dom/SharedWorker.h"
160 #include "mozilla/dom/Storage.h"
161 #include "mozilla/dom/StorageEvent.h"
162 #include "mozilla/dom/StorageEventBinding.h"
163 #include "mozilla/dom/StorageNotifierService.h"
164 #include "mozilla/dom/StorageUtils.h"
165 #include "mozilla/dom/TabMessageTypes.h"
166 #include "mozilla/dom/Timeout.h"
167 #include "mozilla/dom/TimeoutHandler.h"
168 #include "mozilla/dom/TimeoutManager.h"
169 #include "mozilla/dom/ToJSValue.h"
170 #include "mozilla/dom/VRDisplay.h"
171 #include "mozilla/dom/VRDisplayEvent.h"
172 #include "mozilla/dom/VRDisplayEventBinding.h"
173 #include "mozilla/dom/VREventObserver.h"
174 #include "mozilla/dom/VisualViewport.h"
175 #include "mozilla/dom/WebIDLGlobalNameHash.h"
176 #include "mozilla/dom/WindowBinding.h"
177 #include "mozilla/dom/WindowContext.h"
178 #include "mozilla/dom/WindowGlobalChild.h"
179 #include "mozilla/dom/WindowProxyHolder.h"
180 #include "mozilla/dom/WorkerCommon.h"
181 #include "mozilla/dom/Worklet.h"
182 #include "mozilla/dom/XRPermissionRequest.h"
183 #include "mozilla/dom/cache/CacheStorage.h"
184 #include "mozilla/dom/cache/Types.h"
185 #include "mozilla/glean/bindings/Glean.h"
186 #include "mozilla/glean/bindings/GleanPings.h"
187 #include "mozilla/extensions/WebExtensionPolicy.h"
188 #include "mozilla/fallible.h"
189 #include "mozilla/gfx/BasePoint.h"
190 #include "mozilla/gfx/BaseRect.h"
191 #include "mozilla/gfx/BaseSize.h"
192 #include "mozilla/gfx/Rect.h"
193 #include "mozilla/gfx/Types.h"
194 #include "mozilla/intl/LocaleService.h"
195 #include "mozilla/ipc/BackgroundUtils.h"
196 #include "mozilla/ipc/PBackgroundSharedTypes.h"
197 #include "mozilla/net/CookieJarSettings.h"
198 #include "nsAtom.h"
199 #include "nsBaseHashtable.h"
200 #include "nsCCUncollectableMarker.h"
201 #include "nsCOMPtr.h"
202 #include "nsCRT.h"
203 #include "nsCRTGlue.h"
204 #include "nsCanvasFrame.h"
205 #include "nsCharTraits.h"
206 #include "nsCheapSets.h"
207 #include "nsContentUtils.h"
208 #include "nsCoord.h"
209 #include "nsCycleCollectionNoteChild.h"
210 #include "nsCycleCollectionTraversalCallback.h"
211 #include "nsDOMNavigationTiming.h"
212 #include "nsDebug.h"
213 #include "nsDeviceContext.h"
214 #include "nsDocShell.h"
215 #include "nsFocusManager.h"
216 #include "nsFrameMessageManager.h"
217 #include "nsGkAtoms.h"
218 #include "nsGlobalWindowOuter.h"
219 #include "nsHashKeys.h"
220 #include "nsHistory.h"
221 #include "nsIAddonPolicyService.h"
222 #include "nsIArray.h"
223 #include "nsIBaseWindow.h"
224 #include "nsIBrowserChild.h"
225 #include "nsICancelableRunnable.h"
226 #include "nsIChannel.h"
227 #include "nsIContentSecurityPolicy.h"
228 #include "nsIControllers.h"
229 #include "nsICookieJarSettings.h"
230 #include "nsICookieService.h"
231 #include "nsID.h"
232 #include "nsIDOMStorageManager.h"
233 #include "nsIDeviceSensors.h"
234 #include "nsIDocShell.h"
235 #include "nsIDocShellTreeItem.h"
236 #include "nsIDocShellTreeOwner.h"
237 #include "nsIDocumentLoader.h"
238 #include "nsIDragService.h"
239 #include "nsIFocusManager.h"
240 #include "nsIFrame.h"
241 #include "nsIGlobalObject.h"
242 #include "nsIIOService.h"
243 #include "nsIIdleRunnable.h"
244 #include "nsIInterfaceRequestorUtils.h"
245 #include "nsILoadContext.h"
246 #include "nsILoadGroup.h"
247 #include "nsILoadInfo.h"
248 #include "nsINamed.h"
249 #include "nsINode.h"
250 #include "nsIObserver.h"
251 #include "nsIObserverService.h"
252 #include "nsIPermission.h"
253 #include "nsIPermissionManager.h"
254 #include "nsIPrefBranch.h"
255 #include "nsIPrincipal.h"
256 #include "nsIPrompt.h"
257 #include "nsIRunnable.h"
258 #include "nsIScreen.h"
259 #include "nsIScreenManager.h"
260 #include "nsIScriptContext.h"
261 #include "nsIScriptGlobalObject.h"
262 #include "nsIScriptObjectPrincipal.h"
263 #include "nsIScrollableFrame.h"
264 #include "nsISerialEventTarget.h"
265 #include "nsISimpleEnumerator.h"
266 #include "nsISizeOfEventTarget.h"
267 #include "nsISlowScriptDebug.h"
268 #include "nsISupportsUtils.h"
269 #include "nsIThread.h"
270 #include "nsITimedChannel.h"
271 #include "nsIURI.h"
272 #include "nsIWeakReference.h"
273 #include "nsIWebBrowserChrome.h"
274 #include "nsIWebNavigation.h"
275 #include "nsIWebProgressListener.h"
276 #include "nsIWidget.h"
277 #include "nsIWidgetListener.h"
278 #include "nsIXULRuntime.h"
279 #include "nsJSPrincipals.h"
280 #include "nsJSUtils.h"
281 #include "nsLayoutStatics.h"
282 #include "nsLiteralString.h"
283 #include "nsNetUtil.h"
284 #include "nsPIDOMWindow.h"
285 #include "nsPIDOMWindowInlines.h"
286 #include "nsPIWindowRoot.h"
287 #include "nsPoint.h"
288 #include "nsPresContext.h"
289 #include "nsQueryObject.h"
290 #include "nsSandboxFlags.h"
291 #include "nsScreen.h"
292 #include "nsServiceManagerUtils.h"
293 #include "nsString.h"
294 #include "nsStringFlags.h"
295 #include "nsStringFwd.h"
296 #include "nsTArray.h"
297 #include "nsTLiteralString.h"
298 #include "nsTObserverArray.h"
299 #include "nsTStringRepr.h"
300 #include "nsThreadUtils.h"
301 #include "nsWeakReference.h"
302 #include "nsWindowMemoryReporter.h"
303 #include "nsWindowSizes.h"
304 #include "nsWrapperCache.h"
305 #include "nsWrapperCacheInlines.h"
306 #include "nsXULAppAPI.h"
307 #include "nsrootidl.h"
308 #include "prclist.h"
309 #include "prtypes.h"
310 #include "xpcprivate.h"
311 #include "xpcpublic.h"
313 #include "nsIDOMXULControlElement.h"
315 #ifdef NS_PRINTING
316 # include "nsIPrintSettings.h"
317 #endif
319 #ifdef MOZ_WEBSPEECH
320 # include "mozilla/dom/SpeechSynthesis.h"
321 #endif
323 #ifdef ANDROID
324 # include <android/log.h>
325 #endif
327 #ifdef XP_WIN
328 # include "mozilla/Debug.h"
329 # include <process.h>
330 # define getpid _getpid
331 #else
332 # include <unistd.h> // for getpid()
333 #endif
335 using namespace mozilla;
336 using namespace mozilla::dom;
337 using namespace mozilla::dom::ipc;
338 using mozilla::TimeDuration;
339 using mozilla::TimeStamp;
340 using mozilla::dom::GamepadHandle;
341 using mozilla::dom::cache::CacheStorage;
343 #define FORWARD_TO_OUTER(method, args, err_rval) \
344 PR_BEGIN_MACRO \
345 RefPtr<nsGlobalWindowOuter> outer = GetOuterWindowInternal(); \
346 if (!HasActiveDocument()) { \
347 NS_WARNING(outer ? "Inner window does not have active document." \
348 : "No outer window available!"); \
349 return err_rval; \
351 return outer->method args; \
352 PR_END_MACRO
354 static nsGlobalWindowOuter* GetOuterWindowForForwarding(
355 nsGlobalWindowInner* aInner, ErrorResult& aError) {
356 nsGlobalWindowOuter* outer = aInner->GetOuterWindowInternal();
357 if (MOZ_LIKELY(aInner->HasActiveDocument())) {
358 return outer;
360 if (!outer) {
361 NS_WARNING("No outer window available!");
362 aError.Throw(NS_ERROR_NOT_INITIALIZED);
363 } else {
364 aError.Throw(NS_ERROR_XPC_SECURITY_MANAGER_VETO);
366 return nullptr;
369 #define FORWARD_TO_OUTER_OR_THROW(method, args, rv, err_rval) \
370 PR_BEGIN_MACRO \
371 RefPtr<nsGlobalWindowOuter> outer = GetOuterWindowForForwarding(this, rv); \
372 if (MOZ_LIKELY(outer)) { \
373 return outer->method args; \
375 return err_rval; \
376 PR_END_MACRO
378 #define FORWARD_TO_OUTER_VOID(method, args) \
379 PR_BEGIN_MACRO \
380 RefPtr<nsGlobalWindowOuter> outer = GetOuterWindowInternal(); \
381 if (!HasActiveDocument()) { \
382 NS_WARNING(outer ? "Inner window does not have active document." \
383 : "No outer window available!"); \
384 return; \
386 outer->method args; \
387 return; \
388 PR_END_MACRO
390 #define ENSURE_ACTIVE_DOCUMENT(errorresult, err_rval) \
391 PR_BEGIN_MACRO \
392 if (MOZ_UNLIKELY(!HasActiveDocument())) { \
393 aError.Throw(NS_ERROR_XPC_SECURITY_MANAGER_VETO); \
394 return err_rval; \
396 PR_END_MACRO
398 #define DOM_TOUCH_LISTENER_ADDED "dom-touch-listener-added"
399 #define MEMORY_PRESSURE_OBSERVER_TOPIC "memory-pressure"
400 #define PERMISSION_CHANGED_TOPIC "perm-changed"
402 static LazyLogModule gDOMLeakPRLogInner("DOMLeakInner");
403 extern mozilla::LazyLogModule gTimeoutLog;
405 #ifdef DEBUG
406 static LazyLogModule gDocShellAndDOMWindowLeakLogging(
407 "DocShellAndDOMWindowLeak");
408 #endif
410 static FILE* gDumpFile = nullptr;
412 nsGlobalWindowInner::InnerWindowByIdTable*
413 nsGlobalWindowInner::sInnerWindowsById = nullptr;
415 bool nsGlobalWindowInner::sDragServiceDisabled = false;
416 bool nsGlobalWindowInner::sMouseDown = false;
419 * An indirect observer object that means we don't have to implement nsIObserver
420 * on nsGlobalWindow, where any script could see it.
422 class nsGlobalWindowObserver final : public nsIObserver,
423 public nsIInterfaceRequestor,
424 public StorageNotificationObserver {
425 public:
426 explicit nsGlobalWindowObserver(nsGlobalWindowInner* aWindow)
427 : mWindow(aWindow) {}
428 NS_DECL_ISUPPORTS
429 NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
430 const char16_t* aData) override {
431 if (!mWindow) return NS_OK;
432 return mWindow->Observe(aSubject, aTopic, aData);
434 void Forget() { mWindow = nullptr; }
435 NS_IMETHOD GetInterface(const nsIID& aIID, void** aResult) override {
436 if (mWindow && aIID.Equals(NS_GET_IID(nsIDOMWindow)) && mWindow) {
437 return mWindow->QueryInterface(aIID, aResult);
439 return NS_NOINTERFACE;
442 void ObserveStorageNotification(StorageEvent* aEvent,
443 const char16_t* aStorageType,
444 bool aPrivateBrowsing) override {
445 if (mWindow) {
446 mWindow->ObserveStorageNotification(aEvent, aStorageType,
447 aPrivateBrowsing);
451 nsIPrincipal* GetEffectiveCookiePrincipal() const override {
452 return mWindow ? mWindow->GetEffectiveCookiePrincipal() : nullptr;
455 nsIPrincipal* GetEffectiveStoragePrincipal() const override {
456 return mWindow ? mWindow->GetEffectiveStoragePrincipal() : nullptr;
459 bool IsPrivateBrowsing() const override {
460 return mWindow ? mWindow->IsPrivateBrowsing() : false;
463 nsIEventTarget* GetEventTarget() const override {
464 return mWindow ? mWindow->EventTargetFor(TaskCategory::Other) : nullptr;
467 private:
468 ~nsGlobalWindowObserver() = default;
470 // This reference is non-owning and safe because it's cleared by
471 // nsGlobalWindowInner::FreeInnerObjects().
472 nsGlobalWindowInner* MOZ_NON_OWNING_REF mWindow;
475 NS_IMPL_ISUPPORTS(nsGlobalWindowObserver, nsIObserver, nsIInterfaceRequestor)
477 class IdleRequestExecutor;
479 class IdleRequestExecutorTimeoutHandler final : public TimeoutHandler {
480 public:
481 explicit IdleRequestExecutorTimeoutHandler(IdleRequestExecutor* aExecutor)
482 : mExecutor(aExecutor) {}
484 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
485 NS_DECL_CYCLE_COLLECTION_CLASS(IdleRequestExecutorTimeoutHandler)
487 bool Call(const char* /* unused */) override;
489 private:
490 ~IdleRequestExecutorTimeoutHandler() override = default;
491 RefPtr<IdleRequestExecutor> mExecutor;
494 NS_IMPL_CYCLE_COLLECTION(IdleRequestExecutorTimeoutHandler, mExecutor)
496 NS_IMPL_CYCLE_COLLECTING_ADDREF(IdleRequestExecutorTimeoutHandler)
497 NS_IMPL_CYCLE_COLLECTING_RELEASE(IdleRequestExecutorTimeoutHandler)
499 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestExecutorTimeoutHandler)
500 NS_INTERFACE_MAP_ENTRY(nsISupports)
501 NS_INTERFACE_MAP_END
503 class IdleRequestExecutor final : public nsIRunnable,
504 public nsICancelableRunnable,
505 public nsINamed,
506 public nsIIdleRunnable {
507 public:
508 explicit IdleRequestExecutor(nsGlobalWindowInner* aWindow)
509 : mDispatched(false), mDeadline(TimeStamp::Now()), mWindow(aWindow) {
510 MOZ_DIAGNOSTIC_ASSERT(mWindow);
512 mIdlePeriodLimit = {mDeadline, mWindow->LastIdleRequestHandle()};
513 mDelayedExecutorDispatcher = new IdleRequestExecutorTimeoutHandler(this);
516 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
517 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(IdleRequestExecutor, nsIRunnable)
519 NS_DECL_NSIRUNNABLE
520 NS_DECL_NSINAMED
521 nsresult Cancel() override;
522 void SetDeadline(TimeStamp aDeadline) override;
524 bool IsCancelled() const { return !mWindow || mWindow->IsDying(); }
525 // Checks if aRequest shouldn't execute in the current idle period
526 // since it has been queued from a chained call to
527 // requestIdleCallback from within a running idle callback.
528 bool IneligibleForCurrentIdlePeriod(IdleRequest* aRequest) const {
529 return aRequest->Handle() >= mIdlePeriodLimit.mLastRequestIdInIdlePeriod &&
530 TimeStamp::Now() <= mIdlePeriodLimit.mEndOfIdlePeriod;
533 void MaybeUpdateIdlePeriodLimit();
535 // Maybe dispatch the IdleRequestExecutor. MabyeDispatch will
536 // schedule a delayed dispatch if the associated window is in the
537 // background or if given a time to wait until dispatching.
538 void MaybeDispatch(TimeStamp aDelayUntil = TimeStamp());
539 void ScheduleDispatch();
541 private:
542 struct IdlePeriodLimit {
543 TimeStamp mEndOfIdlePeriod;
544 uint32_t mLastRequestIdInIdlePeriod;
547 void DelayedDispatch(uint32_t aDelay);
549 ~IdleRequestExecutor() override = default;
551 bool mDispatched;
552 TimeStamp mDeadline;
553 IdlePeriodLimit mIdlePeriodLimit;
554 RefPtr<nsGlobalWindowInner> mWindow;
555 // The timeout handler responsible for dispatching this executor in
556 // the case of immediate dispatch to the idle queue isn't
557 // desirable. This is used if we've dispatched all idle callbacks
558 // that are allowed to run in the current idle period, or if the
559 // associated window is currently in the background.
560 RefPtr<TimeoutHandler> mDelayedExecutorDispatcher;
561 // If not Nothing() then this value is the handle to the currently
562 // scheduled delayed executor dispatcher. This is needed to be able
563 // to cancel the timeout handler in case of the executor being
564 // cancelled.
565 Maybe<int32_t> mDelayedExecutorHandle;
568 NS_IMPL_CYCLE_COLLECTION(IdleRequestExecutor, mWindow,
569 mDelayedExecutorDispatcher)
571 NS_IMPL_CYCLE_COLLECTING_ADDREF(IdleRequestExecutor)
572 NS_IMPL_CYCLE_COLLECTING_RELEASE(IdleRequestExecutor)
574 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestExecutor)
575 NS_INTERFACE_MAP_ENTRY(nsIRunnable)
576 NS_INTERFACE_MAP_ENTRY(nsICancelableRunnable)
577 NS_INTERFACE_MAP_ENTRY(nsINamed)
578 NS_INTERFACE_MAP_ENTRY(nsIIdleRunnable)
579 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRunnable)
580 NS_INTERFACE_MAP_END
582 NS_IMETHODIMP
583 IdleRequestExecutor::GetName(nsACString& aName) {
584 aName.AssignLiteral("IdleRequestExecutor");
585 return NS_OK;
588 // MOZ_CAN_RUN_SCRIPT_BOUNDARY until nsIRunnable::Run is MOZ_CAN_RUN_SCRIPT.
589 // See bug 1535398.
590 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP IdleRequestExecutor::Run() {
591 MOZ_ASSERT(NS_IsMainThread());
593 mDispatched = false;
594 if (mWindow) {
595 RefPtr<nsGlobalWindowInner> window(mWindow);
596 window->ExecuteIdleRequest(mDeadline);
599 return NS_OK;
602 nsresult IdleRequestExecutor::Cancel() {
603 MOZ_ASSERT(NS_IsMainThread());
605 if (mDelayedExecutorHandle && mWindow) {
606 mWindow->TimeoutManager().ClearTimeout(
607 mDelayedExecutorHandle.value(), Timeout::Reason::eIdleCallbackTimeout);
610 mWindow = nullptr;
611 return NS_OK;
614 void IdleRequestExecutor::SetDeadline(TimeStamp aDeadline) {
615 MOZ_ASSERT(NS_IsMainThread());
617 if (!mWindow) {
618 return;
621 mDeadline = aDeadline;
624 void IdleRequestExecutor::MaybeUpdateIdlePeriodLimit() {
625 if (TimeStamp::Now() > mIdlePeriodLimit.mEndOfIdlePeriod) {
626 mIdlePeriodLimit = {mDeadline, mWindow->LastIdleRequestHandle()};
630 void IdleRequestExecutor::MaybeDispatch(TimeStamp aDelayUntil) {
631 // If we've already dispatched the executor we don't want to do it
632 // again. Also, if we've called IdleRequestExecutor::Cancel mWindow
633 // will be null, which indicates that we shouldn't dispatch this
634 // executor either.
635 if (mDispatched || IsCancelled()) {
636 return;
639 mDispatched = true;
641 nsPIDOMWindowOuter* outer = mWindow->GetOuterWindow();
642 if (outer && outer->IsBackground()) {
643 // Set a timeout handler with a timeout of 0 ms to throttle idle
644 // callback requests coming from a backround window using
645 // background timeout throttling.
646 DelayedDispatch(0);
647 return;
650 TimeStamp now = TimeStamp::Now();
651 if (!aDelayUntil || aDelayUntil < now) {
652 ScheduleDispatch();
653 return;
656 TimeDuration delay = aDelayUntil - now;
657 DelayedDispatch(static_cast<uint32_t>(delay.ToMilliseconds()));
660 void IdleRequestExecutor::ScheduleDispatch() {
661 MOZ_ASSERT(mWindow);
662 mDelayedExecutorHandle = Nothing();
663 RefPtr<IdleRequestExecutor> request = this;
664 NS_DispatchToCurrentThreadQueue(request.forget(), EventQueuePriority::Idle);
667 void IdleRequestExecutor::DelayedDispatch(uint32_t aDelay) {
668 MOZ_ASSERT(mWindow);
669 MOZ_ASSERT(mDelayedExecutorHandle.isNothing());
670 int32_t handle;
671 mWindow->TimeoutManager().SetTimeout(
672 mDelayedExecutorDispatcher, aDelay, false,
673 Timeout::Reason::eIdleCallbackTimeout, &handle);
674 mDelayedExecutorHandle = Some(handle);
677 bool IdleRequestExecutorTimeoutHandler::Call(const char* /* unused */) {
678 if (!mExecutor->IsCancelled()) {
679 mExecutor->ScheduleDispatch();
681 return true;
684 void nsGlobalWindowInner::ScheduleIdleRequestDispatch() {
685 AssertIsOnMainThread();
687 if (!mIdleRequestExecutor) {
688 mIdleRequestExecutor = new IdleRequestExecutor(this);
691 mIdleRequestExecutor->MaybeDispatch();
694 void nsGlobalWindowInner::SuspendIdleRequests() {
695 if (mIdleRequestExecutor) {
696 mIdleRequestExecutor->Cancel();
697 mIdleRequestExecutor = nullptr;
701 void nsGlobalWindowInner::ResumeIdleRequests() {
702 MOZ_ASSERT(!mIdleRequestExecutor);
704 ScheduleIdleRequestDispatch();
707 void nsGlobalWindowInner::RemoveIdleCallback(
708 mozilla::dom::IdleRequest* aRequest) {
709 AssertIsOnMainThread();
711 if (aRequest->HasTimeout()) {
712 mTimeoutManager->ClearTimeout(aRequest->GetTimeoutHandle(),
713 Timeout::Reason::eIdleCallbackTimeout);
716 aRequest->removeFrom(mIdleRequestCallbacks);
719 void nsGlobalWindowInner::RunIdleRequest(IdleRequest* aRequest,
720 DOMHighResTimeStamp aDeadline,
721 bool aDidTimeout) {
722 AssertIsOnMainThread();
723 // XXXbz Do we still need this RefPtr? MOZ_CAN_RUN_SCRIPT should
724 // guarantee that caller is holding a strong ref on the stack.
725 RefPtr<IdleRequest> request(aRequest);
726 RemoveIdleCallback(request);
727 request->IdleRun(this, aDeadline, aDidTimeout);
730 void nsGlobalWindowInner::ExecuteIdleRequest(TimeStamp aDeadline) {
731 AssertIsOnMainThread();
732 RefPtr<IdleRequest> request = mIdleRequestCallbacks.getFirst();
734 if (!request) {
735 // There are no more idle requests, so stop scheduling idle
736 // request callbacks.
737 return;
740 // If the request that we're trying to execute has been queued
741 // during the current idle period, then dispatch it again at the end
742 // of the idle period.
743 if (mIdleRequestExecutor->IneligibleForCurrentIdlePeriod(request)) {
744 mIdleRequestExecutor->MaybeDispatch(aDeadline);
745 return;
748 DOMHighResTimeStamp deadline = 0.0;
750 if (Performance* perf = GetPerformance()) {
751 deadline = perf->GetDOMTiming()->TimeStampToDOMHighRes(aDeadline);
754 mIdleRequestExecutor->MaybeUpdateIdlePeriodLimit();
755 RunIdleRequest(request, deadline, false);
757 // Running the idle callback could've suspended the window, in which
758 // case mIdleRequestExecutor will be null.
759 if (mIdleRequestExecutor) {
760 mIdleRequestExecutor->MaybeDispatch();
764 class IdleRequestTimeoutHandler final : public TimeoutHandler {
765 public:
766 IdleRequestTimeoutHandler(JSContext* aCx, IdleRequest* aIdleRequest,
767 nsPIDOMWindowInner* aWindow)
768 : TimeoutHandler(aCx), mIdleRequest(aIdleRequest), mWindow(aWindow) {}
770 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
771 NS_DECL_CYCLE_COLLECTION_CLASS(IdleRequestTimeoutHandler)
773 MOZ_CAN_RUN_SCRIPT bool Call(const char* /* unused */) override {
774 RefPtr<nsGlobalWindowInner> window(nsGlobalWindowInner::Cast(mWindow));
775 RefPtr<IdleRequest> request(mIdleRequest);
776 window->RunIdleRequest(request, 0.0, true);
777 return true;
780 private:
781 ~IdleRequestTimeoutHandler() override = default;
783 RefPtr<IdleRequest> mIdleRequest;
784 nsCOMPtr<nsPIDOMWindowInner> mWindow;
787 NS_IMPL_CYCLE_COLLECTION(IdleRequestTimeoutHandler, mIdleRequest, mWindow)
789 NS_IMPL_CYCLE_COLLECTING_ADDREF(IdleRequestTimeoutHandler)
790 NS_IMPL_CYCLE_COLLECTING_RELEASE(IdleRequestTimeoutHandler)
792 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestTimeoutHandler)
793 NS_INTERFACE_MAP_ENTRY(nsISupports)
794 NS_INTERFACE_MAP_END
796 uint32_t nsGlobalWindowInner::RequestIdleCallback(
797 JSContext* aCx, IdleRequestCallback& aCallback,
798 const IdleRequestOptions& aOptions, ErrorResult& aError) {
799 AssertIsOnMainThread();
801 if (IsDying()) {
802 return 0;
805 uint32_t handle = mIdleRequestCallbackCounter++;
807 RefPtr<IdleRequest> request = new IdleRequest(&aCallback, handle);
809 if (aOptions.mTimeout.WasPassed()) {
810 int32_t timeoutHandle;
811 RefPtr<TimeoutHandler> handler(
812 new IdleRequestTimeoutHandler(aCx, request, this));
814 nsresult rv = mTimeoutManager->SetTimeout(
815 handler, aOptions.mTimeout.Value(), false,
816 Timeout::Reason::eIdleCallbackTimeout, &timeoutHandle);
818 if (NS_WARN_IF(NS_FAILED(rv))) {
819 return 0;
822 request->SetTimeoutHandle(timeoutHandle);
825 mIdleRequestCallbacks.insertBack(request);
827 if (!IsSuspended()) {
828 ScheduleIdleRequestDispatch();
831 return handle;
834 void nsGlobalWindowInner::CancelIdleCallback(uint32_t aHandle) {
835 for (IdleRequest* r : mIdleRequestCallbacks) {
836 if (r->Handle() == aHandle) {
837 RemoveIdleCallback(r);
838 break;
843 void nsGlobalWindowInner::DisableIdleCallbackRequests() {
844 if (mIdleRequestExecutor) {
845 mIdleRequestExecutor->Cancel();
846 mIdleRequestExecutor = nullptr;
849 while (!mIdleRequestCallbacks.isEmpty()) {
850 RefPtr<IdleRequest> request = mIdleRequestCallbacks.getFirst();
851 RemoveIdleCallback(request);
855 bool nsGlobalWindowInner::IsBackgroundInternal() const {
856 return !mOuterWindow || mOuterWindow->IsBackground();
859 class PromiseDocumentFlushedResolver final {
860 public:
861 PromiseDocumentFlushedResolver(Promise* aPromise,
862 PromiseDocumentFlushedCallback& aCallback)
863 : mPromise(aPromise), mCallback(&aCallback) {}
865 virtual ~PromiseDocumentFlushedResolver() = default;
867 void Call() {
868 nsMutationGuard guard;
869 ErrorResult error;
870 JS::Rooted<JS::Value> returnVal(RootingCx());
871 mCallback->Call(&returnVal, error);
873 if (error.Failed()) {
874 mPromise->MaybeReject(std::move(error));
875 } else if (guard.Mutated(0)) {
876 // Something within the callback mutated the DOM.
877 mPromise->MaybeRejectWithNoModificationAllowedError(
878 "DOM mutated from promiseDocumentFlushed callbacks");
879 } else {
880 mPromise->MaybeResolve(returnVal);
884 RefPtr<Promise> mPromise;
885 RefPtr<PromiseDocumentFlushedCallback> mCallback;
888 //*****************************************************************************
889 //*** nsGlobalWindowInner: Object Management
890 //*****************************************************************************
892 nsGlobalWindowInner::nsGlobalWindowInner(nsGlobalWindowOuter* aOuterWindow,
893 WindowGlobalChild* aActor)
894 : nsPIDOMWindowInner(aOuterWindow, aActor),
895 mHasOrientationChangeListeners(false),
896 mWasOffline(false),
897 mHasHadSlowScript(false),
898 mIsChrome(false),
899 mCleanMessageManager(false),
900 mNeedsFocus(true),
901 mHasFocus(false),
902 mFocusByKeyOccurred(false),
903 mDidFireDocElemInserted(false),
904 mHasGamepad(false),
905 mHasXRSession(false),
906 mHasVRDisplayActivateEvents(false),
907 mXRRuntimeDetectionInFlight(false),
908 mXRPermissionRequestInFlight(false),
909 mXRPermissionGranted(false),
910 mWasCurrentInnerWindow(false),
911 mHasSeenGamepadInput(false),
912 mHintedWasLoading(false),
913 mHasOpenedExternalProtocolFrame(false),
914 mScrollMarksOnHScrollbar(false),
915 mStorageAllowedReasonCache(0),
916 mSuspendDepth(0),
917 mFreezeDepth(0),
918 #ifdef DEBUG
919 mSerial(0),
920 #endif
921 mFocusMethod(0),
922 mIdleRequestCallbackCounter(1),
923 mIdleRequestExecutor(nullptr),
924 mObservingRefresh(false),
925 mIteratingDocumentFlushedResolvers(false),
926 mCanSkipCCGeneration(0) {
927 mIsInnerWindow = true;
929 AssertIsOnMainThread();
930 nsLayoutStatics::AddRef();
932 // Initialize the PRCList (this).
933 PR_INIT_CLIST(this);
935 // add this inner window to the outer window list of inners.
936 PR_INSERT_AFTER(this, aOuterWindow);
938 mTimeoutManager = MakeUnique<dom::TimeoutManager>(
939 *this, StaticPrefs::dom_timeout_max_idle_defer_ms());
941 mObserver = new nsGlobalWindowObserver(this);
942 if (nsCOMPtr<nsIObserverService> os = services::GetObserverService()) {
943 // Watch for online/offline status changes so we can fire events. Use
944 // a strong reference.
945 os->AddObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC, false);
946 os->AddObserver(mObserver, MEMORY_PRESSURE_OBSERVER_TOPIC, false);
947 os->AddObserver(mObserver, PERMISSION_CHANGED_TOPIC, false);
948 os->AddObserver(mObserver, "screen-information-changed", false);
951 Preferences::AddStrongObserver(mObserver, "intl.accept_languages");
953 // Watch for storage notifications so we can fire storage events.
954 RefPtr<StorageNotifierService> sns = StorageNotifierService::GetOrCreate();
955 if (sns) {
956 sns->Register(mObserver);
959 if (XRE_IsContentProcess()) {
960 nsCOMPtr<nsIDocShell> docShell = GetDocShell();
961 if (docShell) {
962 mBrowserChild = docShell->GetBrowserChild();
966 if (gDumpFile == nullptr) {
967 nsAutoCString fname;
968 Preferences::GetCString("browser.dom.window.dump.file", fname);
969 if (!fname.IsEmpty()) {
970 // If this fails to open, Dump() knows to just go to stdout on null.
971 gDumpFile = fopen(fname.get(), "wb+");
972 } else {
973 gDumpFile = stdout;
977 #ifdef DEBUG
978 mSerial = nsContentUtils::InnerOrOuterWindowCreated();
980 MOZ_LOG(gDocShellAndDOMWindowLeakLogging, LogLevel::Info,
981 ("++DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p]\n",
982 nsContentUtils::GetCurrentInnerOrOuterWindowCount(),
983 static_cast<void*>(ToCanonicalSupports(this)), getpid(), mSerial,
984 static_cast<void*>(ToCanonicalSupports(aOuterWindow))));
985 #endif
987 MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
988 ("DOMWINDOW %p created outer=%p", this, aOuterWindow));
990 // Add ourselves to the inner windows list.
991 MOZ_ASSERT(sInnerWindowsById, "Inner Windows hash table must be created!");
992 MOZ_ASSERT(!sInnerWindowsById->Contains(mWindowID),
993 "This window shouldn't be in the hash table yet!");
994 // We seem to see crashes in release builds because of null
995 // |sInnerWindowsById|.
996 if (sInnerWindowsById) {
997 sInnerWindowsById->InsertOrUpdate(mWindowID, this);
1001 #ifdef DEBUG
1003 /* static */
1004 void nsGlobalWindowInner::AssertIsOnMainThread() {
1005 MOZ_ASSERT(NS_IsMainThread());
1008 #endif // DEBUG
1010 /* static */
1011 void nsGlobalWindowInner::Init() {
1012 AssertIsOnMainThread();
1014 NS_ASSERTION(gDOMLeakPRLogInner,
1015 "gDOMLeakPRLogInner should have been initialized!");
1017 sInnerWindowsById = new InnerWindowByIdTable();
1020 nsGlobalWindowInner::~nsGlobalWindowInner() {
1021 AssertIsOnMainThread();
1022 MOZ_ASSERT(!mHintedWasLoading);
1024 if (IsChromeWindow()) {
1025 MOZ_ASSERT(mCleanMessageManager,
1026 "chrome windows may always disconnect the msg manager");
1028 DisconnectAndClearGroupMessageManagers();
1030 if (mChromeFields.mMessageManager) {
1031 static_cast<nsFrameMessageManager*>(mChromeFields.mMessageManager.get())
1032 ->Disconnect();
1035 mCleanMessageManager = false;
1038 // In most cases this should already have been called, but call it again
1039 // here to catch any corner cases.
1040 FreeInnerObjects();
1042 if (sInnerWindowsById) {
1043 sInnerWindowsById->Remove(mWindowID);
1046 nsContentUtils::InnerOrOuterWindowDestroyed();
1048 #ifdef DEBUG
1049 if (MOZ_LOG_TEST(gDocShellAndDOMWindowLeakLogging, LogLevel::Info)) {
1050 nsAutoCString url;
1051 if (mLastOpenedURI) {
1052 url = mLastOpenedURI->GetSpecOrDefault();
1054 // Data URLs can be very long, so truncate to avoid flooding the log.
1055 const uint32_t maxURLLength = 1000;
1056 if (url.Length() > maxURLLength) {
1057 url.Truncate(maxURLLength);
1061 nsGlobalWindowOuter* outer = nsGlobalWindowOuter::Cast(mOuterWindow);
1062 MOZ_LOG(
1063 gDocShellAndDOMWindowLeakLogging, LogLevel::Info,
1064 ("--DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p] [url = "
1065 "%s]\n",
1066 nsContentUtils::GetCurrentInnerOrOuterWindowCount(),
1067 static_cast<void*>(ToCanonicalSupports(this)), getpid(), mSerial,
1068 static_cast<void*>(ToCanonicalSupports(outer)), url.get()));
1070 #endif
1071 MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
1072 ("DOMWINDOW %p destroyed", this));
1074 Telemetry::Accumulate(Telemetry::INNERWINDOWS_WITH_MUTATION_LISTENERS,
1075 mMutationBits ? 1 : 0);
1077 // An inner window is destroyed, pull it out of the outer window's
1078 // list if inner windows.
1080 PR_REMOVE_LINK(this);
1082 // If our outer window's inner window is this window, null out the
1083 // outer window's reference to this window that's being deleted.
1084 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
1085 if (outer) {
1086 outer->MaybeClearInnerWindow(this);
1089 // We don't have to leave the tab group if we are an inner window.
1091 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
1092 if (ac) ac->RemoveWindowAsListener(this);
1094 nsLayoutStatics::Release();
1097 // static
1098 void nsGlobalWindowInner::ShutDown() {
1099 AssertIsOnMainThread();
1101 if (gDumpFile && gDumpFile != stdout) {
1102 fclose(gDumpFile);
1104 gDumpFile = nullptr;
1106 delete sInnerWindowsById;
1107 sInnerWindowsById = nullptr;
1110 void nsGlobalWindowInner::FreeInnerObjects() {
1111 if (IsDying()) {
1112 return;
1114 StartDying();
1116 if (mDoc && mDoc->GetWindowContext()) {
1117 // The document is about to lose its window, so this is a good time to send
1118 // our page use counters.
1120 // (We also do this in Document::SetScriptGlobalObject(nullptr), which
1121 // catches most cases of documents losing their window, but not all.)
1122 mDoc->SendPageUseCounters();
1125 // Make sure that this is called before we null out the document and
1126 // other members that the window destroyed observers could
1127 // re-create.
1128 NotifyDOMWindowDestroyed(this);
1129 if (auto* reporter = nsWindowMemoryReporter::Get()) {
1130 reporter->ObserveDOMWindowDetached(this);
1133 // Kill all of the workers for this window.
1134 CancelWorkersForWindow(*this);
1136 for (RefPtr<mozilla::dom::SharedWorker> pinnedWorker :
1137 mSharedWorkers.ForwardRange()) {
1138 pinnedWorker->Close();
1141 if (mTimeoutManager) {
1142 mTimeoutManager->ClearAllTimeouts();
1145 DisableIdleCallbackRequests();
1147 mChromeEventHandler = nullptr;
1149 if (mListenerManager) {
1150 mListenerManager->RemoveAllListeners();
1151 mListenerManager->Disconnect();
1152 mListenerManager = nullptr;
1155 mHistory = nullptr;
1157 if (mNavigator) {
1158 mNavigator->OnNavigation();
1159 mNavigator->Invalidate();
1160 mNavigator = nullptr;
1163 mScreen = nullptr;
1165 if (mDoc) {
1166 // Remember the document's principal, URI, and CSP.
1167 mDocumentPrincipal = mDoc->NodePrincipal();
1168 mDocumentCookiePrincipal = mDoc->EffectiveCookiePrincipal();
1169 mDocumentStoragePrincipal = mDoc->EffectiveStoragePrincipal();
1170 mDocumentPartitionedPrincipal = mDoc->PartitionedPrincipal();
1171 mDocumentURI = mDoc->GetDocumentURI();
1172 mDocBaseURI = mDoc->GetDocBaseURI();
1173 mDocumentCsp = mDoc->GetCsp();
1175 while (mDoc->EventHandlingSuppressed()) {
1176 mDoc->UnsuppressEventHandlingAndFireEvents(false);
1180 // Remove our reference to the document and the document principal.
1181 mFocusedElement = nullptr;
1183 if (mIndexedDB) {
1184 mIndexedDB->DisconnectFromGlobal(this);
1185 mIndexedDB = nullptr;
1188 nsIGlobalObject::UnlinkObjectsInGlobal();
1190 NotifyWindowIDDestroyed("inner-window-destroyed");
1192 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
1193 mAudioContexts[i]->OnWindowDestroy();
1195 mAudioContexts.Clear();
1197 for (MediaKeys* mediaKeys : mMediaKeysInstances) {
1198 mediaKeys->OnInnerWindowDestroy();
1200 mMediaKeysInstances.Clear();
1202 DisableGamepadUpdates();
1203 mHasGamepad = false;
1204 mGamepads.Clear();
1205 DisableVRUpdates();
1206 mHasXRSession = false;
1207 mHasVRDisplayActivateEvents = false;
1208 mXRRuntimeDetectionInFlight = false;
1209 mXRPermissionRequestInFlight = false;
1210 mXRPermissionGranted = false;
1211 mVRDisplays.Clear();
1213 // This breaks a cycle between the window and the ClientSource object.
1214 mClientSource.reset();
1216 if (mWindowGlobalChild) {
1217 // Remove any remaining listeners.
1218 int64_t nListeners = mWindowGlobalChild->BeforeUnloadListeners();
1219 for (int64_t i = 0; i < nListeners; ++i) {
1220 mWindowGlobalChild->BeforeUnloadRemoved();
1222 MOZ_ASSERT(mWindowGlobalChild->BeforeUnloadListeners() == 0);
1225 // If we have any promiseDocumentFlushed callbacks, fire them now so
1226 // that the Promises can resolve.
1227 CallDocumentFlushedResolvers(/* aUntilExhaustion = */ true);
1229 DisconnectEventTargetObjects();
1231 #ifdef MOZ_WIDGET_ANDROID
1232 DisableOrientationChangeListener();
1233 #endif
1235 if (mObserver) {
1236 if (nsCOMPtr<nsIObserverService> os = services::GetObserverService()) {
1237 os->RemoveObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC);
1238 os->RemoveObserver(mObserver, MEMORY_PRESSURE_OBSERVER_TOPIC);
1239 os->RemoveObserver(mObserver, PERMISSION_CHANGED_TOPIC);
1240 os->RemoveObserver(mObserver, "screen-information-changed");
1243 RefPtr<StorageNotifierService> sns = StorageNotifierService::GetOrCreate();
1244 if (sns) {
1245 sns->Unregister(mObserver);
1248 Preferences::RemoveObserver(mObserver, "intl.accept_languages");
1250 // Drop its reference to this dying window, in case for some bogus reason
1251 // the object stays around.
1252 mObserver->Forget();
1255 mMenubar = nullptr;
1256 mToolbar = nullptr;
1257 mLocationbar = nullptr;
1258 mPersonalbar = nullptr;
1259 mStatusbar = nullptr;
1260 mScrollbars = nullptr;
1262 mConsole = nullptr;
1264 mPaintWorklet = nullptr;
1266 mExternal = nullptr;
1267 mInstallTrigger = nullptr;
1269 if (mLocalStorage) {
1270 mLocalStorage->Disconnect();
1271 mLocalStorage = nullptr;
1273 mSessionStorage = nullptr;
1274 mPerformance = nullptr;
1276 mContentMediaController = nullptr;
1278 if (mWebTaskScheduler) {
1279 mWebTaskScheduler->Disconnect();
1280 mWebTaskScheduler = nullptr;
1283 mSharedWorkers.Clear();
1285 #ifdef MOZ_WEBSPEECH
1286 mSpeechSynthesis = nullptr;
1287 #endif
1289 mGlean = nullptr;
1290 mGleanPings = nullptr;
1292 mParentTarget = nullptr;
1294 if (mCleanMessageManager) {
1295 MOZ_ASSERT(mIsChrome, "only chrome should have msg manager cleaned");
1296 if (mChromeFields.mMessageManager) {
1297 mChromeFields.mMessageManager->Disconnect();
1301 if (mWindowGlobalChild && !mWindowGlobalChild->IsClosed()) {
1302 mWindowGlobalChild->Destroy();
1305 mIntlUtils = nullptr;
1307 HintIsLoading(false);
1310 //*****************************************************************************
1311 // nsGlobalWindowInner::nsISupports
1312 //*****************************************************************************
1314 // QueryInterface implementation for nsGlobalWindowInner
1315 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGlobalWindowInner)
1316 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
1317 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, EventTarget)
1318 NS_INTERFACE_MAP_ENTRY(nsIDOMWindow)
1319 NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
1320 NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObject)
1321 NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
1322 NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget)
1323 NS_INTERFACE_MAP_ENTRY(nsPIDOMWindowInner)
1324 NS_INTERFACE_MAP_ENTRY(mozIDOMWindow)
1325 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDOMChromeWindow, IsChromeWindow())
1326 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
1327 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
1328 NS_INTERFACE_MAP_END
1330 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGlobalWindowInner)
1331 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGlobalWindowInner)
1333 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGlobalWindowInner)
1334 if (tmp->IsBlackForCC(false)) {
1335 if (nsCCUncollectableMarker::InGeneration(tmp->mCanSkipCCGeneration)) {
1336 return true;
1338 tmp->mCanSkipCCGeneration = nsCCUncollectableMarker::sGeneration;
1339 if (EventListenerManager* elm = tmp->GetExistingListenerManager()) {
1340 elm->MarkForCC();
1342 if (tmp->mTimeoutManager) {
1343 tmp->mTimeoutManager->UnmarkGrayTimers();
1345 return true;
1347 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
1349 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsGlobalWindowInner)
1350 return tmp->IsBlackForCC(true);
1351 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
1353 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsGlobalWindowInner)
1354 return tmp->IsBlackForCC(false);
1355 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
1357 NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalWindowInner)
1359 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindowInner)
1360 if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
1361 char name[512];
1362 nsAutoCString uri;
1363 if (tmp->mDoc && tmp->mDoc->GetDocumentURI()) {
1364 uri = tmp->mDoc->GetDocumentURI()->GetSpecOrDefault();
1366 SprintfLiteral(name, "nsGlobalWindowInner # %" PRIu64 " inner %s",
1367 tmp->mWindowID, uri.get());
1368 cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
1369 } else {
1370 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsGlobalWindowInner, tmp->mRefCnt.get())
1373 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNavigator)
1375 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance)
1377 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWebTaskScheduler)
1379 #ifdef MOZ_WEBSPEECH
1380 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSpeechSynthesis)
1381 #endif
1383 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlean)
1384 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGleanPings)
1386 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOuterWindow)
1388 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTopInnerWindow)
1390 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager)
1392 if (tmp->mTimeoutManager) {
1393 tmp->mTimeoutManager->ForEachUnorderedTimeout([&cb](Timeout* timeout) {
1394 cb.NoteNativeChild(timeout, NS_CYCLE_COLLECTION_PARTICIPANT(Timeout));
1398 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocation)
1399 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHistory)
1400 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCustomElements)
1401 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSharedWorkers)
1402 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStorage)
1403 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSessionStorage)
1404 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndexedDB)
1405 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPrincipal)
1406 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentCookiePrincipal)
1407 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentStoragePrincipal)
1408 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPartitionedPrincipal)
1409 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentCsp)
1410 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowserChild)
1411 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDoc)
1413 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleRequestExecutor)
1414 for (IdleRequest* request : tmp->mIdleRequestCallbacks) {
1415 cb.NoteNativeChild(request, NS_CYCLE_COLLECTION_PARTICIPANT(IdleRequest));
1418 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mClientSource)
1420 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGamepads)
1422 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCacheStorage)
1423 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVRDisplays)
1425 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDebuggerNotificationManager)
1427 // Traverse stuff from nsPIDOMWindow
1428 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeEventHandler)
1429 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentTarget)
1430 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFocusedElement)
1431 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowsingContext)
1432 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindowGlobalChild)
1434 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMenubar)
1435 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mToolbar)
1436 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocationbar)
1437 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPersonalbar)
1438 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStatusbar)
1439 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScrollbars)
1440 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCrypto)
1441 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsole)
1442 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPaintWorklet)
1443 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExternal)
1444 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInstallTrigger)
1445 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIntlUtils)
1446 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVisualViewport)
1448 tmp->TraverseObjectsInGlobal(cb);
1450 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeFields.mMessageManager)
1451 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeFields.mGroupMessageManagers)
1453 for (size_t i = 0; i < tmp->mDocumentFlushedResolvers.Length(); i++) {
1454 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentFlushedResolvers[i]->mPromise);
1455 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentFlushedResolvers[i]->mCallback);
1458 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1460 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindowInner)
1461 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
1462 if (sInnerWindowsById) {
1463 sInnerWindowsById->Remove(tmp->mWindowID);
1466 JSObject* wrapper = tmp->GetWrapperPreserveColor();
1467 if (wrapper) {
1468 // Mark our realm as dead, so the JS engine won't hand out our
1469 // global after this point.
1470 JS::SetRealmNonLive(js::GetNonCCWObjectRealm(wrapper));
1473 NS_IMPL_CYCLE_COLLECTION_UNLINK(mNavigator)
1475 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance)
1477 if (tmp->mWebTaskScheduler) {
1478 tmp->mWebTaskScheduler->Disconnect();
1479 NS_IMPL_CYCLE_COLLECTION_UNLINK(mWebTaskScheduler)
1482 #ifdef MOZ_WEBSPEECH
1483 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSpeechSynthesis)
1484 #endif
1486 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlean)
1487 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGleanPings)
1489 if (tmp->mOuterWindow) {
1490 nsGlobalWindowOuter::Cast(tmp->mOuterWindow)->MaybeClearInnerWindow(tmp);
1491 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOuterWindow)
1494 if (tmp->mListenerManager) {
1495 tmp->mListenerManager->Disconnect();
1496 NS_IMPL_CYCLE_COLLECTION_UNLINK(mListenerManager)
1499 // Here the Timeouts list would've been unlinked, but we rely on
1500 // that Timeout objects have been traced and will remove themselves
1501 // while unlinking.
1503 tmp->UpdateTopInnerWindow();
1504 NS_IMPL_CYCLE_COLLECTION_UNLINK(mTopInnerWindow)
1506 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocation)
1507 NS_IMPL_CYCLE_COLLECTION_UNLINK(mHistory)
1508 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCustomElements)
1509 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSharedWorkers)
1510 if (tmp->mLocalStorage) {
1511 tmp->mLocalStorage->Disconnect();
1512 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalStorage)
1514 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSessionStorage)
1515 if (tmp->mIndexedDB) {
1516 tmp->mIndexedDB->DisconnectFromGlobal(tmp);
1517 NS_IMPL_CYCLE_COLLECTION_UNLINK(mIndexedDB)
1519 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPrincipal)
1520 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentCookiePrincipal)
1521 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentStoragePrincipal)
1522 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPartitionedPrincipal)
1523 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentCsp)
1524 NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowserChild)
1525 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDoc)
1527 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGamepads)
1529 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCacheStorage)
1530 NS_IMPL_CYCLE_COLLECTION_UNLINK(mVRDisplays)
1532 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDebuggerNotificationManager)
1534 // Unlink stuff from nsPIDOMWindow
1535 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeEventHandler)
1536 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParentTarget)
1537 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFocusedElement)
1538 NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowsingContext)
1540 MOZ_DIAGNOSTIC_ASSERT(
1541 !tmp->mWindowGlobalChild || tmp->mWindowGlobalChild->IsClosed(),
1542 "How are we unlinking a window before its actor has been destroyed?");
1543 NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindowGlobalChild)
1545 NS_IMPL_CYCLE_COLLECTION_UNLINK(mMenubar)
1546 NS_IMPL_CYCLE_COLLECTION_UNLINK(mToolbar)
1547 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocationbar)
1548 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPersonalbar)
1549 NS_IMPL_CYCLE_COLLECTION_UNLINK(mStatusbar)
1550 NS_IMPL_CYCLE_COLLECTION_UNLINK(mScrollbars)
1551 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCrypto)
1552 NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole)
1553 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPaintWorklet)
1554 NS_IMPL_CYCLE_COLLECTION_UNLINK(mExternal)
1555 NS_IMPL_CYCLE_COLLECTION_UNLINK(mInstallTrigger)
1556 NS_IMPL_CYCLE_COLLECTION_UNLINK(mIntlUtils)
1557 NS_IMPL_CYCLE_COLLECTION_UNLINK(mVisualViewport)
1559 tmp->UnlinkObjectsInGlobal();
1561 NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleRequestExecutor)
1563 // Here the IdleRequest list would've been unlinked, but we rely on
1564 // that IdleRequest objects have been traced and will remove
1565 // themselves while unlinking.
1567 NS_IMPL_CYCLE_COLLECTION_UNLINK(mClientSource)
1569 if (tmp->IsChromeWindow()) {
1570 if (tmp->mChromeFields.mMessageManager) {
1571 static_cast<nsFrameMessageManager*>(
1572 tmp->mChromeFields.mMessageManager.get())
1573 ->Disconnect();
1574 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeFields.mMessageManager)
1576 tmp->DisconnectAndClearGroupMessageManagers();
1577 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeFields.mGroupMessageManagers)
1580 for (size_t i = 0; i < tmp->mDocumentFlushedResolvers.Length(); i++) {
1581 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentFlushedResolvers[i]->mPromise);
1582 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentFlushedResolvers[i]->mCallback);
1584 tmp->mDocumentFlushedResolvers.Clear();
1586 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
1587 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1589 #ifdef DEBUG
1590 void nsGlobalWindowInner::RiskyUnlink() {
1591 NS_CYCLE_COLLECTION_INNERNAME.Unlink(this);
1593 #endif
1595 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGlobalWindowInner)
1596 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
1597 NS_IMPL_CYCLE_COLLECTION_TRACE_END
1599 bool nsGlobalWindowInner::IsBlackForCC(bool aTracingNeeded) {
1600 if (!nsCCUncollectableMarker::sGeneration) {
1601 return false;
1604 return (nsCCUncollectableMarker::InGeneration(GetMarkedCCGeneration()) ||
1605 HasKnownLiveWrapper()) &&
1606 (!aTracingNeeded || HasNothingToTrace(ToSupports(this)));
1609 //*****************************************************************************
1610 // nsGlobalWindowInner::nsIScriptGlobalObject
1611 //*****************************************************************************
1613 bool nsGlobalWindowInner::ShouldResistFingerprinting(
1614 RFPTarget aTarget /* = RFPTarget::Unknown */) const {
1615 if (mDoc) {
1616 return mDoc->ShouldResistFingerprinting(aTarget);
1618 return nsContentUtils::ShouldResistFingerprinting(
1619 "If we do not have a document then we do not have any context"
1620 "to make an informed RFP choice, so we fall back to the global pref",
1621 aTarget);
1624 OriginTrials nsGlobalWindowInner::Trials() const {
1625 return OriginTrials::FromWindow(this);
1628 FontFaceSet* nsGlobalWindowInner::GetFonts() {
1629 if (mDoc) {
1630 return mDoc->Fonts();
1632 return nullptr;
1635 mozilla::Result<mozilla::ipc::PrincipalInfo, nsresult>
1636 nsGlobalWindowInner::GetStorageKey() {
1637 MOZ_ASSERT(NS_IsMainThread());
1639 nsIPrincipal* principal = GetEffectiveStoragePrincipal();
1640 if (!principal) {
1641 return mozilla::Err(NS_ERROR_FAILURE);
1644 mozilla::ipc::PrincipalInfo principalInfo;
1645 nsresult rv = PrincipalToPrincipalInfo(principal, &principalInfo);
1646 if (NS_FAILED(rv)) {
1647 return mozilla::Err(rv);
1650 // Block expanded and null principals, let content and system through.
1651 if (principalInfo.type() !=
1652 mozilla::ipc::PrincipalInfo::TContentPrincipalInfo &&
1653 principalInfo.type() !=
1654 mozilla::ipc::PrincipalInfo::TSystemPrincipalInfo) {
1655 return Err(NS_ERROR_DOM_SECURITY_ERR);
1658 return std::move(principalInfo);
1661 mozilla::dom::StorageManager* nsGlobalWindowInner::GetStorageManager() {
1662 return Navigator()->Storage();
1665 // https://html.spec.whatwg.org/multipage/web-messaging.html#eligible-for-messaging
1666 // * a Window object whose associated Document is fully active
1667 bool nsGlobalWindowInner::IsEligibleForMessaging() { return IsFullyActive(); }
1669 nsresult nsGlobalWindowInner::EnsureScriptEnvironment() {
1670 // NOTE: We can't use FORWARD_TO_OUTER here because we don't want to fail if
1671 // we're called on an inactive inner window.
1672 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
1673 if (!outer) {
1674 NS_WARNING("No outer window available!");
1675 return NS_ERROR_FAILURE;
1677 return outer->EnsureScriptEnvironment();
1680 nsIScriptContext* nsGlobalWindowInner::GetScriptContext() {
1681 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
1682 if (!outer) {
1683 return nullptr;
1685 return outer->GetScriptContext();
1688 void nsGlobalWindowInner::TraceGlobalJSObject(JSTracer* aTrc) {
1689 TraceWrapper(aTrc, "active window global");
1692 void nsGlobalWindowInner::UpdateAutoplayPermission() {
1693 if (!GetWindowContext()) {
1694 return;
1696 uint32_t perm =
1697 media::AutoplayPolicy::GetSiteAutoplayPermission(GetPrincipal());
1698 if (GetWindowContext()->GetAutoplayPermission() == perm) {
1699 return;
1702 // Setting autoplay permission on a discarded context has no effect.
1703 Unused << GetWindowContext()->SetAutoplayPermission(perm);
1706 void nsGlobalWindowInner::UpdateShortcutsPermission() {
1707 if (!GetWindowContext() ||
1708 !GetWindowContext()->GetBrowsingContext()->IsTop()) {
1709 // We only cache the shortcuts permission on top-level WindowContexts
1710 // since we always check the top-level principal for the permission.
1711 return;
1714 uint32_t perm = GetShortcutsPermission(GetPrincipal());
1716 if (GetWindowContext()->GetShortcutsPermission() == perm) {
1717 return;
1720 // If the WindowContext is discarded this has no effect.
1721 Unused << GetWindowContext()->SetShortcutsPermission(perm);
1724 /* static */
1725 uint32_t nsGlobalWindowInner::GetShortcutsPermission(nsIPrincipal* aPrincipal) {
1726 uint32_t perm = nsIPermissionManager::DENY_ACTION;
1727 nsCOMPtr<nsIPermissionManager> permMgr =
1728 mozilla::components::PermissionManager::Service();
1729 if (aPrincipal && permMgr) {
1730 permMgr->TestExactPermissionFromPrincipal(aPrincipal, "shortcuts"_ns,
1731 &perm);
1733 return perm;
1736 void nsGlobalWindowInner::UpdatePopupPermission() {
1737 if (!GetWindowContext()) {
1738 return;
1741 uint32_t perm = PopupBlocker::GetPopupPermission(GetPrincipal());
1742 if (GetWindowContext()->GetPopupPermission() == perm) {
1743 return;
1746 // If the WindowContext is discarded this has no effect.
1747 Unused << GetWindowContext()->SetPopupPermission(perm);
1750 void nsGlobalWindowInner::UpdatePermissions() {
1751 if (!GetWindowContext()) {
1752 return;
1755 nsCOMPtr<nsIPrincipal> principal = GetPrincipal();
1756 RefPtr<WindowContext> windowContext = GetWindowContext();
1758 WindowContext::Transaction txn;
1759 txn.SetAutoplayPermission(
1760 media::AutoplayPolicy::GetSiteAutoplayPermission(principal));
1761 txn.SetPopupPermission(PopupBlocker::GetPopupPermission(principal));
1763 if (windowContext->IsTop()) {
1764 txn.SetShortcutsPermission(GetShortcutsPermission(principal));
1767 // Setting permissions on a discarded WindowContext has no effect
1768 Unused << txn.Commit(windowContext);
1771 void nsGlobalWindowInner::InitDocumentDependentState(JSContext* aCx) {
1772 MOZ_ASSERT(mDoc);
1774 if (MOZ_LOG_TEST(gDOMLeakPRLogInner, LogLevel::Debug)) {
1775 nsIURI* uri = mDoc->GetDocumentURI();
1776 MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
1777 ("DOMWINDOW %p SetNewDocument %s", this,
1778 uri ? uri->GetSpecOrDefault().get() : ""));
1781 mFocusedElement = nullptr;
1782 mLocalStorage = nullptr;
1783 mSessionStorage = nullptr;
1784 mPerformance = nullptr;
1785 if (mWebTaskScheduler) {
1786 mWebTaskScheduler->Disconnect();
1787 mWebTaskScheduler = nullptr;
1790 // This must be called after nullifying the internal objects because here we
1791 // could recreate them, calling the getter methods, and store them into the JS
1792 // slots. If we nullify them after, the slot values and the objects will be
1793 // out of sync.
1794 ClearDocumentDependentSlots(aCx);
1796 if (!mWindowGlobalChild) {
1797 mWindowGlobalChild = WindowGlobalChild::Create(this);
1799 MOZ_ASSERT(!GetWindowContext()->HasBeenUserGestureActivated(),
1800 "WindowContext should always not have user gesture activation at "
1801 "this point.");
1803 UpdatePermissions();
1805 RefPtr<PermissionDelegateHandler> permDelegateHandler =
1806 mDoc->GetPermissionDelegateHandler();
1808 if (permDelegateHandler) {
1809 permDelegateHandler->PopulateAllDelegatedPermissions();
1812 #if defined(MOZ_WIDGET_ANDROID)
1813 // When we insert the new document to the window in the top-level browsing
1814 // context, we should reset the status of the request which is used for the
1815 // previous document.
1816 if (mWindowGlobalChild && GetBrowsingContext() &&
1817 !GetBrowsingContext()->GetParent()) {
1818 // Return value of setting synced field should be checked. See bug 1656492.
1819 Unused << GetBrowsingContext()->ResetGVAutoplayRequestStatus();
1821 #endif
1823 #ifdef DEBUG
1824 mLastOpenedURI = mDoc->GetDocumentURI();
1825 #endif
1827 Telemetry::Accumulate(Telemetry::INNERWINDOWS_WITH_MUTATION_LISTENERS,
1828 mMutationBits ? 1 : 0);
1830 // Clear our mutation bitfield.
1831 mMutationBits = 0;
1834 nsresult nsGlobalWindowInner::EnsureClientSource() {
1835 MOZ_DIAGNOSTIC_ASSERT(mDoc);
1837 bool newClientSource = false;
1839 // Get the load info for the document if we performed a load. Be careful not
1840 // to look at local URLs, though. Local URLs are those that have a scheme of:
1841 // * about:
1842 // * data:
1843 // * blob:
1844 // We also do an additional check here so that we only treat about:blank
1845 // and about:srcdoc as local URLs. Other internal firefox about: URLs should
1846 // not be treated this way.
1847 nsCOMPtr<nsILoadInfo> loadInfo;
1848 nsCOMPtr<nsIChannel> channel = mDoc->GetChannel();
1849 if (channel) {
1850 nsCOMPtr<nsIURI> uri;
1851 Unused << channel->GetURI(getter_AddRefs(uri));
1853 bool ignoreLoadInfo = false;
1855 // Note, this is mostly copied from NS_IsAboutBlank(). Its duplicated
1856 // here so we can efficiently check about:srcdoc as well.
1857 if (uri->SchemeIs("about")) {
1858 nsCString spec = uri->GetSpecOrDefault();
1859 ignoreLoadInfo = spec.EqualsLiteral("about:blank") ||
1860 spec.EqualsLiteral("about:srcdoc");
1861 } else {
1862 // Its not an about: URL, so now check for our other URL types.
1863 ignoreLoadInfo = uri->SchemeIs("data") || uri->SchemeIs("blob");
1866 if (!ignoreLoadInfo) {
1867 loadInfo = channel->LoadInfo();
1871 // Take the initial client source from the docshell immediately. Even if we
1872 // don't end up using it here we should consume it.
1873 UniquePtr<ClientSource> initialClientSource;
1874 nsIDocShell* docshell = GetDocShell();
1875 if (docshell) {
1876 initialClientSource = docshell->TakeInitialClientSource();
1879 // Try to get the reserved client from the LoadInfo. A Client is
1880 // reserved at the start of the channel load if there is not an
1881 // initial about:blank document that will be reused. It is also
1882 // created if the channel load encounters a cross-origin redirect.
1883 if (loadInfo) {
1884 UniquePtr<ClientSource> reservedClient =
1885 loadInfo->TakeReservedClientSource();
1886 if (reservedClient) {
1887 mClientSource.reset();
1888 mClientSource = std::move(reservedClient);
1889 newClientSource = true;
1893 // We don't have a LoadInfo reserved client, but maybe we should
1894 // be inheriting an initial one from the docshell. This means
1895 // that the docshell started the channel load before creating the
1896 // initial about:blank document. This is an optimization, though,
1897 // and it created an initial Client as a placeholder for the document.
1898 // In this case we want to inherit this placeholder Client here.
1899 if (!mClientSource) {
1900 mClientSource = std::move(initialClientSource);
1901 if (mClientSource) {
1902 newClientSource = true;
1906 nsCOMPtr<nsIPrincipal> foreignPartitionedPrincipal;
1908 nsresult rv = StoragePrincipalHelper::GetPrincipal(
1909 this,
1910 StaticPrefs::privacy_partition_serviceWorkers()
1911 ? StoragePrincipalHelper::eForeignPartitionedPrincipal
1912 : StoragePrincipalHelper::eRegularPrincipal,
1913 getter_AddRefs(foreignPartitionedPrincipal));
1914 NS_ENSURE_SUCCESS(rv, rv);
1916 // Verify the final ClientSource principal matches the final document
1917 // principal. The ClientChannelHelper handles things like network
1918 // redirects, but there are other ways the document principal can change.
1919 // For example, if something sets the nsIChannel.owner property, then
1920 // the final channel principal can be anything. Unfortunately there is
1921 // no good way to detect this until after the channel completes loading.
1923 // For now we handle this just by reseting the ClientSource. This will
1924 // result in a new ClientSource with the correct principal being created.
1925 // To APIs like ServiceWorker and Clients API it will look like there was
1926 // an initial content page created that was then immediately replaced.
1927 // This is pretty close to what we are actually doing.
1928 if (mClientSource) {
1929 auto principalOrErr = mClientSource->Info().GetPrincipal();
1930 nsCOMPtr<nsIPrincipal> clientPrincipal =
1931 principalOrErr.isOk() ? principalOrErr.unwrap() : nullptr;
1932 if (!clientPrincipal ||
1933 !clientPrincipal->Equals(foreignPartitionedPrincipal)) {
1934 mClientSource.reset();
1938 // If we don't have a reserved client or an initial client, then create
1939 // one now. This can happen in certain cases where we avoid preallocating
1940 // the client in the docshell. This mainly occurs in situations where
1941 // the principal is not clearly inherited from the parent; e.g. sandboxed
1942 // iframes, window.open(), etc.
1944 // We also do this late ClientSource creation if the final document ended
1945 // up with a different principal.
1947 // TODO: We may not be marking initial about:blank documents created
1948 // this way as controlled by a service worker properly. The
1949 // controller should be coming from the same place as the inheritted
1950 // principal. We do this in docshell, but as mentioned we aren't
1951 // smart enough to handle all cases yet. For example, a
1952 // window.open() with new URL should inherit the controller from
1953 // the opener, but we probably don't handle that yet.
1954 if (!mClientSource) {
1955 mClientSource = ClientManager::CreateSource(
1956 ClientType::Window, EventTargetFor(TaskCategory::Other),
1957 foreignPartitionedPrincipal);
1958 MOZ_DIAGNOSTIC_ASSERT(mClientSource);
1959 newClientSource = true;
1961 // Note, we don't apply the loadinfo controller below if we create
1962 // the ClientSource here.
1965 // The load may have started controlling the Client as well. If
1966 // so, mark it as controlled immediately here. The actor may
1967 // or may not have been notified by the parent side about being
1968 // controlled yet.
1970 // Note: We should be careful not to control a client that was created late.
1971 // These clients were not seen by the ServiceWorkerManager when it
1972 // marked the LoadInfo controlled and it won't know about them. Its
1973 // also possible we are creating the client late due to the final
1974 // principal changing and these clients should definitely not be
1975 // controlled by a service worker with a different principal.
1976 else if (loadInfo) {
1977 const Maybe<ServiceWorkerDescriptor> controller = loadInfo->GetController();
1978 if (controller.isSome()) {
1979 mClientSource->SetController(controller.ref());
1982 // We also have to handle the case where te initial about:blank is
1983 // controlled due to inheritting the service worker from its parent,
1984 // but the actual nsIChannel load is not covered by any service worker.
1985 // In this case we want the final page to be uncontrolled. There is
1986 // an open spec issue about how exactly this should be handled, but for
1987 // now we just force creation of a new ClientSource to clear the
1988 // controller.
1990 // https://github.com/w3c/ServiceWorker/issues/1232
1992 else if (mClientSource->GetController().isSome()) {
1993 mClientSource.reset();
1994 mClientSource = ClientManager::CreateSource(
1995 ClientType::Window, EventTargetFor(TaskCategory::Other),
1996 foreignPartitionedPrincipal);
1997 MOZ_DIAGNOSTIC_ASSERT(mClientSource);
1998 newClientSource = true;
2002 if (mClientSource) {
2003 // Generally the CSP is stored within the Client and cached on the document.
2004 // At the time of CSP parsing however, the Client has not been created yet,
2005 // hence we store the CSP on the document and propagate/sync the CSP with
2006 // Client here when we create the Client.
2007 mClientSource->SetCsp(mDoc->GetCsp());
2009 DocGroup* docGroup = GetDocGroup();
2010 MOZ_DIAGNOSTIC_ASSERT(docGroup);
2011 mClientSource->SetAgentClusterId(docGroup->AgentClusterId());
2013 if (mWindowGlobalChild) {
2014 mWindowGlobalChild->SendSetClientInfo(mClientSource->Info().ToIPC());
2018 // Its possible that we got a client just after being frozen in
2019 // the bfcache. In that case freeze the client immediately.
2020 if (newClientSource && IsFrozen()) {
2021 mClientSource->Freeze();
2024 return NS_OK;
2027 nsresult nsGlobalWindowInner::ExecutionReady() {
2028 nsresult rv = EnsureClientSource();
2029 NS_ENSURE_SUCCESS(rv, rv);
2031 rv = mClientSource->WindowExecutionReady(this);
2032 NS_ENSURE_SUCCESS(rv, rv);
2034 return NS_OK;
2037 void nsGlobalWindowInner::UpdateParentTarget() {
2038 // NOTE: This method is identical to
2039 // nsGlobalWindowOuter::UpdateParentTarget(). IF YOU UPDATE THIS METHOD,
2040 // UPDATE THE OTHER ONE TOO!
2042 // Try to get our frame element's tab child global (its in-process message
2043 // manager). If that fails, fall back to the chrome event handler's tab
2044 // child global, and if it doesn't have one, just use the chrome event
2045 // handler itself.
2047 nsPIDOMWindowOuter* outer = GetOuterWindow();
2048 if (!outer) {
2049 return;
2051 nsCOMPtr<Element> frameElement = outer->GetFrameElementInternal();
2052 nsCOMPtr<EventTarget> eventTarget =
2053 nsContentUtils::TryGetBrowserChildGlobal(frameElement);
2055 if (!eventTarget) {
2056 nsGlobalWindowOuter* topWin = GetInProcessScriptableTopInternal();
2057 if (topWin) {
2058 frameElement = topWin->GetFrameElementInternal();
2059 eventTarget = nsContentUtils::TryGetBrowserChildGlobal(frameElement);
2063 if (!eventTarget) {
2064 eventTarget = nsContentUtils::TryGetBrowserChildGlobal(mChromeEventHandler);
2067 if (!eventTarget) {
2068 eventTarget = mChromeEventHandler;
2071 mParentTarget = eventTarget;
2074 EventTarget* nsGlobalWindowInner::GetTargetForDOMEvent() {
2075 return GetOuterWindowInternal();
2078 void nsGlobalWindowInner::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
2079 EventMessage msg = aVisitor.mEvent->mMessage;
2081 aVisitor.mCanHandle = true;
2082 aVisitor.mForceContentDispatch = true; // FIXME! Bug 329119
2083 if (msg == eResize && aVisitor.mEvent->IsTrusted()) {
2084 // Checking whether the event target is an inner window or not, so we can
2085 // keep the old behavior also in case a child window is handling resize.
2086 if (aVisitor.mEvent->mOriginalTarget &&
2087 aVisitor.mEvent->mOriginalTarget->IsInnerWindow()) {
2088 mIsHandlingResizeEvent = true;
2090 } else if (msg == eMouseDown && aVisitor.mEvent->IsTrusted()) {
2091 sMouseDown = true;
2092 } else if ((msg == eMouseUp || msg == eDragEnd) &&
2093 aVisitor.mEvent->IsTrusted()) {
2094 sMouseDown = false;
2095 if (sDragServiceDisabled) {
2096 nsCOMPtr<nsIDragService> ds =
2097 do_GetService("@mozilla.org/widget/dragservice;1");
2098 if (ds) {
2099 sDragServiceDisabled = false;
2100 ds->Unsuppress();
2105 aVisitor.SetParentTarget(GetParentTarget(), true);
2108 void nsGlobalWindowInner::FireFrameLoadEvent() {
2109 // If we're not in a content frame, or are at a BrowsingContext tree boundary,
2110 // such as the content-chrome boundary, don't fire the "load" event.
2111 if (GetBrowsingContext()->IsTopContent() ||
2112 GetBrowsingContext()->IsChrome()) {
2113 return;
2116 // If embedder is same-process, fire the event on our embedder element.
2118 // XXX: Bug 1440212 is looking into potentially changing this behaviour to act
2119 // more like the remote case when in-process.
2120 RefPtr<Element> element = GetBrowsingContext()->GetEmbedderElement();
2121 if (element) {
2122 nsEventStatus status = nsEventStatus_eIgnore;
2123 WidgetEvent event(/* aIsTrusted = */ true, eLoad);
2124 event.mFlags.mBubbles = false;
2125 event.mFlags.mCancelable = false;
2127 if (mozilla::dom::DocGroup::TryToLoadIframesInBackground()) {
2128 nsDocShell* ds = nsDocShell::Cast(GetDocShell());
2130 if (ds && !ds->HasFakeOnLoadDispatched()) {
2131 EventDispatcher::Dispatch(element, nullptr, &event, nullptr, &status);
2133 } else {
2134 // Most of the time we could get a pres context to pass in here,
2135 // but not always (i.e. if this window is not shown there won't
2136 // be a pres context available). Since we're not firing a GUI
2137 // event we don't need a pres context anyway so we just pass
2138 // null as the pres context all the time here.
2139 EventDispatcher::Dispatch(element, nullptr, &event, nullptr, &status);
2141 return;
2144 // We don't have an in-process embedder. Try to get our `BrowserChild` actor
2145 // to send a message to that embedder. We want to double-check that our outer
2146 // window is actually the one at the root of this browserChild though, just in
2147 // case.
2148 RefPtr<BrowserChild> browserChild =
2149 BrowserChild::GetFrom(static_cast<nsPIDOMWindowInner*>(this));
2150 if (browserChild) {
2151 // Double-check that our outer window is actually at the root of this
2152 // `BrowserChild`, in case we're in an odd maybe-unhosted situation like a
2153 // print preview dialog.
2154 nsCOMPtr<nsPIDOMWindowOuter> rootOuter =
2155 do_GetInterface(browserChild->WebNavigation());
2156 if (!rootOuter || rootOuter != GetOuterWindow()) {
2157 return;
2160 mozilla::Unused << browserChild->SendMaybeFireEmbedderLoadEvents(
2161 EmbedderElementEventType::LoadEvent);
2165 nsresult nsGlobalWindowInner::PostHandleEvent(EventChainPostVisitor& aVisitor) {
2166 // Return early if there is nothing to do.
2167 switch (aVisitor.mEvent->mMessage) {
2168 case eResize:
2169 case eUnload:
2170 case eLoad:
2171 break;
2172 default:
2173 return NS_OK;
2176 /* mChromeEventHandler and mContext go dangling in the middle of this
2177 function under some circumstances (events that destroy the window)
2178 without this addref. */
2179 RefPtr<EventTarget> kungFuDeathGrip1(mChromeEventHandler);
2180 mozilla::Unused
2181 << kungFuDeathGrip1; // These aren't referred to through the function
2182 nsCOMPtr<nsIScriptContext> kungFuDeathGrip2(GetContextInternal());
2183 mozilla::Unused
2184 << kungFuDeathGrip2; // These aren't referred to through the function
2186 if (aVisitor.mEvent->mMessage == eResize) {
2187 mIsHandlingResizeEvent = false;
2188 } else if (aVisitor.mEvent->mMessage == eUnload &&
2189 aVisitor.mEvent->IsTrusted()) {
2190 // If any VR display presentation is active at unload, the next page
2191 // will receive a vrdisplayactive event to indicate that it should
2192 // immediately begin vr presentation. This should occur when navigating
2193 // forwards, navigating backwards, and on page reload.
2194 for (const auto& display : mVRDisplays) {
2195 if (display->IsPresenting()) {
2196 display->StartVRNavigation();
2197 // Save this VR display ID to trigger vrdisplayactivate event
2198 // after the next load event.
2199 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
2200 if (outer) {
2201 outer->SetAutoActivateVRDisplayID(display->DisplayId());
2204 // XXX The WebVR 1.1 spec does not define which of multiple VR
2205 // presenting VR displays will be chosen during navigation.
2206 // As the underlying platform VR API's currently only allow a single
2207 // VR display, it is safe to choose the first VR display for now.
2208 break;
2211 mIsDocumentLoaded = false;
2212 // Tell the parent process that the document is not loaded.
2213 if (mWindowGlobalChild) {
2214 mWindowGlobalChild->SendUpdateDocumentHasLoaded(mIsDocumentLoaded);
2216 } else if (aVisitor.mEvent->mMessage == eLoad &&
2217 aVisitor.mEvent->IsTrusted()) {
2218 // This is page load event since load events don't propagate to |window|.
2219 // @see Document::GetEventTargetParent.
2220 mIsDocumentLoaded = true;
2221 // Tell the parent process that the document is loaded.
2222 if (mWindowGlobalChild) {
2223 mWindowGlobalChild->SendUpdateDocumentHasLoaded(mIsDocumentLoaded);
2226 mTimeoutManager->OnDocumentLoaded();
2228 MOZ_ASSERT(aVisitor.mEvent->IsTrusted());
2229 FireFrameLoadEvent();
2231 if (mVREventObserver) {
2232 mVREventObserver->NotifyAfterLoad();
2235 uint32_t autoActivateVRDisplayID = 0;
2236 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
2237 if (outer) {
2238 autoActivateVRDisplayID = outer->GetAutoActivateVRDisplayID();
2240 if (autoActivateVRDisplayID) {
2241 DispatchVRDisplayActivate(autoActivateVRDisplayID,
2242 VRDisplayEventReason::Navigation);
2246 return NS_OK;
2249 nsresult nsGlobalWindowInner::DefineArgumentsProperty(nsIArray* aArguments) {
2250 nsIScriptContext* ctx = GetOuterWindowInternal()->mContext;
2251 NS_ENSURE_TRUE(aArguments && ctx, NS_ERROR_NOT_INITIALIZED);
2253 JS::Rooted<JSObject*> obj(RootingCx(), GetWrapperPreserveColor());
2254 return ctx->SetProperty(obj, "arguments", aArguments);
2257 //*****************************************************************************
2258 // nsGlobalWindowInner::nsIScriptObjectPrincipal
2259 //*****************************************************************************
2261 nsIPrincipal* nsGlobalWindowInner::GetPrincipal() {
2262 if (mDoc) {
2263 // If we have a document, get the principal from the document
2264 return mDoc->NodePrincipal();
2267 if (mDocumentPrincipal) {
2268 return mDocumentPrincipal;
2271 // If we don't have a principal and we don't have a document we
2272 // ask the parent window for the principal. This can happen when
2273 // loading a frameset that has a <frame src="javascript:xxx">, in
2274 // that case the global window is used in JS before we've loaded
2275 // a document into the window.
2277 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
2278 do_QueryInterface(GetInProcessParentInternal());
2280 if (objPrincipal) {
2281 return objPrincipal->GetPrincipal();
2284 return nullptr;
2287 nsIPrincipal* nsGlobalWindowInner::GetEffectiveCookiePrincipal() {
2288 if (mDoc) {
2289 // If we have a document, get the principal from the document
2290 return mDoc->EffectiveCookiePrincipal();
2293 if (mDocumentCookiePrincipal) {
2294 return mDocumentCookiePrincipal;
2297 // If we don't have a cookie principal and we don't have a document we ask
2298 // the parent window for the cookie principal.
2300 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
2301 do_QueryInterface(GetInProcessParentInternal());
2303 if (objPrincipal) {
2304 return objPrincipal->GetEffectiveCookiePrincipal();
2307 return nullptr;
2310 nsIPrincipal* nsGlobalWindowInner::GetEffectiveStoragePrincipal() {
2311 if (mDoc) {
2312 // If we have a document, get the principal from the document
2313 return mDoc->EffectiveStoragePrincipal();
2316 if (mDocumentStoragePrincipal) {
2317 return mDocumentStoragePrincipal;
2320 // If we don't have a cookie principal and we don't have a document we ask
2321 // the parent window for the cookie principal.
2323 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
2324 do_QueryInterface(GetInProcessParentInternal());
2326 if (objPrincipal) {
2327 return objPrincipal->GetEffectiveStoragePrincipal();
2330 return nullptr;
2333 nsIPrincipal* nsGlobalWindowInner::PartitionedPrincipal() {
2334 if (mDoc) {
2335 // If we have a document, get the principal from the document
2336 return mDoc->PartitionedPrincipal();
2339 if (mDocumentPartitionedPrincipal) {
2340 return mDocumentPartitionedPrincipal;
2343 // If we don't have a partitioned principal and we don't have a document we
2344 // ask the parent window for the partitioned principal.
2346 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
2347 do_QueryInterface(GetInProcessParentInternal());
2349 if (objPrincipal) {
2350 return objPrincipal->PartitionedPrincipal();
2353 return nullptr;
2356 //*****************************************************************************
2357 // nsGlobalWindowInner::nsIDOMWindow
2358 //*****************************************************************************
2360 bool nsPIDOMWindowInner::AddAudioContext(AudioContext* aAudioContext) {
2361 mAudioContexts.AppendElement(aAudioContext);
2363 // Return true if the context should be muted and false if not.
2364 nsIDocShell* docShell = GetDocShell();
2365 return docShell && !docShell->GetAllowMedia() && !aAudioContext->IsOffline();
2368 void nsPIDOMWindowInner::RemoveAudioContext(AudioContext* aAudioContext) {
2369 mAudioContexts.RemoveElement(aAudioContext);
2372 void nsPIDOMWindowInner::MuteAudioContexts() {
2373 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
2374 if (!mAudioContexts[i]->IsOffline()) {
2375 mAudioContexts[i]->Mute();
2380 void nsPIDOMWindowInner::UnmuteAudioContexts() {
2381 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
2382 if (!mAudioContexts[i]->IsOffline()) {
2383 mAudioContexts[i]->Unmute();
2388 WindowProxyHolder nsGlobalWindowInner::Window() {
2389 return WindowProxyHolder(GetBrowsingContext());
2392 Navigator* nsPIDOMWindowInner::Navigator() {
2393 if (!mNavigator) {
2394 mNavigator = new mozilla::dom::Navigator(this);
2397 return mNavigator;
2400 MediaDevices* nsPIDOMWindowInner::GetExtantMediaDevices() const {
2401 return mNavigator ? mNavigator->GetExtantMediaDevices() : nullptr;
2404 VisualViewport* nsGlobalWindowInner::VisualViewport() {
2405 if (!mVisualViewport) {
2406 mVisualViewport = new mozilla::dom::VisualViewport(this);
2409 return mVisualViewport;
2412 nsScreen* nsGlobalWindowInner::GetScreen(ErrorResult& aError) {
2413 if (!mScreen) {
2414 mScreen = nsScreen::Create(this);
2415 if (!mScreen) {
2416 aError.Throw(NS_ERROR_UNEXPECTED);
2417 return nullptr;
2421 return mScreen;
2424 nsHistory* nsGlobalWindowInner::GetHistory(ErrorResult& aError) {
2425 if (!mHistory) {
2426 mHistory = new nsHistory(this);
2429 return mHistory;
2432 CustomElementRegistry* nsGlobalWindowInner::CustomElements() {
2433 if (!mCustomElements) {
2434 mCustomElements = new CustomElementRegistry(this);
2437 return mCustomElements;
2440 CustomElementRegistry* nsGlobalWindowInner::GetExistingCustomElements() {
2441 return mCustomElements;
2444 Performance* nsPIDOMWindowInner::GetPerformance() {
2445 CreatePerformanceObjectIfNeeded();
2446 return mPerformance;
2449 void nsPIDOMWindowInner::QueuePerformanceNavigationTiming() {
2450 CreatePerformanceObjectIfNeeded();
2451 if (mPerformance) {
2452 mPerformance->QueueNavigationTimingEntry();
2456 void nsPIDOMWindowInner::CreatePerformanceObjectIfNeeded() {
2457 if (mPerformance || !mDoc) {
2458 return;
2460 RefPtr<nsDOMNavigationTiming> timing = mDoc->GetNavigationTiming();
2461 nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(mDoc->GetChannel()));
2462 bool timingEnabled = false;
2463 if (!timedChannel ||
2464 !NS_SUCCEEDED(timedChannel->GetTimingEnabled(&timingEnabled)) ||
2465 !timingEnabled) {
2466 timedChannel = nullptr;
2468 if (timing) {
2469 mPerformance = Performance::CreateForMainThread(this, mDoc->NodePrincipal(),
2470 timing, timedChannel);
2474 bool nsPIDOMWindowInner::IsSecureContext() const {
2475 return nsGlobalWindowInner::Cast(this)->IsSecureContext();
2478 void nsPIDOMWindowInner::Suspend(bool aIncludeSubWindows) {
2479 nsGlobalWindowInner::Cast(this)->Suspend(aIncludeSubWindows);
2482 void nsPIDOMWindowInner::Resume(bool aIncludeSubWindows) {
2483 nsGlobalWindowInner::Cast(this)->Resume(aIncludeSubWindows);
2486 void nsPIDOMWindowInner::SyncStateFromParentWindow() {
2487 nsGlobalWindowInner::Cast(this)->SyncStateFromParentWindow();
2490 Maybe<ClientInfo> nsPIDOMWindowInner::GetClientInfo() const {
2491 return nsGlobalWindowInner::Cast(this)->GetClientInfo();
2494 Maybe<ClientState> nsPIDOMWindowInner::GetClientState() const {
2495 return nsGlobalWindowInner::Cast(this)->GetClientState();
2498 Maybe<ServiceWorkerDescriptor> nsPIDOMWindowInner::GetController() const {
2499 return nsGlobalWindowInner::Cast(this)->GetController();
2502 void nsPIDOMWindowInner::SetCsp(nsIContentSecurityPolicy* aCsp) {
2503 return nsGlobalWindowInner::Cast(this)->SetCsp(aCsp);
2506 void nsPIDOMWindowInner::SetPreloadCsp(nsIContentSecurityPolicy* aPreloadCsp) {
2507 return nsGlobalWindowInner::Cast(this)->SetPreloadCsp(aPreloadCsp);
2510 nsIContentSecurityPolicy* nsPIDOMWindowInner::GetCsp() {
2511 return nsGlobalWindowInner::Cast(this)->GetCsp();
2514 void nsPIDOMWindowInner::NoteCalledRegisterForServiceWorkerScope(
2515 const nsACString& aScope) {
2516 nsGlobalWindowInner::Cast(this)->NoteCalledRegisterForServiceWorkerScope(
2517 aScope);
2520 void nsPIDOMWindowInner::NoteDOMContentLoaded() {
2521 nsGlobalWindowInner::Cast(this)->NoteDOMContentLoaded();
2524 bool nsGlobalWindowInner::ShouldReportForServiceWorkerScope(
2525 const nsAString& aScope) {
2526 bool result = false;
2528 nsPIDOMWindowOuter* topOuter = GetInProcessScriptableTop();
2529 NS_ENSURE_TRUE(topOuter, false);
2531 nsGlobalWindowInner* topInner =
2532 nsGlobalWindowInner::Cast(topOuter->GetCurrentInnerWindow());
2533 NS_ENSURE_TRUE(topInner, false);
2535 topInner->ShouldReportForServiceWorkerScopeInternal(
2536 NS_ConvertUTF16toUTF8(aScope), &result);
2537 return result;
2540 InstallTriggerImpl* nsGlobalWindowInner::GetInstallTrigger() {
2541 if (!mInstallTrigger &&
2542 !StaticPrefs::extensions_InstallTriggerImpl_enabled()) {
2543 // Return nullptr when InstallTriggerImpl is disabled by pref,
2544 // which does not yet break the "typeof InstallTrigger !== 'undefined"
2545 // "UA detection" use case, but prevents access to the InstallTriggerImpl
2546 // methods and properties.
2548 // NOTE: a separate pref ("extensions.InstallTrigger.enabled"), associated
2549 // to this property using the [Pref] extended attribute in Window.webidl,
2550 // does instead hide the entire InstallTrigger property.
2552 // See Bug 1754441 for more details about this deprecation.
2553 return nullptr;
2555 if (!mInstallTrigger) {
2556 ErrorResult rv;
2557 mInstallTrigger = ConstructJSImplementation<InstallTriggerImpl>(
2558 "@mozilla.org/addons/installtrigger;1", this, rv);
2559 if (rv.Failed()) {
2560 rv.SuppressException();
2561 return nullptr;
2565 return mInstallTrigger;
2568 nsIDOMWindowUtils* nsGlobalWindowInner::GetWindowUtils(ErrorResult& aRv) {
2569 FORWARD_TO_OUTER_OR_THROW(WindowUtils, (), aRv, nullptr);
2572 CallState nsGlobalWindowInner::ShouldReportForServiceWorkerScopeInternal(
2573 const nsACString& aScope, bool* aResultOut) {
2574 MOZ_DIAGNOSTIC_ASSERT(aResultOut);
2576 // First check to see if this window is controlled. If so, then we have
2577 // found a match and are done.
2578 const Maybe<ServiceWorkerDescriptor> swd = GetController();
2579 if (swd.isSome() && swd.ref().Scope() == aScope) {
2580 *aResultOut = true;
2581 return CallState::Stop;
2584 // Next, check to see if this window has called
2585 // navigator.serviceWorker.register() for this scope. If so, then treat this
2586 // as a match so console reports appear in the devtools console.
2587 if (mClientSource &&
2588 mClientSource->CalledRegisterForServiceWorkerScope(aScope)) {
2589 *aResultOut = true;
2590 return CallState::Stop;
2593 // Finally check the current docshell nsILoadGroup to see if there are any
2594 // outstanding navigation requests. If so, match the scope against the
2595 // channel's URL. We want to show console reports during the FetchEvent
2596 // intercepting the navigation itself.
2597 nsCOMPtr<nsIDocumentLoader> loader(do_QueryInterface(GetDocShell()));
2598 if (loader) {
2599 nsCOMPtr<nsILoadGroup> loadgroup;
2600 Unused << loader->GetLoadGroup(getter_AddRefs(loadgroup));
2601 if (loadgroup) {
2602 nsCOMPtr<nsISimpleEnumerator> iter;
2603 Unused << loadgroup->GetRequests(getter_AddRefs(iter));
2604 if (iter) {
2605 nsCOMPtr<nsISupports> tmp;
2606 bool hasMore = true;
2607 // Check each network request in the load group.
2608 while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) {
2609 iter->GetNext(getter_AddRefs(tmp));
2610 nsCOMPtr<nsIChannel> loadingChannel(do_QueryInterface(tmp));
2611 // Ignore subresource requests. Logging for a subresource
2612 // FetchEvent should be handled above since the client is
2613 // already controlled.
2614 if (!loadingChannel ||
2615 !nsContentUtils::IsNonSubresourceRequest(loadingChannel)) {
2616 continue;
2618 nsCOMPtr<nsIURI> loadingURL;
2619 Unused << loadingChannel->GetURI(getter_AddRefs(loadingURL));
2620 if (!loadingURL) {
2621 continue;
2623 nsAutoCString loadingSpec;
2624 Unused << loadingURL->GetSpec(loadingSpec);
2625 // Perform a simple substring comparison to match the scope
2626 // against the channel URL.
2627 if (StringBeginsWith(loadingSpec, aScope)) {
2628 *aResultOut = true;
2629 return CallState::Stop;
2636 // The current window doesn't care about this service worker, but maybe
2637 // one of our child frames does.
2638 return CallOnInProcessChildren(
2639 &nsGlobalWindowInner::ShouldReportForServiceWorkerScopeInternal, aScope,
2640 aResultOut);
2643 void nsGlobalWindowInner::NoteCalledRegisterForServiceWorkerScope(
2644 const nsACString& aScope) {
2645 if (!mClientSource) {
2646 return;
2649 mClientSource->NoteCalledRegisterForServiceWorkerScope(aScope);
2652 void nsGlobalWindowInner::NoteDOMContentLoaded() {
2653 if (!mClientSource) {
2654 return;
2657 mClientSource->NoteDOMContentLoaded();
2660 void nsGlobalWindowInner::UpdateTopInnerWindow() {
2661 if (IsTopInnerWindow() || !mTopInnerWindow) {
2662 return;
2665 mTopInnerWindow->UpdateWebSocketCount(-(int32_t)mNumOfOpenWebSockets);
2668 bool nsGlobalWindowInner::IsInSyncOperation() {
2669 return GetExtantDoc() && GetExtantDoc()->IsInSyncOperation();
2672 bool nsGlobalWindowInner::IsSharedMemoryAllowedInternal(
2673 nsIPrincipal* aPrincipal) const {
2674 MOZ_ASSERT(NS_IsMainThread());
2676 if (StaticPrefs::
2677 dom_postMessage_sharedArrayBuffer_bypassCOOP_COEP_insecure_enabled()) {
2678 return true;
2681 if (ExtensionPolicyService::GetSingleton().IsExtensionProcess()) {
2682 if (auto* basePrincipal = BasePrincipal::Cast(aPrincipal)) {
2683 if (auto* policy = basePrincipal->AddonPolicy()) {
2684 return policy->IsPrivileged();
2689 return CrossOriginIsolated();
2692 bool nsGlobalWindowInner::CrossOriginIsolated() const {
2693 MOZ_ASSERT(NS_IsMainThread());
2695 RefPtr<BrowsingContext> bc = GetBrowsingContext();
2696 MOZ_DIAGNOSTIC_ASSERT(bc);
2697 return bc->CrossOriginIsolated();
2700 WindowContext* TopWindowContext(nsPIDOMWindowInner& aWindow) {
2701 WindowContext* wc = aWindow.GetWindowContext();
2702 if (!wc) {
2703 return nullptr;
2706 return wc->TopWindowContext();
2709 void nsPIDOMWindowInner::AddPeerConnection() {
2710 MOZ_ASSERT(NS_IsMainThread());
2711 ++mActivePeerConnections;
2712 if (mActivePeerConnections == 1 && mWindowGlobalChild) {
2713 mWindowGlobalChild->SendUpdateActivePeerConnectionStatus(
2714 /*aIsAdded*/ true);
2716 // We need to present having active peer connections immediately. If we need
2717 // to wait for the parent process to come back with this information we
2718 // might start throttling.
2719 if (WindowContext* top = TopWindowContext(*this)) {
2720 top->TransientSetHasActivePeerConnections();
2725 void nsPIDOMWindowInner::RemovePeerConnection() {
2726 MOZ_ASSERT(NS_IsMainThread());
2727 MOZ_ASSERT(mActivePeerConnections > 0);
2728 --mActivePeerConnections;
2729 if (mActivePeerConnections == 0 && mWindowGlobalChild) {
2730 mWindowGlobalChild->SendUpdateActivePeerConnectionStatus(
2731 /*aIsAdded*/ false);
2735 bool nsPIDOMWindowInner::HasActivePeerConnections() {
2736 MOZ_ASSERT(NS_IsMainThread());
2738 WindowContext* topWindowContext = TopWindowContext(*this);
2739 return topWindowContext && topWindowContext->GetHasActivePeerConnections();
2742 void nsPIDOMWindowInner::AddMediaKeysInstance(MediaKeys* aMediaKeys) {
2743 MOZ_ASSERT(NS_IsMainThread());
2744 mMediaKeysInstances.AppendElement(aMediaKeys);
2745 if (mWindowGlobalChild && mMediaKeysInstances.Length() == 1) {
2746 mWindowGlobalChild->BlockBFCacheFor(BFCacheStatus::CONTAINS_EME_CONTENT);
2750 void nsPIDOMWindowInner::RemoveMediaKeysInstance(MediaKeys* aMediaKeys) {
2751 MOZ_ASSERT(NS_IsMainThread());
2752 mMediaKeysInstances.RemoveElement(aMediaKeys);
2753 if (mWindowGlobalChild && mMediaKeysInstances.IsEmpty()) {
2754 mWindowGlobalChild->UnblockBFCacheFor(BFCacheStatus::CONTAINS_EME_CONTENT);
2758 bool nsPIDOMWindowInner::HasActiveMediaKeysInstance() {
2759 MOZ_ASSERT(NS_IsMainThread());
2760 return !mMediaKeysInstances.IsEmpty();
2763 bool nsPIDOMWindowInner::IsPlayingAudio() {
2764 for (uint32_t i = 0; i < mAudioContexts.Length(); i++) {
2765 if (mAudioContexts[i]->IsRunning()) {
2766 return true;
2769 RefPtr<AudioChannelService> acs = AudioChannelService::Get();
2770 if (!acs) {
2771 return false;
2773 auto outer = GetOuterWindow();
2774 if (!outer) {
2775 // We've been unlinked and are about to die. Not a good time to pretend to
2776 // be playing audio.
2777 return false;
2779 return acs->IsWindowActive(outer);
2782 bool nsPIDOMWindowInner::IsDocumentLoaded() const { return mIsDocumentLoaded; }
2784 mozilla::dom::TimeoutManager& nsPIDOMWindowInner::TimeoutManager() {
2785 return *mTimeoutManager;
2788 bool nsPIDOMWindowInner::IsRunningTimeout() {
2789 return TimeoutManager().IsRunningTimeout();
2792 void nsPIDOMWindowInner::TryToCacheTopInnerWindow() {
2793 if (mHasTriedToCacheTopInnerWindow) {
2794 return;
2797 nsGlobalWindowInner* window = nsGlobalWindowInner::Cast(this);
2799 MOZ_ASSERT(!window->IsDying());
2801 mHasTriedToCacheTopInnerWindow = true;
2803 MOZ_ASSERT(window);
2805 if (nsCOMPtr<nsPIDOMWindowOuter> topOutter =
2806 window->GetInProcessScriptableTop()) {
2807 mTopInnerWindow = topOutter->GetCurrentInnerWindow();
2811 void nsPIDOMWindowInner::UpdateActiveIndexedDBDatabaseCount(int32_t aDelta) {
2812 MOZ_ASSERT(NS_IsMainThread());
2814 if (aDelta == 0) {
2815 return;
2818 // We count databases but not transactions because only active databases
2819 // could block throttling.
2820 uint32_t& counter = mTopInnerWindow
2821 ? mTopInnerWindow->mNumOfIndexedDBDatabases
2822 : mNumOfIndexedDBDatabases;
2824 counter += aDelta;
2827 bool nsPIDOMWindowInner::HasActiveIndexedDBDatabases() {
2828 MOZ_ASSERT(NS_IsMainThread());
2830 return mTopInnerWindow ? mTopInnerWindow->mNumOfIndexedDBDatabases > 0
2831 : mNumOfIndexedDBDatabases > 0;
2834 void nsPIDOMWindowInner::UpdateWebSocketCount(int32_t aDelta) {
2835 MOZ_ASSERT(NS_IsMainThread());
2837 if (aDelta == 0) {
2838 return;
2841 if (mTopInnerWindow && !IsTopInnerWindow()) {
2842 mTopInnerWindow->UpdateWebSocketCount(aDelta);
2845 MOZ_DIAGNOSTIC_ASSERT(
2846 aDelta > 0 || ((aDelta + mNumOfOpenWebSockets) < mNumOfOpenWebSockets));
2848 mNumOfOpenWebSockets += aDelta;
2851 bool nsPIDOMWindowInner::HasOpenWebSockets() const {
2852 MOZ_ASSERT(NS_IsMainThread());
2854 return mNumOfOpenWebSockets ||
2855 (mTopInnerWindow && mTopInnerWindow->mNumOfOpenWebSockets);
2858 bool nsPIDOMWindowInner::IsCurrentInnerWindow() const {
2859 if (mozilla::SessionHistoryInParent() && mBrowsingContext &&
2860 mBrowsingContext->IsInBFCache()) {
2861 return false;
2864 if (!mBrowsingContext || mBrowsingContext->IsDiscarded()) {
2865 // If our BrowsingContext has been discarded, we consider ourselves
2866 // still-current if we were current at the time it was discarded.
2867 return mOuterWindow && WasCurrentInnerWindow();
2870 nsPIDOMWindowOuter* outer = mBrowsingContext->GetDOMWindow();
2871 return outer && outer->GetCurrentInnerWindow() == this;
2874 bool nsPIDOMWindowInner::IsFullyActive() const {
2875 WindowContext* wc = GetWindowContext();
2876 if (!wc || wc->IsDiscarded() || !wc->IsCurrent()) {
2877 return false;
2879 return GetBrowsingContext()->AncestorsAreCurrent();
2882 void nsPIDOMWindowInner::SetAudioCapture(bool aCapture) {
2883 RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
2884 if (service) {
2885 service->SetWindowAudioCaptured(GetOuterWindow(), mWindowID, aCapture);
2889 void nsGlobalWindowInner::SetActiveLoadingState(bool aIsLoading) {
2890 MOZ_LOG(
2891 gTimeoutLog, mozilla::LogLevel::Debug,
2892 ("SetActiveLoadingState innerwindow %p: %d", (void*)this, aIsLoading));
2893 if (GetBrowsingContext()) {
2894 // Setting loading on a discarded context has no effect.
2895 Unused << GetBrowsingContext()->SetLoading(aIsLoading);
2898 if (!nsGlobalWindowInner::Cast(this)->IsChromeWindow()) {
2899 mTimeoutManager->SetLoading(aIsLoading);
2902 HintIsLoading(aIsLoading);
2905 void nsGlobalWindowInner::HintIsLoading(bool aIsLoading) {
2906 // Hint to tell the JS GC to use modified triggers during pageload.
2907 if (mHintedWasLoading != aIsLoading) {
2908 using namespace js::gc;
2909 SetPerformanceHint(danger::GetJSContext(), aIsLoading
2910 ? PerformanceHint::InPageLoad
2911 : PerformanceHint::Normal);
2912 mHintedWasLoading = aIsLoading;
2916 // nsISpeechSynthesisGetter
2918 #ifdef MOZ_WEBSPEECH
2919 SpeechSynthesis* nsGlobalWindowInner::GetSpeechSynthesis(ErrorResult& aError) {
2920 if (!mSpeechSynthesis) {
2921 mSpeechSynthesis = new SpeechSynthesis(this);
2924 return mSpeechSynthesis;
2927 bool nsGlobalWindowInner::HasActiveSpeechSynthesis() {
2928 if (mSpeechSynthesis) {
2929 return !mSpeechSynthesis->HasEmptyQueue();
2932 return false;
2935 #endif
2937 mozilla::glean::Glean* nsGlobalWindowInner::Glean() {
2938 if (!mGlean) {
2939 mGlean = new mozilla::glean::Glean();
2942 return mGlean;
2945 mozilla::glean::GleanPings* nsGlobalWindowInner::GleanPings() {
2946 if (!mGleanPings) {
2947 mGleanPings = new mozilla::glean::GleanPings();
2950 return mGleanPings;
2953 Nullable<WindowProxyHolder> nsGlobalWindowInner::GetParent(
2954 ErrorResult& aError) {
2955 FORWARD_TO_OUTER_OR_THROW(GetParentOuter, (), aError, nullptr);
2959 * GetInProcessScriptableParent used to be called when a script read
2960 * window.parent. Under Fission, that is now handled by
2961 * BrowsingContext::GetParent, and the result is a WindowProxyHolder rather than
2962 * an actual global window. This method still exists for legacy callers which
2963 * relied on the old logic, and require in-process windows. However, it only
2964 * works correctly when no out-of-process frames exist between this window and
2965 * the top-level window, so it should not be used in new code.
2967 * In contrast to GetRealParent, GetInProcessScriptableParent respects <iframe
2968 * mozbrowser> boundaries, so if |this| is contained by an <iframe
2969 * mozbrowser>, we will return |this| as its own parent.
2971 nsPIDOMWindowOuter* nsGlobalWindowInner::GetInProcessScriptableParent() {
2972 FORWARD_TO_OUTER(GetInProcessScriptableParent, (), nullptr);
2976 * GetInProcessScriptableTop used to be called when a script read window.top.
2977 * Under Fission, that is now handled by BrowsingContext::Top, and the result is
2978 * a WindowProxyHolder rather than an actual global window. This method still
2979 * exists for legacy callers which relied on the old logic, and require
2980 * in-process windows. However, it only works correctly when no out-of-process
2981 * frames exist between this window and the top-level window, so it should not
2982 * be used in new code.
2984 * In contrast to GetRealTop, GetInProcessScriptableTop respects <iframe
2985 * mozbrowser> boundaries. If we encounter a window owned by an <iframe
2986 * mozbrowser> while walking up the window hierarchy, we'll stop and return that
2987 * window.
2989 nsPIDOMWindowOuter* nsGlobalWindowInner::GetInProcessScriptableTop() {
2990 FORWARD_TO_OUTER(GetInProcessScriptableTop, (), nullptr);
2993 void nsGlobalWindowInner::GetContent(JSContext* aCx,
2994 JS::MutableHandle<JSObject*> aRetval,
2995 CallerType aCallerType,
2996 ErrorResult& aError) {
2997 FORWARD_TO_OUTER_OR_THROW(GetContentOuter,
2998 (aCx, aRetval, aCallerType, aError), aError, );
3001 BarProp* nsGlobalWindowInner::GetMenubar(ErrorResult& aError) {
3002 if (!mMenubar) {
3003 mMenubar = new MenubarProp(this);
3006 return mMenubar;
3009 BarProp* nsGlobalWindowInner::GetToolbar(ErrorResult& aError) {
3010 if (!mToolbar) {
3011 mToolbar = new ToolbarProp(this);
3014 return mToolbar;
3017 BarProp* nsGlobalWindowInner::GetLocationbar(ErrorResult& aError) {
3018 if (!mLocationbar) {
3019 mLocationbar = new LocationbarProp(this);
3021 return mLocationbar;
3024 BarProp* nsGlobalWindowInner::GetPersonalbar(ErrorResult& aError) {
3025 if (!mPersonalbar) {
3026 mPersonalbar = new PersonalbarProp(this);
3028 return mPersonalbar;
3031 BarProp* nsGlobalWindowInner::GetStatusbar(ErrorResult& aError) {
3032 if (!mStatusbar) {
3033 mStatusbar = new StatusbarProp(this);
3035 return mStatusbar;
3038 BarProp* nsGlobalWindowInner::GetScrollbars(ErrorResult& aError) {
3039 if (!mScrollbars) {
3040 mScrollbars = new ScrollbarsProp(this);
3043 return mScrollbars;
3046 bool nsGlobalWindowInner::GetClosed(ErrorResult& aError) {
3047 // If we're called from JS (which is the only way we should be getting called
3048 // here) and we reach this point, that means our JS global is the current
3049 // target of the WindowProxy, which means that we are the "current inner"
3050 // of our outer. So if FORWARD_TO_OUTER fails to forward, that means the
3051 // outer is already torn down, which corresponds to the closed state.
3052 FORWARD_TO_OUTER(GetClosedOuter, (), true);
3055 Nullable<WindowProxyHolder> nsGlobalWindowInner::IndexedGetter(
3056 uint32_t aIndex) {
3057 FORWARD_TO_OUTER(IndexedGetterOuter, (aIndex), nullptr);
3060 namespace {
3062 struct InterfaceShimEntry {
3063 const char* geckoName;
3064 const char* domName;
3067 } // anonymous namespace
3069 // We add shims from Components.interfaces.nsIDOMFoo to window.Foo for each
3070 // interface that has interface constants that sites might be getting off
3071 // of Ci.
3072 const InterfaceShimEntry kInterfaceShimMap[] = {
3073 {"nsIXMLHttpRequest", "XMLHttpRequest"},
3074 {"nsIDOMDOMException", "DOMException"},
3075 {"nsIDOMNode", "Node"},
3076 {"nsIDOMCSSRule", "CSSRule"},
3077 {"nsIDOMEvent", "Event"},
3078 {"nsIDOMNSEvent", "Event"},
3079 {"nsIDOMKeyEvent", "KeyEvent"},
3080 {"nsIDOMMouseEvent", "MouseEvent"},
3081 {"nsIDOMMouseScrollEvent", "MouseScrollEvent"},
3082 {"nsIDOMMutationEvent", "MutationEvent"},
3083 {"nsIDOMUIEvent", "UIEvent"},
3084 {"nsIDOMHTMLMediaElement", "HTMLMediaElement"},
3085 {"nsIDOMRange", "Range"},
3086 // Think about whether Ci.nsINodeFilter can just go away for websites!
3087 {"nsIDOMNodeFilter", "NodeFilter"},
3088 {"nsIDOMXPathResult", "XPathResult"}};
3090 bool nsGlobalWindowInner::ResolveComponentsShim(
3091 JSContext* aCx, JS::Handle<JSObject*> aGlobal,
3092 JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> aDesc) {
3093 // Keep track of how often this happens.
3094 Telemetry::Accumulate(Telemetry::COMPONENTS_SHIM_ACCESSED_BY_CONTENT, true);
3096 // Warn once.
3097 nsCOMPtr<Document> doc = GetExtantDoc();
3098 if (doc) {
3099 doc->WarnOnceAbout(DeprecatedOperations::eComponents, /* asError = */ true);
3102 // Create a fake Components object.
3103 AssertSameCompartment(aCx, aGlobal);
3104 JS::Rooted<JSObject*> components(aCx, JS_NewPlainObject(aCx));
3105 if (NS_WARN_IF(!components)) {
3106 return false;
3109 // Create a fake interfaces object.
3110 JS::Rooted<JSObject*> interfaces(aCx, JS_NewPlainObject(aCx));
3111 if (NS_WARN_IF(!interfaces)) {
3112 return false;
3114 bool ok =
3115 JS_DefineProperty(aCx, components, "interfaces", interfaces,
3116 JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY);
3117 if (NS_WARN_IF(!ok)) {
3118 return false;
3121 // Define a bunch of shims from the Ci.nsIDOMFoo to window.Foo for DOM
3122 // interfaces with constants.
3123 for (uint32_t i = 0; i < ArrayLength(kInterfaceShimMap); ++i) {
3124 // Grab the names from the table.
3125 const char* geckoName = kInterfaceShimMap[i].geckoName;
3126 const char* domName = kInterfaceShimMap[i].domName;
3128 // Look up the appopriate interface object on the global.
3129 JS::Rooted<JS::Value> v(aCx, JS::UndefinedValue());
3130 ok = JS_GetProperty(aCx, aGlobal, domName, &v);
3131 if (NS_WARN_IF(!ok)) {
3132 return false;
3134 if (!v.isObject()) {
3135 NS_WARNING("Unable to find interface object on global");
3136 continue;
3139 // Define the shim on the interfaces object.
3140 ok = JS_DefineProperty(
3141 aCx, interfaces, geckoName, v,
3142 JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY);
3143 if (NS_WARN_IF(!ok)) {
3144 return false;
3148 aDesc.set(mozilla::Some(JS::PropertyDescriptor::Data(
3149 JS::ObjectValue(*components),
3150 {JS::PropertyAttribute::Configurable, JS::PropertyAttribute::Enumerable,
3151 JS::PropertyAttribute::Writable})));
3152 return true;
3155 #ifdef RELEASE_OR_BETA
3156 # define USE_CONTROLLERS_SHIM
3157 #endif
3159 #ifdef USE_CONTROLLERS_SHIM
3160 static const JSClass ControllersShimClass = {"Controllers", 0};
3161 static const JSClass XULControllersShimClass = {"XULControllers", 0};
3162 #endif
3164 bool nsGlobalWindowInner::DoResolve(
3165 JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aId,
3166 JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> aDesc) {
3167 // Note: Keep this in sync with MayResolve.
3169 // Note: The infallibleInit call in GlobalResolve depends on this check.
3170 if (!aId.isString()) {
3171 return true;
3174 bool found;
3175 if (!WebIDLGlobalNameHash::DefineIfEnabled(aCx, aObj, aId, aDesc, &found)) {
3176 return false;
3179 if (found) {
3180 return true;
3183 // We support a cut-down Components.interfaces in case websites are
3184 // using Components.interfaces.nsIFoo.CONSTANT_NAME for the ones
3185 // that have constants.
3186 if (StaticPrefs::dom_use_components_shim() &&
3187 aId == XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_COMPONENTS)) {
3188 return ResolveComponentsShim(aCx, aObj, aDesc);
3191 // We also support a "window.controllers" thing; apparently some
3192 // sites use it for browser-sniffing. See bug 1010577.
3193 #ifdef USE_CONTROLLERS_SHIM
3194 // Note: We use |aObj| rather than |this| to get the principal here, because
3195 // this is called during Window setup when the Document isn't necessarily
3196 // hooked up yet.
3197 if ((aId == XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_CONTROLLERS) ||
3198 aId == XPCJSRuntime::Get()->GetStringID(
3199 XPCJSContext::IDX_CONTROLLERS_CLASS)) &&
3200 !xpc::IsXrayWrapper(aObj) &&
3201 !nsContentUtils::ObjectPrincipal(aObj)->IsSystemPrincipal()) {
3202 if (GetExtantDoc()) {
3203 GetExtantDoc()->WarnOnceAbout(
3204 DeprecatedOperations::eWindow_Cc_ontrollers);
3206 const JSClass* clazz;
3207 if (aId ==
3208 XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_CONTROLLERS)) {
3209 clazz = &XULControllersShimClass;
3210 } else {
3211 clazz = &ControllersShimClass;
3213 MOZ_ASSERT(JS_IsGlobalObject(aObj));
3214 JS::Rooted<JSObject*> shim(aCx, JS_NewObject(aCx, clazz));
3215 if (NS_WARN_IF(!shim)) {
3216 return false;
3219 aDesc.set(mozilla::Some(JS::PropertyDescriptor::Data(
3220 JS::ObjectValue(*shim),
3221 {JS::PropertyAttribute::Configurable, JS::PropertyAttribute::Enumerable,
3222 JS::PropertyAttribute::Writable})));
3223 return true;
3225 #endif
3227 return true;
3230 /* static */
3231 bool nsGlobalWindowInner::MayResolve(jsid aId) {
3232 // Note: This function does not fail and may not have any side-effects.
3233 // Note: Keep this in sync with DoResolve.
3234 if (!aId.isString()) {
3235 return false;
3238 if (aId == XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_COMPONENTS)) {
3239 return true;
3242 if (aId == XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_CONTROLLERS) ||
3243 aId == XPCJSRuntime::Get()->GetStringID(
3244 XPCJSContext::IDX_CONTROLLERS_CLASS)) {
3245 // We only resolve .controllers/.Controllers in release builds and on
3246 // non-chrome windows, but let's not worry about any of that stuff.
3247 return true;
3250 return WebIDLGlobalNameHash::MayResolve(aId);
3253 void nsGlobalWindowInner::GetOwnPropertyNames(
3254 JSContext* aCx, JS::MutableHandleVector<jsid> aNames, bool aEnumerableOnly,
3255 ErrorResult& aRv) {
3256 if (aEnumerableOnly) {
3257 // The names we would return from here get defined on the window via one of
3258 // two codepaths. The ones coming from the WebIDLGlobalNameHash will end up
3259 // in the DefineConstructor function in BindingUtils, which always defines
3260 // things as non-enumerable. The ones coming from the script namespace
3261 // manager get defined by our resolve hook using FillPropertyDescriptor with
3262 // 0 for the property attributes, so non-enumerable as well.
3264 // So in the aEnumerableOnly case we have nothing to do.
3265 return;
3268 // "Components" is marked as enumerable but only resolved on demand :-/.
3269 // aNames.AppendElement(u"Components"_ns);
3271 JS::Rooted<JSObject*> wrapper(aCx, GetWrapper());
3273 // There are actually two ways we can get called here: For normal
3274 // enumeration or for Xray enumeration. In the latter case, we want to
3275 // return all possible WebIDL names, because we don't really support
3276 // deleting these names off our Xray; trying to resolve them will just make
3277 // them come back. In the former case, we want to avoid returning deleted
3278 // names. But the JS engine already knows about the non-deleted
3279 // already-resolved names, so we can just return the so-far-unresolved ones.
3281 // We can tell which case we're in by whether aCx is in our wrapper's
3282 // compartment. If not, we're in the Xray case.
3283 WebIDLGlobalNameHash::NameType nameType =
3284 js::IsObjectInContextCompartment(wrapper, aCx)
3285 ? WebIDLGlobalNameHash::UnresolvedNamesOnly
3286 : WebIDLGlobalNameHash::AllNames;
3287 if (!WebIDLGlobalNameHash::GetNames(aCx, wrapper, nameType, aNames)) {
3288 aRv.NoteJSContextException(aCx);
3292 /* static */
3293 bool nsGlobalWindowInner::IsPrivilegedChromeWindow(JSContext*, JSObject* aObj) {
3294 // For now, have to deal with XPConnect objects here.
3295 nsGlobalWindowInner* win = xpc::WindowOrNull(aObj);
3296 return win && win->IsChromeWindow() &&
3297 nsContentUtils::ObjectPrincipal(aObj) ==
3298 nsContentUtils::GetSystemPrincipal();
3301 /* static */
3302 bool nsGlobalWindowInner::IsRequestIdleCallbackEnabled(JSContext* aCx,
3303 JSObject*) {
3304 // The requestIdleCallback should always be enabled for system code.
3305 return StaticPrefs::dom_requestIdleCallback_enabled() ||
3306 nsContentUtils::IsSystemCaller(aCx);
3309 /* static */
3310 bool nsGlobalWindowInner::DeviceSensorsEnabled(JSContext*, JSObject*) {
3311 return Preferences::GetBool("device.sensors.enabled");
3314 /* static */
3315 bool nsGlobalWindowInner::ContentPropertyEnabled(JSContext* aCx, JSObject*) {
3316 return StaticPrefs::dom_window_content_untrusted_enabled() ||
3317 nsContentUtils::IsSystemCaller(aCx);
3320 /* static */
3321 bool nsGlobalWindowInner::CachesEnabled(JSContext* aCx, JSObject*) {
3322 if (!StaticPrefs::dom_caches_enabled()) {
3323 return false;
3325 if (!JS::GetIsSecureContext(js::GetContextRealm(aCx))) {
3326 return StaticPrefs::dom_caches_testing_enabled() ||
3327 StaticPrefs::dom_serviceWorkers_testing_enabled();
3329 return true;
3332 Crypto* nsGlobalWindowInner::GetCrypto(ErrorResult& aError) {
3333 if (!mCrypto) {
3334 mCrypto = new Crypto(this);
3336 return mCrypto;
3339 nsIControllers* nsGlobalWindowInner::GetControllers(ErrorResult& aError) {
3340 FORWARD_TO_OUTER_OR_THROW(GetControllersOuter, (aError), aError, nullptr);
3343 nsresult nsGlobalWindowInner::GetControllers(nsIControllers** aResult) {
3344 ErrorResult rv;
3345 nsCOMPtr<nsIControllers> controllers = GetControllers(rv);
3346 controllers.forget(aResult);
3348 return rv.StealNSResult();
3351 Nullable<WindowProxyHolder> nsGlobalWindowInner::GetOpenerWindow(
3352 ErrorResult& aError) {
3353 FORWARD_TO_OUTER_OR_THROW(GetOpenerWindowOuter, (), aError, nullptr);
3356 void nsGlobalWindowInner::GetOpener(JSContext* aCx,
3357 JS::MutableHandle<JS::Value> aRetval,
3358 ErrorResult& aError) {
3359 Nullable<WindowProxyHolder> opener = GetOpenerWindow(aError);
3360 if (aError.Failed() || opener.IsNull()) {
3361 aRetval.setNull();
3362 return;
3365 if (!ToJSValue(aCx, opener.Value(), aRetval)) {
3366 aError.NoteJSContextException(aCx);
3370 void nsGlobalWindowInner::SetOpener(JSContext* aCx,
3371 JS::Handle<JS::Value> aOpener,
3372 ErrorResult& aError) {
3373 if (aOpener.isNull()) {
3374 RefPtr<BrowsingContext> bc(GetBrowsingContext());
3375 if (!bc->IsDiscarded()) {
3376 bc->SetOpener(nullptr);
3378 return;
3381 // If something other than null is passed, just define aOpener on our inner
3382 // window's JS object, wrapped into the current compartment so that for Xrays
3383 // we define on the Xray expando object, but don't set it on the outer window,
3384 // so that it'll get reset on navigation. This is just like replaceable
3385 // properties, but we're not quite readonly.
3386 RedefineProperty(aCx, "opener", aOpener, aError);
3389 void nsGlobalWindowInner::GetEvent(OwningEventOrUndefined& aRetval) {
3390 if (mEvent) {
3391 aRetval.SetAsEvent() = mEvent;
3392 } else {
3393 aRetval.SetUndefined();
3397 void nsGlobalWindowInner::GetStatus(nsAString& aStatus, ErrorResult& aError) {
3398 FORWARD_TO_OUTER_OR_THROW(GetStatusOuter, (aStatus), aError, );
3401 void nsGlobalWindowInner::SetStatus(const nsAString& aStatus,
3402 ErrorResult& aError) {
3403 FORWARD_TO_OUTER_OR_THROW(SetStatusOuter, (aStatus), aError, );
3406 void nsGlobalWindowInner::GetName(nsAString& aName, ErrorResult& aError) {
3407 FORWARD_TO_OUTER_OR_THROW(GetNameOuter, (aName), aError, );
3410 void nsGlobalWindowInner::SetName(const nsAString& aName,
3411 mozilla::ErrorResult& aError) {
3412 FORWARD_TO_OUTER_OR_THROW(SetNameOuter, (aName, aError), aError, );
3415 double nsGlobalWindowInner::GetInnerWidth(CallerType aCallerType,
3416 ErrorResult& aError) {
3417 // We ignore aCallerType; we only have that argument because some other things
3418 // called by GetReplaceableWindowCoord need it. If this ever changes, fix
3419 // nsresult nsGlobalWindowInner::GetInnerWidth(double* aInnerWidth)
3420 // to actually take a useful CallerType and pass it in here.
3421 FORWARD_TO_OUTER_OR_THROW(GetInnerWidthOuter, (aError), aError, 0);
3424 void nsGlobalWindowInner::GetInnerWidth(JSContext* aCx,
3425 JS::MutableHandle<JS::Value> aValue,
3426 CallerType aCallerType,
3427 ErrorResult& aError) {
3428 GetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::GetInnerWidth, aValue,
3429 aCallerType, aError);
3432 nsresult nsGlobalWindowInner::GetInnerWidth(double* aInnerWidth) {
3433 ErrorResult rv;
3434 // Callee doesn't care about the caller type, but play it safe.
3435 *aInnerWidth = GetInnerWidth(CallerType::NonSystem, rv);
3437 return rv.StealNSResult();
3440 void nsGlobalWindowInner::SetInnerWidth(double aInnerWidth,
3441 CallerType aCallerType,
3442 ErrorResult& aError) {
3443 FORWARD_TO_OUTER_OR_THROW(SetInnerWidthOuter,
3444 (aInnerWidth, aCallerType, aError), aError, );
3447 void nsGlobalWindowInner::SetInnerWidth(JSContext* aCx,
3448 JS::Handle<JS::Value> aValue,
3449 CallerType aCallerType,
3450 ErrorResult& aError) {
3451 SetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::SetInnerWidth, aValue,
3452 "innerWidth", aCallerType, aError);
3455 double nsGlobalWindowInner::GetInnerHeight(CallerType aCallerType,
3456 ErrorResult& aError) {
3457 // We ignore aCallerType; we only have that argument because some other things
3458 // called by GetReplaceableWindowCoord need it. If this ever changes, fix
3459 // nsresult nsGlobalWindowInner::GetInnerHeight(double* aInnerWidth)
3460 // to actually take a useful CallerType and pass it in here.
3461 FORWARD_TO_OUTER_OR_THROW(GetInnerHeightOuter, (aError), aError, 0);
3464 void nsGlobalWindowInner::GetInnerHeight(JSContext* aCx,
3465 JS::MutableHandle<JS::Value> aValue,
3466 CallerType aCallerType,
3467 ErrorResult& aError) {
3468 GetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::GetInnerHeight, aValue,
3469 aCallerType, aError);
3472 nsresult nsGlobalWindowInner::GetInnerHeight(double* aInnerHeight) {
3473 ErrorResult rv;
3474 // Callee doesn't care about the caller type, but play it safe.
3475 *aInnerHeight = GetInnerHeight(CallerType::NonSystem, rv);
3477 return rv.StealNSResult();
3480 void nsGlobalWindowInner::SetInnerHeight(double aInnerHeight,
3481 CallerType aCallerType,
3482 ErrorResult& aError) {
3483 FORWARD_TO_OUTER_OR_THROW(SetInnerHeightOuter,
3484 (aInnerHeight, aCallerType, aError), aError, );
3487 void nsGlobalWindowInner::SetInnerHeight(JSContext* aCx,
3488 JS::Handle<JS::Value> aValue,
3489 CallerType aCallerType,
3490 ErrorResult& aError) {
3491 SetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::SetInnerHeight, aValue,
3492 "innerHeight", aCallerType, aError);
3495 int32_t nsGlobalWindowInner::GetOuterWidth(CallerType aCallerType,
3496 ErrorResult& aError) {
3497 FORWARD_TO_OUTER_OR_THROW(GetOuterWidthOuter, (aCallerType, aError), aError,
3501 void nsGlobalWindowInner::GetOuterWidth(JSContext* aCx,
3502 JS::MutableHandle<JS::Value> aValue,
3503 CallerType aCallerType,
3504 ErrorResult& aError) {
3505 GetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::GetOuterWidth, aValue,
3506 aCallerType, aError);
3509 int32_t nsGlobalWindowInner::GetOuterHeight(CallerType aCallerType,
3510 ErrorResult& aError) {
3511 FORWARD_TO_OUTER_OR_THROW(GetOuterHeightOuter, (aCallerType, aError), aError,
3515 void nsGlobalWindowInner::GetOuterHeight(JSContext* aCx,
3516 JS::MutableHandle<JS::Value> aValue,
3517 CallerType aCallerType,
3518 ErrorResult& aError) {
3519 GetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::GetOuterHeight, aValue,
3520 aCallerType, aError);
3523 void nsGlobalWindowInner::SetOuterWidth(int32_t aOuterWidth,
3524 CallerType aCallerType,
3525 ErrorResult& aError) {
3526 FORWARD_TO_OUTER_OR_THROW(SetOuterWidthOuter,
3527 (aOuterWidth, aCallerType, aError), aError, );
3530 void nsGlobalWindowInner::SetOuterWidth(JSContext* aCx,
3531 JS::Handle<JS::Value> aValue,
3532 CallerType aCallerType,
3533 ErrorResult& aError) {
3534 SetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::SetOuterWidth, aValue,
3535 "outerWidth", aCallerType, aError);
3538 void nsGlobalWindowInner::SetOuterHeight(int32_t aOuterHeight,
3539 CallerType aCallerType,
3540 ErrorResult& aError) {
3541 FORWARD_TO_OUTER_OR_THROW(SetOuterHeightOuter,
3542 (aOuterHeight, aCallerType, aError), aError, );
3545 void nsGlobalWindowInner::SetOuterHeight(JSContext* aCx,
3546 JS::Handle<JS::Value> aValue,
3547 CallerType aCallerType,
3548 ErrorResult& aError) {
3549 SetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::SetOuterHeight, aValue,
3550 "outerHeight", aCallerType, aError);
3553 double nsGlobalWindowInner::ScreenEdgeSlopX() const {
3554 FORWARD_TO_OUTER(ScreenEdgeSlopX, (), 0);
3557 double nsGlobalWindowInner::ScreenEdgeSlopY() const {
3558 FORWARD_TO_OUTER(ScreenEdgeSlopY, (), 0);
3561 int32_t nsGlobalWindowInner::GetScreenX(CallerType aCallerType,
3562 ErrorResult& aError) {
3563 FORWARD_TO_OUTER_OR_THROW(GetScreenXOuter, (aCallerType, aError), aError, 0);
3566 void nsGlobalWindowInner::GetScreenX(JSContext* aCx,
3567 JS::MutableHandle<JS::Value> aValue,
3568 CallerType aCallerType,
3569 ErrorResult& aError) {
3570 GetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::GetScreenX, aValue,
3571 aCallerType, aError);
3574 float nsGlobalWindowInner::GetMozInnerScreenX(CallerType aCallerType,
3575 ErrorResult& aError) {
3576 FORWARD_TO_OUTER_OR_THROW(GetMozInnerScreenXOuter, (aCallerType), aError, 0);
3579 float nsGlobalWindowInner::GetMozInnerScreenY(CallerType aCallerType,
3580 ErrorResult& aError) {
3581 FORWARD_TO_OUTER_OR_THROW(GetMozInnerScreenYOuter, (aCallerType), aError, 0);
3584 static nsPresContext* GetPresContextForRatio(Document* aDoc) {
3585 if (nsPresContext* presContext = aDoc->GetPresContext()) {
3586 return presContext;
3588 // We're in an undisplayed subdocument... There's not really an awesome way
3589 // to tell what the right DPI is from here, so we try to walk up our parent
3590 // document chain to the extent that the docs can observe each other.
3591 Document* doc = aDoc;
3592 while (doc->StyleOrLayoutObservablyDependsOnParentDocumentLayout()) {
3593 doc = doc->GetInProcessParentDocument();
3594 if (nsPresContext* presContext = doc->GetPresContext()) {
3595 return presContext;
3598 return nullptr;
3601 double nsGlobalWindowInner::GetDevicePixelRatio(CallerType aCallerType,
3602 ErrorResult& aError) {
3603 ENSURE_ACTIVE_DOCUMENT(aError, 0.0);
3605 RefPtr<nsPresContext> presContext = GetPresContextForRatio(mDoc);
3606 if (NS_WARN_IF(!presContext)) {
3607 // Still nothing, oh well.
3608 return 1.0;
3611 if (nsIGlobalObject::ShouldResistFingerprinting(aCallerType,
3612 RFPTarget::Unknown)) {
3613 // Spoofing the DevicePixelRatio causes blurriness in some situations
3614 // on HiDPI displays. pdf.js is a non-system caller; but it can't
3615 // expose the fingerprintable information, so we can safely disable
3616 // spoofing in this situation. It doesn't address the issue for
3617 // web-rendered content (including pdf.js instances on the web.)
3618 // In the future we hope to have a better solution to fix all HiDPI
3619 // blurriness...
3620 nsAutoCString origin;
3621 nsresult rv = this->GetPrincipal()->GetOrigin(origin);
3622 if (NS_FAILED(rv) || origin != "resource://pdf.js"_ns) {
3623 return 1.0;
3627 if (aCallerType == CallerType::NonSystem) {
3628 float overrideDPPX = presContext->GetOverrideDPPX();
3629 if (overrideDPPX > 0.0f) {
3630 return overrideDPPX;
3634 return double(AppUnitsPerCSSPixel()) /
3635 double(presContext->AppUnitsPerDevPixel());
3638 double nsGlobalWindowInner::GetDesktopToDeviceScale(ErrorResult& aError) {
3639 ENSURE_ACTIVE_DOCUMENT(aError, 0.0);
3640 nsPresContext* presContext = GetPresContextForRatio(mDoc);
3641 if (!presContext) {
3642 return 1.0;
3644 return presContext->DeviceContext()->GetDesktopToDeviceScale().scale;
3647 int32_t nsGlobalWindowInner::RequestAnimationFrame(
3648 FrameRequestCallback& aCallback, ErrorResult& aError) {
3649 if (!mDoc) {
3650 return 0;
3653 if (GetWrapperPreserveColor()) {
3654 js::NotifyAnimationActivity(GetWrapperPreserveColor());
3657 DebuggerNotificationDispatch(this,
3658 DebuggerNotificationType::RequestAnimationFrame);
3660 int32_t handle;
3661 aError = mDoc->ScheduleFrameRequestCallback(aCallback, &handle);
3662 return handle;
3665 void nsGlobalWindowInner::CancelAnimationFrame(int32_t aHandle,
3666 ErrorResult& aError) {
3667 if (!mDoc) {
3668 return;
3671 DebuggerNotificationDispatch(this,
3672 DebuggerNotificationType::CancelAnimationFrame);
3674 mDoc->CancelFrameRequestCallback(aHandle);
3677 already_AddRefed<MediaQueryList> nsGlobalWindowInner::MatchMedia(
3678 const nsACString& aMediaQueryList, CallerType aCallerType,
3679 ErrorResult& aError) {
3680 ENSURE_ACTIVE_DOCUMENT(aError, nullptr);
3681 return mDoc->MatchMedia(aMediaQueryList, aCallerType);
3684 void nsGlobalWindowInner::SetScreenX(int32_t aScreenX, CallerType aCallerType,
3685 ErrorResult& aError) {
3686 FORWARD_TO_OUTER_OR_THROW(SetScreenXOuter, (aScreenX, aCallerType, aError),
3687 aError, );
3690 void nsGlobalWindowInner::SetScreenX(JSContext* aCx,
3691 JS::Handle<JS::Value> aValue,
3692 CallerType aCallerType,
3693 ErrorResult& aError) {
3694 SetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::SetScreenX, aValue,
3695 "screenX", aCallerType, aError);
3698 int32_t nsGlobalWindowInner::GetScreenY(CallerType aCallerType,
3699 ErrorResult& aError) {
3700 FORWARD_TO_OUTER_OR_THROW(GetScreenYOuter, (aCallerType, aError), aError, 0);
3703 void nsGlobalWindowInner::GetScreenY(JSContext* aCx,
3704 JS::MutableHandle<JS::Value> aValue,
3705 CallerType aCallerType,
3706 ErrorResult& aError) {
3707 GetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::GetScreenY, aValue,
3708 aCallerType, aError);
3711 void nsGlobalWindowInner::SetScreenY(int32_t aScreenY, CallerType aCallerType,
3712 ErrorResult& aError) {
3713 FORWARD_TO_OUTER_OR_THROW(SetScreenYOuter, (aScreenY, aCallerType, aError),
3714 aError, );
3717 void nsGlobalWindowInner::SetScreenY(JSContext* aCx,
3718 JS::Handle<JS::Value> aValue,
3719 CallerType aCallerType,
3720 ErrorResult& aError) {
3721 SetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::SetScreenY, aValue,
3722 "screenY", aCallerType, aError);
3725 int32_t nsGlobalWindowInner::GetScrollMinX(ErrorResult& aError) {
3726 FORWARD_TO_OUTER_OR_THROW(GetScrollBoundaryOuter, (eSideLeft), aError, 0);
3729 int32_t nsGlobalWindowInner::GetScrollMinY(ErrorResult& aError) {
3730 FORWARD_TO_OUTER_OR_THROW(GetScrollBoundaryOuter, (eSideTop), aError, 0);
3733 int32_t nsGlobalWindowInner::GetScrollMaxX(ErrorResult& aError) {
3734 FORWARD_TO_OUTER_OR_THROW(GetScrollBoundaryOuter, (eSideRight), aError, 0);
3737 int32_t nsGlobalWindowInner::GetScrollMaxY(ErrorResult& aError) {
3738 FORWARD_TO_OUTER_OR_THROW(GetScrollBoundaryOuter, (eSideBottom), aError, 0);
3741 double nsGlobalWindowInner::GetScrollX(ErrorResult& aError) {
3742 FORWARD_TO_OUTER_OR_THROW(GetScrollXOuter, (), aError, 0);
3745 double nsGlobalWindowInner::GetScrollY(ErrorResult& aError) {
3746 FORWARD_TO_OUTER_OR_THROW(GetScrollYOuter, (), aError, 0);
3749 uint32_t nsGlobalWindowInner::Length() { FORWARD_TO_OUTER(Length, (), 0); }
3751 Nullable<WindowProxyHolder> nsGlobalWindowInner::GetTop(
3752 mozilla::ErrorResult& aError) {
3753 FORWARD_TO_OUTER_OR_THROW(GetTopOuter, (), aError, nullptr);
3756 already_AddRefed<BrowsingContext> nsGlobalWindowInner::GetChildWindow(
3757 const nsAString& aName) {
3758 if (GetOuterWindowInternal()) {
3759 return GetOuterWindowInternal()->GetChildWindow(aName);
3761 return nullptr;
3764 void nsGlobalWindowInner::RefreshRealmPrincipal() {
3765 JS::SetRealmPrincipals(js::GetNonCCWObjectRealm(GetWrapperPreserveColor()),
3766 nsJSPrincipals::get(mDoc->NodePrincipal()));
3769 already_AddRefed<nsIWidget> nsGlobalWindowInner::GetMainWidget() {
3770 FORWARD_TO_OUTER(GetMainWidget, (), nullptr);
3773 nsIWidget* nsGlobalWindowInner::GetNearestWidget() const {
3774 if (GetOuterWindowInternal()) {
3775 return GetOuterWindowInternal()->GetNearestWidget();
3777 return nullptr;
3780 void nsGlobalWindowInner::SetFullScreen(bool aFullscreen,
3781 mozilla::ErrorResult& aError) {
3782 FORWARD_TO_OUTER_OR_THROW(SetFullscreenOuter, (aFullscreen, aError), aError,
3783 /* void */);
3786 bool nsGlobalWindowInner::GetFullScreen(ErrorResult& aError) {
3787 FORWARD_TO_OUTER_OR_THROW(GetFullscreenOuter, (), aError, false);
3790 bool nsGlobalWindowInner::GetFullScreen() {
3791 ErrorResult dummy;
3792 bool fullscreen = GetFullScreen(dummy);
3793 dummy.SuppressException();
3794 return fullscreen;
3797 void nsGlobalWindowInner::Dump(const nsAString& aStr) {
3798 if (!nsJSUtils::DumpEnabled()) {
3799 return;
3802 char* cstr = ToNewUTF8String(aStr);
3804 #if defined(XP_MACOSX)
3805 // have to convert \r to \n so that printing to the console works
3806 char *c = cstr, *cEnd = cstr + strlen(cstr);
3807 while (c < cEnd) {
3808 if (*c == '\r') *c = '\n';
3809 c++;
3811 #endif
3813 if (cstr) {
3814 MOZ_LOG(nsContentUtils::DOMDumpLog(), LogLevel::Debug,
3815 ("[Window.Dump] %s", cstr));
3816 #ifdef XP_WIN
3817 PrintToDebugger(cstr);
3818 #endif
3819 #ifdef ANDROID
3820 __android_log_write(ANDROID_LOG_INFO, "GeckoDump", cstr);
3821 #endif
3822 FILE* fp = gDumpFile ? gDumpFile : stdout;
3823 fputs(cstr, fp);
3824 fflush(fp);
3825 free(cstr);
3829 void nsGlobalWindowInner::Alert(nsIPrincipal& aSubjectPrincipal,
3830 ErrorResult& aError) {
3831 Alert(u""_ns, aSubjectPrincipal, aError);
3834 void nsGlobalWindowInner::Alert(const nsAString& aMessage,
3835 nsIPrincipal& aSubjectPrincipal,
3836 ErrorResult& aError) {
3837 FORWARD_TO_OUTER_OR_THROW(AlertOuter, (aMessage, aSubjectPrincipal, aError),
3838 aError, );
3841 bool nsGlobalWindowInner::Confirm(const nsAString& aMessage,
3842 nsIPrincipal& aSubjectPrincipal,
3843 ErrorResult& aError) {
3844 FORWARD_TO_OUTER_OR_THROW(ConfirmOuter, (aMessage, aSubjectPrincipal, aError),
3845 aError, false);
3848 already_AddRefed<Promise> nsGlobalWindowInner::Fetch(
3849 const RequestOrUSVString& aInput, const RequestInit& aInit,
3850 CallerType aCallerType, ErrorResult& aRv) {
3851 return FetchRequest(this, aInput, aInit, aCallerType, aRv);
3854 void nsGlobalWindowInner::Prompt(const nsAString& aMessage,
3855 const nsAString& aInitial, nsAString& aReturn,
3856 nsIPrincipal& aSubjectPrincipal,
3857 ErrorResult& aError) {
3858 FORWARD_TO_OUTER_OR_THROW(
3859 PromptOuter, (aMessage, aInitial, aReturn, aSubjectPrincipal, aError),
3860 aError, );
3863 void nsGlobalWindowInner::Focus(CallerType aCallerType, ErrorResult& aError) {
3864 FORWARD_TO_OUTER_OR_THROW(FocusOuter,
3865 (aCallerType, /* aFromOtherProcess */ false,
3866 nsFocusManager::GenerateFocusActionId()),
3867 aError, );
3870 nsresult nsGlobalWindowInner::Focus(CallerType aCallerType) {
3871 ErrorResult rv;
3872 Focus(aCallerType, rv);
3874 return rv.StealNSResult();
3877 void nsGlobalWindowInner::Blur(CallerType aCallerType, ErrorResult& aError) {
3878 FORWARD_TO_OUTER_OR_THROW(BlurOuter, (aCallerType), aError, );
3881 void nsGlobalWindowInner::Stop(ErrorResult& aError) {
3882 FORWARD_TO_OUTER_OR_THROW(StopOuter, (aError), aError, );
3885 void nsGlobalWindowInner::Print(ErrorResult& aError) {
3886 FORWARD_TO_OUTER_OR_THROW(PrintOuter, (aError), aError, );
3889 Nullable<WindowProxyHolder> nsGlobalWindowInner::PrintPreview(
3890 nsIPrintSettings* aSettings, nsIWebProgressListener* aListener,
3891 nsIDocShell* aDocShellToCloneInto, ErrorResult& aError) {
3892 FORWARD_TO_OUTER_OR_THROW(
3893 Print,
3894 (aSettings,
3895 /* aRemotePrintJob = */ nullptr, aListener, aDocShellToCloneInto,
3896 nsGlobalWindowOuter::IsPreview::Yes,
3897 nsGlobalWindowOuter::IsForWindowDotPrint::No,
3898 /* aPrintPreviewCallback = */ nullptr, aError),
3899 aError, nullptr);
3902 void nsGlobalWindowInner::MoveTo(int32_t aXPos, int32_t aYPos,
3903 CallerType aCallerType, ErrorResult& aError) {
3904 FORWARD_TO_OUTER_OR_THROW(MoveToOuter, (aXPos, aYPos, aCallerType, aError),
3905 aError, );
3908 void nsGlobalWindowInner::MoveBy(int32_t aXDif, int32_t aYDif,
3909 CallerType aCallerType, ErrorResult& aError) {
3910 FORWARD_TO_OUTER_OR_THROW(MoveByOuter, (aXDif, aYDif, aCallerType, aError),
3911 aError, );
3914 void nsGlobalWindowInner::ResizeTo(int32_t aWidth, int32_t aHeight,
3915 CallerType aCallerType,
3916 ErrorResult& aError) {
3917 FORWARD_TO_OUTER_OR_THROW(ResizeToOuter,
3918 (aWidth, aHeight, aCallerType, aError), aError, );
3921 void nsGlobalWindowInner::ResizeBy(int32_t aWidthDif, int32_t aHeightDif,
3922 CallerType aCallerType,
3923 ErrorResult& aError) {
3924 FORWARD_TO_OUTER_OR_THROW(
3925 ResizeByOuter, (aWidthDif, aHeightDif, aCallerType, aError), aError, );
3928 void nsGlobalWindowInner::SizeToContent(CallerType aCallerType,
3929 ErrorResult& aError) {
3930 FORWARD_TO_OUTER_OR_THROW(SizeToContentOuter, (aCallerType, {}, aError),
3931 aError, );
3934 void nsGlobalWindowInner::SizeToContentConstrained(
3935 const SizeToContentConstraints& aConstraints, ErrorResult& aError) {
3936 FORWARD_TO_OUTER_OR_THROW(
3937 SizeToContentOuter, (CallerType::System, aConstraints, aError), aError, );
3940 already_AddRefed<nsPIWindowRoot> nsGlobalWindowInner::GetTopWindowRoot() {
3941 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
3942 if (!outer) {
3943 return nullptr;
3945 return outer->GetTopWindowRoot();
3948 void nsGlobalWindowInner::Scroll(double aXScroll, double aYScroll) {
3949 // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
3950 auto scrollPos = CSSIntPoint::Truncate(mozilla::ToZeroIfNonfinite(aXScroll),
3951 mozilla::ToZeroIfNonfinite(aYScroll));
3952 ScrollTo(scrollPos, ScrollOptions());
3955 void nsGlobalWindowInner::ScrollTo(double aXScroll, double aYScroll) {
3956 // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
3957 auto scrollPos = CSSIntPoint::Truncate(mozilla::ToZeroIfNonfinite(aXScroll),
3958 mozilla::ToZeroIfNonfinite(aYScroll));
3959 ScrollTo(scrollPos, ScrollOptions());
3962 void nsGlobalWindowInner::ScrollTo(const ScrollToOptions& aOptions) {
3963 // When scrolling to a non-zero offset, we need to determine whether that
3964 // position is within our scrollable range, so we need updated layout
3965 // information which requires a layout flush, otherwise all we need is to
3966 // flush frames to be able to access our scrollable frame here.
3967 FlushType flushType =
3968 ((aOptions.mLeft.WasPassed() && aOptions.mLeft.Value() > 0) ||
3969 (aOptions.mTop.WasPassed() && aOptions.mTop.Value() > 0))
3970 ? FlushType::Layout
3971 : FlushType::Frames;
3972 FlushPendingNotifications(flushType);
3973 nsIScrollableFrame* sf = GetScrollFrame();
3975 if (sf) {
3976 CSSIntPoint scrollPos = sf->GetScrollPositionCSSPixels();
3977 if (aOptions.mLeft.WasPassed()) {
3978 scrollPos.x = mozilla::ToZeroIfNonfinite(aOptions.mLeft.Value());
3980 if (aOptions.mTop.WasPassed()) {
3981 scrollPos.y = mozilla::ToZeroIfNonfinite(aOptions.mTop.Value());
3984 ScrollTo(scrollPos, aOptions);
3988 void nsGlobalWindowInner::Scroll(const ScrollToOptions& aOptions) {
3989 ScrollTo(aOptions);
3992 void nsGlobalWindowInner::ScrollTo(const CSSIntPoint& aScroll,
3993 const ScrollOptions& aOptions) {
3994 // When scrolling to a non-zero offset, we need to determine whether that
3995 // position is within our scrollable range, so we need updated layout
3996 // information which requires a layout flush, otherwise all we need is to
3997 // flush frames to be able to access our scrollable frame here.
3998 FlushType flushType =
3999 (aScroll.x || aScroll.y) ? FlushType::Layout : FlushType::Frames;
4000 FlushPendingNotifications(flushType);
4001 nsIScrollableFrame* sf = GetScrollFrame();
4003 if (sf) {
4004 // Here we calculate what the max pixel value is that we can
4005 // scroll to, we do this by dividing maxint with the pixel to
4006 // twips conversion factor, and subtracting 4, the 4 comes from
4007 // experimenting with this value, anything less makes the view
4008 // code not scroll correctly, I have no idea why. -- jst
4009 const int32_t maxpx = nsPresContext::AppUnitsToIntCSSPixels(0x7fffffff) - 4;
4011 CSSIntPoint scroll(aScroll);
4012 if (scroll.x > maxpx) {
4013 scroll.x = maxpx;
4016 if (scroll.y > maxpx) {
4017 scroll.y = maxpx;
4020 ScrollMode scrollMode = sf->IsSmoothScroll(aOptions.mBehavior)
4021 ? ScrollMode::SmoothMsd
4022 : ScrollMode::Instant;
4024 sf->ScrollToCSSPixels(scroll, scrollMode);
4028 void nsGlobalWindowInner::ScrollBy(double aXScrollDif, double aYScrollDif) {
4029 FlushPendingNotifications(FlushType::Layout);
4030 nsIScrollableFrame* sf = GetScrollFrame();
4032 if (sf) {
4033 // It seems like it would make more sense for ScrollBy to use
4034 // SMOOTH mode, but tests seem to depend on the synchronous behaviour.
4035 // Perhaps Web content does too.
4036 ScrollToOptions options;
4037 options.mLeft.Construct(aXScrollDif);
4038 options.mTop.Construct(aYScrollDif);
4039 ScrollBy(options);
4043 void nsGlobalWindowInner::ScrollBy(const ScrollToOptions& aOptions) {
4044 FlushPendingNotifications(FlushType::Layout);
4045 nsIScrollableFrame* sf = GetScrollFrame();
4047 if (sf) {
4048 CSSIntPoint scrollDelta;
4049 if (aOptions.mLeft.WasPassed()) {
4050 scrollDelta.x = mozilla::ToZeroIfNonfinite(aOptions.mLeft.Value());
4052 if (aOptions.mTop.WasPassed()) {
4053 scrollDelta.y = mozilla::ToZeroIfNonfinite(aOptions.mTop.Value());
4056 ScrollMode scrollMode = sf->IsSmoothScroll(aOptions.mBehavior)
4057 ? ScrollMode::SmoothMsd
4058 : ScrollMode::Instant;
4060 sf->ScrollByCSSPixels(scrollDelta, scrollMode);
4064 void nsGlobalWindowInner::ScrollByLines(int32_t numLines,
4065 const ScrollOptions& aOptions) {
4066 FlushPendingNotifications(FlushType::Layout);
4067 nsIScrollableFrame* sf = GetScrollFrame();
4068 if (sf) {
4069 // It seems like it would make more sense for ScrollByLines to use
4070 // SMOOTH mode, but tests seem to depend on the synchronous behaviour.
4071 // Perhaps Web content does too.
4072 ScrollMode scrollMode = sf->IsSmoothScroll(aOptions.mBehavior)
4073 ? ScrollMode::SmoothMsd
4074 : ScrollMode::Instant;
4076 sf->ScrollBy(nsIntPoint(0, numLines), ScrollUnit::LINES, scrollMode);
4080 void nsGlobalWindowInner::ScrollByPages(int32_t numPages,
4081 const ScrollOptions& aOptions) {
4082 FlushPendingNotifications(FlushType::Layout);
4083 nsIScrollableFrame* sf = GetScrollFrame();
4084 if (sf) {
4085 // It seems like it would make more sense for ScrollByPages to use
4086 // SMOOTH mode, but tests seem to depend on the synchronous behaviour.
4087 // Perhaps Web content does too.
4088 ScrollMode scrollMode = sf->IsSmoothScroll(aOptions.mBehavior)
4089 ? ScrollMode::SmoothMsd
4090 : ScrollMode::Instant;
4092 sf->ScrollBy(nsIntPoint(0, numPages), ScrollUnit::PAGES, scrollMode);
4096 void nsGlobalWindowInner::MozScrollSnap() {
4097 FlushPendingNotifications(FlushType::Layout);
4098 nsIScrollableFrame* sf = GetScrollFrame();
4099 if (sf) {
4100 sf->ScrollSnap();
4104 void nsGlobalWindowInner::ClearTimeout(int32_t aHandle) {
4105 DebuggerNotificationDispatch(this, DebuggerNotificationType::ClearTimeout);
4107 if (aHandle > 0) {
4108 mTimeoutManager->ClearTimeout(aHandle, Timeout::Reason::eTimeoutOrInterval);
4112 void nsGlobalWindowInner::ClearInterval(int32_t aHandle) {
4113 DebuggerNotificationDispatch(this, DebuggerNotificationType::ClearInterval);
4115 if (aHandle > 0) {
4116 mTimeoutManager->ClearTimeout(aHandle, Timeout::Reason::eTimeoutOrInterval);
4120 void nsGlobalWindowInner::SetResizable(bool aResizable) const {
4121 // nop
4124 void nsGlobalWindowInner::CaptureEvents() {
4125 if (mDoc) {
4126 mDoc->WarnOnceAbout(DeprecatedOperations::eUseOfCaptureEvents);
4130 void nsGlobalWindowInner::ReleaseEvents() {
4131 if (mDoc) {
4132 mDoc->WarnOnceAbout(DeprecatedOperations::eUseOfReleaseEvents);
4136 Nullable<WindowProxyHolder> nsGlobalWindowInner::Open(const nsAString& aUrl,
4137 const nsAString& aName,
4138 const nsAString& aOptions,
4139 ErrorResult& aError) {
4140 FORWARD_TO_OUTER_OR_THROW(OpenOuter, (aUrl, aName, aOptions, aError), aError,
4141 nullptr);
4144 Nullable<WindowProxyHolder> nsGlobalWindowInner::OpenDialog(
4145 JSContext* aCx, const nsAString& aUrl, const nsAString& aName,
4146 const nsAString& aOptions, const Sequence<JS::Value>& aExtraArgument,
4147 ErrorResult& aError) {
4148 FORWARD_TO_OUTER_OR_THROW(
4149 OpenDialogOuter, (aCx, aUrl, aName, aOptions, aExtraArgument, aError),
4150 aError, nullptr);
4153 WindowProxyHolder nsGlobalWindowInner::GetFrames(ErrorResult& aError) {
4154 FORWARD_TO_OUTER_OR_THROW(GetFramesOuter, (), aError, Window());
4157 void nsGlobalWindowInner::PostMessageMoz(JSContext* aCx,
4158 JS::Handle<JS::Value> aMessage,
4159 const nsAString& aTargetOrigin,
4160 JS::Handle<JS::Value> aTransfer,
4161 nsIPrincipal& aSubjectPrincipal,
4162 ErrorResult& aError) {
4163 FORWARD_TO_OUTER_OR_THROW(
4164 PostMessageMozOuter,
4165 (aCx, aMessage, aTargetOrigin, aTransfer, aSubjectPrincipal, aError),
4166 aError, );
4169 void nsGlobalWindowInner::PostMessageMoz(JSContext* aCx,
4170 JS::Handle<JS::Value> aMessage,
4171 const nsAString& aTargetOrigin,
4172 const Sequence<JSObject*>& aTransfer,
4173 nsIPrincipal& aSubjectPrincipal,
4174 ErrorResult& aRv) {
4175 JS::Rooted<JS::Value> transferArray(aCx, JS::UndefinedValue());
4177 aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransfer,
4178 &transferArray);
4179 if (NS_WARN_IF(aRv.Failed())) {
4180 return;
4183 PostMessageMoz(aCx, aMessage, aTargetOrigin, transferArray, aSubjectPrincipal,
4184 aRv);
4187 void nsGlobalWindowInner::PostMessageMoz(
4188 JSContext* aCx, JS::Handle<JS::Value> aMessage,
4189 const WindowPostMessageOptions& aOptions, nsIPrincipal& aSubjectPrincipal,
4190 ErrorResult& aRv) {
4191 JS::Rooted<JS::Value> transferArray(aCx, JS::UndefinedValue());
4193 aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(
4194 aCx, aOptions.mTransfer, &transferArray);
4195 if (NS_WARN_IF(aRv.Failed())) {
4196 return;
4199 PostMessageMoz(aCx, aMessage, aOptions.mTargetOrigin, transferArray,
4200 aSubjectPrincipal, aRv);
4203 void nsGlobalWindowInner::Close(CallerType aCallerType, ErrorResult& aError) {
4204 FORWARD_TO_OUTER_OR_THROW(CloseOuter, (aCallerType == CallerType::System),
4205 aError, );
4208 nsresult nsGlobalWindowInner::Close() {
4209 FORWARD_TO_OUTER(Close, (), NS_ERROR_UNEXPECTED);
4212 bool nsGlobalWindowInner::IsInModalState() {
4213 FORWARD_TO_OUTER(IsInModalState, (), false);
4216 // static
4217 void nsGlobalWindowInner::NotifyDOMWindowDestroyed(
4218 nsGlobalWindowInner* aWindow) {
4219 nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
4220 if (observerService) {
4221 observerService->NotifyObservers(ToSupports(aWindow),
4222 DOM_WINDOW_DESTROYED_TOPIC, nullptr);
4226 void nsGlobalWindowInner::NotifyWindowIDDestroyed(const char* aTopic) {
4227 nsCOMPtr<nsIRunnable> runnable =
4228 new WindowDestroyedEvent(this, mWindowID, aTopic);
4229 Dispatch(TaskCategory::Other, runnable.forget());
4232 // static
4233 void nsGlobalWindowInner::NotifyDOMWindowFrozen(nsGlobalWindowInner* aWindow) {
4234 if (aWindow) {
4235 nsCOMPtr<nsIObserverService> observerService =
4236 services::GetObserverService();
4237 if (observerService) {
4238 observerService->NotifyObservers(ToSupports(aWindow),
4239 DOM_WINDOW_FROZEN_TOPIC, nullptr);
4244 // static
4245 void nsGlobalWindowInner::NotifyDOMWindowThawed(nsGlobalWindowInner* aWindow) {
4246 if (aWindow) {
4247 nsCOMPtr<nsIObserverService> observerService =
4248 services::GetObserverService();
4249 if (observerService) {
4250 observerService->NotifyObservers(ToSupports(aWindow),
4251 DOM_WINDOW_THAWED_TOPIC, nullptr);
4256 Element* nsGlobalWindowInner::GetFrameElement(nsIPrincipal& aSubjectPrincipal,
4257 ErrorResult& aError) {
4258 FORWARD_TO_OUTER_OR_THROW(GetFrameElement, (aSubjectPrincipal), aError,
4259 nullptr);
4262 Element* nsGlobalWindowInner::GetRealFrameElement(ErrorResult& aError) {
4263 FORWARD_TO_OUTER_OR_THROW(GetFrameElement, (), aError, nullptr);
4266 void nsGlobalWindowInner::UpdateCommands(const nsAString& anAction,
4267 Selection* aSel, int16_t aReason) {
4268 if (GetOuterWindowInternal()) {
4269 GetOuterWindowInternal()->UpdateCommands(anAction, aSel, aReason);
4273 Selection* nsGlobalWindowInner::GetSelection(ErrorResult& aError) {
4274 FORWARD_TO_OUTER_OR_THROW(GetSelectionOuter, (), aError, nullptr);
4277 WebTaskScheduler* nsGlobalWindowInner::Scheduler() {
4278 if (!mWebTaskScheduler) {
4279 mWebTaskScheduler = WebTaskScheduler::CreateForMainThread(this);
4281 MOZ_ASSERT(mWebTaskScheduler);
4282 return mWebTaskScheduler;
4285 bool nsGlobalWindowInner::Find(const nsAString& aString, bool aCaseSensitive,
4286 bool aBackwards, bool aWrapAround,
4287 bool aWholeWord, bool aSearchInFrames,
4288 bool aShowDialog, ErrorResult& aError) {
4289 FORWARD_TO_OUTER_OR_THROW(FindOuter,
4290 (aString, aCaseSensitive, aBackwards, aWrapAround,
4291 aWholeWord, aSearchInFrames, aShowDialog, aError),
4292 aError, false);
4295 void nsGlobalWindowInner::GetOrigin(nsAString& aOrigin) {
4296 nsContentUtils::GetUTFOrigin(GetPrincipal(), aOrigin);
4299 // See also AutoJSAPI::ReportException
4300 void nsGlobalWindowInner::ReportError(JSContext* aCx,
4301 JS::Handle<JS::Value> aError,
4302 CallerType aCallerType,
4303 ErrorResult& aRv) {
4304 if (MOZ_UNLIKELY(!HasActiveDocument())) {
4305 return aRv.Throw(NS_ERROR_XPC_SECURITY_MANAGER_VETO);
4308 JS::ErrorReportBuilder jsReport(aCx);
4309 JS::ExceptionStack exnStack(aCx, aError, nullptr);
4310 if (!jsReport.init(aCx, exnStack, JS::ErrorReportBuilder::NoSideEffects)) {
4311 return aRv.NoteJSContextException(aCx);
4314 RefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport();
4315 bool isChrome = aCallerType == CallerType::System;
4316 xpcReport->Init(jsReport.report(), jsReport.toStringResult().c_str(),
4317 isChrome, WindowID());
4319 JS::RootingContext* rcx = JS::RootingContext::get(aCx);
4320 DispatchScriptErrorEvent(this, rcx, xpcReport, exnStack.exception(),
4321 exnStack.stack());
4324 void nsGlobalWindowInner::Atob(const nsAString& aAsciiBase64String,
4325 nsAString& aBinaryData, ErrorResult& aError) {
4326 aError = nsContentUtils::Atob(aAsciiBase64String, aBinaryData);
4329 void nsGlobalWindowInner::Btoa(const nsAString& aBinaryData,
4330 nsAString& aAsciiBase64String,
4331 ErrorResult& aError) {
4332 aError = nsContentUtils::Btoa(aBinaryData, aAsciiBase64String);
4335 //*****************************************************************************
4336 // EventTarget
4337 //*****************************************************************************
4339 nsPIDOMWindowOuter* nsGlobalWindowInner::GetOwnerGlobalForBindingsInternal() {
4340 return nsPIDOMWindowOuter::GetFromCurrentInner(this);
4343 bool nsGlobalWindowInner::DispatchEvent(Event& aEvent, CallerType aCallerType,
4344 ErrorResult& aRv) {
4345 if (!IsCurrentInnerWindow()) {
4346 NS_WARNING(
4347 "DispatchEvent called on non-current inner window, dropping. "
4348 "Please check the window in the caller instead.");
4349 aRv.Throw(NS_ERROR_FAILURE);
4350 return false;
4353 if (!mDoc) {
4354 aRv.Throw(NS_ERROR_FAILURE);
4355 return false;
4358 // Obtain a presentation shell
4359 RefPtr<nsPresContext> presContext = mDoc->GetPresContext();
4361 nsEventStatus status = nsEventStatus_eIgnore;
4362 // TODO: Bug 1506441
4363 nsresult rv = EventDispatcher::DispatchDOMEvent(
4364 MOZ_KnownLive(ToSupports(this)), nullptr, &aEvent, presContext, &status);
4365 bool retval = !aEvent.DefaultPrevented(aCallerType);
4366 if (NS_FAILED(rv)) {
4367 aRv.Throw(rv);
4369 return retval;
4372 mozilla::Maybe<mozilla::dom::EventCallbackDebuggerNotificationType>
4373 nsGlobalWindowInner::GetDebuggerNotificationType() const {
4374 return mozilla::Some(
4375 mozilla::dom::EventCallbackDebuggerNotificationType::Global);
4378 bool nsGlobalWindowInner::ComputeDefaultWantsUntrusted(ErrorResult& aRv) {
4379 return !nsContentUtils::IsChromeDoc(mDoc);
4382 EventListenerManager* nsGlobalWindowInner::GetOrCreateListenerManager() {
4383 if (!mListenerManager) {
4384 mListenerManager =
4385 new EventListenerManager(static_cast<EventTarget*>(this));
4388 return mListenerManager;
4391 EventListenerManager* nsGlobalWindowInner::GetExistingListenerManager() const {
4392 return mListenerManager;
4395 mozilla::dom::DebuggerNotificationManager*
4396 nsGlobalWindowInner::GetOrCreateDebuggerNotificationManager() {
4397 if (!mDebuggerNotificationManager) {
4398 mDebuggerNotificationManager = new DebuggerNotificationManager(this);
4401 return mDebuggerNotificationManager;
4404 mozilla::dom::DebuggerNotificationManager*
4405 nsGlobalWindowInner::GetExistingDebuggerNotificationManager() {
4406 return mDebuggerNotificationManager;
4409 //*****************************************************************************
4410 // nsGlobalWindowInner::nsPIDOMWindow
4411 //*****************************************************************************
4413 Location* nsGlobalWindowInner::Location() {
4414 if (!mLocation) {
4415 mLocation = new dom::Location(this, GetBrowsingContext());
4418 return mLocation;
4421 void nsGlobalWindowInner::MaybeUpdateTouchState() {
4422 if (mMayHaveTouchEventListener) {
4423 nsCOMPtr<nsIObserverService> observerService =
4424 services::GetObserverService();
4426 if (observerService) {
4427 observerService->NotifyObservers(static_cast<nsIDOMWindow*>(this),
4428 DOM_TOUCH_LISTENER_ADDED, nullptr);
4433 void nsGlobalWindowInner::EnableGamepadUpdates() {
4434 if (mHasGamepad) {
4435 RefPtr<GamepadManager> gamepadManager(GamepadManager::GetService());
4436 if (gamepadManager) {
4437 gamepadManager->AddListener(this);
4442 void nsGlobalWindowInner::DisableGamepadUpdates() {
4443 if (mHasGamepad) {
4444 RefPtr<GamepadManager> gamepadManager(GamepadManager::GetService());
4445 if (gamepadManager) {
4446 gamepadManager->RemoveListener(this);
4451 void nsGlobalWindowInner::EnableVRUpdates() {
4452 // We need to create a VREventObserver before we can either detect XR runtimes
4453 // or start an XR session
4454 if (!mVREventObserver && (mHasXRSession || mXRRuntimeDetectionInFlight)) {
4455 // Assert that we are not creating the observer while IsDying() as
4456 // that would result in a leak. VREventObserver holds a RefPtr to
4457 // this nsGlobalWindowInner and would prevent it from being deallocated.
4458 MOZ_ASSERT(!IsDying(),
4459 "Creating a VREventObserver for an nsGlobalWindow that is "
4460 "dying would cause it to leak.");
4461 mVREventObserver = new VREventObserver(this);
4463 // If the content has an XR session, then we need to tell
4464 // VREventObserver that there is VR activity.
4465 if (mHasXRSession) {
4466 nsPIDOMWindowOuter* outer = GetOuterWindow();
4467 if (outer && !outer->IsBackground()) {
4468 StartVRActivity();
4473 void nsGlobalWindowInner::DisableVRUpdates() {
4474 if (mVREventObserver) {
4475 mVREventObserver->DisconnectFromOwner();
4476 mVREventObserver = nullptr;
4480 void nsGlobalWindowInner::ResetVRTelemetry(bool aUpdate) {
4481 if (mVREventObserver) {
4482 mVREventObserver->UpdateSpentTimeIn2DTelemetry(aUpdate);
4486 void nsGlobalWindowInner::StartVRActivity() {
4488 * If the content has an XR session, tell
4489 * the VREventObserver that the window is accessing
4490 * VR devices.
4492 * It's possible to have a VREventObserver without
4493 * and XR session, if we are using it to get updates
4494 * about XR runtime enumeration. In this case,
4495 * we would not tell the VREventObserver that
4496 * we are accessing VR devices.
4498 if (mVREventObserver && mHasXRSession) {
4499 mVREventObserver->StartActivity();
4503 void nsGlobalWindowInner::StopVRActivity() {
4505 * If the content has an XR session, tell
4506 * the VReventObserver that the window is no longer
4507 * accessing VR devices. This does not stop the
4508 * XR session itself, which may be resumed with
4509 * EnableVRUpdates.
4510 * It's possible to have a VREventObserver without
4511 * and XR session, if we are using it to get updates
4512 * about XR runtime enumeration. In this case,
4513 * we would not tell the VREventObserver that
4514 * we ending an activity that accesses VR devices.
4516 if (mVREventObserver && mHasXRSession) {
4517 mVREventObserver->StopActivity();
4521 void nsGlobalWindowInner::SetFocusedElement(Element* aElement,
4522 uint32_t aFocusMethod,
4523 bool aNeedsFocus) {
4524 if (aElement && aElement->GetComposedDoc() != mDoc) {
4525 NS_WARNING("Trying to set focus to a node from a wrong document");
4526 return;
4529 if (IsDying()) {
4530 NS_ASSERTION(!aElement, "Trying to focus cleaned up window!");
4531 aElement = nullptr;
4532 aNeedsFocus = false;
4534 if (mFocusedElement != aElement) {
4535 UpdateCanvasFocus(false, aElement);
4536 mFocusedElement = aElement;
4537 // TODO: Maybe this should be set on refocus too?
4538 mFocusMethod = aFocusMethod & nsIFocusManager::METHOD_MASK;
4541 if (mFocusedElement) {
4542 // if a node was focused by a keypress, turn on focus rings for the
4543 // window.
4544 if (mFocusMethod & nsIFocusManager::FLAG_BYKEY) {
4545 mUnknownFocusMethodShouldShowOutline = true;
4546 mFocusByKeyOccurred = true;
4547 } else if (nsFocusManager::GetFocusMoveActionCause(mFocusMethod) !=
4548 widget::InputContextAction::CAUSE_UNKNOWN) {
4549 mUnknownFocusMethodShouldShowOutline = false;
4550 } else if (aFocusMethod & nsIFocusManager::FLAG_NOSHOWRING) {
4551 // If we get focused via script, and script has explicitly opted out of
4552 // outlines via FLAG_NOSHOWRING, we don't want to make a refocus start
4553 // showing outlines.
4554 mUnknownFocusMethodShouldShowOutline = false;
4558 if (aNeedsFocus) {
4559 mNeedsFocus = aNeedsFocus;
4563 uint32_t nsGlobalWindowInner::GetFocusMethod() { return mFocusMethod; }
4565 bool nsGlobalWindowInner::ShouldShowFocusRing() {
4566 if (mFocusByKeyOccurred &&
4567 StaticPrefs::browser_display_always_show_rings_after_key_focus()) {
4568 return true;
4570 return StaticPrefs::browser_display_show_focus_rings();
4573 bool nsGlobalWindowInner::TakeFocus(bool aFocus, uint32_t aFocusMethod) {
4574 if (IsDying()) {
4575 return false;
4578 if (aFocus) {
4579 mFocusMethod = aFocusMethod & nsIFocusManager::METHOD_MASK;
4582 if (mHasFocus != aFocus) {
4583 mHasFocus = aFocus;
4584 UpdateCanvasFocus(true, mFocusedElement);
4587 // if mNeedsFocus is true, then the document has not yet received a
4588 // document-level focus event. If there is a root content node, then return
4589 // true to tell the calling focus manager that a focus event is expected. If
4590 // there is no root content node, the document hasn't loaded enough yet, or
4591 // there isn't one and there is no point in firing a focus event.
4592 if (aFocus && mNeedsFocus && mDoc && mDoc->GetRootElement() != nullptr) {
4593 mNeedsFocus = false;
4594 return true;
4597 mNeedsFocus = false;
4598 return false;
4601 void nsGlobalWindowInner::SetReadyForFocus() {
4602 bool oldNeedsFocus = mNeedsFocus;
4603 mNeedsFocus = false;
4605 if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
4606 nsCOMPtr<nsPIDOMWindowOuter> outerWindow = GetOuterWindow();
4607 fm->WindowShown(outerWindow, oldNeedsFocus);
4611 void nsGlobalWindowInner::PageHidden() {
4612 // the window is being hidden, so tell the focus manager that the frame is
4613 // no longer valid. Use the persisted field to determine if the document
4614 // is being destroyed.
4616 if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
4617 nsCOMPtr<nsPIDOMWindowOuter> outerWindow = GetOuterWindow();
4618 fm->WindowHidden(outerWindow, nsFocusManager::GenerateFocusActionId());
4621 mNeedsFocus = true;
4624 class HashchangeCallback : public Runnable {
4625 public:
4626 HashchangeCallback(const nsAString& aOldURL, const nsAString& aNewURL,
4627 nsGlobalWindowInner* aWindow)
4628 : mozilla::Runnable("HashchangeCallback"), mWindow(aWindow) {
4629 MOZ_ASSERT(mWindow);
4630 mOldURL.Assign(aOldURL);
4631 mNewURL.Assign(aNewURL);
4634 NS_IMETHOD Run() override {
4635 MOZ_ASSERT(NS_IsMainThread(), "Should be called on the main thread.");
4636 return mWindow->FireHashchange(mOldURL, mNewURL);
4639 private:
4640 nsString mOldURL;
4641 nsString mNewURL;
4642 RefPtr<nsGlobalWindowInner> mWindow;
4645 nsresult nsGlobalWindowInner::DispatchAsyncHashchange(nsIURI* aOldURI,
4646 nsIURI* aNewURI) {
4647 // Make sure that aOldURI and aNewURI are identical up to the '#', and that
4648 // their hashes are different.
4649 bool equal = false;
4650 NS_ENSURE_STATE(NS_SUCCEEDED(aOldURI->EqualsExceptRef(aNewURI, &equal)) &&
4651 equal);
4652 nsAutoCString oldHash, newHash;
4653 bool oldHasHash, newHasHash;
4654 NS_ENSURE_STATE(NS_SUCCEEDED(aOldURI->GetRef(oldHash)) &&
4655 NS_SUCCEEDED(aNewURI->GetRef(newHash)) &&
4656 NS_SUCCEEDED(aOldURI->GetHasRef(&oldHasHash)) &&
4657 NS_SUCCEEDED(aNewURI->GetHasRef(&newHasHash)) &&
4658 (oldHasHash != newHasHash || !oldHash.Equals(newHash)));
4660 nsAutoCString oldSpec, newSpec;
4661 nsresult rv = aOldURI->GetSpec(oldSpec);
4662 NS_ENSURE_SUCCESS(rv, rv);
4663 rv = aNewURI->GetSpec(newSpec);
4664 NS_ENSURE_SUCCESS(rv, rv);
4666 NS_ConvertUTF8toUTF16 oldWideSpec(oldSpec);
4667 NS_ConvertUTF8toUTF16 newWideSpec(newSpec);
4669 nsCOMPtr<nsIRunnable> callback =
4670 new HashchangeCallback(oldWideSpec, newWideSpec, this);
4671 return Dispatch(TaskCategory::Other, callback.forget());
4674 nsresult nsGlobalWindowInner::FireHashchange(const nsAString& aOldURL,
4675 const nsAString& aNewURL) {
4676 // Don't do anything if the window is frozen.
4677 if (IsFrozen()) {
4678 return NS_OK;
4681 // Get a presentation shell for use in creating the hashchange event.
4682 NS_ENSURE_STATE(IsCurrentInnerWindow());
4684 HashChangeEventInit init;
4685 init.mNewURL = aNewURL;
4686 init.mOldURL = aOldURL;
4688 RefPtr<HashChangeEvent> event =
4689 HashChangeEvent::Constructor(this, u"hashchange"_ns, init);
4691 event->SetTrusted(true);
4693 ErrorResult rv;
4694 DispatchEvent(*event, rv);
4695 return rv.StealNSResult();
4698 nsresult nsGlobalWindowInner::DispatchSyncPopState() {
4699 NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
4700 "Must be safe to run script here.");
4702 // Bail if the window is frozen.
4703 if (IsFrozen()) {
4704 return NS_OK;
4707 AutoJSAPI jsapi;
4708 bool result = jsapi.Init(this);
4709 NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
4711 JSContext* cx = jsapi.cx();
4713 // Get the document's pending state object -- it contains the data we're
4714 // going to send along with the popstate event. The object is serialized
4715 // using structured clone.
4716 JS::Rooted<JS::Value> stateJSValue(cx);
4717 nsresult rv = mDoc->GetStateObject(&stateJSValue);
4718 NS_ENSURE_SUCCESS(rv, rv);
4720 if (!JS_WrapValue(cx, &stateJSValue)) {
4721 return NS_ERROR_OUT_OF_MEMORY;
4724 RootedDictionary<PopStateEventInit> init(cx);
4725 init.mState = stateJSValue;
4727 RefPtr<PopStateEvent> event =
4728 PopStateEvent::Constructor(this, u"popstate"_ns, init);
4729 event->SetTrusted(true);
4730 event->SetTarget(this);
4732 ErrorResult err;
4733 DispatchEvent(*event, err);
4734 return err.StealNSResult();
4737 //-------------------------------------------------------
4738 // Tells the HTMLFrame/CanvasFrame that is now has focus
4739 void nsGlobalWindowInner::UpdateCanvasFocus(bool aFocusChanged,
4740 nsIContent* aNewContent) {
4741 // this is called from the inner window so use GetDocShell
4742 nsIDocShell* docShell = GetDocShell();
4743 if (!docShell) return;
4745 bool editable;
4746 docShell->GetEditable(&editable);
4747 if (editable) return;
4749 PresShell* presShell = docShell->GetPresShell();
4750 if (!presShell || !mDoc) {
4751 return;
4754 Element* rootElement = mDoc->GetRootElement();
4755 if (rootElement) {
4756 if ((mHasFocus || aFocusChanged) &&
4757 (mFocusedElement == rootElement || aNewContent == rootElement)) {
4758 nsCanvasFrame* canvasFrame = presShell->GetCanvasFrame();
4759 if (canvasFrame) {
4760 canvasFrame->SetHasFocus(mHasFocus && rootElement == aNewContent);
4763 } else {
4764 // XXXbz I would expect that there is never a canvasFrame in this case...
4765 nsCanvasFrame* canvasFrame = presShell->GetCanvasFrame();
4766 if (canvasFrame) {
4767 canvasFrame->SetHasFocus(false);
4772 already_AddRefed<nsICSSDeclaration> nsGlobalWindowInner::GetComputedStyle(
4773 Element& aElt, const nsAString& aPseudoElt, ErrorResult& aError) {
4774 return GetComputedStyleHelper(aElt, aPseudoElt, false, aError);
4777 already_AddRefed<nsICSSDeclaration>
4778 nsGlobalWindowInner::GetDefaultComputedStyle(Element& aElt,
4779 const nsAString& aPseudoElt,
4780 ErrorResult& aError) {
4781 return GetComputedStyleHelper(aElt, aPseudoElt, true, aError);
4784 already_AddRefed<nsICSSDeclaration> nsGlobalWindowInner::GetComputedStyleHelper(
4785 Element& aElt, const nsAString& aPseudoElt, bool aDefaultStylesOnly,
4786 ErrorResult& aError) {
4787 FORWARD_TO_OUTER_OR_THROW(GetComputedStyleHelperOuter,
4788 (aElt, aPseudoElt, aDefaultStylesOnly, aError),
4789 aError, nullptr);
4792 Storage* nsGlobalWindowInner::GetSessionStorage(ErrorResult& aError) {
4793 nsIPrincipal* principal = GetPrincipal();
4794 nsIPrincipal* storagePrincipal;
4795 if (StaticPrefs::
4796 privacy_partition_always_partition_third_party_non_cookie_storage_exempt_sessionstorage()) {
4797 storagePrincipal = GetEffectiveCookiePrincipal();
4798 } else {
4799 storagePrincipal = GetEffectiveStoragePrincipal();
4801 BrowsingContext* browsingContext = GetBrowsingContext();
4803 if (!principal || !storagePrincipal || !browsingContext ||
4804 !Storage::StoragePrefIsEnabled()) {
4805 return nullptr;
4808 if (mSessionStorage) {
4809 MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
4810 ("nsGlobalWindowInner %p has %p sessionStorage", this,
4811 mSessionStorage.get()));
4812 bool canAccess =
4813 principal->Subsumes(mSessionStorage->Principal()) &&
4814 storagePrincipal->Subsumes(mSessionStorage->StoragePrincipal());
4815 if (!canAccess) {
4816 mSessionStorage = nullptr;
4820 if (!mSessionStorage) {
4821 nsString documentURI;
4822 if (mDoc) {
4823 aError = mDoc->GetDocumentURI(documentURI);
4824 if (NS_WARN_IF(aError.Failed())) {
4825 return nullptr;
4829 if (!mDoc) {
4830 aError.Throw(NS_ERROR_FAILURE);
4831 return nullptr;
4834 // If the document's sandboxed origin flag is set, then accessing
4835 // sessionStorage is prohibited.
4836 if (mDoc->GetSandboxFlags() & SANDBOXED_ORIGIN) {
4837 aError.ThrowSecurityError(
4838 "Forbidden in a sandboxed document without the 'allow-same-origin' "
4839 "flag.");
4840 return nullptr;
4843 uint32_t rejectedReason = 0;
4844 StorageAccess access = StorageAllowedForWindow(this, &rejectedReason);
4846 // SessionStorage is an ephemeral per-tab per-origin storage that only lives
4847 // as long as the tab is open, although it may survive browser restarts
4848 // thanks to the session store. So we interpret storage access differently
4849 // than we would for persistent per-origin storage like LocalStorage and so
4850 // it may be okay to provide SessionStorage even when we receive a value of
4851 // eDeny.
4853 // ContentBlocking::ShouldAllowAccessFor will return false for 3 main
4854 // reasons.
4856 // 1. Cookies are entirely blocked due to a per-origin permission
4857 // (nsICookiePermission::ACCESS_DENY for the top-level principal or this
4858 // window's principal) or the very broad BEHAVIOR_REJECT. This will return
4859 // eDeny with a reason of STATE_COOKIES_BLOCKED_BY_PERMISSION or
4860 // STATE_COOKIES_BLOCKED_ALL.
4862 // 2. Third-party cookies are limited via BEHAVIOR_REJECT_FOREIGN and
4863 // BEHAVIOR_LIMIT_FOREIGN and this is a third-party window. This will return
4864 // eDeny with a reason of STATE_COOKIES_BLOCKED_FOREIGN.
4866 // 3. Tracking protection (BEHAVIOR_REJECT_TRACKER and
4867 // BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN) is in effect and
4868 // IsThirdPartyTrackingResourceWindow() returned true and there wasn't a
4869 // permission that allows it. This will return ePartitionTrackersOrDeny with
4870 // a reason of STATE_COOKIES_BLOCKED_TRACKER or
4871 // STATE_COOKIES_BLOCKED_SOCIALTRACKER.
4873 // In the 1st case, the user has explicitly indicated that they don't want
4874 // to allow any storage to the origin or all origins and so we throw an
4875 // error and deny access to SessionStorage. In the 2nd case, a legacy
4876 // decision reasoned that there's no harm in providing SessionStorage
4877 // because the information is not durable and cannot escape the current tab.
4878 // The rationale is similar for the 3rd case.
4879 if (access == StorageAccess::eDeny &&
4880 rejectedReason !=
4881 nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN) {
4882 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
4883 return nullptr;
4886 const RefPtr<SessionStorageManager> storageManager =
4887 browsingContext->GetSessionStorageManager();
4888 if (!storageManager) {
4889 aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
4890 return nullptr;
4893 RefPtr<Storage> storage;
4894 aError = storageManager->CreateStorage(this, principal, storagePrincipal,
4895 documentURI, IsPrivateBrowsing(),
4896 getter_AddRefs(storage));
4897 if (aError.Failed()) {
4898 return nullptr;
4901 mSessionStorage = storage;
4902 MOZ_ASSERT(mSessionStorage);
4904 MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
4905 ("nsGlobalWindowInner %p tried to get a new sessionStorage %p",
4906 this, mSessionStorage.get()));
4908 if (!mSessionStorage) {
4909 aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
4910 return nullptr;
4914 MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
4915 ("nsGlobalWindowInner %p returns %p sessionStorage", this,
4916 mSessionStorage.get()));
4918 return mSessionStorage;
4921 Storage* nsGlobalWindowInner::GetLocalStorage(ErrorResult& aError) {
4922 if (!Storage::StoragePrefIsEnabled()) {
4923 return nullptr;
4926 // If the document's sandboxed origin flag is set, then accessing localStorage
4927 // is prohibited.
4928 if (mDoc && mDoc->GetSandboxFlags() & SANDBOXED_ORIGIN) {
4929 aError.ThrowSecurityError(
4930 "Forbidden in a sandboxed document without the 'allow-same-origin' "
4931 "flag.");
4932 return nullptr;
4935 // LocalStorage needs to be exposed in every context except for sandboxes and
4936 // NullPrincipals (data: URLs, for instance). But we need to keep data
4937 // separate in some scenarios: private-browsing and partitioned trackers.
4938 // In private-browsing, LocalStorage keeps data in memory, and it shares
4939 // StorageEvents just with other origins in the same private-browsing
4940 // environment.
4941 // For Partitioned Trackers, we expose a partitioned LocalStorage, which
4942 // doesn't share data with other contexts, and it's just in memory.
4943 // Partitioned localStorage is available only for trackers listed in the
4944 // privacy.restrict3rdpartystorage.partitionedHosts pref. See
4945 // nsContentUtils::IsURIInPrefList to know the syntax for the pref value.
4946 // This is a temporary web-compatibility hack.
4948 StorageAccess access = StorageAllowedForWindow(this);
4950 // We allow partitioned localStorage only to some hosts.
4951 bool isolated = false;
4952 if (ShouldPartitionStorage(access)) {
4953 if (!mDoc) {
4954 access = StorageAccess::eDeny;
4955 } else if (!StoragePartitioningEnabled(access, mDoc->CookieJarSettings())) {
4956 static const char* kPrefName =
4957 "privacy.restrict3rdpartystorage.partitionedHosts";
4959 bool isInList = false;
4960 mDoc->NodePrincipal()->IsURIInPrefList(kPrefName, &isInList);
4961 if (!isInList) {
4962 access = StorageAccess::eDeny;
4963 } else {
4964 isolated = true;
4969 if (access == StorageAccess::eDeny) {
4970 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
4971 return nullptr;
4974 nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
4975 if (mDoc) {
4976 cookieJarSettings = mDoc->CookieJarSettings();
4977 } else {
4978 cookieJarSettings =
4979 net::CookieJarSettings::GetBlockingAll(ShouldResistFingerprinting());
4982 // Note that this behavior is observable: if we grant storage permission to a
4983 // tracker, we pass from the partitioned LocalStorage (or a partitioned cookie
4984 // jar) to the 'normal' one. The previous data is lost and the 2
4985 // window.localStorage objects, before and after the permission granted, will
4986 // be different.
4987 if (mLocalStorage) {
4988 if ((mLocalStorage->Type() == (isolated ? Storage::ePartitionedLocalStorage
4989 : Storage::eLocalStorage)) &&
4990 (mLocalStorage->StoragePrincipal() == GetEffectiveStoragePrincipal())) {
4991 return mLocalStorage;
4994 // storage needs change
4995 mLocalStorage = nullptr;
4998 MOZ_ASSERT(!mLocalStorage);
5000 if (!isolated) {
5001 RefPtr<Storage> storage;
5003 if (NextGenLocalStorageEnabled()) {
5004 aError = LSObject::CreateForWindow(this, getter_AddRefs(storage));
5005 } else {
5006 nsresult rv;
5007 nsCOMPtr<nsIDOMStorageManager> storageManager =
5008 do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv);
5009 if (NS_FAILED(rv)) {
5010 aError.Throw(rv);
5011 return nullptr;
5014 nsString documentURI;
5015 if (mDoc) {
5016 aError = mDoc->GetDocumentURI(documentURI);
5017 if (NS_WARN_IF(aError.Failed())) {
5018 return nullptr;
5022 nsIPrincipal* principal = GetPrincipal();
5023 if (!principal) {
5024 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
5025 return nullptr;
5028 nsIPrincipal* storagePrincipal = GetEffectiveStoragePrincipal();
5029 if (!storagePrincipal) {
5030 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
5031 return nullptr;
5034 aError = storageManager->CreateStorage(this, principal, storagePrincipal,
5035 documentURI, IsPrivateBrowsing(),
5036 getter_AddRefs(storage));
5039 if (aError.Failed()) {
5040 return nullptr;
5043 mLocalStorage = storage;
5044 } else {
5045 nsresult rv;
5046 nsCOMPtr<nsIDOMSessionStorageManager> storageManager =
5047 do_GetService("@mozilla.org/dom/sessionStorage-manager;1", &rv);
5048 if (NS_FAILED(rv)) {
5049 aError.Throw(rv);
5050 return nullptr;
5053 nsIPrincipal* principal = GetPrincipal();
5054 if (!principal) {
5055 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
5056 return nullptr;
5059 nsIPrincipal* storagePrincipal = GetEffectiveStoragePrincipal();
5060 if (!storagePrincipal) {
5061 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
5062 return nullptr;
5065 RefPtr<SessionStorageCache> cache;
5066 if (isolated) {
5067 cache = new SessionStorageCache();
5068 } else {
5069 // This will clone the session storage if it exists.
5070 rv = storageManager->GetSessionStorageCache(principal, storagePrincipal,
5071 &cache);
5072 if (NS_FAILED(rv)) {
5073 aError.Throw(rv);
5074 return nullptr;
5078 mLocalStorage =
5079 new PartitionedLocalStorage(this, principal, storagePrincipal, cache);
5082 MOZ_ASSERT(mLocalStorage);
5083 MOZ_ASSERT(
5084 mLocalStorage->Type() ==
5085 (isolated ? Storage::ePartitionedLocalStorage : Storage::eLocalStorage));
5086 return mLocalStorage;
5089 IDBFactory* nsGlobalWindowInner::GetIndexedDB(JSContext* aCx,
5090 ErrorResult& aError) {
5091 if (!mIndexedDB) {
5092 // This may keep mIndexedDB null without setting an error.
5093 auto res = IDBFactory::CreateForWindow(this);
5094 if (res.isErr()) {
5095 aError = res.unwrapErr();
5096 } else {
5097 mIndexedDB = res.unwrap();
5101 return mIndexedDB;
5104 //*****************************************************************************
5105 // nsGlobalWindowInner::nsIInterfaceRequestor
5106 //*****************************************************************************
5108 NS_IMETHODIMP
5109 nsGlobalWindowInner::GetInterface(const nsIID& aIID, void** aSink) {
5110 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
5111 NS_ENSURE_TRUE(outer, NS_ERROR_NOT_INITIALIZED);
5113 nsresult rv = outer->GetInterfaceInternal(aIID, aSink);
5114 if (rv == NS_ERROR_NO_INTERFACE) {
5115 return QueryInterface(aIID, aSink);
5117 return rv;
5120 void nsGlobalWindowInner::GetInterface(JSContext* aCx,
5121 JS::Handle<JS::Value> aIID,
5122 JS::MutableHandle<JS::Value> aRetval,
5123 ErrorResult& aError) {
5124 dom::GetInterface(aCx, this, aIID, aRetval, aError);
5127 already_AddRefed<CacheStorage> nsGlobalWindowInner::GetCaches(
5128 ErrorResult& aRv) {
5129 if (!mCacheStorage) {
5130 bool forceTrustedOrigin =
5131 GetBrowsingContext() &&
5132 GetBrowsingContext()->Top()->GetServiceWorkersTestingEnabled();
5133 mCacheStorage = CacheStorage::CreateOnMainThread(
5134 cache::DEFAULT_NAMESPACE, this, GetEffectiveStoragePrincipal(),
5135 forceTrustedOrigin, aRv);
5138 RefPtr<CacheStorage> ref = mCacheStorage;
5139 return ref.forget();
5142 void nsGlobalWindowInner::FireOfflineStatusEventIfChanged() {
5143 if (!IsCurrentInnerWindow()) return;
5145 // Don't fire an event if the status hasn't changed
5146 if (mWasOffline == NS_IsOffline()) {
5147 return;
5150 mWasOffline = !mWasOffline;
5152 nsAutoString name;
5153 if (mWasOffline) {
5154 name.AssignLiteral("offline");
5155 } else {
5156 name.AssignLiteral("online");
5158 nsContentUtils::DispatchTrustedEvent(mDoc, static_cast<EventTarget*>(this),
5159 name, CanBubble::eNo, Cancelable::eNo);
5162 nsGlobalWindowInner::SlowScriptResponse
5163 nsGlobalWindowInner::ShowSlowScriptDialog(JSContext* aCx,
5164 const nsString& aAddonId,
5165 const double aDuration) {
5166 nsresult rv;
5168 if (Preferences::GetBool("dom.always_stop_slow_scripts")) {
5169 return KillSlowScript;
5172 // If it isn't safe to run script, then it isn't safe to bring up the prompt
5173 // (since that spins the event loop). In that (rare) case, we just kill the
5174 // script and report a warning.
5175 if (!nsContentUtils::IsSafeToRunScript()) {
5176 JS::WarnASCII(aCx, "A long running script was terminated");
5177 return KillSlowScript;
5180 // If our document is not active, just kill the script: we've been unloaded
5181 if (!HasActiveDocument()) {
5182 return KillSlowScript;
5185 // Check if we should offer the option to debug
5186 JS::AutoFilename filename;
5187 unsigned lineno;
5188 // Computing the line number can be very expensive (see bug 1330231 for
5189 // example), and we don't use the line number anywhere except than in the
5190 // parent process, so we avoid computing it elsewhere. This gives us most of
5191 // the wins we are interested in, since the source of the slowness here is
5192 // minified scripts which is more common in Web content that is loaded in the
5193 // content process.
5194 unsigned* linenop = XRE_IsParentProcess() ? &lineno : nullptr;
5195 bool hasFrame = JS::DescribeScriptedCaller(aCx, &filename, linenop);
5197 // Record the slow script event if we haven't done so already for this inner
5198 // window (which represents a particular page to the user).
5199 if (!mHasHadSlowScript) {
5200 Telemetry::Accumulate(Telemetry::SLOW_SCRIPT_PAGE_COUNT, 1);
5202 mHasHadSlowScript = true;
5204 // Override the cursor to something that we're sure the user can see.
5205 SetCursor("auto"_ns, IgnoreErrors());
5207 if (XRE_IsContentProcess() && ProcessHangMonitor::Get()) {
5208 ProcessHangMonitor::SlowScriptAction action;
5209 RefPtr<ProcessHangMonitor> monitor = ProcessHangMonitor::Get();
5210 nsIDocShell* docShell = GetDocShell();
5211 nsCOMPtr<nsIBrowserChild> child =
5212 docShell ? docShell->GetBrowserChild() : nullptr;
5213 action =
5214 monitor->NotifySlowScript(child, filename.get(), aAddonId, aDuration);
5215 if (action == ProcessHangMonitor::Terminate) {
5216 return KillSlowScript;
5219 if (action == ProcessHangMonitor::StartDebugger) {
5220 // Spin a nested event loop so that the debugger in the parent can fetch
5221 // any information it needs. Once the debugger has started, return to the
5222 // script.
5223 RefPtr<nsGlobalWindowOuter> outer = GetOuterWindowInternal();
5224 outer->EnterModalState();
5225 SpinEventLoopUntil("nsGlobalWindowInner::ShowSlowScriptDialog"_ns, [&]() {
5226 return monitor->IsDebuggerStartupComplete();
5228 outer->LeaveModalState();
5229 return ContinueSlowScript;
5232 return ContinueSlowScriptAndKeepNotifying;
5235 // Reached only on non-e10s - once per slow script dialog.
5236 // On e10s - we probe once at ProcessHangsMonitor.jsm
5237 Telemetry::Accumulate(Telemetry::SLOW_SCRIPT_NOTICE_COUNT, 1);
5239 // Get the nsIPrompt interface from the docshell
5240 nsCOMPtr<nsIDocShell> ds = GetDocShell();
5241 NS_ENSURE_TRUE(ds, KillSlowScript);
5242 nsCOMPtr<nsIPrompt> prompt = do_GetInterface(ds);
5243 NS_ENSURE_TRUE(prompt, KillSlowScript);
5245 // Prioritize the SlowScriptDebug interface over JSD1.
5246 nsCOMPtr<nsISlowScriptDebugCallback> debugCallback;
5248 if (hasFrame) {
5249 const char* debugCID = "@mozilla.org/dom/slow-script-debug;1";
5250 nsCOMPtr<nsISlowScriptDebug> debugService = do_GetService(debugCID, &rv);
5251 if (NS_SUCCEEDED(rv)) {
5252 debugService->GetActivationHandler(getter_AddRefs(debugCallback));
5256 bool failed = false;
5257 auto getString = [&](const char* name,
5258 nsContentUtils::PropertiesFile propFile =
5259 nsContentUtils::eDOM_PROPERTIES) {
5260 nsAutoString result;
5261 nsresult rv = nsContentUtils::GetLocalizedString(propFile, name, result);
5263 // GetStringFromName can return NS_OK and still give nullptr string
5264 failed = failed || NS_FAILED(rv) || result.IsEmpty();
5265 return result;
5268 bool isAddonScript = !aAddonId.IsEmpty();
5269 bool showDebugButton = debugCallback && !isAddonScript;
5271 // Get localizable strings
5273 nsAutoString title, checkboxMsg, debugButton, msg;
5274 if (isAddonScript) {
5275 title = getString("KillAddonScriptTitle");
5276 checkboxMsg = getString("KillAddonScriptGlobalMessage");
5278 auto appName =
5279 getString("brandShortName", nsContentUtils::eBRAND_PROPERTIES);
5281 nsCOMPtr<nsIAddonPolicyService> aps =
5282 do_GetService("@mozilla.org/addons/policy-service;1");
5283 nsString addonName;
5284 if (!aps || NS_FAILED(aps->GetExtensionName(aAddonId, addonName))) {
5285 addonName = aAddonId;
5288 rv = nsContentUtils::FormatLocalizedString(
5289 msg, nsContentUtils::eDOM_PROPERTIES, "KillAddonScriptMessage",
5290 addonName, appName);
5292 failed = failed || NS_FAILED(rv);
5293 } else {
5294 title = getString("KillScriptTitle");
5295 checkboxMsg = getString("DontAskAgain");
5297 if (showDebugButton) {
5298 debugButton = getString("DebugScriptButton");
5299 msg = getString("KillScriptWithDebugMessage");
5300 } else {
5301 msg = getString("KillScriptMessage");
5305 auto stopButton = getString("StopScriptButton");
5306 auto waitButton = getString("WaitForScriptButton");
5308 if (failed) {
5309 NS_ERROR("Failed to get localized strings.");
5310 return ContinueSlowScript;
5313 // Append file and line number information, if available
5314 if (filename.get()) {
5315 nsAutoString scriptLocation;
5316 // We want to drop the middle part of too-long locations. We'll
5317 // define "too-long" as longer than 60 UTF-16 code units. Just
5318 // have to be a bit careful about unpaired surrogates.
5319 NS_ConvertUTF8toUTF16 filenameUTF16(filename.get());
5320 if (filenameUTF16.Length() > 60) {
5321 // XXXbz Do we need to insert any bidi overrides here?
5322 size_t cutStart = 30;
5323 size_t cutLength = filenameUTF16.Length() - 60;
5324 MOZ_ASSERT(cutLength > 0);
5325 if (NS_IS_LOW_SURROGATE(filenameUTF16[cutStart])) {
5326 // Don't truncate before the low surrogate, in case it's preceded by a
5327 // high surrogate and forms a single Unicode character. Instead, just
5328 // include the low surrogate.
5329 ++cutStart;
5330 --cutLength;
5332 if (NS_IS_LOW_SURROGATE(filenameUTF16[cutStart + cutLength])) {
5333 // Likewise, don't drop a trailing low surrogate here. We want to
5334 // increase cutLength, since it might be 0 already so we can't very well
5335 // decrease it.
5336 ++cutLength;
5339 // Insert U+2026 HORIZONTAL ELLIPSIS
5340 filenameUTF16.ReplaceLiteral(cutStart, cutLength, u"\x2026");
5342 rv = nsContentUtils::FormatLocalizedString(
5343 scriptLocation, nsContentUtils::eDOM_PROPERTIES, "KillScriptLocation",
5344 filenameUTF16);
5346 if (NS_SUCCEEDED(rv)) {
5347 msg.AppendLiteral("\n\n");
5348 msg.Append(scriptLocation);
5349 msg.Append(':');
5350 msg.AppendInt(lineno);
5354 uint32_t buttonFlags = nsIPrompt::BUTTON_POS_1_DEFAULT +
5355 (nsIPrompt::BUTTON_TITLE_IS_STRING *
5356 (nsIPrompt::BUTTON_POS_0 + nsIPrompt::BUTTON_POS_1));
5358 // Add a third button if necessary.
5359 if (showDebugButton)
5360 buttonFlags += nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_2;
5362 bool checkboxValue = false;
5363 int32_t buttonPressed = 0; // In case the user exits dialog by clicking X.
5365 // Null out the operation callback while we're re-entering JS here.
5366 AutoDisableJSInterruptCallback disabler(aCx);
5368 // Open the dialog.
5369 rv = prompt->ConfirmEx(
5370 title.get(), msg.get(), buttonFlags, waitButton.get(), stopButton.get(),
5371 debugButton.get(), checkboxMsg.get(), &checkboxValue, &buttonPressed);
5374 if (buttonPressed == 0) {
5375 if (checkboxValue && !isAddonScript && NS_SUCCEEDED(rv))
5376 return AlwaysContinueSlowScript;
5377 return ContinueSlowScript;
5380 if (buttonPressed == 2) {
5381 MOZ_RELEASE_ASSERT(debugCallback);
5383 rv = debugCallback->HandleSlowScriptDebug(this);
5384 return NS_SUCCEEDED(rv) ? ContinueSlowScript : KillSlowScript;
5387 JS_ClearPendingException(aCx);
5389 return KillSlowScript;
5392 nsresult nsGlobalWindowInner::Observe(nsISupports* aSubject, const char* aTopic,
5393 const char16_t* aData) {
5394 if (!nsCRT::strcmp(aTopic, NS_IOSERVICE_OFFLINE_STATUS_TOPIC)) {
5395 if (!IsFrozen()) {
5396 // Fires an offline status event if the offline status has changed
5397 FireOfflineStatusEventIfChanged();
5399 return NS_OK;
5402 if (!nsCRT::strcmp(aTopic, MEMORY_PRESSURE_OBSERVER_TOPIC)) {
5403 if (mPerformance) {
5404 mPerformance->MemoryPressure();
5406 RemoveReportRecords();
5407 return NS_OK;
5410 if (!nsCRT::strcmp(aTopic, PERMISSION_CHANGED_TOPIC)) {
5411 nsCOMPtr<nsIPermission> perm(do_QueryInterface(aSubject));
5412 if (!perm) {
5413 // A null permission indicates that the entire permission list
5414 // was cleared.
5415 MOZ_ASSERT(!nsCRT::strcmp(aData, u"cleared"));
5416 UpdatePermissions();
5417 return NS_OK;
5420 nsAutoCString type;
5421 perm->GetType(type);
5422 if (type == "autoplay-media"_ns) {
5423 UpdateAutoplayPermission();
5424 } else if (type == "shortcuts"_ns) {
5425 UpdateShortcutsPermission();
5426 } else if (type == "popup"_ns) {
5427 UpdatePopupPermission();
5430 if (!mDoc) {
5431 return NS_OK;
5434 RefPtr<PermissionDelegateHandler> permDelegateHandler =
5435 mDoc->GetPermissionDelegateHandler();
5437 if (permDelegateHandler) {
5438 permDelegateHandler->UpdateDelegatedPermission(type);
5441 return NS_OK;
5444 if (!nsCRT::strcmp(aTopic, "screen-information-changed")) {
5445 if (mScreen) {
5446 if (RefPtr<ScreenOrientation> orientation =
5447 mScreen->GetOrientationIfExists()) {
5448 orientation->MaybeChanged();
5451 if (mHasOrientationChangeListeners) {
5452 int32_t oldAngle = mOrientationAngle;
5453 mOrientationAngle = Orientation(CallerType::System);
5454 if (mOrientationAngle != oldAngle && IsCurrentInnerWindow()) {
5455 nsCOMPtr<nsPIDOMWindowOuter> outer = GetOuterWindow();
5456 outer->DispatchCustomEvent(u"orientationchange"_ns);
5459 return NS_OK;
5462 if (!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
5463 MOZ_ASSERT(!NS_strcmp(aData, u"intl.accept_languages"));
5465 // The user preferred languages have changed, we need to fire an event on
5466 // Window object and invalidate the cache for navigator.languages. It is
5467 // done for every change which can be a waste of cycles but those should be
5468 // fairly rare.
5469 // We MUST invalidate navigator.languages before sending the event in the
5470 // very likely situation where an event handler will try to read its value.
5472 if (mNavigator) {
5473 Navigator_Binding::ClearCachedLanguageValue(mNavigator);
5474 Navigator_Binding::ClearCachedLanguagesValue(mNavigator);
5477 // The event has to be dispatched only to the current inner window.
5478 if (!IsCurrentInnerWindow()) {
5479 return NS_OK;
5482 RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
5483 event->InitEvent(u"languagechange"_ns, false, false);
5484 event->SetTrusted(true);
5486 ErrorResult rv;
5487 DispatchEvent(*event, rv);
5488 return rv.StealNSResult();
5491 NS_WARNING("unrecognized topic in nsGlobalWindowInner::Observe");
5492 return NS_ERROR_FAILURE;
5495 void nsGlobalWindowInner::ObserveStorageNotification(
5496 StorageEvent* aEvent, const char16_t* aStorageType, bool aPrivateBrowsing) {
5497 MOZ_ASSERT(aEvent);
5499 // The private browsing check must be done here again because this window
5500 // could have changed its state before the notification check and now. This
5501 // happens in case this window did have a docShell at that time.
5502 if (aPrivateBrowsing != IsPrivateBrowsing()) {
5503 return;
5506 // LocalStorage can only exist on an inner window, and we don't want to
5507 // generate events on frozen or otherwise-navigated-away from windows.
5508 // (Actually, this code used to try and buffer events for frozen windows,
5509 // but it never worked, so we've removed it. See bug 1285898.)
5510 if (!IsCurrentInnerWindow() || IsFrozen()) {
5511 return;
5514 nsIPrincipal* principal = GetPrincipal();
5515 if (!principal) {
5516 return;
5519 bool fireMozStorageChanged = false;
5520 nsAutoString eventType;
5521 eventType.AssignLiteral("storage");
5523 if (!NS_strcmp(aStorageType, u"sessionStorage")) {
5524 RefPtr<Storage> changingStorage = aEvent->GetStorageArea();
5525 MOZ_ASSERT(changingStorage);
5527 bool check = false;
5529 if (const RefPtr<SessionStorageManager> storageManager =
5530 GetBrowsingContext()->GetSessionStorageManager()) {
5531 nsresult rv = storageManager->CheckStorage(GetEffectiveStoragePrincipal(),
5532 changingStorage, &check);
5533 if (NS_FAILED(rv)) {
5534 return;
5538 if (!check) {
5539 // This storage event is not coming from our storage or is coming
5540 // from a different docshell, i.e. it is a clone, ignore this event.
5541 return;
5544 MOZ_LOG(
5545 gDOMLeakPRLogInner, LogLevel::Debug,
5546 ("nsGlobalWindowInner %p with sessionStorage %p passing event from %p",
5547 this, mSessionStorage.get(), changingStorage.get()));
5549 fireMozStorageChanged = mSessionStorage == changingStorage;
5550 if (fireMozStorageChanged) {
5551 eventType.AssignLiteral("MozSessionStorageChanged");
5555 else {
5556 MOZ_ASSERT(!NS_strcmp(aStorageType, u"localStorage"));
5558 nsIPrincipal* storagePrincipal = GetEffectiveStoragePrincipal();
5559 if (!storagePrincipal) {
5560 return;
5563 MOZ_DIAGNOSTIC_ASSERT(StorageUtils::PrincipalsEqual(aEvent->GetPrincipal(),
5564 storagePrincipal));
5566 fireMozStorageChanged =
5567 mLocalStorage && mLocalStorage == aEvent->GetStorageArea();
5569 if (fireMozStorageChanged) {
5570 eventType.AssignLiteral("MozLocalStorageChanged");
5574 // Clone the storage event included in the observer notification. We want
5575 // to dispatch clones rather than the original event.
5576 IgnoredErrorResult error;
5577 RefPtr<StorageEvent> clonedEvent =
5578 CloneStorageEvent(eventType, aEvent, error);
5579 if (error.Failed() || !clonedEvent) {
5580 return;
5583 clonedEvent->SetTrusted(true);
5585 if (fireMozStorageChanged) {
5586 WidgetEvent* internalEvent = clonedEvent->WidgetEventPtr();
5587 internalEvent->mFlags.mOnlyChromeDispatch = true;
5590 DispatchEvent(*clonedEvent);
5593 already_AddRefed<StorageEvent> nsGlobalWindowInner::CloneStorageEvent(
5594 const nsAString& aType, const RefPtr<StorageEvent>& aEvent,
5595 ErrorResult& aRv) {
5596 StorageEventInit dict;
5598 dict.mBubbles = aEvent->Bubbles();
5599 dict.mCancelable = aEvent->Cancelable();
5600 aEvent->GetKey(dict.mKey);
5601 aEvent->GetOldValue(dict.mOldValue);
5602 aEvent->GetNewValue(dict.mNewValue);
5603 aEvent->GetUrl(dict.mUrl);
5605 RefPtr<Storage> storageArea = aEvent->GetStorageArea();
5607 RefPtr<Storage> storage;
5609 // If null, this is a localStorage event received by IPC.
5610 if (!storageArea) {
5611 storage = GetLocalStorage(aRv);
5612 if (!NextGenLocalStorageEnabled()) {
5613 if (aRv.Failed() || !storage) {
5614 return nullptr;
5617 if (storage->Type() == Storage::eLocalStorage) {
5618 RefPtr<LocalStorage> localStorage =
5619 static_cast<LocalStorage*>(storage.get());
5621 // We must apply the current change to the 'local' localStorage.
5622 localStorage->ApplyEvent(aEvent);
5625 } else if (storageArea->Type() == Storage::eSessionStorage) {
5626 storage = GetSessionStorage(aRv);
5627 } else {
5628 MOZ_ASSERT(storageArea->Type() == Storage::eLocalStorage);
5629 storage = GetLocalStorage(aRv);
5632 if (aRv.Failed() || !storage) {
5633 return nullptr;
5636 if (storage->Type() == Storage::ePartitionedLocalStorage) {
5637 // This error message is not exposed.
5638 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
5639 return nullptr;
5642 MOZ_ASSERT(storage);
5643 MOZ_ASSERT_IF(storageArea, storage->IsForkOf(storageArea));
5645 dict.mStorageArea = storage;
5647 RefPtr<StorageEvent> event = StorageEvent::Constructor(this, aType, dict);
5648 return event.forget();
5651 void nsGlobalWindowInner::Suspend(bool aIncludeSubWindows) {
5652 MOZ_ASSERT(NS_IsMainThread());
5654 // We can only safely suspend windows that are the current inner window. If
5655 // its not the current inner, then we are in one of two different cases.
5656 // Either we are in the bfcache or we are doomed window that is going away.
5657 // When a window becomes inactive we purposely avoid placing already suspended
5658 // windows into the bfcache. It only expects windows suspended due to the
5659 // Freeze() method which occurs while the window is still the current inner.
5660 // So we must not call Suspend() on bfcache windows at this point or this
5661 // invariant will be broken. If the window is doomed there is no point in
5662 // suspending it since it will soon be gone.
5663 if (!IsCurrentInnerWindow()) {
5664 return;
5667 // All in-process descendants are also suspended. This ensure mSuspendDepth
5668 // is set properly and the timers are properly canceled for each in-process
5669 // descendant.
5670 if (aIncludeSubWindows) {
5671 CallOnInProcessDescendants(&nsGlobalWindowInner::Suspend, false);
5674 mSuspendDepth += 1;
5675 if (mSuspendDepth != 1) {
5676 return;
5679 if (mWindowGlobalChild) {
5680 mWindowGlobalChild->BlockBFCacheFor(BFCacheStatus::SUSPENDED);
5683 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
5684 if (ac) {
5685 for (uint32_t i = 0; i < mEnabledSensors.Length(); i++)
5686 ac->RemoveWindowListener(mEnabledSensors[i], this);
5688 DisableGamepadUpdates();
5689 DisableVRUpdates();
5691 SuspendWorkersForWindow(*this);
5693 for (RefPtr<mozilla::dom::SharedWorker> pinnedWorker :
5694 mSharedWorkers.ForwardRange()) {
5695 pinnedWorker->Suspend();
5698 SuspendIdleRequests();
5700 mTimeoutManager->Suspend();
5702 // Suspend all of the AudioContexts for this window
5703 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
5704 mAudioContexts[i]->SuspendFromChrome();
5708 void nsGlobalWindowInner::Resume(bool aIncludeSubWindows) {
5709 MOZ_ASSERT(NS_IsMainThread());
5711 // We can only safely resume a window if its the current inner window. If
5712 // its not the current inner, then we are in one of two different cases.
5713 // Either we are in the bfcache or we are doomed window that is going away.
5714 // If a window is suspended when it becomes inactive we purposely do not
5715 // put it in the bfcache, so Resume should never be needed in that case.
5716 // If the window is doomed then there is no point in resuming it.
5717 if (!IsCurrentInnerWindow()) {
5718 return;
5721 // Resume all in-process descendants. This restores timers recursively
5722 // canceled in Suspend() and ensures all in-process descendants have the
5723 // correct mSuspendDepth.
5724 if (aIncludeSubWindows) {
5725 CallOnInProcessDescendants(&nsGlobalWindowInner::Resume, false);
5728 if (mSuspendDepth == 0) {
5729 // Ignore if the window is not suspended.
5730 return;
5733 mSuspendDepth -= 1;
5735 if (mSuspendDepth != 0) {
5736 return;
5739 // We should not be able to resume a frozen window. It must be Thaw()'d
5740 // first.
5741 MOZ_ASSERT(mFreezeDepth == 0);
5743 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
5744 if (ac) {
5745 for (uint32_t i = 0; i < mEnabledSensors.Length(); i++)
5746 ac->AddWindowListener(mEnabledSensors[i], this);
5748 EnableGamepadUpdates();
5749 EnableVRUpdates();
5751 // Resume all of the AudioContexts for this window
5752 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
5753 mAudioContexts[i]->ResumeFromChrome();
5756 if (RefPtr<MediaDevices> devices = GetExtantMediaDevices()) {
5757 devices->WindowResumed();
5760 mTimeoutManager->Resume();
5762 ResumeIdleRequests();
5764 // Resume all of the workers for this window. We must do this
5765 // after timeouts since workers may have queued events that can trigger
5766 // a setTimeout().
5767 ResumeWorkersForWindow(*this);
5769 for (RefPtr<mozilla::dom::SharedWorker> pinnedWorker :
5770 mSharedWorkers.ForwardRange()) {
5771 pinnedWorker->Resume();
5774 if (mWindowGlobalChild) {
5775 mWindowGlobalChild->UnblockBFCacheFor(BFCacheStatus::SUSPENDED);
5779 bool nsGlobalWindowInner::IsSuspended() const {
5780 MOZ_ASSERT(NS_IsMainThread());
5781 return mSuspendDepth != 0;
5784 void nsGlobalWindowInner::Freeze(bool aIncludeSubWindows) {
5785 MOZ_ASSERT(NS_IsMainThread());
5786 Suspend(aIncludeSubWindows);
5787 FreezeInternal(aIncludeSubWindows);
5790 void nsGlobalWindowInner::FreezeInternal(bool aIncludeSubWindows) {
5791 MOZ_ASSERT(NS_IsMainThread());
5792 MOZ_DIAGNOSTIC_ASSERT(IsCurrentInnerWindow());
5793 MOZ_DIAGNOSTIC_ASSERT(IsSuspended());
5795 HintIsLoading(false);
5797 if (aIncludeSubWindows) {
5798 CallOnInProcessChildren(&nsGlobalWindowInner::FreezeInternal,
5799 aIncludeSubWindows);
5802 mFreezeDepth += 1;
5803 MOZ_ASSERT(mSuspendDepth >= mFreezeDepth);
5804 if (mFreezeDepth != 1) {
5805 return;
5808 FreezeWorkersForWindow(*this);
5810 for (RefPtr<mozilla::dom::SharedWorker> pinnedWorker :
5811 mSharedWorkers.ForwardRange()) {
5812 pinnedWorker->Freeze();
5815 mTimeoutManager->Freeze();
5816 if (mClientSource) {
5817 mClientSource->Freeze();
5820 NotifyDOMWindowFrozen(this);
5823 void nsGlobalWindowInner::Thaw(bool aIncludeSubWindows) {
5824 MOZ_ASSERT(NS_IsMainThread());
5825 ThawInternal(aIncludeSubWindows);
5826 Resume(aIncludeSubWindows);
5829 void nsGlobalWindowInner::ThawInternal(bool aIncludeSubWindows) {
5830 MOZ_ASSERT(NS_IsMainThread());
5831 MOZ_DIAGNOSTIC_ASSERT(IsCurrentInnerWindow());
5832 MOZ_DIAGNOSTIC_ASSERT(IsSuspended());
5834 if (aIncludeSubWindows) {
5835 CallOnInProcessChildren(&nsGlobalWindowInner::ThawInternal,
5836 aIncludeSubWindows);
5839 MOZ_ASSERT(mFreezeDepth != 0);
5840 mFreezeDepth -= 1;
5841 MOZ_ASSERT(mSuspendDepth >= mFreezeDepth);
5842 if (mFreezeDepth != 0) {
5843 return;
5846 if (mClientSource) {
5847 mClientSource->Thaw();
5849 mTimeoutManager->Thaw();
5851 ThawWorkersForWindow(*this);
5853 for (RefPtr<mozilla::dom::SharedWorker> pinnedWorker :
5854 mSharedWorkers.ForwardRange()) {
5855 pinnedWorker->Thaw();
5858 NotifyDOMWindowThawed(this);
5861 bool nsGlobalWindowInner::IsFrozen() const {
5862 MOZ_ASSERT(NS_IsMainThread());
5863 bool frozen = mFreezeDepth != 0;
5864 MOZ_ASSERT_IF(frozen, IsSuspended());
5865 return frozen;
5868 void nsGlobalWindowInner::SyncStateFromParentWindow() {
5869 // This method should only be called on an inner window that has been
5870 // assigned to an outer window already.
5871 MOZ_ASSERT(IsCurrentInnerWindow());
5872 nsPIDOMWindowOuter* outer = GetOuterWindow();
5873 MOZ_ASSERT(outer);
5875 // Attempt to find our parent windows.
5876 nsCOMPtr<Element> frame = outer->GetFrameElementInternal();
5877 nsPIDOMWindowOuter* parentOuter =
5878 frame ? frame->OwnerDoc()->GetWindow() : nullptr;
5879 nsGlobalWindowInner* parentInner =
5880 parentOuter
5881 ? nsGlobalWindowInner::Cast(parentOuter->GetCurrentInnerWindow())
5882 : nullptr;
5884 // If our outer is in a modal state, but our parent is not in a modal
5885 // state, then we must apply the suspend directly. If our parent is
5886 // in a modal state then we should get the suspend automatically
5887 // via the parentSuspendDepth application below.
5888 if ((!parentInner || !parentInner->IsInModalState()) && IsInModalState()) {
5889 Suspend();
5892 uint32_t parentFreezeDepth = parentInner ? parentInner->mFreezeDepth : 0;
5893 uint32_t parentSuspendDepth = parentInner ? parentInner->mSuspendDepth : 0;
5895 // Since every Freeze() calls Suspend(), the suspend count must
5896 // be equal or greater to the freeze count.
5897 MOZ_ASSERT(parentFreezeDepth <= parentSuspendDepth);
5899 // First apply the Freeze() calls.
5900 for (uint32_t i = 0; i < parentFreezeDepth; ++i) {
5901 Freeze();
5904 // Now apply only the number of Suspend() calls to reach the target
5905 // suspend count after applying the Freeze() calls.
5906 for (uint32_t i = 0; i < (parentSuspendDepth - parentFreezeDepth); ++i) {
5907 Suspend();
5911 void nsGlobalWindowInner::UpdateBackgroundState() {
5912 if (RefPtr<MediaDevices> devices = GetExtantMediaDevices()) {
5913 devices->BackgroundStateChanged();
5915 mTimeoutManager->UpdateBackgroundState();
5918 template <typename Method, typename... Args>
5919 CallState nsGlobalWindowInner::CallOnInProcessDescendantsInternal(
5920 BrowsingContext* aBrowsingContext, bool aChildOnly, Method aMethod,
5921 Args&&... aArgs) {
5922 MOZ_ASSERT(NS_IsMainThread());
5923 MOZ_ASSERT(aBrowsingContext);
5925 CallState state = CallState::Continue;
5926 for (const RefPtr<BrowsingContext>& bc : aBrowsingContext->Children()) {
5927 if (nsCOMPtr<nsPIDOMWindowOuter> pWin = bc->GetDOMWindow()) {
5928 auto* win = nsGlobalWindowOuter::Cast(pWin);
5929 if (nsGlobalWindowInner* inner = win->GetCurrentInnerWindowInternal()) {
5930 // Call the descendant method using our helper CallDescendant() template
5931 // method. This allows us to handle both void returning methods and
5932 // methods that return CallState explicitly. For void returning methods
5933 // we assume CallState::Continue.
5934 using returnType = decltype((inner->*aMethod)(aArgs...));
5935 state = CallDescendant<returnType>(inner, aMethod, aArgs...);
5937 if (state == CallState::Stop) {
5938 return state;
5943 if (!aChildOnly) {
5944 state = CallOnInProcessDescendantsInternal(bc.get(), aChildOnly, aMethod,
5945 aArgs...);
5946 if (state == CallState::Stop) {
5947 return state;
5952 return state;
5955 Maybe<ClientInfo> nsGlobalWindowInner::GetClientInfo() const {
5956 MOZ_ASSERT(NS_IsMainThread());
5957 if (mDoc && mDoc->IsStaticDocument()) {
5958 if (Maybe<ClientInfo> info = mDoc->GetOriginalDocument()->GetClientInfo()) {
5959 return info;
5963 Maybe<ClientInfo> clientInfo;
5964 if (mClientSource) {
5965 clientInfo.emplace(mClientSource->Info());
5967 return clientInfo;
5970 Maybe<ClientState> nsGlobalWindowInner::GetClientState() const {
5971 MOZ_ASSERT(NS_IsMainThread());
5972 if (mDoc && mDoc->IsStaticDocument()) {
5973 if (Maybe<ClientState> state =
5974 mDoc->GetOriginalDocument()->GetClientState()) {
5975 return state;
5979 Maybe<ClientState> clientState;
5980 if (mClientSource) {
5981 Result<ClientState, ErrorResult> res = mClientSource->SnapshotState();
5982 if (res.isOk()) {
5983 clientState.emplace(res.unwrap());
5984 } else {
5985 res.unwrapErr().SuppressException();
5988 return clientState;
5991 Maybe<ServiceWorkerDescriptor> nsGlobalWindowInner::GetController() const {
5992 MOZ_ASSERT(NS_IsMainThread());
5993 if (mDoc && mDoc->IsStaticDocument()) {
5994 if (Maybe<ServiceWorkerDescriptor> controller =
5995 mDoc->GetOriginalDocument()->GetController()) {
5996 return controller;
6000 Maybe<ServiceWorkerDescriptor> controller;
6001 if (mClientSource) {
6002 controller = mClientSource->GetController();
6004 return controller;
6007 void nsGlobalWindowInner::SetCsp(nsIContentSecurityPolicy* aCsp) {
6008 if (!mClientSource) {
6009 return;
6011 mClientSource->SetCsp(aCsp);
6012 // Also cache the CSP within the document
6013 mDoc->SetCsp(aCsp);
6015 if (mWindowGlobalChild) {
6016 mWindowGlobalChild->SendSetClientInfo(mClientSource->Info().ToIPC());
6020 void nsGlobalWindowInner::SetPreloadCsp(nsIContentSecurityPolicy* aPreloadCsp) {
6021 if (!mClientSource) {
6022 return;
6024 mClientSource->SetPreloadCsp(aPreloadCsp);
6025 // Also cache the preload CSP within the document
6026 mDoc->SetPreloadCsp(aPreloadCsp);
6028 if (mWindowGlobalChild) {
6029 mWindowGlobalChild->SendSetClientInfo(mClientSource->Info().ToIPC());
6033 nsIContentSecurityPolicy* nsGlobalWindowInner::GetCsp() {
6034 if (mDoc) {
6035 return mDoc->GetCsp();
6038 // If the window is partially torn down and has its document nulled out,
6039 // we query the CSP we snapshot in FreeInnerObjects.
6040 if (mDocumentCsp) {
6041 return mDocumentCsp;
6043 return nullptr;
6046 RefPtr<ServiceWorker> nsGlobalWindowInner::GetOrCreateServiceWorker(
6047 const ServiceWorkerDescriptor& aDescriptor) {
6048 MOZ_ASSERT(NS_IsMainThread());
6049 RefPtr<ServiceWorker> ref;
6050 ForEachEventTargetObject([&](DOMEventTargetHelper* aTarget, bool* aDoneOut) {
6051 RefPtr<ServiceWorker> sw = do_QueryObject(aTarget);
6052 if (!sw || !sw->Descriptor().Matches(aDescriptor)) {
6053 return;
6056 ref = std::move(sw);
6057 *aDoneOut = true;
6060 if (!ref) {
6061 ref = ServiceWorker::Create(this, aDescriptor);
6064 return ref;
6067 RefPtr<mozilla::dom::ServiceWorkerRegistration>
6068 nsGlobalWindowInner::GetServiceWorkerRegistration(
6069 const mozilla::dom::ServiceWorkerRegistrationDescriptor& aDescriptor)
6070 const {
6071 MOZ_ASSERT(NS_IsMainThread());
6072 RefPtr<ServiceWorkerRegistration> ref;
6073 ForEachEventTargetObject([&](DOMEventTargetHelper* aTarget, bool* aDoneOut) {
6074 RefPtr<ServiceWorkerRegistration> swr = do_QueryObject(aTarget);
6075 if (!swr || !swr->MatchesDescriptor(aDescriptor)) {
6076 return;
6079 ref = std::move(swr);
6080 *aDoneOut = true;
6082 return ref;
6085 RefPtr<ServiceWorkerRegistration>
6086 nsGlobalWindowInner::GetOrCreateServiceWorkerRegistration(
6087 const ServiceWorkerRegistrationDescriptor& aDescriptor) {
6088 MOZ_ASSERT(NS_IsMainThread());
6089 RefPtr<ServiceWorkerRegistration> ref =
6090 GetServiceWorkerRegistration(aDescriptor);
6091 if (!ref) {
6092 ref = ServiceWorkerRegistration::CreateForMainThread(this, aDescriptor);
6094 return ref;
6097 StorageAccess nsGlobalWindowInner::GetStorageAccess() {
6098 return StorageAllowedForWindow(this);
6101 nsresult nsGlobalWindowInner::FireDelayedDOMEvents(bool aIncludeSubWindows) {
6102 // Fires an offline status event if the offline status has changed
6103 FireOfflineStatusEventIfChanged();
6105 if (!aIncludeSubWindows) {
6106 return NS_OK;
6109 nsCOMPtr<nsIDocShell> docShell = GetDocShell();
6110 if (docShell) {
6111 int32_t childCount = 0;
6112 docShell->GetInProcessChildCount(&childCount);
6114 // Take a copy of the current children so that modifications to
6115 // the child list don't affect to the iteration.
6116 AutoTArray<nsCOMPtr<nsIDocShellTreeItem>, 8> children;
6117 for (int32_t i = 0; i < childCount; ++i) {
6118 nsCOMPtr<nsIDocShellTreeItem> childShell;
6119 docShell->GetInProcessChildAt(i, getter_AddRefs(childShell));
6120 if (childShell) {
6121 children.AppendElement(childShell);
6125 for (nsCOMPtr<nsIDocShellTreeItem> childShell : children) {
6126 if (nsCOMPtr<nsPIDOMWindowOuter> pWin = childShell->GetWindow()) {
6127 auto* win = nsGlobalWindowOuter::Cast(pWin);
6128 win->FireDelayedDOMEvents(true);
6133 return NS_OK;
6136 //*****************************************************************************
6137 // nsGlobalWindowInner: Window Control Functions
6138 //*****************************************************************************
6140 nsPIDOMWindowOuter* nsGlobalWindowInner::GetInProcessParentInternal() {
6141 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
6142 if (!outer) {
6143 // No outer window available!
6144 return nullptr;
6146 return outer->GetInProcessParentInternal();
6149 nsIPrincipal* nsGlobalWindowInner::GetTopLevelAntiTrackingPrincipal() {
6150 nsPIDOMWindowOuter* outerWindow = GetOuterWindowInternal();
6151 if (!outerWindow) {
6152 return nullptr;
6155 nsPIDOMWindowOuter* topLevelOuterWindow =
6156 GetBrowsingContext()->Top()->GetDOMWindow();
6157 if (!topLevelOuterWindow) {
6158 return nullptr;
6161 bool stopAtOurLevel =
6162 mDoc && mDoc->CookieJarSettings()->GetCookieBehavior() ==
6163 nsICookieService::BEHAVIOR_REJECT_TRACKER;
6165 if (stopAtOurLevel && topLevelOuterWindow == outerWindow) {
6166 return nullptr;
6169 nsPIDOMWindowInner* topLevelInnerWindow =
6170 topLevelOuterWindow->GetCurrentInnerWindow();
6171 if (NS_WARN_IF(!topLevelInnerWindow)) {
6172 return nullptr;
6175 nsIPrincipal* topLevelPrincipal =
6176 nsGlobalWindowInner::Cast(topLevelInnerWindow)->GetPrincipal();
6177 if (NS_WARN_IF(!topLevelPrincipal)) {
6178 return nullptr;
6181 return topLevelPrincipal;
6184 nsIPrincipal* nsGlobalWindowInner::GetClientPrincipal() {
6185 return mClientSource ? mClientSource->GetPrincipal() : nullptr;
6188 //*****************************************************************************
6189 // nsGlobalWindowInner: Timeout Functions
6190 //*****************************************************************************
6192 class WindowScriptTimeoutHandler final : public ScriptTimeoutHandler {
6193 public:
6194 NS_DECL_ISUPPORTS_INHERITED
6195 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(WindowScriptTimeoutHandler,
6196 ScriptTimeoutHandler)
6198 WindowScriptTimeoutHandler(JSContext* aCx, nsIGlobalObject* aGlobal,
6199 const nsAString& aExpression)
6200 : ScriptTimeoutHandler(aCx, aGlobal, aExpression),
6201 mInitiatingScript(ScriptLoader::GetActiveScript(aCx)) {}
6203 MOZ_CAN_RUN_SCRIPT virtual bool Call(const char* aExecutionReason) override;
6205 private:
6206 virtual ~WindowScriptTimeoutHandler() = default;
6208 // Initiating script for use when evaluating mExpr on the main thread.
6209 RefPtr<JS::loader::LoadedScript> mInitiatingScript;
6212 NS_IMPL_CYCLE_COLLECTION_INHERITED(WindowScriptTimeoutHandler,
6213 ScriptTimeoutHandler, mInitiatingScript)
6215 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WindowScriptTimeoutHandler)
6216 NS_INTERFACE_MAP_END_INHERITING(ScriptTimeoutHandler)
6218 NS_IMPL_ADDREF_INHERITED(WindowScriptTimeoutHandler, ScriptTimeoutHandler)
6219 NS_IMPL_RELEASE_INHERITED(WindowScriptTimeoutHandler, ScriptTimeoutHandler)
6221 bool WindowScriptTimeoutHandler::Call(const char* aExecutionReason) {
6222 // New script entry point required, due to the "Create a script" sub-step
6223 // of
6224 // http://www.whatwg.org/specs/web-apps/current-work/#timer-initialisation-steps
6225 nsAutoMicroTask mt;
6226 AutoEntryScript aes(mGlobal, aExecutionReason, true);
6227 JS::CompileOptions options(aes.cx());
6228 options.setFileAndLine(mFileName.get(), mLineNo);
6229 options.setNoScriptRval(true);
6230 options.setIntroductionType("domTimer");
6231 JS::Rooted<JSObject*> global(aes.cx(), mGlobal->GetGlobalJSObject());
6233 JSExecutionContext exec(aes.cx(), global, options);
6234 nsresult rv = exec.Compile(mExpr);
6236 JS::Rooted<JSScript*> script(aes.cx(), exec.MaybeGetScript());
6237 if (script) {
6238 if (mInitiatingScript) {
6239 mInitiatingScript->AssociateWithScript(script);
6242 rv = exec.ExecScript();
6245 if (rv == NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW_UNCATCHABLE) {
6246 return false;
6250 return true;
6253 nsGlobalWindowInner* nsGlobalWindowInner::InnerForSetTimeoutOrInterval(
6254 ErrorResult& aError) {
6255 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
6256 nsGlobalWindowInner* currentInner =
6257 outer ? outer->GetCurrentInnerWindowInternal() : this;
6259 // If forwardTo is not the window with an active document then we want the
6260 // call to setTimeout/Interval to be a noop, so return null but don't set an
6261 // error.
6262 return HasActiveDocument() ? currentInner : nullptr;
6265 int32_t nsGlobalWindowInner::SetTimeout(JSContext* aCx, Function& aFunction,
6266 int32_t aTimeout,
6267 const Sequence<JS::Value>& aArguments,
6268 ErrorResult& aError) {
6269 return SetTimeoutOrInterval(aCx, aFunction, aTimeout, aArguments, false,
6270 aError);
6273 int32_t nsGlobalWindowInner::SetTimeout(JSContext* aCx,
6274 const nsAString& aHandler,
6275 int32_t aTimeout,
6276 const Sequence<JS::Value>& /* unused */,
6277 ErrorResult& aError) {
6278 return SetTimeoutOrInterval(aCx, aHandler, aTimeout, false, aError);
6281 int32_t nsGlobalWindowInner::SetInterval(JSContext* aCx, Function& aFunction,
6282 const int32_t aTimeout,
6283 const Sequence<JS::Value>& aArguments,
6284 ErrorResult& aError) {
6285 return SetTimeoutOrInterval(aCx, aFunction, aTimeout, aArguments, true,
6286 aError);
6289 int32_t nsGlobalWindowInner::SetInterval(
6290 JSContext* aCx, const nsAString& aHandler, const int32_t aTimeout,
6291 const Sequence<JS::Value>& /* unused */, ErrorResult& aError) {
6292 return SetTimeoutOrInterval(aCx, aHandler, aTimeout, true, aError);
6295 int32_t nsGlobalWindowInner::SetTimeoutOrInterval(
6296 JSContext* aCx, Function& aFunction, int32_t aTimeout,
6297 const Sequence<JS::Value>& aArguments, bool aIsInterval,
6298 ErrorResult& aError) {
6299 nsGlobalWindowInner* inner = InnerForSetTimeoutOrInterval(aError);
6300 if (!inner) {
6301 return -1;
6304 if (inner != this) {
6305 RefPtr<nsGlobalWindowInner> innerRef(inner);
6306 return innerRef->SetTimeoutOrInterval(aCx, aFunction, aTimeout, aArguments,
6307 aIsInterval, aError);
6310 DebuggerNotificationDispatch(
6311 this, aIsInterval ? DebuggerNotificationType::SetInterval
6312 : DebuggerNotificationType::SetTimeout);
6314 if (!GetContextInternal() || !HasJSGlobal()) {
6315 // This window was already closed, or never properly initialized,
6316 // don't let a timer be scheduled on such a window.
6317 aError.Throw(NS_ERROR_NOT_INITIALIZED);
6318 return 0;
6321 nsTArray<JS::Heap<JS::Value>> args;
6322 if (!args.AppendElements(aArguments, fallible)) {
6323 aError.Throw(NS_ERROR_OUT_OF_MEMORY);
6324 return 0;
6327 RefPtr<TimeoutHandler> handler =
6328 new CallbackTimeoutHandler(aCx, this, &aFunction, std::move(args));
6330 int32_t result;
6331 aError =
6332 mTimeoutManager->SetTimeout(handler, aTimeout, aIsInterval,
6333 Timeout::Reason::eTimeoutOrInterval, &result);
6334 return result;
6337 int32_t nsGlobalWindowInner::SetTimeoutOrInterval(JSContext* aCx,
6338 const nsAString& aHandler,
6339 int32_t aTimeout,
6340 bool aIsInterval,
6341 ErrorResult& aError) {
6342 nsGlobalWindowInner* inner = InnerForSetTimeoutOrInterval(aError);
6343 if (!inner) {
6344 return -1;
6347 if (inner != this) {
6348 RefPtr<nsGlobalWindowInner> innerRef(inner);
6349 return innerRef->SetTimeoutOrInterval(aCx, aHandler, aTimeout, aIsInterval,
6350 aError);
6353 DebuggerNotificationDispatch(
6354 this, aIsInterval ? DebuggerNotificationType::SetInterval
6355 : DebuggerNotificationType::SetTimeout);
6357 if (!GetContextInternal() || !HasJSGlobal()) {
6358 // This window was already closed, or never properly initialized,
6359 // don't let a timer be scheduled on such a window.
6360 aError.Throw(NS_ERROR_NOT_INITIALIZED);
6361 return 0;
6364 bool allowEval = false;
6365 aError = CSPEvalChecker::CheckForWindow(aCx, this, aHandler, &allowEval);
6366 if (NS_WARN_IF(aError.Failed()) || !allowEval) {
6367 return 0;
6370 RefPtr<TimeoutHandler> handler =
6371 new WindowScriptTimeoutHandler(aCx, this, aHandler);
6373 int32_t result;
6374 aError =
6375 mTimeoutManager->SetTimeout(handler, aTimeout, aIsInterval,
6376 Timeout::Reason::eTimeoutOrInterval, &result);
6377 return result;
6380 static const char* GetTimeoutReasonString(Timeout* aTimeout) {
6381 switch (aTimeout->mReason) {
6382 case Timeout::Reason::eTimeoutOrInterval:
6383 if (aTimeout->mIsInterval) {
6384 return "setInterval handler";
6386 return "setTimeout handler";
6387 case Timeout::Reason::eIdleCallbackTimeout:
6388 return "setIdleCallback handler (timed out)";
6389 case Timeout::Reason::eAbortSignalTimeout:
6390 return "AbortSignal timeout";
6391 case Timeout::Reason::eDelayedWebTaskTimeout:
6392 return "delayedWebTaskCallback handler (timed out)";
6393 default:
6394 MOZ_CRASH("Unexpected enum value");
6395 return "";
6397 MOZ_CRASH("Unexpected enum value");
6398 return "";
6401 bool nsGlobalWindowInner::RunTimeoutHandler(Timeout* aTimeout,
6402 nsIScriptContext* aScx) {
6403 // Hold on to the timeout in case mExpr or mFunObj releases its
6404 // doc.
6405 // XXXbz Our caller guarantees it'll hold on to the timeout (because
6406 // we're MOZ_CAN_RUN_SCRIPT), so we can probably stop doing that...
6407 RefPtr<Timeout> timeout = aTimeout;
6408 Timeout* last_running_timeout = mTimeoutManager->BeginRunningTimeout(timeout);
6409 timeout->mRunning = true;
6411 // Push this timeout's popup control state, which should only be
6412 // enabled the first time a timeout fires that was created while
6413 // popups were enabled and with a delay less than
6414 // "dom.disable_open_click_delay".
6415 AutoPopupStatePusher popupStatePusher(timeout->mPopupState);
6417 // Clear the timeout's popup state, if any, to prevent interval
6418 // timeouts from repeatedly opening poups.
6419 timeout->mPopupState = PopupBlocker::openAbused;
6421 uint32_t nestingLevel = TimeoutManager::GetNestingLevel();
6422 TimeoutManager::SetNestingLevel(timeout->mNestingLevel);
6424 const char* reason = GetTimeoutReasonString(timeout);
6426 nsCString str;
6427 if (profiler_thread_is_being_profiled_for_markers()) {
6428 TimeDuration originalInterval = timeout->When() - timeout->SubmitTime();
6429 str.Append(reason);
6430 str.Append(" with interval ");
6431 str.AppendInt(int(originalInterval.ToMilliseconds()));
6432 str.Append("ms: ");
6433 nsCString handlerDescription;
6434 timeout->mScriptHandler->GetDescription(handlerDescription);
6435 str.Append(handlerDescription);
6437 AUTO_PROFILER_MARKER_TEXT("setTimeout callback", DOM,
6438 MarkerOptions(MarkerStack::TakeBacktrace(
6439 timeout->TakeProfilerBacktrace()),
6440 MarkerInnerWindowId(mWindowID)),
6441 str);
6443 bool abortIntervalHandler;
6445 RefPtr<TimeoutHandler> handler(timeout->mScriptHandler);
6447 CallbackDebuggerNotificationGuard guard(
6448 this, timeout->mIsInterval
6449 ? DebuggerNotificationType::SetIntervalCallback
6450 : DebuggerNotificationType::SetTimeoutCallback);
6451 abortIntervalHandler = !handler->Call(reason);
6454 // If we received an uncatchable exception, do not schedule the timeout again.
6455 // This allows the slow script dialog to break easy DoS attacks like
6456 // setInterval(function() { while(1); }, 100);
6457 if (abortIntervalHandler) {
6458 // If it wasn't an interval timer to begin with, this does nothing. If it
6459 // was, we'll treat it as a timeout that we just ran and discard it when
6460 // we return.
6461 timeout->mIsInterval = false;
6464 // We ignore any failures from calling EvaluateString() on the context or
6465 // Call() on a Function here since we're in a loop
6466 // where we're likely to be running timeouts whose OS timers
6467 // didn't fire in time and we don't want to not fire those timers
6468 // now just because execution of one timer failed. We can't
6469 // propagate the error to anyone who cares about it from this
6470 // point anyway, and the script context should have already reported
6471 // the script error in the usual way - so we just drop it.
6473 TimeoutManager::SetNestingLevel(nestingLevel);
6475 mTimeoutManager->EndRunningTimeout(last_running_timeout);
6476 timeout->mRunning = false;
6478 return timeout->mCleared;
6481 //*****************************************************************************
6482 // nsGlobalWindowInner: Helper Functions
6483 //*****************************************************************************
6485 already_AddRefed<nsIDocShellTreeOwner> nsGlobalWindowInner::GetTreeOwner() {
6486 FORWARD_TO_OUTER(GetTreeOwner, (), nullptr);
6489 already_AddRefed<nsIWebBrowserChrome>
6490 nsGlobalWindowInner::GetWebBrowserChrome() {
6491 nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
6493 nsCOMPtr<nsIWebBrowserChrome> browserChrome = do_GetInterface(treeOwner);
6494 return browserChrome.forget();
6497 nsIScrollableFrame* nsGlobalWindowInner::GetScrollFrame() {
6498 FORWARD_TO_OUTER(GetScrollFrame, (), nullptr);
6501 bool nsGlobalWindowInner::IsPrivateBrowsing() {
6502 nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(GetDocShell());
6503 return loadContext && loadContext->UsePrivateBrowsing();
6506 void nsGlobalWindowInner::FlushPendingNotifications(FlushType aType) {
6507 if (mDoc) {
6508 mDoc->FlushPendingNotifications(aType);
6512 void nsGlobalWindowInner::EnableDeviceSensor(uint32_t aType) {
6513 bool alreadyEnabled = false;
6514 for (uint32_t i = 0; i < mEnabledSensors.Length(); i++) {
6515 if (mEnabledSensors[i] == aType) {
6516 alreadyEnabled = true;
6517 break;
6521 mEnabledSensors.AppendElement(aType);
6523 if (alreadyEnabled) {
6524 return;
6527 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
6528 if (ac) {
6529 ac->AddWindowListener(aType, this);
6533 void nsGlobalWindowInner::DisableDeviceSensor(uint32_t aType) {
6534 int32_t doomedElement = -1;
6535 int32_t listenerCount = 0;
6536 for (uint32_t i = 0; i < mEnabledSensors.Length(); i++) {
6537 if (mEnabledSensors[i] == aType) {
6538 doomedElement = i;
6539 listenerCount++;
6543 if (doomedElement == -1) {
6544 return;
6547 mEnabledSensors.RemoveElementAt(doomedElement);
6549 if (listenerCount > 1) {
6550 return;
6553 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
6554 if (ac) {
6555 ac->RemoveWindowListener(aType, this);
6559 #if defined(MOZ_WIDGET_ANDROID)
6560 void nsGlobalWindowInner::EnableOrientationChangeListener() {
6561 if (!ShouldResistFingerprinting()) {
6562 mHasOrientationChangeListeners = true;
6563 mOrientationAngle = Orientation(CallerType::System);
6567 void nsGlobalWindowInner::DisableOrientationChangeListener() {
6568 mHasOrientationChangeListeners = false;
6570 #endif
6572 void nsGlobalWindowInner::SetHasGamepadEventListener(
6573 bool aHasGamepad /* = true*/) {
6574 mHasGamepad = aHasGamepad;
6575 if (aHasGamepad) {
6576 EnableGamepadUpdates();
6580 void nsGlobalWindowInner::NotifyDetectXRRuntimesCompleted() {
6581 if (!mXRRuntimeDetectionInFlight) {
6582 return;
6584 mXRRuntimeDetectionInFlight = false;
6585 if (mXRPermissionRequestInFlight) {
6586 return;
6588 gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
6589 bool supported = vm->RuntimeSupportsVR();
6590 if (!supported) {
6591 // A VR runtime was not installed; we can suppress
6592 // the permission prompt
6593 OnXRPermissionRequestCancel();
6594 return;
6596 // A VR runtime was found. Display a permission prompt before
6597 // allowing it to be accessed.
6598 // Connect to the VRManager in order to receive the runtime
6599 // detection results.
6600 mXRPermissionRequestInFlight = true;
6601 RefPtr<XRPermissionRequest> request =
6602 new XRPermissionRequest(this, WindowID());
6603 Unused << NS_WARN_IF(NS_FAILED(request->Start()));
6606 void nsGlobalWindowInner::RequestXRPermission() {
6607 if (IsDying()) {
6608 // Do not proceed if the window is dying, as that will result
6609 // in leaks of objects that get re-allocated after FreeInnerObjects
6610 // has been called, including mVREventObserver.
6611 return;
6613 if (mXRPermissionGranted) {
6614 // Don't prompt redundantly once permission to
6615 // access XR devices has been granted.
6616 OnXRPermissionRequestAllow();
6617 return;
6619 if (mXRRuntimeDetectionInFlight || mXRPermissionRequestInFlight) {
6620 // Don't allow multiple simultaneous permissions requests;
6621 return;
6623 // Before displaying a permission prompt, detect
6624 // if there is any VR runtime installed.
6625 gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
6626 mXRRuntimeDetectionInFlight = true;
6627 EnableVRUpdates();
6628 vm->DetectRuntimes();
6631 void nsGlobalWindowInner::OnXRPermissionRequestAllow() {
6632 mXRPermissionRequestInFlight = false;
6633 if (IsDying()) {
6634 // The window may have started dying while the permission request
6635 // is in flight.
6636 // Do not proceed if the window is dying, as that will result
6637 // in leaks of objects that get re-allocated after FreeInnerObjects
6638 // has been called, including mNavigator.
6639 return;
6641 mXRPermissionGranted = true;
6643 NotifyHasXRSession();
6645 dom::Navigator* nav = Navigator();
6646 MOZ_ASSERT(nav != nullptr);
6647 nav->OnXRPermissionRequestAllow();
6650 void nsGlobalWindowInner::OnXRPermissionRequestCancel() {
6651 mXRPermissionRequestInFlight = false;
6652 if (IsDying()) {
6653 // The window may have started dying while the permission request
6654 // is in flight.
6655 // Do not proceed if the window is dying, as that will result
6656 // in leaks of objects that get re-allocated after FreeInnerObjects
6657 // has been called, including mNavigator.
6658 return;
6660 dom::Navigator* nav = Navigator();
6661 MOZ_ASSERT(nav != nullptr);
6662 nav->OnXRPermissionRequestCancel();
6665 void nsGlobalWindowInner::EventListenerAdded(nsAtom* aType) {
6666 if (aType == nsGkAtoms::onvrdisplayactivate ||
6667 aType == nsGkAtoms::onvrdisplayconnect ||
6668 aType == nsGkAtoms::onvrdisplaydeactivate ||
6669 aType == nsGkAtoms::onvrdisplaydisconnect ||
6670 aType == nsGkAtoms::onvrdisplaypresentchange) {
6671 RequestXRPermission();
6674 if (aType == nsGkAtoms::onvrdisplayactivate) {
6675 mHasVRDisplayActivateEvents = true;
6678 if (aType == nsGkAtoms::onunload && mWindowGlobalChild) {
6679 if (++mUnloadOrBeforeUnloadListenerCount == 1) {
6680 mWindowGlobalChild->BlockBFCacheFor(BFCacheStatus::UNLOAD_LISTENER);
6684 if (aType == nsGkAtoms::onbeforeunload && mWindowGlobalChild) {
6685 if (!mozilla::SessionHistoryInParent() ||
6686 !StaticPrefs::
6687 docshell_shistory_bfcache_ship_allow_beforeunload_listeners()) {
6688 if (++mUnloadOrBeforeUnloadListenerCount == 1) {
6689 mWindowGlobalChild->BlockBFCacheFor(
6690 BFCacheStatus::BEFOREUNLOAD_LISTENER);
6693 if (!mDoc || !(mDoc->GetSandboxFlags() & SANDBOXED_MODALS)) {
6694 mWindowGlobalChild->BeforeUnloadAdded();
6695 MOZ_ASSERT(mWindowGlobalChild->BeforeUnloadListeners() > 0);
6699 // We need to initialize localStorage in order to receive notifications.
6700 if (aType == nsGkAtoms::onstorage) {
6701 ErrorResult rv;
6702 GetLocalStorage(rv);
6703 rv.SuppressException();
6705 if (NextGenLocalStorageEnabled() && mLocalStorage &&
6706 mLocalStorage->Type() == Storage::eLocalStorage) {
6707 auto object = static_cast<LSObject*>(mLocalStorage.get());
6709 Unused << NS_WARN_IF(NS_FAILED(object->EnsureObserver()));
6714 void nsGlobalWindowInner::EventListenerRemoved(nsAtom* aType) {
6715 if (aType == nsGkAtoms::onunload && mWindowGlobalChild) {
6716 MOZ_ASSERT(mUnloadOrBeforeUnloadListenerCount > 0);
6717 if (--mUnloadOrBeforeUnloadListenerCount == 0) {
6718 mWindowGlobalChild->UnblockBFCacheFor(BFCacheStatus::UNLOAD_LISTENER);
6722 if (aType == nsGkAtoms::onbeforeunload && mWindowGlobalChild) {
6723 if (!mozilla::SessionHistoryInParent() ||
6724 !StaticPrefs::
6725 docshell_shistory_bfcache_ship_allow_beforeunload_listeners()) {
6726 if (--mUnloadOrBeforeUnloadListenerCount == 0) {
6727 mWindowGlobalChild->UnblockBFCacheFor(
6728 BFCacheStatus::BEFOREUNLOAD_LISTENER);
6731 if (!mDoc || !(mDoc->GetSandboxFlags() & SANDBOXED_MODALS)) {
6732 mWindowGlobalChild->BeforeUnloadRemoved();
6733 MOZ_ASSERT(mWindowGlobalChild->BeforeUnloadListeners() >= 0);
6737 if (aType == nsGkAtoms::onstorage) {
6738 if (NextGenLocalStorageEnabled() && mLocalStorage &&
6739 mLocalStorage->Type() == Storage::eLocalStorage &&
6740 // The remove event is fired even if this isn't the last listener, so
6741 // only remove if there are no other listeners left.
6742 mListenerManager &&
6743 !mListenerManager->HasListenersFor(nsGkAtoms::onstorage)) {
6744 auto object = static_cast<LSObject*>(mLocalStorage.get());
6746 object->DropObserver();
6751 void nsGlobalWindowInner::NotifyHasXRSession() {
6752 if (IsDying()) {
6753 // Do not proceed if the window is dying, as that will result
6754 // in leaks of objects that get re-allocated after FreeInnerObjects
6755 // has been called, including mVREventObserver.
6756 return;
6758 if (mWindowGlobalChild && !mHasXRSession) {
6759 mWindowGlobalChild->BlockBFCacheFor(BFCacheStatus::HAS_USED_VR);
6761 mHasXRSession = true;
6762 EnableVRUpdates();
6765 bool nsGlobalWindowInner::HasUsedVR() const {
6766 // Returns true only if content has enumerated and activated
6767 // XR devices. Detection of XR runtimes without activation
6768 // will not cause true to be returned.
6769 return mHasXRSession;
6772 bool nsGlobalWindowInner::IsVRContentDetected() const {
6773 // Returns true only if the content will respond to
6774 // the VRDisplayActivate event.
6775 return mHasVRDisplayActivateEvents;
6778 bool nsGlobalWindowInner::IsVRContentPresenting() const {
6779 for (const auto& display : mVRDisplays) {
6780 if (display->IsAnyPresenting(gfx::kVRGroupAll)) {
6781 return true;
6784 return false;
6787 void nsGlobalWindowInner::AddSizeOfIncludingThis(
6788 nsWindowSizes& aWindowSizes) const {
6789 aWindowSizes.mDOMSizes.mDOMOtherSize +=
6790 aWindowSizes.mState.mMallocSizeOf(this);
6791 aWindowSizes.mDOMSizes.mDOMOtherSize +=
6792 nsIGlobalObject::ShallowSizeOfExcludingThis(
6793 aWindowSizes.mState.mMallocSizeOf);
6795 EventListenerManager* elm = GetExistingListenerManager();
6796 if (elm) {
6797 aWindowSizes.mDOMSizes.mDOMOtherSize +=
6798 elm->SizeOfIncludingThis(aWindowSizes.mState.mMallocSizeOf);
6799 aWindowSizes.mDOMEventListenersCount += elm->ListenerCount();
6801 if (mDoc) {
6802 // Multiple global windows can share a document. So only measure the
6803 // document if it (a) doesn't have a global window, or (b) it's the
6804 // primary document for the window.
6805 if (!mDoc->GetInnerWindow() || mDoc->GetInnerWindow() == this) {
6806 mDoc->DocAddSizeOfIncludingThis(aWindowSizes);
6810 if (mNavigator) {
6811 aWindowSizes.mDOMSizes.mDOMOtherSize +=
6812 mNavigator->SizeOfIncludingThis(aWindowSizes.mState.mMallocSizeOf);
6815 ForEachEventTargetObject([&](DOMEventTargetHelper* et, bool* aDoneOut) {
6816 if (nsCOMPtr<nsISizeOfEventTarget> iSizeOf = do_QueryObject(et)) {
6817 aWindowSizes.mDOMSizes.mDOMEventTargetsSize +=
6818 iSizeOf->SizeOfEventTargetIncludingThis(
6819 aWindowSizes.mState.mMallocSizeOf);
6821 if (EventListenerManager* elm = et->GetExistingListenerManager()) {
6822 aWindowSizes.mDOMEventListenersCount += elm->ListenerCount();
6824 ++aWindowSizes.mDOMEventTargetsCount;
6827 if (mPerformance) {
6828 aWindowSizes.mDOMSizes.mDOMPerformanceUserEntries =
6829 mPerformance->SizeOfUserEntries(aWindowSizes.mState.mMallocSizeOf);
6830 aWindowSizes.mDOMSizes.mDOMPerformanceResourceEntries =
6831 mPerformance->SizeOfResourceEntries(aWindowSizes.mState.mMallocSizeOf);
6832 aWindowSizes.mDOMSizes.mDOMPerformanceEventEntries =
6833 mPerformance->SizeOfEventEntries(aWindowSizes.mState.mMallocSizeOf);
6837 void nsGlobalWindowInner::RegisterDataDocumentForMemoryReporting(
6838 Document* aDocument) {
6839 aDocument->SetAddedToMemoryReportAsDataDocument();
6840 mDataDocumentsForMemoryReporting.AppendElement(
6841 do_GetWeakReference(aDocument));
6844 void nsGlobalWindowInner::UnregisterDataDocumentForMemoryReporting(
6845 Document* aDocument) {
6846 nsWeakPtr doc = do_GetWeakReference(aDocument);
6847 MOZ_ASSERT(mDataDocumentsForMemoryReporting.Contains(doc));
6848 mDataDocumentsForMemoryReporting.RemoveElement(doc);
6851 void nsGlobalWindowInner::CollectDOMSizesForDataDocuments(
6852 nsWindowSizes& aSize) const {
6853 for (const nsWeakPtr& ptr : mDataDocumentsForMemoryReporting) {
6854 if (nsCOMPtr<Document> doc = do_QueryReferent(ptr)) {
6855 doc->DocAddSizeOfIncludingThis(aSize);
6860 void nsGlobalWindowInner::AddGamepad(GamepadHandle aHandle, Gamepad* aGamepad) {
6861 // Create the index we will present to content based on which indices are
6862 // already taken, as required by the spec.
6863 // https://w3c.github.io/gamepad/gamepad.html#widl-Gamepad-index
6864 int index = 0;
6865 while (mGamepadIndexSet.Contains(index)) {
6866 ++index;
6868 mGamepadIndexSet.Put(index);
6869 aGamepad->SetIndex(index);
6870 mGamepads.InsertOrUpdate(aHandle, RefPtr{aGamepad});
6873 void nsGlobalWindowInner::RemoveGamepad(GamepadHandle aHandle) {
6874 RefPtr<Gamepad> gamepad;
6875 if (!mGamepads.Get(aHandle, getter_AddRefs(gamepad))) {
6876 return;
6878 // Free up the index we were using so it can be reused
6879 mGamepadIndexSet.Remove(gamepad->Index());
6880 mGamepads.Remove(aHandle);
6883 void nsGlobalWindowInner::GetGamepads(nsTArray<RefPtr<Gamepad>>& aGamepads) {
6884 aGamepads.Clear();
6886 // navigator.getGamepads() always returns an empty array when
6887 // privacy.resistFingerprinting is true.
6888 if (ShouldResistFingerprinting()) {
6889 return;
6892 // mGamepads.Count() may not be sufficient, but it's not harmful.
6893 aGamepads.SetCapacity(mGamepads.Count());
6894 for (const auto& entry : mGamepads) {
6895 Gamepad* gamepad = entry.GetWeak();
6896 aGamepads.EnsureLengthAtLeast(gamepad->Index() + 1);
6897 aGamepads[gamepad->Index()] = gamepad;
6901 already_AddRefed<Gamepad> nsGlobalWindowInner::GetGamepad(
6902 GamepadHandle aHandle) {
6903 RefPtr<Gamepad> gamepad;
6905 if (mGamepads.Get(aHandle, getter_AddRefs(gamepad))) {
6906 return gamepad.forget();
6909 return nullptr;
6912 void nsGlobalWindowInner::SetHasSeenGamepadInput(bool aHasSeen) {
6913 mHasSeenGamepadInput = aHasSeen;
6916 bool nsGlobalWindowInner::HasSeenGamepadInput() { return mHasSeenGamepadInput; }
6918 void nsGlobalWindowInner::SyncGamepadState() {
6919 if (mHasSeenGamepadInput) {
6920 RefPtr<GamepadManager> gamepadManager(GamepadManager::GetService());
6921 for (const auto& entry : mGamepads) {
6922 gamepadManager->SyncGamepadState(entry.GetKey(), this, entry.GetWeak());
6927 void nsGlobalWindowInner::StopGamepadHaptics() {
6928 if (mHasSeenGamepadInput) {
6929 RefPtr<GamepadManager> gamepadManager(GamepadManager::GetService());
6930 gamepadManager->StopHaptics();
6934 bool nsGlobalWindowInner::UpdateVRDisplays(
6935 nsTArray<RefPtr<mozilla::dom::VRDisplay>>& aDevices) {
6936 VRDisplay::UpdateVRDisplays(mVRDisplays, this);
6937 aDevices = mVRDisplays.Clone();
6938 return true;
6941 void nsGlobalWindowInner::NotifyActiveVRDisplaysChanged() {
6942 if (mNavigator) {
6943 mNavigator->NotifyActiveVRDisplaysChanged();
6947 void nsGlobalWindowInner::NotifyPresentationGenerationChanged(
6948 uint32_t aDisplayID) {
6949 for (const auto& display : mVRDisplays) {
6950 if (display->DisplayId() == aDisplayID) {
6951 display->OnPresentationGenerationChanged();
6956 void nsGlobalWindowInner::DispatchVRDisplayActivate(
6957 uint32_t aDisplayID, mozilla::dom::VRDisplayEventReason aReason) {
6958 // Ensure that our list of displays is up to date
6959 VRDisplay::UpdateVRDisplays(mVRDisplays, this);
6961 // Search for the display identified with aDisplayID and fire the
6962 // event if found.
6963 for (const auto& display : mVRDisplays) {
6964 if (display->DisplayId() == aDisplayID) {
6965 if (aReason != VRDisplayEventReason::Navigation &&
6966 display->IsAnyPresenting(gfx::kVRGroupContent)) {
6967 // We only want to trigger this event if nobody is presenting to the
6968 // display already or when a page is loaded by navigating away
6969 // from a page with an active VR Presentation.
6970 continue;
6973 VRDisplayEventInit init;
6974 init.mBubbles = false;
6975 init.mCancelable = false;
6976 init.mDisplay = display;
6977 init.mReason.Construct(aReason);
6979 RefPtr<VRDisplayEvent> event =
6980 VRDisplayEvent::Constructor(this, u"vrdisplayactivate"_ns, init);
6981 // vrdisplayactivate is a trusted event, allowing VRDisplay.requestPresent
6982 // to be used in response to link traversal, user request (chrome UX), and
6983 // HMD mounting detection sensors.
6984 event->SetTrusted(true);
6985 // VRDisplay.requestPresent normally requires a user gesture; however, an
6986 // exception is made to allow it to be called in response to
6987 // vrdisplayactivate during VR link traversal.
6988 display->StartHandlingVRNavigationEvent();
6989 DispatchEvent(*event);
6990 display->StopHandlingVRNavigationEvent();
6991 // Once we dispatch the event, we must not access any members as an event
6992 // listener can do anything, including closing windows.
6993 return;
6998 void nsGlobalWindowInner::DispatchVRDisplayDeactivate(
6999 uint32_t aDisplayID, mozilla::dom::VRDisplayEventReason aReason) {
7000 // Ensure that our list of displays is up to date
7001 VRDisplay::UpdateVRDisplays(mVRDisplays, this);
7003 // Search for the display identified with aDisplayID and fire the
7004 // event if found.
7005 for (const auto& display : mVRDisplays) {
7006 if (display->DisplayId() == aDisplayID && display->IsPresenting()) {
7007 // We only want to trigger this event to content that is presenting to
7008 // the display already.
7010 VRDisplayEventInit init;
7011 init.mBubbles = false;
7012 init.mCancelable = false;
7013 init.mDisplay = display;
7014 init.mReason.Construct(aReason);
7016 RefPtr<VRDisplayEvent> event =
7017 VRDisplayEvent::Constructor(this, u"vrdisplaydeactivate"_ns, init);
7018 event->SetTrusted(true);
7019 DispatchEvent(*event);
7020 // Once we dispatch the event, we must not access any members as an event
7021 // listener can do anything, including closing windows.
7022 return;
7027 void nsGlobalWindowInner::DispatchVRDisplayConnect(uint32_t aDisplayID) {
7028 // Ensure that our list of displays is up to date
7029 VRDisplay::UpdateVRDisplays(mVRDisplays, this);
7031 // Search for the display identified with aDisplayID and fire the
7032 // event if found.
7033 for (const auto& display : mVRDisplays) {
7034 if (display->DisplayId() == aDisplayID) {
7035 // Fire event even if not presenting to the display.
7036 VRDisplayEventInit init;
7037 init.mBubbles = false;
7038 init.mCancelable = false;
7039 init.mDisplay = display;
7040 // VRDisplayEvent.reason is not set for vrdisplayconnect
7042 RefPtr<VRDisplayEvent> event =
7043 VRDisplayEvent::Constructor(this, u"vrdisplayconnect"_ns, init);
7044 event->SetTrusted(true);
7045 DispatchEvent(*event);
7046 // Once we dispatch the event, we must not access any members as an event
7047 // listener can do anything, including closing windows.
7048 return;
7053 void nsGlobalWindowInner::DispatchVRDisplayDisconnect(uint32_t aDisplayID) {
7054 // Ensure that our list of displays is up to date
7055 VRDisplay::UpdateVRDisplays(mVRDisplays, this);
7057 // Search for the display identified with aDisplayID and fire the
7058 // event if found.
7059 for (const auto& display : mVRDisplays) {
7060 if (display->DisplayId() == aDisplayID) {
7061 // Fire event even if not presenting to the display.
7062 VRDisplayEventInit init;
7063 init.mBubbles = false;
7064 init.mCancelable = false;
7065 init.mDisplay = display;
7066 // VRDisplayEvent.reason is not set for vrdisplaydisconnect
7068 RefPtr<VRDisplayEvent> event =
7069 VRDisplayEvent::Constructor(this, u"vrdisplaydisconnect"_ns, init);
7070 event->SetTrusted(true);
7071 DispatchEvent(*event);
7072 // Once we dispatch the event, we must not access any members as an event
7073 // listener can do anything, including closing windows.
7074 return;
7079 void nsGlobalWindowInner::DispatchVRDisplayPresentChange(uint32_t aDisplayID) {
7080 // Ensure that our list of displays is up to date
7081 VRDisplay::UpdateVRDisplays(mVRDisplays, this);
7083 // Search for the display identified with aDisplayID and fire the
7084 // event if found.
7085 for (const auto& display : mVRDisplays) {
7086 if (display->DisplayId() == aDisplayID) {
7087 // Fire event even if not presenting to the display.
7088 VRDisplayEventInit init;
7089 init.mBubbles = false;
7090 init.mCancelable = false;
7091 init.mDisplay = display;
7092 // VRDisplayEvent.reason is not set for vrdisplaypresentchange
7093 RefPtr<VRDisplayEvent> event =
7094 VRDisplayEvent::Constructor(this, u"vrdisplaypresentchange"_ns, init);
7095 event->SetTrusted(true);
7096 DispatchEvent(*event);
7097 // Once we dispatch the event, we must not access any members as an event
7098 // listener can do anything, including closing windows.
7099 return;
7104 enum WindowState {
7105 // These constants need to match the constants in Window.webidl
7106 STATE_MAXIMIZED = 1,
7107 STATE_MINIMIZED = 2,
7108 STATE_NORMAL = 3,
7109 STATE_FULLSCREEN = 4
7112 uint16_t nsGlobalWindowInner::WindowState() {
7113 nsCOMPtr<nsIWidget> widget = GetMainWidget();
7115 int32_t mode = widget ? widget->SizeMode() : 0;
7117 switch (mode) {
7118 case nsSizeMode_Minimized:
7119 return STATE_MINIMIZED;
7120 case nsSizeMode_Maximized:
7121 return STATE_MAXIMIZED;
7122 case nsSizeMode_Fullscreen:
7123 return STATE_FULLSCREEN;
7124 case nsSizeMode_Normal:
7125 return STATE_NORMAL;
7126 default:
7127 NS_WARNING("Illegal window state for this chrome window");
7128 break;
7131 return STATE_NORMAL;
7134 bool nsGlobalWindowInner::IsFullyOccluded() {
7135 nsCOMPtr<nsIWidget> widget = GetMainWidget();
7136 return widget && widget->IsFullyOccluded();
7139 void nsGlobalWindowInner::Maximize() {
7140 if (nsCOMPtr<nsIWidget> widget = GetMainWidget()) {
7141 widget->SetSizeMode(nsSizeMode_Maximized);
7145 void nsGlobalWindowInner::Minimize() {
7146 if (nsCOMPtr<nsIWidget> widget = GetMainWidget()) {
7147 widget->SetSizeMode(nsSizeMode_Minimized);
7151 void nsGlobalWindowInner::Restore() {
7152 if (nsCOMPtr<nsIWidget> widget = GetMainWidget()) {
7153 widget->SetSizeMode(nsSizeMode_Normal);
7157 void nsGlobalWindowInner::GetWorkspaceID(nsAString& workspaceID) {
7158 workspaceID.Truncate();
7159 if (nsCOMPtr<nsIWidget> widget = GetMainWidget()) {
7160 return widget->GetWorkspaceID(workspaceID);
7164 void nsGlobalWindowInner::MoveToWorkspace(const nsAString& workspaceID) {
7165 if (nsCOMPtr<nsIWidget> widget = GetMainWidget()) {
7166 widget->MoveToWorkspace(workspaceID);
7170 void nsGlobalWindowInner::GetAttention(ErrorResult& aResult) {
7171 return GetAttentionWithCycleCount(-1, aResult);
7174 void nsGlobalWindowInner::GetAttentionWithCycleCount(int32_t aCycleCount,
7175 ErrorResult& aError) {
7176 nsCOMPtr<nsIWidget> widget = GetMainWidget();
7178 if (widget) {
7179 aError = widget->GetAttention(aCycleCount);
7183 already_AddRefed<Promise> nsGlobalWindowInner::PromiseDocumentFlushed(
7184 PromiseDocumentFlushedCallback& aCallback, ErrorResult& aError) {
7185 MOZ_RELEASE_ASSERT(IsChromeWindow());
7187 if (!IsCurrentInnerWindow()) {
7188 aError.ThrowInvalidStateError("Not the current inner window");
7189 return nullptr;
7191 if (!mDoc) {
7192 aError.ThrowInvalidStateError("No document");
7193 return nullptr;
7196 if (mIteratingDocumentFlushedResolvers) {
7197 aError.ThrowInvalidStateError("Already iterating through resolvers");
7198 return nullptr;
7201 PresShell* presShell = mDoc->GetPresShell();
7202 if (!presShell) {
7203 aError.ThrowInvalidStateError("No pres shell");
7204 return nullptr;
7207 // We need to associate the lifetime of the Promise to the lifetime
7208 // of the caller's global. That way, if the window we're observing
7209 // refresh driver ticks on goes away before our observer is fired,
7210 // we can still resolve the Promise.
7211 nsIGlobalObject* global = GetIncumbentGlobal();
7212 if (!global) {
7213 aError.ThrowInvalidStateError("No incumbent global");
7214 return nullptr;
7217 RefPtr<Promise> resultPromise = Promise::Create(global, aError);
7218 if (aError.Failed()) {
7219 return nullptr;
7222 UniquePtr<PromiseDocumentFlushedResolver> flushResolver(
7223 new PromiseDocumentFlushedResolver(resultPromise, aCallback));
7225 if (!presShell->NeedStyleFlush() && !presShell->NeedLayoutFlush()) {
7226 flushResolver->Call();
7227 return resultPromise.forget();
7230 if (!TryToObserveRefresh()) {
7231 aError.ThrowInvalidStateError("Couldn't observe refresh");
7232 return nullptr;
7235 mDocumentFlushedResolvers.AppendElement(std::move(flushResolver));
7236 return resultPromise.forget();
7239 bool nsGlobalWindowInner::TryToObserveRefresh() {
7240 if (mObservingRefresh) {
7241 return true;
7244 if (!mDoc) {
7245 return false;
7248 nsPresContext* pc = mDoc->GetPresContext();
7249 if (!pc) {
7250 return false;
7253 mObservingRefresh = true;
7254 auto observer = MakeRefPtr<ManagedPostRefreshObserver>(
7255 pc, [win = RefPtr{this}](bool aWasCanceled) {
7256 if (win->MaybeCallDocumentFlushedResolvers(
7257 /* aUntilExhaustion = */ aWasCanceled)) {
7258 return ManagedPostRefreshObserver::Unregister::No;
7260 win->mObservingRefresh = false;
7261 return ManagedPostRefreshObserver::Unregister::Yes;
7263 pc->RegisterManagedPostRefreshObserver(observer.get());
7264 return mObservingRefresh;
7267 void nsGlobalWindowInner::CallDocumentFlushedResolvers(bool aUntilExhaustion) {
7268 while (true) {
7270 // To coalesce MicroTask checkpoints inside callback call, enclose the
7271 // inner loop with nsAutoMicroTask, and perform a MicroTask checkpoint
7272 // after the loop.
7273 nsAutoMicroTask mt;
7275 mIteratingDocumentFlushedResolvers = true;
7277 auto resolvers = std::move(mDocumentFlushedResolvers);
7278 for (const auto& resolver : resolvers) {
7279 resolver->Call();
7282 mIteratingDocumentFlushedResolvers = false;
7285 // Leaving nsAutoMicroTask above will perform MicroTask checkpoint, and
7286 // Promise callbacks there may create mDocumentFlushedResolvers items.
7288 // If there's no new resolvers, or we're not exhausting the queue, there's
7289 // nothing to do (we'll keep observing if there's any new observer).
7291 // Otherwise, keep looping to call all promises. This case can happen while
7292 // destroying the window. This violates the constraint that the
7293 // promiseDocumentFlushed callback only ever run when no flush is needed,
7294 // but it's necessary to resolve the Promise returned by that.
7295 if (!aUntilExhaustion || mDocumentFlushedResolvers.IsEmpty()) {
7296 break;
7301 bool nsGlobalWindowInner::MaybeCallDocumentFlushedResolvers(
7302 bool aUntilExhaustion) {
7303 MOZ_ASSERT(mDoc);
7305 PresShell* presShell = mDoc->GetPresShell();
7306 if (!presShell || aUntilExhaustion) {
7307 CallDocumentFlushedResolvers(/* aUntilExhaustion = */ true);
7308 return false;
7311 if (presShell->NeedStyleFlush() || presShell->NeedLayoutFlush()) {
7312 // By the time our observer fired, something has already invalidated
7313 // style or layout - or perhaps we're still in the middle of a flush that
7314 // was interrupted. In either case, we'll wait until the next refresh driver
7315 // tick instead and try again.
7316 return true;
7319 CallDocumentFlushedResolvers(/* aUntilExhaustion = */ false);
7320 return !mDocumentFlushedResolvers.IsEmpty();
7323 already_AddRefed<nsWindowRoot> nsGlobalWindowInner::GetWindowRoot(
7324 mozilla::ErrorResult& aError) {
7325 FORWARD_TO_OUTER_OR_THROW(GetWindowRootOuter, (), aError, nullptr);
7328 void nsGlobalWindowInner::SetCursor(const nsACString& aCursor,
7329 ErrorResult& aError) {
7330 FORWARD_TO_OUTER_OR_THROW(SetCursorOuter, (aCursor, aError), aError, );
7333 NS_IMETHODIMP
7334 nsGlobalWindowInner::GetBrowserDOMWindow(nsIBrowserDOMWindow** aBrowserWindow) {
7335 MOZ_RELEASE_ASSERT(IsChromeWindow());
7337 ErrorResult rv;
7338 NS_IF_ADDREF(*aBrowserWindow = GetBrowserDOMWindow(rv));
7339 return rv.StealNSResult();
7342 nsIBrowserDOMWindow* nsGlobalWindowInner::GetBrowserDOMWindow(
7343 ErrorResult& aError) {
7344 FORWARD_TO_OUTER_OR_THROW(GetBrowserDOMWindowOuter, (), aError, nullptr);
7347 void nsGlobalWindowInner::SetBrowserDOMWindow(
7348 nsIBrowserDOMWindow* aBrowserWindow, ErrorResult& aError) {
7349 FORWARD_TO_OUTER_OR_THROW(SetBrowserDOMWindowOuter, (aBrowserWindow),
7350 aError, );
7353 void nsGlobalWindowInner::NotifyDefaultButtonLoaded(Element& aDefaultButton,
7354 ErrorResult& aError) {
7355 // Don't snap to a disabled button.
7356 nsCOMPtr<nsIDOMXULControlElement> xulControl = aDefaultButton.AsXULControl();
7357 if (!xulControl) {
7358 aError.Throw(NS_ERROR_FAILURE);
7359 return;
7361 bool disabled;
7362 aError = xulControl->GetDisabled(&disabled);
7363 if (aError.Failed() || disabled) {
7364 return;
7367 // Get the button rect in screen coordinates.
7368 nsIFrame* frame = aDefaultButton.GetPrimaryFrame();
7369 if (!frame) {
7370 aError.Throw(NS_ERROR_FAILURE);
7371 return;
7373 LayoutDeviceIntRect buttonRect = LayoutDeviceIntRect::FromAppUnitsToNearest(
7374 frame->GetScreenRectInAppUnits(),
7375 frame->PresContext()->AppUnitsPerDevPixel());
7377 // Get the widget rect in screen coordinates.
7378 nsIWidget* widget = GetNearestWidget();
7379 if (!widget) {
7380 aError.Throw(NS_ERROR_FAILURE);
7381 return;
7383 LayoutDeviceIntRect widgetRect = widget->GetScreenBounds();
7385 // Convert the buttonRect coordinates from screen to the widget.
7386 buttonRect -= widgetRect.TopLeft();
7387 nsresult rv = widget->OnDefaultButtonLoaded(buttonRect);
7388 if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) {
7389 aError.Throw(rv);
7393 ChromeMessageBroadcaster* nsGlobalWindowInner::MessageManager() {
7394 MOZ_ASSERT(IsChromeWindow());
7395 if (!mChromeFields.mMessageManager) {
7396 RefPtr<ChromeMessageBroadcaster> globalMM =
7397 nsFrameMessageManager::GetGlobalMessageManager();
7398 mChromeFields.mMessageManager = new ChromeMessageBroadcaster(globalMM);
7400 return mChromeFields.mMessageManager;
7403 ChromeMessageBroadcaster* nsGlobalWindowInner::GetGroupMessageManager(
7404 const nsAString& aGroup) {
7405 MOZ_ASSERT(IsChromeWindow());
7407 return mChromeFields.mGroupMessageManagers
7408 .LookupOrInsertWith(
7409 aGroup,
7410 [&] {
7411 return MakeAndAddRef<ChromeMessageBroadcaster>(MessageManager());
7413 .get();
7416 void nsGlobalWindowInner::InitWasOffline() { mWasOffline = NS_IsOffline(); }
7418 int16_t nsGlobalWindowInner::Orientation(CallerType aCallerType) {
7419 // GetOrientationAngle() returns 0, 90, 180 or 270.
7420 // window.orientation returns -90, 0, 90 or 180.
7421 if (nsIGlobalObject::ShouldResistFingerprinting(
7422 aCallerType, RFPTarget::ScreenOrientation)) {
7423 return 0;
7425 nsScreen* s = GetScreen(IgnoreErrors());
7426 if (!s) {
7427 return 0;
7429 int16_t angle = AssertedCast<int16_t>(s->GetOrientationAngle());
7430 return angle <= 180 ? angle : angle - 360;
7433 already_AddRefed<Console> nsGlobalWindowInner::GetConsole(JSContext* aCx,
7434 ErrorResult& aRv) {
7435 if (!mConsole) {
7436 mConsole = Console::Create(aCx, this, aRv);
7437 if (NS_WARN_IF(aRv.Failed())) {
7438 return nullptr;
7442 RefPtr<Console> console = mConsole;
7443 return console.forget();
7446 bool nsGlobalWindowInner::IsSecureContext() const {
7447 JS::Realm* realm = js::GetNonCCWObjectRealm(GetWrapperPreserveColor());
7448 return JS::GetIsSecureContext(realm);
7451 External* nsGlobalWindowInner::External() {
7452 if (!mExternal) {
7453 mExternal = new dom::External(ToSupports(this));
7456 return mExternal;
7459 void nsGlobalWindowInner::GetSidebar(OwningExternalOrWindowProxy& aResult) {
7460 // First check for a named frame named "sidebar"
7461 RefPtr<BrowsingContext> domWindow = GetChildWindow(u"sidebar"_ns);
7462 if (domWindow) {
7463 aResult.SetAsWindowProxy() = std::move(domWindow);
7464 return;
7467 RefPtr<dom::External> external = External();
7468 if (external) {
7469 aResult.SetAsExternal() = external;
7473 void nsGlobalWindowInner::ClearDocumentDependentSlots(JSContext* aCx) {
7474 // If JSAPI OOMs here, there is basically nothing we can do to recover safely.
7475 if (!Window_Binding::ClearCachedDocumentValue(aCx, this) ||
7476 !Window_Binding::ClearCachedPerformanceValue(aCx, this)) {
7477 MOZ_CRASH("Unhandlable OOM while clearing document dependent slots.");
7481 /* static */
7482 JSObject* nsGlobalWindowInner::CreateNamedPropertiesObject(
7483 JSContext* aCx, JS::Handle<JSObject*> aProto) {
7484 return WindowNamedPropertiesHandler::Create(aCx, aProto);
7487 void nsGlobalWindowInner::RedefineProperty(JSContext* aCx,
7488 const char* aPropName,
7489 JS::Handle<JS::Value> aValue,
7490 ErrorResult& aError) {
7491 JS::Rooted<JSObject*> thisObj(aCx, GetWrapperPreserveColor());
7492 if (!thisObj) {
7493 aError.Throw(NS_ERROR_UNEXPECTED);
7494 return;
7497 if (!JS_WrapObject(aCx, &thisObj) ||
7498 !JS_DefineProperty(aCx, thisObj, aPropName, aValue, JSPROP_ENUMERATE)) {
7499 aError.Throw(NS_ERROR_FAILURE);
7503 template <typename T>
7504 void nsGlobalWindowInner::GetReplaceableWindowCoord(
7505 JSContext* aCx, nsGlobalWindowInner::WindowCoordGetter<T> aGetter,
7506 JS::MutableHandle<JS::Value> aRetval, CallerType aCallerType,
7507 ErrorResult& aError) {
7508 T coord = (this->*aGetter)(aCallerType, aError);
7509 if (!aError.Failed() && !ToJSValue(aCx, coord, aRetval)) {
7510 aError.Throw(NS_ERROR_FAILURE);
7514 template <typename T>
7515 void nsGlobalWindowInner::SetReplaceableWindowCoord(
7516 JSContext* aCx, nsGlobalWindowInner::WindowCoordSetter<T> aSetter,
7517 JS::Handle<JS::Value> aValue, const char* aPropName, CallerType aCallerType,
7518 ErrorResult& aError) {
7520 * If caller is not chrome and the user has not explicitly exempted the site,
7521 * just treat this the way we would an IDL replaceable property.
7523 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
7524 if (StaticPrefs::dom_window_position_size_properties_replaceable_enabled() ||
7525 !outer || !outer->CanMoveResizeWindows(aCallerType) ||
7526 mBrowsingContext->IsSubframe()) {
7527 MOZ_DIAGNOSTIC_ASSERT(aCallerType != CallerType::System,
7528 "Setting this property in chrome code does nothing "
7529 "anymore, use resizeTo/moveTo as needed");
7530 RedefineProperty(aCx, aPropName, aValue, aError);
7531 return;
7534 T value;
7535 if (!ValueToPrimitive<T, eDefault>(aCx, aValue, aPropName, &value)) {
7536 aError.Throw(NS_ERROR_UNEXPECTED);
7537 return;
7540 (this->*aSetter)(value, aCallerType, aError);
7543 void nsGlobalWindowInner::FireOnNewGlobalObject() {
7544 // AutoEntryScript required to invoke debugger hook, which is a
7545 // Gecko-specific concept at present.
7546 AutoEntryScript aes(this, "nsGlobalWindowInner report new global");
7547 JS::Rooted<JSObject*> global(aes.cx(), GetWrapper());
7548 JS_FireOnNewGlobalObject(aes.cx(), global);
7551 #if defined(_WINDOWS_) && !defined(MOZ_WRAPPED_WINDOWS_H)
7552 # pragma message( \
7553 "wrapper failure reason: " MOZ_WINDOWS_WRAPPER_DISABLED_REASON)
7554 # error "Never include unwrapped windows.h in this file!"
7555 #endif
7557 already_AddRefed<Promise> nsGlobalWindowInner::CreateImageBitmap(
7558 const ImageBitmapSource& aImage, const ImageBitmapOptions& aOptions,
7559 ErrorResult& aRv) {
7560 return ImageBitmap::Create(this, aImage, Nothing(), aOptions, aRv);
7563 already_AddRefed<Promise> nsGlobalWindowInner::CreateImageBitmap(
7564 const ImageBitmapSource& aImage, int32_t aSx, int32_t aSy, int32_t aSw,
7565 int32_t aSh, const ImageBitmapOptions& aOptions, ErrorResult& aRv) {
7566 return ImageBitmap::Create(
7567 this, aImage, Some(gfx::IntRect(aSx, aSy, aSw, aSh)), aOptions, aRv);
7570 // https://html.spec.whatwg.org/#structured-cloning
7571 void nsGlobalWindowInner::StructuredClone(
7572 JSContext* aCx, JS::Handle<JS::Value> aValue,
7573 const StructuredSerializeOptions& aOptions,
7574 JS::MutableHandle<JS::Value> aRetval, ErrorResult& aError) {
7575 nsContentUtils::StructuredClone(aCx, this, aValue, aOptions, aRetval, aError);
7578 nsresult nsGlobalWindowInner::Dispatch(
7579 TaskCategory aCategory, already_AddRefed<nsIRunnable>&& aRunnable) {
7580 MOZ_RELEASE_ASSERT(NS_IsMainThread());
7581 if (GetDocGroup()) {
7582 return GetDocGroup()->Dispatch(aCategory, std::move(aRunnable));
7584 return DispatcherTrait::Dispatch(aCategory, std::move(aRunnable));
7587 nsISerialEventTarget* nsGlobalWindowInner::EventTargetFor(
7588 TaskCategory aCategory) const {
7589 MOZ_RELEASE_ASSERT(NS_IsMainThread());
7590 if (GetDocGroup()) {
7591 return GetDocGroup()->EventTargetFor(aCategory);
7593 return DispatcherTrait::EventTargetFor(aCategory);
7596 AbstractThread* nsGlobalWindowInner::AbstractMainThreadFor(
7597 TaskCategory aCategory) {
7598 MOZ_RELEASE_ASSERT(NS_IsMainThread());
7599 if (GetDocGroup()) {
7600 return GetDocGroup()->AbstractMainThreadFor(aCategory);
7602 return DispatcherTrait::AbstractMainThreadFor(aCategory);
7605 Worklet* nsGlobalWindowInner::GetPaintWorklet(ErrorResult& aRv) {
7606 if (!mPaintWorklet) {
7607 nsIPrincipal* principal = GetPrincipal();
7608 if (!principal) {
7609 aRv.Throw(NS_ERROR_FAILURE);
7610 return nullptr;
7613 mPaintWorklet = PaintWorkletImpl::CreateWorklet(this, principal);
7616 return mPaintWorklet;
7619 void nsGlobalWindowInner::GetRegionalPrefsLocales(
7620 nsTArray<nsString>& aLocales) {
7621 MOZ_ASSERT(mozilla::intl::LocaleService::GetInstance());
7623 AutoTArray<nsCString, 10> rpLocales;
7624 mozilla::intl::LocaleService::GetInstance()->GetRegionalPrefsLocales(
7625 rpLocales);
7627 for (const auto& loc : rpLocales) {
7628 aLocales.AppendElement(NS_ConvertUTF8toUTF16(loc));
7632 void nsGlobalWindowInner::GetWebExposedLocales(nsTArray<nsString>& aLocales) {
7633 MOZ_ASSERT(mozilla::intl::LocaleService::GetInstance());
7635 AutoTArray<nsCString, 10> rpLocales;
7636 mozilla::intl::LocaleService::GetInstance()->GetWebExposedLocales(rpLocales);
7638 for (const auto& loc : rpLocales) {
7639 aLocales.AppendElement(NS_ConvertUTF8toUTF16(loc));
7643 IntlUtils* nsGlobalWindowInner::GetIntlUtils(ErrorResult& aError) {
7644 if (!mIntlUtils) {
7645 mIntlUtils = new IntlUtils(this);
7648 return mIntlUtils;
7651 void nsGlobalWindowInner::StoreSharedWorker(SharedWorker* aSharedWorker) {
7652 MOZ_ASSERT(aSharedWorker);
7653 MOZ_ASSERT(!mSharedWorkers.Contains(aSharedWorker));
7655 mSharedWorkers.AppendElement(aSharedWorker);
7658 void nsGlobalWindowInner::ForgetSharedWorker(SharedWorker* aSharedWorker) {
7659 MOZ_ASSERT(aSharedWorker);
7660 MOZ_ASSERT(mSharedWorkers.Contains(aSharedWorker));
7662 mSharedWorkers.RemoveElement(aSharedWorker);
7665 void nsGlobalWindowInner::StorageAccessPermissionGranted() {
7666 // Invalidate cached StorageAllowed field so that calls to GetLocalStorage
7667 // give us the updated localStorage object.
7668 ClearStorageAllowedCache();
7670 // If we're always partitioning non-cookie third party storage then
7671 // there is no need to clear it when the user accepts requestStorageAccess.
7672 if (StaticPrefs::
7673 privacy_partition_always_partition_third_party_non_cookie_storage()) {
7674 // Just reset the active cookie and storage principals
7675 nsCOMPtr<nsICookieJarSettings> cjs;
7676 if (mDoc) {
7677 cjs = mDoc->CookieJarSettings();
7679 StorageAccess storageAccess = StorageAllowedForWindow(this);
7680 if (ShouldPartitionStorage(storageAccess) &&
7681 StoragePartitioningEnabled(storageAccess, cjs)) {
7682 if (mDoc) {
7683 mDoc->ClearActiveCookieAndStoragePrincipals();
7685 return;
7689 PropagateStorageAccessPermissionGrantedToWorkers(*this);
7691 // If we have a partitioned localStorage, it's time to replace it with a real
7692 // one in order to receive notifications.
7694 if (mLocalStorage) {
7695 IgnoredErrorResult error;
7696 GetLocalStorage(error);
7697 if (NS_WARN_IF(error.Failed())) {
7698 return;
7701 MOZ_ASSERT(mLocalStorage &&
7702 mLocalStorage->Type() == Storage::eLocalStorage);
7704 if (NextGenLocalStorageEnabled() && mListenerManager &&
7705 mListenerManager->HasListenersFor(nsGkAtoms::onstorage)) {
7706 auto object = static_cast<LSObject*>(mLocalStorage.get());
7708 object->EnsureObserver();
7712 // Reset the IndexedDB factory.
7713 mIndexedDB = nullptr;
7715 // Reset DOM Cache
7716 mCacheStorage = nullptr;
7718 // Reset the active cookie and storage principals
7719 if (mDoc) {
7720 mDoc->ClearActiveCookieAndStoragePrincipals();
7721 if (mWindowGlobalChild) {
7722 // XXX(farre): This is a bit backwards, but clearing the cookie
7723 // principal might make us end up with a new effective storage
7724 // principal on the child side than on the parent side, which
7725 // means that we need to sync it. See bug 1705359.
7726 mWindowGlobalChild->SetDocumentPrincipal(
7727 mDoc->NodePrincipal(), mDoc->EffectiveStoragePrincipal());
7732 ContentMediaController* nsGlobalWindowInner::GetContentMediaController() {
7733 if (mContentMediaController) {
7734 return mContentMediaController;
7736 if (!mBrowsingContext) {
7737 return nullptr;
7740 mContentMediaController = new ContentMediaController(mBrowsingContext->Id());
7741 return mContentMediaController;
7744 void nsGlobalWindowInner::SetScrollMarks(const nsTArray<uint32_t>& aScrollMarks,
7745 bool aOnHScrollbar) {
7746 mScrollMarks.Assign(aScrollMarks);
7747 mScrollMarksOnHScrollbar = aOnHScrollbar;
7749 // Mark the scrollbar for repainting.
7750 if (mDoc) {
7751 PresShell* presShell = mDoc->GetPresShell();
7752 if (presShell) {
7753 nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollable();
7754 if (sf) {
7755 sf->InvalidateScrollbars();
7761 /* static */
7762 already_AddRefed<nsGlobalWindowInner> nsGlobalWindowInner::Create(
7763 nsGlobalWindowOuter* aOuterWindow, bool aIsChrome,
7764 WindowGlobalChild* aActor) {
7765 RefPtr<nsGlobalWindowInner> window =
7766 new nsGlobalWindowInner(aOuterWindow, aActor);
7767 if (aIsChrome) {
7768 window->mIsChrome = true;
7769 window->mCleanMessageManager = true;
7772 if (aActor) {
7773 aActor->InitWindowGlobal(window);
7776 window->InitWasOffline();
7777 return window.forget();
7780 JS::loader::ModuleLoaderBase* nsGlobalWindowInner::GetModuleLoader(
7781 JSContext* aCx) {
7782 Document* document = GetDocument();
7783 if (!document) {
7784 return nullptr;
7787 ScriptLoader* loader = document->ScriptLoader();
7788 if (!loader) {
7789 return nullptr;
7792 return loader->GetModuleLoader();
7795 nsIURI* nsPIDOMWindowInner::GetDocumentURI() const {
7796 return mDoc ? mDoc->GetDocumentURI() : mDocumentURI.get();
7799 nsIURI* nsPIDOMWindowInner::GetDocBaseURI() const {
7800 return mDoc ? mDoc->GetDocBaseURI() : mDocBaseURI.get();
7803 mozilla::dom::WindowContext* nsPIDOMWindowInner::GetWindowContext() const {
7804 return mWindowGlobalChild ? mWindowGlobalChild->WindowContext() : nullptr;
7807 bool nsPIDOMWindowInner::RemoveFromBFCacheSync() {
7808 if (Document* doc = GetExtantDoc()) {
7809 return doc->RemoveFromBFCacheSync();
7811 return false;
7814 void nsPIDOMWindowInner::MaybeCreateDoc() {
7815 // XXX: Forward to outer?
7816 MOZ_ASSERT(!mDoc);
7817 if (nsIDocShell* docShell = GetDocShell()) {
7818 // Note that |document| here is the same thing as our mDoc, but we
7819 // don't have to explicitly set the member variable because the docshell
7820 // has already called SetNewDocument().
7821 nsCOMPtr<Document> document = docShell->GetDocument();
7822 Unused << document;
7826 mozilla::dom::DocGroup* nsPIDOMWindowInner::GetDocGroup() const {
7827 Document* doc = GetExtantDoc();
7828 if (doc) {
7829 return doc->GetDocGroup();
7831 return nullptr;
7834 mozilla::dom::BrowsingContextGroup*
7835 nsPIDOMWindowInner::GetBrowsingContextGroup() const {
7836 return mBrowsingContext ? mBrowsingContext->Group() : nullptr;
7839 nsIGlobalObject* nsPIDOMWindowInner::AsGlobal() {
7840 return nsGlobalWindowInner::Cast(this);
7843 const nsIGlobalObject* nsPIDOMWindowInner::AsGlobal() const {
7844 return nsGlobalWindowInner::Cast(this);
7847 void nsPIDOMWindowInner::SaveStorageAccessPermissionGranted() {
7848 mStorageAccessPermissionGranted = true;
7850 nsGlobalWindowInner::Cast(this)->StorageAccessPermissionGranted();
7853 bool nsPIDOMWindowInner::HasStorageAccessPermissionGranted() {
7854 return mStorageAccessPermissionGranted;
7857 nsPIDOMWindowInner::nsPIDOMWindowInner(nsPIDOMWindowOuter* aOuterWindow,
7858 WindowGlobalChild* aActor)
7859 : mMutationBits(0),
7860 mIsDocumentLoaded(false),
7861 mIsHandlingResizeEvent(false),
7862 mMayHavePaintEventListener(false),
7863 mMayHaveTouchEventListener(false),
7864 mMayHaveSelectionChangeEventListener(false),
7865 mMayHaveFormSelectEventListener(false),
7866 mMayHaveMouseEnterLeaveEventListener(false),
7867 mMayHavePointerEnterLeaveEventListener(false),
7868 mMayHaveTransitionEventListener(false),
7869 mMayHaveBeforeInputEventListenerForTelemetry(false),
7870 mMutationObserverHasObservedNodeForTelemetry(false),
7871 mOuterWindow(aOuterWindow),
7872 mWindowID(0),
7873 mHasNotifiedGlobalCreated(false),
7874 mMarkedCCGeneration(0),
7875 mHasTriedToCacheTopInnerWindow(false),
7876 mNumOfIndexedDBDatabases(0),
7877 mNumOfOpenWebSockets(0),
7878 mEvent(nullptr),
7879 mStorageAccessPermissionGranted(false),
7880 mWindowGlobalChild(aActor),
7881 mWasSuspendedByGroup(false) {
7882 MOZ_ASSERT(aOuterWindow);
7883 mBrowsingContext = aOuterWindow->GetBrowsingContext();
7885 if (mWindowGlobalChild) {
7886 mWindowID = aActor->InnerWindowId();
7888 MOZ_ASSERT(mWindowGlobalChild->BrowsingContext() == mBrowsingContext);
7889 } else {
7890 mWindowID = nsContentUtils::GenerateWindowId();
7894 nsPIDOMWindowInner::~nsPIDOMWindowInner() = default;
7896 #undef FORWARD_TO_OUTER
7897 #undef FORWARD_TO_OUTER_OR_THROW
7898 #undef FORWARD_TO_OUTER_VOID