Bug 1686668 [wpt PR 27185] - Update wpt metadata, a=testonly
[gecko.git] / dom / base / nsGlobalWindowInner.cpp
blobf431cd63f5cd63a3fed30155cfd6105d51ac4b4f
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 <math.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <cstdint>
15 #include <new>
16 #include <type_traits>
17 #include <utility>
18 #include "AudioChannelService.h"
19 #include "AutoplayPolicy.h"
20 #include "Crypto.h"
21 #include "GeckoProfiler.h"
22 #include "MainThreadUtils.h"
23 #include "Navigator.h"
24 #include "PaintWorkletImpl.h"
25 #include "SessionStorageCache.h"
26 #include "Units.h"
27 #include "VRManagerChild.h"
28 #include "WindowDestroyedEvent.h"
29 #include "WindowNamedPropertiesHandler.h"
30 #include "js/ComparisonOperators.h"
31 #include "js/CompileOptions.h"
32 #include "js/Id.h"
33 #include "js/PropertyDescriptor.h"
34 #include "js/RealmOptions.h"
35 #include "js/RootingAPI.h"
36 #include "js/TypeDecls.h"
37 #include "js/Value.h"
38 #include "js/Warnings.h"
39 #include "js/shadow/String.h"
40 #include "jsapi.h"
41 #include "jsfriendapi.h"
42 #include "mozIDOMWindow.h"
43 #include "moz_external_vr.h"
44 #include "mozilla/AlreadyAddRefed.h"
45 #include "mozilla/ArrayIterator.h"
46 #include "mozilla/ArrayUtils.h"
47 #include "mozilla/Attributes.h"
48 #include "mozilla/BaseProfilerMarkersPrerequisites.h"
49 #include "mozilla/BasicEvents.h"
50 #include "mozilla/CallState.h"
51 #include "mozilla/CycleCollectedJSContext.h"
52 #include "mozilla/DOMEventTargetHelper.h"
53 #include "mozilla/ErrorResult.h"
54 #include "mozilla/EventDispatcher.h"
55 #include "mozilla/EventListenerManager.h"
56 #include "mozilla/EventQueue.h"
57 #include "mozilla/FloatingPoint.h"
58 #include "mozilla/FlushType.h"
59 #include "mozilla/Likely.h"
60 #include "mozilla/LinkedList.h"
61 #include "mozilla/Logging.h"
62 #include "mozilla/MacroForEach.h"
63 #include "mozilla/Maybe.h"
64 #include "mozilla/OwningNonNull.h"
65 #include "mozilla/PermissionDelegateHandler.h"
66 #include "mozilla/Preferences.h"
67 #include "mozilla/PresShell.h"
68 #include "mozilla/ProcessHangMonitor.h"
69 #include "mozilla/RefPtr.h"
70 #include "mozilla/Result.h"
71 #include "mozilla/ScopeExit.h"
72 #include "mozilla/ScrollOrigin.h"
73 #include "mozilla/ScrollTypes.h"
74 #include "mozilla/Services.h"
75 #include "mozilla/SizeOfState.h"
76 #include "mozilla/Span.h"
77 #include "mozilla/SpinEventLoopUntil.h"
78 #include "mozilla/Sprintf.h"
79 #include "mozilla/StaticPrefs_browser.h"
80 #include "mozilla/StaticPrefs_dom.h"
81 #include "mozilla/StorageAccess.h"
82 #include "mozilla/TaskCategory.h"
83 #include "mozilla/Telemetry.h"
84 #include "mozilla/TelemetryHistogramEnums.h"
85 #include "mozilla/TimeStamp.h"
86 #include "mozilla/UniquePtr.h"
87 #include "mozilla/Unused.h"
88 #include "mozilla/dom/AudioContext.h"
89 #include "mozilla/dom/BarProps.h"
90 #include "mozilla/dom/BindingDeclarations.h"
91 #include "mozilla/dom/BindingUtils.h"
92 #include "mozilla/dom/BrowserChild.h"
93 #include "mozilla/dom/BrowsingContext.h"
94 #include "mozilla/dom/CSPEvalChecker.h"
95 #include "mozilla/dom/CallbackDebuggerNotification.h"
96 #include "mozilla/dom/ChromeMessageBroadcaster.h"
97 #include "mozilla/dom/ClientInfo.h"
98 #include "mozilla/dom/ClientManager.h"
99 #include "mozilla/dom/ClientSource.h"
100 #include "mozilla/dom/ClientState.h"
101 #include "mozilla/dom/ClientsBinding.h"
102 #include "mozilla/dom/Console.h"
103 #include "mozilla/dom/ContentFrameMessageManager.h"
104 #include "mozilla/dom/ContentMediaController.h"
105 #include "mozilla/dom/CustomElementRegistry.h"
106 #include "mozilla/dom/DOMJSProxyHandler.h"
107 #include "mozilla/dom/DebuggerNotification.h"
108 #include "mozilla/dom/DebuggerNotificationBinding.h"
109 #include "mozilla/dom/DebuggerNotificationManager.h"
110 #include "mozilla/dom/DispatcherTrait.h"
111 #include "mozilla/dom/DocGroup.h"
112 #include "mozilla/dom/Document.h"
113 #include "mozilla/dom/DocumentInlines.h"
114 #include "mozilla/dom/Element.h"
115 #include "mozilla/dom/Event.h"
116 #include "mozilla/dom/EventTarget.h"
117 #include "mozilla/dom/Fetch.h"
118 #include "mozilla/dom/Gamepad.h"
119 #include "mozilla/dom/GamepadHandle.h"
120 #include "mozilla/dom/GamepadManager.h"
121 #include "mozilla/dom/HashChangeEvent.h"
122 #include "mozilla/dom/HashChangeEventBinding.h"
123 #include "mozilla/dom/IDBFactory.h"
124 #include "mozilla/dom/IdleRequest.h"
125 #include "mozilla/dom/ImageBitmap.h"
126 #include "mozilla/dom/ImageBitmapSource.h"
127 #include "mozilla/dom/InstallTriggerBinding.h"
128 #include "mozilla/dom/IntlUtils.h"
129 #include "mozilla/dom/JSExecutionContext.h"
130 #include "mozilla/dom/LSObject.h"
131 #include "mozilla/dom/LoadedScript.h"
132 #include "mozilla/dom/LocalStorage.h"
133 #include "mozilla/dom/LocalStorageCommon.h"
134 #include "mozilla/dom/Location.h"
135 #include "mozilla/dom/MediaKeys.h"
136 #include "mozilla/dom/NavigatorBinding.h"
137 #include "mozilla/dom/Nullable.h"
138 #include "mozilla/dom/PartitionedLocalStorage.h"
139 #include "mozilla/dom/Performance.h"
140 #include "mozilla/dom/PopStateEvent.h"
141 #include "mozilla/dom/PopStateEventBinding.h"
142 #include "mozilla/dom/PopupBlocker.h"
143 #include "mozilla/dom/PrimitiveConversions.h"
144 #include "mozilla/dom/Promise.h"
145 #include "mozilla/dom/RootedDictionary.h"
146 #include "mozilla/dom/ScriptLoader.h"
147 #include "mozilla/dom/ScriptSettings.h"
148 #include "mozilla/dom/ServiceWorker.h"
149 #include "mozilla/dom/ServiceWorkerDescriptor.h"
150 #include "mozilla/dom/ServiceWorkerRegistration.h"
151 #include "mozilla/dom/SessionStorageManager.h"
152 #include "mozilla/dom/SharedWorker.h"
153 #include "mozilla/dom/Storage.h"
154 #include "mozilla/dom/StorageEvent.h"
155 #include "mozilla/dom/StorageEventBinding.h"
156 #include "mozilla/dom/StorageNotifierService.h"
157 #include "mozilla/dom/StorageUtils.h"
158 #include "mozilla/dom/TabMessageTypes.h"
159 #include "mozilla/dom/Timeout.h"
160 #include "mozilla/dom/TimeoutHandler.h"
161 #include "mozilla/dom/TimeoutManager.h"
162 #include "mozilla/dom/ToJSValue.h"
163 #include "mozilla/dom/U2F.h"
164 #include "mozilla/dom/VRDisplay.h"
165 #include "mozilla/dom/VRDisplayEvent.h"
166 #include "mozilla/dom/VRDisplayEventBinding.h"
167 #include "mozilla/dom/VREventObserver.h"
168 #include "mozilla/dom/VisualViewport.h"
169 #include "mozilla/dom/WakeLock.h"
170 #include "mozilla/dom/WebIDLGlobalNameHash.h"
171 #include "mozilla/dom/WindowBinding.h"
172 #include "mozilla/dom/WindowContext.h"
173 #include "mozilla/dom/WindowGlobalChild.h"
174 #include "mozilla/dom/WindowProxyHolder.h"
175 #include "mozilla/dom/WorkerCommon.h"
176 #include "mozilla/dom/Worklet.h"
177 #include "mozilla/dom/XRPermissionRequest.h"
178 #include "mozilla/dom/cache/CacheStorage.h"
179 #include "mozilla/dom/cache/Types.h"
180 #include "mozilla/fallible.h"
181 #include "mozilla/gfx/BasePoint.h"
182 #include "mozilla/gfx/BaseRect.h"
183 #include "mozilla/gfx/BaseSize.h"
184 #include "mozilla/gfx/Rect.h"
185 #include "mozilla/gfx/Types.h"
186 #include "mozilla/intl/LocaleService.h"
187 #include "mozilla/net/CookieJarSettings.h"
188 #include "nsAtom.h"
189 #include "nsBaseHashtable.h"
190 #include "nsCCUncollectableMarker.h"
191 #include "nsCOMPtr.h"
192 #include "nsCRT.h"
193 #include "nsCRTGlue.h"
194 #include "nsCanvasFrame.h"
195 #include "nsCharTraits.h"
196 #include "nsCheapSets.h"
197 #include "nsContentUtils.h"
198 #include "nsCoord.h"
199 #include "nsCycleCollectionNoteChild.h"
200 #include "nsCycleCollectionTraversalCallback.h"
201 #include "nsDOMNavigationTiming.h"
202 #include "nsDOMOfflineResourceList.h"
203 #include "nsDebug.h"
204 #include "nsDocShell.h"
205 #include "nsFocusManager.h"
206 #include "nsFrameMessageManager.h"
207 #include "nsGkAtoms.h"
208 #include "nsGlobalWindowOuter.h"
209 #include "nsHashKeys.h"
210 #include "nsHistory.h"
211 #include "nsIAddonPolicyService.h"
212 #include "nsIArray.h"
213 #include "nsIBaseWindow.h"
214 #include "nsIBrowserChild.h"
215 #include "nsICancelableRunnable.h"
216 #include "nsIChannel.h"
217 #include "nsIContentSecurityPolicy.h"
218 #include "nsIControllers.h"
219 #include "nsICookieJarSettings.h"
220 #include "nsICookieService.h"
221 #include "nsID.h"
222 #include "nsIDOMStorageManager.h"
223 #include "nsIDeviceSensors.h"
224 #include "nsIDocShell.h"
225 #include "nsIDocShellTreeItem.h"
226 #include "nsIDocShellTreeOwner.h"
227 #include "nsIDocumentLoader.h"
228 #include "nsIDragService.h"
229 #include "nsIFocusManager.h"
230 #include "nsIFrame.h"
231 #include "nsIGlobalObject.h"
232 #include "nsIIOService.h"
233 #include "nsIIdleRunnable.h"
234 #include "nsIInterfaceRequestorUtils.h"
235 #include "nsILoadContext.h"
236 #include "nsILoadGroup.h"
237 #include "nsILoadInfo.h"
238 #include "nsINamed.h"
239 #include "nsINode.h"
240 #include "nsIObserver.h"
241 #include "nsIObserverService.h"
242 #include "nsIPermission.h"
243 #include "nsIPermissionManager.h"
244 #include "nsIPrefBranch.h"
245 #include "nsIPrincipal.h"
246 #include "nsIPrompt.h"
247 #include "nsIRunnable.h"
248 #include "nsIScreen.h"
249 #include "nsIScreenManager.h"
250 #include "nsIScriptContext.h"
251 #include "nsIScriptGlobalObject.h"
252 #include "nsIScriptObjectPrincipal.h"
253 #include "nsIScrollableFrame.h"
254 #include "nsISerialEventTarget.h"
255 #include "nsISimpleEnumerator.h"
256 #include "nsISizeOfEventTarget.h"
257 #include "nsISlowScriptDebug.h"
258 #include "nsISupportsUtils.h"
259 #include "nsIThread.h"
260 #include "nsITimedChannel.h"
261 #include "nsIURI.h"
262 #include "nsIVariant.h"
263 #include "nsIWeakReference.h"
264 #include "nsIWebBrowserChrome.h"
265 #include "nsIWebNavigation.h"
266 #include "nsIWebProgressListener.h"
267 #include "nsIWidget.h"
268 #include "nsIWidgetListener.h"
269 #include "nsJSPrincipals.h"
270 #include "nsJSUtils.h"
271 #include "nsLayoutStatics.h"
272 #include "nsLiteralString.h"
273 #include "nsNetUtil.h"
274 #include "nsPIDOMWindow.h"
275 #include "nsPIDOMWindowInlines.h"
276 #include "nsPIWindowRoot.h"
277 #include "nsPoint.h"
278 #include "nsPresContext.h"
279 #include "nsQueryObject.h"
280 #include "nsRefPtrHashtable.h"
281 #include "nsSandboxFlags.h"
282 #include "nsScreen.h"
283 #include "nsServiceManagerUtils.h"
284 #include "nsString.h"
285 #include "nsStringFlags.h"
286 #include "nsStringFwd.h"
287 #include "nsTArray.h"
288 #include "nsTLiteralString.h"
289 #include "nsTObserverArray.h"
290 #include "nsTStringRepr.h"
291 #include "nsThreadUtils.h"
292 #include "nsWeakReference.h"
293 #include "nsWindowMemoryReporter.h"
294 #include "nsWindowSizes.h"
295 #include "nsWrapperCache.h"
296 #include "nsWrapperCacheInlines.h"
297 #include "nsXULAppAPI.h"
298 #include "nsrootidl.h"
299 #include "prclist.h"
300 #include "prtypes.h"
301 #include "xpcprivate.h"
302 #include "xpcpublic.h"
304 #ifdef MOZ_XUL
305 # include "nsIDOMXULControlElement.h"
306 # include "nsMenuPopupFrame.h"
307 #endif
309 #ifdef NS_PRINTING
310 # include "nsIPrintSettings.h"
311 # include "nsIPrintSettingsService.h"
312 # include "nsIWebBrowserPrint.h"
313 #endif
315 #ifdef HAVE_SIDEBAR
316 # include "mozilla/dom/ExternalBinding.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 MOZ_WIDGET_ANDROID
328 # include "mozilla/dom/WindowOrientationObserver.h"
329 #endif
331 #ifdef XP_WIN
332 # include "mozilla/Debug.h"
333 # include <process.h>
334 # define getpid _getpid
335 #else
336 # include <unistd.h> // for getpid()
337 #endif
339 using namespace mozilla;
340 using namespace mozilla::dom;
341 using namespace mozilla::dom::ipc;
342 using mozilla::TimeDuration;
343 using mozilla::TimeStamp;
344 using mozilla::dom::GamepadHandle;
345 using mozilla::dom::cache::CacheStorage;
347 #define FORWARD_TO_OUTER(method, args, err_rval) \
348 PR_BEGIN_MACRO \
349 nsGlobalWindowOuter* outer = GetOuterWindowInternal(); \
350 if (!HasActiveDocument()) { \
351 NS_WARNING(outer ? "Inner window does not have active document." \
352 : "No outer window available!"); \
353 return err_rval; \
355 return outer->method args; \
356 PR_END_MACRO
358 static nsGlobalWindowOuter* GetOuterWindowForForwarding(
359 nsGlobalWindowInner* aInner, ErrorResult& aError) {
360 nsGlobalWindowOuter* outer = aInner->GetOuterWindowInternal();
361 if (MOZ_LIKELY(aInner->HasActiveDocument())) {
362 return outer;
364 if (!outer) {
365 NS_WARNING("No outer window available!");
366 aError.Throw(NS_ERROR_NOT_INITIALIZED);
367 } else {
368 aError.Throw(NS_ERROR_XPC_SECURITY_MANAGER_VETO);
370 return nullptr;
373 #define FORWARD_TO_OUTER_OR_THROW(method, args, errorresult, err_rval) \
374 PR_BEGIN_MACRO \
375 nsGlobalWindowOuter* outer = GetOuterWindowForForwarding(this, errorresult); \
376 if (MOZ_LIKELY(outer)) { \
377 return outer->method args; \
379 return err_rval; \
380 PR_END_MACRO
382 #define FORWARD_TO_OUTER_VOID(method, args) \
383 PR_BEGIN_MACRO \
384 nsGlobalWindowOuter* outer = GetOuterWindowInternal(); \
385 if (!HasActiveDocument()) { \
386 NS_WARNING(outer ? "Inner window does not have active document." \
387 : "No outer window available!"); \
388 return; \
390 outer->method args; \
391 return; \
392 PR_END_MACRO
394 #define ENSURE_ACTIVE_DOCUMENT(errorresult, err_rval) \
395 PR_BEGIN_MACRO \
396 if (MOZ_UNLIKELY(!HasActiveDocument())) { \
397 aError.Throw(NS_ERROR_XPC_SECURITY_MANAGER_VETO); \
398 return err_rval; \
400 PR_END_MACRO
402 #define DOM_TOUCH_LISTENER_ADDED "dom-touch-listener-added"
403 #define MEMORY_PRESSURE_OBSERVER_TOPIC "memory-pressure"
404 #define PERMISSION_CHANGED_TOPIC "perm-changed"
406 // Amount of time allowed between alert/prompt/confirm before enabling
407 // the stop dialog checkbox.
408 #define DEFAULT_SUCCESSIVE_DIALOG_TIME_LIMIT 3 // 3 sec
410 // Maximum number of successive dialogs before we prompt users to disable
411 // dialogs for this window.
412 #define MAX_SUCCESSIVE_DIALOG_COUNT 5
414 static LazyLogModule gDOMLeakPRLogInner("DOMLeakInner");
415 extern mozilla::LazyLogModule gTimeoutLog;
417 #ifdef DEBUG
418 static LazyLogModule gDocShellAndDOMWindowLeakLogging(
419 "DocShellAndDOMWindowLeak");
420 #endif
422 static FILE* gDumpFile = nullptr;
424 nsGlobalWindowInner::InnerWindowByIdTable*
425 nsGlobalWindowInner::sInnerWindowsById = nullptr;
427 bool nsGlobalWindowInner::sDragServiceDisabled = false;
428 bool nsGlobalWindowInner::sMouseDown = false;
431 * An indirect observer object that means we don't have to implement nsIObserver
432 * on nsGlobalWindow, where any script could see it.
434 class nsGlobalWindowObserver final : public nsIObserver,
435 public nsIInterfaceRequestor,
436 public StorageNotificationObserver {
437 public:
438 explicit nsGlobalWindowObserver(nsGlobalWindowInner* aWindow)
439 : mWindow(aWindow) {}
440 NS_DECL_ISUPPORTS
441 NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
442 const char16_t* aData) override {
443 if (!mWindow) return NS_OK;
444 return mWindow->Observe(aSubject, aTopic, aData);
446 void Forget() { mWindow = nullptr; }
447 NS_IMETHOD GetInterface(const nsIID& aIID, void** aResult) override {
448 if (mWindow && aIID.Equals(NS_GET_IID(nsIDOMWindow)) && mWindow) {
449 return mWindow->QueryInterface(aIID, aResult);
451 return NS_NOINTERFACE;
454 void ObserveStorageNotification(StorageEvent* aEvent,
455 const char16_t* aStorageType,
456 bool aPrivateBrowsing) override {
457 if (mWindow) {
458 mWindow->ObserveStorageNotification(aEvent, aStorageType,
459 aPrivateBrowsing);
463 nsIPrincipal* GetEffectiveStoragePrincipal() const override {
464 return mWindow ? mWindow->GetEffectiveStoragePrincipal() : nullptr;
467 bool IsPrivateBrowsing() const override {
468 return mWindow ? mWindow->IsPrivateBrowsing() : false;
471 nsIEventTarget* GetEventTarget() const override {
472 return mWindow ? mWindow->EventTargetFor(TaskCategory::Other) : nullptr;
475 private:
476 ~nsGlobalWindowObserver() = default;
478 // This reference is non-owning and safe because it's cleared by
479 // nsGlobalWindowInner::FreeInnerObjects().
480 nsGlobalWindowInner* MOZ_NON_OWNING_REF mWindow;
483 NS_IMPL_ISUPPORTS(nsGlobalWindowObserver, nsIObserver, nsIInterfaceRequestor)
485 class IdleRequestExecutor;
487 class IdleRequestExecutorTimeoutHandler final : public TimeoutHandler {
488 public:
489 explicit IdleRequestExecutorTimeoutHandler(IdleRequestExecutor* aExecutor)
490 : mExecutor(aExecutor) {}
492 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
493 NS_DECL_CYCLE_COLLECTION_CLASS(IdleRequestExecutorTimeoutHandler)
495 bool Call(const char* /* unused */) override;
497 private:
498 ~IdleRequestExecutorTimeoutHandler() override = default;
499 RefPtr<IdleRequestExecutor> mExecutor;
502 NS_IMPL_CYCLE_COLLECTION(IdleRequestExecutorTimeoutHandler, mExecutor)
504 NS_IMPL_CYCLE_COLLECTING_ADDREF(IdleRequestExecutorTimeoutHandler)
505 NS_IMPL_CYCLE_COLLECTING_RELEASE(IdleRequestExecutorTimeoutHandler)
507 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestExecutorTimeoutHandler)
508 NS_INTERFACE_MAP_ENTRY(nsISupports)
509 NS_INTERFACE_MAP_END
511 class IdleRequestExecutor final : public nsIRunnable,
512 public nsICancelableRunnable,
513 public nsINamed,
514 public nsIIdleRunnable {
515 public:
516 explicit IdleRequestExecutor(nsGlobalWindowInner* aWindow)
517 : mDispatched(false), mDeadline(TimeStamp::Now()), mWindow(aWindow) {
518 MOZ_DIAGNOSTIC_ASSERT(mWindow);
520 mIdlePeriodLimit = {mDeadline, mWindow->LastIdleRequestHandle()};
521 mDelayedExecutorDispatcher = new IdleRequestExecutorTimeoutHandler(this);
524 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
525 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(IdleRequestExecutor, nsIRunnable)
527 NS_DECL_NSIRUNNABLE
528 NS_DECL_NSINAMED
529 nsresult Cancel() override;
530 void SetDeadline(TimeStamp aDeadline) override;
532 bool IsCancelled() const { return !mWindow || mWindow->IsDying(); }
533 // Checks if aRequest shouldn't execute in the current idle period
534 // since it has been queued from a chained call to
535 // requestIdleCallback from within a running idle callback.
536 bool IneligibleForCurrentIdlePeriod(IdleRequest* aRequest) const {
537 return aRequest->Handle() >= mIdlePeriodLimit.mLastRequestIdInIdlePeriod &&
538 TimeStamp::Now() <= mIdlePeriodLimit.mEndOfIdlePeriod;
541 void MaybeUpdateIdlePeriodLimit();
543 // Maybe dispatch the IdleRequestExecutor. MabyeDispatch will
544 // schedule a delayed dispatch if the associated window is in the
545 // background or if given a time to wait until dispatching.
546 void MaybeDispatch(TimeStamp aDelayUntil = TimeStamp());
547 void ScheduleDispatch();
549 private:
550 struct IdlePeriodLimit {
551 TimeStamp mEndOfIdlePeriod;
552 uint32_t mLastRequestIdInIdlePeriod;
555 void DelayedDispatch(uint32_t aDelay);
557 ~IdleRequestExecutor() override = default;
559 bool mDispatched;
560 TimeStamp mDeadline;
561 IdlePeriodLimit mIdlePeriodLimit;
562 RefPtr<nsGlobalWindowInner> mWindow;
563 // The timeout handler responsible for dispatching this executor in
564 // the case of immediate dispatch to the idle queue isn't
565 // desirable. This is used if we've dispatched all idle callbacks
566 // that are allowed to run in the current idle period, or if the
567 // associated window is currently in the background.
568 RefPtr<TimeoutHandler> mDelayedExecutorDispatcher;
569 // If not Nothing() then this value is the handle to the currently
570 // scheduled delayed executor dispatcher. This is needed to be able
571 // to cancel the timeout handler in case of the executor being
572 // cancelled.
573 Maybe<int32_t> mDelayedExecutorHandle;
576 NS_IMPL_CYCLE_COLLECTION_CLASS(IdleRequestExecutor)
578 NS_IMPL_CYCLE_COLLECTING_ADDREF(IdleRequestExecutor)
579 NS_IMPL_CYCLE_COLLECTING_RELEASE(IdleRequestExecutor)
581 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IdleRequestExecutor)
582 NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
583 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDelayedExecutorDispatcher)
584 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
586 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IdleRequestExecutor)
587 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
588 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDelayedExecutorDispatcher)
589 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
591 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestExecutor)
592 NS_INTERFACE_MAP_ENTRY(nsIRunnable)
593 NS_INTERFACE_MAP_ENTRY(nsICancelableRunnable)
594 NS_INTERFACE_MAP_ENTRY(nsINamed)
595 NS_INTERFACE_MAP_ENTRY(nsIIdleRunnable)
596 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRunnable)
597 NS_INTERFACE_MAP_END
599 NS_IMETHODIMP
600 IdleRequestExecutor::GetName(nsACString& aName) {
601 aName.AssignLiteral("IdleRequestExecutor");
602 return NS_OK;
605 // MOZ_CAN_RUN_SCRIPT_BOUNDARY until nsIRunnable::Run is MOZ_CAN_RUN_SCRIPT.
606 // See bug 1535398.
607 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP IdleRequestExecutor::Run() {
608 MOZ_ASSERT(NS_IsMainThread());
610 mDispatched = false;
611 if (mWindow) {
612 RefPtr<nsGlobalWindowInner> window(mWindow);
613 window->ExecuteIdleRequest(mDeadline);
616 return NS_OK;
619 nsresult IdleRequestExecutor::Cancel() {
620 MOZ_ASSERT(NS_IsMainThread());
622 if (mDelayedExecutorHandle && mWindow) {
623 mWindow->TimeoutManager().ClearTimeout(
624 mDelayedExecutorHandle.value(), Timeout::Reason::eIdleCallbackTimeout);
627 mWindow = nullptr;
628 return NS_OK;
631 void IdleRequestExecutor::SetDeadline(TimeStamp aDeadline) {
632 MOZ_ASSERT(NS_IsMainThread());
634 if (!mWindow) {
635 return;
638 mDeadline = aDeadline;
641 void IdleRequestExecutor::MaybeUpdateIdlePeriodLimit() {
642 if (TimeStamp::Now() > mIdlePeriodLimit.mEndOfIdlePeriod) {
643 mIdlePeriodLimit = {mDeadline, mWindow->LastIdleRequestHandle()};
647 void IdleRequestExecutor::MaybeDispatch(TimeStamp aDelayUntil) {
648 // If we've already dispatched the executor we don't want to do it
649 // again. Also, if we've called IdleRequestExecutor::Cancel mWindow
650 // will be null, which indicates that we shouldn't dispatch this
651 // executor either.
652 if (mDispatched || IsCancelled()) {
653 return;
656 mDispatched = true;
658 nsPIDOMWindowOuter* outer = mWindow->GetOuterWindow();
659 if (outer && outer->IsBackground()) {
660 // Set a timeout handler with a timeout of 0 ms to throttle idle
661 // callback requests coming from a backround window using
662 // background timeout throttling.
663 DelayedDispatch(0);
664 return;
667 TimeStamp now = TimeStamp::Now();
668 if (!aDelayUntil || aDelayUntil < now) {
669 ScheduleDispatch();
670 return;
673 TimeDuration delay = aDelayUntil - now;
674 DelayedDispatch(static_cast<uint32_t>(delay.ToMilliseconds()));
677 void IdleRequestExecutor::ScheduleDispatch() {
678 MOZ_ASSERT(mWindow);
679 mDelayedExecutorHandle = Nothing();
680 RefPtr<IdleRequestExecutor> request = this;
681 NS_DispatchToCurrentThreadQueue(request.forget(), EventQueuePriority::Idle);
684 void IdleRequestExecutor::DelayedDispatch(uint32_t aDelay) {
685 MOZ_ASSERT(mWindow);
686 MOZ_ASSERT(mDelayedExecutorHandle.isNothing());
687 int32_t handle;
688 mWindow->TimeoutManager().SetTimeout(
689 mDelayedExecutorDispatcher, aDelay, false,
690 Timeout::Reason::eIdleCallbackTimeout, &handle);
691 mDelayedExecutorHandle = Some(handle);
694 bool IdleRequestExecutorTimeoutHandler::Call(const char* /* unused */) {
695 if (!mExecutor->IsCancelled()) {
696 mExecutor->ScheduleDispatch();
698 return true;
701 void nsGlobalWindowInner::ScheduleIdleRequestDispatch() {
702 AssertIsOnMainThread();
704 if (!mIdleRequestExecutor) {
705 mIdleRequestExecutor = new IdleRequestExecutor(this);
708 mIdleRequestExecutor->MaybeDispatch();
711 void nsGlobalWindowInner::SuspendIdleRequests() {
712 if (mIdleRequestExecutor) {
713 mIdleRequestExecutor->Cancel();
714 mIdleRequestExecutor = nullptr;
718 void nsGlobalWindowInner::ResumeIdleRequests() {
719 MOZ_ASSERT(!mIdleRequestExecutor);
721 ScheduleIdleRequestDispatch();
724 void nsGlobalWindowInner::RemoveIdleCallback(
725 mozilla::dom::IdleRequest* aRequest) {
726 AssertIsOnMainThread();
728 if (aRequest->HasTimeout()) {
729 mTimeoutManager->ClearTimeout(aRequest->GetTimeoutHandle(),
730 Timeout::Reason::eIdleCallbackTimeout);
733 aRequest->removeFrom(mIdleRequestCallbacks);
736 void nsGlobalWindowInner::RunIdleRequest(IdleRequest* aRequest,
737 DOMHighResTimeStamp aDeadline,
738 bool aDidTimeout) {
739 AssertIsOnMainThread();
740 // XXXbz Do we still need this RefPtr? MOZ_CAN_RUN_SCRIPT should
741 // guarantee that caller is holding a strong ref on the stack.
742 RefPtr<IdleRequest> request(aRequest);
743 RemoveIdleCallback(request);
744 request->IdleRun(this, aDeadline, aDidTimeout);
747 void nsGlobalWindowInner::ExecuteIdleRequest(TimeStamp aDeadline) {
748 AssertIsOnMainThread();
749 RefPtr<IdleRequest> request = mIdleRequestCallbacks.getFirst();
751 if (!request) {
752 // There are no more idle requests, so stop scheduling idle
753 // request callbacks.
754 return;
757 // If the request that we're trying to execute has been queued
758 // during the current idle period, then dispatch it again at the end
759 // of the idle period.
760 if (mIdleRequestExecutor->IneligibleForCurrentIdlePeriod(request)) {
761 mIdleRequestExecutor->MaybeDispatch(aDeadline);
762 return;
765 DOMHighResTimeStamp deadline = 0.0;
767 if (Performance* perf = GetPerformance()) {
768 deadline = perf->GetDOMTiming()->TimeStampToDOMHighRes(aDeadline);
771 mIdleRequestExecutor->MaybeUpdateIdlePeriodLimit();
772 RunIdleRequest(request, deadline, false);
774 // Running the idle callback could've suspended the window, in which
775 // case mIdleRequestExecutor will be null.
776 if (mIdleRequestExecutor) {
777 mIdleRequestExecutor->MaybeDispatch();
781 class IdleRequestTimeoutHandler final : public TimeoutHandler {
782 public:
783 IdleRequestTimeoutHandler(JSContext* aCx, IdleRequest* aIdleRequest,
784 nsPIDOMWindowInner* aWindow)
785 : TimeoutHandler(aCx), mIdleRequest(aIdleRequest), mWindow(aWindow) {}
787 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
788 NS_DECL_CYCLE_COLLECTION_CLASS(IdleRequestTimeoutHandler)
790 MOZ_CAN_RUN_SCRIPT bool Call(const char* /* unused */) override {
791 RefPtr<nsGlobalWindowInner> window(nsGlobalWindowInner::Cast(mWindow));
792 RefPtr<IdleRequest> request(mIdleRequest);
793 window->RunIdleRequest(request, 0.0, true);
794 return true;
797 private:
798 ~IdleRequestTimeoutHandler() override = default;
800 RefPtr<IdleRequest> mIdleRequest;
801 nsCOMPtr<nsPIDOMWindowInner> mWindow;
804 NS_IMPL_CYCLE_COLLECTION(IdleRequestTimeoutHandler, mIdleRequest, mWindow)
806 NS_IMPL_CYCLE_COLLECTING_ADDREF(IdleRequestTimeoutHandler)
807 NS_IMPL_CYCLE_COLLECTING_RELEASE(IdleRequestTimeoutHandler)
809 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestTimeoutHandler)
810 NS_INTERFACE_MAP_ENTRY(nsISupports)
811 NS_INTERFACE_MAP_END
813 uint32_t nsGlobalWindowInner::RequestIdleCallback(
814 JSContext* aCx, IdleRequestCallback& aCallback,
815 const IdleRequestOptions& aOptions, ErrorResult& aError) {
816 AssertIsOnMainThread();
818 if (IsDying()) {
819 return 0;
822 uint32_t handle = mIdleRequestCallbackCounter++;
824 RefPtr<IdleRequest> request = new IdleRequest(&aCallback, handle);
826 if (aOptions.mTimeout.WasPassed()) {
827 int32_t timeoutHandle;
828 RefPtr<TimeoutHandler> handler(
829 new IdleRequestTimeoutHandler(aCx, request, this));
831 nsresult rv = mTimeoutManager->SetTimeout(
832 handler, aOptions.mTimeout.Value(), false,
833 Timeout::Reason::eIdleCallbackTimeout, &timeoutHandle);
835 if (NS_WARN_IF(NS_FAILED(rv))) {
836 return 0;
839 request->SetTimeoutHandle(timeoutHandle);
842 mIdleRequestCallbacks.insertBack(request);
844 if (!IsSuspended()) {
845 ScheduleIdleRequestDispatch();
848 return handle;
851 void nsGlobalWindowInner::CancelIdleCallback(uint32_t aHandle) {
852 for (IdleRequest* r : mIdleRequestCallbacks) {
853 if (r->Handle() == aHandle) {
854 RemoveIdleCallback(r);
855 break;
860 void nsGlobalWindowInner::DisableIdleCallbackRequests() {
861 if (mIdleRequestExecutor) {
862 mIdleRequestExecutor->Cancel();
863 mIdleRequestExecutor = nullptr;
866 while (!mIdleRequestCallbacks.isEmpty()) {
867 RefPtr<IdleRequest> request = mIdleRequestCallbacks.getFirst();
868 RemoveIdleCallback(request);
872 bool nsGlobalWindowInner::IsBackgroundInternal() const {
873 return !mOuterWindow || mOuterWindow->IsBackground();
876 class PromiseDocumentFlushedResolver final {
877 public:
878 PromiseDocumentFlushedResolver(Promise* aPromise,
879 PromiseDocumentFlushedCallback& aCallback)
880 : mPromise(aPromise), mCallback(&aCallback) {}
882 virtual ~PromiseDocumentFlushedResolver() = default;
884 void Call() {
885 nsMutationGuard guard;
886 ErrorResult error;
887 JS::Rooted<JS::Value> returnVal(RootingCx());
888 mCallback->Call(&returnVal, error);
890 if (error.Failed()) {
891 mPromise->MaybeReject(std::move(error));
892 } else if (guard.Mutated(0)) {
893 // Something within the callback mutated the DOM.
894 mPromise->MaybeReject(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
895 } else {
896 mPromise->MaybeResolve(returnVal);
900 void Cancel() { mPromise->MaybeReject(NS_ERROR_ABORT); }
902 RefPtr<Promise> mPromise;
903 RefPtr<PromiseDocumentFlushedCallback> mCallback;
906 //*****************************************************************************
907 //*** nsGlobalWindowInner: Object Management
908 //*****************************************************************************
910 nsGlobalWindowInner::nsGlobalWindowInner(nsGlobalWindowOuter* aOuterWindow,
911 WindowGlobalChild* aActor)
912 : nsPIDOMWindowInner(aOuterWindow, aActor),
913 mWasOffline(false),
914 mHasHadSlowScript(false),
915 mIsChrome(false),
916 mCleanMessageManager(false),
917 mNeedsFocus(true),
918 mHasFocus(false),
919 mFocusByKeyOccurred(false),
920 mDidFireDocElemInserted(false),
921 mHasGamepad(false),
922 mHasXRSession(false),
923 mHasVRDisplayActivateEvents(false),
924 mXRRuntimeDetectionInFlight(false),
925 mXRPermissionRequestInFlight(false),
926 mXRPermissionGranted(false),
927 mWasCurrentInnerWindow(false),
928 mHasSeenGamepadInput(false),
929 mHintedWasLoading(false),
930 mHasOpenedExternalProtocolFrame(false),
931 mSuspendDepth(0),
932 mFreezeDepth(0),
933 #ifdef DEBUG
934 mSerial(0),
935 #endif
936 mFocusMethod(0),
937 mIdleRequestCallbackCounter(1),
938 mIdleRequestExecutor(nullptr),
939 mDialogAbuseCount(0),
940 mAreDialogsEnabled(true),
941 mObservingDidRefresh(false),
942 mIteratingDocumentFlushedResolvers(false),
943 mCanSkipCCGeneration(0),
944 mBeforeUnloadListenerCount(0) {
945 mIsInnerWindow = true;
947 AssertIsOnMainThread();
948 nsLayoutStatics::AddRef();
950 // Initialize the PRCList (this).
951 PR_INIT_CLIST(this);
953 // add this inner window to the outer window list of inners.
954 PR_INSERT_AFTER(this, aOuterWindow);
956 mTimeoutManager = MakeUnique<dom::TimeoutManager>(
957 *this, StaticPrefs::dom_timeout_max_idle_defer_ms());
959 mObserver = new nsGlobalWindowObserver(this);
960 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
961 if (os) {
962 // Watch for online/offline status changes so we can fire events. Use
963 // a strong reference.
964 os->AddObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC, false);
965 os->AddObserver(mObserver, MEMORY_PRESSURE_OBSERVER_TOPIC, false);
966 os->AddObserver(mObserver, PERMISSION_CHANGED_TOPIC, false);
969 Preferences::AddStrongObserver(mObserver, "intl.accept_languages");
971 // Watch for storage notifications so we can fire storage events.
972 RefPtr<StorageNotifierService> sns = StorageNotifierService::GetOrCreate();
973 if (sns) {
974 sns->Register(mObserver);
977 if (XRE_IsContentProcess()) {
978 nsCOMPtr<nsIDocShell> docShell = GetDocShell();
979 if (docShell) {
980 mBrowserChild = docShell->GetBrowserChild();
984 if (gDumpFile == nullptr) {
985 nsAutoCString fname;
986 Preferences::GetCString("browser.dom.window.dump.file", fname);
987 if (!fname.IsEmpty()) {
988 // If this fails to open, Dump() knows to just go to stdout on null.
989 gDumpFile = fopen(fname.get(), "wb+");
990 } else {
991 gDumpFile = stdout;
995 #ifdef DEBUG
996 mSerial = nsContentUtils::InnerOrOuterWindowCreated();
998 MOZ_LOG(gDocShellAndDOMWindowLeakLogging, LogLevel::Info,
999 ("++DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p]\n",
1000 nsContentUtils::GetCurrentInnerOrOuterWindowCount(),
1001 static_cast<void*>(ToCanonicalSupports(this)), getpid(), mSerial,
1002 static_cast<void*>(ToCanonicalSupports(aOuterWindow))));
1003 #endif
1005 MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
1006 ("DOMWINDOW %p created outer=%p", this, aOuterWindow));
1008 // Add ourselves to the inner windows list.
1009 MOZ_ASSERT(sInnerWindowsById, "Inner Windows hash table must be created!");
1010 MOZ_ASSERT(!sInnerWindowsById->Get(mWindowID),
1011 "This window shouldn't be in the hash table yet!");
1012 // We seem to see crashes in release builds because of null
1013 // |sInnerWindowsById|.
1014 if (sInnerWindowsById) {
1015 sInnerWindowsById->Put(mWindowID, this);
1019 #ifdef DEBUG
1021 /* static */
1022 void nsGlobalWindowInner::AssertIsOnMainThread() {
1023 MOZ_ASSERT(NS_IsMainThread());
1026 #endif // DEBUG
1028 /* static */
1029 void nsGlobalWindowInner::Init() {
1030 AssertIsOnMainThread();
1032 NS_ASSERTION(gDOMLeakPRLogInner,
1033 "gDOMLeakPRLogInner should have been initialized!");
1035 sInnerWindowsById = new InnerWindowByIdTable();
1038 nsGlobalWindowInner::~nsGlobalWindowInner() {
1039 AssertIsOnMainThread();
1040 MOZ_ASSERT(!mHintedWasLoading);
1042 if (IsChromeWindow()) {
1043 MOZ_ASSERT(mCleanMessageManager,
1044 "chrome windows may always disconnect the msg manager");
1046 DisconnectAndClearGroupMessageManagers();
1048 if (mChromeFields.mMessageManager) {
1049 static_cast<nsFrameMessageManager*>(mChromeFields.mMessageManager.get())
1050 ->Disconnect();
1053 mCleanMessageManager = false;
1056 // In most cases this should already have been called, but call it again
1057 // here to catch any corner cases.
1058 FreeInnerObjects();
1060 if (sInnerWindowsById) {
1061 sInnerWindowsById->Remove(mWindowID);
1064 nsContentUtils::InnerOrOuterWindowDestroyed();
1066 #ifdef DEBUG
1067 if (MOZ_LOG_TEST(gDocShellAndDOMWindowLeakLogging, LogLevel::Info)) {
1068 nsAutoCString url;
1069 if (mLastOpenedURI) {
1070 url = mLastOpenedURI->GetSpecOrDefault();
1072 // Data URLs can be very long, so truncate to avoid flooding the log.
1073 const uint32_t maxURLLength = 1000;
1074 if (url.Length() > maxURLLength) {
1075 url.Truncate(maxURLLength);
1079 nsGlobalWindowOuter* outer = nsGlobalWindowOuter::Cast(mOuterWindow);
1080 MOZ_LOG(
1081 gDocShellAndDOMWindowLeakLogging, LogLevel::Info,
1082 ("--DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p] [url = "
1083 "%s]\n",
1084 nsContentUtils::GetCurrentInnerOrOuterWindowCount(),
1085 static_cast<void*>(ToCanonicalSupports(this)), getpid(), mSerial,
1086 static_cast<void*>(ToCanonicalSupports(outer)), url.get()));
1088 #endif
1089 MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
1090 ("DOMWINDOW %p destroyed", this));
1092 Telemetry::Accumulate(Telemetry::INNERWINDOWS_WITH_MUTATION_LISTENERS,
1093 mMutationBits ? 1 : 0);
1095 // An inner window is destroyed, pull it out of the outer window's
1096 // list if inner windows.
1098 PR_REMOVE_LINK(this);
1100 // If our outer window's inner window is this window, null out the
1101 // outer window's reference to this window that's being deleted.
1102 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
1103 if (outer) {
1104 outer->MaybeClearInnerWindow(this);
1107 // We don't have to leave the tab group if we are an inner window.
1109 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
1110 if (ac) ac->RemoveWindowAsListener(this);
1112 nsLayoutStatics::Release();
1115 // static
1116 void nsGlobalWindowInner::ShutDown() {
1117 AssertIsOnMainThread();
1119 if (gDumpFile && gDumpFile != stdout) {
1120 fclose(gDumpFile);
1122 gDumpFile = nullptr;
1124 delete sInnerWindowsById;
1125 sInnerWindowsById = nullptr;
1128 void nsGlobalWindowInner::FreeInnerObjects() {
1129 if (IsDying()) {
1130 return;
1132 StartDying();
1134 if (mDoc && mDoc->GetWindowContext()) {
1135 // The document is about to lose its window, so this is a good time to send
1136 // our page use counters.
1138 // (We also do this in Document::SetScriptGlobalObject(nullptr), which
1139 // catches most cases of documents losing their window, but not all.)
1140 mDoc->SendPageUseCounters();
1143 // Make sure that this is called before we null out the document and
1144 // other members that the window destroyed observers could
1145 // re-create.
1146 NotifyDOMWindowDestroyed(this);
1147 if (auto* reporter = nsWindowMemoryReporter::Get()) {
1148 reporter->ObserveDOMWindowDetached(this);
1151 // Kill all of the workers for this window.
1152 CancelWorkersForWindow(*this);
1154 for (RefPtr<mozilla::dom::SharedWorker> pinnedWorker :
1155 mSharedWorkers.ForwardRange()) {
1156 pinnedWorker->Close();
1159 if (mTimeoutManager) {
1160 mTimeoutManager->ClearAllTimeouts();
1163 DisableIdleCallbackRequests();
1165 mChromeEventHandler = nullptr;
1167 if (mListenerManager) {
1168 mListenerManager->RemoveAllListeners();
1169 mListenerManager->Disconnect();
1170 mListenerManager = nullptr;
1173 mHistory = nullptr;
1175 if (mNavigator) {
1176 mNavigator->OnNavigation();
1177 mNavigator->Invalidate();
1178 mNavigator = nullptr;
1181 mScreen = nullptr;
1183 #if defined(MOZ_WIDGET_ANDROID)
1184 mOrientationChangeObserver = nullptr;
1185 #endif
1187 if (mDoc) {
1188 // Remember the document's principal, URI, and CSP.
1189 mDocumentPrincipal = mDoc->NodePrincipal();
1190 mDocumentStoragePrincipal = mDoc->EffectiveStoragePrincipal();
1191 mDocumentPartitionedPrincipal = mDoc->PartitionedPrincipal();
1192 mDocumentURI = mDoc->GetDocumentURI();
1193 mDocBaseURI = mDoc->GetDocBaseURI();
1194 mDocumentCsp = mDoc->GetCsp();
1196 while (mDoc->EventHandlingSuppressed()) {
1197 mDoc->UnsuppressEventHandlingAndFireEvents(false);
1200 if (mObservingDidRefresh) {
1201 PresShell* presShell = mDoc->GetPresShell();
1202 if (presShell) {
1203 Unused << presShell->RemovePostRefreshObserver(this);
1208 // Remove our reference to the document and the document principal.
1209 mFocusedElement = nullptr;
1211 if (mApplicationCache) {
1212 static_cast<nsDOMOfflineResourceList*>(mApplicationCache.get())
1213 ->Disconnect();
1214 mApplicationCache = nullptr;
1217 if (mIndexedDB) {
1218 mIndexedDB->DisconnectFromGlobal(this);
1219 mIndexedDB = nullptr;
1222 nsIGlobalObject::UnlinkObjectsInGlobal();
1224 NotifyWindowIDDestroyed("inner-window-destroyed");
1226 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
1227 mAudioContexts[i]->OnWindowDestroy();
1229 mAudioContexts.Clear();
1231 for (MediaKeys* mediaKeys : mMediaKeysInstances) {
1232 mediaKeys->OnInnerWindowDestroy();
1234 mMediaKeysInstances.Clear();
1236 DisableGamepadUpdates();
1237 mHasGamepad = false;
1238 mGamepads.Clear();
1239 DisableVRUpdates();
1240 mHasXRSession = false;
1241 mHasVRDisplayActivateEvents = false;
1242 mXRRuntimeDetectionInFlight = false;
1243 mXRPermissionRequestInFlight = false;
1244 mXRPermissionGranted = false;
1245 mVRDisplays.Clear();
1247 // This breaks a cycle between the window and the ClientSource object.
1248 mClientSource.reset();
1250 if (mWindowGlobalChild) {
1251 // Remove any remaining listeners.
1252 int64_t nListeners = mWindowGlobalChild->BeforeUnloadListeners();
1253 for (int64_t i = 0; i < nListeners; ++i) {
1254 mWindowGlobalChild->BeforeUnloadRemoved();
1256 MOZ_ASSERT(mWindowGlobalChild->BeforeUnloadListeners() == 0);
1259 // If we have any promiseDocumentFlushed callbacks, fire them now so
1260 // that the Promises can resolve.
1261 CallDocumentFlushedResolvers();
1262 mObservingDidRefresh = false;
1264 DisconnectEventTargetObjects();
1266 if (mObserver) {
1267 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
1268 if (os) {
1269 os->RemoveObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC);
1270 os->RemoveObserver(mObserver, MEMORY_PRESSURE_OBSERVER_TOPIC);
1271 os->RemoveObserver(mObserver, PERMISSION_CHANGED_TOPIC);
1274 RefPtr<StorageNotifierService> sns = StorageNotifierService::GetOrCreate();
1275 if (sns) {
1276 sns->Unregister(mObserver);
1279 Preferences::RemoveObserver(mObserver, "intl.accept_languages");
1281 // Drop its reference to this dying window, in case for some bogus reason
1282 // the object stays around.
1283 mObserver->Forget();
1286 mMenubar = nullptr;
1287 mToolbar = nullptr;
1288 mLocationbar = nullptr;
1289 mPersonalbar = nullptr;
1290 mStatusbar = nullptr;
1291 mScrollbars = nullptr;
1293 mConsole = nullptr;
1295 mPaintWorklet = nullptr;
1297 mExternal = nullptr;
1298 mInstallTrigger = nullptr;
1300 mLocalStorage = nullptr;
1301 mSessionStorage = nullptr;
1302 mPerformance = nullptr;
1304 mContentMediaController = nullptr;
1306 mSharedWorkers.Clear();
1308 #ifdef MOZ_WEBSPEECH
1309 mSpeechSynthesis = nullptr;
1310 #endif
1312 #ifdef MOZ_GLEAN
1313 mGlean = nullptr;
1314 mGleanPings = nullptr;
1315 #endif
1317 mParentTarget = nullptr;
1319 if (mCleanMessageManager) {
1320 MOZ_ASSERT(mIsChrome, "only chrome should have msg manager cleaned");
1321 if (mChromeFields.mMessageManager) {
1322 mChromeFields.mMessageManager->Disconnect();
1326 if (mWindowGlobalChild && !mWindowGlobalChild->IsClosed()) {
1327 mWindowGlobalChild->Destroy();
1329 mWindowGlobalChild = nullptr;
1331 mIntlUtils = nullptr;
1333 HintIsLoading(false);
1336 //*****************************************************************************
1337 // nsGlobalWindowInner::nsISupports
1338 //*****************************************************************************
1340 // QueryInterface implementation for nsGlobalWindowInner
1341 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGlobalWindowInner)
1342 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
1343 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, EventTarget)
1344 NS_INTERFACE_MAP_ENTRY(nsIDOMWindow)
1345 NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
1346 NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObject)
1347 NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
1348 NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget)
1349 NS_INTERFACE_MAP_ENTRY(nsPIDOMWindowInner)
1350 NS_INTERFACE_MAP_ENTRY(mozIDOMWindow)
1351 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDOMChromeWindow, IsChromeWindow())
1352 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
1353 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
1354 NS_INTERFACE_MAP_END
1356 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGlobalWindowInner)
1357 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGlobalWindowInner)
1359 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGlobalWindowInner)
1360 if (tmp->IsBlackForCC(false)) {
1361 if (nsCCUncollectableMarker::InGeneration(tmp->mCanSkipCCGeneration)) {
1362 return true;
1364 tmp->mCanSkipCCGeneration = nsCCUncollectableMarker::sGeneration;
1365 if (EventListenerManager* elm = tmp->GetExistingListenerManager()) {
1366 elm->MarkForCC();
1368 if (tmp->mTimeoutManager) {
1369 tmp->mTimeoutManager->UnmarkGrayTimers();
1371 return true;
1373 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
1375 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsGlobalWindowInner)
1376 return tmp->IsBlackForCC(true);
1377 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
1379 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsGlobalWindowInner)
1380 return tmp->IsBlackForCC(false);
1381 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
1383 NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalWindowInner)
1385 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindowInner)
1386 if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
1387 char name[512];
1388 nsAutoCString uri;
1389 if (tmp->mDoc && tmp->mDoc->GetDocumentURI()) {
1390 uri = tmp->mDoc->GetDocumentURI()->GetSpecOrDefault();
1392 SprintfLiteral(name, "nsGlobalWindowInner # %" PRIu64 " inner %s",
1393 tmp->mWindowID, uri.get());
1394 cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
1395 } else {
1396 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsGlobalWindowInner, tmp->mRefCnt.get())
1399 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNavigator)
1401 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance)
1403 #ifdef MOZ_WEBSPEECH
1404 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSpeechSynthesis)
1405 #endif
1407 #ifdef MOZ_GLEAN
1408 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlean)
1409 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGleanPings)
1410 #endif
1412 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOuterWindow)
1414 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTopInnerWindow)
1416 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager)
1418 if (tmp->mTimeoutManager) {
1419 tmp->mTimeoutManager->ForEachUnorderedTimeout([&cb](Timeout* timeout) {
1420 cb.NoteNativeChild(timeout, NS_CYCLE_COLLECTION_PARTICIPANT(Timeout));
1424 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocation)
1425 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHistory)
1426 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCustomElements)
1427 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSharedWorkers)
1428 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStorage)
1429 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSessionStorage)
1430 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mApplicationCache)
1431 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndexedDB)
1432 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPrincipal)
1433 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentStoragePrincipal)
1434 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPartitionedPrincipal)
1435 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentCsp)
1436 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowserChild)
1437 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDoc)
1439 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleRequestExecutor)
1440 for (IdleRequest* request : tmp->mIdleRequestCallbacks) {
1441 cb.NoteNativeChild(request, NS_CYCLE_COLLECTION_PARTICIPANT(IdleRequest));
1444 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mClientSource)
1446 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGamepads)
1448 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCacheStorage)
1449 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVRDisplays)
1451 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDebuggerNotificationManager)
1453 // Traverse stuff from nsPIDOMWindow
1454 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeEventHandler)
1455 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentTarget)
1456 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFocusedElement)
1458 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMenubar)
1459 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mToolbar)
1460 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocationbar)
1461 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPersonalbar)
1462 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStatusbar)
1463 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScrollbars)
1464 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCrypto)
1465 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mU2F)
1466 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsole)
1467 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPaintWorklet)
1468 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExternal)
1469 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInstallTrigger)
1470 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIntlUtils)
1472 tmp->TraverseObjectsInGlobal(cb);
1474 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeFields.mMessageManager)
1475 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeFields.mGroupMessageManagers)
1477 for (size_t i = 0; i < tmp->mDocumentFlushedResolvers.Length(); i++) {
1478 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentFlushedResolvers[i]->mPromise);
1479 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentFlushedResolvers[i]->mCallback);
1482 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1484 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindowInner)
1485 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
1486 if (sInnerWindowsById) {
1487 sInnerWindowsById->Remove(tmp->mWindowID);
1490 JSObject* wrapper = tmp->GetWrapperPreserveColor();
1491 if (wrapper) {
1492 // Mark our realm as dead, so the JS engine won't hand out our
1493 // global after this point.
1494 JS::RealmBehaviorsRef(js::GetNonCCWObjectRealm(wrapper)).setNonLive();
1497 NS_IMPL_CYCLE_COLLECTION_UNLINK(mNavigator)
1499 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance)
1501 #ifdef MOZ_WEBSPEECH
1502 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSpeechSynthesis)
1503 #endif
1505 #ifdef MOZ_GLEAN
1506 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlean)
1507 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGleanPings)
1508 #endif
1510 if (tmp->mOuterWindow) {
1511 nsGlobalWindowOuter::Cast(tmp->mOuterWindow)->MaybeClearInnerWindow(tmp);
1512 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOuterWindow)
1515 if (tmp->mListenerManager) {
1516 tmp->mListenerManager->Disconnect();
1517 NS_IMPL_CYCLE_COLLECTION_UNLINK(mListenerManager)
1520 // Here the Timeouts list would've been unlinked, but we rely on
1521 // that Timeout objects have been traced and will remove themselves
1522 // while unlinking.
1524 tmp->UpdateTopInnerWindow();
1525 NS_IMPL_CYCLE_COLLECTION_UNLINK(mTopInnerWindow)
1527 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocation)
1528 NS_IMPL_CYCLE_COLLECTION_UNLINK(mHistory)
1529 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCustomElements)
1530 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSharedWorkers)
1531 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalStorage)
1532 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSessionStorage)
1533 if (tmp->mApplicationCache) {
1534 static_cast<nsDOMOfflineResourceList*>(tmp->mApplicationCache.get())
1535 ->Disconnect();
1536 NS_IMPL_CYCLE_COLLECTION_UNLINK(mApplicationCache)
1538 if (tmp->mIndexedDB) {
1539 tmp->mIndexedDB->DisconnectFromGlobal(tmp);
1540 NS_IMPL_CYCLE_COLLECTION_UNLINK(mIndexedDB)
1542 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPrincipal)
1543 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentStoragePrincipal)
1544 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPartitionedPrincipal)
1545 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentCsp)
1546 NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowserChild)
1547 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDoc)
1549 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGamepads)
1551 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCacheStorage)
1552 NS_IMPL_CYCLE_COLLECTION_UNLINK(mVRDisplays)
1554 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDebuggerNotificationManager)
1556 // Unlink stuff from nsPIDOMWindow
1557 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeEventHandler)
1558 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParentTarget)
1559 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFocusedElement)
1561 NS_IMPL_CYCLE_COLLECTION_UNLINK(mMenubar)
1562 NS_IMPL_CYCLE_COLLECTION_UNLINK(mToolbar)
1563 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocationbar)
1564 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPersonalbar)
1565 NS_IMPL_CYCLE_COLLECTION_UNLINK(mStatusbar)
1566 NS_IMPL_CYCLE_COLLECTION_UNLINK(mScrollbars)
1567 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCrypto)
1568 NS_IMPL_CYCLE_COLLECTION_UNLINK(mU2F)
1569 NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole)
1570 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPaintWorklet)
1571 NS_IMPL_CYCLE_COLLECTION_UNLINK(mExternal)
1572 NS_IMPL_CYCLE_COLLECTION_UNLINK(mInstallTrigger)
1573 NS_IMPL_CYCLE_COLLECTION_UNLINK(mIntlUtils)
1575 tmp->UnlinkObjectsInGlobal();
1577 NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleRequestExecutor)
1579 // Here the IdleRequest list would've been unlinked, but we rely on
1580 // that IdleRequest objects have been traced and will remove
1581 // themselves while unlinking.
1583 NS_IMPL_CYCLE_COLLECTION_UNLINK(mClientSource)
1585 if (tmp->IsChromeWindow()) {
1586 if (tmp->mChromeFields.mMessageManager) {
1587 static_cast<nsFrameMessageManager*>(
1588 tmp->mChromeFields.mMessageManager.get())
1589 ->Disconnect();
1590 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeFields.mMessageManager)
1592 tmp->DisconnectAndClearGroupMessageManagers();
1593 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeFields.mGroupMessageManagers)
1596 for (size_t i = 0; i < tmp->mDocumentFlushedResolvers.Length(); i++) {
1597 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentFlushedResolvers[i]->mPromise);
1598 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentFlushedResolvers[i]->mCallback);
1600 tmp->mDocumentFlushedResolvers.Clear();
1602 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
1603 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1605 #ifdef DEBUG
1606 void nsGlobalWindowInner::RiskyUnlink() {
1607 NS_CYCLE_COLLECTION_INNERNAME.Unlink(this);
1609 #endif
1611 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGlobalWindowInner)
1612 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
1613 NS_IMPL_CYCLE_COLLECTION_TRACE_END
1615 bool nsGlobalWindowInner::IsBlackForCC(bool aTracingNeeded) {
1616 if (!nsCCUncollectableMarker::sGeneration) {
1617 return false;
1620 return (nsCCUncollectableMarker::InGeneration(GetMarkedCCGeneration()) ||
1621 HasKnownLiveWrapper()) &&
1622 (!aTracingNeeded || HasNothingToTrace(ToSupports(this)));
1625 //*****************************************************************************
1626 // nsGlobalWindowInner::nsIScriptGlobalObject
1627 //*****************************************************************************
1629 nsresult nsGlobalWindowInner::EnsureScriptEnvironment() {
1630 // NOTE: We can't use FORWARD_TO_OUTER here because we don't want to fail if
1631 // we're called on an inactive inner window.
1632 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
1633 if (!outer) {
1634 NS_WARNING("No outer window available!");
1635 return NS_ERROR_FAILURE;
1637 return outer->EnsureScriptEnvironment();
1640 nsIScriptContext* nsGlobalWindowInner::GetScriptContext() {
1641 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
1642 if (!outer) {
1643 return nullptr;
1645 return outer->GetScriptContext();
1648 void nsGlobalWindowInner::TraceGlobalJSObject(JSTracer* aTrc) {
1649 TraceWrapper(aTrc, "active window global");
1652 void nsGlobalWindowInner::UpdateAutoplayPermission() {
1653 if (!GetWindowContext()) {
1654 return;
1656 uint32_t perm = AutoplayPolicy::GetSiteAutoplayPermission(GetPrincipal());
1657 if (GetWindowContext()->GetAutoplayPermission() == perm) {
1658 return;
1661 // Setting autoplay permission on a discarded context has no effect.
1662 Unused << GetWindowContext()->SetAutoplayPermission(perm);
1665 void nsGlobalWindowInner::UpdateShortcutsPermission() {
1666 if (!GetWindowContext() ||
1667 !GetWindowContext()->GetBrowsingContext()->IsTop()) {
1668 // We only cache the shortcuts permission on top-level WindowContexts
1669 // since we always check the top-level principal for the permission.
1670 return;
1673 uint32_t perm = GetShortcutsPermission(GetPrincipal());
1675 if (GetWindowContext()->GetShortcutsPermission() == perm) {
1676 return;
1679 // If the WindowContext is discarded this has no effect.
1680 Unused << GetWindowContext()->SetShortcutsPermission(perm);
1683 /* static */
1684 uint32_t nsGlobalWindowInner::GetShortcutsPermission(nsIPrincipal* aPrincipal) {
1685 uint32_t perm = nsIPermissionManager::DENY_ACTION;
1686 nsCOMPtr<nsIPermissionManager> permMgr =
1687 mozilla::services::GetPermissionManager();
1688 if (aPrincipal && permMgr) {
1689 permMgr->TestExactPermissionFromPrincipal(aPrincipal, "shortcuts"_ns,
1690 &perm);
1692 return perm;
1695 void nsGlobalWindowInner::UpdatePopupPermission() {
1696 if (!GetWindowContext()) {
1697 return;
1700 uint32_t perm = PopupBlocker::GetPopupPermission(GetPrincipal());
1701 if (GetWindowContext()->GetPopupPermission() == perm) {
1702 return;
1705 // If the WindowContext is discarded this has no effect.
1706 Unused << GetWindowContext()->SetPopupPermission(perm);
1709 void nsGlobalWindowInner::UpdatePermissions() {
1710 if (!GetWindowContext()) {
1711 return;
1714 nsCOMPtr<nsIPrincipal> principal = GetPrincipal();
1715 RefPtr<WindowContext> windowContext = GetWindowContext();
1717 WindowContext::Transaction txn;
1718 txn.SetAutoplayPermission(
1719 AutoplayPolicy::GetSiteAutoplayPermission(principal));
1720 txn.SetPopupPermission(PopupBlocker::GetPopupPermission(principal));
1722 if (windowContext->IsTop()) {
1723 txn.SetShortcutsPermission(GetShortcutsPermission(principal));
1726 // Setting permissions on a discarded WindowContext has no effect
1727 Unused << txn.Commit(windowContext);
1730 void nsGlobalWindowInner::InitDocumentDependentState(JSContext* aCx) {
1731 MOZ_ASSERT(mDoc);
1733 if (MOZ_LOG_TEST(gDOMLeakPRLogInner, LogLevel::Debug)) {
1734 nsIURI* uri = mDoc->GetDocumentURI();
1735 MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
1736 ("DOMWINDOW %p SetNewDocument %s", this,
1737 uri ? uri->GetSpecOrDefault().get() : ""));
1740 mFocusedElement = nullptr;
1741 mLocalStorage = nullptr;
1742 mSessionStorage = nullptr;
1743 mPerformance = nullptr;
1745 // This must be called after nullifying the internal objects because here we
1746 // could recreate them, calling the getter methods, and store them into the JS
1747 // slots. If we nullify them after, the slot values and the objects will be
1748 // out of sync.
1749 ClearDocumentDependentSlots(aCx);
1751 if (!mWindowGlobalChild) {
1752 mWindowGlobalChild = WindowGlobalChild::Create(this);
1754 MOZ_ASSERT(!GetWindowContext()->HasBeenUserGestureActivated(),
1755 "WindowContext should always not have user gesture activation at "
1756 "this point.");
1758 UpdatePermissions();
1760 RefPtr<PermissionDelegateHandler> permDelegateHandler =
1761 mDoc->GetPermissionDelegateHandler();
1763 if (permDelegateHandler) {
1764 permDelegateHandler->PopulateAllDelegatedPermissions();
1767 #if defined(MOZ_WIDGET_ANDROID)
1768 // When we insert the new document to the window in the top-level browsing
1769 // context, we should reset the status of the request which is used for the
1770 // previous document.
1771 if (mWindowGlobalChild && GetBrowsingContext() &&
1772 !GetBrowsingContext()->GetParent()) {
1773 // Return value of setting synced field should be checked. See bug 1656492.
1774 Unused << GetBrowsingContext()->ResetGVAutoplayRequestStatus();
1776 #endif
1778 #ifdef DEBUG
1779 mLastOpenedURI = mDoc->GetDocumentURI();
1780 #endif
1782 Telemetry::Accumulate(Telemetry::INNERWINDOWS_WITH_MUTATION_LISTENERS,
1783 mMutationBits ? 1 : 0);
1785 // Clear our mutation bitfield.
1786 mMutationBits = 0;
1789 nsresult nsGlobalWindowInner::EnsureClientSource() {
1790 MOZ_DIAGNOSTIC_ASSERT(mDoc);
1792 bool newClientSource = false;
1794 // Get the load info for the document if we performed a load. Be careful not
1795 // to look at local URLs, though. Local URLs are those that have a scheme of:
1796 // * about:
1797 // * data:
1798 // * blob:
1799 // We also do an additional check here so that we only treat about:blank
1800 // and about:srcdoc as local URLs. Other internal firefox about: URLs should
1801 // not be treated this way.
1802 nsCOMPtr<nsILoadInfo> loadInfo;
1803 nsCOMPtr<nsIChannel> channel = mDoc->GetChannel();
1804 if (channel) {
1805 nsCOMPtr<nsIURI> uri;
1806 Unused << channel->GetURI(getter_AddRefs(uri));
1808 bool ignoreLoadInfo = false;
1810 // Note, this is mostly copied from NS_IsAboutBlank(). Its duplicated
1811 // here so we can efficiently check about:srcdoc as well.
1812 if (uri->SchemeIs("about")) {
1813 nsCString spec = uri->GetSpecOrDefault();
1814 ignoreLoadInfo = spec.EqualsLiteral("about:blank") ||
1815 spec.EqualsLiteral("about:srcdoc");
1816 } else {
1817 // Its not an about: URL, so now check for our other URL types.
1818 ignoreLoadInfo = uri->SchemeIs("data") || uri->SchemeIs("blob");
1821 if (!ignoreLoadInfo) {
1822 loadInfo = channel->LoadInfo();
1826 // Take the initial client source from the docshell immediately. Even if we
1827 // don't end up using it here we should consume it.
1828 UniquePtr<ClientSource> initialClientSource;
1829 nsIDocShell* docshell = GetDocShell();
1830 if (docshell) {
1831 initialClientSource = docshell->TakeInitialClientSource();
1834 // Try to get the reserved client from the LoadInfo. A Client is
1835 // reserved at the start of the channel load if there is not an
1836 // initial about:blank document that will be reused. It is also
1837 // created if the channel load encounters a cross-origin redirect.
1838 if (loadInfo) {
1839 UniquePtr<ClientSource> reservedClient =
1840 loadInfo->TakeReservedClientSource();
1841 if (reservedClient) {
1842 mClientSource.reset();
1843 mClientSource = std::move(reservedClient);
1844 newClientSource = true;
1848 // We don't have a LoadInfo reserved client, but maybe we should
1849 // be inheriting an initial one from the docshell. This means
1850 // that the docshell started the channel load before creating the
1851 // initial about:blank document. This is an optimization, though,
1852 // and it created an initial Client as a placeholder for the document.
1853 // In this case we want to inherit this placeholder Client here.
1854 if (!mClientSource) {
1855 mClientSource = std::move(initialClientSource);
1856 if (mClientSource) {
1857 newClientSource = true;
1861 // Verify the final ClientSource principal matches the final document
1862 // principal. The ClientChannelHelper handles things like network
1863 // redirects, but there are other ways the document principal can change.
1864 // For example, if something sets the nsIChannel.owner property, then
1865 // the final channel principal can be anything. Unfortunately there is
1866 // no good way to detect this until after the channel completes loading.
1868 // For now we handle this just by reseting the ClientSource. This will
1869 // result in a new ClientSource with the correct principal being created.
1870 // To APIs like ServiceWorker and Clients API it will look like there was
1871 // an initial content page created that was then immediately replaced.
1872 // This is pretty close to what we are actually doing.
1873 if (mClientSource) {
1874 auto principalOrErr = mClientSource->Info().GetPrincipal();
1875 nsCOMPtr<nsIPrincipal> clientPrincipal =
1876 principalOrErr.isOk() ? principalOrErr.unwrap() : nullptr;
1877 if (!clientPrincipal || !clientPrincipal->Equals(mDoc->NodePrincipal())) {
1878 mClientSource.reset();
1882 // If we don't have a reserved client or an initial client, then create
1883 // one now. This can happen in certain cases where we avoid preallocating
1884 // the client in the docshell. This mainly occurs in situations where
1885 // the principal is not clearly inherited from the parent; e.g. sandboxed
1886 // iframes, window.open(), etc.
1888 // We also do this late ClientSource creation if the final document ended
1889 // up with a different principal.
1891 // TODO: We may not be marking initial about:blank documents created
1892 // this way as controlled by a service worker properly. The
1893 // controller should be coming from the same place as the inheritted
1894 // principal. We do this in docshell, but as mentioned we aren't
1895 // smart enough to handle all cases yet. For example, a
1896 // window.open() with new URL should inherit the controller from
1897 // the opener, but we probably don't handle that yet.
1898 if (!mClientSource) {
1899 mClientSource = ClientManager::CreateSource(
1900 ClientType::Window, EventTargetFor(TaskCategory::Other),
1901 mDoc->NodePrincipal());
1902 MOZ_DIAGNOSTIC_ASSERT(mClientSource);
1903 newClientSource = true;
1905 // Note, we don't apply the loadinfo controller below if we create
1906 // the ClientSource here.
1909 // The load may have started controlling the Client as well. If
1910 // so, mark it as controlled immediately here. The actor may
1911 // or may not have been notified by the parent side about being
1912 // controlled yet.
1914 // Note: We should be careful not to control a client that was created late.
1915 // These clients were not seen by the ServiceWorkerManager when it
1916 // marked the LoadInfo controlled and it won't know about them. Its
1917 // also possible we are creating the client late due to the final
1918 // principal changing and these clients should definitely not be
1919 // controlled by a service worker with a different principal.
1920 else if (loadInfo) {
1921 const Maybe<ServiceWorkerDescriptor> controller = loadInfo->GetController();
1922 if (controller.isSome()) {
1923 mClientSource->SetController(controller.ref());
1926 // We also have to handle the case where te initial about:blank is
1927 // controlled due to inheritting the service worker from its parent,
1928 // but the actual nsIChannel load is not covered by any service worker.
1929 // In this case we want the final page to be uncontrolled. There is
1930 // an open spec issue about how exactly this should be handled, but for
1931 // now we just force creation of a new ClientSource to clear the
1932 // controller.
1934 // https://github.com/w3c/ServiceWorker/issues/1232
1936 else if (mClientSource->GetController().isSome()) {
1937 mClientSource.reset();
1938 mClientSource = ClientManager::CreateSource(
1939 ClientType::Window, EventTargetFor(TaskCategory::Other),
1940 mDoc->NodePrincipal());
1941 MOZ_DIAGNOSTIC_ASSERT(mClientSource);
1942 newClientSource = true;
1946 if (mClientSource) {
1947 // Generally the CSP is stored within the Client and cached on the document.
1948 // At the time of CSP parsing however, the Client has not been created yet,
1949 // hence we store the CSP on the document and propagate/sync the CSP with
1950 // Client here when we create the Client.
1951 mClientSource->SetCsp(mDoc->GetCsp());
1953 DocGroup* docGroup = GetDocGroup();
1954 MOZ_DIAGNOSTIC_ASSERT(docGroup);
1955 mClientSource->SetAgentClusterId(docGroup->AgentClusterId());
1957 if (mWindowGlobalChild) {
1958 mWindowGlobalChild->SendSetClientInfo(mClientSource->Info().ToIPC());
1962 // Its possible that we got a client just after being frozen in
1963 // the bfcache. In that case freeze the client immediately.
1964 if (newClientSource && IsFrozen()) {
1965 mClientSource->Freeze();
1968 return NS_OK;
1971 nsresult nsGlobalWindowInner::ExecutionReady() {
1972 nsresult rv = EnsureClientSource();
1973 NS_ENSURE_SUCCESS(rv, rv);
1975 rv = mClientSource->WindowExecutionReady(this);
1976 NS_ENSURE_SUCCESS(rv, rv);
1978 return NS_OK;
1981 void nsGlobalWindowInner::UpdateParentTarget() {
1982 // NOTE: This method is identical to
1983 // nsGlobalWindowOuter::UpdateParentTarget(). IF YOU UPDATE THIS METHOD,
1984 // UPDATE THE OTHER ONE TOO!
1986 // Try to get our frame element's tab child global (its in-process message
1987 // manager). If that fails, fall back to the chrome event handler's tab
1988 // child global, and if it doesn't have one, just use the chrome event
1989 // handler itself.
1991 nsPIDOMWindowOuter* outer = GetOuterWindow();
1992 if (!outer) {
1993 return;
1995 nsCOMPtr<Element> frameElement = outer->GetFrameElementInternal();
1996 nsCOMPtr<EventTarget> eventTarget =
1997 nsContentUtils::TryGetBrowserChildGlobal(frameElement);
1999 if (!eventTarget) {
2000 nsGlobalWindowOuter* topWin = GetInProcessScriptableTopInternal();
2001 if (topWin) {
2002 frameElement = topWin->GetFrameElementInternal();
2003 eventTarget = nsContentUtils::TryGetBrowserChildGlobal(frameElement);
2007 if (!eventTarget) {
2008 eventTarget = nsContentUtils::TryGetBrowserChildGlobal(mChromeEventHandler);
2011 if (!eventTarget) {
2012 eventTarget = mChromeEventHandler;
2015 mParentTarget = eventTarget;
2018 EventTarget* nsGlobalWindowInner::GetTargetForDOMEvent() {
2019 return GetOuterWindowInternal();
2022 void nsGlobalWindowInner::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
2023 EventMessage msg = aVisitor.mEvent->mMessage;
2025 aVisitor.mCanHandle = true;
2026 aVisitor.mForceContentDispatch = true; // FIXME! Bug 329119
2027 if (msg == eResize && aVisitor.mEvent->IsTrusted()) {
2028 // QIing to window so that we can keep the old behavior also in case
2029 // a child window is handling resize.
2030 nsCOMPtr<nsPIDOMWindowInner> window =
2031 do_QueryInterface(aVisitor.mEvent->mOriginalTarget);
2032 if (window) {
2033 mIsHandlingResizeEvent = true;
2035 } else if (msg == eMouseDown && aVisitor.mEvent->IsTrusted()) {
2036 sMouseDown = true;
2037 } else if ((msg == eMouseUp || msg == eDragEnd) &&
2038 aVisitor.mEvent->IsTrusted()) {
2039 sMouseDown = false;
2040 if (sDragServiceDisabled) {
2041 nsCOMPtr<nsIDragService> ds =
2042 do_GetService("@mozilla.org/widget/dragservice;1");
2043 if (ds) {
2044 sDragServiceDisabled = false;
2045 ds->Unsuppress();
2050 aVisitor.SetParentTarget(GetParentTarget(), true);
2053 bool nsGlobalWindowInner::DialogsAreBeingAbused() {
2054 NS_ASSERTION(GetInProcessScriptableTopInternal() &&
2055 GetInProcessScriptableTopInternal()
2056 ->GetCurrentInnerWindowInternal() == this,
2057 "DialogsAreBeingAbused called with invalid window");
2059 if (mLastDialogQuitTime.IsNull() || nsContentUtils::IsCallerChrome()) {
2060 return false;
2063 TimeDuration dialogInterval(TimeStamp::Now() - mLastDialogQuitTime);
2064 if (dialogInterval.ToSeconds() <
2065 Preferences::GetInt("dom.successive_dialog_time_limit",
2066 DEFAULT_SUCCESSIVE_DIALOG_TIME_LIMIT)) {
2067 mDialogAbuseCount++;
2069 return PopupBlocker::GetPopupControlState() > PopupBlocker::openAllowed ||
2070 mDialogAbuseCount > MAX_SUCCESSIVE_DIALOG_COUNT;
2073 // Reset the abuse counter
2074 mDialogAbuseCount = 0;
2076 return false;
2079 void nsGlobalWindowInner::FireFrameLoadEvent() {
2080 // If we're not in a content frame, or are at a BrowsingContext tree boundary,
2081 // such as the content-chrome boundary, don't fire the "load" event.
2082 if (GetBrowsingContext()->IsTopContent() ||
2083 GetBrowsingContext()->IsChrome()) {
2084 return;
2087 // If embedder is same-process, fire the event on our embedder element.
2089 // XXX: Bug 1440212 is looking into potentially changing this behaviour to act
2090 // more like the remote case when in-process.
2091 RefPtr<Element> element = GetBrowsingContext()->GetEmbedderElement();
2092 if (element) {
2093 nsEventStatus status = nsEventStatus_eIgnore;
2094 WidgetEvent event(/* aIsTrusted = */ true, eLoad);
2095 event.mFlags.mBubbles = false;
2096 event.mFlags.mCancelable = false;
2098 if (mozilla::dom::DocGroup::TryToLoadIframesInBackground()) {
2099 nsDocShell* ds = nsDocShell::Cast(GetDocShell());
2101 if (ds && !ds->HasFakeOnLoadDispatched()) {
2102 EventDispatcher::Dispatch(element, nullptr, &event, nullptr, &status);
2104 } else {
2105 // Most of the time we could get a pres context to pass in here,
2106 // but not always (i.e. if this window is not shown there won't
2107 // be a pres context available). Since we're not firing a GUI
2108 // event we don't need a pres context anyway so we just pass
2109 // null as the pres context all the time here.
2110 EventDispatcher::Dispatch(element, nullptr, &event, nullptr, &status);
2112 return;
2115 // We don't have an in-process embedder. Try to get our `BrowserChild` actor
2116 // to send a message to that embedder. We want to double-check that our outer
2117 // window is actually the one at the root of this browserChild though, just in
2118 // case.
2119 RefPtr<BrowserChild> browserChild =
2120 BrowserChild::GetFrom(static_cast<nsPIDOMWindowInner*>(this));
2121 if (browserChild) {
2122 // Double-check that our outer window is actually at the root of this
2123 // `BrowserChild`, in case we're in an odd maybe-unhosted situation like a
2124 // print preview dialog.
2125 nsCOMPtr<nsPIDOMWindowOuter> rootOuter =
2126 do_GetInterface(browserChild->WebNavigation());
2127 if (!rootOuter || rootOuter != GetOuterWindow()) {
2128 return;
2131 mozilla::Unused << browserChild->SendMaybeFireEmbedderLoadEvents(
2132 EmbedderElementEventType::LoadEvent);
2136 nsresult nsGlobalWindowInner::PostHandleEvent(EventChainPostVisitor& aVisitor) {
2137 // Return early if there is nothing to do.
2138 switch (aVisitor.mEvent->mMessage) {
2139 case eResize:
2140 case eUnload:
2141 case eLoad:
2142 break;
2143 default:
2144 return NS_OK;
2147 /* mChromeEventHandler and mContext go dangling in the middle of this
2148 function under some circumstances (events that destroy the window)
2149 without this addref. */
2150 RefPtr<EventTarget> kungFuDeathGrip1(mChromeEventHandler);
2151 mozilla::Unused
2152 << kungFuDeathGrip1; // These aren't referred to through the function
2153 nsCOMPtr<nsIScriptContext> kungFuDeathGrip2(GetContextInternal());
2154 mozilla::Unused
2155 << kungFuDeathGrip2; // These aren't referred to through the function
2157 if (aVisitor.mEvent->mMessage == eResize) {
2158 mIsHandlingResizeEvent = false;
2159 } else if (aVisitor.mEvent->mMessage == eUnload &&
2160 aVisitor.mEvent->IsTrusted()) {
2161 // If any VR display presentation is active at unload, the next page
2162 // will receive a vrdisplayactive event to indicate that it should
2163 // immediately begin vr presentation. This should occur when navigating
2164 // forwards, navigating backwards, and on page reload.
2165 for (const auto& display : mVRDisplays) {
2166 if (display->IsPresenting()) {
2167 display->StartVRNavigation();
2168 // Save this VR display ID to trigger vrdisplayactivate event
2169 // after the next load event.
2170 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
2171 if (outer) {
2172 outer->SetAutoActivateVRDisplayID(display->DisplayId());
2175 // XXX The WebVR 1.1 spec does not define which of multiple VR
2176 // presenting VR displays will be chosen during navigation.
2177 // As the underlying platform VR API's currently only allow a single
2178 // VR display, it is safe to choose the first VR display for now.
2179 break;
2182 mIsDocumentLoaded = false;
2183 // Tell the parent process that the document is not loaded.
2184 if (mWindowGlobalChild) {
2185 mWindowGlobalChild->SendUpdateDocumentHasLoaded(mIsDocumentLoaded);
2187 } else if (aVisitor.mEvent->mMessage == eLoad &&
2188 aVisitor.mEvent->IsTrusted()) {
2189 // This is page load event since load events don't propagate to |window|.
2190 // @see Document::GetEventTargetParent.
2191 mIsDocumentLoaded = true;
2192 // Tell the parent process that the document is loaded.
2193 if (mWindowGlobalChild) {
2194 mWindowGlobalChild->SendUpdateDocumentHasLoaded(mIsDocumentLoaded);
2197 mTimeoutManager->OnDocumentLoaded();
2199 MOZ_ASSERT(aVisitor.mEvent->IsTrusted());
2200 FireFrameLoadEvent();
2202 if (mVREventObserver) {
2203 mVREventObserver->NotifyAfterLoad();
2206 uint32_t autoActivateVRDisplayID = 0;
2207 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
2208 if (outer) {
2209 autoActivateVRDisplayID = outer->GetAutoActivateVRDisplayID();
2211 if (autoActivateVRDisplayID) {
2212 DispatchVRDisplayActivate(autoActivateVRDisplayID,
2213 VRDisplayEventReason::Navigation);
2217 return NS_OK;
2220 nsresult nsGlobalWindowInner::DefineArgumentsProperty(nsIArray* aArguments) {
2221 nsIScriptContext* ctx = GetOuterWindowInternal()->mContext;
2222 NS_ENSURE_TRUE(aArguments && ctx, NS_ERROR_NOT_INITIALIZED);
2224 JS::Rooted<JSObject*> obj(RootingCx(), GetWrapperPreserveColor());
2225 return ctx->SetProperty(obj, "arguments", aArguments);
2228 //*****************************************************************************
2229 // nsGlobalWindowInner::nsIScriptObjectPrincipal
2230 //*****************************************************************************
2232 nsIPrincipal* nsGlobalWindowInner::GetPrincipal() {
2233 if (mDoc) {
2234 // If we have a document, get the principal from the document
2235 return mDoc->NodePrincipal();
2238 if (mDocumentPrincipal) {
2239 return mDocumentPrincipal;
2242 // If we don't have a principal and we don't have a document we
2243 // ask the parent window for the principal. This can happen when
2244 // loading a frameset that has a <frame src="javascript:xxx">, in
2245 // that case the global window is used in JS before we've loaded
2246 // a document into the window.
2248 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
2249 do_QueryInterface(GetInProcessParentInternal());
2251 if (objPrincipal) {
2252 return objPrincipal->GetPrincipal();
2255 return nullptr;
2258 nsIPrincipal* nsGlobalWindowInner::GetEffectiveStoragePrincipal() {
2259 if (mDoc) {
2260 // If we have a document, get the principal from the document
2261 return mDoc->EffectiveStoragePrincipal();
2264 if (mDocumentStoragePrincipal) {
2265 return mDocumentStoragePrincipal;
2268 // If we don't have a storage principal and we don't have a document we ask
2269 // the parent window for the storage principal.
2271 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
2272 do_QueryInterface(GetInProcessParentInternal());
2274 if (objPrincipal) {
2275 return objPrincipal->GetEffectiveStoragePrincipal();
2278 return nullptr;
2281 nsIPrincipal* nsGlobalWindowInner::PartitionedPrincipal() {
2282 if (mDoc) {
2283 // If we have a document, get the principal from the document
2284 return mDoc->PartitionedPrincipal();
2287 if (mDocumentPartitionedPrincipal) {
2288 return mDocumentPartitionedPrincipal;
2291 // If we don't have a partitioned principal and we don't have a document we
2292 // ask the parent window for the partitioned principal.
2294 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
2295 do_QueryInterface(GetInProcessParentInternal());
2297 if (objPrincipal) {
2298 return objPrincipal->PartitionedPrincipal();
2301 return nullptr;
2304 //*****************************************************************************
2305 // nsGlobalWindowInner::nsIDOMWindow
2306 //*****************************************************************************
2308 bool nsPIDOMWindowInner::AddAudioContext(AudioContext* aAudioContext) {
2309 mAudioContexts.AppendElement(aAudioContext);
2311 // Return true if the context should be muted and false if not.
2312 nsIDocShell* docShell = GetDocShell();
2313 return docShell && !docShell->GetAllowMedia() && !aAudioContext->IsOffline();
2316 void nsPIDOMWindowInner::RemoveAudioContext(AudioContext* aAudioContext) {
2317 mAudioContexts.RemoveElement(aAudioContext);
2320 void nsPIDOMWindowInner::MuteAudioContexts() {
2321 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
2322 if (!mAudioContexts[i]->IsOffline()) {
2323 mAudioContexts[i]->Mute();
2328 void nsPIDOMWindowInner::UnmuteAudioContexts() {
2329 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
2330 if (!mAudioContexts[i]->IsOffline()) {
2331 mAudioContexts[i]->Unmute();
2336 WindowProxyHolder nsGlobalWindowInner::Window() {
2337 return WindowProxyHolder(GetBrowsingContext());
2340 Navigator* nsPIDOMWindowInner::Navigator() {
2341 if (!mNavigator) {
2342 mNavigator = new mozilla::dom::Navigator(this);
2345 return mNavigator;
2348 VisualViewport* nsGlobalWindowInner::VisualViewport() {
2349 if (!mVisualViewport) {
2350 mVisualViewport = new mozilla::dom::VisualViewport(this);
2353 return mVisualViewport;
2356 nsScreen* nsGlobalWindowInner::GetScreen(ErrorResult& aError) {
2357 if (!mScreen) {
2358 mScreen = nsScreen::Create(this);
2359 if (!mScreen) {
2360 aError.Throw(NS_ERROR_UNEXPECTED);
2361 return nullptr;
2365 return mScreen;
2368 nsHistory* nsGlobalWindowInner::GetHistory(ErrorResult& aError) {
2369 if (!mHistory) {
2370 mHistory = new nsHistory(this);
2373 return mHistory;
2376 CustomElementRegistry* nsGlobalWindowInner::CustomElements() {
2377 if (!mCustomElements) {
2378 mCustomElements = new CustomElementRegistry(this);
2381 return mCustomElements;
2384 CustomElementRegistry* nsGlobalWindowInner::GetExistingCustomElements() {
2385 return mCustomElements;
2388 Performance* nsPIDOMWindowInner::GetPerformance() {
2389 CreatePerformanceObjectIfNeeded();
2390 return mPerformance;
2393 void nsPIDOMWindowInner::QueuePerformanceNavigationTiming() {
2394 CreatePerformanceObjectIfNeeded();
2395 if (mPerformance) {
2396 mPerformance->QueueNavigationTimingEntry();
2400 void nsPIDOMWindowInner::CreatePerformanceObjectIfNeeded() {
2401 if (mPerformance || !mDoc) {
2402 return;
2404 RefPtr<nsDOMNavigationTiming> timing = mDoc->GetNavigationTiming();
2405 nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(mDoc->GetChannel()));
2406 bool timingEnabled = false;
2407 if (!timedChannel ||
2408 !NS_SUCCEEDED(timedChannel->GetTimingEnabled(&timingEnabled)) ||
2409 !timingEnabled) {
2410 timedChannel = nullptr;
2412 if (timing) {
2413 mPerformance = Performance::CreateForMainThread(this, mDoc->NodePrincipal(),
2414 timing, timedChannel);
2418 bool nsPIDOMWindowInner::IsSecureContext() const {
2419 return nsGlobalWindowInner::Cast(this)->IsSecureContext();
2422 void nsPIDOMWindowInner::Suspend() {
2423 nsGlobalWindowInner::Cast(this)->Suspend();
2426 void nsPIDOMWindowInner::Resume() { nsGlobalWindowInner::Cast(this)->Resume(); }
2428 void nsPIDOMWindowInner::SyncStateFromParentWindow() {
2429 nsGlobalWindowInner::Cast(this)->SyncStateFromParentWindow();
2432 Maybe<ClientInfo> nsPIDOMWindowInner::GetClientInfo() const {
2433 return nsGlobalWindowInner::Cast(this)->GetClientInfo();
2436 Maybe<ClientState> nsPIDOMWindowInner::GetClientState() const {
2437 return nsGlobalWindowInner::Cast(this)->GetClientState();
2440 Maybe<ServiceWorkerDescriptor> nsPIDOMWindowInner::GetController() const {
2441 return nsGlobalWindowInner::Cast(this)->GetController();
2444 void nsPIDOMWindowInner::SetCsp(nsIContentSecurityPolicy* aCsp) {
2445 return nsGlobalWindowInner::Cast(this)->SetCsp(aCsp);
2448 void nsPIDOMWindowInner::SetPreloadCsp(nsIContentSecurityPolicy* aPreloadCsp) {
2449 return nsGlobalWindowInner::Cast(this)->SetPreloadCsp(aPreloadCsp);
2452 nsIContentSecurityPolicy* nsPIDOMWindowInner::GetCsp() {
2453 return nsGlobalWindowInner::Cast(this)->GetCsp();
2456 void nsPIDOMWindowInner::NoteCalledRegisterForServiceWorkerScope(
2457 const nsACString& aScope) {
2458 nsGlobalWindowInner::Cast(this)->NoteCalledRegisterForServiceWorkerScope(
2459 aScope);
2462 void nsPIDOMWindowInner::NoteDOMContentLoaded() {
2463 nsGlobalWindowInner::Cast(this)->NoteDOMContentLoaded();
2466 bool nsGlobalWindowInner::ShouldReportForServiceWorkerScope(
2467 const nsAString& aScope) {
2468 bool result = false;
2470 nsPIDOMWindowOuter* topOuter = GetInProcessScriptableTop();
2471 NS_ENSURE_TRUE(topOuter, false);
2473 nsGlobalWindowInner* topInner =
2474 nsGlobalWindowInner::Cast(topOuter->GetCurrentInnerWindow());
2475 NS_ENSURE_TRUE(topInner, false);
2477 topInner->ShouldReportForServiceWorkerScopeInternal(
2478 NS_ConvertUTF16toUTF8(aScope), &result);
2479 return result;
2482 InstallTriggerImpl* nsGlobalWindowInner::GetInstallTrigger() {
2483 if (!mInstallTrigger) {
2484 ErrorResult rv;
2485 mInstallTrigger = ConstructJSImplementation<InstallTriggerImpl>(
2486 "@mozilla.org/addons/installtrigger;1", this, rv);
2487 if (rv.Failed()) {
2488 rv.SuppressException();
2489 return nullptr;
2493 return mInstallTrigger;
2496 nsIDOMWindowUtils* nsGlobalWindowInner::GetWindowUtils(ErrorResult& aRv) {
2497 FORWARD_TO_OUTER_OR_THROW(WindowUtils, (), aRv, nullptr);
2500 CallState nsGlobalWindowInner::ShouldReportForServiceWorkerScopeInternal(
2501 const nsACString& aScope, bool* aResultOut) {
2502 MOZ_DIAGNOSTIC_ASSERT(aResultOut);
2504 // First check to see if this window is controlled. If so, then we have
2505 // found a match and are done.
2506 const Maybe<ServiceWorkerDescriptor> swd = GetController();
2507 if (swd.isSome() && swd.ref().Scope() == aScope) {
2508 *aResultOut = true;
2509 return CallState::Stop;
2512 // Next, check to see if this window has called
2513 // navigator.serviceWorker.register() for this scope. If so, then treat this
2514 // as a match so console reports appear in the devtools console.
2515 if (mClientSource &&
2516 mClientSource->CalledRegisterForServiceWorkerScope(aScope)) {
2517 *aResultOut = true;
2518 return CallState::Stop;
2521 // Finally check the current docshell nsILoadGroup to see if there are any
2522 // outstanding navigation requests. If so, match the scope against the
2523 // channel's URL. We want to show console reports during the FetchEvent
2524 // intercepting the navigation itself.
2525 nsCOMPtr<nsIDocumentLoader> loader(do_QueryInterface(GetDocShell()));
2526 if (loader) {
2527 nsCOMPtr<nsILoadGroup> loadgroup;
2528 Unused << loader->GetLoadGroup(getter_AddRefs(loadgroup));
2529 if (loadgroup) {
2530 nsCOMPtr<nsISimpleEnumerator> iter;
2531 Unused << loadgroup->GetRequests(getter_AddRefs(iter));
2532 if (iter) {
2533 nsCOMPtr<nsISupports> tmp;
2534 bool hasMore = true;
2535 // Check each network request in the load group.
2536 while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) {
2537 iter->GetNext(getter_AddRefs(tmp));
2538 nsCOMPtr<nsIChannel> loadingChannel(do_QueryInterface(tmp));
2539 // Ignore subresource requests. Logging for a subresource
2540 // FetchEvent should be handled above since the client is
2541 // already controlled.
2542 if (!loadingChannel ||
2543 !nsContentUtils::IsNonSubresourceRequest(loadingChannel)) {
2544 continue;
2546 nsCOMPtr<nsIURI> loadingURL;
2547 Unused << loadingChannel->GetURI(getter_AddRefs(loadingURL));
2548 if (!loadingURL) {
2549 continue;
2551 nsAutoCString loadingSpec;
2552 Unused << loadingURL->GetSpec(loadingSpec);
2553 // Perform a simple substring comparison to match the scope
2554 // against the channel URL.
2555 if (StringBeginsWith(loadingSpec, aScope)) {
2556 *aResultOut = true;
2557 return CallState::Stop;
2564 // The current window doesn't care about this service worker, but maybe
2565 // one of our child frames does.
2566 return CallOnInProcessChildren(
2567 &nsGlobalWindowInner::ShouldReportForServiceWorkerScopeInternal, aScope,
2568 aResultOut);
2571 void nsGlobalWindowInner::NoteCalledRegisterForServiceWorkerScope(
2572 const nsACString& aScope) {
2573 if (!mClientSource) {
2574 return;
2577 mClientSource->NoteCalledRegisterForServiceWorkerScope(aScope);
2580 void nsGlobalWindowInner::NoteDOMContentLoaded() {
2581 if (!mClientSource) {
2582 return;
2585 mClientSource->NoteDOMContentLoaded();
2588 void nsGlobalWindowInner::UpdateTopInnerWindow() {
2589 if (IsTopInnerWindow() || !mTopInnerWindow) {
2590 return;
2593 mTopInnerWindow->UpdateWebSocketCount(-(int32_t)mNumOfOpenWebSockets);
2596 bool nsGlobalWindowInner::IsInSyncOperation() {
2597 return GetExtantDoc() && GetExtantDoc()->IsInSyncOperation();
2600 bool nsGlobalWindowInner::IsSharedMemoryAllowed() const {
2601 MOZ_ASSERT(NS_IsMainThread());
2603 if (StaticPrefs::
2604 dom_postMessage_sharedArrayBuffer_bypassCOOP_COEP_insecure_enabled()) {
2605 return true;
2608 return CrossOriginIsolated();
2611 bool nsGlobalWindowInner::CrossOriginIsolated() const {
2612 MOZ_ASSERT(NS_IsMainThread());
2614 RefPtr<BrowsingContext> bc = GetBrowsingContext();
2615 MOZ_DIAGNOSTIC_ASSERT(bc);
2616 return bc->CrossOriginIsolated();
2619 void nsPIDOMWindowInner::AddPeerConnection() {
2620 MOZ_ASSERT(NS_IsMainThread());
2621 mTopInnerWindow ? mTopInnerWindow->mActivePeerConnections++
2622 : mActivePeerConnections++;
2625 void nsPIDOMWindowInner::RemovePeerConnection() {
2626 MOZ_ASSERT(NS_IsMainThread());
2627 MOZ_ASSERT(mTopInnerWindow ? mTopInnerWindow->mActivePeerConnections
2628 : mActivePeerConnections);
2630 mTopInnerWindow ? mTopInnerWindow->mActivePeerConnections--
2631 : mActivePeerConnections--;
2634 bool nsPIDOMWindowInner::HasActivePeerConnections() {
2635 MOZ_ASSERT(NS_IsMainThread());
2636 return mTopInnerWindow ? mTopInnerWindow->mActivePeerConnections
2637 : mActivePeerConnections;
2640 void nsPIDOMWindowInner::AddMediaKeysInstance(MediaKeys* aMediaKeys) {
2641 MOZ_ASSERT(NS_IsMainThread());
2642 mMediaKeysInstances.AppendElement(aMediaKeys);
2645 void nsPIDOMWindowInner::RemoveMediaKeysInstance(MediaKeys* aMediaKeys) {
2646 MOZ_ASSERT(NS_IsMainThread());
2647 mMediaKeysInstances.RemoveElement(aMediaKeys);
2650 bool nsPIDOMWindowInner::HasActiveMediaKeysInstance() {
2651 MOZ_ASSERT(NS_IsMainThread());
2652 return !mMediaKeysInstances.IsEmpty();
2655 bool nsPIDOMWindowInner::IsPlayingAudio() {
2656 for (uint32_t i = 0; i < mAudioContexts.Length(); i++) {
2657 if (mAudioContexts[i]->IsRunning()) {
2658 return true;
2661 RefPtr<AudioChannelService> acs = AudioChannelService::Get();
2662 if (!acs) {
2663 return false;
2665 auto outer = GetOuterWindow();
2666 if (!outer) {
2667 // We've been unlinked and are about to die. Not a good time to pretend to
2668 // be playing audio.
2669 return false;
2671 return acs->IsWindowActive(outer);
2674 bool nsPIDOMWindowInner::IsDocumentLoaded() const { return mIsDocumentLoaded; }
2676 mozilla::dom::TimeoutManager& nsPIDOMWindowInner::TimeoutManager() {
2677 return *mTimeoutManager;
2680 bool nsPIDOMWindowInner::IsRunningTimeout() {
2681 return TimeoutManager().IsRunningTimeout();
2684 void nsPIDOMWindowInner::TryToCacheTopInnerWindow() {
2685 if (mHasTriedToCacheTopInnerWindow) {
2686 return;
2689 nsGlobalWindowInner* window = nsGlobalWindowInner::Cast(this);
2691 MOZ_ASSERT(!window->IsDying());
2693 mHasTriedToCacheTopInnerWindow = true;
2695 MOZ_ASSERT(window);
2697 if (nsCOMPtr<nsPIDOMWindowOuter> topOutter =
2698 window->GetInProcessScriptableTop()) {
2699 mTopInnerWindow = topOutter->GetCurrentInnerWindow();
2703 void nsPIDOMWindowInner::UpdateActiveIndexedDBDatabaseCount(int32_t aDelta) {
2704 MOZ_ASSERT(NS_IsMainThread());
2706 if (aDelta == 0) {
2707 return;
2710 // We count databases but not transactions because only active databases
2711 // could block throttling.
2712 uint32_t& counter = mTopInnerWindow
2713 ? mTopInnerWindow->mNumOfIndexedDBDatabases
2714 : mNumOfIndexedDBDatabases;
2716 counter += aDelta;
2719 bool nsPIDOMWindowInner::HasActiveIndexedDBDatabases() {
2720 MOZ_ASSERT(NS_IsMainThread());
2722 return mTopInnerWindow ? mTopInnerWindow->mNumOfIndexedDBDatabases > 0
2723 : mNumOfIndexedDBDatabases > 0;
2726 void nsPIDOMWindowInner::UpdateWebSocketCount(int32_t aDelta) {
2727 MOZ_ASSERT(NS_IsMainThread());
2729 if (aDelta == 0) {
2730 return;
2733 if (mTopInnerWindow && !IsTopInnerWindow()) {
2734 mTopInnerWindow->UpdateWebSocketCount(aDelta);
2737 MOZ_DIAGNOSTIC_ASSERT(
2738 aDelta > 0 || ((aDelta + mNumOfOpenWebSockets) < mNumOfOpenWebSockets));
2740 mNumOfOpenWebSockets += aDelta;
2743 bool nsPIDOMWindowInner::HasOpenWebSockets() const {
2744 MOZ_ASSERT(NS_IsMainThread());
2746 return mNumOfOpenWebSockets ||
2747 (mTopInnerWindow && mTopInnerWindow->mNumOfOpenWebSockets);
2750 bool nsPIDOMWindowInner::IsCurrentInnerWindow() const {
2751 auto* bc = GetBrowsingContext();
2752 MOZ_ASSERT(bc);
2754 if (bc->IsDiscarded()) {
2755 // If our BrowsingContext has been discarded, we consider ourselves
2756 // still-current if we were current at the time it was discarded.
2757 return mOuterWindow && WasCurrentInnerWindow();
2760 nsPIDOMWindowOuter* outer = bc->GetDOMWindow();
2761 return outer && outer->GetCurrentInnerWindow() == this;
2764 bool nsPIDOMWindowInner::IsFullyActive() const {
2765 WindowContext* wc = GetWindowContext();
2766 if (!wc || wc->IsDiscarded() || wc->IsCached()) {
2767 return false;
2769 return GetBrowsingContext()->AncestorsAreCurrent();
2772 void nsPIDOMWindowInner::SetAudioCapture(bool aCapture) {
2773 RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
2774 if (service) {
2775 service->SetWindowAudioCaptured(GetOuterWindow(), mWindowID, aCapture);
2779 void nsGlobalWindowInner::SetActiveLoadingState(bool aIsLoading) {
2780 MOZ_LOG(
2781 gTimeoutLog, mozilla::LogLevel::Debug,
2782 ("SetActiveLoadingState innerwindow %p: %d", (void*)this, aIsLoading));
2783 if (GetBrowsingContext()) {
2784 // Setting loading on a discarded context has no effect.
2785 Unused << GetBrowsingContext()->SetLoading(aIsLoading);
2788 if (!nsGlobalWindowInner::Cast(this)->IsChromeWindow()) {
2789 mTimeoutManager->SetLoading(aIsLoading);
2792 HintIsLoading(aIsLoading);
2795 void nsGlobalWindowInner::HintIsLoading(bool aIsLoading) {
2796 // Hint to tell the JS GC to use modified triggers during pageload.
2797 if (mHintedWasLoading != aIsLoading) {
2798 using namespace js::gc;
2799 SetPerformanceHint(danger::GetJSContext(), aIsLoading
2800 ? PerformanceHint::InPageLoad
2801 : PerformanceHint::Normal);
2802 mHintedWasLoading = aIsLoading;
2806 // nsISpeechSynthesisGetter
2808 #ifdef MOZ_WEBSPEECH
2809 SpeechSynthesis* nsGlobalWindowInner::GetSpeechSynthesis(ErrorResult& aError) {
2810 if (!mSpeechSynthesis) {
2811 mSpeechSynthesis = new SpeechSynthesis(this);
2814 return mSpeechSynthesis;
2817 bool nsGlobalWindowInner::HasActiveSpeechSynthesis() {
2818 if (mSpeechSynthesis) {
2819 return !mSpeechSynthesis->HasEmptyQueue();
2822 return false;
2825 #endif
2827 #ifdef MOZ_GLEAN
2828 mozilla::glean::Glean* nsGlobalWindowInner::Glean() {
2829 if (!mGlean) {
2830 mGlean = new mozilla::glean::Glean();
2833 return mGlean;
2836 mozilla::glean::GleanPings* nsGlobalWindowInner::GleanPings() {
2837 if (!mGleanPings) {
2838 mGleanPings = new mozilla::glean::GleanPings();
2841 return mGleanPings;
2843 #endif
2845 Nullable<WindowProxyHolder> nsGlobalWindowInner::GetParent(
2846 ErrorResult& aError) {
2847 FORWARD_TO_OUTER_OR_THROW(GetParentOuter, (), aError, nullptr);
2851 * GetInProcessScriptableParent used to be called when a script read
2852 * window.parent. Under Fission, that is now handled by
2853 * BrowsingContext::GetParent, and the result is a WindowProxyHolder rather than
2854 * an actual global window. This method still exists for legacy callers which
2855 * relied on the old logic, and require in-process windows. However, it only
2856 * works correctly when no out-of-process frames exist between this window and
2857 * the top-level window, so it should not be used in new code.
2859 * In contrast to GetRealParent, GetInProcessScriptableParent respects <iframe
2860 * mozbrowser> boundaries, so if |this| is contained by an <iframe
2861 * mozbrowser>, we will return |this| as its own parent.
2863 nsPIDOMWindowOuter* nsGlobalWindowInner::GetInProcessScriptableParent() {
2864 FORWARD_TO_OUTER(GetInProcessScriptableParent, (), nullptr);
2868 * GetInProcessScriptableTop used to be called when a script read window.top.
2869 * Under Fission, that is now handled by BrowsingContext::Top, and the result is
2870 * a WindowProxyHolder rather than an actual global window. This method still
2871 * exists for legacy callers which relied on the old logic, and require
2872 * in-process windows. However, it only works correctly when no out-of-process
2873 * frames exist between this window and the top-level window, so it should not
2874 * be used in new code.
2876 * In contrast to GetRealTop, GetInProcessScriptableTop respects <iframe
2877 * mozbrowser> boundaries. If we encounter a window owned by an <iframe
2878 * mozbrowser> while walking up the window hierarchy, we'll stop and return that
2879 * window.
2881 nsPIDOMWindowOuter* nsGlobalWindowInner::GetInProcessScriptableTop() {
2882 FORWARD_TO_OUTER(GetInProcessScriptableTop, (), nullptr);
2885 void nsGlobalWindowInner::GetContent(JSContext* aCx,
2886 JS::MutableHandle<JSObject*> aRetval,
2887 CallerType aCallerType,
2888 ErrorResult& aError) {
2889 FORWARD_TO_OUTER_OR_THROW(GetContentOuter,
2890 (aCx, aRetval, aCallerType, aError), aError, );
2893 BarProp* nsGlobalWindowInner::GetMenubar(ErrorResult& aError) {
2894 if (!mMenubar) {
2895 mMenubar = new MenubarProp(this);
2898 return mMenubar;
2901 BarProp* nsGlobalWindowInner::GetToolbar(ErrorResult& aError) {
2902 if (!mToolbar) {
2903 mToolbar = new ToolbarProp(this);
2906 return mToolbar;
2909 BarProp* nsGlobalWindowInner::GetLocationbar(ErrorResult& aError) {
2910 if (!mLocationbar) {
2911 mLocationbar = new LocationbarProp(this);
2913 return mLocationbar;
2916 BarProp* nsGlobalWindowInner::GetPersonalbar(ErrorResult& aError) {
2917 if (!mPersonalbar) {
2918 mPersonalbar = new PersonalbarProp(this);
2920 return mPersonalbar;
2923 BarProp* nsGlobalWindowInner::GetStatusbar(ErrorResult& aError) {
2924 if (!mStatusbar) {
2925 mStatusbar = new StatusbarProp(this);
2927 return mStatusbar;
2930 BarProp* nsGlobalWindowInner::GetScrollbars(ErrorResult& aError) {
2931 if (!mScrollbars) {
2932 mScrollbars = new ScrollbarsProp(this);
2935 return mScrollbars;
2938 bool nsGlobalWindowInner::GetClosed(ErrorResult& aError) {
2939 // If we're called from JS (which is the only way we should be getting called
2940 // here) and we reach this point, that means our JS global is the current
2941 // target of the WindowProxy, which means that we are the "current inner"
2942 // of our outer. So if FORWARD_TO_OUTER fails to forward, that means the
2943 // outer is already torn down, which corresponds to the closed state.
2944 FORWARD_TO_OUTER(GetClosedOuter, (), true);
2947 Nullable<WindowProxyHolder> nsGlobalWindowInner::IndexedGetter(
2948 uint32_t aIndex) {
2949 FORWARD_TO_OUTER(IndexedGetterOuter, (aIndex), nullptr);
2952 namespace {
2954 struct InterfaceShimEntry {
2955 const char* geckoName;
2956 const char* domName;
2959 } // anonymous namespace
2961 // We add shims from Components.interfaces.nsIDOMFoo to window.Foo for each
2962 // interface that has interface constants that sites might be getting off
2963 // of Ci.
2964 const InterfaceShimEntry kInterfaceShimMap[] = {
2965 {"nsIXMLHttpRequest", "XMLHttpRequest"},
2966 {"nsIDOMDOMException", "DOMException"},
2967 {"nsIDOMNode", "Node"},
2968 {"nsIDOMCSSRule", "CSSRule"},
2969 {"nsIDOMEvent", "Event"},
2970 {"nsIDOMNSEvent", "Event"},
2971 {"nsIDOMKeyEvent", "KeyEvent"},
2972 {"nsIDOMMouseEvent", "MouseEvent"},
2973 {"nsIDOMMouseScrollEvent", "MouseScrollEvent"},
2974 {"nsIDOMMutationEvent", "MutationEvent"},
2975 {"nsIDOMUIEvent", "UIEvent"},
2976 {"nsIDOMHTMLMediaElement", "HTMLMediaElement"},
2977 {"nsIDOMRange", "Range"},
2978 // Think about whether Ci.nsINodeFilter can just go away for websites!
2979 {"nsIDOMNodeFilter", "NodeFilter"},
2980 {"nsIDOMXPathResult", "XPathResult"}};
2982 bool nsGlobalWindowInner::ResolveComponentsShim(
2983 JSContext* aCx, JS::Handle<JSObject*> aGlobal,
2984 JS::MutableHandle<JS::PropertyDescriptor> aDesc) {
2985 // Keep track of how often this happens.
2986 Telemetry::Accumulate(Telemetry::COMPONENTS_SHIM_ACCESSED_BY_CONTENT, true);
2988 // Warn once.
2989 nsCOMPtr<Document> doc = GetExtantDoc();
2990 if (doc) {
2991 doc->WarnOnceAbout(DeprecatedOperations::eComponents, /* asError = */ true);
2994 // Create a fake Components object.
2995 AssertSameCompartment(aCx, aGlobal);
2996 JS::Rooted<JSObject*> components(aCx, JS_NewPlainObject(aCx));
2997 if (NS_WARN_IF(!components)) {
2998 return false;
3001 // Create a fake interfaces object.
3002 JS::Rooted<JSObject*> interfaces(aCx, JS_NewPlainObject(aCx));
3003 if (NS_WARN_IF(!interfaces)) {
3004 return false;
3006 bool ok =
3007 JS_DefineProperty(aCx, components, "interfaces", interfaces,
3008 JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY);
3009 if (NS_WARN_IF(!ok)) {
3010 return false;
3013 // Define a bunch of shims from the Ci.nsIDOMFoo to window.Foo for DOM
3014 // interfaces with constants.
3015 for (uint32_t i = 0; i < ArrayLength(kInterfaceShimMap); ++i) {
3016 // Grab the names from the table.
3017 const char* geckoName = kInterfaceShimMap[i].geckoName;
3018 const char* domName = kInterfaceShimMap[i].domName;
3020 // Look up the appopriate interface object on the global.
3021 JS::Rooted<JS::Value> v(aCx, JS::UndefinedValue());
3022 ok = JS_GetProperty(aCx, aGlobal, domName, &v);
3023 if (NS_WARN_IF(!ok)) {
3024 return false;
3026 if (!v.isObject()) {
3027 NS_WARNING("Unable to find interface object on global");
3028 continue;
3031 // Define the shim on the interfaces object.
3032 ok = JS_DefineProperty(
3033 aCx, interfaces, geckoName, v,
3034 JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY);
3035 if (NS_WARN_IF(!ok)) {
3036 return false;
3040 FillPropertyDescriptor(aDesc, aGlobal, JS::ObjectValue(*components), false);
3042 return true;
3045 #ifdef RELEASE_OR_BETA
3046 # define USE_CONTROLLERS_SHIM
3047 #endif
3049 #ifdef USE_CONTROLLERS_SHIM
3050 static const JSClass ControllersShimClass = {"Controllers", 0};
3051 static const JSClass XULControllersShimClass = {"XULControllers", 0};
3052 #endif
3054 bool nsGlobalWindowInner::DoResolve(
3055 JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aId,
3056 JS::MutableHandle<JS::PropertyDescriptor> aDesc) {
3057 // Note: Keep this in sync with MayResolve.
3059 // Note: The infallibleInit call in GlobalResolve depends on this check.
3060 if (!JSID_IS_STRING(aId)) {
3061 return true;
3064 bool found;
3065 if (!WebIDLGlobalNameHash::DefineIfEnabled(aCx, aObj, aId, aDesc, &found)) {
3066 return false;
3069 if (found) {
3070 return true;
3073 // We support a cut-down Components.interfaces in case websites are
3074 // using Components.interfaces.nsIFoo.CONSTANT_NAME for the ones
3075 // that have constants.
3076 if (StaticPrefs::dom_use_components_shim() &&
3077 aId == XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_COMPONENTS)) {
3078 return ResolveComponentsShim(aCx, aObj, aDesc);
3081 // We also support a "window.controllers" thing; apparently some
3082 // sites use it for browser-sniffing. See bug 1010577.
3083 #ifdef USE_CONTROLLERS_SHIM
3084 // Note: We use |aObj| rather than |this| to get the principal here, because
3085 // this is called during Window setup when the Document isn't necessarily
3086 // hooked up yet.
3087 if ((aId == XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_CONTROLLERS) ||
3088 aId == XPCJSRuntime::Get()->GetStringID(
3089 XPCJSContext::IDX_CONTROLLERS_CLASS)) &&
3090 !xpc::IsXrayWrapper(aObj) &&
3091 !nsContentUtils::ObjectPrincipal(aObj)->IsSystemPrincipal()) {
3092 if (GetExtantDoc()) {
3093 GetExtantDoc()->WarnOnceAbout(
3094 DeprecatedOperations::eWindow_Cc_ontrollers);
3096 const JSClass* clazz;
3097 if (aId ==
3098 XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_CONTROLLERS)) {
3099 clazz = &XULControllersShimClass;
3100 } else {
3101 clazz = &ControllersShimClass;
3103 MOZ_ASSERT(JS_IsGlobalObject(aObj));
3104 JS::Rooted<JSObject*> shim(aCx, JS_NewObject(aCx, clazz));
3105 if (NS_WARN_IF(!shim)) {
3106 return false;
3108 FillPropertyDescriptor(aDesc, aObj, JS::ObjectValue(*shim),
3109 /* readOnly = */ false);
3110 return true;
3112 #endif
3114 return true;
3117 /* static */
3118 bool nsGlobalWindowInner::MayResolve(jsid aId) {
3119 // Note: This function does not fail and may not have any side-effects.
3120 // Note: Keep this in sync with DoResolve.
3121 if (!JSID_IS_STRING(aId)) {
3122 return false;
3125 if (aId == XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_COMPONENTS)) {
3126 return true;
3129 if (aId == XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_CONTROLLERS) ||
3130 aId == XPCJSRuntime::Get()->GetStringID(
3131 XPCJSContext::IDX_CONTROLLERS_CLASS)) {
3132 // We only resolve .controllers/.Controllers in release builds and on
3133 // non-chrome windows, but let's not worry about any of that stuff.
3134 return true;
3137 return WebIDLGlobalNameHash::MayResolve(aId);
3140 void nsGlobalWindowInner::GetOwnPropertyNames(
3141 JSContext* aCx, JS::MutableHandleVector<jsid> aNames, bool aEnumerableOnly,
3142 ErrorResult& aRv) {
3143 if (aEnumerableOnly) {
3144 // The names we would return from here get defined on the window via one of
3145 // two codepaths. The ones coming from the WebIDLGlobalNameHash will end up
3146 // in the DefineConstructor function in BindingUtils, which always defines
3147 // things as non-enumerable. The ones coming from the script namespace
3148 // manager get defined by our resolve hook using FillPropertyDescriptor with
3149 // 0 for the property attributes, so non-enumerable as well.
3151 // So in the aEnumerableOnly case we have nothing to do.
3152 return;
3155 // "Components" is marked as enumerable but only resolved on demand :-/.
3156 // aNames.AppendElement(u"Components"_ns);
3158 JS::Rooted<JSObject*> wrapper(aCx, GetWrapper());
3160 // There are actually two ways we can get called here: For normal
3161 // enumeration or for Xray enumeration. In the latter case, we want to
3162 // return all possible WebIDL names, because we don't really support
3163 // deleting these names off our Xray; trying to resolve them will just make
3164 // them come back. In the former case, we want to avoid returning deleted
3165 // names. But the JS engine already knows about the non-deleted
3166 // already-resolved names, so we can just return the so-far-unresolved ones.
3168 // We can tell which case we're in by whether aCx is in our wrapper's
3169 // compartment. If not, we're in the Xray case.
3170 WebIDLGlobalNameHash::NameType nameType =
3171 js::IsObjectInContextCompartment(wrapper, aCx)
3172 ? WebIDLGlobalNameHash::UnresolvedNamesOnly
3173 : WebIDLGlobalNameHash::AllNames;
3174 if (!WebIDLGlobalNameHash::GetNames(aCx, wrapper, nameType, aNames)) {
3175 aRv.NoteJSContextException(aCx);
3179 /* static */
3180 bool nsGlobalWindowInner::IsPrivilegedChromeWindow(JSContext*, JSObject* aObj) {
3181 // For now, have to deal with XPConnect objects here.
3182 nsGlobalWindowInner* win = xpc::WindowOrNull(aObj);
3183 return win && win->IsChromeWindow() &&
3184 nsContentUtils::ObjectPrincipal(aObj) ==
3185 nsContentUtils::GetSystemPrincipal();
3188 /* static */
3189 bool nsGlobalWindowInner::IsRequestIdleCallbackEnabled(JSContext* aCx,
3190 JSObject*) {
3191 // The requestIdleCallback should always be enabled for system code.
3192 return StaticPrefs::dom_requestIdleCallback_enabled() ||
3193 nsContentUtils::IsSystemCaller(aCx);
3196 /* static */
3197 bool nsGlobalWindowInner::DeviceSensorsEnabled(JSContext*, JSObject*) {
3198 return Preferences::GetBool("device.sensors.enabled");
3201 /* static */
3202 bool nsGlobalWindowInner::ContentPropertyEnabled(JSContext* aCx, JSObject*) {
3203 return StaticPrefs::dom_window_content_untrusted_enabled() ||
3204 nsContentUtils::IsSystemCaller(aCx);
3207 nsDOMOfflineResourceList* nsGlobalWindowInner::GetApplicationCache(
3208 ErrorResult& aError) {
3209 if (!mApplicationCache) {
3210 nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(GetDocShell()));
3211 if (!webNav || !mDoc) {
3212 aError.Throw(NS_ERROR_FAILURE);
3213 return nullptr;
3216 nsCOMPtr<nsIURI> uri;
3217 aError = webNav->GetCurrentURI(getter_AddRefs(uri));
3218 if (aError.Failed()) {
3219 return nullptr;
3222 nsCOMPtr<nsIURI> manifestURI;
3223 nsContentUtils::GetOfflineAppManifest(mDoc, getter_AddRefs(manifestURI));
3225 RefPtr<nsDOMOfflineResourceList> applicationCache =
3226 new nsDOMOfflineResourceList(manifestURI, uri, mDoc->NodePrincipal(),
3227 this);
3229 applicationCache->Init();
3231 mApplicationCache = applicationCache;
3234 return mApplicationCache;
3237 nsDOMOfflineResourceList* nsGlobalWindowInner::GetApplicationCache() {
3238 return GetApplicationCache(IgnoreErrors());
3241 Crypto* nsGlobalWindowInner::GetCrypto(ErrorResult& aError) {
3242 if (!mCrypto) {
3243 mCrypto = new Crypto(this);
3245 return mCrypto;
3248 mozilla::dom::U2F* nsGlobalWindowInner::GetU2f(ErrorResult& aError) {
3249 if (!mU2F) {
3250 RefPtr<U2F> u2f = new U2F(this);
3251 u2f->Init(aError);
3252 if (NS_WARN_IF(aError.Failed())) {
3253 return nullptr;
3256 mU2F = u2f;
3258 return mU2F;
3261 nsIControllers* nsGlobalWindowInner::GetControllers(ErrorResult& aError) {
3262 FORWARD_TO_OUTER_OR_THROW(GetControllersOuter, (aError), aError, nullptr);
3265 nsresult nsGlobalWindowInner::GetControllers(nsIControllers** aResult) {
3266 ErrorResult rv;
3267 nsCOMPtr<nsIControllers> controllers = GetControllers(rv);
3268 controllers.forget(aResult);
3270 return rv.StealNSResult();
3273 Nullable<WindowProxyHolder> nsGlobalWindowInner::GetOpenerWindow(
3274 ErrorResult& aError) {
3275 FORWARD_TO_OUTER_OR_THROW(GetOpenerWindowOuter, (), aError, nullptr);
3278 void nsGlobalWindowInner::GetOpener(JSContext* aCx,
3279 JS::MutableHandle<JS::Value> aRetval,
3280 ErrorResult& aError) {
3281 Nullable<WindowProxyHolder> opener = GetOpenerWindow(aError);
3282 if (aError.Failed() || opener.IsNull()) {
3283 aRetval.setNull();
3284 return;
3287 if (!ToJSValue(aCx, opener.Value(), aRetval)) {
3288 aError.NoteJSContextException(aCx);
3292 void nsGlobalWindowInner::SetOpener(JSContext* aCx,
3293 JS::Handle<JS::Value> aOpener,
3294 ErrorResult& aError) {
3295 if (aOpener.isNull()) {
3296 RefPtr<BrowsingContext> bc(GetBrowsingContext());
3297 if (!bc->IsDiscarded()) {
3298 bc->SetOpener(nullptr);
3300 return;
3303 // If something other than null is passed, just define aOpener on our inner
3304 // window's JS object, wrapped into the current compartment so that for Xrays
3305 // we define on the Xray expando object, but don't set it on the outer window,
3306 // so that it'll get reset on navigation. This is just like replaceable
3307 // properties, but we're not quite readonly.
3308 RedefineProperty(aCx, "opener", aOpener, aError);
3311 void nsGlobalWindowInner::GetEvent(JSContext* aCx,
3312 JS::MutableHandle<JS::Value> aRetval) {
3313 if (mEvent) {
3314 Unused << nsContentUtils::WrapNative(aCx, mEvent, aRetval);
3315 } else {
3316 aRetval.setUndefined();
3320 void nsGlobalWindowInner::GetStatus(nsAString& aStatus, ErrorResult& aError) {
3321 FORWARD_TO_OUTER_OR_THROW(GetStatusOuter, (aStatus), aError, );
3324 void nsGlobalWindowInner::SetStatus(const nsAString& aStatus,
3325 ErrorResult& aError) {
3326 FORWARD_TO_OUTER_OR_THROW(SetStatusOuter, (aStatus), aError, );
3329 void nsGlobalWindowInner::GetName(nsAString& aName, ErrorResult& aError) {
3330 FORWARD_TO_OUTER_OR_THROW(GetNameOuter, (aName), aError, );
3333 void nsGlobalWindowInner::SetName(const nsAString& aName,
3334 mozilla::ErrorResult& aError) {
3335 FORWARD_TO_OUTER_OR_THROW(SetNameOuter, (aName, aError), aError, );
3338 double nsGlobalWindowInner::GetInnerWidth(CallerType aCallerType,
3339 ErrorResult& aError) {
3340 // We ignore aCallerType; we only have that argument because some other things
3341 // called by GetReplaceableWindowCoord need it. If this ever changes, fix
3342 // nsresult nsGlobalWindowInner::GetInnerWidth(double* aInnerWidth)
3343 // to actually take a useful CallerType and pass it in here.
3344 FORWARD_TO_OUTER_OR_THROW(GetInnerWidthOuter, (aError), aError, 0);
3347 void nsGlobalWindowInner::GetInnerWidth(JSContext* aCx,
3348 JS::MutableHandle<JS::Value> aValue,
3349 CallerType aCallerType,
3350 ErrorResult& aError) {
3351 GetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::GetInnerWidth, aValue,
3352 aCallerType, aError);
3355 nsresult nsGlobalWindowInner::GetInnerWidth(double* aInnerWidth) {
3356 ErrorResult rv;
3357 // Callee doesn't care about the caller type, but play it safe.
3358 *aInnerWidth = GetInnerWidth(CallerType::NonSystem, rv);
3360 return rv.StealNSResult();
3363 void nsGlobalWindowInner::SetInnerWidth(double aInnerWidth,
3364 CallerType aCallerType,
3365 ErrorResult& aError) {
3366 FORWARD_TO_OUTER_OR_THROW(SetInnerWidthOuter,
3367 (aInnerWidth, aCallerType, aError), aError, );
3370 void nsGlobalWindowInner::SetInnerWidth(JSContext* aCx,
3371 JS::Handle<JS::Value> aValue,
3372 CallerType aCallerType,
3373 ErrorResult& aError) {
3374 SetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::SetInnerWidth, aValue,
3375 "innerWidth", aCallerType, aError);
3378 double nsGlobalWindowInner::GetInnerHeight(CallerType aCallerType,
3379 ErrorResult& aError) {
3380 // We ignore aCallerType; we only have that argument because some other things
3381 // called by GetReplaceableWindowCoord need it. If this ever changes, fix
3382 // nsresult nsGlobalWindowInner::GetInnerHeight(double* aInnerWidth)
3383 // to actually take a useful CallerType and pass it in here.
3384 FORWARD_TO_OUTER_OR_THROW(GetInnerHeightOuter, (aError), aError, 0);
3387 void nsGlobalWindowInner::GetInnerHeight(JSContext* aCx,
3388 JS::MutableHandle<JS::Value> aValue,
3389 CallerType aCallerType,
3390 ErrorResult& aError) {
3391 GetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::GetInnerHeight, aValue,
3392 aCallerType, aError);
3395 nsresult nsGlobalWindowInner::GetInnerHeight(double* aInnerHeight) {
3396 ErrorResult rv;
3397 // Callee doesn't care about the caller type, but play it safe.
3398 *aInnerHeight = GetInnerHeight(CallerType::NonSystem, rv);
3400 return rv.StealNSResult();
3403 void nsGlobalWindowInner::SetInnerHeight(double aInnerHeight,
3404 CallerType aCallerType,
3405 ErrorResult& aError) {
3406 FORWARD_TO_OUTER_OR_THROW(SetInnerHeightOuter,
3407 (aInnerHeight, aCallerType, aError), aError, );
3410 void nsGlobalWindowInner::SetInnerHeight(JSContext* aCx,
3411 JS::Handle<JS::Value> aValue,
3412 CallerType aCallerType,
3413 ErrorResult& aError) {
3414 SetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::SetInnerHeight, aValue,
3415 "innerHeight", aCallerType, aError);
3418 int32_t nsGlobalWindowInner::GetOuterWidth(CallerType aCallerType,
3419 ErrorResult& aError) {
3420 FORWARD_TO_OUTER_OR_THROW(GetOuterWidthOuter, (aCallerType, aError), aError,
3424 void nsGlobalWindowInner::GetOuterWidth(JSContext* aCx,
3425 JS::MutableHandle<JS::Value> aValue,
3426 CallerType aCallerType,
3427 ErrorResult& aError) {
3428 GetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::GetOuterWidth, aValue,
3429 aCallerType, aError);
3432 int32_t nsGlobalWindowInner::GetOuterHeight(CallerType aCallerType,
3433 ErrorResult& aError) {
3434 FORWARD_TO_OUTER_OR_THROW(GetOuterHeightOuter, (aCallerType, aError), aError,
3438 void nsGlobalWindowInner::GetOuterHeight(JSContext* aCx,
3439 JS::MutableHandle<JS::Value> aValue,
3440 CallerType aCallerType,
3441 ErrorResult& aError) {
3442 GetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::GetOuterHeight, aValue,
3443 aCallerType, aError);
3446 void nsGlobalWindowInner::SetOuterWidth(int32_t aOuterWidth,
3447 CallerType aCallerType,
3448 ErrorResult& aError) {
3449 FORWARD_TO_OUTER_OR_THROW(SetOuterWidthOuter,
3450 (aOuterWidth, aCallerType, aError), aError, );
3453 void nsGlobalWindowInner::SetOuterWidth(JSContext* aCx,
3454 JS::Handle<JS::Value> aValue,
3455 CallerType aCallerType,
3456 ErrorResult& aError) {
3457 SetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::SetOuterWidth, aValue,
3458 "outerWidth", aCallerType, aError);
3461 void nsGlobalWindowInner::SetOuterHeight(int32_t aOuterHeight,
3462 CallerType aCallerType,
3463 ErrorResult& aError) {
3464 FORWARD_TO_OUTER_OR_THROW(SetOuterHeightOuter,
3465 (aOuterHeight, aCallerType, aError), aError, );
3468 void nsGlobalWindowInner::SetOuterHeight(JSContext* aCx,
3469 JS::Handle<JS::Value> aValue,
3470 CallerType aCallerType,
3471 ErrorResult& aError) {
3472 SetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::SetOuterHeight, aValue,
3473 "outerHeight", aCallerType, aError);
3476 int32_t nsGlobalWindowInner::GetScreenX(CallerType aCallerType,
3477 ErrorResult& aError) {
3478 FORWARD_TO_OUTER_OR_THROW(GetScreenXOuter, (aCallerType, aError), aError, 0);
3481 void nsGlobalWindowInner::GetScreenX(JSContext* aCx,
3482 JS::MutableHandle<JS::Value> aValue,
3483 CallerType aCallerType,
3484 ErrorResult& aError) {
3485 GetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::GetScreenX, aValue,
3486 aCallerType, aError);
3489 float nsGlobalWindowInner::GetMozInnerScreenX(CallerType aCallerType,
3490 ErrorResult& aError) {
3491 FORWARD_TO_OUTER_OR_THROW(GetMozInnerScreenXOuter, (aCallerType), aError, 0);
3494 float nsGlobalWindowInner::GetMozInnerScreenY(CallerType aCallerType,
3495 ErrorResult& aError) {
3496 FORWARD_TO_OUTER_OR_THROW(GetMozInnerScreenYOuter, (aCallerType), aError, 0);
3499 double nsGlobalWindowInner::GetDevicePixelRatio(CallerType aCallerType,
3500 ErrorResult& aError) {
3501 FORWARD_TO_OUTER_OR_THROW(GetDevicePixelRatioOuter, (aCallerType), aError,
3502 0.0);
3505 uint64_t nsGlobalWindowInner::GetMozPaintCount(ErrorResult& aError) {
3506 FORWARD_TO_OUTER_OR_THROW(GetMozPaintCountOuter, (), aError, 0);
3509 int32_t nsGlobalWindowInner::RequestAnimationFrame(
3510 FrameRequestCallback& aCallback, ErrorResult& aError) {
3511 if (!mDoc) {
3512 return 0;
3515 if (GetWrapperPreserveColor()) {
3516 js::NotifyAnimationActivity(GetWrapperPreserveColor());
3519 DebuggerNotificationDispatch(this,
3520 DebuggerNotificationType::RequestAnimationFrame);
3522 int32_t handle;
3523 aError = mDoc->ScheduleFrameRequestCallback(aCallback, &handle);
3524 return handle;
3527 void nsGlobalWindowInner::CancelAnimationFrame(int32_t aHandle,
3528 ErrorResult& aError) {
3529 if (!mDoc) {
3530 return;
3533 DebuggerNotificationDispatch(this,
3534 DebuggerNotificationType::CancelAnimationFrame);
3536 mDoc->CancelFrameRequestCallback(aHandle);
3539 already_AddRefed<MediaQueryList> nsGlobalWindowInner::MatchMedia(
3540 const nsACString& aMediaQueryList, CallerType aCallerType,
3541 ErrorResult& aError) {
3542 ENSURE_ACTIVE_DOCUMENT(aError, nullptr);
3543 return mDoc->MatchMedia(aMediaQueryList, aCallerType);
3546 void nsGlobalWindowInner::SetScreenX(int32_t aScreenX, CallerType aCallerType,
3547 ErrorResult& aError) {
3548 FORWARD_TO_OUTER_OR_THROW(SetScreenXOuter, (aScreenX, aCallerType, aError),
3549 aError, );
3552 void nsGlobalWindowInner::SetScreenX(JSContext* aCx,
3553 JS::Handle<JS::Value> aValue,
3554 CallerType aCallerType,
3555 ErrorResult& aError) {
3556 SetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::SetScreenX, aValue,
3557 "screenX", aCallerType, aError);
3560 int32_t nsGlobalWindowInner::GetScreenY(CallerType aCallerType,
3561 ErrorResult& aError) {
3562 FORWARD_TO_OUTER_OR_THROW(GetScreenYOuter, (aCallerType, aError), aError, 0);
3565 void nsGlobalWindowInner::GetScreenY(JSContext* aCx,
3566 JS::MutableHandle<JS::Value> aValue,
3567 CallerType aCallerType,
3568 ErrorResult& aError) {
3569 GetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::GetScreenY, aValue,
3570 aCallerType, aError);
3573 void nsGlobalWindowInner::SetScreenY(int32_t aScreenY, CallerType aCallerType,
3574 ErrorResult& aError) {
3575 FORWARD_TO_OUTER_OR_THROW(SetScreenYOuter, (aScreenY, aCallerType, aError),
3576 aError, );
3579 void nsGlobalWindowInner::SetScreenY(JSContext* aCx,
3580 JS::Handle<JS::Value> aValue,
3581 CallerType aCallerType,
3582 ErrorResult& aError) {
3583 SetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::SetScreenY, aValue,
3584 "screenY", aCallerType, aError);
3587 int32_t nsGlobalWindowInner::GetScrollMinX(ErrorResult& aError) {
3588 FORWARD_TO_OUTER_OR_THROW(GetScrollBoundaryOuter, (eSideLeft), aError, 0);
3591 int32_t nsGlobalWindowInner::GetScrollMinY(ErrorResult& aError) {
3592 FORWARD_TO_OUTER_OR_THROW(GetScrollBoundaryOuter, (eSideTop), aError, 0);
3595 int32_t nsGlobalWindowInner::GetScrollMaxX(ErrorResult& aError) {
3596 FORWARD_TO_OUTER_OR_THROW(GetScrollBoundaryOuter, (eSideRight), aError, 0);
3599 int32_t nsGlobalWindowInner::GetScrollMaxY(ErrorResult& aError) {
3600 FORWARD_TO_OUTER_OR_THROW(GetScrollBoundaryOuter, (eSideBottom), aError, 0);
3603 double nsGlobalWindowInner::GetScrollX(ErrorResult& aError) {
3604 FORWARD_TO_OUTER_OR_THROW(GetScrollXOuter, (), aError, 0);
3607 double nsGlobalWindowInner::GetScrollY(ErrorResult& aError) {
3608 FORWARD_TO_OUTER_OR_THROW(GetScrollYOuter, (), aError, 0);
3611 uint32_t nsGlobalWindowInner::Length() { FORWARD_TO_OUTER(Length, (), 0); }
3613 Nullable<WindowProxyHolder> nsGlobalWindowInner::GetTop(
3614 mozilla::ErrorResult& aError) {
3615 FORWARD_TO_OUTER_OR_THROW(GetTopOuter, (), aError, nullptr);
3618 already_AddRefed<BrowsingContext> nsGlobalWindowInner::GetChildWindow(
3619 const nsAString& aName) {
3620 if (GetOuterWindowInternal()) {
3621 return GetOuterWindowInternal()->GetChildWindow(aName);
3623 return nullptr;
3626 void nsGlobalWindowInner::RefreshRealmPrincipal() {
3627 JS::SetRealmPrincipals(js::GetNonCCWObjectRealm(GetWrapperPreserveColor()),
3628 nsJSPrincipals::get(mDoc->NodePrincipal()));
3631 already_AddRefed<nsIWidget> nsGlobalWindowInner::GetMainWidget() {
3632 FORWARD_TO_OUTER(GetMainWidget, (), nullptr);
3635 nsIWidget* nsGlobalWindowInner::GetNearestWidget() const {
3636 if (GetOuterWindowInternal()) {
3637 return GetOuterWindowInternal()->GetNearestWidget();
3639 return nullptr;
3642 void nsGlobalWindowInner::SetFullScreen(bool aFullscreen,
3643 mozilla::ErrorResult& aError) {
3644 FORWARD_TO_OUTER_OR_THROW(SetFullscreenOuter, (aFullscreen, aError), aError,
3645 /* void */);
3648 bool nsGlobalWindowInner::GetFullScreen(ErrorResult& aError) {
3649 FORWARD_TO_OUTER_OR_THROW(GetFullscreenOuter, (), aError, false);
3652 bool nsGlobalWindowInner::GetFullScreen() {
3653 ErrorResult dummy;
3654 bool fullscreen = GetFullScreen(dummy);
3655 dummy.SuppressException();
3656 return fullscreen;
3659 void nsGlobalWindowInner::Dump(const nsAString& aStr) {
3660 if (!nsJSUtils::DumpEnabled()) {
3661 return;
3664 char* cstr = ToNewUTF8String(aStr);
3666 #if defined(XP_MACOSX)
3667 // have to convert \r to \n so that printing to the console works
3668 char *c = cstr, *cEnd = cstr + strlen(cstr);
3669 while (c < cEnd) {
3670 if (*c == '\r') *c = '\n';
3671 c++;
3673 #endif
3675 if (cstr) {
3676 MOZ_LOG(nsContentUtils::DOMDumpLog(), LogLevel::Debug,
3677 ("[Window.Dump] %s", cstr));
3678 #ifdef XP_WIN
3679 PrintToDebugger(cstr);
3680 #endif
3681 #ifdef ANDROID
3682 __android_log_write(ANDROID_LOG_INFO, "GeckoDump", cstr);
3683 #endif
3684 FILE* fp = gDumpFile ? gDumpFile : stdout;
3685 fputs(cstr, fp);
3686 fflush(fp);
3687 free(cstr);
3691 void nsGlobalWindowInner::Alert(nsIPrincipal& aSubjectPrincipal,
3692 ErrorResult& aError) {
3693 Alert(u""_ns, aSubjectPrincipal, aError);
3696 void nsGlobalWindowInner::Alert(const nsAString& aMessage,
3697 nsIPrincipal& aSubjectPrincipal,
3698 ErrorResult& aError) {
3699 FORWARD_TO_OUTER_OR_THROW(AlertOuter, (aMessage, aSubjectPrincipal, aError),
3700 aError, );
3703 bool nsGlobalWindowInner::Confirm(const nsAString& aMessage,
3704 nsIPrincipal& aSubjectPrincipal,
3705 ErrorResult& aError) {
3706 FORWARD_TO_OUTER_OR_THROW(ConfirmOuter, (aMessage, aSubjectPrincipal, aError),
3707 aError, false);
3710 already_AddRefed<Promise> nsGlobalWindowInner::Fetch(
3711 const RequestOrUSVString& aInput, const RequestInit& aInit,
3712 CallerType aCallerType, ErrorResult& aRv) {
3713 return FetchRequest(this, aInput, aInit, aCallerType, aRv);
3716 void nsGlobalWindowInner::Prompt(const nsAString& aMessage,
3717 const nsAString& aInitial, nsAString& aReturn,
3718 nsIPrincipal& aSubjectPrincipal,
3719 ErrorResult& aError) {
3720 FORWARD_TO_OUTER_OR_THROW(
3721 PromptOuter, (aMessage, aInitial, aReturn, aSubjectPrincipal, aError),
3722 aError, );
3725 void nsGlobalWindowInner::Focus(CallerType aCallerType, ErrorResult& aError) {
3726 FORWARD_TO_OUTER_OR_THROW(
3727 FocusOuter, (aCallerType, nsFocusManager::GenerateFocusActionId()),
3728 aError, );
3731 nsresult nsGlobalWindowInner::Focus(CallerType aCallerType) {
3732 ErrorResult rv;
3733 Focus(aCallerType, rv);
3735 return rv.StealNSResult();
3738 void nsGlobalWindowInner::Blur(ErrorResult& aError) {
3739 FORWARD_TO_OUTER_OR_THROW(BlurOuter, (), aError, );
3742 void nsGlobalWindowInner::Stop(ErrorResult& aError) {
3743 FORWARD_TO_OUTER_OR_THROW(StopOuter, (aError), aError, );
3746 void nsGlobalWindowInner::Print(ErrorResult& aError) {
3747 FORWARD_TO_OUTER_OR_THROW(PrintOuter, (aError), aError, );
3750 Nullable<WindowProxyHolder> nsGlobalWindowInner::PrintPreview(
3751 nsIPrintSettings* aSettings, nsIWebProgressListener* aListener,
3752 nsIDocShell* aDocShellToCloneInto, ErrorResult& aError) {
3753 FORWARD_TO_OUTER_OR_THROW(Print,
3754 (aSettings, aListener, aDocShellToCloneInto,
3755 nsGlobalWindowOuter::IsPreview::Yes,
3756 nsGlobalWindowOuter::IsForWindowDotPrint::No,
3757 /* aPrintPreviewCallback = */ nullptr, aError),
3758 aError, nullptr);
3761 void nsGlobalWindowInner::MoveTo(int32_t aXPos, int32_t aYPos,
3762 CallerType aCallerType, ErrorResult& aError) {
3763 FORWARD_TO_OUTER_OR_THROW(MoveToOuter, (aXPos, aYPos, aCallerType, aError),
3764 aError, );
3767 void nsGlobalWindowInner::MoveBy(int32_t aXDif, int32_t aYDif,
3768 CallerType aCallerType, ErrorResult& aError) {
3769 FORWARD_TO_OUTER_OR_THROW(MoveByOuter, (aXDif, aYDif, aCallerType, aError),
3770 aError, );
3773 void nsGlobalWindowInner::ResizeTo(int32_t aWidth, int32_t aHeight,
3774 CallerType aCallerType,
3775 ErrorResult& aError) {
3776 FORWARD_TO_OUTER_OR_THROW(ResizeToOuter,
3777 (aWidth, aHeight, aCallerType, aError), aError, );
3780 void nsGlobalWindowInner::ResizeBy(int32_t aWidthDif, int32_t aHeightDif,
3781 CallerType aCallerType,
3782 ErrorResult& aError) {
3783 FORWARD_TO_OUTER_OR_THROW(
3784 ResizeByOuter, (aWidthDif, aHeightDif, aCallerType, aError), aError, );
3787 void nsGlobalWindowInner::SizeToContent(CallerType aCallerType,
3788 ErrorResult& aError) {
3789 FORWARD_TO_OUTER_OR_THROW(SizeToContentOuter, (aCallerType, aError),
3790 aError, );
3793 already_AddRefed<nsPIWindowRoot> nsGlobalWindowInner::GetTopWindowRoot() {
3794 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
3795 if (!outer) {
3796 return nullptr;
3798 return outer->GetTopWindowRoot();
3801 void nsGlobalWindowInner::Scroll(double aXScroll, double aYScroll) {
3802 // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
3803 auto scrollPos = CSSIntPoint::Truncate(mozilla::ToZeroIfNonfinite(aXScroll),
3804 mozilla::ToZeroIfNonfinite(aYScroll));
3805 ScrollTo(scrollPos, ScrollOptions());
3808 void nsGlobalWindowInner::ScrollTo(double aXScroll, double aYScroll) {
3809 // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
3810 auto scrollPos = CSSIntPoint::Truncate(mozilla::ToZeroIfNonfinite(aXScroll),
3811 mozilla::ToZeroIfNonfinite(aYScroll));
3812 ScrollTo(scrollPos, ScrollOptions());
3815 void nsGlobalWindowInner::ScrollTo(const ScrollToOptions& aOptions) {
3816 // When scrolling to a non-zero offset, we need to determine whether that
3817 // position is within our scrollable range, so we need updated layout
3818 // information which requires a layout flush, otherwise all we need is to
3819 // flush frames to be able to access our scrollable frame here.
3820 FlushType flushType =
3821 ((aOptions.mLeft.WasPassed() && aOptions.mLeft.Value() > 0) ||
3822 (aOptions.mTop.WasPassed() && aOptions.mTop.Value() > 0))
3823 ? FlushType::Layout
3824 : FlushType::Frames;
3825 FlushPendingNotifications(flushType);
3826 nsIScrollableFrame* sf = GetScrollFrame();
3828 if (sf) {
3829 CSSIntPoint scrollPos = sf->GetScrollPositionCSSPixels();
3830 if (aOptions.mLeft.WasPassed()) {
3831 scrollPos.x = mozilla::ToZeroIfNonfinite(aOptions.mLeft.Value());
3833 if (aOptions.mTop.WasPassed()) {
3834 scrollPos.y = mozilla::ToZeroIfNonfinite(aOptions.mTop.Value());
3837 ScrollTo(scrollPos, aOptions);
3841 void nsGlobalWindowInner::Scroll(const ScrollToOptions& aOptions) {
3842 ScrollTo(aOptions);
3845 void nsGlobalWindowInner::ScrollTo(const CSSIntPoint& aScroll,
3846 const ScrollOptions& aOptions) {
3847 // When scrolling to a non-zero offset, we need to determine whether that
3848 // position is within our scrollable range, so we need updated layout
3849 // information which requires a layout flush, otherwise all we need is to
3850 // flush frames to be able to access our scrollable frame here.
3851 FlushType flushType =
3852 (aScroll.x || aScroll.y) ? FlushType::Layout : FlushType::Frames;
3853 FlushPendingNotifications(flushType);
3854 nsIScrollableFrame* sf = GetScrollFrame();
3856 if (sf) {
3857 // Here we calculate what the max pixel value is that we can
3858 // scroll to, we do this by dividing maxint with the pixel to
3859 // twips conversion factor, and subtracting 4, the 4 comes from
3860 // experimenting with this value, anything less makes the view
3861 // code not scroll correctly, I have no idea why. -- jst
3862 const int32_t maxpx = nsPresContext::AppUnitsToIntCSSPixels(0x7fffffff) - 4;
3864 CSSIntPoint scroll(aScroll);
3865 if (scroll.x > maxpx) {
3866 scroll.x = maxpx;
3869 if (scroll.y > maxpx) {
3870 scroll.y = maxpx;
3873 ScrollMode scrollMode = sf->IsSmoothScroll(aOptions.mBehavior)
3874 ? ScrollMode::SmoothMsd
3875 : ScrollMode::Instant;
3877 sf->ScrollToCSSPixels(scroll, scrollMode);
3881 void nsGlobalWindowInner::ScrollBy(double aXScrollDif, double aYScrollDif) {
3882 FlushPendingNotifications(FlushType::Layout);
3883 nsIScrollableFrame* sf = GetScrollFrame();
3885 if (sf) {
3886 // It seems like it would make more sense for ScrollBy to use
3887 // SMOOTH mode, but tests seem to depend on the synchronous behaviour.
3888 // Perhaps Web content does too.
3889 ScrollToOptions options;
3890 options.mLeft.Construct(aXScrollDif);
3891 options.mTop.Construct(aYScrollDif);
3892 ScrollBy(options);
3896 void nsGlobalWindowInner::ScrollBy(const ScrollToOptions& aOptions) {
3897 FlushPendingNotifications(FlushType::Layout);
3898 nsIScrollableFrame* sf = GetScrollFrame();
3900 if (sf) {
3901 CSSIntPoint scrollDelta;
3902 if (aOptions.mLeft.WasPassed()) {
3903 scrollDelta.x = mozilla::ToZeroIfNonfinite(aOptions.mLeft.Value());
3905 if (aOptions.mTop.WasPassed()) {
3906 scrollDelta.y = mozilla::ToZeroIfNonfinite(aOptions.mTop.Value());
3909 ScrollMode scrollMode = sf->IsSmoothScroll(aOptions.mBehavior)
3910 ? ScrollMode::SmoothMsd
3911 : ScrollMode::Instant;
3913 sf->ScrollByCSSPixels(scrollDelta, scrollMode,
3914 mozilla::ScrollOrigin::Relative);
3918 void nsGlobalWindowInner::ScrollByLines(int32_t numLines,
3919 const ScrollOptions& aOptions) {
3920 FlushPendingNotifications(FlushType::Layout);
3921 nsIScrollableFrame* sf = GetScrollFrame();
3922 if (sf) {
3923 // It seems like it would make more sense for ScrollByLines to use
3924 // SMOOTH mode, but tests seem to depend on the synchronous behaviour.
3925 // Perhaps Web content does too.
3926 ScrollMode scrollMode = sf->IsSmoothScroll(aOptions.mBehavior)
3927 ? ScrollMode::SmoothMsd
3928 : ScrollMode::Instant;
3930 sf->ScrollBy(nsIntPoint(0, numLines), ScrollUnit::LINES, scrollMode);
3934 void nsGlobalWindowInner::ScrollByPages(int32_t numPages,
3935 const ScrollOptions& aOptions) {
3936 FlushPendingNotifications(FlushType::Layout);
3937 nsIScrollableFrame* sf = GetScrollFrame();
3938 if (sf) {
3939 // It seems like it would make more sense for ScrollByPages to use
3940 // SMOOTH mode, but tests seem to depend on the synchronous behaviour.
3941 // Perhaps Web content does too.
3942 ScrollMode scrollMode = sf->IsSmoothScroll(aOptions.mBehavior)
3943 ? ScrollMode::SmoothMsd
3944 : ScrollMode::Instant;
3946 sf->ScrollBy(nsIntPoint(0, numPages), ScrollUnit::PAGES, scrollMode);
3950 void nsGlobalWindowInner::MozScrollSnap() {
3951 FlushPendingNotifications(FlushType::Layout);
3952 nsIScrollableFrame* sf = GetScrollFrame();
3953 if (sf) {
3954 sf->ScrollSnap();
3958 void nsGlobalWindowInner::ClearTimeout(int32_t aHandle) {
3959 DebuggerNotificationDispatch(this, DebuggerNotificationType::ClearTimeout);
3961 if (aHandle > 0) {
3962 mTimeoutManager->ClearTimeout(aHandle, Timeout::Reason::eTimeoutOrInterval);
3966 void nsGlobalWindowInner::ClearInterval(int32_t aHandle) {
3967 DebuggerNotificationDispatch(this, DebuggerNotificationType::ClearInterval);
3969 if (aHandle > 0) {
3970 mTimeoutManager->ClearTimeout(aHandle, Timeout::Reason::eTimeoutOrInterval);
3974 void nsGlobalWindowInner::SetResizable(bool aResizable) const {
3975 // nop
3978 void nsGlobalWindowInner::CaptureEvents() {
3979 if (mDoc) {
3980 mDoc->WarnOnceAbout(DeprecatedOperations::eUseOfCaptureEvents);
3984 void nsGlobalWindowInner::ReleaseEvents() {
3985 if (mDoc) {
3986 mDoc->WarnOnceAbout(DeprecatedOperations::eUseOfReleaseEvents);
3990 Nullable<WindowProxyHolder> nsGlobalWindowInner::Open(const nsAString& aUrl,
3991 const nsAString& aName,
3992 const nsAString& aOptions,
3993 ErrorResult& aError) {
3994 FORWARD_TO_OUTER_OR_THROW(OpenOuter, (aUrl, aName, aOptions, aError), aError,
3995 nullptr);
3998 Nullable<WindowProxyHolder> nsGlobalWindowInner::OpenDialog(
3999 JSContext* aCx, const nsAString& aUrl, const nsAString& aName,
4000 const nsAString& aOptions, const Sequence<JS::Value>& aExtraArgument,
4001 ErrorResult& aError) {
4002 FORWARD_TO_OUTER_OR_THROW(
4003 OpenDialogOuter, (aCx, aUrl, aName, aOptions, aExtraArgument, aError),
4004 aError, nullptr);
4007 WindowProxyHolder nsGlobalWindowInner::GetFrames(ErrorResult& aError) {
4008 FORWARD_TO_OUTER_OR_THROW(GetFramesOuter, (), aError, Window());
4011 void nsGlobalWindowInner::PostMessageMoz(JSContext* aCx,
4012 JS::Handle<JS::Value> aMessage,
4013 const nsAString& aTargetOrigin,
4014 JS::Handle<JS::Value> aTransfer,
4015 nsIPrincipal& aSubjectPrincipal,
4016 ErrorResult& aError) {
4017 FORWARD_TO_OUTER_OR_THROW(
4018 PostMessageMozOuter,
4019 (aCx, aMessage, aTargetOrigin, aTransfer, aSubjectPrincipal, aError),
4020 aError, );
4023 void nsGlobalWindowInner::PostMessageMoz(JSContext* aCx,
4024 JS::Handle<JS::Value> aMessage,
4025 const nsAString& aTargetOrigin,
4026 const Sequence<JSObject*>& aTransfer,
4027 nsIPrincipal& aSubjectPrincipal,
4028 ErrorResult& aRv) {
4029 JS::Rooted<JS::Value> transferArray(aCx, JS::UndefinedValue());
4031 aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransfer,
4032 &transferArray);
4033 if (NS_WARN_IF(aRv.Failed())) {
4034 return;
4037 PostMessageMoz(aCx, aMessage, aTargetOrigin, transferArray, aSubjectPrincipal,
4038 aRv);
4041 void nsGlobalWindowInner::PostMessageMoz(
4042 JSContext* aCx, JS::Handle<JS::Value> aMessage,
4043 const WindowPostMessageOptions& aOptions, nsIPrincipal& aSubjectPrincipal,
4044 ErrorResult& aRv) {
4045 JS::Rooted<JS::Value> transferArray(aCx, JS::UndefinedValue());
4047 aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(
4048 aCx, aOptions.mTransfer, &transferArray);
4049 if (NS_WARN_IF(aRv.Failed())) {
4050 return;
4053 PostMessageMoz(aCx, aMessage, aOptions.mTargetOrigin, transferArray,
4054 aSubjectPrincipal, aRv);
4057 void nsGlobalWindowInner::Close(CallerType aCallerType, ErrorResult& aError) {
4058 FORWARD_TO_OUTER_OR_THROW(CloseOuter, (aCallerType == CallerType::System),
4059 aError, );
4062 nsresult nsGlobalWindowInner::Close() {
4063 FORWARD_TO_OUTER(Close, (), NS_ERROR_UNEXPECTED);
4066 bool nsGlobalWindowInner::IsInModalState() {
4067 FORWARD_TO_OUTER(IsInModalState, (), false);
4070 // static
4071 void nsGlobalWindowInner::NotifyDOMWindowDestroyed(
4072 nsGlobalWindowInner* aWindow) {
4073 nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
4074 if (observerService) {
4075 observerService->NotifyObservers(ToSupports(aWindow),
4076 DOM_WINDOW_DESTROYED_TOPIC, nullptr);
4080 void nsGlobalWindowInner::NotifyWindowIDDestroyed(const char* aTopic) {
4081 nsCOMPtr<nsIRunnable> runnable =
4082 new WindowDestroyedEvent(this, mWindowID, aTopic);
4083 Dispatch(TaskCategory::Other, runnable.forget());
4086 // static
4087 void nsGlobalWindowInner::NotifyDOMWindowFrozen(nsGlobalWindowInner* aWindow) {
4088 if (aWindow) {
4089 nsCOMPtr<nsIObserverService> observerService =
4090 services::GetObserverService();
4091 if (observerService) {
4092 observerService->NotifyObservers(ToSupports(aWindow),
4093 DOM_WINDOW_FROZEN_TOPIC, nullptr);
4098 // static
4099 void nsGlobalWindowInner::NotifyDOMWindowThawed(nsGlobalWindowInner* aWindow) {
4100 if (aWindow) {
4101 nsCOMPtr<nsIObserverService> observerService =
4102 services::GetObserverService();
4103 if (observerService) {
4104 observerService->NotifyObservers(ToSupports(aWindow),
4105 DOM_WINDOW_THAWED_TOPIC, nullptr);
4110 Element* nsGlobalWindowInner::GetFrameElement(nsIPrincipal& aSubjectPrincipal,
4111 ErrorResult& aError) {
4112 FORWARD_TO_OUTER_OR_THROW(GetFrameElement, (aSubjectPrincipal), aError,
4113 nullptr);
4116 Element* nsGlobalWindowInner::GetRealFrameElement(ErrorResult& aError) {
4117 FORWARD_TO_OUTER_OR_THROW(GetFrameElement, (), aError, nullptr);
4120 void nsGlobalWindowInner::UpdateCommands(const nsAString& anAction,
4121 Selection* aSel, int16_t aReason) {
4122 if (GetOuterWindowInternal()) {
4123 GetOuterWindowInternal()->UpdateCommands(anAction, aSel, aReason);
4127 Selection* nsGlobalWindowInner::GetSelection(ErrorResult& aError) {
4128 FORWARD_TO_OUTER_OR_THROW(GetSelectionOuter, (), aError, nullptr);
4131 bool nsGlobalWindowInner::Find(const nsAString& aString, bool aCaseSensitive,
4132 bool aBackwards, bool aWrapAround,
4133 bool aWholeWord, bool aSearchInFrames,
4134 bool aShowDialog, ErrorResult& aError) {
4135 FORWARD_TO_OUTER_OR_THROW(FindOuter,
4136 (aString, aCaseSensitive, aBackwards, aWrapAround,
4137 aWholeWord, aSearchInFrames, aShowDialog, aError),
4138 aError, false);
4141 void nsGlobalWindowInner::GetOrigin(nsAString& aOrigin) {
4142 nsContentUtils::GetUTFOrigin(GetPrincipal(), aOrigin);
4145 void nsGlobalWindowInner::Atob(const nsAString& aAsciiBase64String,
4146 nsAString& aBinaryData, ErrorResult& aError) {
4147 aError = nsContentUtils::Atob(aAsciiBase64String, aBinaryData);
4150 void nsGlobalWindowInner::Btoa(const nsAString& aBinaryData,
4151 nsAString& aAsciiBase64String,
4152 ErrorResult& aError) {
4153 aError = nsContentUtils::Btoa(aBinaryData, aAsciiBase64String);
4156 //*****************************************************************************
4157 // EventTarget
4158 //*****************************************************************************
4160 nsPIDOMWindowOuter* nsGlobalWindowInner::GetOwnerGlobalForBindingsInternal() {
4161 return nsPIDOMWindowOuter::GetFromCurrentInner(this);
4164 bool nsGlobalWindowInner::DispatchEvent(Event& aEvent, CallerType aCallerType,
4165 ErrorResult& aRv) {
4166 if (!IsCurrentInnerWindow()) {
4167 NS_WARNING(
4168 "DispatchEvent called on non-current inner window, dropping. "
4169 "Please check the window in the caller instead.");
4170 aRv.Throw(NS_ERROR_FAILURE);
4171 return false;
4174 if (!mDoc) {
4175 aRv.Throw(NS_ERROR_FAILURE);
4176 return false;
4179 // Obtain a presentation shell
4180 RefPtr<nsPresContext> presContext = mDoc->GetPresContext();
4182 nsEventStatus status = nsEventStatus_eIgnore;
4183 nsresult rv = EventDispatcher::DispatchDOMEvent(
4184 ToSupports(this), nullptr, &aEvent, presContext, &status);
4185 bool retval = !aEvent.DefaultPrevented(aCallerType);
4186 if (NS_FAILED(rv)) {
4187 aRv.Throw(rv);
4189 return retval;
4192 mozilla::Maybe<mozilla::dom::EventCallbackDebuggerNotificationType>
4193 nsGlobalWindowInner::GetDebuggerNotificationType() const {
4194 return mozilla::Some(
4195 mozilla::dom::EventCallbackDebuggerNotificationType::Global);
4198 bool nsGlobalWindowInner::ComputeDefaultWantsUntrusted(ErrorResult& aRv) {
4199 return !nsContentUtils::IsChromeDoc(mDoc);
4202 EventListenerManager* nsGlobalWindowInner::GetOrCreateListenerManager() {
4203 if (!mListenerManager) {
4204 mListenerManager =
4205 new EventListenerManager(static_cast<EventTarget*>(this));
4208 return mListenerManager;
4211 EventListenerManager* nsGlobalWindowInner::GetExistingListenerManager() const {
4212 return mListenerManager;
4215 mozilla::dom::DebuggerNotificationManager*
4216 nsGlobalWindowInner::GetOrCreateDebuggerNotificationManager() {
4217 if (!mDebuggerNotificationManager) {
4218 mDebuggerNotificationManager = new DebuggerNotificationManager(this);
4221 return mDebuggerNotificationManager;
4224 mozilla::dom::DebuggerNotificationManager*
4225 nsGlobalWindowInner::GetExistingDebuggerNotificationManager() {
4226 return mDebuggerNotificationManager;
4229 //*****************************************************************************
4230 // nsGlobalWindowInner::nsPIDOMWindow
4231 //*****************************************************************************
4233 Location* nsGlobalWindowInner::Location() {
4234 if (!mLocation) {
4235 mLocation = new dom::Location(this, GetBrowsingContext());
4238 return mLocation;
4241 void nsGlobalWindowInner::MaybeUpdateTouchState() {
4242 if (mMayHaveTouchEventListener) {
4243 nsCOMPtr<nsIObserverService> observerService =
4244 services::GetObserverService();
4246 if (observerService) {
4247 observerService->NotifyObservers(static_cast<nsIDOMWindow*>(this),
4248 DOM_TOUCH_LISTENER_ADDED, nullptr);
4253 void nsGlobalWindowInner::EnableGamepadUpdates() {
4254 if (mHasGamepad) {
4255 RefPtr<GamepadManager> gamepadManager(GamepadManager::GetService());
4256 if (gamepadManager) {
4257 gamepadManager->AddListener(this);
4262 void nsGlobalWindowInner::DisableGamepadUpdates() {
4263 if (mHasGamepad) {
4264 RefPtr<GamepadManager> gamepadManager(GamepadManager::GetService());
4265 if (gamepadManager) {
4266 gamepadManager->RemoveListener(this);
4271 void nsGlobalWindowInner::EnableVRUpdates() {
4272 // We need to create a VREventObserver before we can either detect XR runtimes
4273 // or start an XR session
4274 if (!mVREventObserver && (mHasXRSession || mXRRuntimeDetectionInFlight)) {
4275 // Assert that we are not creating the observer while IsDying() as
4276 // that would result in a leak. VREventObserver holds a RefPtr to
4277 // this nsGlobalWindowInner and would prevent it from being deallocated.
4278 MOZ_ASSERT(!IsDying(),
4279 "Creating a VREventObserver for an nsGlobalWindow that is "
4280 "dying would cause it to leak.");
4281 mVREventObserver = new VREventObserver(this);
4283 // If the content has an XR session, then we need to tell
4284 // VREventObserver that there is VR activity.
4285 if (mHasXRSession) {
4286 nsPIDOMWindowOuter* outer = GetOuterWindow();
4287 if (outer && !outer->IsBackground()) {
4288 StartVRActivity();
4293 void nsGlobalWindowInner::DisableVRUpdates() {
4294 if (mVREventObserver) {
4295 mVREventObserver->DisconnectFromOwner();
4296 mVREventObserver = nullptr;
4300 void nsGlobalWindowInner::ResetVRTelemetry(bool aUpdate) {
4301 if (mVREventObserver) {
4302 mVREventObserver->UpdateSpentTimeIn2DTelemetry(aUpdate);
4306 void nsGlobalWindowInner::StartVRActivity() {
4308 * If the content has an XR session, tell
4309 * the VREventObserver that the window is accessing
4310 * VR devices.
4312 * It's possible to have a VREventObserver without
4313 * and XR session, if we are using it to get updates
4314 * about XR runtime enumeration. In this case,
4315 * we would not tell the VREventObserver that
4316 * we are accessing VR devices.
4318 if (mVREventObserver && mHasXRSession) {
4319 mVREventObserver->StartActivity();
4323 void nsGlobalWindowInner::StopVRActivity() {
4325 * If the content has an XR session, tell
4326 * the VReventObserver that the window is no longer
4327 * accessing VR devices. This does not stop the
4328 * XR session itself, which may be resumed with
4329 * EnableVRUpdates.
4330 * It's possible to have a VREventObserver without
4331 * and XR session, if we are using it to get updates
4332 * about XR runtime enumeration. In this case,
4333 * we would not tell the VREventObserver that
4334 * we ending an activity that accesses VR devices.
4336 if (mVREventObserver && mHasXRSession) {
4337 mVREventObserver->StopActivity();
4341 void nsGlobalWindowInner::SetFocusedElement(Element* aElement,
4342 uint32_t aFocusMethod,
4343 bool aNeedsFocus) {
4344 if (aElement && aElement->GetComposedDoc() != mDoc) {
4345 NS_WARNING("Trying to set focus to a node from a wrong document");
4346 return;
4349 if (IsDying()) {
4350 NS_ASSERTION(!aElement, "Trying to focus cleaned up window!");
4351 aElement = nullptr;
4352 aNeedsFocus = false;
4354 if (mFocusedElement != aElement) {
4355 UpdateCanvasFocus(false, aElement);
4356 mFocusedElement = aElement;
4357 mFocusMethod = aFocusMethod & FOCUSMETHOD_MASK;
4360 if (mFocusedElement) {
4361 // if a node was focused by a keypress, turn on focus rings for the
4362 // window.
4363 if (mFocusMethod & nsIFocusManager::FLAG_BYKEY) {
4364 mFocusByKeyOccurred = true;
4368 if (aNeedsFocus) mNeedsFocus = aNeedsFocus;
4371 uint32_t nsGlobalWindowInner::GetFocusMethod() { return mFocusMethod; }
4373 bool nsGlobalWindowInner::ShouldShowFocusRing() {
4374 if (mFocusByKeyOccurred &&
4375 StaticPrefs::browser_display_always_show_rings_after_key_focus()) {
4376 return true;
4379 nsCOMPtr<nsPIWindowRoot> root = GetTopWindowRoot();
4380 return root && root->ShowFocusRings();
4383 bool nsGlobalWindowInner::TakeFocus(bool aFocus, uint32_t aFocusMethod) {
4384 if (IsDying()) {
4385 return false;
4388 if (aFocus) mFocusMethod = aFocusMethod & FOCUSMETHOD_MASK;
4390 if (mHasFocus != aFocus) {
4391 mHasFocus = aFocus;
4392 UpdateCanvasFocus(true, mFocusedElement);
4395 // if mNeedsFocus is true, then the document has not yet received a
4396 // document-level focus event. If there is a root content node, then return
4397 // true to tell the calling focus manager that a focus event is expected. If
4398 // there is no root content node, the document hasn't loaded enough yet, or
4399 // there isn't one and there is no point in firing a focus event.
4400 if (aFocus && mNeedsFocus && mDoc && mDoc->GetRootElement() != nullptr) {
4401 mNeedsFocus = false;
4402 return true;
4405 mNeedsFocus = false;
4406 return false;
4409 void nsGlobalWindowInner::SetReadyForFocus() {
4410 bool oldNeedsFocus = mNeedsFocus;
4411 mNeedsFocus = false;
4413 nsFocusManager* fm = nsFocusManager::GetFocusManager();
4414 if (fm) {
4415 fm->WindowShown(GetOuterWindow(), oldNeedsFocus);
4419 void nsGlobalWindowInner::PageHidden() {
4420 // the window is being hidden, so tell the focus manager that the frame is
4421 // no longer valid. Use the persisted field to determine if the document
4422 // is being destroyed.
4424 nsFocusManager* fm = nsFocusManager::GetFocusManager();
4425 if (fm) {
4426 fm->WindowHidden(GetOuterWindow(), nsFocusManager::GenerateFocusActionId());
4429 mNeedsFocus = true;
4432 class HashchangeCallback : public Runnable {
4433 public:
4434 HashchangeCallback(const nsAString& aOldURL, const nsAString& aNewURL,
4435 nsGlobalWindowInner* aWindow)
4436 : mozilla::Runnable("HashchangeCallback"), mWindow(aWindow) {
4437 MOZ_ASSERT(mWindow);
4438 mOldURL.Assign(aOldURL);
4439 mNewURL.Assign(aNewURL);
4442 NS_IMETHOD Run() override {
4443 MOZ_ASSERT(NS_IsMainThread(), "Should be called on the main thread.");
4444 return mWindow->FireHashchange(mOldURL, mNewURL);
4447 private:
4448 nsString mOldURL;
4449 nsString mNewURL;
4450 RefPtr<nsGlobalWindowInner> mWindow;
4453 nsresult nsGlobalWindowInner::DispatchAsyncHashchange(nsIURI* aOldURI,
4454 nsIURI* aNewURI) {
4455 // Make sure that aOldURI and aNewURI are identical up to the '#', and that
4456 // their hashes are different.
4457 bool equal = false;
4458 NS_ENSURE_STATE(NS_SUCCEEDED(aOldURI->EqualsExceptRef(aNewURI, &equal)) &&
4459 equal);
4460 nsAutoCString oldHash, newHash;
4461 bool oldHasHash, newHasHash;
4462 NS_ENSURE_STATE(NS_SUCCEEDED(aOldURI->GetRef(oldHash)) &&
4463 NS_SUCCEEDED(aNewURI->GetRef(newHash)) &&
4464 NS_SUCCEEDED(aOldURI->GetHasRef(&oldHasHash)) &&
4465 NS_SUCCEEDED(aNewURI->GetHasRef(&newHasHash)) &&
4466 (oldHasHash != newHasHash || !oldHash.Equals(newHash)));
4468 nsAutoCString oldSpec, newSpec;
4469 nsresult rv = aOldURI->GetSpec(oldSpec);
4470 NS_ENSURE_SUCCESS(rv, rv);
4471 rv = aNewURI->GetSpec(newSpec);
4472 NS_ENSURE_SUCCESS(rv, rv);
4474 NS_ConvertUTF8toUTF16 oldWideSpec(oldSpec);
4475 NS_ConvertUTF8toUTF16 newWideSpec(newSpec);
4477 nsCOMPtr<nsIRunnable> callback =
4478 new HashchangeCallback(oldWideSpec, newWideSpec, this);
4479 return Dispatch(TaskCategory::Other, callback.forget());
4482 nsresult nsGlobalWindowInner::FireHashchange(const nsAString& aOldURL,
4483 const nsAString& aNewURL) {
4484 // Don't do anything if the window is frozen.
4485 if (IsFrozen()) {
4486 return NS_OK;
4489 // Get a presentation shell for use in creating the hashchange event.
4490 NS_ENSURE_STATE(IsCurrentInnerWindow());
4492 HashChangeEventInit init;
4493 init.mNewURL = aNewURL;
4494 init.mOldURL = aOldURL;
4496 RefPtr<HashChangeEvent> event =
4497 HashChangeEvent::Constructor(this, u"hashchange"_ns, init);
4499 event->SetTrusted(true);
4501 ErrorResult rv;
4502 DispatchEvent(*event, rv);
4503 return rv.StealNSResult();
4506 nsresult nsGlobalWindowInner::DispatchSyncPopState() {
4507 NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
4508 "Must be safe to run script here.");
4510 // Bail if the window is frozen.
4511 if (IsFrozen()) {
4512 return NS_OK;
4515 // Get the document's pending state object -- it contains the data we're
4516 // going to send along with the popstate event. The object is serialized
4517 // using structured clone.
4518 nsCOMPtr<nsIVariant> stateObj;
4519 nsresult rv = mDoc->GetStateObject(getter_AddRefs(stateObj));
4520 NS_ENSURE_SUCCESS(rv, rv);
4522 AutoJSAPI jsapi;
4523 bool result = jsapi.Init(this);
4524 NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
4526 JSContext* cx = jsapi.cx();
4527 JS::Rooted<JS::Value> stateJSValue(cx, JS::NullValue());
4528 result = stateObj ? VariantToJsval(cx, stateObj, &stateJSValue) : true;
4529 NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
4531 RootedDictionary<PopStateEventInit> init(cx);
4532 init.mState = stateJSValue;
4534 RefPtr<PopStateEvent> event =
4535 PopStateEvent::Constructor(this, u"popstate"_ns, init);
4536 event->SetTrusted(true);
4537 event->SetTarget(this);
4539 ErrorResult err;
4540 DispatchEvent(*event, err);
4541 return err.StealNSResult();
4544 //-------------------------------------------------------
4545 // Tells the HTMLFrame/CanvasFrame that is now has focus
4546 void nsGlobalWindowInner::UpdateCanvasFocus(bool aFocusChanged,
4547 nsIContent* aNewContent) {
4548 // this is called from the inner window so use GetDocShell
4549 nsIDocShell* docShell = GetDocShell();
4550 if (!docShell) return;
4552 bool editable;
4553 docShell->GetEditable(&editable);
4554 if (editable) return;
4556 PresShell* presShell = docShell->GetPresShell();
4557 if (!presShell || !mDoc) {
4558 return;
4561 Element* rootElement = mDoc->GetRootElement();
4562 if (rootElement) {
4563 if ((mHasFocus || aFocusChanged) &&
4564 (mFocusedElement == rootElement || aNewContent == rootElement)) {
4565 nsCanvasFrame* canvasFrame = presShell->GetCanvasFrame();
4566 if (canvasFrame) {
4567 canvasFrame->SetHasFocus(mHasFocus && rootElement == aNewContent);
4570 } else {
4571 // XXXbz I would expect that there is never a canvasFrame in this case...
4572 nsCanvasFrame* canvasFrame = presShell->GetCanvasFrame();
4573 if (canvasFrame) {
4574 canvasFrame->SetHasFocus(false);
4579 already_AddRefed<nsICSSDeclaration> nsGlobalWindowInner::GetComputedStyle(
4580 Element& aElt, const nsAString& aPseudoElt, ErrorResult& aError) {
4581 return GetComputedStyleHelper(aElt, aPseudoElt, false, aError);
4584 already_AddRefed<nsICSSDeclaration>
4585 nsGlobalWindowInner::GetDefaultComputedStyle(Element& aElt,
4586 const nsAString& aPseudoElt,
4587 ErrorResult& aError) {
4588 return GetComputedStyleHelper(aElt, aPseudoElt, true, aError);
4591 already_AddRefed<nsICSSDeclaration> nsGlobalWindowInner::GetComputedStyleHelper(
4592 Element& aElt, const nsAString& aPseudoElt, bool aDefaultStylesOnly,
4593 ErrorResult& aError) {
4594 FORWARD_TO_OUTER_OR_THROW(GetComputedStyleHelperOuter,
4595 (aElt, aPseudoElt, aDefaultStylesOnly), aError,
4596 nullptr);
4599 Storage* nsGlobalWindowInner::GetSessionStorage(ErrorResult& aError) {
4600 nsIPrincipal* principal = GetPrincipal();
4601 nsIPrincipal* storagePrincipal = GetEffectiveStoragePrincipal();
4602 BrowsingContext* browsingContext = GetBrowsingContext();
4604 if (!principal || !storagePrincipal || !browsingContext ||
4605 !Storage::StoragePrefIsEnabled()) {
4606 return nullptr;
4609 if (mSessionStorage) {
4610 MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
4611 ("nsGlobalWindowInner %p has %p sessionStorage", this,
4612 mSessionStorage.get()));
4613 bool canAccess =
4614 principal->Subsumes(mSessionStorage->Principal()) &&
4615 storagePrincipal->Subsumes(mSessionStorage->StoragePrincipal());
4616 if (!canAccess) {
4617 mSessionStorage = nullptr;
4621 if (!mSessionStorage) {
4622 nsString documentURI;
4623 if (mDoc) {
4624 aError = mDoc->GetDocumentURI(documentURI);
4625 if (NS_WARN_IF(aError.Failed())) {
4626 return nullptr;
4630 // If the document has the sandboxed origin flag set
4631 // don't allow access to sessionStorage.
4632 if (!mDoc) {
4633 aError.Throw(NS_ERROR_FAILURE);
4634 return nullptr;
4637 if (mDoc->GetSandboxFlags() & SANDBOXED_ORIGIN) {
4638 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
4639 return nullptr;
4642 uint32_t rejectedReason = 0;
4643 StorageAccess access = StorageAllowedForWindow(this, &rejectedReason);
4645 // SessionStorage is an ephemeral per-tab per-origin storage that only lives
4646 // as long as the tab is open, although it may survive browser restarts
4647 // thanks to the session store. So we interpret storage access differently
4648 // than we would for persistent per-origin storage like LocalStorage and so
4649 // it may be okay to provide SessionStorage even when we receive a value of
4650 // eDeny.
4652 // ContentBlocking::ShouldAllowAccessFor will return false for 3 main
4653 // reasons.
4655 // 1. Cookies are entirely blocked due to a per-origin permission
4656 // (nsICookiePermission::ACCESS_DENY for the top-level principal or this
4657 // window's principal) or the very broad BEHAVIOR_REJECT. This will return
4658 // eDeny with a reason of STATE_COOKIES_BLOCKED_BY_PERMISSION or
4659 // STATE_COOKIES_BLOCKED_ALL.
4661 // 2. Third-party cookies are limited via BEHAVIOR_REJECT_FOREIGN and
4662 // BEHAVIOR_LIMIT_FOREIGN and this is a third-party window. This will return
4663 // eDeny with a reason of STATE_COOKIES_BLOCKED_FOREIGN.
4665 // 3. Tracking protection (BEHAVIOR_REJECT_TRACKER and
4666 // BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN) is in effect and
4667 // IsThirdPartyTrackingResourceWindow() returned true and there wasn't a
4668 // permission that allows it. This will return ePartitionTrackersOrDeny with
4669 // a reason of STATE_COOKIES_BLOCKED_TRACKER or
4670 // STATE_COOKIES_BLOCKED_SOCIALTRACKER.
4672 // In the 1st case, the user has explicitly indicated that they don't want
4673 // to allow any storage to the origin or all origins and so we throw an
4674 // error and deny access to SessionStorage. In the 2nd case, a legacy
4675 // decision reasoned that there's no harm in providing SessionStorage
4676 // because the information is not durable and cannot escape the current tab.
4677 // The rationale is similar for the 3rd case.
4678 if (access == StorageAccess::eDeny &&
4679 rejectedReason !=
4680 nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN) {
4681 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
4682 return nullptr;
4685 const RefPtr<SessionStorageManager> storageManager =
4686 browsingContext->GetSessionStorageManager();
4687 if (!storageManager) {
4688 aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
4689 return nullptr;
4692 RefPtr<Storage> storage;
4693 aError = storageManager->CreateStorage(this, principal, storagePrincipal,
4694 documentURI, IsPrivateBrowsing(),
4695 getter_AddRefs(storage));
4696 if (aError.Failed()) {
4697 return nullptr;
4700 mSessionStorage = storage;
4701 MOZ_ASSERT(mSessionStorage);
4703 MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
4704 ("nsGlobalWindowInner %p tried to get a new sessionStorage %p",
4705 this, mSessionStorage.get()));
4707 if (!mSessionStorage) {
4708 aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
4709 return nullptr;
4713 MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
4714 ("nsGlobalWindowInner %p returns %p sessionStorage", this,
4715 mSessionStorage.get()));
4717 return mSessionStorage;
4720 Storage* nsGlobalWindowInner::GetLocalStorage(ErrorResult& aError) {
4721 if (!Storage::StoragePrefIsEnabled()) {
4722 return nullptr;
4725 // LocalStorage needs to be exposed in every context except for sandboxes and
4726 // NullPrincipals (data: URLs, for instance). But we need to keep data
4727 // separate in some scenarios: private-browsing and partitioned trackers.
4728 // In private-browsing, LocalStorage keeps data in memory, and it shares
4729 // StorageEvents just with other origins in the same private-browsing
4730 // environment.
4731 // For Partitioned Trackers, we expose a partitioned LocalStorage, which
4732 // doesn't share data with other contexts, and it's just in memory.
4733 // Partitioned localStorage is available only for trackers listed in the
4734 // privacy.restrict3rdpartystorage.partitionedHosts pref. See
4735 // nsContentUtils::IsURIInPrefList to know the syntax for the pref value.
4736 // This is a temporary web-compatibility hack.
4738 StorageAccess access = StorageAllowedForWindow(this);
4740 // We allow partitioned localStorage only to some hosts.
4741 bool isolated = false;
4742 if (ShouldPartitionStorage(access)) {
4743 if (!mDoc) {
4744 access = StorageAccess::eDeny;
4745 } else if (!StoragePartitioningEnabled(access, mDoc->CookieJarSettings())) {
4746 static const char* kPrefName =
4747 "privacy.restrict3rdpartystorage.partitionedHosts";
4749 bool isInList = false;
4750 mDoc->NodePrincipal()->IsURIInPrefList(kPrefName, &isInList);
4751 if (!isInList) {
4752 access = StorageAccess::eDeny;
4753 } else {
4754 isolated = true;
4759 if (access == StorageAccess::eDeny) {
4760 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
4761 return nullptr;
4764 nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
4765 if (mDoc) {
4766 cookieJarSettings = mDoc->CookieJarSettings();
4767 } else {
4768 cookieJarSettings = net::CookieJarSettings::GetBlockingAll();
4771 // Note that this behavior is observable: if we grant storage permission to a
4772 // tracker, we pass from the partitioned LocalStorage (or a partitioned cookie
4773 // jar) to the 'normal' one. The previous data is lost and the 2
4774 // window.localStorage objects, before and after the permission granted, will
4775 // be different.
4776 if (mLocalStorage) {
4777 if ((mLocalStorage->Type() == (isolated ? Storage::ePartitionedLocalStorage
4778 : Storage::eLocalStorage)) &&
4779 (mLocalStorage->StoragePrincipal() == GetEffectiveStoragePrincipal())) {
4780 return mLocalStorage;
4783 // storage needs change
4784 mLocalStorage = nullptr;
4787 MOZ_ASSERT(!mLocalStorage);
4789 if (!isolated) {
4790 RefPtr<Storage> storage;
4792 if (NextGenLocalStorageEnabled()) {
4793 aError = LSObject::CreateForWindow(this, getter_AddRefs(storage));
4794 } else {
4795 nsresult rv;
4796 nsCOMPtr<nsIDOMStorageManager> storageManager =
4797 do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv);
4798 if (NS_FAILED(rv)) {
4799 aError.Throw(rv);
4800 return nullptr;
4803 nsString documentURI;
4804 if (mDoc) {
4805 aError = mDoc->GetDocumentURI(documentURI);
4806 if (NS_WARN_IF(aError.Failed())) {
4807 return nullptr;
4811 nsIPrincipal* principal = GetPrincipal();
4812 if (!principal) {
4813 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
4814 return nullptr;
4817 nsIPrincipal* storagePrincipal = GetEffectiveStoragePrincipal();
4818 if (!storagePrincipal) {
4819 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
4820 return nullptr;
4823 aError = storageManager->CreateStorage(this, principal, storagePrincipal,
4824 documentURI, IsPrivateBrowsing(),
4825 getter_AddRefs(storage));
4828 if (aError.Failed()) {
4829 return nullptr;
4832 mLocalStorage = storage;
4833 } else {
4834 nsresult rv;
4835 nsCOMPtr<nsIDOMSessionStorageManager> storageManager =
4836 do_GetService("@mozilla.org/dom/sessionStorage-manager;1", &rv);
4837 if (NS_FAILED(rv)) {
4838 aError.Throw(rv);
4839 return nullptr;
4842 nsIPrincipal* principal = GetPrincipal();
4843 if (!principal) {
4844 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
4845 return nullptr;
4848 nsIPrincipal* storagePrincipal = GetEffectiveStoragePrincipal();
4849 if (!storagePrincipal) {
4850 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
4851 return nullptr;
4854 RefPtr<SessionStorageCache> cache;
4855 if (isolated) {
4856 cache = new SessionStorageCache();
4857 } else {
4858 // This will clone the session storage if it exists.
4859 rv = storageManager->GetSessionStorageCache(principal, storagePrincipal,
4860 &cache);
4861 if (NS_FAILED(rv)) {
4862 aError.Throw(rv);
4863 return nullptr;
4867 mLocalStorage =
4868 new PartitionedLocalStorage(this, principal, storagePrincipal, cache);
4871 MOZ_ASSERT(mLocalStorage);
4872 MOZ_ASSERT(
4873 mLocalStorage->Type() ==
4874 (isolated ? Storage::ePartitionedLocalStorage : Storage::eLocalStorage));
4875 return mLocalStorage;
4878 IDBFactory* nsGlobalWindowInner::GetIndexedDB(ErrorResult& aError) {
4879 if (!mIndexedDB) {
4880 // This may keep mIndexedDB null without setting an error.
4881 auto res = IDBFactory::CreateForWindow(this);
4882 if (res.isErr()) {
4883 aError = res.unwrapErr();
4884 } else {
4885 mIndexedDB = res.unwrap();
4889 return mIndexedDB;
4892 //*****************************************************************************
4893 // nsGlobalWindowInner::nsIInterfaceRequestor
4894 //*****************************************************************************
4896 NS_IMETHODIMP
4897 nsGlobalWindowInner::GetInterface(const nsIID& aIID, void** aSink) {
4898 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
4899 NS_ENSURE_TRUE(outer, NS_ERROR_NOT_INITIALIZED);
4901 nsresult rv = outer->GetInterfaceInternal(aIID, aSink);
4902 if (rv == NS_ERROR_NO_INTERFACE) {
4903 return QueryInterface(aIID, aSink);
4905 return rv;
4908 void nsGlobalWindowInner::GetInterface(JSContext* aCx,
4909 JS::Handle<JS::Value> aIID,
4910 JS::MutableHandle<JS::Value> aRetval,
4911 ErrorResult& aError) {
4912 dom::GetInterface(aCx, this, aIID, aRetval, aError);
4915 already_AddRefed<CacheStorage> nsGlobalWindowInner::GetCaches(
4916 ErrorResult& aRv) {
4917 if (!mCacheStorage) {
4918 bool forceTrustedOrigin =
4919 GetOuterWindow()->GetServiceWorkersTestingEnabled();
4920 mCacheStorage = CacheStorage::CreateOnMainThread(
4921 cache::DEFAULT_NAMESPACE, this, GetEffectiveStoragePrincipal(),
4922 forceTrustedOrigin, aRv);
4925 RefPtr<CacheStorage> ref = mCacheStorage;
4926 return ref.forget();
4929 void nsGlobalWindowInner::FireOfflineStatusEventIfChanged() {
4930 if (!IsCurrentInnerWindow()) return;
4932 // Don't fire an event if the status hasn't changed
4933 if (mWasOffline == NS_IsOffline()) {
4934 return;
4937 mWasOffline = !mWasOffline;
4939 nsAutoString name;
4940 if (mWasOffline) {
4941 name.AssignLiteral("offline");
4942 } else {
4943 name.AssignLiteral("online");
4945 nsContentUtils::DispatchTrustedEvent(mDoc, static_cast<EventTarget*>(this),
4946 name, CanBubble::eNo, Cancelable::eNo);
4949 nsGlobalWindowInner::SlowScriptResponse
4950 nsGlobalWindowInner::ShowSlowScriptDialog(JSContext* aCx,
4951 const nsString& aAddonId,
4952 const double aDuration) {
4953 nsresult rv;
4955 if (Preferences::GetBool("dom.always_stop_slow_scripts")) {
4956 return KillSlowScript;
4959 // If it isn't safe to run script, then it isn't safe to bring up the prompt
4960 // (since that spins the event loop). In that (rare) case, we just kill the
4961 // script and report a warning.
4962 if (!nsContentUtils::IsSafeToRunScript()) {
4963 JS::WarnASCII(aCx, "A long running script was terminated");
4964 return KillSlowScript;
4967 // If our document is not active, just kill the script: we've been unloaded
4968 if (!HasActiveDocument()) {
4969 return KillSlowScript;
4972 // Check if we should offer the option to debug
4973 JS::AutoFilename filename;
4974 unsigned lineno;
4975 // Computing the line number can be very expensive (see bug 1330231 for
4976 // example), and we don't use the line number anywhere except than in the
4977 // parent process, so we avoid computing it elsewhere. This gives us most of
4978 // the wins we are interested in, since the source of the slowness here is
4979 // minified scripts which is more common in Web content that is loaded in the
4980 // content process.
4981 unsigned* linenop = XRE_IsParentProcess() ? &lineno : nullptr;
4982 bool hasFrame = JS::DescribeScriptedCaller(aCx, &filename, linenop);
4984 // Record the slow script event if we haven't done so already for this inner
4985 // window (which represents a particular page to the user).
4986 if (!mHasHadSlowScript) {
4987 Telemetry::Accumulate(Telemetry::SLOW_SCRIPT_PAGE_COUNT, 1);
4989 mHasHadSlowScript = true;
4991 // Override the cursor to something that we're sure the user can see.
4992 SetCursor("auto"_ns, IgnoreErrors());
4994 if (XRE_IsContentProcess() && ProcessHangMonitor::Get()) {
4995 ProcessHangMonitor::SlowScriptAction action;
4996 RefPtr<ProcessHangMonitor> monitor = ProcessHangMonitor::Get();
4997 nsIDocShell* docShell = GetDocShell();
4998 nsCOMPtr<nsIBrowserChild> child =
4999 docShell ? docShell->GetBrowserChild() : nullptr;
5000 action =
5001 monitor->NotifySlowScript(child, filename.get(), aAddonId, aDuration);
5002 if (action == ProcessHangMonitor::Terminate) {
5003 return KillSlowScript;
5005 if (action == ProcessHangMonitor::TerminateGlobal) {
5006 return KillScriptGlobal;
5009 if (action == ProcessHangMonitor::StartDebugger) {
5010 // Spin a nested event loop so that the debugger in the parent can fetch
5011 // any information it needs. Once the debugger has started, return to the
5012 // script.
5013 RefPtr<nsGlobalWindowOuter> outer = GetOuterWindowInternal();
5014 outer->EnterModalState();
5015 SpinEventLoopUntil(
5016 [&]() { return monitor->IsDebuggerStartupComplete(); });
5017 outer->LeaveModalState();
5018 return ContinueSlowScript;
5021 return ContinueSlowScriptAndKeepNotifying;
5024 // Reached only on non-e10s - once per slow script dialog.
5025 // On e10s - we probe once at ProcessHangsMonitor.jsm
5026 Telemetry::Accumulate(Telemetry::SLOW_SCRIPT_NOTICE_COUNT, 1);
5028 // Get the nsIPrompt interface from the docshell
5029 nsCOMPtr<nsIDocShell> ds = GetDocShell();
5030 NS_ENSURE_TRUE(ds, KillSlowScript);
5031 nsCOMPtr<nsIPrompt> prompt = do_GetInterface(ds);
5032 NS_ENSURE_TRUE(prompt, KillSlowScript);
5034 // Prioritize the SlowScriptDebug interface over JSD1.
5035 nsCOMPtr<nsISlowScriptDebugCallback> debugCallback;
5037 if (hasFrame) {
5038 const char* debugCID = "@mozilla.org/dom/slow-script-debug;1";
5039 nsCOMPtr<nsISlowScriptDebug> debugService = do_GetService(debugCID, &rv);
5040 if (NS_SUCCEEDED(rv)) {
5041 debugService->GetActivationHandler(getter_AddRefs(debugCallback));
5045 bool failed = false;
5046 auto getString = [&](const char* name,
5047 nsContentUtils::PropertiesFile propFile =
5048 nsContentUtils::eDOM_PROPERTIES) {
5049 nsAutoString result;
5050 nsresult rv = nsContentUtils::GetLocalizedString(propFile, name, result);
5052 // GetStringFromName can return NS_OK and still give nullptr string
5053 failed = failed || NS_FAILED(rv) || result.IsEmpty();
5054 return result;
5057 bool isAddonScript = !aAddonId.IsEmpty();
5058 bool showDebugButton = debugCallback && !isAddonScript;
5060 // Get localizable strings
5062 nsAutoString title, checkboxMsg, debugButton, msg;
5063 if (isAddonScript) {
5064 title = getString("KillAddonScriptTitle");
5065 checkboxMsg = getString("KillAddonScriptGlobalMessage");
5067 auto appName =
5068 getString("brandShortName", nsContentUtils::eBRAND_PROPERTIES);
5070 nsCOMPtr<nsIAddonPolicyService> aps =
5071 do_GetService("@mozilla.org/addons/policy-service;1");
5072 nsString addonName;
5073 if (!aps || NS_FAILED(aps->GetExtensionName(aAddonId, addonName))) {
5074 addonName = aAddonId;
5077 rv = nsContentUtils::FormatLocalizedString(
5078 msg, nsContentUtils::eDOM_PROPERTIES, "KillAddonScriptMessage",
5079 addonName, appName);
5081 failed = failed || NS_FAILED(rv);
5082 } else {
5083 title = getString("KillScriptTitle");
5084 checkboxMsg = getString("DontAskAgain");
5086 if (showDebugButton) {
5087 debugButton = getString("DebugScriptButton");
5088 msg = getString("KillScriptWithDebugMessage");
5089 } else {
5090 msg = getString("KillScriptMessage");
5094 auto stopButton = getString("StopScriptButton");
5095 auto waitButton = getString("WaitForScriptButton");
5097 if (failed) {
5098 NS_ERROR("Failed to get localized strings.");
5099 return ContinueSlowScript;
5102 // Append file and line number information, if available
5103 if (filename.get()) {
5104 nsAutoString scriptLocation;
5105 // We want to drop the middle part of too-long locations. We'll
5106 // define "too-long" as longer than 60 UTF-16 code units. Just
5107 // have to be a bit careful about unpaired surrogates.
5108 NS_ConvertUTF8toUTF16 filenameUTF16(filename.get());
5109 if (filenameUTF16.Length() > 60) {
5110 // XXXbz Do we need to insert any bidi overrides here?
5111 size_t cutStart = 30;
5112 size_t cutLength = filenameUTF16.Length() - 60;
5113 MOZ_ASSERT(cutLength > 0);
5114 if (NS_IS_LOW_SURROGATE(filenameUTF16[cutStart])) {
5115 // Don't truncate before the low surrogate, in case it's preceded by a
5116 // high surrogate and forms a single Unicode character. Instead, just
5117 // include the low surrogate.
5118 ++cutStart;
5119 --cutLength;
5121 if (NS_IS_LOW_SURROGATE(filenameUTF16[cutStart + cutLength])) {
5122 // Likewise, don't drop a trailing low surrogate here. We want to
5123 // increase cutLength, since it might be 0 already so we can't very well
5124 // decrease it.
5125 ++cutLength;
5128 // Insert U+2026 HORIZONTAL ELLIPSIS
5129 filenameUTF16.ReplaceLiteral(cutStart, cutLength, u"\x2026");
5131 rv = nsContentUtils::FormatLocalizedString(
5132 scriptLocation, nsContentUtils::eDOM_PROPERTIES, "KillScriptLocation",
5133 filenameUTF16);
5135 if (NS_SUCCEEDED(rv)) {
5136 msg.AppendLiteral("\n\n");
5137 msg.Append(scriptLocation);
5138 msg.Append(':');
5139 msg.AppendInt(lineno);
5143 uint32_t buttonFlags = nsIPrompt::BUTTON_POS_1_DEFAULT +
5144 (nsIPrompt::BUTTON_TITLE_IS_STRING *
5145 (nsIPrompt::BUTTON_POS_0 + nsIPrompt::BUTTON_POS_1));
5147 // Add a third button if necessary.
5148 if (showDebugButton)
5149 buttonFlags += nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_2;
5151 bool checkboxValue = false;
5152 int32_t buttonPressed = 0; // In case the user exits dialog by clicking X.
5154 // Null out the operation callback while we're re-entering JS here.
5155 AutoDisableJSInterruptCallback disabler(aCx);
5157 // Open the dialog.
5158 rv = prompt->ConfirmEx(
5159 title.get(), msg.get(), buttonFlags, waitButton.get(), stopButton.get(),
5160 debugButton.get(), checkboxMsg.get(), &checkboxValue, &buttonPressed);
5163 if (buttonPressed == 0) {
5164 if (checkboxValue && !isAddonScript && NS_SUCCEEDED(rv))
5165 return AlwaysContinueSlowScript;
5166 return ContinueSlowScript;
5169 if (buttonPressed == 2) {
5170 MOZ_RELEASE_ASSERT(debugCallback);
5172 rv = debugCallback->HandleSlowScriptDebug(this);
5173 return NS_SUCCEEDED(rv) ? ContinueSlowScript : KillSlowScript;
5176 JS_ClearPendingException(aCx);
5178 if (checkboxValue && isAddonScript) return KillScriptGlobal;
5179 return KillSlowScript;
5182 nsresult nsGlobalWindowInner::Observe(nsISupports* aSubject, const char* aTopic,
5183 const char16_t* aData) {
5184 if (!nsCRT::strcmp(aTopic, NS_IOSERVICE_OFFLINE_STATUS_TOPIC)) {
5185 if (!IsFrozen()) {
5186 // Fires an offline status event if the offline status has changed
5187 FireOfflineStatusEventIfChanged();
5189 return NS_OK;
5192 if (!nsCRT::strcmp(aTopic, MEMORY_PRESSURE_OBSERVER_TOPIC)) {
5193 if (mPerformance) {
5194 mPerformance->MemoryPressure();
5196 RemoveReportRecords();
5197 return NS_OK;
5200 if (!nsCRT::strcmp(aTopic, "offline-cache-update-added")) {
5201 if (mApplicationCache) return NS_OK;
5203 // Instantiate the application object now. It observes update belonging to
5204 // this window's document and correctly updates the applicationCache object
5205 // state.
5206 nsCOMPtr<nsIObserver> observer = GetApplicationCache();
5207 if (observer) observer->Observe(aSubject, aTopic, aData);
5209 return NS_OK;
5212 if (!nsCRT::strcmp(aTopic, PERMISSION_CHANGED_TOPIC)) {
5213 nsCOMPtr<nsIPermission> perm(do_QueryInterface(aSubject));
5214 if (!perm) {
5215 // A null permission indicates that the entire permission list
5216 // was cleared.
5217 MOZ_ASSERT(!nsCRT::strcmp(aData, u"cleared"));
5218 UpdatePermissions();
5219 return NS_OK;
5222 nsAutoCString type;
5223 perm->GetType(type);
5224 if (type == "autoplay-media"_ns) {
5225 UpdateAutoplayPermission();
5226 } else if (type == "shortcuts"_ns) {
5227 UpdateShortcutsPermission();
5228 } else if (type == "popup"_ns) {
5229 UpdatePopupPermission();
5232 if (!mDoc) {
5233 return NS_OK;
5236 RefPtr<PermissionDelegateHandler> permDelegateHandler =
5237 mDoc->GetPermissionDelegateHandler();
5239 if (permDelegateHandler) {
5240 permDelegateHandler->UpdateDelegatedPermission(type);
5243 return NS_OK;
5246 if (!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
5247 MOZ_ASSERT(!NS_strcmp(aData, u"intl.accept_languages"));
5249 // The user preferred languages have changed, we need to fire an event on
5250 // Window object and invalidate the cache for navigator.languages. It is
5251 // done for every change which can be a waste of cycles but those should be
5252 // fairly rare.
5253 // We MUST invalidate navigator.languages before sending the event in the
5254 // very likely situation where an event handler will try to read its value.
5256 if (mNavigator) {
5257 Navigator_Binding::ClearCachedLanguageValue(mNavigator);
5258 Navigator_Binding::ClearCachedLanguagesValue(mNavigator);
5261 // The event has to be dispatched only to the current inner window.
5262 if (!IsCurrentInnerWindow()) {
5263 return NS_OK;
5266 RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
5267 event->InitEvent(u"languagechange"_ns, false, false);
5268 event->SetTrusted(true);
5270 ErrorResult rv;
5271 DispatchEvent(*event, rv);
5272 return rv.StealNSResult();
5275 NS_WARNING("unrecognized topic in nsGlobalWindowInner::Observe");
5276 return NS_ERROR_FAILURE;
5279 void nsGlobalWindowInner::ObserveStorageNotification(
5280 StorageEvent* aEvent, const char16_t* aStorageType, bool aPrivateBrowsing) {
5281 MOZ_ASSERT(aEvent);
5283 // The private browsing check must be done here again because this window
5284 // could have changed its state before the notification check and now. This
5285 // happens in case this window did have a docShell at that time.
5286 if (aPrivateBrowsing != IsPrivateBrowsing()) {
5287 return;
5290 // LocalStorage can only exist on an inner window, and we don't want to
5291 // generate events on frozen or otherwise-navigated-away from windows.
5292 // (Actually, this code used to try and buffer events for frozen windows,
5293 // but it never worked, so we've removed it. See bug 1285898.)
5294 if (!IsCurrentInnerWindow() || IsFrozen()) {
5295 return;
5298 nsIPrincipal* principal = GetPrincipal();
5299 if (!principal) {
5300 return;
5303 bool fireMozStorageChanged = false;
5304 nsAutoString eventType;
5305 eventType.AssignLiteral("storage");
5307 if (!NS_strcmp(aStorageType, u"sessionStorage")) {
5308 RefPtr<Storage> changingStorage = aEvent->GetStorageArea();
5309 MOZ_ASSERT(changingStorage);
5311 bool check = false;
5313 if (const RefPtr<SessionStorageManager> storageManager =
5314 GetBrowsingContext()->GetSessionStorageManager()) {
5315 nsresult rv = storageManager->CheckStorage(GetEffectiveStoragePrincipal(),
5316 changingStorage, &check);
5317 if (NS_FAILED(rv)) {
5318 return;
5322 if (!check) {
5323 // This storage event is not coming from our storage or is coming
5324 // from a different docshell, i.e. it is a clone, ignore this event.
5325 return;
5328 MOZ_LOG(
5329 gDOMLeakPRLogInner, LogLevel::Debug,
5330 ("nsGlobalWindowInner %p with sessionStorage %p passing event from %p",
5331 this, mSessionStorage.get(), changingStorage.get()));
5333 fireMozStorageChanged = mSessionStorage == changingStorage;
5334 if (fireMozStorageChanged) {
5335 eventType.AssignLiteral("MozSessionStorageChanged");
5339 else {
5340 MOZ_ASSERT(!NS_strcmp(aStorageType, u"localStorage"));
5342 nsIPrincipal* storagePrincipal = GetEffectiveStoragePrincipal();
5343 if (!storagePrincipal) {
5344 return;
5347 MOZ_DIAGNOSTIC_ASSERT(StorageUtils::PrincipalsEqual(aEvent->GetPrincipal(),
5348 storagePrincipal));
5350 fireMozStorageChanged =
5351 mLocalStorage && mLocalStorage == aEvent->GetStorageArea();
5353 if (fireMozStorageChanged) {
5354 eventType.AssignLiteral("MozLocalStorageChanged");
5358 // Clone the storage event included in the observer notification. We want
5359 // to dispatch clones rather than the original event.
5360 IgnoredErrorResult error;
5361 RefPtr<StorageEvent> clonedEvent =
5362 CloneStorageEvent(eventType, aEvent, error);
5363 if (error.Failed() || !clonedEvent) {
5364 return;
5367 clonedEvent->SetTrusted(true);
5369 if (fireMozStorageChanged) {
5370 WidgetEvent* internalEvent = clonedEvent->WidgetEventPtr();
5371 internalEvent->mFlags.mOnlyChromeDispatch = true;
5374 DispatchEvent(*clonedEvent);
5377 already_AddRefed<StorageEvent> nsGlobalWindowInner::CloneStorageEvent(
5378 const nsAString& aType, const RefPtr<StorageEvent>& aEvent,
5379 ErrorResult& aRv) {
5380 StorageEventInit dict;
5382 dict.mBubbles = aEvent->Bubbles();
5383 dict.mCancelable = aEvent->Cancelable();
5384 aEvent->GetKey(dict.mKey);
5385 aEvent->GetOldValue(dict.mOldValue);
5386 aEvent->GetNewValue(dict.mNewValue);
5387 aEvent->GetUrl(dict.mUrl);
5389 RefPtr<Storage> storageArea = aEvent->GetStorageArea();
5391 RefPtr<Storage> storage;
5393 // If null, this is a localStorage event received by IPC.
5394 if (!storageArea) {
5395 storage = GetLocalStorage(aRv);
5396 if (!NextGenLocalStorageEnabled()) {
5397 if (aRv.Failed() || !storage) {
5398 return nullptr;
5401 if (storage->Type() == Storage::eLocalStorage) {
5402 RefPtr<LocalStorage> localStorage =
5403 static_cast<LocalStorage*>(storage.get());
5405 // We must apply the current change to the 'local' localStorage.
5406 localStorage->ApplyEvent(aEvent);
5409 } else if (storageArea->Type() == Storage::eSessionStorage) {
5410 storage = GetSessionStorage(aRv);
5411 } else {
5412 MOZ_ASSERT(storageArea->Type() == Storage::eLocalStorage);
5413 storage = GetLocalStorage(aRv);
5416 if (aRv.Failed() || !storage) {
5417 return nullptr;
5420 if (storage->Type() == Storage::ePartitionedLocalStorage) {
5421 // This error message is not exposed.
5422 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
5423 return nullptr;
5426 MOZ_ASSERT(storage);
5427 MOZ_ASSERT_IF(storageArea, storage->IsForkOf(storageArea));
5429 dict.mStorageArea = storage;
5431 RefPtr<StorageEvent> event = StorageEvent::Constructor(this, aType, dict);
5432 return event.forget();
5435 void nsGlobalWindowInner::Suspend() {
5436 MOZ_ASSERT(NS_IsMainThread());
5438 // We can only safely suspend windows that are the current inner window. If
5439 // its not the current inner, then we are in one of two different cases.
5440 // Either we are in the bfcache or we are doomed window that is going away.
5441 // When a window becomes inactive we purposely avoid placing already suspended
5442 // windows into the bfcache. It only expects windows suspended due to the
5443 // Freeze() method which occurs while the window is still the current inner.
5444 // So we must not call Suspend() on bfcache windows at this point or this
5445 // invariant will be broken. If the window is doomed there is no point in
5446 // suspending it since it will soon be gone.
5447 if (!IsCurrentInnerWindow()) {
5448 return;
5451 // All children are also suspended. This ensure mSuspendDepth is
5452 // set properly and the timers are properly canceled for each child.
5453 CallOnInProcessChildren(&nsGlobalWindowInner::Suspend);
5455 mSuspendDepth += 1;
5456 if (mSuspendDepth != 1) {
5457 return;
5460 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
5461 if (ac) {
5462 for (uint32_t i = 0; i < mEnabledSensors.Length(); i++)
5463 ac->RemoveWindowListener(mEnabledSensors[i], this);
5465 DisableGamepadUpdates();
5466 DisableVRUpdates();
5468 SuspendWorkersForWindow(*this);
5470 for (RefPtr<mozilla::dom::SharedWorker> pinnedWorker :
5471 mSharedWorkers.ForwardRange()) {
5472 pinnedWorker->Suspend();
5475 SuspendIdleRequests();
5477 mTimeoutManager->Suspend();
5479 // Suspend all of the AudioContexts for this window
5480 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
5481 mAudioContexts[i]->SuspendFromChrome();
5485 void nsGlobalWindowInner::Resume() {
5486 MOZ_ASSERT(NS_IsMainThread());
5488 // We can only safely resume a window if its the current inner window. If
5489 // its not the current inner, then we are in one of two different cases.
5490 // Either we are in the bfcache or we are doomed window that is going away.
5491 // If a window is suspended when it becomes inactive we purposely do not
5492 // put it in the bfcache, so Resume should never be needed in that case.
5493 // If the window is doomed then there is no point in resuming it.
5494 if (!IsCurrentInnerWindow()) {
5495 return;
5498 // Resume all children. This restores timers recursively canceled
5499 // in Suspend() and ensures all children have the correct mSuspendDepth.
5500 CallOnInProcessChildren(&nsGlobalWindowInner::Resume);
5502 if (mSuspendDepth == 0) {
5503 // Ignore if the window is not suspended.
5504 return;
5507 mSuspendDepth -= 1;
5509 if (mSuspendDepth != 0) {
5510 return;
5513 // We should not be able to resume a frozen window. It must be Thaw()'d
5514 // first.
5515 MOZ_ASSERT(mFreezeDepth == 0);
5517 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
5518 if (ac) {
5519 for (uint32_t i = 0; i < mEnabledSensors.Length(); i++)
5520 ac->AddWindowListener(mEnabledSensors[i], this);
5522 EnableGamepadUpdates();
5523 EnableVRUpdates();
5525 // Resume all of the AudioContexts for this window
5526 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
5527 mAudioContexts[i]->ResumeFromChrome();
5530 mTimeoutManager->Resume();
5532 ResumeIdleRequests();
5534 // Resume all of the workers for this window. We must do this
5535 // after timeouts since workers may have queued events that can trigger
5536 // a setTimeout().
5537 ResumeWorkersForWindow(*this);
5539 for (RefPtr<mozilla::dom::SharedWorker> pinnedWorker :
5540 mSharedWorkers.ForwardRange()) {
5541 pinnedWorker->Resume();
5545 bool nsGlobalWindowInner::IsSuspended() const {
5546 MOZ_ASSERT(NS_IsMainThread());
5547 return mSuspendDepth != 0;
5550 void nsGlobalWindowInner::Freeze() {
5551 MOZ_ASSERT(NS_IsMainThread());
5552 Suspend();
5553 FreezeInternal();
5556 void nsGlobalWindowInner::FreezeInternal() {
5557 MOZ_ASSERT(NS_IsMainThread());
5558 MOZ_DIAGNOSTIC_ASSERT(IsCurrentInnerWindow());
5559 MOZ_DIAGNOSTIC_ASSERT(IsSuspended());
5561 HintIsLoading(false);
5563 CallOnInProcessChildren(&nsGlobalWindowInner::FreezeInternal);
5565 mFreezeDepth += 1;
5566 MOZ_ASSERT(mSuspendDepth >= mFreezeDepth);
5567 if (mFreezeDepth != 1) {
5568 return;
5571 FreezeWorkersForWindow(*this);
5573 for (RefPtr<mozilla::dom::SharedWorker> pinnedWorker :
5574 mSharedWorkers.ForwardRange()) {
5575 pinnedWorker->Freeze();
5578 mTimeoutManager->Freeze();
5579 if (mClientSource) {
5580 mClientSource->Freeze();
5583 NotifyDOMWindowFrozen(this);
5586 void nsGlobalWindowInner::Thaw() {
5587 MOZ_ASSERT(NS_IsMainThread());
5588 ThawInternal();
5589 Resume();
5592 void nsGlobalWindowInner::ThawInternal() {
5593 MOZ_ASSERT(NS_IsMainThread());
5594 MOZ_DIAGNOSTIC_ASSERT(IsCurrentInnerWindow());
5595 MOZ_DIAGNOSTIC_ASSERT(IsSuspended());
5597 CallOnInProcessChildren(&nsGlobalWindowInner::ThawInternal);
5599 MOZ_ASSERT(mFreezeDepth != 0);
5600 mFreezeDepth -= 1;
5601 MOZ_ASSERT(mSuspendDepth >= mFreezeDepth);
5602 if (mFreezeDepth != 0) {
5603 return;
5606 if (mClientSource) {
5607 mClientSource->Thaw();
5609 mTimeoutManager->Thaw();
5611 ThawWorkersForWindow(*this);
5613 for (RefPtr<mozilla::dom::SharedWorker> pinnedWorker :
5614 mSharedWorkers.ForwardRange()) {
5615 pinnedWorker->Thaw();
5618 NotifyDOMWindowThawed(this);
5621 bool nsGlobalWindowInner::IsFrozen() const {
5622 MOZ_ASSERT(NS_IsMainThread());
5623 bool frozen = mFreezeDepth != 0;
5624 MOZ_ASSERT_IF(frozen, IsSuspended());
5625 return frozen;
5628 void nsGlobalWindowInner::SyncStateFromParentWindow() {
5629 // This method should only be called on an inner window that has been
5630 // assigned to an outer window already.
5631 MOZ_ASSERT(IsCurrentInnerWindow());
5632 nsPIDOMWindowOuter* outer = GetOuterWindow();
5633 MOZ_ASSERT(outer);
5635 // Attempt to find our parent windows.
5636 nsCOMPtr<Element> frame = outer->GetFrameElementInternal();
5637 nsPIDOMWindowOuter* parentOuter =
5638 frame ? frame->OwnerDoc()->GetWindow() : nullptr;
5639 nsGlobalWindowInner* parentInner =
5640 parentOuter
5641 ? nsGlobalWindowInner::Cast(parentOuter->GetCurrentInnerWindow())
5642 : nullptr;
5644 // If our outer is in a modal state, but our parent is not in a modal
5645 // state, then we must apply the suspend directly. If our parent is
5646 // in a modal state then we should get the suspend automatically
5647 // via the parentSuspendDepth application below.
5648 if ((!parentInner || !parentInner->IsInModalState()) && IsInModalState()) {
5649 Suspend();
5652 uint32_t parentFreezeDepth = parentInner ? parentInner->mFreezeDepth : 0;
5653 uint32_t parentSuspendDepth = parentInner ? parentInner->mSuspendDepth : 0;
5655 // Since every Freeze() calls Suspend(), the suspend count must
5656 // be equal or greater to the freeze count.
5657 MOZ_ASSERT(parentFreezeDepth <= parentSuspendDepth);
5659 // First apply the Freeze() calls.
5660 for (uint32_t i = 0; i < parentFreezeDepth; ++i) {
5661 Freeze();
5664 // Now apply only the number of Suspend() calls to reach the target
5665 // suspend count after applying the Freeze() calls.
5666 for (uint32_t i = 0; i < (parentSuspendDepth - parentFreezeDepth); ++i) {
5667 Suspend();
5671 template <typename Method, typename... Args>
5672 CallState nsGlobalWindowInner::CallOnInProcessChildren(Method aMethod,
5673 Args&... aArgs) {
5674 MOZ_ASSERT(NS_IsMainThread());
5675 MOZ_ASSERT(IsCurrentInnerWindow());
5677 CallState state = CallState::Continue;
5679 nsCOMPtr<nsIDocShell> docShell = GetDocShell();
5680 if (!docShell) {
5681 return state;
5684 for (const RefPtr<BrowsingContext>& bc : GetBrowsingContext()->Children()) {
5685 nsCOMPtr<nsPIDOMWindowOuter> pWin = bc->GetDOMWindow();
5686 if (!pWin) {
5687 continue;
5690 auto* win = nsGlobalWindowOuter::Cast(pWin);
5691 nsGlobalWindowInner* inner = win->GetCurrentInnerWindowInternal();
5693 // This is a bit hackish. Only freeze/suspend windows which are truly our
5694 // subwindows.
5695 nsCOMPtr<Element> frame = pWin->GetFrameElementInternal();
5696 if (!mDoc || !frame || mDoc != frame->OwnerDoc() || !inner) {
5697 continue;
5700 // Call the child method using our helper CallChild() template method.
5701 // This allows us to handle both void returning methods and methods
5702 // that return CallState explicitly. For void returning methods we
5703 // assume CallState::Continue.
5704 typedef decltype((inner->*aMethod)(aArgs...)) returnType;
5705 state = CallChild<returnType>(inner, aMethod, aArgs...);
5707 if (state == CallState::Stop) {
5708 return state;
5712 return state;
5715 Maybe<ClientInfo> nsGlobalWindowInner::GetClientInfo() const {
5716 MOZ_ASSERT(NS_IsMainThread());
5717 if (mDoc && mDoc->IsStaticDocument()) {
5718 if (Maybe<ClientInfo> info = mDoc->GetOriginalDocument()->GetClientInfo()) {
5719 return info;
5723 Maybe<ClientInfo> clientInfo;
5724 if (mClientSource) {
5725 clientInfo.emplace(mClientSource->Info());
5727 return clientInfo;
5730 Maybe<ClientState> nsGlobalWindowInner::GetClientState() const {
5731 MOZ_ASSERT(NS_IsMainThread());
5732 if (mDoc && mDoc->IsStaticDocument()) {
5733 if (Maybe<ClientState> state =
5734 mDoc->GetOriginalDocument()->GetClientState()) {
5735 return state;
5739 Maybe<ClientState> clientState;
5740 if (mClientSource) {
5741 Result<ClientState, ErrorResult> res = mClientSource->SnapshotState();
5742 if (res.isOk()) {
5743 clientState.emplace(res.unwrap());
5744 } else {
5745 res.unwrapErr().SuppressException();
5748 return clientState;
5751 Maybe<ServiceWorkerDescriptor> nsGlobalWindowInner::GetController() const {
5752 MOZ_ASSERT(NS_IsMainThread());
5753 if (mDoc && mDoc->IsStaticDocument()) {
5754 if (Maybe<ServiceWorkerDescriptor> controller =
5755 mDoc->GetOriginalDocument()->GetController()) {
5756 return controller;
5760 Maybe<ServiceWorkerDescriptor> controller;
5761 if (mClientSource) {
5762 controller = mClientSource->GetController();
5764 return controller;
5767 void nsGlobalWindowInner::SetCsp(nsIContentSecurityPolicy* aCsp) {
5768 if (!mClientSource) {
5769 return;
5771 mClientSource->SetCsp(aCsp);
5772 // Also cache the CSP within the document
5773 mDoc->SetCsp(aCsp);
5775 if (mWindowGlobalChild) {
5776 mWindowGlobalChild->SendSetClientInfo(mClientSource->Info().ToIPC());
5780 void nsGlobalWindowInner::SetPreloadCsp(nsIContentSecurityPolicy* aPreloadCsp) {
5781 if (!mClientSource) {
5782 return;
5784 mClientSource->SetPreloadCsp(aPreloadCsp);
5785 // Also cache the preload CSP within the document
5786 mDoc->SetPreloadCsp(aPreloadCsp);
5788 if (mWindowGlobalChild) {
5789 mWindowGlobalChild->SendSetClientInfo(mClientSource->Info().ToIPC());
5793 nsIContentSecurityPolicy* nsGlobalWindowInner::GetCsp() {
5794 if (mDoc) {
5795 return mDoc->GetCsp();
5798 // If the window is partially torn down and has its document nulled out,
5799 // we query the CSP we snapshot in FreeInnerObjects.
5800 if (mDocumentCsp) {
5801 return mDocumentCsp;
5803 return nullptr;
5806 RefPtr<ServiceWorker> nsGlobalWindowInner::GetOrCreateServiceWorker(
5807 const ServiceWorkerDescriptor& aDescriptor) {
5808 MOZ_ASSERT(NS_IsMainThread());
5809 RefPtr<ServiceWorker> ref;
5810 ForEachEventTargetObject([&](DOMEventTargetHelper* aTarget, bool* aDoneOut) {
5811 RefPtr<ServiceWorker> sw = do_QueryObject(aTarget);
5812 if (!sw || !sw->Descriptor().Matches(aDescriptor)) {
5813 return;
5816 ref = std::move(sw);
5817 *aDoneOut = true;
5820 if (!ref) {
5821 ref = ServiceWorker::Create(this, aDescriptor);
5824 return ref;
5827 RefPtr<mozilla::dom::ServiceWorkerRegistration>
5828 nsGlobalWindowInner::GetServiceWorkerRegistration(
5829 const mozilla::dom::ServiceWorkerRegistrationDescriptor& aDescriptor)
5830 const {
5831 MOZ_ASSERT(NS_IsMainThread());
5832 RefPtr<ServiceWorkerRegistration> ref;
5833 ForEachEventTargetObject([&](DOMEventTargetHelper* aTarget, bool* aDoneOut) {
5834 RefPtr<ServiceWorkerRegistration> swr = do_QueryObject(aTarget);
5835 if (!swr || !swr->MatchesDescriptor(aDescriptor)) {
5836 return;
5839 ref = std::move(swr);
5840 *aDoneOut = true;
5842 return ref;
5845 RefPtr<ServiceWorkerRegistration>
5846 nsGlobalWindowInner::GetOrCreateServiceWorkerRegistration(
5847 const ServiceWorkerRegistrationDescriptor& aDescriptor) {
5848 MOZ_ASSERT(NS_IsMainThread());
5849 RefPtr<ServiceWorkerRegistration> ref =
5850 GetServiceWorkerRegistration(aDescriptor);
5851 if (!ref) {
5852 ref = ServiceWorkerRegistration::CreateForMainThread(this, aDescriptor);
5854 return ref;
5857 nsresult nsGlobalWindowInner::FireDelayedDOMEvents() {
5858 if (mApplicationCache) {
5859 static_cast<nsDOMOfflineResourceList*>(mApplicationCache.get())
5860 ->FirePendingEvents();
5863 // Fires an offline status event if the offline status has changed
5864 FireOfflineStatusEventIfChanged();
5866 nsCOMPtr<nsIDocShell> docShell = GetDocShell();
5867 if (docShell) {
5868 int32_t childCount = 0;
5869 docShell->GetInProcessChildCount(&childCount);
5871 // Take a copy of the current children so that modifications to
5872 // the child list don't affect to the iteration.
5873 AutoTArray<nsCOMPtr<nsIDocShellTreeItem>, 8> children;
5874 for (int32_t i = 0; i < childCount; ++i) {
5875 nsCOMPtr<nsIDocShellTreeItem> childShell;
5876 docShell->GetInProcessChildAt(i, getter_AddRefs(childShell));
5877 if (childShell) {
5878 children.AppendElement(childShell);
5882 for (nsCOMPtr<nsIDocShellTreeItem> childShell : children) {
5883 if (nsCOMPtr<nsPIDOMWindowOuter> pWin = childShell->GetWindow()) {
5884 auto* win = nsGlobalWindowOuter::Cast(pWin);
5885 win->FireDelayedDOMEvents();
5890 return NS_OK;
5893 //*****************************************************************************
5894 // nsGlobalWindowInner: Window Control Functions
5895 //*****************************************************************************
5897 nsPIDOMWindowOuter* nsGlobalWindowInner::GetInProcessParentInternal() {
5898 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
5899 if (!outer) {
5900 // No outer window available!
5901 return nullptr;
5903 return outer->GetInProcessParentInternal();
5906 nsIPrincipal* nsGlobalWindowInner::GetTopLevelAntiTrackingPrincipal() {
5907 nsPIDOMWindowOuter* outerWindow = GetOuterWindowInternal();
5908 if (!outerWindow) {
5909 return nullptr;
5912 nsPIDOMWindowOuter* topLevelOuterWindow =
5913 GetBrowsingContext()->Top()->GetDOMWindow();
5914 if (!topLevelOuterWindow) {
5915 return nullptr;
5918 bool stopAtOurLevel =
5919 mDoc && mDoc->CookieJarSettings()->GetCookieBehavior() ==
5920 nsICookieService::BEHAVIOR_REJECT_TRACKER;
5922 if (stopAtOurLevel && topLevelOuterWindow == outerWindow) {
5923 return nullptr;
5926 nsPIDOMWindowInner* topLevelInnerWindow =
5927 topLevelOuterWindow->GetCurrentInnerWindow();
5928 if (NS_WARN_IF(!topLevelInnerWindow)) {
5929 return nullptr;
5932 nsIPrincipal* topLevelPrincipal =
5933 nsGlobalWindowInner::Cast(topLevelInnerWindow)->GetPrincipal();
5934 if (NS_WARN_IF(!topLevelPrincipal)) {
5935 return nullptr;
5938 return topLevelPrincipal;
5941 nsIPrincipal* nsGlobalWindowInner::GetTopLevelStorageAreaPrincipal() {
5942 if (mDoc && (mDoc->StorageAccessSandboxed())) {
5943 // Storage access is disabled
5944 return nullptr;
5947 BrowsingContext* parent = GetBrowsingContext()->GetParent();
5948 nsPIDOMWindowOuter* outerWindow = parent ? parent->GetDOMWindow() : nullptr;
5949 if (!outerWindow) {
5950 // No outer window available!
5951 return nullptr;
5954 if (!outerWindow->GetBrowsingContext()->IsTop()) {
5955 return nullptr;
5958 nsPIDOMWindowInner* innerWindow = outerWindow->GetCurrentInnerWindow();
5959 if (NS_WARN_IF(!innerWindow)) {
5960 return nullptr;
5963 nsIPrincipal* parentPrincipal =
5964 nsGlobalWindowInner::Cast(innerWindow)->GetPrincipal();
5965 if (NS_WARN_IF(!parentPrincipal)) {
5966 return nullptr;
5969 return parentPrincipal;
5972 //*****************************************************************************
5973 // nsGlobalWindowInner: Timeout Functions
5974 //*****************************************************************************
5976 class WindowScriptTimeoutHandler final : public ScriptTimeoutHandler {
5977 public:
5978 NS_DECL_ISUPPORTS_INHERITED
5979 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(WindowScriptTimeoutHandler,
5980 ScriptTimeoutHandler)
5982 WindowScriptTimeoutHandler(JSContext* aCx, nsIGlobalObject* aGlobal,
5983 const nsAString& aExpression)
5984 : ScriptTimeoutHandler(aCx, aGlobal, aExpression),
5985 mInitiatingScript(ScriptLoader::GetActiveScript(aCx)) {}
5987 MOZ_CAN_RUN_SCRIPT virtual bool Call(const char* aExecutionReason) override;
5989 private:
5990 virtual ~WindowScriptTimeoutHandler() = default;
5992 // Initiating script for use when evaluating mExpr on the main thread.
5993 RefPtr<LoadedScript> mInitiatingScript;
5996 NS_IMPL_CYCLE_COLLECTION_INHERITED(WindowScriptTimeoutHandler,
5997 ScriptTimeoutHandler, mInitiatingScript)
5999 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WindowScriptTimeoutHandler)
6000 NS_INTERFACE_MAP_END_INHERITING(ScriptTimeoutHandler)
6002 NS_IMPL_ADDREF_INHERITED(WindowScriptTimeoutHandler, ScriptTimeoutHandler)
6003 NS_IMPL_RELEASE_INHERITED(WindowScriptTimeoutHandler, ScriptTimeoutHandler)
6005 bool WindowScriptTimeoutHandler::Call(const char* aExecutionReason) {
6006 // New script entry point required, due to the "Create a script" sub-step
6007 // of
6008 // http://www.whatwg.org/specs/web-apps/current-work/#timer-initialisation-steps
6009 nsAutoMicroTask mt;
6010 AutoEntryScript aes(mGlobal, aExecutionReason, true);
6011 JS::CompileOptions options(aes.cx());
6012 options.setFileAndLine(mFileName.get(), mLineNo);
6013 options.setNoScriptRval(true);
6014 options.setIntroductionType("domTimer");
6015 JS::Rooted<JSObject*> global(aes.cx(), mGlobal->GetGlobalJSObject());
6017 JSExecutionContext exec(aes.cx(), global);
6018 nsresult rv = exec.Compile(options, mExpr);
6020 JS::Rooted<JSScript*> script(aes.cx(), exec.MaybeGetScript());
6021 if (script) {
6022 if (mInitiatingScript) {
6023 mInitiatingScript->AssociateWithScript(script);
6026 rv = exec.ExecScript();
6029 if (rv == NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW_UNCATCHABLE) {
6030 return false;
6034 return true;
6037 nsGlobalWindowInner* nsGlobalWindowInner::InnerForSetTimeoutOrInterval(
6038 ErrorResult& aError) {
6039 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
6040 nsGlobalWindowInner* currentInner =
6041 outer ? outer->GetCurrentInnerWindowInternal() : this;
6043 // If forwardTo is not the window with an active document then we want the
6044 // call to setTimeout/Interval to be a noop, so return null but don't set an
6045 // error.
6046 return HasActiveDocument() ? currentInner : nullptr;
6049 int32_t nsGlobalWindowInner::SetTimeout(JSContext* aCx, Function& aFunction,
6050 int32_t aTimeout,
6051 const Sequence<JS::Value>& aArguments,
6052 ErrorResult& aError) {
6053 return SetTimeoutOrInterval(aCx, aFunction, aTimeout, aArguments, false,
6054 aError);
6057 int32_t nsGlobalWindowInner::SetTimeout(JSContext* aCx,
6058 const nsAString& aHandler,
6059 int32_t aTimeout,
6060 const Sequence<JS::Value>& /* unused */,
6061 ErrorResult& aError) {
6062 return SetTimeoutOrInterval(aCx, aHandler, aTimeout, false, aError);
6065 int32_t nsGlobalWindowInner::SetInterval(JSContext* aCx, Function& aFunction,
6066 const int32_t aTimeout,
6067 const Sequence<JS::Value>& aArguments,
6068 ErrorResult& aError) {
6069 return SetTimeoutOrInterval(aCx, aFunction, aTimeout, aArguments, true,
6070 aError);
6073 int32_t nsGlobalWindowInner::SetInterval(
6074 JSContext* aCx, const nsAString& aHandler, const int32_t aTimeout,
6075 const Sequence<JS::Value>& /* unused */, ErrorResult& aError) {
6076 return SetTimeoutOrInterval(aCx, aHandler, aTimeout, true, aError);
6079 int32_t nsGlobalWindowInner::SetTimeoutOrInterval(
6080 JSContext* aCx, Function& aFunction, int32_t aTimeout,
6081 const Sequence<JS::Value>& aArguments, bool aIsInterval,
6082 ErrorResult& aError) {
6083 nsGlobalWindowInner* inner = InnerForSetTimeoutOrInterval(aError);
6084 if (!inner) {
6085 return -1;
6088 if (inner != this) {
6089 RefPtr<nsGlobalWindowInner> innerRef(inner);
6090 return innerRef->SetTimeoutOrInterval(aCx, aFunction, aTimeout, aArguments,
6091 aIsInterval, aError);
6094 DebuggerNotificationDispatch(
6095 this, aIsInterval ? DebuggerNotificationType::SetInterval
6096 : DebuggerNotificationType::SetTimeout);
6098 if (!GetContextInternal() || !HasJSGlobal()) {
6099 // This window was already closed, or never properly initialized,
6100 // don't let a timer be scheduled on such a window.
6101 aError.Throw(NS_ERROR_NOT_INITIALIZED);
6102 return 0;
6105 nsTArray<JS::Heap<JS::Value>> args;
6106 if (!args.AppendElements(aArguments, fallible)) {
6107 aError.Throw(NS_ERROR_OUT_OF_MEMORY);
6108 return 0;
6111 RefPtr<TimeoutHandler> handler =
6112 new CallbackTimeoutHandler(aCx, this, &aFunction, std::move(args));
6114 int32_t result;
6115 aError =
6116 mTimeoutManager->SetTimeout(handler, aTimeout, aIsInterval,
6117 Timeout::Reason::eTimeoutOrInterval, &result);
6118 return result;
6121 int32_t nsGlobalWindowInner::SetTimeoutOrInterval(JSContext* aCx,
6122 const nsAString& aHandler,
6123 int32_t aTimeout,
6124 bool aIsInterval,
6125 ErrorResult& aError) {
6126 nsGlobalWindowInner* inner = InnerForSetTimeoutOrInterval(aError);
6127 if (!inner) {
6128 return -1;
6131 if (inner != this) {
6132 RefPtr<nsGlobalWindowInner> innerRef(inner);
6133 return innerRef->SetTimeoutOrInterval(aCx, aHandler, aTimeout, aIsInterval,
6134 aError);
6137 DebuggerNotificationDispatch(
6138 this, aIsInterval ? DebuggerNotificationType::SetInterval
6139 : DebuggerNotificationType::SetTimeout);
6141 if (!GetContextInternal() || !HasJSGlobal()) {
6142 // This window was already closed, or never properly initialized,
6143 // don't let a timer be scheduled on such a window.
6144 aError.Throw(NS_ERROR_NOT_INITIALIZED);
6145 return 0;
6148 bool allowEval = false;
6149 aError = CSPEvalChecker::CheckForWindow(aCx, this, aHandler, &allowEval);
6150 if (NS_WARN_IF(aError.Failed()) || !allowEval) {
6151 return 0;
6154 RefPtr<TimeoutHandler> handler =
6155 new WindowScriptTimeoutHandler(aCx, this, aHandler);
6157 int32_t result;
6158 aError =
6159 mTimeoutManager->SetTimeout(handler, aTimeout, aIsInterval,
6160 Timeout::Reason::eTimeoutOrInterval, &result);
6161 return result;
6164 static const char* GetTimeoutReasonString(Timeout* aTimeout) {
6165 switch (aTimeout->mReason) {
6166 case Timeout::Reason::eTimeoutOrInterval:
6167 if (aTimeout->mIsInterval) {
6168 return "setInterval handler";
6170 return "setTimeout handler";
6171 case Timeout::Reason::eIdleCallbackTimeout:
6172 return "setIdleCallback handler (timed out)";
6173 default:
6174 MOZ_CRASH("Unexpected enum value");
6175 return "";
6179 bool nsGlobalWindowInner::RunTimeoutHandler(Timeout* aTimeout,
6180 nsIScriptContext* aScx) {
6181 // Hold on to the timeout in case mExpr or mFunObj releases its
6182 // doc.
6183 // XXXbz Our caller guarantees it'll hold on to the timeout (because
6184 // we're MOZ_CAN_RUN_SCRIPT), so we can probably stop doing that...
6185 RefPtr<Timeout> timeout = aTimeout;
6186 Timeout* last_running_timeout = mTimeoutManager->BeginRunningTimeout(timeout);
6187 timeout->mRunning = true;
6189 // Push this timeout's popup control state, which should only be
6190 // enabled the first time a timeout fires that was created while
6191 // popups were enabled and with a delay less than
6192 // "dom.disable_open_click_delay".
6193 AutoPopupStatePusher popupStatePusher(timeout->mPopupState);
6195 // Clear the timeout's popup state, if any, to prevent interval
6196 // timeouts from repeatedly opening poups.
6197 timeout->mPopupState = PopupBlocker::openAbused;
6199 uint32_t nestingLevel = TimeoutManager::GetNestingLevel();
6200 TimeoutManager::SetNestingLevel(timeout->mNestingLevel);
6202 const char* reason = GetTimeoutReasonString(timeout);
6204 #ifdef MOZ_GECKO_PROFILER
6205 nsCString str;
6206 if (profiler_can_accept_markers()) {
6207 TimeDuration originalInterval = timeout->When() - timeout->SubmitTime();
6208 str.Append(reason);
6209 str.Append(" with interval ");
6210 str.AppendInt(int(originalInterval.ToMilliseconds()));
6211 str.Append("ms: ");
6212 nsCString handlerDescription;
6213 timeout->mScriptHandler->GetDescription(handlerDescription);
6214 str.Append(handlerDescription);
6216 AUTO_PROFILER_MARKER_TEXT("setTimeout callback", DOM,
6217 MarkerOptions(MarkerStack::TakeBacktrace(
6218 timeout->TakeProfilerBacktrace()),
6219 MarkerInnerWindowId(mWindowID)),
6220 str);
6221 #endif
6223 bool abortIntervalHandler;
6225 RefPtr<TimeoutHandler> handler(timeout->mScriptHandler);
6227 CallbackDebuggerNotificationGuard guard(
6228 this, timeout->mIsInterval
6229 ? DebuggerNotificationType::SetIntervalCallback
6230 : DebuggerNotificationType::SetTimeoutCallback);
6231 abortIntervalHandler = !handler->Call(reason);
6234 // If we received an uncatchable exception, do not schedule the timeout again.
6235 // This allows the slow script dialog to break easy DoS attacks like
6236 // setInterval(function() { while(1); }, 100);
6237 if (abortIntervalHandler) {
6238 // If it wasn't an interval timer to begin with, this does nothing. If it
6239 // was, we'll treat it as a timeout that we just ran and discard it when
6240 // we return.
6241 timeout->mIsInterval = false;
6244 // We ignore any failures from calling EvaluateString() on the context or
6245 // Call() on a Function here since we're in a loop
6246 // where we're likely to be running timeouts whose OS timers
6247 // didn't fire in time and we don't want to not fire those timers
6248 // now just because execution of one timer failed. We can't
6249 // propagate the error to anyone who cares about it from this
6250 // point anyway, and the script context should have already reported
6251 // the script error in the usual way - so we just drop it.
6253 TimeoutManager::SetNestingLevel(nestingLevel);
6255 mTimeoutManager->EndRunningTimeout(last_running_timeout);
6256 timeout->mRunning = false;
6258 return timeout->mCleared;
6261 //*****************************************************************************
6262 // nsGlobalWindowInner: Helper Functions
6263 //*****************************************************************************
6265 already_AddRefed<nsIDocShellTreeOwner> nsGlobalWindowInner::GetTreeOwner() {
6266 FORWARD_TO_OUTER(GetTreeOwner, (), nullptr);
6269 already_AddRefed<nsIWebBrowserChrome>
6270 nsGlobalWindowInner::GetWebBrowserChrome() {
6271 nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
6273 nsCOMPtr<nsIWebBrowserChrome> browserChrome = do_GetInterface(treeOwner);
6274 return browserChrome.forget();
6277 nsIScrollableFrame* nsGlobalWindowInner::GetScrollFrame() {
6278 FORWARD_TO_OUTER(GetScrollFrame, (), nullptr);
6281 bool nsGlobalWindowInner::IsPrivateBrowsing() {
6282 nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(GetDocShell());
6283 return loadContext && loadContext->UsePrivateBrowsing();
6286 void nsGlobalWindowInner::FlushPendingNotifications(FlushType aType) {
6287 if (mDoc) {
6288 mDoc->FlushPendingNotifications(aType);
6292 void nsGlobalWindowInner::EnableDeviceSensor(uint32_t aType) {
6293 bool alreadyEnabled = false;
6294 for (uint32_t i = 0; i < mEnabledSensors.Length(); i++) {
6295 if (mEnabledSensors[i] == aType) {
6296 alreadyEnabled = true;
6297 break;
6301 mEnabledSensors.AppendElement(aType);
6303 if (alreadyEnabled) {
6304 return;
6307 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
6308 if (ac) {
6309 ac->AddWindowListener(aType, this);
6313 void nsGlobalWindowInner::DisableDeviceSensor(uint32_t aType) {
6314 int32_t doomedElement = -1;
6315 int32_t listenerCount = 0;
6316 for (uint32_t i = 0; i < mEnabledSensors.Length(); i++) {
6317 if (mEnabledSensors[i] == aType) {
6318 doomedElement = i;
6319 listenerCount++;
6323 if (doomedElement == -1) {
6324 return;
6327 mEnabledSensors.RemoveElementAt(doomedElement);
6329 if (listenerCount > 1) {
6330 return;
6333 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
6334 if (ac) {
6335 ac->RemoveWindowListener(aType, this);
6339 #if defined(MOZ_WIDGET_ANDROID)
6340 void nsGlobalWindowInner::EnableOrientationChangeListener() {
6341 if (!nsContentUtils::ShouldResistFingerprinting(GetDocShell()) &&
6342 !mOrientationChangeObserver) {
6343 mOrientationChangeObserver = MakeUnique<WindowOrientationObserver>(this);
6347 void nsGlobalWindowInner::DisableOrientationChangeListener() {
6348 mOrientationChangeObserver = nullptr;
6350 #endif
6352 void nsGlobalWindowInner::SetHasGamepadEventListener(
6353 bool aHasGamepad /* = true*/) {
6354 mHasGamepad = aHasGamepad;
6355 if (aHasGamepad) {
6356 EnableGamepadUpdates();
6360 void nsGlobalWindowInner::NotifyDetectXRRuntimesCompleted() {
6361 if (!mXRRuntimeDetectionInFlight) {
6362 return;
6364 mXRRuntimeDetectionInFlight = false;
6365 if (mXRPermissionRequestInFlight) {
6366 return;
6368 gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
6369 bool supported = vm->RuntimeSupportsVR();
6370 if (!supported) {
6371 // A VR runtime was not installed; we can suppress
6372 // the permission prompt
6373 OnXRPermissionRequestCancel();
6374 return;
6376 // A VR runtime was found. Display a permission prompt before
6377 // allowing it to be accessed.
6378 // Connect to the VRManager in order to receive the runtime
6379 // detection results.
6380 mXRPermissionRequestInFlight = true;
6381 RefPtr<XRPermissionRequest> request =
6382 new XRPermissionRequest(this, WindowID());
6383 Unused << NS_WARN_IF(NS_FAILED(request->Start()));
6386 void nsGlobalWindowInner::RequestXRPermission() {
6387 if (IsDying()) {
6388 // Do not proceed if the window is dying, as that will result
6389 // in leaks of objects that get re-allocated after FreeInnerObjects
6390 // has been called, including mVREventObserver.
6391 return;
6393 if (mXRPermissionGranted) {
6394 // Don't prompt redundantly once permission to
6395 // access XR devices has been granted.
6396 OnXRPermissionRequestAllow();
6397 return;
6399 if (mXRRuntimeDetectionInFlight || mXRPermissionRequestInFlight) {
6400 // Don't allow multiple simultaneous permissions requests;
6401 return;
6403 // Before displaying a permission prompt, detect
6404 // if there is any VR runtime installed.
6405 gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
6406 mXRRuntimeDetectionInFlight = true;
6407 EnableVRUpdates();
6408 vm->DetectRuntimes();
6411 void nsGlobalWindowInner::OnXRPermissionRequestAllow() {
6412 mXRPermissionRequestInFlight = false;
6413 if (IsDying()) {
6414 // The window may have started dying while the permission request
6415 // is in flight.
6416 // Do not proceed if the window is dying, as that will result
6417 // in leaks of objects that get re-allocated after FreeInnerObjects
6418 // has been called, including mNavigator.
6419 return;
6421 mXRPermissionGranted = true;
6423 NotifyHasXRSession();
6425 dom::Navigator* nav = Navigator();
6426 MOZ_ASSERT(nav != nullptr);
6427 nav->OnXRPermissionRequestAllow();
6430 void nsGlobalWindowInner::OnXRPermissionRequestCancel() {
6431 mXRPermissionRequestInFlight = false;
6432 if (IsDying()) {
6433 // The window may have started dying while the permission request
6434 // is in flight.
6435 // Do not proceed if the window is dying, as that will result
6436 // in leaks of objects that get re-allocated after FreeInnerObjects
6437 // has been called, including mNavigator.
6438 return;
6440 dom::Navigator* nav = Navigator();
6441 MOZ_ASSERT(nav != nullptr);
6442 nav->OnXRPermissionRequestCancel();
6445 void nsGlobalWindowInner::EventListenerAdded(nsAtom* aType) {
6446 if (aType == nsGkAtoms::onvrdisplayactivate ||
6447 aType == nsGkAtoms::onvrdisplayconnect ||
6448 aType == nsGkAtoms::onvrdisplaydeactivate ||
6449 aType == nsGkAtoms::onvrdisplaydisconnect ||
6450 aType == nsGkAtoms::onvrdisplaypresentchange) {
6451 RequestXRPermission();
6454 if (aType == nsGkAtoms::onvrdisplayactivate) {
6455 mHasVRDisplayActivateEvents = true;
6458 if (aType == nsGkAtoms::onbeforeunload && mWindowGlobalChild &&
6459 (!mDoc || !(mDoc->GetSandboxFlags() & SANDBOXED_MODALS))) {
6460 mWindowGlobalChild->BeforeUnloadAdded();
6461 MOZ_ASSERT(mWindowGlobalChild->BeforeUnloadListeners() > 0);
6464 // We need to initialize localStorage in order to receive notifications.
6465 if (aType == nsGkAtoms::onstorage) {
6466 ErrorResult rv;
6467 GetLocalStorage(rv);
6468 rv.SuppressException();
6470 if (NextGenLocalStorageEnabled() && mLocalStorage &&
6471 mLocalStorage->Type() == Storage::eLocalStorage) {
6472 auto object = static_cast<LSObject*>(mLocalStorage.get());
6474 Unused << NS_WARN_IF(NS_FAILED(object->EnsureObserver()));
6479 void nsGlobalWindowInner::EventListenerRemoved(nsAtom* aType) {
6480 if (aType == nsGkAtoms::onbeforeunload && mWindowGlobalChild &&
6481 (!mDoc || !(mDoc->GetSandboxFlags() & SANDBOXED_MODALS))) {
6482 mWindowGlobalChild->BeforeUnloadRemoved();
6483 MOZ_ASSERT(mWindowGlobalChild->BeforeUnloadListeners() >= 0);
6486 if (aType == nsGkAtoms::onstorage) {
6487 if (NextGenLocalStorageEnabled() && mLocalStorage &&
6488 mLocalStorage->Type() == Storage::eLocalStorage &&
6489 // The remove event is fired even if this isn't the last listener, so
6490 // only remove if there are no other listeners left.
6491 mListenerManager &&
6492 !mListenerManager->HasListenersFor(nsGkAtoms::onstorage)) {
6493 auto object = static_cast<LSObject*>(mLocalStorage.get());
6495 object->DropObserver();
6500 void nsGlobalWindowInner::NotifyHasXRSession() {
6501 if (IsDying()) {
6502 // Do not proceed if the window is dying, as that will result
6503 // in leaks of objects that get re-allocated after FreeInnerObjects
6504 // has been called, including mVREventObserver.
6505 return;
6507 mHasXRSession = true;
6508 EnableVRUpdates();
6511 bool nsGlobalWindowInner::HasUsedVR() const {
6512 // Returns true only if content has enumerated and activated
6513 // XR devices. Detection of XR runtimes without activation
6514 // will not cause true to be returned.
6515 return mHasXRSession;
6518 bool nsGlobalWindowInner::IsVRContentDetected() const {
6519 // Returns true only if the content will respond to
6520 // the VRDisplayActivate event.
6521 return mHasVRDisplayActivateEvents;
6524 bool nsGlobalWindowInner::IsVRContentPresenting() const {
6525 for (const auto& display : mVRDisplays) {
6526 if (display->IsAnyPresenting(gfx::kVRGroupAll)) {
6527 return true;
6530 return false;
6533 void nsGlobalWindowInner::AddSizeOfIncludingThis(
6534 nsWindowSizes& aWindowSizes) const {
6535 aWindowSizes.mDOMOtherSize += aWindowSizes.mState.mMallocSizeOf(this);
6536 aWindowSizes.mDOMOtherSize += nsIGlobalObject::ShallowSizeOfExcludingThis(
6537 aWindowSizes.mState.mMallocSizeOf);
6539 EventListenerManager* elm = GetExistingListenerManager();
6540 if (elm) {
6541 aWindowSizes.mDOMOtherSize +=
6542 elm->SizeOfIncludingThis(aWindowSizes.mState.mMallocSizeOf);
6543 aWindowSizes.mDOMEventListenersCount += elm->ListenerCount();
6545 if (mDoc) {
6546 // Multiple global windows can share a document. So only measure the
6547 // document if it (a) doesn't have a global window, or (b) it's the
6548 // primary document for the window.
6549 if (!mDoc->GetInnerWindow() || mDoc->GetInnerWindow() == this) {
6550 mDoc->DocAddSizeOfIncludingThis(aWindowSizes);
6554 if (mNavigator) {
6555 aWindowSizes.mDOMOtherSize +=
6556 mNavigator->SizeOfIncludingThis(aWindowSizes.mState.mMallocSizeOf);
6559 ForEachEventTargetObject([&](DOMEventTargetHelper* et, bool* aDoneOut) {
6560 if (nsCOMPtr<nsISizeOfEventTarget> iSizeOf = do_QueryObject(et)) {
6561 aWindowSizes.mDOMEventTargetsSize +=
6562 iSizeOf->SizeOfEventTargetIncludingThis(
6563 aWindowSizes.mState.mMallocSizeOf);
6565 if (EventListenerManager* elm = et->GetExistingListenerManager()) {
6566 aWindowSizes.mDOMEventListenersCount += elm->ListenerCount();
6568 ++aWindowSizes.mDOMEventTargetsCount;
6571 if (mPerformance) {
6572 aWindowSizes.mDOMPerformanceUserEntries =
6573 mPerformance->SizeOfUserEntries(aWindowSizes.mState.mMallocSizeOf);
6574 aWindowSizes.mDOMPerformanceResourceEntries =
6575 mPerformance->SizeOfResourceEntries(aWindowSizes.mState.mMallocSizeOf);
6579 void nsGlobalWindowInner::AddGamepad(GamepadHandle aHandle, Gamepad* aGamepad) {
6580 // Create the index we will present to content based on which indices are
6581 // already taken, as required by the spec.
6582 // https://w3c.github.io/gamepad/gamepad.html#widl-Gamepad-index
6583 int index = 0;
6584 while (mGamepadIndexSet.Contains(index)) {
6585 ++index;
6587 mGamepadIndexSet.Put(index);
6588 aGamepad->SetIndex(index);
6589 mGamepads.Put(aHandle, RefPtr{aGamepad});
6592 void nsGlobalWindowInner::RemoveGamepad(GamepadHandle aHandle) {
6593 RefPtr<Gamepad> gamepad;
6594 if (!mGamepads.Get(aHandle, getter_AddRefs(gamepad))) {
6595 return;
6597 // Free up the index we were using so it can be reused
6598 mGamepadIndexSet.Remove(gamepad->Index());
6599 mGamepads.Remove(aHandle);
6602 void nsGlobalWindowInner::GetGamepads(nsTArray<RefPtr<Gamepad>>& aGamepads) {
6603 aGamepads.Clear();
6605 // navigator.getGamepads() always returns an empty array when
6606 // privacy.resistFingerprinting is true.
6607 if (nsContentUtils::ShouldResistFingerprinting(GetDocShell())) {
6608 return;
6611 // mGamepads.Count() may not be sufficient, but it's not harmful.
6612 aGamepads.SetCapacity(mGamepads.Count());
6613 for (auto iter = mGamepads.Iter(); !iter.Done(); iter.Next()) {
6614 Gamepad* gamepad = iter.UserData();
6615 aGamepads.EnsureLengthAtLeast(gamepad->Index() + 1);
6616 aGamepads[gamepad->Index()] = gamepad;
6620 already_AddRefed<Gamepad> nsGlobalWindowInner::GetGamepad(
6621 GamepadHandle aHandle) {
6622 RefPtr<Gamepad> gamepad;
6624 if (mGamepads.Get(aHandle, getter_AddRefs(gamepad))) {
6625 return gamepad.forget();
6628 return nullptr;
6631 void nsGlobalWindowInner::SetHasSeenGamepadInput(bool aHasSeen) {
6632 mHasSeenGamepadInput = aHasSeen;
6635 bool nsGlobalWindowInner::HasSeenGamepadInput() { return mHasSeenGamepadInput; }
6637 void nsGlobalWindowInner::SyncGamepadState() {
6638 if (mHasSeenGamepadInput) {
6639 RefPtr<GamepadManager> gamepadManager(GamepadManager::GetService());
6640 for (auto iter = mGamepads.Iter(); !iter.Done(); iter.Next()) {
6641 gamepadManager->SyncGamepadState(iter.Key(), this, iter.UserData());
6646 void nsGlobalWindowInner::StopGamepadHaptics() {
6647 if (mHasSeenGamepadInput) {
6648 RefPtr<GamepadManager> gamepadManager(GamepadManager::GetService());
6649 gamepadManager->StopHaptics();
6653 bool nsGlobalWindowInner::UpdateVRDisplays(
6654 nsTArray<RefPtr<mozilla::dom::VRDisplay>>& aDevices) {
6655 VRDisplay::UpdateVRDisplays(mVRDisplays, this);
6656 aDevices = mVRDisplays.Clone();
6657 return true;
6660 void nsGlobalWindowInner::NotifyActiveVRDisplaysChanged() {
6661 if (mNavigator) {
6662 mNavigator->NotifyActiveVRDisplaysChanged();
6666 void nsGlobalWindowInner::NotifyPresentationGenerationChanged(
6667 uint32_t aDisplayID) {
6668 for (const auto& display : mVRDisplays) {
6669 if (display->DisplayId() == aDisplayID) {
6670 display->OnPresentationGenerationChanged();
6675 void nsGlobalWindowInner::DispatchVRDisplayActivate(
6676 uint32_t aDisplayID, mozilla::dom::VRDisplayEventReason aReason) {
6677 // Ensure that our list of displays is up to date
6678 VRDisplay::UpdateVRDisplays(mVRDisplays, this);
6680 // Search for the display identified with aDisplayID and fire the
6681 // event if found.
6682 for (const auto& display : mVRDisplays) {
6683 if (display->DisplayId() == aDisplayID) {
6684 if (aReason != VRDisplayEventReason::Navigation &&
6685 display->IsAnyPresenting(gfx::kVRGroupContent)) {
6686 // We only want to trigger this event if nobody is presenting to the
6687 // display already or when a page is loaded by navigating away
6688 // from a page with an active VR Presentation.
6689 continue;
6692 VRDisplayEventInit init;
6693 init.mBubbles = false;
6694 init.mCancelable = false;
6695 init.mDisplay = display;
6696 init.mReason.Construct(aReason);
6698 RefPtr<VRDisplayEvent> event =
6699 VRDisplayEvent::Constructor(this, u"vrdisplayactivate"_ns, init);
6700 // vrdisplayactivate is a trusted event, allowing VRDisplay.requestPresent
6701 // to be used in response to link traversal, user request (chrome UX), and
6702 // HMD mounting detection sensors.
6703 event->SetTrusted(true);
6704 // VRDisplay.requestPresent normally requires a user gesture; however, an
6705 // exception is made to allow it to be called in response to
6706 // vrdisplayactivate during VR link traversal.
6707 display->StartHandlingVRNavigationEvent();
6708 DispatchEvent(*event);
6709 display->StopHandlingVRNavigationEvent();
6710 // Once we dispatch the event, we must not access any members as an event
6711 // listener can do anything, including closing windows.
6712 return;
6717 void nsGlobalWindowInner::DispatchVRDisplayDeactivate(
6718 uint32_t aDisplayID, mozilla::dom::VRDisplayEventReason aReason) {
6719 // Ensure that our list of displays is up to date
6720 VRDisplay::UpdateVRDisplays(mVRDisplays, this);
6722 // Search for the display identified with aDisplayID and fire the
6723 // event if found.
6724 for (const auto& display : mVRDisplays) {
6725 if (display->DisplayId() == aDisplayID && display->IsPresenting()) {
6726 // We only want to trigger this event to content that is presenting to
6727 // the display already.
6729 VRDisplayEventInit init;
6730 init.mBubbles = false;
6731 init.mCancelable = false;
6732 init.mDisplay = display;
6733 init.mReason.Construct(aReason);
6735 RefPtr<VRDisplayEvent> event =
6736 VRDisplayEvent::Constructor(this, u"vrdisplaydeactivate"_ns, init);
6737 event->SetTrusted(true);
6738 DispatchEvent(*event);
6739 // Once we dispatch the event, we must not access any members as an event
6740 // listener can do anything, including closing windows.
6741 return;
6746 void nsGlobalWindowInner::DispatchVRDisplayConnect(uint32_t aDisplayID) {
6747 // Ensure that our list of displays is up to date
6748 VRDisplay::UpdateVRDisplays(mVRDisplays, this);
6750 // Search for the display identified with aDisplayID and fire the
6751 // event if found.
6752 for (const auto& display : mVRDisplays) {
6753 if (display->DisplayId() == aDisplayID) {
6754 // Fire event even if not presenting to the display.
6755 VRDisplayEventInit init;
6756 init.mBubbles = false;
6757 init.mCancelable = false;
6758 init.mDisplay = display;
6759 // VRDisplayEvent.reason is not set for vrdisplayconnect
6761 RefPtr<VRDisplayEvent> event =
6762 VRDisplayEvent::Constructor(this, u"vrdisplayconnect"_ns, init);
6763 event->SetTrusted(true);
6764 DispatchEvent(*event);
6765 // Once we dispatch the event, we must not access any members as an event
6766 // listener can do anything, including closing windows.
6767 return;
6772 void nsGlobalWindowInner::DispatchVRDisplayDisconnect(uint32_t aDisplayID) {
6773 // Ensure that our list of displays is up to date
6774 VRDisplay::UpdateVRDisplays(mVRDisplays, this);
6776 // Search for the display identified with aDisplayID and fire the
6777 // event if found.
6778 for (const auto& display : mVRDisplays) {
6779 if (display->DisplayId() == aDisplayID) {
6780 // Fire event even if not presenting to the display.
6781 VRDisplayEventInit init;
6782 init.mBubbles = false;
6783 init.mCancelable = false;
6784 init.mDisplay = display;
6785 // VRDisplayEvent.reason is not set for vrdisplaydisconnect
6787 RefPtr<VRDisplayEvent> event =
6788 VRDisplayEvent::Constructor(this, u"vrdisplaydisconnect"_ns, init);
6789 event->SetTrusted(true);
6790 DispatchEvent(*event);
6791 // Once we dispatch the event, we must not access any members as an event
6792 // listener can do anything, including closing windows.
6793 return;
6798 void nsGlobalWindowInner::DispatchVRDisplayPresentChange(uint32_t aDisplayID) {
6799 // Ensure that our list of displays is up to date
6800 VRDisplay::UpdateVRDisplays(mVRDisplays, this);
6802 // Search for the display identified with aDisplayID and fire the
6803 // event if found.
6804 for (const auto& display : mVRDisplays) {
6805 if (display->DisplayId() == aDisplayID) {
6806 // Fire event even if not presenting to the display.
6807 VRDisplayEventInit init;
6808 init.mBubbles = false;
6809 init.mCancelable = false;
6810 init.mDisplay = display;
6811 // VRDisplayEvent.reason is not set for vrdisplaypresentchange
6812 RefPtr<VRDisplayEvent> event =
6813 VRDisplayEvent::Constructor(this, u"vrdisplaypresentchange"_ns, init);
6814 event->SetTrusted(true);
6815 DispatchEvent(*event);
6816 // Once we dispatch the event, we must not access any members as an event
6817 // listener can do anything, including closing windows.
6818 return;
6823 enum WindowState {
6824 // These constants need to match the constants in Window.webidl
6825 STATE_MAXIMIZED = 1,
6826 STATE_MINIMIZED = 2,
6827 STATE_NORMAL = 3,
6828 STATE_FULLSCREEN = 4
6831 uint16_t nsGlobalWindowInner::WindowState() {
6832 nsCOMPtr<nsIWidget> widget = GetMainWidget();
6834 int32_t mode = widget ? widget->SizeMode() : 0;
6836 switch (mode) {
6837 case nsSizeMode_Minimized:
6838 return STATE_MINIMIZED;
6839 case nsSizeMode_Maximized:
6840 return STATE_MAXIMIZED;
6841 case nsSizeMode_Fullscreen:
6842 return STATE_FULLSCREEN;
6843 case nsSizeMode_Normal:
6844 return STATE_NORMAL;
6845 default:
6846 NS_WARNING("Illegal window state for this chrome window");
6847 break;
6850 return STATE_NORMAL;
6853 bool nsGlobalWindowInner::IsFullyOccluded() {
6854 nsCOMPtr<nsIWidget> widget = GetMainWidget();
6855 return widget && widget->IsFullyOccluded();
6858 void nsGlobalWindowInner::Maximize() {
6859 nsCOMPtr<nsIWidget> widget = GetMainWidget();
6861 if (widget) {
6862 widget->SetSizeMode(nsSizeMode_Maximized);
6866 void nsGlobalWindowInner::Minimize() {
6867 nsCOMPtr<nsIWidget> widget = GetMainWidget();
6869 if (widget) {
6870 widget->SetSizeMode(nsSizeMode_Minimized);
6874 void nsGlobalWindowInner::Restore() {
6875 nsCOMPtr<nsIWidget> widget = GetMainWidget();
6877 if (widget) {
6878 widget->SetSizeMode(nsSizeMode_Normal);
6882 void nsGlobalWindowInner::GetWorkspaceID(nsAString& workspaceID) {
6883 nsCOMPtr<nsIWidget> widget = GetMainWidget();
6885 workspaceID.Truncate();
6886 if (widget) {
6887 return widget->GetWorkspaceID(workspaceID);
6891 void nsGlobalWindowInner::MoveToWorkspace(const nsAString& workspaceID) {
6892 nsCOMPtr<nsIWidget> widget = GetMainWidget();
6894 if (widget) {
6895 widget->MoveToWorkspace(workspaceID);
6899 void nsGlobalWindowInner::GetAttention(ErrorResult& aResult) {
6900 return GetAttentionWithCycleCount(-1, aResult);
6903 void nsGlobalWindowInner::GetAttentionWithCycleCount(int32_t aCycleCount,
6904 ErrorResult& aError) {
6905 nsCOMPtr<nsIWidget> widget = GetMainWidget();
6907 if (widget) {
6908 aError = widget->GetAttention(aCycleCount);
6912 already_AddRefed<Promise> nsGlobalWindowInner::PromiseDocumentFlushed(
6913 PromiseDocumentFlushedCallback& aCallback, ErrorResult& aError) {
6914 MOZ_RELEASE_ASSERT(IsChromeWindow());
6916 if (!IsCurrentInnerWindow()) {
6917 aError.Throw(NS_ERROR_FAILURE);
6918 return nullptr;
6921 if (mIteratingDocumentFlushedResolvers) {
6922 aError.Throw(NS_ERROR_FAILURE);
6923 return nullptr;
6926 if (!mDoc) {
6927 aError.Throw(NS_ERROR_FAILURE);
6928 return nullptr;
6931 PresShell* presShell = mDoc->GetPresShell();
6932 if (!presShell) {
6933 aError.Throw(NS_ERROR_FAILURE);
6934 return nullptr;
6937 // We need to associate the lifetime of the Promise to the lifetime
6938 // of the caller's global. That way, if the window we're observing
6939 // refresh driver ticks on goes away before our observer is fired,
6940 // we can still resolve the Promise.
6941 nsIGlobalObject* global = GetIncumbentGlobal();
6942 if (!global) {
6943 aError.Throw(NS_ERROR_FAILURE);
6944 return nullptr;
6947 RefPtr<Promise> resultPromise = Promise::Create(global, aError);
6948 if (aError.Failed()) {
6949 return nullptr;
6952 UniquePtr<PromiseDocumentFlushedResolver> flushResolver(
6953 new PromiseDocumentFlushedResolver(resultPromise, aCallback));
6955 if (!presShell->NeedStyleFlush() && !presShell->NeedLayoutFlush()) {
6956 flushResolver->Call();
6957 return resultPromise.forget();
6960 if (!mObservingDidRefresh) {
6961 bool success = presShell->AddPostRefreshObserver(this);
6962 if (!success) {
6963 aError.Throw(NS_ERROR_FAILURE);
6964 return nullptr;
6966 mObservingDidRefresh = true;
6969 mDocumentFlushedResolvers.AppendElement(std::move(flushResolver));
6970 return resultPromise.forget();
6973 template <bool call>
6974 void nsGlobalWindowInner::CallOrCancelDocumentFlushedResolvers() {
6975 MOZ_ASSERT(!mIteratingDocumentFlushedResolvers);
6977 while (true) {
6979 // To coalesce MicroTask checkpoints inside callback call, enclose the
6980 // inner loop with nsAutoMicroTask, and perform a MicroTask checkpoint
6981 // after the loop.
6982 nsAutoMicroTask mt;
6984 mIteratingDocumentFlushedResolvers = true;
6985 for (const auto& documentFlushedResolver : mDocumentFlushedResolvers) {
6986 if (call) {
6987 documentFlushedResolver->Call();
6988 } else {
6989 documentFlushedResolver->Cancel();
6992 mDocumentFlushedResolvers.Clear();
6993 mIteratingDocumentFlushedResolvers = false;
6996 // Leaving nsAutoMicroTask above will perform MicroTask checkpoint, and
6997 // Promise callbacks there may create mDocumentFlushedResolvers items.
6999 // If there's no new item, there's nothing to do here.
7000 if (!mDocumentFlushedResolvers.Length()) {
7001 break;
7004 // If there are new items, the observer is not added for them when calling
7005 // PromiseDocumentFlushed. Add here and leave.
7006 // FIXME: Handle this case inside PromiseDocumentFlushed (bug 1442824).
7007 if (mDoc) {
7008 PresShell* presShell = mDoc->GetPresShell();
7009 if (presShell) {
7010 Unused << presShell->AddPostRefreshObserver(this);
7011 break;
7015 // If we fail adding observer, keep looping to resolve or reject all
7016 // promises. This case happens while destroying window.
7017 // This violates the constraint that the promiseDocumentFlushed callback
7018 // only ever run when no flush needed, but it's necessary to resolve
7019 // Promise returned by that.
7023 void nsGlobalWindowInner::CallDocumentFlushedResolvers() {
7024 CallOrCancelDocumentFlushedResolvers<true>();
7027 void nsGlobalWindowInner::CancelDocumentFlushedResolvers() {
7028 CallOrCancelDocumentFlushedResolvers<false>();
7031 void nsGlobalWindowInner::DidRefresh() {
7032 RefPtr<nsGlobalWindowInner> kungFuDeathGrip(this);
7034 auto rejectionGuard = MakeScopeExit([&] {
7035 CancelDocumentFlushedResolvers();
7036 mObservingDidRefresh = false;
7039 MOZ_ASSERT(mDoc);
7041 PresShell* presShell = mDoc->GetPresShell();
7042 MOZ_ASSERT(presShell);
7044 if (presShell->NeedStyleFlush() || presShell->NeedLayoutFlush()) {
7045 // By the time our observer fired, something has already invalidated
7046 // style or layout - or perhaps we're still in the middle of a flush that
7047 // was interrupted. In either case, we'll wait until the next refresh driver
7048 // tick instead and try again.
7049 rejectionGuard.release();
7050 return;
7053 bool success = presShell->RemovePostRefreshObserver(this);
7054 if (!success) {
7055 return;
7058 rejectionGuard.release();
7060 CallDocumentFlushedResolvers();
7061 mObservingDidRefresh = false;
7064 already_AddRefed<nsWindowRoot> nsGlobalWindowInner::GetWindowRoot(
7065 mozilla::ErrorResult& aError) {
7066 FORWARD_TO_OUTER_OR_THROW(GetWindowRootOuter, (), aError, nullptr);
7069 void nsGlobalWindowInner::SetCursor(const nsACString& aCursor,
7070 ErrorResult& aError) {
7071 FORWARD_TO_OUTER_OR_THROW(SetCursorOuter, (aCursor, aError), aError, );
7074 NS_IMETHODIMP
7075 nsGlobalWindowInner::GetBrowserDOMWindow(nsIBrowserDOMWindow** aBrowserWindow) {
7076 MOZ_RELEASE_ASSERT(IsChromeWindow());
7078 ErrorResult rv;
7079 NS_IF_ADDREF(*aBrowserWindow = GetBrowserDOMWindow(rv));
7080 return rv.StealNSResult();
7083 nsIBrowserDOMWindow* nsGlobalWindowInner::GetBrowserDOMWindow(
7084 ErrorResult& aError) {
7085 FORWARD_TO_OUTER_OR_THROW(GetBrowserDOMWindowOuter, (), aError, nullptr);
7088 void nsGlobalWindowInner::SetBrowserDOMWindow(
7089 nsIBrowserDOMWindow* aBrowserWindow, ErrorResult& aError) {
7090 FORWARD_TO_OUTER_OR_THROW(SetBrowserDOMWindowOuter, (aBrowserWindow),
7091 aError, );
7094 void nsGlobalWindowInner::NotifyDefaultButtonLoaded(Element& aDefaultButton,
7095 ErrorResult& aError) {
7096 #ifdef MOZ_XUL
7097 // Don't snap to a disabled button.
7098 nsCOMPtr<nsIDOMXULControlElement> xulControl = aDefaultButton.AsXULControl();
7099 if (!xulControl) {
7100 aError.Throw(NS_ERROR_FAILURE);
7101 return;
7103 bool disabled;
7104 aError = xulControl->GetDisabled(&disabled);
7105 if (aError.Failed() || disabled) {
7106 return;
7109 // Get the button rect in screen coordinates.
7110 nsIFrame* frame = aDefaultButton.GetPrimaryFrame();
7111 if (!frame) {
7112 aError.Throw(NS_ERROR_FAILURE);
7113 return;
7115 LayoutDeviceIntRect buttonRect = LayoutDeviceIntRect::FromAppUnitsToNearest(
7116 frame->GetScreenRectInAppUnits(),
7117 frame->PresContext()->AppUnitsPerDevPixel());
7119 // Get the widget rect in screen coordinates.
7120 nsIWidget* widget = GetNearestWidget();
7121 if (!widget) {
7122 aError.Throw(NS_ERROR_FAILURE);
7123 return;
7125 LayoutDeviceIntRect widgetRect = widget->GetScreenBounds();
7127 // Convert the buttonRect coordinates from screen to the widget.
7128 buttonRect -= widgetRect.TopLeft();
7129 nsresult rv = widget->OnDefaultButtonLoaded(buttonRect);
7130 if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) {
7131 aError.Throw(rv);
7133 #else
7134 aError.Throw(NS_ERROR_NOT_IMPLEMENTED);
7135 #endif
7138 ChromeMessageBroadcaster* nsGlobalWindowInner::MessageManager() {
7139 MOZ_ASSERT(IsChromeWindow());
7140 if (!mChromeFields.mMessageManager) {
7141 RefPtr<ChromeMessageBroadcaster> globalMM =
7142 nsFrameMessageManager::GetGlobalMessageManager();
7143 mChromeFields.mMessageManager = new ChromeMessageBroadcaster(globalMM);
7145 return mChromeFields.mMessageManager;
7148 ChromeMessageBroadcaster* nsGlobalWindowInner::GetGroupMessageManager(
7149 const nsAString& aGroup) {
7150 MOZ_ASSERT(IsChromeWindow());
7152 RefPtr<ChromeMessageBroadcaster> messageManager =
7153 mChromeFields.mGroupMessageManagers.LookupForAdd(aGroup).OrInsert(
7154 [this]() { return new ChromeMessageBroadcaster(MessageManager()); });
7155 return messageManager;
7158 void nsGlobalWindowInner::InitWasOffline() { mWasOffline = NS_IsOffline(); }
7160 #if defined(MOZ_WIDGET_ANDROID)
7161 int16_t nsGlobalWindowInner::Orientation(CallerType aCallerType) const {
7162 return nsContentUtils::ResistFingerprinting(aCallerType)
7164 : WindowOrientationObserver::OrientationAngle();
7166 #endif
7168 already_AddRefed<Console> nsGlobalWindowInner::GetConsole(JSContext* aCx,
7169 ErrorResult& aRv) {
7170 if (!mConsole) {
7171 mConsole = Console::Create(aCx, this, aRv);
7172 if (NS_WARN_IF(aRv.Failed())) {
7173 return nullptr;
7177 RefPtr<Console> console = mConsole;
7178 return console.forget();
7181 bool nsGlobalWindowInner::IsSecureContext() const {
7182 JS::Realm* realm = js::GetNonCCWObjectRealm(GetWrapperPreserveColor());
7183 return JS::GetIsSecureContext(realm);
7186 External* nsGlobalWindowInner::GetExternal(ErrorResult& aRv) {
7187 #ifdef HAVE_SIDEBAR
7188 if (!mExternal) {
7189 mExternal = ConstructJSImplementation<External>("@mozilla.org/sidebar;1",
7190 this, aRv);
7191 if (aRv.Failed()) {
7192 return nullptr;
7196 return static_cast<External*>(mExternal.get());
7197 #else
7198 aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
7199 return nullptr;
7200 #endif
7203 void nsGlobalWindowInner::GetSidebar(OwningExternalOrWindowProxy& aResult,
7204 ErrorResult& aRv) {
7205 #ifdef HAVE_SIDEBAR
7206 // First check for a named frame named "sidebar"
7207 RefPtr<BrowsingContext> domWindow = GetChildWindow(u"sidebar"_ns);
7208 if (domWindow) {
7209 aResult.SetAsWindowProxy() = std::move(domWindow);
7210 return;
7213 RefPtr<External> external = GetExternal(aRv);
7214 if (external) {
7215 aResult.SetAsExternal() = external;
7217 #else
7218 aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
7219 #endif
7222 void nsGlobalWindowInner::ClearDocumentDependentSlots(JSContext* aCx) {
7223 // If JSAPI OOMs here, there is basically nothing we can do to recover safely.
7224 if (!Window_Binding::ClearCachedDocumentValue(aCx, this) ||
7225 !Window_Binding::ClearCachedPerformanceValue(aCx, this)) {
7226 MOZ_CRASH("Unhandlable OOM while clearing document dependent slots.");
7230 /* static */
7231 JSObject* nsGlobalWindowInner::CreateNamedPropertiesObject(
7232 JSContext* aCx, JS::Handle<JSObject*> aProto) {
7233 return WindowNamedPropertiesHandler::Create(aCx, aProto);
7236 void nsGlobalWindowInner::RedefineProperty(JSContext* aCx,
7237 const char* aPropName,
7238 JS::Handle<JS::Value> aValue,
7239 ErrorResult& aError) {
7240 JS::Rooted<JSObject*> thisObj(aCx, GetWrapperPreserveColor());
7241 if (!thisObj) {
7242 aError.Throw(NS_ERROR_UNEXPECTED);
7243 return;
7246 if (!JS_WrapObject(aCx, &thisObj) ||
7247 !JS_DefineProperty(aCx, thisObj, aPropName, aValue, JSPROP_ENUMERATE)) {
7248 aError.Throw(NS_ERROR_FAILURE);
7252 template <typename T>
7253 void nsGlobalWindowInner::GetReplaceableWindowCoord(
7254 JSContext* aCx, nsGlobalWindowInner::WindowCoordGetter<T> aGetter,
7255 JS::MutableHandle<JS::Value> aRetval, CallerType aCallerType,
7256 ErrorResult& aError) {
7257 T coord = (this->*aGetter)(aCallerType, aError);
7258 if (!aError.Failed() && !ToJSValue(aCx, coord, aRetval)) {
7259 aError.Throw(NS_ERROR_FAILURE);
7263 template <typename T>
7264 void nsGlobalWindowInner::SetReplaceableWindowCoord(
7265 JSContext* aCx, nsGlobalWindowInner::WindowCoordSetter<T> aSetter,
7266 JS::Handle<JS::Value> aValue, const char* aPropName, CallerType aCallerType,
7267 ErrorResult& aError) {
7269 * If caller is not chrome and the user has not explicitly exempted the site,
7270 * just treat this the way we would an IDL replaceable property.
7272 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
7273 if (!outer || !outer->CanMoveResizeWindows(aCallerType) ||
7274 mBrowsingContext->IsFrame()) {
7275 RedefineProperty(aCx, aPropName, aValue, aError);
7276 return;
7279 T value;
7280 if (!ValueToPrimitive<T, eDefault>(aCx, aValue, aPropName, &value)) {
7281 aError.Throw(NS_ERROR_UNEXPECTED);
7282 return;
7285 if (nsContentUtils::ShouldResistFingerprinting(GetDocShell())) {
7286 bool innerWidthSpecified = false;
7287 bool innerHeightSpecified = false;
7288 bool outerWidthSpecified = false;
7289 bool outerHeightSpecified = false;
7291 if (strcmp(aPropName, "innerWidth") == 0) {
7292 innerWidthSpecified = true;
7293 } else if (strcmp(aPropName, "innerHeight") == 0) {
7294 innerHeightSpecified = true;
7295 } else if (strcmp(aPropName, "outerWidth") == 0) {
7296 outerWidthSpecified = true;
7297 } else if (strcmp(aPropName, "outerHeight") == 0) {
7298 outerHeightSpecified = true;
7301 if (innerWidthSpecified || innerHeightSpecified || outerWidthSpecified ||
7302 outerHeightSpecified) {
7303 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = outer->GetTreeOwnerWindow();
7304 nsCOMPtr<nsIScreen> screen;
7305 nsCOMPtr<nsIScreenManager> screenMgr(
7306 do_GetService("@mozilla.org/gfx/screenmanager;1"));
7307 int32_t winLeft = 0;
7308 int32_t winTop = 0;
7309 int32_t winWidth = 0;
7310 int32_t winHeight = 0;
7311 double scale = 1.0;
7313 if (treeOwnerAsWin && screenMgr) {
7314 // Acquire current window size.
7315 treeOwnerAsWin->GetUnscaledDevicePixelsPerCSSPixel(&scale);
7316 treeOwnerAsWin->GetPositionAndSize(&winLeft, &winTop, &winWidth,
7317 &winHeight);
7318 winLeft = NSToIntRound(winHeight / scale);
7319 winTop = NSToIntRound(winWidth / scale);
7320 winWidth = NSToIntRound(winWidth / scale);
7321 winHeight = NSToIntRound(winHeight / scale);
7323 // Acquire content window size.
7324 CSSSize contentSize;
7325 outer->GetInnerSize(contentSize);
7327 screenMgr->ScreenForRect(winLeft, winTop, winWidth, winHeight,
7328 getter_AddRefs(screen));
7330 if (screen) {
7331 int32_t roundedValue = std::round(value);
7332 int32_t* targetContentWidth = nullptr;
7333 int32_t* targetContentHeight = nullptr;
7334 int32_t screenWidth = 0;
7335 int32_t screenHeight = 0;
7336 int32_t chromeWidth = 0;
7337 int32_t chromeHeight = 0;
7338 int32_t inputWidth = 0;
7339 int32_t inputHeight = 0;
7340 int32_t unused = 0;
7342 // Get screen dimensions (in device pixels)
7343 screen->GetAvailRect(&unused, &unused, &screenWidth, &screenHeight);
7344 // Convert them to CSS pixels
7345 screenWidth = NSToIntRound(screenWidth / scale);
7346 screenHeight = NSToIntRound(screenHeight / scale);
7348 // Calculate the chrome UI size.
7349 chromeWidth = winWidth - contentSize.width;
7350 chromeHeight = winHeight - contentSize.height;
7352 if (innerWidthSpecified || outerWidthSpecified) {
7353 inputWidth = value;
7354 targetContentWidth = &roundedValue;
7355 targetContentHeight = &unused;
7356 } else if (innerHeightSpecified || outerHeightSpecified) {
7357 inputHeight = value;
7358 targetContentWidth = &unused;
7359 targetContentHeight = &roundedValue;
7362 nsContentUtils::CalcRoundedWindowSizeForResistingFingerprinting(
7363 chromeWidth, chromeHeight, screenWidth, screenHeight, inputWidth,
7364 inputHeight, outerWidthSpecified, outerHeightSpecified,
7365 targetContentWidth, targetContentHeight);
7366 value = T(roundedValue);
7372 (this->*aSetter)(value, aCallerType, aError);
7375 void nsGlobalWindowInner::FireOnNewGlobalObject() {
7376 // AutoEntryScript required to invoke debugger hook, which is a
7377 // Gecko-specific concept at present.
7378 AutoEntryScript aes(this, "nsGlobalWindowInner report new global");
7379 JS::Rooted<JSObject*> global(aes.cx(), GetWrapper());
7380 JS_FireOnNewGlobalObject(aes.cx(), global);
7383 #if defined(_WINDOWS_) && !defined(MOZ_WRAPPED_WINDOWS_H)
7384 # pragma message( \
7385 "wrapper failure reason: " MOZ_WINDOWS_WRAPPER_DISABLED_REASON)
7386 # error "Never include unwrapped windows.h in this file!"
7387 #endif
7389 already_AddRefed<Promise> nsGlobalWindowInner::CreateImageBitmap(
7390 const ImageBitmapSource& aImage, ErrorResult& aRv) {
7391 return ImageBitmap::Create(this, aImage, Nothing(), aRv);
7394 already_AddRefed<Promise> nsGlobalWindowInner::CreateImageBitmap(
7395 const ImageBitmapSource& aImage, int32_t aSx, int32_t aSy, int32_t aSw,
7396 int32_t aSh, ErrorResult& aRv) {
7397 return ImageBitmap::Create(this, aImage,
7398 Some(gfx::IntRect(aSx, aSy, aSw, aSh)), aRv);
7401 nsresult nsGlobalWindowInner::Dispatch(
7402 TaskCategory aCategory, already_AddRefed<nsIRunnable>&& aRunnable) {
7403 MOZ_RELEASE_ASSERT(NS_IsMainThread());
7404 if (GetDocGroup()) {
7405 return GetDocGroup()->Dispatch(aCategory, std::move(aRunnable));
7407 return DispatcherTrait::Dispatch(aCategory, std::move(aRunnable));
7410 nsISerialEventTarget* nsGlobalWindowInner::EventTargetFor(
7411 TaskCategory aCategory) const {
7412 MOZ_RELEASE_ASSERT(NS_IsMainThread());
7413 if (GetDocGroup()) {
7414 return GetDocGroup()->EventTargetFor(aCategory);
7416 return DispatcherTrait::EventTargetFor(aCategory);
7419 AbstractThread* nsGlobalWindowInner::AbstractMainThreadFor(
7420 TaskCategory aCategory) {
7421 MOZ_RELEASE_ASSERT(NS_IsMainThread());
7422 if (GetDocGroup()) {
7423 return GetDocGroup()->AbstractMainThreadFor(aCategory);
7425 return DispatcherTrait::AbstractMainThreadFor(aCategory);
7428 Worklet* nsGlobalWindowInner::GetPaintWorklet(ErrorResult& aRv) {
7429 if (!mPaintWorklet) {
7430 nsIPrincipal* principal = GetPrincipal();
7431 if (!principal) {
7432 aRv.Throw(NS_ERROR_FAILURE);
7433 return nullptr;
7436 mPaintWorklet = PaintWorkletImpl::CreateWorklet(this, principal);
7439 return mPaintWorklet;
7442 void nsGlobalWindowInner::GetRegionalPrefsLocales(
7443 nsTArray<nsString>& aLocales) {
7444 MOZ_ASSERT(mozilla::intl::LocaleService::GetInstance());
7446 AutoTArray<nsCString, 10> rpLocales;
7447 mozilla::intl::LocaleService::GetInstance()->GetRegionalPrefsLocales(
7448 rpLocales);
7450 for (const auto& loc : rpLocales) {
7451 aLocales.AppendElement(NS_ConvertUTF8toUTF16(loc));
7455 void nsGlobalWindowInner::GetWebExposedLocales(nsTArray<nsString>& aLocales) {
7456 MOZ_ASSERT(mozilla::intl::LocaleService::GetInstance());
7458 AutoTArray<nsCString, 10> rpLocales;
7459 mozilla::intl::LocaleService::GetInstance()->GetWebExposedLocales(rpLocales);
7461 for (const auto& loc : rpLocales) {
7462 aLocales.AppendElement(NS_ConvertUTF8toUTF16(loc));
7466 IntlUtils* nsGlobalWindowInner::GetIntlUtils(ErrorResult& aError) {
7467 if (!mIntlUtils) {
7468 mIntlUtils = new IntlUtils(this);
7471 return mIntlUtils;
7474 void nsGlobalWindowInner::StoreSharedWorker(SharedWorker* aSharedWorker) {
7475 MOZ_ASSERT(aSharedWorker);
7476 MOZ_ASSERT(!mSharedWorkers.Contains(aSharedWorker));
7478 mSharedWorkers.AppendElement(aSharedWorker);
7481 void nsGlobalWindowInner::ForgetSharedWorker(SharedWorker* aSharedWorker) {
7482 MOZ_ASSERT(aSharedWorker);
7483 MOZ_ASSERT(mSharedWorkers.Contains(aSharedWorker));
7485 mSharedWorkers.RemoveElement(aSharedWorker);
7488 void nsGlobalWindowInner::StorageAccessPermissionGranted() {
7489 PropagateStorageAccessPermissionGrantedToWorkers(*this);
7491 // If we have a partitioned localStorage, it's time to replace it with a real
7492 // one in order to receive notifications.
7494 if (mLocalStorage) {
7495 IgnoredErrorResult error;
7496 GetLocalStorage(error);
7497 if (NS_WARN_IF(error.Failed())) {
7498 return;
7501 MOZ_ASSERT(mLocalStorage &&
7502 mLocalStorage->Type() == Storage::eLocalStorage);
7504 if (NextGenLocalStorageEnabled() && mListenerManager &&
7505 mListenerManager->HasListenersFor(nsGkAtoms::onstorage)) {
7506 auto object = static_cast<LSObject*>(mLocalStorage.get());
7508 object->EnsureObserver();
7512 // Reset the IndexedDB factory.
7513 mIndexedDB = nullptr;
7515 // Reset DOM Cache
7516 mCacheStorage = nullptr;
7518 // Reset the active storage principal
7519 if (mDoc) {
7520 mDoc->ClearActiveStoragePrincipal();
7524 ContentMediaController* nsGlobalWindowInner::GetContentMediaController() {
7525 if (mContentMediaController) {
7526 return mContentMediaController;
7528 if (!mBrowsingContext) {
7529 return nullptr;
7532 mContentMediaController = new ContentMediaController(mBrowsingContext->Id());
7533 return mContentMediaController;
7536 /* static */
7537 already_AddRefed<nsGlobalWindowInner> nsGlobalWindowInner::Create(
7538 nsGlobalWindowOuter* aOuterWindow, bool aIsChrome,
7539 WindowGlobalChild* aActor) {
7540 RefPtr<nsGlobalWindowInner> window =
7541 new nsGlobalWindowInner(aOuterWindow, aActor);
7542 if (aIsChrome) {
7543 window->mIsChrome = true;
7544 window->mCleanMessageManager = true;
7547 if (aActor) {
7548 aActor->InitWindowGlobal(window);
7551 window->InitWasOffline();
7552 return window.forget();
7555 nsIURI* nsPIDOMWindowInner::GetDocumentURI() const {
7556 return mDoc ? mDoc->GetDocumentURI() : mDocumentURI.get();
7559 nsIURI* nsPIDOMWindowInner::GetDocBaseURI() const {
7560 return mDoc ? mDoc->GetDocBaseURI() : mDocBaseURI.get();
7563 mozilla::dom::WindowContext* nsPIDOMWindowInner::GetWindowContext() const {
7564 return mWindowGlobalChild ? mWindowGlobalChild->WindowContext() : nullptr;
7567 bool nsPIDOMWindowInner::RemoveFromBFCacheSync() {
7568 if (Document* doc = GetExtantDoc()) {
7569 return doc->RemoveFromBFCacheSync();
7571 return false;
7574 void nsPIDOMWindowInner::MaybeCreateDoc() {
7575 // XXX: Forward to outer?
7576 MOZ_ASSERT(!mDoc);
7577 if (nsIDocShell* docShell = GetDocShell()) {
7578 // Note that |document| here is the same thing as our mDoc, but we
7579 // don't have to explicitly set the member variable because the docshell
7580 // has already called SetNewDocument().
7581 nsCOMPtr<Document> document = docShell->GetDocument();
7582 Unused << document;
7586 mozilla::dom::DocGroup* nsPIDOMWindowInner::GetDocGroup() const {
7587 Document* doc = GetExtantDoc();
7588 if (doc) {
7589 return doc->GetDocGroup();
7591 return nullptr;
7594 mozilla::dom::BrowsingContextGroup*
7595 nsPIDOMWindowInner::GetBrowsingContextGroup() const {
7596 return mBrowsingContext ? mBrowsingContext->Group() : nullptr;
7599 nsIGlobalObject* nsPIDOMWindowInner::AsGlobal() {
7600 return nsGlobalWindowInner::Cast(this);
7603 const nsIGlobalObject* nsPIDOMWindowInner::AsGlobal() const {
7604 return nsGlobalWindowInner::Cast(this);
7607 void nsPIDOMWindowInner::SaveStorageAccessPermissionGranted() {
7608 mStorageAccessPermissionGranted = true;
7610 nsGlobalWindowInner::Cast(this)->StorageAccessPermissionGranted();
7613 bool nsPIDOMWindowInner::HasStorageAccessPermissionGranted() {
7614 return mStorageAccessPermissionGranted;
7617 nsPIDOMWindowInner::nsPIDOMWindowInner(nsPIDOMWindowOuter* aOuterWindow,
7618 WindowGlobalChild* aActor)
7619 : mMutationBits(0),
7620 mActivePeerConnections(0),
7621 mIsDocumentLoaded(false),
7622 mIsHandlingResizeEvent(false),
7623 mMayHavePaintEventListener(false),
7624 mMayHaveTouchEventListener(false),
7625 mMayHaveSelectionChangeEventListener(false),
7626 mMayHaveMouseEnterLeaveEventListener(false),
7627 mMayHavePointerEnterLeaveEventListener(false),
7628 mMayHaveBeforeInputEventListenerForTelemetry(false),
7629 mMutationObserverHasObservedNodeForTelemetry(false),
7630 mOuterWindow(aOuterWindow),
7631 mWindowID(0),
7632 mHasNotifiedGlobalCreated(false),
7633 mMarkedCCGeneration(0),
7634 mHasTriedToCacheTopInnerWindow(false),
7635 mNumOfIndexedDBDatabases(0),
7636 mNumOfOpenWebSockets(0),
7637 mEvent(nullptr),
7638 mStorageAccessPermissionGranted(false),
7639 mWindowGlobalChild(aActor),
7640 mWasSuspendedByGroup(false) {
7641 MOZ_ASSERT(aOuterWindow);
7642 mBrowsingContext = aOuterWindow->GetBrowsingContext();
7644 if (mWindowGlobalChild) {
7645 mWindowID = aActor->InnerWindowId();
7647 MOZ_ASSERT(mWindowGlobalChild->BrowsingContext() == mBrowsingContext);
7648 } else {
7649 mWindowID = nsContentUtils::GenerateWindowId();
7653 nsPIDOMWindowInner::~nsPIDOMWindowInner() = default;
7655 #undef FORWARD_TO_OUTER
7656 #undef FORWARD_TO_OUTER_OR_THROW
7657 #undef FORWARD_TO_OUTER_VOID