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/. */
6
7 #include "nsGlobalWindowInner.h"
8
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/ExtensionPolicyService.h"
58 #include "mozilla/FloatingPoint.h"
59 #include "mozilla/FlushType.h"
60 #include "mozilla/Likely.h"
61 #include "mozilla/LinkedList.h"
62 #include "mozilla/Logging.h"
63 #include "mozilla/MacroForEach.h"
64 #include "mozilla/Maybe.h"
65 #include "mozilla/OwningNonNull.h"
66 #include "mozilla/PermissionDelegateHandler.h"
67 #include "mozilla/Preferences.h"
68 #include "mozilla/PresShell.h"
69 #include "mozilla/ProcessHangMonitor.h"
70 #include "mozilla/RefPtr.h"
71 #include "mozilla/Result.h"
72 #include "mozilla/ScopeExit.h"
73 #include "mozilla/ScrollOrigin.h"
74 #include "mozilla/ScrollTypes.h"
75 #include "mozilla/Components.h"
76 #include "mozilla/SizeOfState.h"
77 #include "mozilla/Span.h"
78 #include "mozilla/SpinEventLoopUntil.h"
79 #include "mozilla/Sprintf.h"
80 #include "mozilla/StaticPrefs_browser.h"
81 #include "mozilla/StaticPrefs_dom.h"
82 #include "mozilla/StorageAccess.h"
83 #include "mozilla/TaskCategory.h"
84 #include "mozilla/Telemetry.h"
85 #include "mozilla/TelemetryHistogramEnums.h"
86 #include "mozilla/TimeStamp.h"
87 #include "mozilla/UniquePtr.h"
88 #include "mozilla/Unused.h"
89 #include "mozilla/dom/AudioContext.h"
90 #include "mozilla/dom/AutoEntryScript.h"
91 #include "mozilla/dom/BarProps.h"
92 #include "mozilla/dom/BindingDeclarations.h"
93 #include "mozilla/dom/BindingUtils.h"
94 #include "mozilla/dom/BrowserChild.h"
95 #include "mozilla/dom/BrowsingContext.h"
96 #include "mozilla/dom/CSPEvalChecker.h"
97 #include "mozilla/dom/CallbackDebuggerNotification.h"
98 #include "mozilla/dom/ChromeMessageBroadcaster.h"
99 #include "mozilla/dom/ClientInfo.h"
100 #include "mozilla/dom/ClientManager.h"
101 #include "mozilla/dom/ClientSource.h"
102 #include "mozilla/dom/ClientState.h"
103 #include "mozilla/dom/ClientsBinding.h"
104 #include "mozilla/dom/Console.h"
105 #include "mozilla/dom/ContentFrameMessageManager.h"
106 #include "mozilla/dom/ContentMediaController.h"
107 #include "mozilla/dom/CustomElementRegistry.h"
108 #include "mozilla/dom/DOMJSProxyHandler.h"
109 #include "mozilla/dom/DebuggerNotification.h"
110 #include "mozilla/dom/DebuggerNotificationBinding.h"
111 #include "mozilla/dom/DebuggerNotificationManager.h"
112 #include "mozilla/dom/DispatcherTrait.h"
113 #include "mozilla/dom/DocGroup.h"
114 #include "mozilla/dom/Document.h"
115 #include "mozilla/dom/DocumentInlines.h"
116 #include "mozilla/dom/Element.h"
117 #include "mozilla/dom/Event.h"
118 #include "mozilla/dom/EventTarget.h"
119 #include "mozilla/dom/External.h"
120 #include "mozilla/dom/Fetch.h"
121 #include "mozilla/dom/Gamepad.h"
122 #include "mozilla/dom/GamepadHandle.h"
123 #include "mozilla/dom/GamepadManager.h"
124 #include "mozilla/dom/HashChangeEvent.h"
125 #include "mozilla/dom/HashChangeEventBinding.h"
126 #include "mozilla/dom/IDBFactory.h"
127 #include "mozilla/dom/IdleRequest.h"
128 #include "mozilla/dom/ImageBitmap.h"
129 #include "mozilla/dom/ImageBitmapSource.h"
130 #include "mozilla/dom/InstallTriggerBinding.h"
131 #include "mozilla/dom/IntlUtils.h"
132 #include "mozilla/dom/JSExecutionContext.h"
133 #include "mozilla/dom/LSObject.h"
134 #include "mozilla/dom/LoadedScript.h"
135 #include "mozilla/dom/LocalStorage.h"
136 #include "mozilla/dom/LocalStorageCommon.h"
137 #include "mozilla/dom/Location.h"
138 #include "mozilla/dom/MediaKeys.h"
139 #include "mozilla/dom/NavigatorBinding.h"
140 #include "mozilla/dom/Nullable.h"
141 #include "mozilla/dom/PartitionedLocalStorage.h"
142 #include "mozilla/dom/Performance.h"
143 #include "mozilla/dom/PopStateEvent.h"
144 #include "mozilla/dom/PopStateEventBinding.h"
145 #include "mozilla/dom/PopupBlocker.h"
146 #include "mozilla/dom/PrimitiveConversions.h"
147 #include "mozilla/dom/Promise.h"
148 #include "mozilla/dom/RootedDictionary.h"
149 #include "mozilla/dom/ScriptLoader.h"
150 #include "mozilla/dom/ScriptSettings.h"
151 #include "mozilla/dom/ServiceWorker.h"
152 #include "mozilla/dom/ServiceWorkerDescriptor.h"
153 #include "mozilla/dom/ServiceWorkerRegistration.h"
154 #include "mozilla/dom/SessionStorageManager.h"
155 #include "mozilla/dom/SharedWorker.h"
156 #include "mozilla/dom/Storage.h"
157 #include "mozilla/dom/StorageEvent.h"
158 #include "mozilla/dom/StorageEventBinding.h"
159 #include "mozilla/dom/StorageNotifierService.h"
160 #include "mozilla/dom/StorageUtils.h"
161 #include "mozilla/dom/TabMessageTypes.h"
162 #include "mozilla/dom/Timeout.h"
163 #include "mozilla/dom/TimeoutHandler.h"
164 #include "mozilla/dom/TimeoutManager.h"
165 #include "mozilla/dom/ToJSValue.h"
166 #include "mozilla/dom/U2F.h"
167 #include "mozilla/dom/VRDisplay.h"
168 #include "mozilla/dom/VRDisplayEvent.h"
169 #include "mozilla/dom/VRDisplayEventBinding.h"
170 #include "mozilla/dom/VREventObserver.h"
171 #include "mozilla/dom/VisualViewport.h"
172 #include "mozilla/dom/WakeLock.h"
173 #include "mozilla/dom/WebIDLGlobalNameHash.h"
174 #include "mozilla/dom/WindowBinding.h"
175 #include "mozilla/dom/WindowContext.h"
176 #include "mozilla/dom/WindowGlobalChild.h"
177 #include "mozilla/dom/WindowProxyHolder.h"
178 #include "mozilla/dom/WorkerCommon.h"
179 #include "mozilla/dom/Worklet.h"
180 #include "mozilla/dom/XRPermissionRequest.h"
181 #include "mozilla/dom/cache/CacheStorage.h"
182 #include "mozilla/dom/cache/Types.h"
183 #include "mozilla/extensions/WebExtensionPolicy.h"
184 #include "mozilla/fallible.h"
185 #include "mozilla/gfx/BasePoint.h"
186 #include "mozilla/gfx/BaseRect.h"
187 #include "mozilla/gfx/BaseSize.h"
188 #include "mozilla/gfx/Rect.h"
189 #include "mozilla/gfx/Types.h"
190 #include "mozilla/intl/LocaleService.h"
191 #include "mozilla/net/CookieJarSettings.h"
192 #include "nsAtom.h"
193 #include "nsBaseHashtable.h"
194 #include "nsCCUncollectableMarker.h"
195 #include "nsCOMPtr.h"
196 #include "nsCRT.h"
197 #include "nsCRTGlue.h"
198 #include "nsCanvasFrame.h"
199 #include "nsCharTraits.h"
200 #include "nsCheapSets.h"
201 #include "nsContentUtils.h"
202 #include "nsCoord.h"
203 #include "nsCycleCollectionNoteChild.h"
204 #include "nsCycleCollectionTraversalCallback.h"
205 #include "nsDOMNavigationTiming.h"
206 #include "nsDOMOfflineResourceList.h"
207 #include "nsDebug.h"
208 #include "nsDocShell.h"
209 #include "nsFocusManager.h"
210 #include "nsFrameMessageManager.h"
211 #include "nsGkAtoms.h"
212 #include "nsGlobalWindowOuter.h"
213 #include "nsHashKeys.h"
214 #include "nsHistory.h"
215 #include "nsIAddonPolicyService.h"
216 #include "nsIArray.h"
217 #include "nsIBaseWindow.h"
218 #include "nsIBrowserChild.h"
219 #include "nsICancelableRunnable.h"
220 #include "nsIChannel.h"
221 #include "nsIContentSecurityPolicy.h"
222 #include "nsIControllers.h"
223 #include "nsICookieJarSettings.h"
224 #include "nsICookieService.h"
225 #include "nsID.h"
226 #include "nsIDOMStorageManager.h"
227 #include "nsIDeviceSensors.h"
228 #include "nsIDocShell.h"
229 #include "nsIDocShellTreeItem.h"
230 #include "nsIDocShellTreeOwner.h"
231 #include "nsIDocumentLoader.h"
232 #include "nsIDragService.h"
233 #include "nsIFocusManager.h"
234 #include "nsIFrame.h"
235 #include "nsIGlobalObject.h"
236 #include "nsIIOService.h"
237 #include "nsIIdleRunnable.h"
238 #include "nsIInterfaceRequestorUtils.h"
239 #include "nsILoadContext.h"
240 #include "nsILoadGroup.h"
241 #include "nsILoadInfo.h"
242 #include "nsINamed.h"
243 #include "nsINode.h"
244 #include "nsIObserver.h"
245 #include "nsIObserverService.h"
246 #include "nsIPermission.h"
247 #include "nsIPermissionManager.h"
248 #include "nsIPrefBranch.h"
249 #include "nsIPrincipal.h"
250 #include "nsIPrompt.h"
251 #include "nsIRunnable.h"
252 #include "nsIScreen.h"
253 #include "nsIScreenManager.h"
254 #include "nsIScriptContext.h"
255 #include "nsIScriptGlobalObject.h"
256 #include "nsIScriptObjectPrincipal.h"
257 #include "nsIScrollableFrame.h"
258 #include "nsISerialEventTarget.h"
259 #include "nsISimpleEnumerator.h"
260 #include "nsISizeOfEventTarget.h"
261 #include "nsISlowScriptDebug.h"
262 #include "nsISupportsUtils.h"
263 #include "nsIThread.h"
264 #include "nsITimedChannel.h"
265 #include "nsIURI.h"
266 #include "nsIVariant.h"
267 #include "nsIWeakReference.h"
268 #include "nsIWebBrowserChrome.h"
269 #include "nsIWebNavigation.h"
270 #include "nsIWebProgressListener.h"
271 #include "nsIWidget.h"
272 #include "nsIWidgetListener.h"
273 #include "nsIXULRuntime.h"
274 #include "nsJSPrincipals.h"
275 #include "nsJSUtils.h"
276 #include "nsLayoutStatics.h"
277 #include "nsLiteralString.h"
278 #include "nsNetUtil.h"
279 #include "nsPIDOMWindow.h"
280 #include "nsPIDOMWindowInlines.h"
281 #include "nsPIWindowRoot.h"
282 #include "nsPoint.h"
283 #include "nsPresContext.h"
284 #include "nsQueryObject.h"
285 #include "nsRefPtrHashtable.h"
286 #include "nsSandboxFlags.h"
287 #include "nsScreen.h"
288 #include "nsServiceManagerUtils.h"
289 #include "nsString.h"
290 #include "nsStringFlags.h"
291 #include "nsStringFwd.h"
292 #include "nsTArray.h"
293 #include "nsTLiteralString.h"
294 #include "nsTObserverArray.h"
295 #include "nsTStringRepr.h"
296 #include "nsThreadUtils.h"
297 #include "nsWeakReference.h"
298 #include "nsWindowMemoryReporter.h"
299 #include "nsWindowSizes.h"
300 #include "nsWrapperCache.h"
301 #include "nsWrapperCacheInlines.h"
302 #include "nsXULAppAPI.h"
303 #include "nsrootidl.h"
304 #include "prclist.h"
305 #include "prtypes.h"
306 #include "xpcprivate.h"
307 #include "xpcpublic.h"
308
309 #ifdef MOZ_XUL
310 # include "nsIDOMXULControlElement.h"
311 # include "nsMenuPopupFrame.h"
312 #endif
313
314 #ifdef NS_PRINTING
315 # include "nsIPrintSettings.h"
316 # include "nsIPrintSettingsService.h"
317 # include "nsIWebBrowserPrint.h"
318 #endif
319
320 #ifdef MOZ_WEBSPEECH
321 # include "mozilla/dom/SpeechSynthesis.h"
322 #endif
323
324 #ifdef ANDROID
325 # include <android/log.h>
326 #endif
327
328 #ifdef MOZ_WIDGET_ANDROID
329 # include "mozilla/dom/WindowOrientationObserver.h"
330 #endif
331
332 #ifdef XP_WIN
333 # include "mozilla/Debug.h"
334 # include <process.h>
335 # define getpid _getpid
336 #else
337 # include <unistd.h> // for getpid()
338 #endif
339
340 using namespace mozilla;
341 using namespace mozilla::dom;
342 using namespace mozilla::dom::ipc;
343 using mozilla::TimeDuration;
344 using mozilla::TimeStamp;
345 using mozilla::dom::GamepadHandle;
346 using mozilla::dom::cache::CacheStorage;
347
348 #define FORWARD_TO_OUTER(method, args, err_rval) \
349 PR_BEGIN_MACRO \
350 nsGlobalWindowOuter* outer = GetOuterWindowInternal(); \
351 if (!HasActiveDocument()) { \
352 NS_WARNING(outer ? "Inner window does not have active document." \
353 : "No outer window available!"); \
354 return err_rval; \
355 } \
356 return outer->method args; \
357 PR_END_MACRO
358
GetOuterWindowForForwarding(nsGlobalWindowInner * aInner,ErrorResult & aError)359 static nsGlobalWindowOuter* GetOuterWindowForForwarding(
360 nsGlobalWindowInner* aInner, ErrorResult& aError) {
361 nsGlobalWindowOuter* outer = aInner->GetOuterWindowInternal();
362 if (MOZ_LIKELY(aInner->HasActiveDocument())) {
363 return outer;
364 }
365 if (!outer) {
366 NS_WARNING("No outer window available!");
367 aError.Throw(NS_ERROR_NOT_INITIALIZED);
368 } else {
369 aError.Throw(NS_ERROR_XPC_SECURITY_MANAGER_VETO);
370 }
371 return nullptr;
372 }
373
374 #define FORWARD_TO_OUTER_OR_THROW(method, args, errorresult, err_rval) \
375 PR_BEGIN_MACRO \
376 nsGlobalWindowOuter* outer = GetOuterWindowForForwarding(this, errorresult); \
377 if (MOZ_LIKELY(outer)) { \
378 return outer->method args; \
379 } \
380 return err_rval; \
381 PR_END_MACRO
382
383 #define FORWARD_TO_OUTER_VOID(method, args) \
384 PR_BEGIN_MACRO \
385 nsGlobalWindowOuter* outer = GetOuterWindowInternal(); \
386 if (!HasActiveDocument()) { \
387 NS_WARNING(outer ? "Inner window does not have active document." \
388 : "No outer window available!"); \
389 return; \
390 } \
391 outer->method args; \
392 return; \
393 PR_END_MACRO
394
395 #define ENSURE_ACTIVE_DOCUMENT(errorresult, err_rval) \
396 PR_BEGIN_MACRO \
397 if (MOZ_UNLIKELY(!HasActiveDocument())) { \
398 aError.Throw(NS_ERROR_XPC_SECURITY_MANAGER_VETO); \
399 return err_rval; \
400 } \
401 PR_END_MACRO
402
403 #define DOM_TOUCH_LISTENER_ADDED "dom-touch-listener-added"
404 #define MEMORY_PRESSURE_OBSERVER_TOPIC "memory-pressure"
405 #define PERMISSION_CHANGED_TOPIC "perm-changed"
406
407 // Amount of time allowed between alert/prompt/confirm before enabling
408 // the stop dialog checkbox.
409 #define DEFAULT_SUCCESSIVE_DIALOG_TIME_LIMIT 3 // 3 sec
410
411 // Maximum number of successive dialogs before we prompt users to disable
412 // dialogs for this window.
413 #define MAX_SUCCESSIVE_DIALOG_COUNT 5
414
415 static LazyLogModule gDOMLeakPRLogInner("DOMLeakInner");
416 extern mozilla::LazyLogModule gTimeoutLog;
417
418 #ifdef DEBUG
419 static LazyLogModule gDocShellAndDOMWindowLeakLogging(
420 "DocShellAndDOMWindowLeak");
421 #endif
422
423 static FILE* gDumpFile = nullptr;
424
425 nsGlobalWindowInner::InnerWindowByIdTable*
426 nsGlobalWindowInner::sInnerWindowsById = nullptr;
427
428 bool nsGlobalWindowInner::sDragServiceDisabled = false;
429 bool nsGlobalWindowInner::sMouseDown = false;
430
431 /**
432 * An indirect observer object that means we don't have to implement nsIObserver
433 * on nsGlobalWindow, where any script could see it.
434 */
435 class nsGlobalWindowObserver final : public nsIObserver,
436 public nsIInterfaceRequestor,
437 public StorageNotificationObserver {
438 public:
nsGlobalWindowObserver(nsGlobalWindowInner * aWindow)439 explicit nsGlobalWindowObserver(nsGlobalWindowInner* aWindow)
440 : mWindow(aWindow) {}
441 NS_DECL_ISUPPORTS
Observe(nsISupports * aSubject,const char * aTopic,const char16_t * aData)442 NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
443 const char16_t* aData) override {
444 if (!mWindow) return NS_OK;
445 return mWindow->Observe(aSubject, aTopic, aData);
446 }
Forget()447 void Forget() { mWindow = nullptr; }
GetInterface(const nsIID & aIID,void ** aResult)448 NS_IMETHOD GetInterface(const nsIID& aIID, void** aResult) override {
449 if (mWindow && aIID.Equals(NS_GET_IID(nsIDOMWindow)) && mWindow) {
450 return mWindow->QueryInterface(aIID, aResult);
451 }
452 return NS_NOINTERFACE;
453 }
454
ObserveStorageNotification(StorageEvent * aEvent,const char16_t * aStorageType,bool aPrivateBrowsing)455 void ObserveStorageNotification(StorageEvent* aEvent,
456 const char16_t* aStorageType,
457 bool aPrivateBrowsing) override {
458 if (mWindow) {
459 mWindow->ObserveStorageNotification(aEvent, aStorageType,
460 aPrivateBrowsing);
461 }
462 }
463
GetEffectiveStoragePrincipal() const464 nsIPrincipal* GetEffectiveStoragePrincipal() const override {
465 return mWindow ? mWindow->GetEffectiveStoragePrincipal() : nullptr;
466 }
467
IsPrivateBrowsing() const468 bool IsPrivateBrowsing() const override {
469 return mWindow ? mWindow->IsPrivateBrowsing() : false;
470 }
471
GetEventTarget() const472 nsIEventTarget* GetEventTarget() const override {
473 return mWindow ? mWindow->EventTargetFor(TaskCategory::Other) : nullptr;
474 }
475
476 private:
477 ~nsGlobalWindowObserver() = default;
478
479 // This reference is non-owning and safe because it's cleared by
480 // nsGlobalWindowInner::FreeInnerObjects().
481 nsGlobalWindowInner* MOZ_NON_OWNING_REF mWindow;
482 };
483
484 NS_IMPL_ISUPPORTS(nsGlobalWindowObserver, nsIObserver, nsIInterfaceRequestor)
485
486 class IdleRequestExecutor;
487
488 class IdleRequestExecutorTimeoutHandler final : public TimeoutHandler {
489 public:
IdleRequestExecutorTimeoutHandler(IdleRequestExecutor * aExecutor)490 explicit IdleRequestExecutorTimeoutHandler(IdleRequestExecutor* aExecutor)
491 : mExecutor(aExecutor) {}
492
493 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
494 NS_DECL_CYCLE_COLLECTION_CLASS(IdleRequestExecutorTimeoutHandler)
495
496 bool Call(const char* /* unused */) override;
497
498 private:
499 ~IdleRequestExecutorTimeoutHandler() override = default;
500 RefPtr<IdleRequestExecutor> mExecutor;
501 };
502
503 NS_IMPL_CYCLE_COLLECTION(IdleRequestExecutorTimeoutHandler, mExecutor)
504
505 NS_IMPL_CYCLE_COLLECTING_ADDREF(IdleRequestExecutorTimeoutHandler)
506 NS_IMPL_CYCLE_COLLECTING_RELEASE(IdleRequestExecutorTimeoutHandler)
507
508 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestExecutorTimeoutHandler)
509 NS_INTERFACE_MAP_ENTRY(nsISupports)
510 NS_INTERFACE_MAP_END
511
512 class IdleRequestExecutor final : public nsIRunnable,
513 public nsICancelableRunnable,
514 public nsINamed,
515 public nsIIdleRunnable {
516 public:
IdleRequestExecutor(nsGlobalWindowInner * aWindow)517 explicit IdleRequestExecutor(nsGlobalWindowInner* aWindow)
518 : mDispatched(false), mDeadline(TimeStamp::Now()), mWindow(aWindow) {
519 MOZ_DIAGNOSTIC_ASSERT(mWindow);
520
521 mIdlePeriodLimit = {mDeadline, mWindow->LastIdleRequestHandle()};
522 mDelayedExecutorDispatcher = new IdleRequestExecutorTimeoutHandler(this);
523 }
524
525 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
526 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(IdleRequestExecutor, nsIRunnable)
527
528 NS_DECL_NSIRUNNABLE
529 NS_DECL_NSINAMED
530 nsresult Cancel() override;
531 void SetDeadline(TimeStamp aDeadline) override;
532
IsCancelled() const533 bool IsCancelled() const { return !mWindow || mWindow->IsDying(); }
534 // Checks if aRequest shouldn't execute in the current idle period
535 // since it has been queued from a chained call to
536 // requestIdleCallback from within a running idle callback.
IneligibleForCurrentIdlePeriod(IdleRequest * aRequest) const537 bool IneligibleForCurrentIdlePeriod(IdleRequest* aRequest) const {
538 return aRequest->Handle() >= mIdlePeriodLimit.mLastRequestIdInIdlePeriod &&
539 TimeStamp::Now() <= mIdlePeriodLimit.mEndOfIdlePeriod;
540 }
541
542 void MaybeUpdateIdlePeriodLimit();
543
544 // Maybe dispatch the IdleRequestExecutor. MabyeDispatch will
545 // schedule a delayed dispatch if the associated window is in the
546 // background or if given a time to wait until dispatching.
547 void MaybeDispatch(TimeStamp aDelayUntil = TimeStamp());
548 void ScheduleDispatch();
549
550 private:
551 struct IdlePeriodLimit {
552 TimeStamp mEndOfIdlePeriod;
553 uint32_t mLastRequestIdInIdlePeriod;
554 };
555
556 void DelayedDispatch(uint32_t aDelay);
557
558 ~IdleRequestExecutor() override = default;
559
560 bool mDispatched;
561 TimeStamp mDeadline;
562 IdlePeriodLimit mIdlePeriodLimit;
563 RefPtr<nsGlobalWindowInner> mWindow;
564 // The timeout handler responsible for dispatching this executor in
565 // the case of immediate dispatch to the idle queue isn't
566 // desirable. This is used if we've dispatched all idle callbacks
567 // that are allowed to run in the current idle period, or if the
568 // associated window is currently in the background.
569 RefPtr<TimeoutHandler> mDelayedExecutorDispatcher;
570 // If not Nothing() then this value is the handle to the currently
571 // scheduled delayed executor dispatcher. This is needed to be able
572 // to cancel the timeout handler in case of the executor being
573 // cancelled.
574 Maybe<int32_t> mDelayedExecutorHandle;
575 };
576
577 NS_IMPL_CYCLE_COLLECTION_CLASS(IdleRequestExecutor)
578
NS_IMPL_CYCLE_COLLECTING_ADDREF(IdleRequestExecutor)579 NS_IMPL_CYCLE_COLLECTING_ADDREF(IdleRequestExecutor)
580 NS_IMPL_CYCLE_COLLECTING_RELEASE(IdleRequestExecutor)
581
582 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IdleRequestExecutor)
583 NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
584 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDelayedExecutorDispatcher)
585 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
586
587 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IdleRequestExecutor)
588 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
589 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDelayedExecutorDispatcher)
590 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
591
592 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestExecutor)
593 NS_INTERFACE_MAP_ENTRY(nsIRunnable)
594 NS_INTERFACE_MAP_ENTRY(nsICancelableRunnable)
595 NS_INTERFACE_MAP_ENTRY(nsINamed)
596 NS_INTERFACE_MAP_ENTRY(nsIIdleRunnable)
597 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRunnable)
598 NS_INTERFACE_MAP_END
599
600 NS_IMETHODIMP
601 IdleRequestExecutor::GetName(nsACString& aName) {
602 aName.AssignLiteral("IdleRequestExecutor");
603 return NS_OK;
604 }
605
606 // MOZ_CAN_RUN_SCRIPT_BOUNDARY until nsIRunnable::Run is MOZ_CAN_RUN_SCRIPT.
607 // See bug 1535398.
Run()608 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP IdleRequestExecutor::Run() {
609 MOZ_ASSERT(NS_IsMainThread());
610
611 mDispatched = false;
612 if (mWindow) {
613 RefPtr<nsGlobalWindowInner> window(mWindow);
614 window->ExecuteIdleRequest(mDeadline);
615 }
616
617 return NS_OK;
618 }
619
Cancel()620 nsresult IdleRequestExecutor::Cancel() {
621 MOZ_ASSERT(NS_IsMainThread());
622
623 if (mDelayedExecutorHandle && mWindow) {
624 mWindow->TimeoutManager().ClearTimeout(
625 mDelayedExecutorHandle.value(), Timeout::Reason::eIdleCallbackTimeout);
626 }
627
628 mWindow = nullptr;
629 return NS_OK;
630 }
631
SetDeadline(TimeStamp aDeadline)632 void IdleRequestExecutor::SetDeadline(TimeStamp aDeadline) {
633 MOZ_ASSERT(NS_IsMainThread());
634
635 if (!mWindow) {
636 return;
637 }
638
639 mDeadline = aDeadline;
640 }
641
MaybeUpdateIdlePeriodLimit()642 void IdleRequestExecutor::MaybeUpdateIdlePeriodLimit() {
643 if (TimeStamp::Now() > mIdlePeriodLimit.mEndOfIdlePeriod) {
644 mIdlePeriodLimit = {mDeadline, mWindow->LastIdleRequestHandle()};
645 }
646 }
647
MaybeDispatch(TimeStamp aDelayUntil)648 void IdleRequestExecutor::MaybeDispatch(TimeStamp aDelayUntil) {
649 // If we've already dispatched the executor we don't want to do it
650 // again. Also, if we've called IdleRequestExecutor::Cancel mWindow
651 // will be null, which indicates that we shouldn't dispatch this
652 // executor either.
653 if (mDispatched || IsCancelled()) {
654 return;
655 }
656
657 mDispatched = true;
658
659 nsPIDOMWindowOuter* outer = mWindow->GetOuterWindow();
660 if (outer && outer->IsBackground()) {
661 // Set a timeout handler with a timeout of 0 ms to throttle idle
662 // callback requests coming from a backround window using
663 // background timeout throttling.
664 DelayedDispatch(0);
665 return;
666 }
667
668 TimeStamp now = TimeStamp::Now();
669 if (!aDelayUntil || aDelayUntil < now) {
670 ScheduleDispatch();
671 return;
672 }
673
674 TimeDuration delay = aDelayUntil - now;
675 DelayedDispatch(static_cast<uint32_t>(delay.ToMilliseconds()));
676 }
677
ScheduleDispatch()678 void IdleRequestExecutor::ScheduleDispatch() {
679 MOZ_ASSERT(mWindow);
680 mDelayedExecutorHandle = Nothing();
681 RefPtr<IdleRequestExecutor> request = this;
682 NS_DispatchToCurrentThreadQueue(request.forget(), EventQueuePriority::Idle);
683 }
684
DelayedDispatch(uint32_t aDelay)685 void IdleRequestExecutor::DelayedDispatch(uint32_t aDelay) {
686 MOZ_ASSERT(mWindow);
687 MOZ_ASSERT(mDelayedExecutorHandle.isNothing());
688 int32_t handle;
689 mWindow->TimeoutManager().SetTimeout(
690 mDelayedExecutorDispatcher, aDelay, false,
691 Timeout::Reason::eIdleCallbackTimeout, &handle);
692 mDelayedExecutorHandle = Some(handle);
693 }
694
Call(const char *)695 bool IdleRequestExecutorTimeoutHandler::Call(const char* /* unused */) {
696 if (!mExecutor->IsCancelled()) {
697 mExecutor->ScheduleDispatch();
698 }
699 return true;
700 }
701
ScheduleIdleRequestDispatch()702 void nsGlobalWindowInner::ScheduleIdleRequestDispatch() {
703 AssertIsOnMainThread();
704
705 if (!mIdleRequestExecutor) {
706 mIdleRequestExecutor = new IdleRequestExecutor(this);
707 }
708
709 mIdleRequestExecutor->MaybeDispatch();
710 }
711
SuspendIdleRequests()712 void nsGlobalWindowInner::SuspendIdleRequests() {
713 if (mIdleRequestExecutor) {
714 mIdleRequestExecutor->Cancel();
715 mIdleRequestExecutor = nullptr;
716 }
717 }
718
ResumeIdleRequests()719 void nsGlobalWindowInner::ResumeIdleRequests() {
720 MOZ_ASSERT(!mIdleRequestExecutor);
721
722 ScheduleIdleRequestDispatch();
723 }
724
RemoveIdleCallback(mozilla::dom::IdleRequest * aRequest)725 void nsGlobalWindowInner::RemoveIdleCallback(
726 mozilla::dom::IdleRequest* aRequest) {
727 AssertIsOnMainThread();
728
729 if (aRequest->HasTimeout()) {
730 mTimeoutManager->ClearTimeout(aRequest->GetTimeoutHandle(),
731 Timeout::Reason::eIdleCallbackTimeout);
732 }
733
734 aRequest->removeFrom(mIdleRequestCallbacks);
735 }
736
RunIdleRequest(IdleRequest * aRequest,DOMHighResTimeStamp aDeadline,bool aDidTimeout)737 void nsGlobalWindowInner::RunIdleRequest(IdleRequest* aRequest,
738 DOMHighResTimeStamp aDeadline,
739 bool aDidTimeout) {
740 AssertIsOnMainThread();
741 // XXXbz Do we still need this RefPtr? MOZ_CAN_RUN_SCRIPT should
742 // guarantee that caller is holding a strong ref on the stack.
743 RefPtr<IdleRequest> request(aRequest);
744 RemoveIdleCallback(request);
745 request->IdleRun(this, aDeadline, aDidTimeout);
746 }
747
ExecuteIdleRequest(TimeStamp aDeadline)748 void nsGlobalWindowInner::ExecuteIdleRequest(TimeStamp aDeadline) {
749 AssertIsOnMainThread();
750 RefPtr<IdleRequest> request = mIdleRequestCallbacks.getFirst();
751
752 if (!request) {
753 // There are no more idle requests, so stop scheduling idle
754 // request callbacks.
755 return;
756 }
757
758 // If the request that we're trying to execute has been queued
759 // during the current idle period, then dispatch it again at the end
760 // of the idle period.
761 if (mIdleRequestExecutor->IneligibleForCurrentIdlePeriod(request)) {
762 mIdleRequestExecutor->MaybeDispatch(aDeadline);
763 return;
764 }
765
766 DOMHighResTimeStamp deadline = 0.0;
767
768 if (Performance* perf = GetPerformance()) {
769 deadline = perf->GetDOMTiming()->TimeStampToDOMHighRes(aDeadline);
770 }
771
772 mIdleRequestExecutor->MaybeUpdateIdlePeriodLimit();
773 RunIdleRequest(request, deadline, false);
774
775 // Running the idle callback could've suspended the window, in which
776 // case mIdleRequestExecutor will be null.
777 if (mIdleRequestExecutor) {
778 mIdleRequestExecutor->MaybeDispatch();
779 }
780 }
781
782 class IdleRequestTimeoutHandler final : public TimeoutHandler {
783 public:
IdleRequestTimeoutHandler(JSContext * aCx,IdleRequest * aIdleRequest,nsPIDOMWindowInner * aWindow)784 IdleRequestTimeoutHandler(JSContext* aCx, IdleRequest* aIdleRequest,
785 nsPIDOMWindowInner* aWindow)
786 : TimeoutHandler(aCx), mIdleRequest(aIdleRequest), mWindow(aWindow) {}
787
788 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(IdleRequestTimeoutHandler)789 NS_DECL_CYCLE_COLLECTION_CLASS(IdleRequestTimeoutHandler)
790
791 MOZ_CAN_RUN_SCRIPT bool Call(const char* /* unused */) override {
792 RefPtr<nsGlobalWindowInner> window(nsGlobalWindowInner::Cast(mWindow));
793 RefPtr<IdleRequest> request(mIdleRequest);
794 window->RunIdleRequest(request, 0.0, true);
795 return true;
796 }
797
798 private:
799 ~IdleRequestTimeoutHandler() override = default;
800
801 RefPtr<IdleRequest> mIdleRequest;
802 nsCOMPtr<nsPIDOMWindowInner> mWindow;
803 };
804
NS_IMPL_CYCLE_COLLECTION(IdleRequestTimeoutHandler,mIdleRequest,mWindow)805 NS_IMPL_CYCLE_COLLECTION(IdleRequestTimeoutHandler, mIdleRequest, mWindow)
806
807 NS_IMPL_CYCLE_COLLECTING_ADDREF(IdleRequestTimeoutHandler)
808 NS_IMPL_CYCLE_COLLECTING_RELEASE(IdleRequestTimeoutHandler)
809
810 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestTimeoutHandler)
811 NS_INTERFACE_MAP_ENTRY(nsISupports)
812 NS_INTERFACE_MAP_END
813
814 uint32_t nsGlobalWindowInner::RequestIdleCallback(
815 JSContext* aCx, IdleRequestCallback& aCallback,
816 const IdleRequestOptions& aOptions, ErrorResult& aError) {
817 AssertIsOnMainThread();
818
819 if (IsDying()) {
820 return 0;
821 }
822
823 uint32_t handle = mIdleRequestCallbackCounter++;
824
825 RefPtr<IdleRequest> request = new IdleRequest(&aCallback, handle);
826
827 if (aOptions.mTimeout.WasPassed()) {
828 int32_t timeoutHandle;
829 RefPtr<TimeoutHandler> handler(
830 new IdleRequestTimeoutHandler(aCx, request, this));
831
832 nsresult rv = mTimeoutManager->SetTimeout(
833 handler, aOptions.mTimeout.Value(), false,
834 Timeout::Reason::eIdleCallbackTimeout, &timeoutHandle);
835
836 if (NS_WARN_IF(NS_FAILED(rv))) {
837 return 0;
838 }
839
840 request->SetTimeoutHandle(timeoutHandle);
841 }
842
843 mIdleRequestCallbacks.insertBack(request);
844
845 if (!IsSuspended()) {
846 ScheduleIdleRequestDispatch();
847 }
848
849 return handle;
850 }
851
CancelIdleCallback(uint32_t aHandle)852 void nsGlobalWindowInner::CancelIdleCallback(uint32_t aHandle) {
853 for (IdleRequest* r : mIdleRequestCallbacks) {
854 if (r->Handle() == aHandle) {
855 RemoveIdleCallback(r);
856 break;
857 }
858 }
859 }
860
DisableIdleCallbackRequests()861 void nsGlobalWindowInner::DisableIdleCallbackRequests() {
862 if (mIdleRequestExecutor) {
863 mIdleRequestExecutor->Cancel();
864 mIdleRequestExecutor = nullptr;
865 }
866
867 while (!mIdleRequestCallbacks.isEmpty()) {
868 RefPtr<IdleRequest> request = mIdleRequestCallbacks.getFirst();
869 RemoveIdleCallback(request);
870 }
871 }
872
IsBackgroundInternal() const873 bool nsGlobalWindowInner::IsBackgroundInternal() const {
874 return !mOuterWindow || mOuterWindow->IsBackground();
875 }
876
877 class PromiseDocumentFlushedResolver final {
878 public:
PromiseDocumentFlushedResolver(Promise * aPromise,PromiseDocumentFlushedCallback & aCallback)879 PromiseDocumentFlushedResolver(Promise* aPromise,
880 PromiseDocumentFlushedCallback& aCallback)
881 : mPromise(aPromise), mCallback(&aCallback) {}
882
883 virtual ~PromiseDocumentFlushedResolver() = default;
884
Call()885 void Call() {
886 nsMutationGuard guard;
887 ErrorResult error;
888 JS::Rooted<JS::Value> returnVal(RootingCx());
889 mCallback->Call(&returnVal, error);
890
891 if (error.Failed()) {
892 mPromise->MaybeReject(std::move(error));
893 } else if (guard.Mutated(0)) {
894 // Something within the callback mutated the DOM.
895 mPromise->MaybeReject(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
896 } else {
897 mPromise->MaybeResolve(returnVal);
898 }
899 }
900
901 RefPtr<Promise> mPromise;
902 RefPtr<PromiseDocumentFlushedCallback> mCallback;
903 };
904
905 //*****************************************************************************
906 //*** nsGlobalWindowInner: Object Management
907 //*****************************************************************************
908
nsGlobalWindowInner(nsGlobalWindowOuter * aOuterWindow,WindowGlobalChild * aActor)909 nsGlobalWindowInner::nsGlobalWindowInner(nsGlobalWindowOuter* aOuterWindow,
910 WindowGlobalChild* aActor)
911 : nsPIDOMWindowInner(aOuterWindow, aActor),
912 mWasOffline(false),
913 mHasHadSlowScript(false),
914 mIsChrome(false),
915 mCleanMessageManager(false),
916 mNeedsFocus(true),
917 mHasFocus(false),
918 mFocusByKeyOccurred(false),
919 mDidFireDocElemInserted(false),
920 mHasGamepad(false),
921 mHasXRSession(false),
922 mHasVRDisplayActivateEvents(false),
923 mXRRuntimeDetectionInFlight(false),
924 mXRPermissionRequestInFlight(false),
925 mXRPermissionGranted(false),
926 mWasCurrentInnerWindow(false),
927 mHasSeenGamepadInput(false),
928 mHintedWasLoading(false),
929 mHasOpenedExternalProtocolFrame(false),
930 mSuspendDepth(0),
931 mFreezeDepth(0),
932 #ifdef DEBUG
933 mSerial(0),
934 #endif
935 mFocusMethod(0),
936 mIdleRequestCallbackCounter(1),
937 mIdleRequestExecutor(nullptr),
938 mDialogAbuseCount(0),
939 mAreDialogsEnabled(true),
940 mObservingRefresh(false),
941 mIteratingDocumentFlushedResolvers(false),
942 mCanSkipCCGeneration(0) {
943 mIsInnerWindow = true;
944
945 AssertIsOnMainThread();
946 nsLayoutStatics::AddRef();
947
948 // Initialize the PRCList (this).
949 PR_INIT_CLIST(this);
950
951 // add this inner window to the outer window list of inners.
952 PR_INSERT_AFTER(this, aOuterWindow);
953
954 mTimeoutManager = MakeUnique<dom::TimeoutManager>(
955 *this, StaticPrefs::dom_timeout_max_idle_defer_ms());
956
957 mObserver = new nsGlobalWindowObserver(this);
958 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
959 if (os) {
960 // Watch for online/offline status changes so we can fire events. Use
961 // a strong reference.
962 os->AddObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC, false);
963 os->AddObserver(mObserver, MEMORY_PRESSURE_OBSERVER_TOPIC, false);
964 os->AddObserver(mObserver, PERMISSION_CHANGED_TOPIC, false);
965 }
966
967 Preferences::AddStrongObserver(mObserver, "intl.accept_languages");
968
969 // Watch for storage notifications so we can fire storage events.
970 RefPtr<StorageNotifierService> sns = StorageNotifierService::GetOrCreate();
971 if (sns) {
972 sns->Register(mObserver);
973 }
974
975 if (XRE_IsContentProcess()) {
976 nsCOMPtr<nsIDocShell> docShell = GetDocShell();
977 if (docShell) {
978 mBrowserChild = docShell->GetBrowserChild();
979 }
980 }
981
982 if (gDumpFile == nullptr) {
983 nsAutoCString fname;
984 Preferences::GetCString("browser.dom.window.dump.file", fname);
985 if (!fname.IsEmpty()) {
986 // If this fails to open, Dump() knows to just go to stdout on null.
987 gDumpFile = fopen(fname.get(), "wb+");
988 } else {
989 gDumpFile = stdout;
990 }
991 }
992
993 #ifdef DEBUG
994 mSerial = nsContentUtils::InnerOrOuterWindowCreated();
995
996 MOZ_LOG(gDocShellAndDOMWindowLeakLogging, LogLevel::Info,
997 ("++DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p]\n",
998 nsContentUtils::GetCurrentInnerOrOuterWindowCount(),
999 static_cast<void*>(ToCanonicalSupports(this)), getpid(), mSerial,
1000 static_cast<void*>(ToCanonicalSupports(aOuterWindow))));
1001 #endif
1002
1003 MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
1004 ("DOMWINDOW %p created outer=%p", this, aOuterWindow));
1005
1006 // Add ourselves to the inner windows list.
1007 MOZ_ASSERT(sInnerWindowsById, "Inner Windows hash table must be created!");
1008 MOZ_ASSERT(!sInnerWindowsById->Contains(mWindowID),
1009 "This window shouldn't be in the hash table yet!");
1010 // We seem to see crashes in release builds because of null
1011 // |sInnerWindowsById|.
1012 if (sInnerWindowsById) {
1013 sInnerWindowsById->InsertOrUpdate(mWindowID, this);
1014 }
1015 }
1016
1017 #ifdef DEBUG
1018
1019 /* static */
AssertIsOnMainThread()1020 void nsGlobalWindowInner::AssertIsOnMainThread() {
1021 MOZ_ASSERT(NS_IsMainThread());
1022 }
1023
1024 #endif // DEBUG
1025
1026 /* static */
Init()1027 void nsGlobalWindowInner::Init() {
1028 AssertIsOnMainThread();
1029
1030 NS_ASSERTION(gDOMLeakPRLogInner,
1031 "gDOMLeakPRLogInner should have been initialized!");
1032
1033 sInnerWindowsById = new InnerWindowByIdTable();
1034 }
1035
~nsGlobalWindowInner()1036 nsGlobalWindowInner::~nsGlobalWindowInner() {
1037 AssertIsOnMainThread();
1038 MOZ_ASSERT(!mHintedWasLoading);
1039
1040 if (IsChromeWindow()) {
1041 MOZ_ASSERT(mCleanMessageManager,
1042 "chrome windows may always disconnect the msg manager");
1043
1044 DisconnectAndClearGroupMessageManagers();
1045
1046 if (mChromeFields.mMessageManager) {
1047 static_cast<nsFrameMessageManager*>(mChromeFields.mMessageManager.get())
1048 ->Disconnect();
1049 }
1050
1051 mCleanMessageManager = false;
1052 }
1053
1054 // In most cases this should already have been called, but call it again
1055 // here to catch any corner cases.
1056 FreeInnerObjects();
1057
1058 if (sInnerWindowsById) {
1059 sInnerWindowsById->Remove(mWindowID);
1060 }
1061
1062 nsContentUtils::InnerOrOuterWindowDestroyed();
1063
1064 #ifdef DEBUG
1065 if (MOZ_LOG_TEST(gDocShellAndDOMWindowLeakLogging, LogLevel::Info)) {
1066 nsAutoCString url;
1067 if (mLastOpenedURI) {
1068 url = mLastOpenedURI->GetSpecOrDefault();
1069
1070 // Data URLs can be very long, so truncate to avoid flooding the log.
1071 const uint32_t maxURLLength = 1000;
1072 if (url.Length() > maxURLLength) {
1073 url.Truncate(maxURLLength);
1074 }
1075 }
1076
1077 nsGlobalWindowOuter* outer = nsGlobalWindowOuter::Cast(mOuterWindow);
1078 MOZ_LOG(
1079 gDocShellAndDOMWindowLeakLogging, LogLevel::Info,
1080 ("--DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p] [url = "
1081 "%s]\n",
1082 nsContentUtils::GetCurrentInnerOrOuterWindowCount(),
1083 static_cast<void*>(ToCanonicalSupports(this)), getpid(), mSerial,
1084 static_cast<void*>(ToCanonicalSupports(outer)), url.get()));
1085 }
1086 #endif
1087 MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
1088 ("DOMWINDOW %p destroyed", this));
1089
1090 Telemetry::Accumulate(Telemetry::INNERWINDOWS_WITH_MUTATION_LISTENERS,
1091 mMutationBits ? 1 : 0);
1092
1093 // An inner window is destroyed, pull it out of the outer window's
1094 // list if inner windows.
1095
1096 PR_REMOVE_LINK(this);
1097
1098 // If our outer window's inner window is this window, null out the
1099 // outer window's reference to this window that's being deleted.
1100 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
1101 if (outer) {
1102 outer->MaybeClearInnerWindow(this);
1103 }
1104
1105 // We don't have to leave the tab group if we are an inner window.
1106
1107 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
1108 if (ac) ac->RemoveWindowAsListener(this);
1109
1110 nsLayoutStatics::Release();
1111 }
1112
1113 // static
ShutDown()1114 void nsGlobalWindowInner::ShutDown() {
1115 AssertIsOnMainThread();
1116
1117 if (gDumpFile && gDumpFile != stdout) {
1118 fclose(gDumpFile);
1119 }
1120 gDumpFile = nullptr;
1121
1122 delete sInnerWindowsById;
1123 sInnerWindowsById = nullptr;
1124 }
1125
FreeInnerObjects()1126 void nsGlobalWindowInner::FreeInnerObjects() {
1127 if (IsDying()) {
1128 return;
1129 }
1130 StartDying();
1131
1132 if (mDoc && mDoc->GetWindowContext()) {
1133 // The document is about to lose its window, so this is a good time to send
1134 // our page use counters.
1135 //
1136 // (We also do this in Document::SetScriptGlobalObject(nullptr), which
1137 // catches most cases of documents losing their window, but not all.)
1138 mDoc->SendPageUseCounters();
1139 }
1140
1141 // Make sure that this is called before we null out the document and
1142 // other members that the window destroyed observers could
1143 // re-create.
1144 NotifyDOMWindowDestroyed(this);
1145 if (auto* reporter = nsWindowMemoryReporter::Get()) {
1146 reporter->ObserveDOMWindowDetached(this);
1147 }
1148
1149 // Kill all of the workers for this window.
1150 CancelWorkersForWindow(*this);
1151
1152 for (RefPtr<mozilla::dom::SharedWorker> pinnedWorker :
1153 mSharedWorkers.ForwardRange()) {
1154 pinnedWorker->Close();
1155 }
1156
1157 if (mTimeoutManager) {
1158 mTimeoutManager->ClearAllTimeouts();
1159 }
1160
1161 DisableIdleCallbackRequests();
1162
1163 mChromeEventHandler = nullptr;
1164
1165 if (mListenerManager) {
1166 mListenerManager->RemoveAllListeners();
1167 mListenerManager->Disconnect();
1168 mListenerManager = nullptr;
1169 }
1170
1171 mHistory = nullptr;
1172
1173 if (mNavigator) {
1174 mNavigator->OnNavigation();
1175 mNavigator->Invalidate();
1176 mNavigator = nullptr;
1177 }
1178
1179 mScreen = nullptr;
1180
1181 #if defined(MOZ_WIDGET_ANDROID)
1182 mOrientationChangeObserver = nullptr;
1183 #endif
1184
1185 if (mDoc) {
1186 // Remember the document's principal, URI, and CSP.
1187 mDocumentPrincipal = mDoc->NodePrincipal();
1188 mDocumentStoragePrincipal = mDoc->EffectiveStoragePrincipal();
1189 mDocumentPartitionedPrincipal = mDoc->PartitionedPrincipal();
1190 mDocumentURI = mDoc->GetDocumentURI();
1191 mDocBaseURI = mDoc->GetDocBaseURI();
1192 mDocumentCsp = mDoc->GetCsp();
1193
1194 while (mDoc->EventHandlingSuppressed()) {
1195 mDoc->UnsuppressEventHandlingAndFireEvents(false);
1196 }
1197 }
1198
1199 // Remove our reference to the document and the document principal.
1200 mFocusedElement = nullptr;
1201
1202 if (mApplicationCache) {
1203 static_cast<nsDOMOfflineResourceList*>(mApplicationCache.get())
1204 ->Disconnect();
1205 mApplicationCache = nullptr;
1206 }
1207
1208 if (mIndexedDB) {
1209 mIndexedDB->DisconnectFromGlobal(this);
1210 mIndexedDB = nullptr;
1211 }
1212
1213 nsIGlobalObject::UnlinkObjectsInGlobal();
1214
1215 NotifyWindowIDDestroyed("inner-window-destroyed");
1216
1217 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
1218 mAudioContexts[i]->OnWindowDestroy();
1219 }
1220 mAudioContexts.Clear();
1221
1222 for (MediaKeys* mediaKeys : mMediaKeysInstances) {
1223 mediaKeys->OnInnerWindowDestroy();
1224 }
1225 mMediaKeysInstances.Clear();
1226
1227 DisableGamepadUpdates();
1228 mHasGamepad = false;
1229 mGamepads.Clear();
1230 DisableVRUpdates();
1231 mHasXRSession = false;
1232 mHasVRDisplayActivateEvents = false;
1233 mXRRuntimeDetectionInFlight = false;
1234 mXRPermissionRequestInFlight = false;
1235 mXRPermissionGranted = false;
1236 mVRDisplays.Clear();
1237
1238 // This breaks a cycle between the window and the ClientSource object.
1239 mClientSource.reset();
1240
1241 if (mWindowGlobalChild) {
1242 // Remove any remaining listeners.
1243 int64_t nListeners = mWindowGlobalChild->BeforeUnloadListeners();
1244 for (int64_t i = 0; i < nListeners; ++i) {
1245 mWindowGlobalChild->BeforeUnloadRemoved();
1246 }
1247 MOZ_ASSERT(mWindowGlobalChild->BeforeUnloadListeners() == 0);
1248 }
1249
1250 // If we have any promiseDocumentFlushed callbacks, fire them now so
1251 // that the Promises can resolve.
1252 CallDocumentFlushedResolvers(/* aUntilExhaustion = */ true);
1253
1254 DisconnectEventTargetObjects();
1255
1256 if (mObserver) {
1257 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
1258 if (os) {
1259 os->RemoveObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC);
1260 os->RemoveObserver(mObserver, MEMORY_PRESSURE_OBSERVER_TOPIC);
1261 os->RemoveObserver(mObserver, PERMISSION_CHANGED_TOPIC);
1262 }
1263
1264 RefPtr<StorageNotifierService> sns = StorageNotifierService::GetOrCreate();
1265 if (sns) {
1266 sns->Unregister(mObserver);
1267 }
1268
1269 Preferences::RemoveObserver(mObserver, "intl.accept_languages");
1270
1271 // Drop its reference to this dying window, in case for some bogus reason
1272 // the object stays around.
1273 mObserver->Forget();
1274 }
1275
1276 mMenubar = nullptr;
1277 mToolbar = nullptr;
1278 mLocationbar = nullptr;
1279 mPersonalbar = nullptr;
1280 mStatusbar = nullptr;
1281 mScrollbars = nullptr;
1282
1283 mConsole = nullptr;
1284
1285 mPaintWorklet = nullptr;
1286
1287 mExternal = nullptr;
1288 mInstallTrigger = nullptr;
1289
1290 if (mLocalStorage) {
1291 mLocalStorage->Disconnect();
1292 mLocalStorage = nullptr;
1293 }
1294 mSessionStorage = nullptr;
1295 mPerformance = nullptr;
1296
1297 mContentMediaController = nullptr;
1298
1299 mSharedWorkers.Clear();
1300
1301 #ifdef MOZ_WEBSPEECH
1302 mSpeechSynthesis = nullptr;
1303 #endif
1304
1305 mGlean = nullptr;
1306 mGleanPings = nullptr;
1307
1308 mParentTarget = nullptr;
1309
1310 if (mCleanMessageManager) {
1311 MOZ_ASSERT(mIsChrome, "only chrome should have msg manager cleaned");
1312 if (mChromeFields.mMessageManager) {
1313 mChromeFields.mMessageManager->Disconnect();
1314 }
1315 }
1316
1317 if (mWindowGlobalChild && !mWindowGlobalChild->IsClosed()) {
1318 mWindowGlobalChild->Destroy();
1319 }
1320 mWindowGlobalChild = nullptr;
1321
1322 mIntlUtils = nullptr;
1323
1324 HintIsLoading(false);
1325 }
1326
1327 //*****************************************************************************
1328 // nsGlobalWindowInner::nsISupports
1329 //*****************************************************************************
1330
1331 // QueryInterface implementation for nsGlobalWindowInner
1332 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGlobalWindowInner)
1333 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
1334 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, EventTarget)
1335 NS_INTERFACE_MAP_ENTRY(nsIDOMWindow)
1336 NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
1337 NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObject)
1338 NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
1339 NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget)
1340 NS_INTERFACE_MAP_ENTRY(nsPIDOMWindowInner)
1341 NS_INTERFACE_MAP_ENTRY(mozIDOMWindow)
1342 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDOMChromeWindow, IsChromeWindow())
1343 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
1344 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
1345 NS_INTERFACE_MAP_END
1346
1347 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGlobalWindowInner)
1348 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGlobalWindowInner)
1349
1350 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGlobalWindowInner)
1351 if (tmp->IsBlackForCC(false)) {
1352 if (nsCCUncollectableMarker::InGeneration(tmp->mCanSkipCCGeneration)) {
1353 return true;
1354 }
1355 tmp->mCanSkipCCGeneration = nsCCUncollectableMarker::sGeneration;
1356 if (EventListenerManager* elm = tmp->GetExistingListenerManager()) {
1357 elm->MarkForCC();
1358 }
1359 if (tmp->mTimeoutManager) {
1360 tmp->mTimeoutManager->UnmarkGrayTimers();
1361 }
1362 return true;
1363 }
1364 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
1365
1366 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsGlobalWindowInner)
1367 return tmp->IsBlackForCC(true);
1368 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
1369
1370 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsGlobalWindowInner)
1371 return tmp->IsBlackForCC(false);
1372 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
1373
1374 NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalWindowInner)
1375
1376 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindowInner)
1377 if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
1378 char name[512];
1379 nsAutoCString uri;
1380 if (tmp->mDoc && tmp->mDoc->GetDocumentURI()) {
1381 uri = tmp->mDoc->GetDocumentURI()->GetSpecOrDefault();
1382 }
1383 SprintfLiteral(name, "nsGlobalWindowInner # %" PRIu64 " inner %s",
1384 tmp->mWindowID, uri.get());
1385 cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
1386 } else {
1387 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsGlobalWindowInner, tmp->mRefCnt.get())
1388 }
1389
1390 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNavigator)
1391
1392 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance)
1393
1394 #ifdef MOZ_WEBSPEECH
1395 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSpeechSynthesis)
1396 #endif
1397
1398 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlean)
1399 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGleanPings)
1400
1401 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOuterWindow)
1402
1403 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTopInnerWindow)
1404
1405 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager)
1406
1407 if (tmp->mTimeoutManager) {
__anonc51ae67b0102(Timeout* timeout) 1408 tmp->mTimeoutManager->ForEachUnorderedTimeout([&cb](Timeout* timeout) {
1409 cb.NoteNativeChild(timeout, NS_CYCLE_COLLECTION_PARTICIPANT(Timeout));
1410 });
1411 }
1412
1413 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocation)
1414 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHistory)
1415 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCustomElements)
1416 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSharedWorkers)
1417 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStorage)
1418 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSessionStorage)
1419 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mApplicationCache)
1420 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndexedDB)
1421 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPrincipal)
1422 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentStoragePrincipal)
1423 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPartitionedPrincipal)
1424 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentCsp)
1425 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowserChild)
1426 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDoc)
1427
1428 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleRequestExecutor)
1429 for (IdleRequest* request : tmp->mIdleRequestCallbacks) {
1430 cb.NoteNativeChild(request, NS_CYCLE_COLLECTION_PARTICIPANT(IdleRequest));
1431 }
1432
1433 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mClientSource)
1434
1435 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGamepads)
1436
1437 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCacheStorage)
1438 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVRDisplays)
1439
1440 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDebuggerNotificationManager)
1441
1442 // Traverse stuff from nsPIDOMWindow
1443 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeEventHandler)
1444 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentTarget)
1445 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFocusedElement)
1446 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowsingContext)
1447 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindowGlobalChild)
1448
1449 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMenubar)
1450 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mToolbar)
1451 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocationbar)
1452 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPersonalbar)
1453 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStatusbar)
1454 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScrollbars)
1455 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCrypto)
1456 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mU2F)
1457 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsole)
1458 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPaintWorklet)
1459 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExternal)
1460 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInstallTrigger)
1461 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIntlUtils)
1462
1463 tmp->TraverseObjectsInGlobal(cb);
1464
1465 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeFields.mMessageManager)
1466 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeFields.mGroupMessageManagers)
1467
1468 for (size_t i = 0; i < tmp->mDocumentFlushedResolvers.Length(); i++) {
1469 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentFlushedResolvers[i]->mPromise);
1470 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentFlushedResolvers[i]->mCallback);
1471 }
1472
1473 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1474
1475 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindowInner)
1476 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
1477 if (sInnerWindowsById) {
1478 sInnerWindowsById->Remove(tmp->mWindowID);
1479 }
1480
1481 JSObject* wrapper = tmp->GetWrapperPreserveColor();
1482 if (wrapper) {
1483 // Mark our realm as dead, so the JS engine won't hand out our
1484 // global after this point.
1485 JS::SetRealmNonLive(js::GetNonCCWObjectRealm(wrapper));
1486 }
1487
1488 NS_IMPL_CYCLE_COLLECTION_UNLINK(mNavigator)
1489
1490 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance)
1491
1492 #ifdef MOZ_WEBSPEECH
1493 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSpeechSynthesis)
1494 #endif
1495
1496 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlean)
1497 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGleanPings)
1498
1499 if (tmp->mOuterWindow) {
1500 nsGlobalWindowOuter::Cast(tmp->mOuterWindow)->MaybeClearInnerWindow(tmp);
1501 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOuterWindow)
1502 }
1503
1504 if (tmp->mListenerManager) {
1505 tmp->mListenerManager->Disconnect();
1506 NS_IMPL_CYCLE_COLLECTION_UNLINK(mListenerManager)
1507 }
1508
1509 // Here the Timeouts list would've been unlinked, but we rely on
1510 // that Timeout objects have been traced and will remove themselves
1511 // while unlinking.
1512
1513 tmp->UpdateTopInnerWindow();
1514 NS_IMPL_CYCLE_COLLECTION_UNLINK(mTopInnerWindow)
1515
1516 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocation)
1517 NS_IMPL_CYCLE_COLLECTION_UNLINK(mHistory)
1518 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCustomElements)
1519 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSharedWorkers)
1520 if (tmp->mLocalStorage) {
1521 tmp->mLocalStorage->Disconnect();
1522 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalStorage)
1523 }
1524 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSessionStorage)
1525 if (tmp->mApplicationCache) {
1526 static_cast<nsDOMOfflineResourceList*>(tmp->mApplicationCache.get())
1527 ->Disconnect();
1528 NS_IMPL_CYCLE_COLLECTION_UNLINK(mApplicationCache)
1529 }
1530 if (tmp->mIndexedDB) {
1531 tmp->mIndexedDB->DisconnectFromGlobal(tmp);
1532 NS_IMPL_CYCLE_COLLECTION_UNLINK(mIndexedDB)
1533 }
1534 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPrincipal)
1535 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentStoragePrincipal)
1536 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPartitionedPrincipal)
1537 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentCsp)
1538 NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowserChild)
1539 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDoc)
1540
1541 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGamepads)
1542
1543 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCacheStorage)
1544 NS_IMPL_CYCLE_COLLECTION_UNLINK(mVRDisplays)
1545
1546 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDebuggerNotificationManager)
1547
1548 // Unlink stuff from nsPIDOMWindow
1549 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeEventHandler)
1550 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParentTarget)
1551 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFocusedElement)
1552 NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowsingContext)
1553
1554 MOZ_DIAGNOSTIC_ASSERT(
1555 !tmp->mWindowGlobalChild || tmp->mWindowGlobalChild->IsClosed(),
1556 "How are we unlinking a window before its actor has been destroyed?");
1557 NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindowGlobalChild)
1558
1559 NS_IMPL_CYCLE_COLLECTION_UNLINK(mMenubar)
1560 NS_IMPL_CYCLE_COLLECTION_UNLINK(mToolbar)
1561 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocationbar)
1562 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPersonalbar)
1563 NS_IMPL_CYCLE_COLLECTION_UNLINK(mStatusbar)
1564 NS_IMPL_CYCLE_COLLECTION_UNLINK(mScrollbars)
1565 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCrypto)
1566 NS_IMPL_CYCLE_COLLECTION_UNLINK(mU2F)
1567 NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole)
1568 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPaintWorklet)
1569 NS_IMPL_CYCLE_COLLECTION_UNLINK(mExternal)
1570 NS_IMPL_CYCLE_COLLECTION_UNLINK(mInstallTrigger)
1571 NS_IMPL_CYCLE_COLLECTION_UNLINK(mIntlUtils)
1572
1573 tmp->UnlinkObjectsInGlobal();
1574
1575 NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleRequestExecutor)
1576
1577 // Here the IdleRequest list would've been unlinked, but we rely on
1578 // that IdleRequest objects have been traced and will remove
1579 // themselves while unlinking.
1580
1581 NS_IMPL_CYCLE_COLLECTION_UNLINK(mClientSource)
1582
1583 if (tmp->IsChromeWindow()) {
1584 if (tmp->mChromeFields.mMessageManager) {
1585 static_cast<nsFrameMessageManager*>(
1586 tmp->mChromeFields.mMessageManager.get())
1587 ->Disconnect();
1588 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeFields.mMessageManager)
1589 }
1590 tmp->DisconnectAndClearGroupMessageManagers();
1591 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeFields.mGroupMessageManagers)
1592 }
1593
1594 for (size_t i = 0; i < tmp->mDocumentFlushedResolvers.Length(); i++) {
1595 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentFlushedResolvers[i]->mPromise);
1596 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentFlushedResolvers[i]->mCallback);
1597 }
1598 tmp->mDocumentFlushedResolvers.Clear();
1599
1600 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
1601 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1602
1603 #ifdef DEBUG
RiskyUnlink()1604 void nsGlobalWindowInner::RiskyUnlink() {
1605 NS_CYCLE_COLLECTION_INNERNAME.Unlink(this);
1606 }
1607 #endif
1608
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGlobalWindowInner)1609 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGlobalWindowInner)
1610 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
1611 NS_IMPL_CYCLE_COLLECTION_TRACE_END
1612
1613 bool nsGlobalWindowInner::IsBlackForCC(bool aTracingNeeded) {
1614 if (!nsCCUncollectableMarker::sGeneration) {
1615 return false;
1616 }
1617
1618 return (nsCCUncollectableMarker::InGeneration(GetMarkedCCGeneration()) ||
1619 HasKnownLiveWrapper()) &&
1620 (!aTracingNeeded || HasNothingToTrace(ToSupports(this)));
1621 }
1622
1623 //*****************************************************************************
1624 // nsGlobalWindowInner::nsIScriptGlobalObject
1625 //*****************************************************************************
1626
EnsureScriptEnvironment()1627 nsresult nsGlobalWindowInner::EnsureScriptEnvironment() {
1628 // NOTE: We can't use FORWARD_TO_OUTER here because we don't want to fail if
1629 // we're called on an inactive inner window.
1630 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
1631 if (!outer) {
1632 NS_WARNING("No outer window available!");
1633 return NS_ERROR_FAILURE;
1634 }
1635 return outer->EnsureScriptEnvironment();
1636 }
1637
GetScriptContext()1638 nsIScriptContext* nsGlobalWindowInner::GetScriptContext() {
1639 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
1640 if (!outer) {
1641 return nullptr;
1642 }
1643 return outer->GetScriptContext();
1644 }
1645
TraceGlobalJSObject(JSTracer * aTrc)1646 void nsGlobalWindowInner::TraceGlobalJSObject(JSTracer* aTrc) {
1647 TraceWrapper(aTrc, "active window global");
1648 }
1649
UpdateAutoplayPermission()1650 void nsGlobalWindowInner::UpdateAutoplayPermission() {
1651 if (!GetWindowContext()) {
1652 return;
1653 }
1654 uint32_t perm = AutoplayPolicy::GetSiteAutoplayPermission(GetPrincipal());
1655 if (GetWindowContext()->GetAutoplayPermission() == perm) {
1656 return;
1657 }
1658
1659 // Setting autoplay permission on a discarded context has no effect.
1660 Unused << GetWindowContext()->SetAutoplayPermission(perm);
1661 }
1662
UpdateShortcutsPermission()1663 void nsGlobalWindowInner::UpdateShortcutsPermission() {
1664 if (!GetWindowContext() ||
1665 !GetWindowContext()->GetBrowsingContext()->IsTop()) {
1666 // We only cache the shortcuts permission on top-level WindowContexts
1667 // since we always check the top-level principal for the permission.
1668 return;
1669 }
1670
1671 uint32_t perm = GetShortcutsPermission(GetPrincipal());
1672
1673 if (GetWindowContext()->GetShortcutsPermission() == perm) {
1674 return;
1675 }
1676
1677 // If the WindowContext is discarded this has no effect.
1678 Unused << GetWindowContext()->SetShortcutsPermission(perm);
1679 }
1680
1681 /* static */
GetShortcutsPermission(nsIPrincipal * aPrincipal)1682 uint32_t nsGlobalWindowInner::GetShortcutsPermission(nsIPrincipal* aPrincipal) {
1683 uint32_t perm = nsIPermissionManager::DENY_ACTION;
1684 nsCOMPtr<nsIPermissionManager> permMgr =
1685 mozilla::components::PermissionManager::Service();
1686 if (aPrincipal && permMgr) {
1687 permMgr->TestExactPermissionFromPrincipal(aPrincipal, "shortcuts"_ns,
1688 &perm);
1689 }
1690 return perm;
1691 }
1692
UpdatePopupPermission()1693 void nsGlobalWindowInner::UpdatePopupPermission() {
1694 if (!GetWindowContext()) {
1695 return;
1696 }
1697
1698 uint32_t perm = PopupBlocker::GetPopupPermission(GetPrincipal());
1699 if (GetWindowContext()->GetPopupPermission() == perm) {
1700 return;
1701 }
1702
1703 // If the WindowContext is discarded this has no effect.
1704 Unused << GetWindowContext()->SetPopupPermission(perm);
1705 }
1706
UpdatePermissions()1707 void nsGlobalWindowInner::UpdatePermissions() {
1708 if (!GetWindowContext()) {
1709 return;
1710 }
1711
1712 nsCOMPtr<nsIPrincipal> principal = GetPrincipal();
1713 RefPtr<WindowContext> windowContext = GetWindowContext();
1714
1715 WindowContext::Transaction txn;
1716 txn.SetAutoplayPermission(
1717 AutoplayPolicy::GetSiteAutoplayPermission(principal));
1718 txn.SetPopupPermission(PopupBlocker::GetPopupPermission(principal));
1719
1720 if (windowContext->IsTop()) {
1721 txn.SetShortcutsPermission(GetShortcutsPermission(principal));
1722 }
1723
1724 // Setting permissions on a discarded WindowContext has no effect
1725 Unused << txn.Commit(windowContext);
1726 }
1727
InitDocumentDependentState(JSContext * aCx)1728 void nsGlobalWindowInner::InitDocumentDependentState(JSContext* aCx) {
1729 MOZ_ASSERT(mDoc);
1730
1731 if (MOZ_LOG_TEST(gDOMLeakPRLogInner, LogLevel::Debug)) {
1732 nsIURI* uri = mDoc->GetDocumentURI();
1733 MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
1734 ("DOMWINDOW %p SetNewDocument %s", this,
1735 uri ? uri->GetSpecOrDefault().get() : ""));
1736 }
1737
1738 mFocusedElement = nullptr;
1739 mLocalStorage = nullptr;
1740 mSessionStorage = nullptr;
1741 mPerformance = nullptr;
1742
1743 // This must be called after nullifying the internal objects because here we
1744 // could recreate them, calling the getter methods, and store them into the JS
1745 // slots. If we nullify them after, the slot values and the objects will be
1746 // out of sync.
1747 ClearDocumentDependentSlots(aCx);
1748
1749 if (!mWindowGlobalChild) {
1750 mWindowGlobalChild = WindowGlobalChild::Create(this);
1751 }
1752 MOZ_ASSERT(!GetWindowContext()->HasBeenUserGestureActivated(),
1753 "WindowContext should always not have user gesture activation at "
1754 "this point.");
1755
1756 UpdatePermissions();
1757
1758 RefPtr<PermissionDelegateHandler> permDelegateHandler =
1759 mDoc->GetPermissionDelegateHandler();
1760
1761 if (permDelegateHandler) {
1762 permDelegateHandler->PopulateAllDelegatedPermissions();
1763 }
1764
1765 #if defined(MOZ_WIDGET_ANDROID)
1766 // When we insert the new document to the window in the top-level browsing
1767 // context, we should reset the status of the request which is used for the
1768 // previous document.
1769 if (mWindowGlobalChild && GetBrowsingContext() &&
1770 !GetBrowsingContext()->GetParent()) {
1771 // Return value of setting synced field should be checked. See bug 1656492.
1772 Unused << GetBrowsingContext()->ResetGVAutoplayRequestStatus();
1773 }
1774 #endif
1775
1776 #ifdef DEBUG
1777 mLastOpenedURI = mDoc->GetDocumentURI();
1778 #endif
1779
1780 Telemetry::Accumulate(Telemetry::INNERWINDOWS_WITH_MUTATION_LISTENERS,
1781 mMutationBits ? 1 : 0);
1782
1783 // Clear our mutation bitfield.
1784 mMutationBits = 0;
1785 }
1786
EnsureClientSource()1787 nsresult nsGlobalWindowInner::EnsureClientSource() {
1788 MOZ_DIAGNOSTIC_ASSERT(mDoc);
1789
1790 bool newClientSource = false;
1791
1792 // Get the load info for the document if we performed a load. Be careful not
1793 // to look at local URLs, though. Local URLs are those that have a scheme of:
1794 // * about:
1795 // * data:
1796 // * blob:
1797 // We also do an additional check here so that we only treat about:blank
1798 // and about:srcdoc as local URLs. Other internal firefox about: URLs should
1799 // not be treated this way.
1800 nsCOMPtr<nsILoadInfo> loadInfo;
1801 nsCOMPtr<nsIChannel> channel = mDoc->GetChannel();
1802 if (channel) {
1803 nsCOMPtr<nsIURI> uri;
1804 Unused << channel->GetURI(getter_AddRefs(uri));
1805
1806 bool ignoreLoadInfo = false;
1807
1808 // Note, this is mostly copied from NS_IsAboutBlank(). Its duplicated
1809 // here so we can efficiently check about:srcdoc as well.
1810 if (uri->SchemeIs("about")) {
1811 nsCString spec = uri->GetSpecOrDefault();
1812 ignoreLoadInfo = spec.EqualsLiteral("about:blank") ||
1813 spec.EqualsLiteral("about:srcdoc");
1814 } else {
1815 // Its not an about: URL, so now check for our other URL types.
1816 ignoreLoadInfo = uri->SchemeIs("data") || uri->SchemeIs("blob");
1817 }
1818
1819 if (!ignoreLoadInfo) {
1820 loadInfo = channel->LoadInfo();
1821 }
1822 }
1823
1824 // Take the initial client source from the docshell immediately. Even if we
1825 // don't end up using it here we should consume it.
1826 UniquePtr<ClientSource> initialClientSource;
1827 nsIDocShell* docshell = GetDocShell();
1828 if (docshell) {
1829 initialClientSource = docshell->TakeInitialClientSource();
1830 }
1831
1832 // Try to get the reserved client from the LoadInfo. A Client is
1833 // reserved at the start of the channel load if there is not an
1834 // initial about:blank document that will be reused. It is also
1835 // created if the channel load encounters a cross-origin redirect.
1836 if (loadInfo) {
1837 UniquePtr<ClientSource> reservedClient =
1838 loadInfo->TakeReservedClientSource();
1839 if (reservedClient) {
1840 mClientSource.reset();
1841 mClientSource = std::move(reservedClient);
1842 newClientSource = true;
1843 }
1844 }
1845
1846 // We don't have a LoadInfo reserved client, but maybe we should
1847 // be inheriting an initial one from the docshell. This means
1848 // that the docshell started the channel load before creating the
1849 // initial about:blank document. This is an optimization, though,
1850 // and it created an initial Client as a placeholder for the document.
1851 // In this case we want to inherit this placeholder Client here.
1852 if (!mClientSource) {
1853 mClientSource = std::move(initialClientSource);
1854 if (mClientSource) {
1855 newClientSource = true;
1856 }
1857 }
1858
1859 // Verify the final ClientSource principal matches the final document
1860 // principal. The ClientChannelHelper handles things like network
1861 // redirects, but there are other ways the document principal can change.
1862 // For example, if something sets the nsIChannel.owner property, then
1863 // the final channel principal can be anything. Unfortunately there is
1864 // no good way to detect this until after the channel completes loading.
1865 //
1866 // For now we handle this just by reseting the ClientSource. This will
1867 // result in a new ClientSource with the correct principal being created.
1868 // To APIs like ServiceWorker and Clients API it will look like there was
1869 // an initial content page created that was then immediately replaced.
1870 // This is pretty close to what we are actually doing.
1871 if (mClientSource) {
1872 auto principalOrErr = mClientSource->Info().GetPrincipal();
1873 nsCOMPtr<nsIPrincipal> clientPrincipal =
1874 principalOrErr.isOk() ? principalOrErr.unwrap() : nullptr;
1875 if (!clientPrincipal || !clientPrincipal->Equals(mDoc->NodePrincipal())) {
1876 mClientSource.reset();
1877 }
1878 }
1879
1880 // If we don't have a reserved client or an initial client, then create
1881 // one now. This can happen in certain cases where we avoid preallocating
1882 // the client in the docshell. This mainly occurs in situations where
1883 // the principal is not clearly inherited from the parent; e.g. sandboxed
1884 // iframes, window.open(), etc.
1885 //
1886 // We also do this late ClientSource creation if the final document ended
1887 // up with a different principal.
1888 //
1889 // TODO: We may not be marking initial about:blank documents created
1890 // this way as controlled by a service worker properly. The
1891 // controller should be coming from the same place as the inheritted
1892 // principal. We do this in docshell, but as mentioned we aren't
1893 // smart enough to handle all cases yet. For example, a
1894 // window.open() with new URL should inherit the controller from
1895 // the opener, but we probably don't handle that yet.
1896 if (!mClientSource) {
1897 mClientSource = ClientManager::CreateSource(
1898 ClientType::Window, EventTargetFor(TaskCategory::Other),
1899 mDoc->NodePrincipal());
1900 MOZ_DIAGNOSTIC_ASSERT(mClientSource);
1901 newClientSource = true;
1902
1903 // Note, we don't apply the loadinfo controller below if we create
1904 // the ClientSource here.
1905 }
1906
1907 // The load may have started controlling the Client as well. If
1908 // so, mark it as controlled immediately here. The actor may
1909 // or may not have been notified by the parent side about being
1910 // controlled yet.
1911 //
1912 // Note: We should be careful not to control a client that was created late.
1913 // These clients were not seen by the ServiceWorkerManager when it
1914 // marked the LoadInfo controlled and it won't know about them. Its
1915 // also possible we are creating the client late due to the final
1916 // principal changing and these clients should definitely not be
1917 // controlled by a service worker with a different principal.
1918 else if (loadInfo) {
1919 const Maybe<ServiceWorkerDescriptor> controller = loadInfo->GetController();
1920 if (controller.isSome()) {
1921 mClientSource->SetController(controller.ref());
1922 }
1923
1924 // We also have to handle the case where te initial about:blank is
1925 // controlled due to inheritting the service worker from its parent,
1926 // but the actual nsIChannel load is not covered by any service worker.
1927 // In this case we want the final page to be uncontrolled. There is
1928 // an open spec issue about how exactly this should be handled, but for
1929 // now we just force creation of a new ClientSource to clear the
1930 // controller.
1931 //
1932 // https://github.com/w3c/ServiceWorker/issues/1232
1933 //
1934 else if (mClientSource->GetController().isSome()) {
1935 mClientSource.reset();
1936 mClientSource = ClientManager::CreateSource(
1937 ClientType::Window, EventTargetFor(TaskCategory::Other),
1938 mDoc->NodePrincipal());
1939 MOZ_DIAGNOSTIC_ASSERT(mClientSource);
1940 newClientSource = true;
1941 }
1942 }
1943
1944 if (mClientSource) {
1945 // Generally the CSP is stored within the Client and cached on the document.
1946 // At the time of CSP parsing however, the Client has not been created yet,
1947 // hence we store the CSP on the document and propagate/sync the CSP with
1948 // Client here when we create the Client.
1949 mClientSource->SetCsp(mDoc->GetCsp());
1950
1951 DocGroup* docGroup = GetDocGroup();
1952 MOZ_DIAGNOSTIC_ASSERT(docGroup);
1953 mClientSource->SetAgentClusterId(docGroup->AgentClusterId());
1954
1955 if (mWindowGlobalChild) {
1956 mWindowGlobalChild->SendSetClientInfo(mClientSource->Info().ToIPC());
1957 }
1958 }
1959
1960 // Its possible that we got a client just after being frozen in
1961 // the bfcache. In that case freeze the client immediately.
1962 if (newClientSource && IsFrozen()) {
1963 mClientSource->Freeze();
1964 }
1965
1966 return NS_OK;
1967 }
1968
ExecutionReady()1969 nsresult nsGlobalWindowInner::ExecutionReady() {
1970 nsresult rv = EnsureClientSource();
1971 NS_ENSURE_SUCCESS(rv, rv);
1972
1973 rv = mClientSource->WindowExecutionReady(this);
1974 NS_ENSURE_SUCCESS(rv, rv);
1975
1976 return NS_OK;
1977 }
1978
UpdateParentTarget()1979 void nsGlobalWindowInner::UpdateParentTarget() {
1980 // NOTE: This method is identical to
1981 // nsGlobalWindowOuter::UpdateParentTarget(). IF YOU UPDATE THIS METHOD,
1982 // UPDATE THE OTHER ONE TOO!
1983
1984 // Try to get our frame element's tab child global (its in-process message
1985 // manager). If that fails, fall back to the chrome event handler's tab
1986 // child global, and if it doesn't have one, just use the chrome event
1987 // handler itself.
1988
1989 nsPIDOMWindowOuter* outer = GetOuterWindow();
1990 if (!outer) {
1991 return;
1992 }
1993 nsCOMPtr<Element> frameElement = outer->GetFrameElementInternal();
1994 nsCOMPtr<EventTarget> eventTarget =
1995 nsContentUtils::TryGetBrowserChildGlobal(frameElement);
1996
1997 if (!eventTarget) {
1998 nsGlobalWindowOuter* topWin = GetInProcessScriptableTopInternal();
1999 if (topWin) {
2000 frameElement = topWin->GetFrameElementInternal();
2001 eventTarget = nsContentUtils::TryGetBrowserChildGlobal(frameElement);
2002 }
2003 }
2004
2005 if (!eventTarget) {
2006 eventTarget = nsContentUtils::TryGetBrowserChildGlobal(mChromeEventHandler);
2007 }
2008
2009 if (!eventTarget) {
2010 eventTarget = mChromeEventHandler;
2011 }
2012
2013 mParentTarget = eventTarget;
2014 }
2015
GetTargetForDOMEvent()2016 EventTarget* nsGlobalWindowInner::GetTargetForDOMEvent() {
2017 return GetOuterWindowInternal();
2018 }
2019
GetEventTargetParent(EventChainPreVisitor & aVisitor)2020 void nsGlobalWindowInner::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
2021 EventMessage msg = aVisitor.mEvent->mMessage;
2022
2023 aVisitor.mCanHandle = true;
2024 aVisitor.mForceContentDispatch = true; // FIXME! Bug 329119
2025 if (msg == eResize && aVisitor.mEvent->IsTrusted()) {
2026 // QIing to window so that we can keep the old behavior also in case
2027 // a child window is handling resize.
2028 nsCOMPtr<nsPIDOMWindowInner> window =
2029 do_QueryInterface(aVisitor.mEvent->mOriginalTarget);
2030 if (window) {
2031 mIsHandlingResizeEvent = true;
2032 }
2033 } else if (msg == eMouseDown && aVisitor.mEvent->IsTrusted()) {
2034 sMouseDown = true;
2035 } else if ((msg == eMouseUp || msg == eDragEnd) &&
2036 aVisitor.mEvent->IsTrusted()) {
2037 sMouseDown = false;
2038 if (sDragServiceDisabled) {
2039 nsCOMPtr<nsIDragService> ds =
2040 do_GetService("@mozilla.org/widget/dragservice;1");
2041 if (ds) {
2042 sDragServiceDisabled = false;
2043 ds->Unsuppress();
2044 }
2045 }
2046 }
2047
2048 aVisitor.SetParentTarget(GetParentTarget(), true);
2049 }
2050
DialogsAreBeingAbused()2051 bool nsGlobalWindowInner::DialogsAreBeingAbused() {
2052 NS_ASSERTION(GetInProcessScriptableTopInternal() &&
2053 GetInProcessScriptableTopInternal()
2054 ->GetCurrentInnerWindowInternal() == this,
2055 "DialogsAreBeingAbused called with invalid window");
2056
2057 if (mLastDialogQuitTime.IsNull() || nsContentUtils::IsCallerChrome()) {
2058 return false;
2059 }
2060
2061 TimeDuration dialogInterval(TimeStamp::Now() - mLastDialogQuitTime);
2062 if (dialogInterval.ToSeconds() <
2063 Preferences::GetInt("dom.successive_dialog_time_limit",
2064 DEFAULT_SUCCESSIVE_DIALOG_TIME_LIMIT)) {
2065 mDialogAbuseCount++;
2066
2067 return PopupBlocker::GetPopupControlState() > PopupBlocker::openAllowed ||
2068 mDialogAbuseCount > MAX_SUCCESSIVE_DIALOG_COUNT;
2069 }
2070
2071 // Reset the abuse counter
2072 mDialogAbuseCount = 0;
2073
2074 return false;
2075 }
2076
FireFrameLoadEvent()2077 void nsGlobalWindowInner::FireFrameLoadEvent() {
2078 // If we're not in a content frame, or are at a BrowsingContext tree boundary,
2079 // such as the content-chrome boundary, don't fire the "load" event.
2080 if (GetBrowsingContext()->IsTopContent() ||
2081 GetBrowsingContext()->IsChrome()) {
2082 return;
2083 }
2084
2085 // If embedder is same-process, fire the event on our embedder element.
2086 //
2087 // XXX: Bug 1440212 is looking into potentially changing this behaviour to act
2088 // more like the remote case when in-process.
2089 RefPtr<Element> element = GetBrowsingContext()->GetEmbedderElement();
2090 if (element) {
2091 nsEventStatus status = nsEventStatus_eIgnore;
2092 WidgetEvent event(/* aIsTrusted = */ true, eLoad);
2093 event.mFlags.mBubbles = false;
2094 event.mFlags.mCancelable = false;
2095
2096 if (mozilla::dom::DocGroup::TryToLoadIframesInBackground()) {
2097 nsDocShell* ds = nsDocShell::Cast(GetDocShell());
2098
2099 if (ds && !ds->HasFakeOnLoadDispatched()) {
2100 EventDispatcher::Dispatch(element, nullptr, &event, nullptr, &status);
2101 }
2102 } else {
2103 // Most of the time we could get a pres context to pass in here,
2104 // but not always (i.e. if this window is not shown there won't
2105 // be a pres context available). Since we're not firing a GUI
2106 // event we don't need a pres context anyway so we just pass
2107 // null as the pres context all the time here.
2108 EventDispatcher::Dispatch(element, nullptr, &event, nullptr, &status);
2109 }
2110 return;
2111 }
2112
2113 // We don't have an in-process embedder. Try to get our `BrowserChild` actor
2114 // to send a message to that embedder. We want to double-check that our outer
2115 // window is actually the one at the root of this browserChild though, just in
2116 // case.
2117 RefPtr<BrowserChild> browserChild =
2118 BrowserChild::GetFrom(static_cast<nsPIDOMWindowInner*>(this));
2119 if (browserChild) {
2120 // Double-check that our outer window is actually at the root of this
2121 // `BrowserChild`, in case we're in an odd maybe-unhosted situation like a
2122 // print preview dialog.
2123 nsCOMPtr<nsPIDOMWindowOuter> rootOuter =
2124 do_GetInterface(browserChild->WebNavigation());
2125 if (!rootOuter || rootOuter != GetOuterWindow()) {
2126 return;
2127 }
2128
2129 mozilla::Unused << browserChild->SendMaybeFireEmbedderLoadEvents(
2130 EmbedderElementEventType::LoadEvent);
2131 }
2132 }
2133
PostHandleEvent(EventChainPostVisitor & aVisitor)2134 nsresult nsGlobalWindowInner::PostHandleEvent(EventChainPostVisitor& aVisitor) {
2135 // Return early if there is nothing to do.
2136 switch (aVisitor.mEvent->mMessage) {
2137 case eResize:
2138 case eUnload:
2139 case eLoad:
2140 break;
2141 default:
2142 return NS_OK;
2143 }
2144
2145 /* mChromeEventHandler and mContext go dangling in the middle of this
2146 function under some circumstances (events that destroy the window)
2147 without this addref. */
2148 RefPtr<EventTarget> kungFuDeathGrip1(mChromeEventHandler);
2149 mozilla::Unused
2150 << kungFuDeathGrip1; // These aren't referred to through the function
2151 nsCOMPtr<nsIScriptContext> kungFuDeathGrip2(GetContextInternal());
2152 mozilla::Unused
2153 << kungFuDeathGrip2; // These aren't referred to through the function
2154
2155 if (aVisitor.mEvent->mMessage == eResize) {
2156 mIsHandlingResizeEvent = false;
2157 } else if (aVisitor.mEvent->mMessage == eUnload &&
2158 aVisitor.mEvent->IsTrusted()) {
2159 // If any VR display presentation is active at unload, the next page
2160 // will receive a vrdisplayactive event to indicate that it should
2161 // immediately begin vr presentation. This should occur when navigating
2162 // forwards, navigating backwards, and on page reload.
2163 for (const auto& display : mVRDisplays) {
2164 if (display->IsPresenting()) {
2165 display->StartVRNavigation();
2166 // Save this VR display ID to trigger vrdisplayactivate event
2167 // after the next load event.
2168 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
2169 if (outer) {
2170 outer->SetAutoActivateVRDisplayID(display->DisplayId());
2171 }
2172
2173 // XXX The WebVR 1.1 spec does not define which of multiple VR
2174 // presenting VR displays will be chosen during navigation.
2175 // As the underlying platform VR API's currently only allow a single
2176 // VR display, it is safe to choose the first VR display for now.
2177 break;
2178 }
2179 }
2180 mIsDocumentLoaded = false;
2181 // Tell the parent process that the document is not loaded.
2182 if (mWindowGlobalChild) {
2183 mWindowGlobalChild->SendUpdateDocumentHasLoaded(mIsDocumentLoaded);
2184 }
2185 } else if (aVisitor.mEvent->mMessage == eLoad &&
2186 aVisitor.mEvent->IsTrusted()) {
2187 // This is page load event since load events don't propagate to |window|.
2188 // @see Document::GetEventTargetParent.
2189 mIsDocumentLoaded = true;
2190 // Tell the parent process that the document is loaded.
2191 if (mWindowGlobalChild) {
2192 mWindowGlobalChild->SendUpdateDocumentHasLoaded(mIsDocumentLoaded);
2193 }
2194
2195 mTimeoutManager->OnDocumentLoaded();
2196
2197 MOZ_ASSERT(aVisitor.mEvent->IsTrusted());
2198 FireFrameLoadEvent();
2199
2200 if (mVREventObserver) {
2201 mVREventObserver->NotifyAfterLoad();
2202 }
2203
2204 uint32_t autoActivateVRDisplayID = 0;
2205 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
2206 if (outer) {
2207 autoActivateVRDisplayID = outer->GetAutoActivateVRDisplayID();
2208 }
2209 if (autoActivateVRDisplayID) {
2210 DispatchVRDisplayActivate(autoActivateVRDisplayID,
2211 VRDisplayEventReason::Navigation);
2212 }
2213 }
2214
2215 return NS_OK;
2216 }
2217
DefineArgumentsProperty(nsIArray * aArguments)2218 nsresult nsGlobalWindowInner::DefineArgumentsProperty(nsIArray* aArguments) {
2219 nsIScriptContext* ctx = GetOuterWindowInternal()->mContext;
2220 NS_ENSURE_TRUE(aArguments && ctx, NS_ERROR_NOT_INITIALIZED);
2221
2222 JS::Rooted<JSObject*> obj(RootingCx(), GetWrapperPreserveColor());
2223 return ctx->SetProperty(obj, "arguments", aArguments);
2224 }
2225
2226 //*****************************************************************************
2227 // nsGlobalWindowInner::nsIScriptObjectPrincipal
2228 //*****************************************************************************
2229
GetPrincipal()2230 nsIPrincipal* nsGlobalWindowInner::GetPrincipal() {
2231 if (mDoc) {
2232 // If we have a document, get the principal from the document
2233 return mDoc->NodePrincipal();
2234 }
2235
2236 if (mDocumentPrincipal) {
2237 return mDocumentPrincipal;
2238 }
2239
2240 // If we don't have a principal and we don't have a document we
2241 // ask the parent window for the principal. This can happen when
2242 // loading a frameset that has a <frame src="javascript:xxx">, in
2243 // that case the global window is used in JS before we've loaded
2244 // a document into the window.
2245
2246 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
2247 do_QueryInterface(GetInProcessParentInternal());
2248
2249 if (objPrincipal) {
2250 return objPrincipal->GetPrincipal();
2251 }
2252
2253 return nullptr;
2254 }
2255
GetEffectiveStoragePrincipal()2256 nsIPrincipal* nsGlobalWindowInner::GetEffectiveStoragePrincipal() {
2257 if (mDoc) {
2258 // If we have a document, get the principal from the document
2259 return mDoc->EffectiveStoragePrincipal();
2260 }
2261
2262 if (mDocumentStoragePrincipal) {
2263 return mDocumentStoragePrincipal;
2264 }
2265
2266 // If we don't have a storage principal and we don't have a document we ask
2267 // the parent window for the storage principal.
2268
2269 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
2270 do_QueryInterface(GetInProcessParentInternal());
2271
2272 if (objPrincipal) {
2273 return objPrincipal->GetEffectiveStoragePrincipal();
2274 }
2275
2276 return nullptr;
2277 }
2278
PartitionedPrincipal()2279 nsIPrincipal* nsGlobalWindowInner::PartitionedPrincipal() {
2280 if (mDoc) {
2281 // If we have a document, get the principal from the document
2282 return mDoc->PartitionedPrincipal();
2283 }
2284
2285 if (mDocumentPartitionedPrincipal) {
2286 return mDocumentPartitionedPrincipal;
2287 }
2288
2289 // If we don't have a partitioned principal and we don't have a document we
2290 // ask the parent window for the partitioned principal.
2291
2292 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
2293 do_QueryInterface(GetInProcessParentInternal());
2294
2295 if (objPrincipal) {
2296 return objPrincipal->PartitionedPrincipal();
2297 }
2298
2299 return nullptr;
2300 }
2301
2302 //*****************************************************************************
2303 // nsGlobalWindowInner::nsIDOMWindow
2304 //*****************************************************************************
2305
AddAudioContext(AudioContext * aAudioContext)2306 bool nsPIDOMWindowInner::AddAudioContext(AudioContext* aAudioContext) {
2307 mAudioContexts.AppendElement(aAudioContext);
2308
2309 // Return true if the context should be muted and false if not.
2310 nsIDocShell* docShell = GetDocShell();
2311 return docShell && !docShell->GetAllowMedia() && !aAudioContext->IsOffline();
2312 }
2313
RemoveAudioContext(AudioContext * aAudioContext)2314 void nsPIDOMWindowInner::RemoveAudioContext(AudioContext* aAudioContext) {
2315 mAudioContexts.RemoveElement(aAudioContext);
2316 }
2317
MuteAudioContexts()2318 void nsPIDOMWindowInner::MuteAudioContexts() {
2319 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
2320 if (!mAudioContexts[i]->IsOffline()) {
2321 mAudioContexts[i]->Mute();
2322 }
2323 }
2324 }
2325
UnmuteAudioContexts()2326 void nsPIDOMWindowInner::UnmuteAudioContexts() {
2327 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
2328 if (!mAudioContexts[i]->IsOffline()) {
2329 mAudioContexts[i]->Unmute();
2330 }
2331 }
2332 }
2333
Window()2334 WindowProxyHolder nsGlobalWindowInner::Window() {
2335 return WindowProxyHolder(GetBrowsingContext());
2336 }
2337
Navigator()2338 Navigator* nsPIDOMWindowInner::Navigator() {
2339 if (!mNavigator) {
2340 mNavigator = new mozilla::dom::Navigator(this);
2341 }
2342
2343 return mNavigator;
2344 }
2345
VisualViewport()2346 VisualViewport* nsGlobalWindowInner::VisualViewport() {
2347 if (!mVisualViewport) {
2348 mVisualViewport = new mozilla::dom::VisualViewport(this);
2349 }
2350
2351 return mVisualViewport;
2352 }
2353
GetScreen(ErrorResult & aError)2354 nsScreen* nsGlobalWindowInner::GetScreen(ErrorResult& aError) {
2355 if (!mScreen) {
2356 mScreen = nsScreen::Create(this);
2357 if (!mScreen) {
2358 aError.Throw(NS_ERROR_UNEXPECTED);
2359 return nullptr;
2360 }
2361 }
2362
2363 return mScreen;
2364 }
2365
GetHistory(ErrorResult & aError)2366 nsHistory* nsGlobalWindowInner::GetHistory(ErrorResult& aError) {
2367 if (!mHistory) {
2368 mHistory = new nsHistory(this);
2369 }
2370
2371 return mHistory;
2372 }
2373
CustomElements()2374 CustomElementRegistry* nsGlobalWindowInner::CustomElements() {
2375 if (!mCustomElements) {
2376 mCustomElements = new CustomElementRegistry(this);
2377 }
2378
2379 return mCustomElements;
2380 }
2381
GetExistingCustomElements()2382 CustomElementRegistry* nsGlobalWindowInner::GetExistingCustomElements() {
2383 return mCustomElements;
2384 }
2385
GetPerformance()2386 Performance* nsPIDOMWindowInner::GetPerformance() {
2387 CreatePerformanceObjectIfNeeded();
2388 return mPerformance;
2389 }
2390
QueuePerformanceNavigationTiming()2391 void nsPIDOMWindowInner::QueuePerformanceNavigationTiming() {
2392 CreatePerformanceObjectIfNeeded();
2393 if (mPerformance) {
2394 mPerformance->QueueNavigationTimingEntry();
2395 }
2396 }
2397
CreatePerformanceObjectIfNeeded()2398 void nsPIDOMWindowInner::CreatePerformanceObjectIfNeeded() {
2399 if (mPerformance || !mDoc) {
2400 return;
2401 }
2402 RefPtr<nsDOMNavigationTiming> timing = mDoc->GetNavigationTiming();
2403 nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(mDoc->GetChannel()));
2404 bool timingEnabled = false;
2405 if (!timedChannel ||
2406 !NS_SUCCEEDED(timedChannel->GetTimingEnabled(&timingEnabled)) ||
2407 !timingEnabled) {
2408 timedChannel = nullptr;
2409 }
2410 if (timing) {
2411 mPerformance = Performance::CreateForMainThread(this, mDoc->NodePrincipal(),
2412 timing, timedChannel);
2413 }
2414 }
2415
IsSecureContext() const2416 bool nsPIDOMWindowInner::IsSecureContext() const {
2417 return nsGlobalWindowInner::Cast(this)->IsSecureContext();
2418 }
2419
Suspend(bool aIncludeSubWindows)2420 void nsPIDOMWindowInner::Suspend(bool aIncludeSubWindows) {
2421 nsGlobalWindowInner::Cast(this)->Suspend(aIncludeSubWindows);
2422 }
2423
Resume(bool aIncludeSubWindows)2424 void nsPIDOMWindowInner::Resume(bool aIncludeSubWindows) {
2425 nsGlobalWindowInner::Cast(this)->Resume(aIncludeSubWindows);
2426 }
2427
SyncStateFromParentWindow()2428 void nsPIDOMWindowInner::SyncStateFromParentWindow() {
2429 nsGlobalWindowInner::Cast(this)->SyncStateFromParentWindow();
2430 }
2431
GetClientInfo() const2432 Maybe<ClientInfo> nsPIDOMWindowInner::GetClientInfo() const {
2433 return nsGlobalWindowInner::Cast(this)->GetClientInfo();
2434 }
2435
GetClientState() const2436 Maybe<ClientState> nsPIDOMWindowInner::GetClientState() const {
2437 return nsGlobalWindowInner::Cast(this)->GetClientState();
2438 }
2439
GetController() const2440 Maybe<ServiceWorkerDescriptor> nsPIDOMWindowInner::GetController() const {
2441 return nsGlobalWindowInner::Cast(this)->GetController();
2442 }
2443
SetCsp(nsIContentSecurityPolicy * aCsp)2444 void nsPIDOMWindowInner::SetCsp(nsIContentSecurityPolicy* aCsp) {
2445 return nsGlobalWindowInner::Cast(this)->SetCsp(aCsp);
2446 }
2447
SetPreloadCsp(nsIContentSecurityPolicy * aPreloadCsp)2448 void nsPIDOMWindowInner::SetPreloadCsp(nsIContentSecurityPolicy* aPreloadCsp) {
2449 return nsGlobalWindowInner::Cast(this)->SetPreloadCsp(aPreloadCsp);
2450 }
2451
GetCsp()2452 nsIContentSecurityPolicy* nsPIDOMWindowInner::GetCsp() {
2453 return nsGlobalWindowInner::Cast(this)->GetCsp();
2454 }
2455
NoteCalledRegisterForServiceWorkerScope(const nsACString & aScope)2456 void nsPIDOMWindowInner::NoteCalledRegisterForServiceWorkerScope(
2457 const nsACString& aScope) {
2458 nsGlobalWindowInner::Cast(this)->NoteCalledRegisterForServiceWorkerScope(
2459 aScope);
2460 }
2461
NoteDOMContentLoaded()2462 void nsPIDOMWindowInner::NoteDOMContentLoaded() {
2463 nsGlobalWindowInner::Cast(this)->NoteDOMContentLoaded();
2464 }
2465
ShouldReportForServiceWorkerScope(const nsAString & aScope)2466 bool nsGlobalWindowInner::ShouldReportForServiceWorkerScope(
2467 const nsAString& aScope) {
2468 bool result = false;
2469
2470 nsPIDOMWindowOuter* topOuter = GetInProcessScriptableTop();
2471 NS_ENSURE_TRUE(topOuter, false);
2472
2473 nsGlobalWindowInner* topInner =
2474 nsGlobalWindowInner::Cast(topOuter->GetCurrentInnerWindow());
2475 NS_ENSURE_TRUE(topInner, false);
2476
2477 topInner->ShouldReportForServiceWorkerScopeInternal(
2478 NS_ConvertUTF16toUTF8(aScope), &result);
2479 return result;
2480 }
2481
GetInstallTrigger()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;
2490 }
2491 }
2492
2493 return mInstallTrigger;
2494 }
2495
GetWindowUtils(ErrorResult & aRv)2496 nsIDOMWindowUtils* nsGlobalWindowInner::GetWindowUtils(ErrorResult& aRv) {
2497 FORWARD_TO_OUTER_OR_THROW(WindowUtils, (), aRv, nullptr);
2498 }
2499
ShouldReportForServiceWorkerScopeInternal(const nsACString & aScope,bool * aResultOut)2500 CallState nsGlobalWindowInner::ShouldReportForServiceWorkerScopeInternal(
2501 const nsACString& aScope, bool* aResultOut) {
2502 MOZ_DIAGNOSTIC_ASSERT(aResultOut);
2503
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;
2510 }
2511
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;
2519 }
2520
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;
2545 }
2546 nsCOMPtr<nsIURI> loadingURL;
2547 Unused << loadingChannel->GetURI(getter_AddRefs(loadingURL));
2548 if (!loadingURL) {
2549 continue;
2550 }
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;
2558 }
2559 }
2560 }
2561 }
2562 }
2563
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);
2569 }
2570
NoteCalledRegisterForServiceWorkerScope(const nsACString & aScope)2571 void nsGlobalWindowInner::NoteCalledRegisterForServiceWorkerScope(
2572 const nsACString& aScope) {
2573 if (!mClientSource) {
2574 return;
2575 }
2576
2577 mClientSource->NoteCalledRegisterForServiceWorkerScope(aScope);
2578 }
2579
NoteDOMContentLoaded()2580 void nsGlobalWindowInner::NoteDOMContentLoaded() {
2581 if (!mClientSource) {
2582 return;
2583 }
2584
2585 mClientSource->NoteDOMContentLoaded();
2586 }
2587
UpdateTopInnerWindow()2588 void nsGlobalWindowInner::UpdateTopInnerWindow() {
2589 if (IsTopInnerWindow() || !mTopInnerWindow) {
2590 return;
2591 }
2592
2593 mTopInnerWindow->UpdateWebSocketCount(-(int32_t)mNumOfOpenWebSockets);
2594 }
2595
IsInSyncOperation()2596 bool nsGlobalWindowInner::IsInSyncOperation() {
2597 return GetExtantDoc() && GetExtantDoc()->IsInSyncOperation();
2598 }
2599
IsSharedMemoryAllowedInternal(nsIPrincipal * aPrincipal) const2600 bool nsGlobalWindowInner::IsSharedMemoryAllowedInternal(
2601 nsIPrincipal* aPrincipal) const {
2602 MOZ_ASSERT(NS_IsMainThread());
2603
2604 if (StaticPrefs::
2605 dom_postMessage_sharedArrayBuffer_bypassCOOP_COEP_insecure_enabled()) {
2606 return true;
2607 }
2608
2609 if (ExtensionPolicyService::GetSingleton().IsExtensionProcess()) {
2610 if (auto* basePrincipal = BasePrincipal::Cast(aPrincipal)) {
2611 if (auto* policy = basePrincipal->AddonPolicy()) {
2612 return policy->IsPrivileged();
2613 }
2614 }
2615 }
2616
2617 return CrossOriginIsolated();
2618 }
2619
CrossOriginIsolated() const2620 bool nsGlobalWindowInner::CrossOriginIsolated() const {
2621 MOZ_ASSERT(NS_IsMainThread());
2622
2623 RefPtr<BrowsingContext> bc = GetBrowsingContext();
2624 MOZ_DIAGNOSTIC_ASSERT(bc);
2625 return bc->CrossOriginIsolated();
2626 }
2627
AddPeerConnection()2628 void nsPIDOMWindowInner::AddPeerConnection() {
2629 MOZ_ASSERT(NS_IsMainThread());
2630 mTopInnerWindow ? mTopInnerWindow->mTotalActivePeerConnections++
2631 : mTotalActivePeerConnections++;
2632 ++mActivePeerConnections;
2633 if (mActivePeerConnections == 1 && mWindowGlobalChild) {
2634 mWindowGlobalChild->BlockBFCacheFor(BFCacheStatus::ACTIVE_PEER_CONNECTION);
2635 }
2636 }
2637
RemovePeerConnection()2638 void nsPIDOMWindowInner::RemovePeerConnection() {
2639 MOZ_ASSERT(NS_IsMainThread());
2640 MOZ_ASSERT(mTopInnerWindow ? mTopInnerWindow->mTotalActivePeerConnections
2641 : mTotalActivePeerConnections);
2642 MOZ_ASSERT(mActivePeerConnections > 0);
2643
2644 mTopInnerWindow ? mTopInnerWindow->mTotalActivePeerConnections--
2645 : mTotalActivePeerConnections--;
2646 --mActivePeerConnections;
2647 if (mActivePeerConnections == 0 && mWindowGlobalChild) {
2648 mWindowGlobalChild->UnblockBFCacheFor(
2649 BFCacheStatus::ACTIVE_PEER_CONNECTION);
2650 }
2651 }
2652
HasActivePeerConnections()2653 bool nsPIDOMWindowInner::HasActivePeerConnections() {
2654 MOZ_ASSERT(NS_IsMainThread());
2655 return mTopInnerWindow ? mTopInnerWindow->mTotalActivePeerConnections
2656 : mTotalActivePeerConnections;
2657 }
2658
AddMediaKeysInstance(MediaKeys * aMediaKeys)2659 void nsPIDOMWindowInner::AddMediaKeysInstance(MediaKeys* aMediaKeys) {
2660 MOZ_ASSERT(NS_IsMainThread());
2661 mMediaKeysInstances.AppendElement(aMediaKeys);
2662 if (mWindowGlobalChild && mMediaKeysInstances.Length() == 1) {
2663 mWindowGlobalChild->BlockBFCacheFor(BFCacheStatus::CONTAINS_EME_CONTENT);
2664 }
2665 }
2666
RemoveMediaKeysInstance(MediaKeys * aMediaKeys)2667 void nsPIDOMWindowInner::RemoveMediaKeysInstance(MediaKeys* aMediaKeys) {
2668 MOZ_ASSERT(NS_IsMainThread());
2669 mMediaKeysInstances.RemoveElement(aMediaKeys);
2670 if (mWindowGlobalChild && mMediaKeysInstances.IsEmpty()) {
2671 mWindowGlobalChild->UnblockBFCacheFor(BFCacheStatus::CONTAINS_EME_CONTENT);
2672 }
2673 }
2674
HasActiveMediaKeysInstance()2675 bool nsPIDOMWindowInner::HasActiveMediaKeysInstance() {
2676 MOZ_ASSERT(NS_IsMainThread());
2677 return !mMediaKeysInstances.IsEmpty();
2678 }
2679
IsPlayingAudio()2680 bool nsPIDOMWindowInner::IsPlayingAudio() {
2681 for (uint32_t i = 0; i < mAudioContexts.Length(); i++) {
2682 if (mAudioContexts[i]->IsRunning()) {
2683 return true;
2684 }
2685 }
2686 RefPtr<AudioChannelService> acs = AudioChannelService::Get();
2687 if (!acs) {
2688 return false;
2689 }
2690 auto outer = GetOuterWindow();
2691 if (!outer) {
2692 // We've been unlinked and are about to die. Not a good time to pretend to
2693 // be playing audio.
2694 return false;
2695 }
2696 return acs->IsWindowActive(outer);
2697 }
2698
IsDocumentLoaded() const2699 bool nsPIDOMWindowInner::IsDocumentLoaded() const { return mIsDocumentLoaded; }
2700
TimeoutManager()2701 mozilla::dom::TimeoutManager& nsPIDOMWindowInner::TimeoutManager() {
2702 return *mTimeoutManager;
2703 }
2704
IsRunningTimeout()2705 bool nsPIDOMWindowInner::IsRunningTimeout() {
2706 return TimeoutManager().IsRunningTimeout();
2707 }
2708
TryToCacheTopInnerWindow()2709 void nsPIDOMWindowInner::TryToCacheTopInnerWindow() {
2710 if (mHasTriedToCacheTopInnerWindow) {
2711 return;
2712 }
2713
2714 nsGlobalWindowInner* window = nsGlobalWindowInner::Cast(this);
2715
2716 MOZ_ASSERT(!window->IsDying());
2717
2718 mHasTriedToCacheTopInnerWindow = true;
2719
2720 MOZ_ASSERT(window);
2721
2722 if (nsCOMPtr<nsPIDOMWindowOuter> topOutter =
2723 window->GetInProcessScriptableTop()) {
2724 mTopInnerWindow = topOutter->GetCurrentInnerWindow();
2725 }
2726 }
2727
UpdateActiveIndexedDBDatabaseCount(int32_t aDelta)2728 void nsPIDOMWindowInner::UpdateActiveIndexedDBDatabaseCount(int32_t aDelta) {
2729 MOZ_ASSERT(NS_IsMainThread());
2730
2731 if (aDelta == 0) {
2732 return;
2733 }
2734
2735 // We count databases but not transactions because only active databases
2736 // could block throttling.
2737 uint32_t& counter = mTopInnerWindow
2738 ? mTopInnerWindow->mNumOfIndexedDBDatabases
2739 : mNumOfIndexedDBDatabases;
2740
2741 counter += aDelta;
2742 }
2743
HasActiveIndexedDBDatabases()2744 bool nsPIDOMWindowInner::HasActiveIndexedDBDatabases() {
2745 MOZ_ASSERT(NS_IsMainThread());
2746
2747 return mTopInnerWindow ? mTopInnerWindow->mNumOfIndexedDBDatabases > 0
2748 : mNumOfIndexedDBDatabases > 0;
2749 }
2750
UpdateWebSocketCount(int32_t aDelta)2751 void nsPIDOMWindowInner::UpdateWebSocketCount(int32_t aDelta) {
2752 MOZ_ASSERT(NS_IsMainThread());
2753
2754 if (aDelta == 0) {
2755 return;
2756 }
2757
2758 if (mTopInnerWindow && !IsTopInnerWindow()) {
2759 mTopInnerWindow->UpdateWebSocketCount(aDelta);
2760 }
2761
2762 MOZ_DIAGNOSTIC_ASSERT(
2763 aDelta > 0 || ((aDelta + mNumOfOpenWebSockets) < mNumOfOpenWebSockets));
2764
2765 mNumOfOpenWebSockets += aDelta;
2766 }
2767
HasOpenWebSockets() const2768 bool nsPIDOMWindowInner::HasOpenWebSockets() const {
2769 MOZ_ASSERT(NS_IsMainThread());
2770
2771 return mNumOfOpenWebSockets ||
2772 (mTopInnerWindow && mTopInnerWindow->mNumOfOpenWebSockets);
2773 }
2774
IsCurrentInnerWindow() const2775 bool nsPIDOMWindowInner::IsCurrentInnerWindow() const {
2776 if (mozilla::SessionHistoryInParent() && mBrowsingContext &&
2777 mBrowsingContext->IsInBFCache()) {
2778 return false;
2779 }
2780
2781 if (!mBrowsingContext || mBrowsingContext->IsDiscarded()) {
2782 // If our BrowsingContext has been discarded, we consider ourselves
2783 // still-current if we were current at the time it was discarded.
2784 return mOuterWindow && WasCurrentInnerWindow();
2785 }
2786
2787 nsPIDOMWindowOuter* outer = mBrowsingContext->GetDOMWindow();
2788 return outer && outer->GetCurrentInnerWindow() == this;
2789 }
2790
IsFullyActive() const2791 bool nsPIDOMWindowInner::IsFullyActive() const {
2792 WindowContext* wc = GetWindowContext();
2793 if (!wc || wc->IsDiscarded() || wc->IsCached()) {
2794 return false;
2795 }
2796 return GetBrowsingContext()->AncestorsAreCurrent();
2797 }
2798
SetAudioCapture(bool aCapture)2799 void nsPIDOMWindowInner::SetAudioCapture(bool aCapture) {
2800 RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
2801 if (service) {
2802 service->SetWindowAudioCaptured(GetOuterWindow(), mWindowID, aCapture);
2803 }
2804 }
2805
SetActiveLoadingState(bool aIsLoading)2806 void nsGlobalWindowInner::SetActiveLoadingState(bool aIsLoading) {
2807 MOZ_LOG(
2808 gTimeoutLog, mozilla::LogLevel::Debug,
2809 ("SetActiveLoadingState innerwindow %p: %d", (void*)this, aIsLoading));
2810 if (GetBrowsingContext()) {
2811 // Setting loading on a discarded context has no effect.
2812 Unused << GetBrowsingContext()->SetLoading(aIsLoading);
2813 }
2814
2815 if (!nsGlobalWindowInner::Cast(this)->IsChromeWindow()) {
2816 mTimeoutManager->SetLoading(aIsLoading);
2817 }
2818
2819 HintIsLoading(aIsLoading);
2820 }
2821
HintIsLoading(bool aIsLoading)2822 void nsGlobalWindowInner::HintIsLoading(bool aIsLoading) {
2823 // Hint to tell the JS GC to use modified triggers during pageload.
2824 if (mHintedWasLoading != aIsLoading) {
2825 using namespace js::gc;
2826 SetPerformanceHint(danger::GetJSContext(), aIsLoading
2827 ? PerformanceHint::InPageLoad
2828 : PerformanceHint::Normal);
2829 mHintedWasLoading = aIsLoading;
2830 }
2831 }
2832
2833 // nsISpeechSynthesisGetter
2834
2835 #ifdef MOZ_WEBSPEECH
GetSpeechSynthesis(ErrorResult & aError)2836 SpeechSynthesis* nsGlobalWindowInner::GetSpeechSynthesis(ErrorResult& aError) {
2837 if (!mSpeechSynthesis) {
2838 mSpeechSynthesis = new SpeechSynthesis(this);
2839 }
2840
2841 return mSpeechSynthesis;
2842 }
2843
HasActiveSpeechSynthesis()2844 bool nsGlobalWindowInner::HasActiveSpeechSynthesis() {
2845 if (mSpeechSynthesis) {
2846 return !mSpeechSynthesis->HasEmptyQueue();
2847 }
2848
2849 return false;
2850 }
2851
2852 #endif
2853
Glean()2854 mozilla::glean::Glean* nsGlobalWindowInner::Glean() {
2855 if (!mGlean) {
2856 mGlean = new mozilla::glean::Glean();
2857 }
2858
2859 return mGlean;
2860 }
2861
GleanPings()2862 mozilla::glean::GleanPings* nsGlobalWindowInner::GleanPings() {
2863 if (!mGleanPings) {
2864 mGleanPings = new mozilla::glean::GleanPings();
2865 }
2866
2867 return mGleanPings;
2868 }
2869
GetParent(ErrorResult & aError)2870 Nullable<WindowProxyHolder> nsGlobalWindowInner::GetParent(
2871 ErrorResult& aError) {
2872 FORWARD_TO_OUTER_OR_THROW(GetParentOuter, (), aError, nullptr);
2873 }
2874
2875 /**
2876 * GetInProcessScriptableParent used to be called when a script read
2877 * window.parent. Under Fission, that is now handled by
2878 * BrowsingContext::GetParent, and the result is a WindowProxyHolder rather than
2879 * an actual global window. This method still exists for legacy callers which
2880 * relied on the old logic, and require in-process windows. However, it only
2881 * works correctly when no out-of-process frames exist between this window and
2882 * the top-level window, so it should not be used in new code.
2883 *
2884 * In contrast to GetRealParent, GetInProcessScriptableParent respects <iframe
2885 * mozbrowser> boundaries, so if |this| is contained by an <iframe
2886 * mozbrowser>, we will return |this| as its own parent.
2887 */
GetInProcessScriptableParent()2888 nsPIDOMWindowOuter* nsGlobalWindowInner::GetInProcessScriptableParent() {
2889 FORWARD_TO_OUTER(GetInProcessScriptableParent, (), nullptr);
2890 }
2891
2892 /**
2893 * GetInProcessScriptableTop used to be called when a script read window.top.
2894 * Under Fission, that is now handled by BrowsingContext::Top, and the result is
2895 * a WindowProxyHolder rather than an actual global window. This method still
2896 * exists for legacy callers which relied on the old logic, and require
2897 * in-process windows. However, it only works correctly when no out-of-process
2898 * frames exist between this window and the top-level window, so it should not
2899 * be used in new code.
2900 *
2901 * In contrast to GetRealTop, GetInProcessScriptableTop respects <iframe
2902 * mozbrowser> boundaries. If we encounter a window owned by an <iframe
2903 * mozbrowser> while walking up the window hierarchy, we'll stop and return that
2904 * window.
2905 */
GetInProcessScriptableTop()2906 nsPIDOMWindowOuter* nsGlobalWindowInner::GetInProcessScriptableTop() {
2907 FORWARD_TO_OUTER(GetInProcessScriptableTop, (), nullptr);
2908 }
2909
GetContent(JSContext * aCx,JS::MutableHandle<JSObject * > aRetval,CallerType aCallerType,ErrorResult & aError)2910 void nsGlobalWindowInner::GetContent(JSContext* aCx,
2911 JS::MutableHandle<JSObject*> aRetval,
2912 CallerType aCallerType,
2913 ErrorResult& aError) {
2914 FORWARD_TO_OUTER_OR_THROW(GetContentOuter,
2915 (aCx, aRetval, aCallerType, aError), aError, );
2916 }
2917
GetMenubar(ErrorResult & aError)2918 BarProp* nsGlobalWindowInner::GetMenubar(ErrorResult& aError) {
2919 if (!mMenubar) {
2920 mMenubar = new MenubarProp(this);
2921 }
2922
2923 return mMenubar;
2924 }
2925
GetToolbar(ErrorResult & aError)2926 BarProp* nsGlobalWindowInner::GetToolbar(ErrorResult& aError) {
2927 if (!mToolbar) {
2928 mToolbar = new ToolbarProp(this);
2929 }
2930
2931 return mToolbar;
2932 }
2933
GetLocationbar(ErrorResult & aError)2934 BarProp* nsGlobalWindowInner::GetLocationbar(ErrorResult& aError) {
2935 if (!mLocationbar) {
2936 mLocationbar = new LocationbarProp(this);
2937 }
2938 return mLocationbar;
2939 }
2940
GetPersonalbar(ErrorResult & aError)2941 BarProp* nsGlobalWindowInner::GetPersonalbar(ErrorResult& aError) {
2942 if (!mPersonalbar) {
2943 mPersonalbar = new PersonalbarProp(this);
2944 }
2945 return mPersonalbar;
2946 }
2947
GetStatusbar(ErrorResult & aError)2948 BarProp* nsGlobalWindowInner::GetStatusbar(ErrorResult& aError) {
2949 if (!mStatusbar) {
2950 mStatusbar = new StatusbarProp(this);
2951 }
2952 return mStatusbar;
2953 }
2954
GetScrollbars(ErrorResult & aError)2955 BarProp* nsGlobalWindowInner::GetScrollbars(ErrorResult& aError) {
2956 if (!mScrollbars) {
2957 mScrollbars = new ScrollbarsProp(this);
2958 }
2959
2960 return mScrollbars;
2961 }
2962
GetClosed(ErrorResult & aError)2963 bool nsGlobalWindowInner::GetClosed(ErrorResult& aError) {
2964 // If we're called from JS (which is the only way we should be getting called
2965 // here) and we reach this point, that means our JS global is the current
2966 // target of the WindowProxy, which means that we are the "current inner"
2967 // of our outer. So if FORWARD_TO_OUTER fails to forward, that means the
2968 // outer is already torn down, which corresponds to the closed state.
2969 FORWARD_TO_OUTER(GetClosedOuter, (), true);
2970 }
2971
IndexedGetter(uint32_t aIndex)2972 Nullable<WindowProxyHolder> nsGlobalWindowInner::IndexedGetter(
2973 uint32_t aIndex) {
2974 FORWARD_TO_OUTER(IndexedGetterOuter, (aIndex), nullptr);
2975 }
2976
2977 namespace {
2978
2979 struct InterfaceShimEntry {
2980 const char* geckoName;
2981 const char* domName;
2982 };
2983
2984 } // anonymous namespace
2985
2986 // We add shims from Components.interfaces.nsIDOMFoo to window.Foo for each
2987 // interface that has interface constants that sites might be getting off
2988 // of Ci.
2989 const InterfaceShimEntry kInterfaceShimMap[] = {
2990 {"nsIXMLHttpRequest", "XMLHttpRequest"},
2991 {"nsIDOMDOMException", "DOMException"},
2992 {"nsIDOMNode", "Node"},
2993 {"nsIDOMCSSRule", "CSSRule"},
2994 {"nsIDOMEvent", "Event"},
2995 {"nsIDOMNSEvent", "Event"},
2996 {"nsIDOMKeyEvent", "KeyEvent"},
2997 {"nsIDOMMouseEvent", "MouseEvent"},
2998 {"nsIDOMMouseScrollEvent", "MouseScrollEvent"},
2999 {"nsIDOMMutationEvent", "MutationEvent"},
3000 {"nsIDOMUIEvent", "UIEvent"},
3001 {"nsIDOMHTMLMediaElement", "HTMLMediaElement"},
3002 {"nsIDOMRange", "Range"},
3003 // Think about whether Ci.nsINodeFilter can just go away for websites!
3004 {"nsIDOMNodeFilter", "NodeFilter"},
3005 {"nsIDOMXPathResult", "XPathResult"}};
3006
ResolveComponentsShim(JSContext * aCx,JS::Handle<JSObject * > aGlobal,JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> aDesc)3007 bool nsGlobalWindowInner::ResolveComponentsShim(
3008 JSContext* aCx, JS::Handle<JSObject*> aGlobal,
3009 JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> aDesc) {
3010 // Keep track of how often this happens.
3011 Telemetry::Accumulate(Telemetry::COMPONENTS_SHIM_ACCESSED_BY_CONTENT, true);
3012
3013 // Warn once.
3014 nsCOMPtr<Document> doc = GetExtantDoc();
3015 if (doc) {
3016 doc->WarnOnceAbout(DeprecatedOperations::eComponents, /* asError = */ true);
3017 }
3018
3019 // Create a fake Components object.
3020 AssertSameCompartment(aCx, aGlobal);
3021 JS::Rooted<JSObject*> components(aCx, JS_NewPlainObject(aCx));
3022 if (NS_WARN_IF(!components)) {
3023 return false;
3024 }
3025
3026 // Create a fake interfaces object.
3027 JS::Rooted<JSObject*> interfaces(aCx, JS_NewPlainObject(aCx));
3028 if (NS_WARN_IF(!interfaces)) {
3029 return false;
3030 }
3031 bool ok =
3032 JS_DefineProperty(aCx, components, "interfaces", interfaces,
3033 JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY);
3034 if (NS_WARN_IF(!ok)) {
3035 return false;
3036 }
3037
3038 // Define a bunch of shims from the Ci.nsIDOMFoo to window.Foo for DOM
3039 // interfaces with constants.
3040 for (uint32_t i = 0; i < ArrayLength(kInterfaceShimMap); ++i) {
3041 // Grab the names from the table.
3042 const char* geckoName = kInterfaceShimMap[i].geckoName;
3043 const char* domName = kInterfaceShimMap[i].domName;
3044
3045 // Look up the appopriate interface object on the global.
3046 JS::Rooted<JS::Value> v(aCx, JS::UndefinedValue());
3047 ok = JS_GetProperty(aCx, aGlobal, domName, &v);
3048 if (NS_WARN_IF(!ok)) {
3049 return false;
3050 }
3051 if (!v.isObject()) {
3052 NS_WARNING("Unable to find interface object on global");
3053 continue;
3054 }
3055
3056 // Define the shim on the interfaces object.
3057 ok = JS_DefineProperty(
3058 aCx, interfaces, geckoName, v,
3059 JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY);
3060 if (NS_WARN_IF(!ok)) {
3061 return false;
3062 }
3063 }
3064
3065 aDesc.set(mozilla::Some(JS::PropertyDescriptor::Data(
3066 JS::ObjectValue(*components),
3067 {JS::PropertyAttribute::Configurable, JS::PropertyAttribute::Enumerable,
3068 JS::PropertyAttribute::Writable})));
3069 return true;
3070 }
3071
3072 #ifdef RELEASE_OR_BETA
3073 # define USE_CONTROLLERS_SHIM
3074 #endif
3075
3076 #ifdef USE_CONTROLLERS_SHIM
3077 static const JSClass ControllersShimClass = {"Controllers", 0};
3078 static const JSClass XULControllersShimClass = {"XULControllers", 0};
3079 #endif
3080
DoResolve(JSContext * aCx,JS::Handle<JSObject * > aObj,JS::Handle<jsid> aId,JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> aDesc)3081 bool nsGlobalWindowInner::DoResolve(
3082 JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aId,
3083 JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> aDesc) {
3084 // Note: Keep this in sync with MayResolve.
3085
3086 // Note: The infallibleInit call in GlobalResolve depends on this check.
3087 if (!JSID_IS_STRING(aId)) {
3088 return true;
3089 }
3090
3091 bool found;
3092 if (!WebIDLGlobalNameHash::DefineIfEnabled(aCx, aObj, aId, aDesc, &found)) {
3093 return false;
3094 }
3095
3096 if (found) {
3097 return true;
3098 }
3099
3100 // We support a cut-down Components.interfaces in case websites are
3101 // using Components.interfaces.nsIFoo.CONSTANT_NAME for the ones
3102 // that have constants.
3103 if (StaticPrefs::dom_use_components_shim() &&
3104 aId == XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_COMPONENTS)) {
3105 return ResolveComponentsShim(aCx, aObj, aDesc);
3106 }
3107
3108 // We also support a "window.controllers" thing; apparently some
3109 // sites use it for browser-sniffing. See bug 1010577.
3110 #ifdef USE_CONTROLLERS_SHIM
3111 // Note: We use |aObj| rather than |this| to get the principal here, because
3112 // this is called during Window setup when the Document isn't necessarily
3113 // hooked up yet.
3114 if ((aId == XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_CONTROLLERS) ||
3115 aId == XPCJSRuntime::Get()->GetStringID(
3116 XPCJSContext::IDX_CONTROLLERS_CLASS)) &&
3117 !xpc::IsXrayWrapper(aObj) &&
3118 !nsContentUtils::ObjectPrincipal(aObj)->IsSystemPrincipal()) {
3119 if (GetExtantDoc()) {
3120 GetExtantDoc()->WarnOnceAbout(
3121 DeprecatedOperations::eWindow_Cc_ontrollers);
3122 }
3123 const JSClass* clazz;
3124 if (aId ==
3125 XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_CONTROLLERS)) {
3126 clazz = &XULControllersShimClass;
3127 } else {
3128 clazz = &ControllersShimClass;
3129 }
3130 MOZ_ASSERT(JS_IsGlobalObject(aObj));
3131 JS::Rooted<JSObject*> shim(aCx, JS_NewObject(aCx, clazz));
3132 if (NS_WARN_IF(!shim)) {
3133 return false;
3134 }
3135
3136 aDesc.set(mozilla::Some(JS::PropertyDescriptor::Data(
3137 JS::ObjectValue(*shim),
3138 {JS::PropertyAttribute::Configurable, JS::PropertyAttribute::Enumerable,
3139 JS::PropertyAttribute::Writable})));
3140 return true;
3141 }
3142 #endif
3143
3144 return true;
3145 }
3146
3147 /* static */
MayResolve(jsid aId)3148 bool nsGlobalWindowInner::MayResolve(jsid aId) {
3149 // Note: This function does not fail and may not have any side-effects.
3150 // Note: Keep this in sync with DoResolve.
3151 if (!JSID_IS_STRING(aId)) {
3152 return false;
3153 }
3154
3155 if (aId == XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_COMPONENTS)) {
3156 return true;
3157 }
3158
3159 if (aId == XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_CONTROLLERS) ||
3160 aId == XPCJSRuntime::Get()->GetStringID(
3161 XPCJSContext::IDX_CONTROLLERS_CLASS)) {
3162 // We only resolve .controllers/.Controllers in release builds and on
3163 // non-chrome windows, but let's not worry about any of that stuff.
3164 return true;
3165 }
3166
3167 return WebIDLGlobalNameHash::MayResolve(aId);
3168 }
3169
GetOwnPropertyNames(JSContext * aCx,JS::MutableHandleVector<jsid> aNames,bool aEnumerableOnly,ErrorResult & aRv)3170 void nsGlobalWindowInner::GetOwnPropertyNames(
3171 JSContext* aCx, JS::MutableHandleVector<jsid> aNames, bool aEnumerableOnly,
3172 ErrorResult& aRv) {
3173 if (aEnumerableOnly) {
3174 // The names we would return from here get defined on the window via one of
3175 // two codepaths. The ones coming from the WebIDLGlobalNameHash will end up
3176 // in the DefineConstructor function in BindingUtils, which always defines
3177 // things as non-enumerable. The ones coming from the script namespace
3178 // manager get defined by our resolve hook using FillPropertyDescriptor with
3179 // 0 for the property attributes, so non-enumerable as well.
3180 //
3181 // So in the aEnumerableOnly case we have nothing to do.
3182 return;
3183 }
3184
3185 // "Components" is marked as enumerable but only resolved on demand :-/.
3186 // aNames.AppendElement(u"Components"_ns);
3187
3188 JS::Rooted<JSObject*> wrapper(aCx, GetWrapper());
3189
3190 // There are actually two ways we can get called here: For normal
3191 // enumeration or for Xray enumeration. In the latter case, we want to
3192 // return all possible WebIDL names, because we don't really support
3193 // deleting these names off our Xray; trying to resolve them will just make
3194 // them come back. In the former case, we want to avoid returning deleted
3195 // names. But the JS engine already knows about the non-deleted
3196 // already-resolved names, so we can just return the so-far-unresolved ones.
3197 //
3198 // We can tell which case we're in by whether aCx is in our wrapper's
3199 // compartment. If not, we're in the Xray case.
3200 WebIDLGlobalNameHash::NameType nameType =
3201 js::IsObjectInContextCompartment(wrapper, aCx)
3202 ? WebIDLGlobalNameHash::UnresolvedNamesOnly
3203 : WebIDLGlobalNameHash::AllNames;
3204 if (!WebIDLGlobalNameHash::GetNames(aCx, wrapper, nameType, aNames)) {
3205 aRv.NoteJSContextException(aCx);
3206 }
3207 }
3208
3209 /* static */
IsPrivilegedChromeWindow(JSContext *,JSObject * aObj)3210 bool nsGlobalWindowInner::IsPrivilegedChromeWindow(JSContext*, JSObject* aObj) {
3211 // For now, have to deal with XPConnect objects here.
3212 nsGlobalWindowInner* win = xpc::WindowOrNull(aObj);
3213 return win && win->IsChromeWindow() &&
3214 nsContentUtils::ObjectPrincipal(aObj) ==
3215 nsContentUtils::GetSystemPrincipal();
3216 }
3217
3218 /* static */
IsRequestIdleCallbackEnabled(JSContext * aCx,JSObject *)3219 bool nsGlobalWindowInner::IsRequestIdleCallbackEnabled(JSContext* aCx,
3220 JSObject*) {
3221 // The requestIdleCallback should always be enabled for system code.
3222 return StaticPrefs::dom_requestIdleCallback_enabled() ||
3223 nsContentUtils::IsSystemCaller(aCx);
3224 }
3225
3226 /* static */
DeviceSensorsEnabled(JSContext *,JSObject *)3227 bool nsGlobalWindowInner::DeviceSensorsEnabled(JSContext*, JSObject*) {
3228 return Preferences::GetBool("device.sensors.enabled");
3229 }
3230
3231 /* static */
ContentPropertyEnabled(JSContext * aCx,JSObject *)3232 bool nsGlobalWindowInner::ContentPropertyEnabled(JSContext* aCx, JSObject*) {
3233 return StaticPrefs::dom_window_content_untrusted_enabled() ||
3234 nsContentUtils::IsSystemCaller(aCx);
3235 }
3236
GetApplicationCache(ErrorResult & aError)3237 nsDOMOfflineResourceList* nsGlobalWindowInner::GetApplicationCache(
3238 ErrorResult& aError) {
3239 if (!mApplicationCache) {
3240 nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(GetDocShell()));
3241 if (!webNav || !mDoc) {
3242 aError.Throw(NS_ERROR_FAILURE);
3243 return nullptr;
3244 }
3245
3246 nsCOMPtr<nsIURI> uri;
3247 aError = webNav->GetCurrentURI(getter_AddRefs(uri));
3248 if (aError.Failed()) {
3249 return nullptr;
3250 }
3251
3252 nsCOMPtr<nsIURI> manifestURI;
3253 nsContentUtils::GetOfflineAppManifest(mDoc, getter_AddRefs(manifestURI));
3254
3255 RefPtr<nsDOMOfflineResourceList> applicationCache =
3256 new nsDOMOfflineResourceList(manifestURI, uri, mDoc->NodePrincipal(),
3257 this);
3258
3259 mApplicationCache = applicationCache;
3260 }
3261
3262 return mApplicationCache;
3263 }
3264
GetApplicationCache()3265 nsDOMOfflineResourceList* nsGlobalWindowInner::GetApplicationCache() {
3266 return GetApplicationCache(IgnoreErrors());
3267 }
3268
GetCrypto(ErrorResult & aError)3269 Crypto* nsGlobalWindowInner::GetCrypto(ErrorResult& aError) {
3270 if (!mCrypto) {
3271 mCrypto = new Crypto(this);
3272 }
3273 return mCrypto;
3274 }
3275
GetU2f(ErrorResult & aError)3276 mozilla::dom::U2F* nsGlobalWindowInner::GetU2f(ErrorResult& aError) {
3277 if (!mU2F) {
3278 RefPtr<U2F> u2f = new U2F(this);
3279 u2f->Init(aError);
3280 if (NS_WARN_IF(aError.Failed())) {
3281 return nullptr;
3282 }
3283
3284 mU2F = u2f;
3285 }
3286 return mU2F;
3287 }
3288
GetControllers(ErrorResult & aError)3289 nsIControllers* nsGlobalWindowInner::GetControllers(ErrorResult& aError) {
3290 FORWARD_TO_OUTER_OR_THROW(GetControllersOuter, (aError), aError, nullptr);
3291 }
3292
GetControllers(nsIControllers ** aResult)3293 nsresult nsGlobalWindowInner::GetControllers(nsIControllers** aResult) {
3294 ErrorResult rv;
3295 nsCOMPtr<nsIControllers> controllers = GetControllers(rv);
3296 controllers.forget(aResult);
3297
3298 return rv.StealNSResult();
3299 }
3300
GetOpenerWindow(ErrorResult & aError)3301 Nullable<WindowProxyHolder> nsGlobalWindowInner::GetOpenerWindow(
3302 ErrorResult& aError) {
3303 FORWARD_TO_OUTER_OR_THROW(GetOpenerWindowOuter, (), aError, nullptr);
3304 }
3305
GetOpener(JSContext * aCx,JS::MutableHandle<JS::Value> aRetval,ErrorResult & aError)3306 void nsGlobalWindowInner::GetOpener(JSContext* aCx,
3307 JS::MutableHandle<JS::Value> aRetval,
3308 ErrorResult& aError) {
3309 Nullable<WindowProxyHolder> opener = GetOpenerWindow(aError);
3310 if (aError.Failed() || opener.IsNull()) {
3311 aRetval.setNull();
3312 return;
3313 }
3314
3315 if (!ToJSValue(aCx, opener.Value(), aRetval)) {
3316 aError.NoteJSContextException(aCx);
3317 }
3318 }
3319
SetOpener(JSContext * aCx,JS::Handle<JS::Value> aOpener,ErrorResult & aError)3320 void nsGlobalWindowInner::SetOpener(JSContext* aCx,
3321 JS::Handle<JS::Value> aOpener,
3322 ErrorResult& aError) {
3323 if (aOpener.isNull()) {
3324 RefPtr<BrowsingContext> bc(GetBrowsingContext());
3325 if (!bc->IsDiscarded()) {
3326 bc->SetOpener(nullptr);
3327 }
3328 return;
3329 }
3330
3331 // If something other than null is passed, just define aOpener on our inner
3332 // window's JS object, wrapped into the current compartment so that for Xrays
3333 // we define on the Xray expando object, but don't set it on the outer window,
3334 // so that it'll get reset on navigation. This is just like replaceable
3335 // properties, but we're not quite readonly.
3336 RedefineProperty(aCx, "opener", aOpener, aError);
3337 }
3338
GetEvent(JSContext * aCx,JS::MutableHandle<JS::Value> aRetval)3339 void nsGlobalWindowInner::GetEvent(JSContext* aCx,
3340 JS::MutableHandle<JS::Value> aRetval) {
3341 if (mEvent) {
3342 Unused << nsContentUtils::WrapNative(aCx, mEvent, aRetval);
3343 } else {
3344 aRetval.setUndefined();
3345 }
3346 }
3347
GetStatus(nsAString & aStatus,ErrorResult & aError)3348 void nsGlobalWindowInner::GetStatus(nsAString& aStatus, ErrorResult& aError) {
3349 FORWARD_TO_OUTER_OR_THROW(GetStatusOuter, (aStatus), aError, );
3350 }
3351
SetStatus(const nsAString & aStatus,ErrorResult & aError)3352 void nsGlobalWindowInner::SetStatus(const nsAString& aStatus,
3353 ErrorResult& aError) {
3354 FORWARD_TO_OUTER_OR_THROW(SetStatusOuter, (aStatus), aError, );
3355 }
3356
GetName(nsAString & aName,ErrorResult & aError)3357 void nsGlobalWindowInner::GetName(nsAString& aName, ErrorResult& aError) {
3358 FORWARD_TO_OUTER_OR_THROW(GetNameOuter, (aName), aError, );
3359 }
3360
SetName(const nsAString & aName,mozilla::ErrorResult & aError)3361 void nsGlobalWindowInner::SetName(const nsAString& aName,
3362 mozilla::ErrorResult& aError) {
3363 FORWARD_TO_OUTER_OR_THROW(SetNameOuter, (aName, aError), aError, );
3364 }
3365
GetInnerWidth(CallerType aCallerType,ErrorResult & aError)3366 double nsGlobalWindowInner::GetInnerWidth(CallerType aCallerType,
3367 ErrorResult& aError) {
3368 // We ignore aCallerType; we only have that argument because some other things
3369 // called by GetReplaceableWindowCoord need it. If this ever changes, fix
3370 // nsresult nsGlobalWindowInner::GetInnerWidth(double* aInnerWidth)
3371 // to actually take a useful CallerType and pass it in here.
3372 FORWARD_TO_OUTER_OR_THROW(GetInnerWidthOuter, (aError), aError, 0);
3373 }
3374
GetInnerWidth(JSContext * aCx,JS::MutableHandle<JS::Value> aValue,CallerType aCallerType,ErrorResult & aError)3375 void nsGlobalWindowInner::GetInnerWidth(JSContext* aCx,
3376 JS::MutableHandle<JS::Value> aValue,
3377 CallerType aCallerType,
3378 ErrorResult& aError) {
3379 GetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::GetInnerWidth, aValue,
3380 aCallerType, aError);
3381 }
3382
GetInnerWidth(double * aInnerWidth)3383 nsresult nsGlobalWindowInner::GetInnerWidth(double* aInnerWidth) {
3384 ErrorResult rv;
3385 // Callee doesn't care about the caller type, but play it safe.
3386 *aInnerWidth = GetInnerWidth(CallerType::NonSystem, rv);
3387
3388 return rv.StealNSResult();
3389 }
3390
SetInnerWidth(double aInnerWidth,CallerType aCallerType,ErrorResult & aError)3391 void nsGlobalWindowInner::SetInnerWidth(double aInnerWidth,
3392 CallerType aCallerType,
3393 ErrorResult& aError) {
3394 FORWARD_TO_OUTER_OR_THROW(SetInnerWidthOuter,
3395 (aInnerWidth, aCallerType, aError), aError, );
3396 }
3397
SetInnerWidth(JSContext * aCx,JS::Handle<JS::Value> aValue,CallerType aCallerType,ErrorResult & aError)3398 void nsGlobalWindowInner::SetInnerWidth(JSContext* aCx,
3399 JS::Handle<JS::Value> aValue,
3400 CallerType aCallerType,
3401 ErrorResult& aError) {
3402 SetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::SetInnerWidth, aValue,
3403 "innerWidth", aCallerType, aError);
3404 }
3405
GetInnerHeight(CallerType aCallerType,ErrorResult & aError)3406 double nsGlobalWindowInner::GetInnerHeight(CallerType aCallerType,
3407 ErrorResult& aError) {
3408 // We ignore aCallerType; we only have that argument because some other things
3409 // called by GetReplaceableWindowCoord need it. If this ever changes, fix
3410 // nsresult nsGlobalWindowInner::GetInnerHeight(double* aInnerWidth)
3411 // to actually take a useful CallerType and pass it in here.
3412 FORWARD_TO_OUTER_OR_THROW(GetInnerHeightOuter, (aError), aError, 0);
3413 }
3414
GetInnerHeight(JSContext * aCx,JS::MutableHandle<JS::Value> aValue,CallerType aCallerType,ErrorResult & aError)3415 void nsGlobalWindowInner::GetInnerHeight(JSContext* aCx,
3416 JS::MutableHandle<JS::Value> aValue,
3417 CallerType aCallerType,
3418 ErrorResult& aError) {
3419 GetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::GetInnerHeight, aValue,
3420 aCallerType, aError);
3421 }
3422
GetInnerHeight(double * aInnerHeight)3423 nsresult nsGlobalWindowInner::GetInnerHeight(double* aInnerHeight) {
3424 ErrorResult rv;
3425 // Callee doesn't care about the caller type, but play it safe.
3426 *aInnerHeight = GetInnerHeight(CallerType::NonSystem, rv);
3427
3428 return rv.StealNSResult();
3429 }
3430
SetInnerHeight(double aInnerHeight,CallerType aCallerType,ErrorResult & aError)3431 void nsGlobalWindowInner::SetInnerHeight(double aInnerHeight,
3432 CallerType aCallerType,
3433 ErrorResult& aError) {
3434 FORWARD_TO_OUTER_OR_THROW(SetInnerHeightOuter,
3435 (aInnerHeight, aCallerType, aError), aError, );
3436 }
3437
SetInnerHeight(JSContext * aCx,JS::Handle<JS::Value> aValue,CallerType aCallerType,ErrorResult & aError)3438 void nsGlobalWindowInner::SetInnerHeight(JSContext* aCx,
3439 JS::Handle<JS::Value> aValue,
3440 CallerType aCallerType,
3441 ErrorResult& aError) {
3442 SetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::SetInnerHeight, aValue,
3443 "innerHeight", aCallerType, aError);
3444 }
3445
GetOuterWidth(CallerType aCallerType,ErrorResult & aError)3446 int32_t nsGlobalWindowInner::GetOuterWidth(CallerType aCallerType,
3447 ErrorResult& aError) {
3448 FORWARD_TO_OUTER_OR_THROW(GetOuterWidthOuter, (aCallerType, aError), aError,
3449 0);
3450 }
3451
GetOuterWidth(JSContext * aCx,JS::MutableHandle<JS::Value> aValue,CallerType aCallerType,ErrorResult & aError)3452 void nsGlobalWindowInner::GetOuterWidth(JSContext* aCx,
3453 JS::MutableHandle<JS::Value> aValue,
3454 CallerType aCallerType,
3455 ErrorResult& aError) {
3456 GetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::GetOuterWidth, aValue,
3457 aCallerType, aError);
3458 }
3459
GetOuterHeight(CallerType aCallerType,ErrorResult & aError)3460 int32_t nsGlobalWindowInner::GetOuterHeight(CallerType aCallerType,
3461 ErrorResult& aError) {
3462 FORWARD_TO_OUTER_OR_THROW(GetOuterHeightOuter, (aCallerType, aError), aError,
3463 0);
3464 }
3465
GetOuterHeight(JSContext * aCx,JS::MutableHandle<JS::Value> aValue,CallerType aCallerType,ErrorResult & aError)3466 void nsGlobalWindowInner::GetOuterHeight(JSContext* aCx,
3467 JS::MutableHandle<JS::Value> aValue,
3468 CallerType aCallerType,
3469 ErrorResult& aError) {
3470 GetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::GetOuterHeight, aValue,
3471 aCallerType, aError);
3472 }
3473
SetOuterWidth(int32_t aOuterWidth,CallerType aCallerType,ErrorResult & aError)3474 void nsGlobalWindowInner::SetOuterWidth(int32_t aOuterWidth,
3475 CallerType aCallerType,
3476 ErrorResult& aError) {
3477 FORWARD_TO_OUTER_OR_THROW(SetOuterWidthOuter,
3478 (aOuterWidth, aCallerType, aError), aError, );
3479 }
3480
SetOuterWidth(JSContext * aCx,JS::Handle<JS::Value> aValue,CallerType aCallerType,ErrorResult & aError)3481 void nsGlobalWindowInner::SetOuterWidth(JSContext* aCx,
3482 JS::Handle<JS::Value> aValue,
3483 CallerType aCallerType,
3484 ErrorResult& aError) {
3485 SetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::SetOuterWidth, aValue,
3486 "outerWidth", aCallerType, aError);
3487 }
3488
SetOuterHeight(int32_t aOuterHeight,CallerType aCallerType,ErrorResult & aError)3489 void nsGlobalWindowInner::SetOuterHeight(int32_t aOuterHeight,
3490 CallerType aCallerType,
3491 ErrorResult& aError) {
3492 FORWARD_TO_OUTER_OR_THROW(SetOuterHeightOuter,
3493 (aOuterHeight, aCallerType, aError), aError, );
3494 }
3495
SetOuterHeight(JSContext * aCx,JS::Handle<JS::Value> aValue,CallerType aCallerType,ErrorResult & aError)3496 void nsGlobalWindowInner::SetOuterHeight(JSContext* aCx,
3497 JS::Handle<JS::Value> aValue,
3498 CallerType aCallerType,
3499 ErrorResult& aError) {
3500 SetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::SetOuterHeight, aValue,
3501 "outerHeight", aCallerType, aError);
3502 }
3503
GetScreenX(CallerType aCallerType,ErrorResult & aError)3504 int32_t nsGlobalWindowInner::GetScreenX(CallerType aCallerType,
3505 ErrorResult& aError) {
3506 FORWARD_TO_OUTER_OR_THROW(GetScreenXOuter, (aCallerType, aError), aError, 0);
3507 }
3508
GetScreenX(JSContext * aCx,JS::MutableHandle<JS::Value> aValue,CallerType aCallerType,ErrorResult & aError)3509 void nsGlobalWindowInner::GetScreenX(JSContext* aCx,
3510 JS::MutableHandle<JS::Value> aValue,
3511 CallerType aCallerType,
3512 ErrorResult& aError) {
3513 GetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::GetScreenX, aValue,
3514 aCallerType, aError);
3515 }
3516
GetMozInnerScreenX(CallerType aCallerType,ErrorResult & aError)3517 float nsGlobalWindowInner::GetMozInnerScreenX(CallerType aCallerType,
3518 ErrorResult& aError) {
3519 FORWARD_TO_OUTER_OR_THROW(GetMozInnerScreenXOuter, (aCallerType), aError, 0);
3520 }
3521
GetMozInnerScreenY(CallerType aCallerType,ErrorResult & aError)3522 float nsGlobalWindowInner::GetMozInnerScreenY(CallerType aCallerType,
3523 ErrorResult& aError) {
3524 FORWARD_TO_OUTER_OR_THROW(GetMozInnerScreenYOuter, (aCallerType), aError, 0);
3525 }
3526
GetDevicePixelRatio(CallerType aCallerType,ErrorResult & aError)3527 double nsGlobalWindowInner::GetDevicePixelRatio(CallerType aCallerType,
3528 ErrorResult& aError) {
3529 FORWARD_TO_OUTER_OR_THROW(GetDevicePixelRatioOuter, (aCallerType), aError,
3530 0.0);
3531 }
3532
GetMozPaintCount(ErrorResult & aError)3533 uint64_t nsGlobalWindowInner::GetMozPaintCount(ErrorResult& aError) {
3534 FORWARD_TO_OUTER_OR_THROW(GetMozPaintCountOuter, (), aError, 0);
3535 }
3536
RequestAnimationFrame(FrameRequestCallback & aCallback,ErrorResult & aError)3537 int32_t nsGlobalWindowInner::RequestAnimationFrame(
3538 FrameRequestCallback& aCallback, ErrorResult& aError) {
3539 if (!mDoc) {
3540 return 0;
3541 }
3542
3543 if (GetWrapperPreserveColor()) {
3544 js::NotifyAnimationActivity(GetWrapperPreserveColor());
3545 }
3546
3547 DebuggerNotificationDispatch(this,
3548 DebuggerNotificationType::RequestAnimationFrame);
3549
3550 int32_t handle;
3551 aError = mDoc->ScheduleFrameRequestCallback(aCallback, &handle);
3552 return handle;
3553 }
3554
CancelAnimationFrame(int32_t aHandle,ErrorResult & aError)3555 void nsGlobalWindowInner::CancelAnimationFrame(int32_t aHandle,
3556 ErrorResult& aError) {
3557 if (!mDoc) {
3558 return;
3559 }
3560
3561 DebuggerNotificationDispatch(this,
3562 DebuggerNotificationType::CancelAnimationFrame);
3563
3564 mDoc->CancelFrameRequestCallback(aHandle);
3565 }
3566
MatchMedia(const nsACString & aMediaQueryList,CallerType aCallerType,ErrorResult & aError)3567 already_AddRefed<MediaQueryList> nsGlobalWindowInner::MatchMedia(
3568 const nsACString& aMediaQueryList, CallerType aCallerType,
3569 ErrorResult& aError) {
3570 ENSURE_ACTIVE_DOCUMENT(aError, nullptr);
3571 return mDoc->MatchMedia(aMediaQueryList, aCallerType);
3572 }
3573
SetScreenX(int32_t aScreenX,CallerType aCallerType,ErrorResult & aError)3574 void nsGlobalWindowInner::SetScreenX(int32_t aScreenX, CallerType aCallerType,
3575 ErrorResult& aError) {
3576 FORWARD_TO_OUTER_OR_THROW(SetScreenXOuter, (aScreenX, aCallerType, aError),
3577 aError, );
3578 }
3579
SetScreenX(JSContext * aCx,JS::Handle<JS::Value> aValue,CallerType aCallerType,ErrorResult & aError)3580 void nsGlobalWindowInner::SetScreenX(JSContext* aCx,
3581 JS::Handle<JS::Value> aValue,
3582 CallerType aCallerType,
3583 ErrorResult& aError) {
3584 SetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::SetScreenX, aValue,
3585 "screenX", aCallerType, aError);
3586 }
3587
GetScreenY(CallerType aCallerType,ErrorResult & aError)3588 int32_t nsGlobalWindowInner::GetScreenY(CallerType aCallerType,
3589 ErrorResult& aError) {
3590 FORWARD_TO_OUTER_OR_THROW(GetScreenYOuter, (aCallerType, aError), aError, 0);
3591 }
3592
GetScreenY(JSContext * aCx,JS::MutableHandle<JS::Value> aValue,CallerType aCallerType,ErrorResult & aError)3593 void nsGlobalWindowInner::GetScreenY(JSContext* aCx,
3594 JS::MutableHandle<JS::Value> aValue,
3595 CallerType aCallerType,
3596 ErrorResult& aError) {
3597 GetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::GetScreenY, aValue,
3598 aCallerType, aError);
3599 }
3600
SetScreenY(int32_t aScreenY,CallerType aCallerType,ErrorResult & aError)3601 void nsGlobalWindowInner::SetScreenY(int32_t aScreenY, CallerType aCallerType,
3602 ErrorResult& aError) {
3603 FORWARD_TO_OUTER_OR_THROW(SetScreenYOuter, (aScreenY, aCallerType, aError),
3604 aError, );
3605 }
3606
SetScreenY(JSContext * aCx,JS::Handle<JS::Value> aValue,CallerType aCallerType,ErrorResult & aError)3607 void nsGlobalWindowInner::SetScreenY(JSContext* aCx,
3608 JS::Handle<JS::Value> aValue,
3609 CallerType aCallerType,
3610 ErrorResult& aError) {
3611 SetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::SetScreenY, aValue,
3612 "screenY", aCallerType, aError);
3613 }
3614
GetScrollMinX(ErrorResult & aError)3615 int32_t nsGlobalWindowInner::GetScrollMinX(ErrorResult& aError) {
3616 FORWARD_TO_OUTER_OR_THROW(GetScrollBoundaryOuter, (eSideLeft), aError, 0);
3617 }
3618
GetScrollMinY(ErrorResult & aError)3619 int32_t nsGlobalWindowInner::GetScrollMinY(ErrorResult& aError) {
3620 FORWARD_TO_OUTER_OR_THROW(GetScrollBoundaryOuter, (eSideTop), aError, 0);
3621 }
3622
GetScrollMaxX(ErrorResult & aError)3623 int32_t nsGlobalWindowInner::GetScrollMaxX(ErrorResult& aError) {
3624 FORWARD_TO_OUTER_OR_THROW(GetScrollBoundaryOuter, (eSideRight), aError, 0);
3625 }
3626
GetScrollMaxY(ErrorResult & aError)3627 int32_t nsGlobalWindowInner::GetScrollMaxY(ErrorResult& aError) {
3628 FORWARD_TO_OUTER_OR_THROW(GetScrollBoundaryOuter, (eSideBottom), aError, 0);
3629 }
3630
GetScrollX(ErrorResult & aError)3631 double nsGlobalWindowInner::GetScrollX(ErrorResult& aError) {
3632 FORWARD_TO_OUTER_OR_THROW(GetScrollXOuter, (), aError, 0);
3633 }
3634
GetScrollY(ErrorResult & aError)3635 double nsGlobalWindowInner::GetScrollY(ErrorResult& aError) {
3636 FORWARD_TO_OUTER_OR_THROW(GetScrollYOuter, (), aError, 0);
3637 }
3638
Length()3639 uint32_t nsGlobalWindowInner::Length() { FORWARD_TO_OUTER(Length, (), 0); }
3640
GetTop(mozilla::ErrorResult & aError)3641 Nullable<WindowProxyHolder> nsGlobalWindowInner::GetTop(
3642 mozilla::ErrorResult& aError) {
3643 FORWARD_TO_OUTER_OR_THROW(GetTopOuter, (), aError, nullptr);
3644 }
3645
GetChildWindow(const nsAString & aName)3646 already_AddRefed<BrowsingContext> nsGlobalWindowInner::GetChildWindow(
3647 const nsAString& aName) {
3648 if (GetOuterWindowInternal()) {
3649 return GetOuterWindowInternal()->GetChildWindow(aName);
3650 }
3651 return nullptr;
3652 }
3653
RefreshRealmPrincipal()3654 void nsGlobalWindowInner::RefreshRealmPrincipal() {
3655 JS::SetRealmPrincipals(js::GetNonCCWObjectRealm(GetWrapperPreserveColor()),
3656 nsJSPrincipals::get(mDoc->NodePrincipal()));
3657 }
3658
GetMainWidget()3659 already_AddRefed<nsIWidget> nsGlobalWindowInner::GetMainWidget() {
3660 FORWARD_TO_OUTER(GetMainWidget, (), nullptr);
3661 }
3662
GetNearestWidget() const3663 nsIWidget* nsGlobalWindowInner::GetNearestWidget() const {
3664 if (GetOuterWindowInternal()) {
3665 return GetOuterWindowInternal()->GetNearestWidget();
3666 }
3667 return nullptr;
3668 }
3669
SetFullScreen(bool aFullscreen,mozilla::ErrorResult & aError)3670 void nsGlobalWindowInner::SetFullScreen(bool aFullscreen,
3671 mozilla::ErrorResult& aError) {
3672 FORWARD_TO_OUTER_OR_THROW(SetFullscreenOuter, (aFullscreen, aError), aError,
3673 /* void */);
3674 }
3675
GetFullScreen(ErrorResult & aError)3676 bool nsGlobalWindowInner::GetFullScreen(ErrorResult& aError) {
3677 FORWARD_TO_OUTER_OR_THROW(GetFullscreenOuter, (), aError, false);
3678 }
3679
GetFullScreen()3680 bool nsGlobalWindowInner::GetFullScreen() {
3681 ErrorResult dummy;
3682 bool fullscreen = GetFullScreen(dummy);
3683 dummy.SuppressException();
3684 return fullscreen;
3685 }
3686
Dump(const nsAString & aStr)3687 void nsGlobalWindowInner::Dump(const nsAString& aStr) {
3688 if (!nsJSUtils::DumpEnabled()) {
3689 return;
3690 }
3691
3692 char* cstr = ToNewUTF8String(aStr);
3693
3694 #if defined(XP_MACOSX)
3695 // have to convert \r to \n so that printing to the console works
3696 char *c = cstr, *cEnd = cstr + strlen(cstr);
3697 while (c < cEnd) {
3698 if (*c == '\r') *c = '\n';
3699 c++;
3700 }
3701 #endif
3702
3703 if (cstr) {
3704 MOZ_LOG(nsContentUtils::DOMDumpLog(), LogLevel::Debug,
3705 ("[Window.Dump] %s", cstr));
3706 #ifdef XP_WIN
3707 PrintToDebugger(cstr);
3708 #endif
3709 #ifdef ANDROID
3710 __android_log_write(ANDROID_LOG_INFO, "GeckoDump", cstr);
3711 #endif
3712 FILE* fp = gDumpFile ? gDumpFile : stdout;
3713 fputs(cstr, fp);
3714 fflush(fp);
3715 free(cstr);
3716 }
3717 }
3718
Alert(nsIPrincipal & aSubjectPrincipal,ErrorResult & aError)3719 void nsGlobalWindowInner::Alert(nsIPrincipal& aSubjectPrincipal,
3720 ErrorResult& aError) {
3721 Alert(u""_ns, aSubjectPrincipal, aError);
3722 }
3723
Alert(const nsAString & aMessage,nsIPrincipal & aSubjectPrincipal,ErrorResult & aError)3724 void nsGlobalWindowInner::Alert(const nsAString& aMessage,
3725 nsIPrincipal& aSubjectPrincipal,
3726 ErrorResult& aError) {
3727 FORWARD_TO_OUTER_OR_THROW(AlertOuter, (aMessage, aSubjectPrincipal, aError),
3728 aError, );
3729 }
3730
Confirm(const nsAString & aMessage,nsIPrincipal & aSubjectPrincipal,ErrorResult & aError)3731 bool nsGlobalWindowInner::Confirm(const nsAString& aMessage,
3732 nsIPrincipal& aSubjectPrincipal,
3733 ErrorResult& aError) {
3734 FORWARD_TO_OUTER_OR_THROW(ConfirmOuter, (aMessage, aSubjectPrincipal, aError),
3735 aError, false);
3736 }
3737
Fetch(const RequestOrUSVString & aInput,const RequestInit & aInit,CallerType aCallerType,ErrorResult & aRv)3738 already_AddRefed<Promise> nsGlobalWindowInner::Fetch(
3739 const RequestOrUSVString& aInput, const RequestInit& aInit,
3740 CallerType aCallerType, ErrorResult& aRv) {
3741 return FetchRequest(this, aInput, aInit, aCallerType, aRv);
3742 }
3743
Prompt(const nsAString & aMessage,const nsAString & aInitial,nsAString & aReturn,nsIPrincipal & aSubjectPrincipal,ErrorResult & aError)3744 void nsGlobalWindowInner::Prompt(const nsAString& aMessage,
3745 const nsAString& aInitial, nsAString& aReturn,
3746 nsIPrincipal& aSubjectPrincipal,
3747 ErrorResult& aError) {
3748 FORWARD_TO_OUTER_OR_THROW(
3749 PromptOuter, (aMessage, aInitial, aReturn, aSubjectPrincipal, aError),
3750 aError, );
3751 }
3752
Focus(CallerType aCallerType,ErrorResult & aError)3753 void nsGlobalWindowInner::Focus(CallerType aCallerType, ErrorResult& aError) {
3754 FORWARD_TO_OUTER_OR_THROW(FocusOuter,
3755 (aCallerType, /* aFromOtherProcess */ false,
3756 nsFocusManager::GenerateFocusActionId()),
3757 aError, );
3758 }
3759
Focus(CallerType aCallerType)3760 nsresult nsGlobalWindowInner::Focus(CallerType aCallerType) {
3761 ErrorResult rv;
3762 Focus(aCallerType, rv);
3763
3764 return rv.StealNSResult();
3765 }
3766
Blur(CallerType aCallerType,ErrorResult & aError)3767 void nsGlobalWindowInner::Blur(CallerType aCallerType, ErrorResult& aError) {
3768 FORWARD_TO_OUTER_OR_THROW(BlurOuter, (aCallerType), aError, );
3769 }
3770
Stop(ErrorResult & aError)3771 void nsGlobalWindowInner::Stop(ErrorResult& aError) {
3772 FORWARD_TO_OUTER_OR_THROW(StopOuter, (aError), aError, );
3773 }
3774
Print(ErrorResult & aError)3775 void nsGlobalWindowInner::Print(ErrorResult& aError) {
3776 FORWARD_TO_OUTER_OR_THROW(PrintOuter, (aError), aError, );
3777 }
3778
PrintPreview(nsIPrintSettings * aSettings,nsIWebProgressListener * aListener,nsIDocShell * aDocShellToCloneInto,ErrorResult & aError)3779 Nullable<WindowProxyHolder> nsGlobalWindowInner::PrintPreview(
3780 nsIPrintSettings* aSettings, nsIWebProgressListener* aListener,
3781 nsIDocShell* aDocShellToCloneInto, ErrorResult& aError) {
3782 FORWARD_TO_OUTER_OR_THROW(Print,
3783 (aSettings, aListener, aDocShellToCloneInto,
3784 nsGlobalWindowOuter::IsPreview::Yes,
3785 nsGlobalWindowOuter::IsForWindowDotPrint::No,
3786 /* aPrintPreviewCallback = */ nullptr, aError),
3787 aError, nullptr);
3788 }
3789
MoveTo(int32_t aXPos,int32_t aYPos,CallerType aCallerType,ErrorResult & aError)3790 void nsGlobalWindowInner::MoveTo(int32_t aXPos, int32_t aYPos,
3791 CallerType aCallerType, ErrorResult& aError) {
3792 FORWARD_TO_OUTER_OR_THROW(MoveToOuter, (aXPos, aYPos, aCallerType, aError),
3793 aError, );
3794 }
3795
MoveBy(int32_t aXDif,int32_t aYDif,CallerType aCallerType,ErrorResult & aError)3796 void nsGlobalWindowInner::MoveBy(int32_t aXDif, int32_t aYDif,
3797 CallerType aCallerType, ErrorResult& aError) {
3798 FORWARD_TO_OUTER_OR_THROW(MoveByOuter, (aXDif, aYDif, aCallerType, aError),
3799 aError, );
3800 }
3801
ResizeTo(int32_t aWidth,int32_t aHeight,CallerType aCallerType,ErrorResult & aError)3802 void nsGlobalWindowInner::ResizeTo(int32_t aWidth, int32_t aHeight,
3803 CallerType aCallerType,
3804 ErrorResult& aError) {
3805 FORWARD_TO_OUTER_OR_THROW(ResizeToOuter,
3806 (aWidth, aHeight, aCallerType, aError), aError, );
3807 }
3808
ResizeBy(int32_t aWidthDif,int32_t aHeightDif,CallerType aCallerType,ErrorResult & aError)3809 void nsGlobalWindowInner::ResizeBy(int32_t aWidthDif, int32_t aHeightDif,
3810 CallerType aCallerType,
3811 ErrorResult& aError) {
3812 FORWARD_TO_OUTER_OR_THROW(
3813 ResizeByOuter, (aWidthDif, aHeightDif, aCallerType, aError), aError, );
3814 }
3815
SizeToContent(CallerType aCallerType,ErrorResult & aError)3816 void nsGlobalWindowInner::SizeToContent(CallerType aCallerType,
3817 ErrorResult& aError) {
3818 FORWARD_TO_OUTER_OR_THROW(SizeToContentOuter, (aCallerType, aError),
3819 aError, );
3820 }
3821
GetTopWindowRoot()3822 already_AddRefed<nsPIWindowRoot> nsGlobalWindowInner::GetTopWindowRoot() {
3823 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
3824 if (!outer) {
3825 return nullptr;
3826 }
3827 return outer->GetTopWindowRoot();
3828 }
3829
Scroll(double aXScroll,double aYScroll)3830 void nsGlobalWindowInner::Scroll(double aXScroll, double aYScroll) {
3831 // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
3832 auto scrollPos = CSSIntPoint::Truncate(mozilla::ToZeroIfNonfinite(aXScroll),
3833 mozilla::ToZeroIfNonfinite(aYScroll));
3834 ScrollTo(scrollPos, ScrollOptions());
3835 }
3836
ScrollTo(double aXScroll,double aYScroll)3837 void nsGlobalWindowInner::ScrollTo(double aXScroll, double aYScroll) {
3838 // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
3839 auto scrollPos = CSSIntPoint::Truncate(mozilla::ToZeroIfNonfinite(aXScroll),
3840 mozilla::ToZeroIfNonfinite(aYScroll));
3841 ScrollTo(scrollPos, ScrollOptions());
3842 }
3843
ScrollTo(const ScrollToOptions & aOptions)3844 void nsGlobalWindowInner::ScrollTo(const ScrollToOptions& aOptions) {
3845 // When scrolling to a non-zero offset, we need to determine whether that
3846 // position is within our scrollable range, so we need updated layout
3847 // information which requires a layout flush, otherwise all we need is to
3848 // flush frames to be able to access our scrollable frame here.
3849 FlushType flushType =
3850 ((aOptions.mLeft.WasPassed() && aOptions.mLeft.Value() > 0) ||
3851 (aOptions.mTop.WasPassed() && aOptions.mTop.Value() > 0))
3852 ? FlushType::Layout
3853 : FlushType::Frames;
3854 FlushPendingNotifications(flushType);
3855 nsIScrollableFrame* sf = GetScrollFrame();
3856
3857 if (sf) {
3858 CSSIntPoint scrollPos = sf->GetScrollPositionCSSPixels();
3859 if (aOptions.mLeft.WasPassed()) {
3860 scrollPos.x = mozilla::ToZeroIfNonfinite(aOptions.mLeft.Value());
3861 }
3862 if (aOptions.mTop.WasPassed()) {
3863 scrollPos.y = mozilla::ToZeroIfNonfinite(aOptions.mTop.Value());
3864 }
3865
3866 ScrollTo(scrollPos, aOptions);
3867 }
3868 }
3869
Scroll(const ScrollToOptions & aOptions)3870 void nsGlobalWindowInner::Scroll(const ScrollToOptions& aOptions) {
3871 ScrollTo(aOptions);
3872 }
3873
ScrollTo(const CSSIntPoint & aScroll,const ScrollOptions & aOptions)3874 void nsGlobalWindowInner::ScrollTo(const CSSIntPoint& aScroll,
3875 const ScrollOptions& aOptions) {
3876 // When scrolling to a non-zero offset, we need to determine whether that
3877 // position is within our scrollable range, so we need updated layout
3878 // information which requires a layout flush, otherwise all we need is to
3879 // flush frames to be able to access our scrollable frame here.
3880 FlushType flushType =
3881 (aScroll.x || aScroll.y) ? FlushType::Layout : FlushType::Frames;
3882 FlushPendingNotifications(flushType);
3883 nsIScrollableFrame* sf = GetScrollFrame();
3884
3885 if (sf) {
3886 // Here we calculate what the max pixel value is that we can
3887 // scroll to, we do this by dividing maxint with the pixel to
3888 // twips conversion factor, and subtracting 4, the 4 comes from
3889 // experimenting with this value, anything less makes the view
3890 // code not scroll correctly, I have no idea why. -- jst
3891 const int32_t maxpx = nsPresContext::AppUnitsToIntCSSPixels(0x7fffffff) - 4;
3892
3893 CSSIntPoint scroll(aScroll);
3894 if (scroll.x > maxpx) {
3895 scroll.x = maxpx;
3896 }
3897
3898 if (scroll.y > maxpx) {
3899 scroll.y = maxpx;
3900 }
3901
3902 ScrollMode scrollMode = sf->IsSmoothScroll(aOptions.mBehavior)
3903 ? ScrollMode::SmoothMsd
3904 : ScrollMode::Instant;
3905
3906 sf->ScrollToCSSPixels(scroll, scrollMode);
3907 }
3908 }
3909
ScrollBy(double aXScrollDif,double aYScrollDif)3910 void nsGlobalWindowInner::ScrollBy(double aXScrollDif, double aYScrollDif) {
3911 FlushPendingNotifications(FlushType::Layout);
3912 nsIScrollableFrame* sf = GetScrollFrame();
3913
3914 if (sf) {
3915 // It seems like it would make more sense for ScrollBy to use
3916 // SMOOTH mode, but tests seem to depend on the synchronous behaviour.
3917 // Perhaps Web content does too.
3918 ScrollToOptions options;
3919 options.mLeft.Construct(aXScrollDif);
3920 options.mTop.Construct(aYScrollDif);
3921 ScrollBy(options);
3922 }
3923 }
3924
ScrollBy(const ScrollToOptions & aOptions)3925 void nsGlobalWindowInner::ScrollBy(const ScrollToOptions& aOptions) {
3926 FlushPendingNotifications(FlushType::Layout);
3927 nsIScrollableFrame* sf = GetScrollFrame();
3928
3929 if (sf) {
3930 CSSIntPoint scrollDelta;
3931 if (aOptions.mLeft.WasPassed()) {
3932 scrollDelta.x = mozilla::ToZeroIfNonfinite(aOptions.mLeft.Value());
3933 }
3934 if (aOptions.mTop.WasPassed()) {
3935 scrollDelta.y = mozilla::ToZeroIfNonfinite(aOptions.mTop.Value());
3936 }
3937
3938 ScrollMode scrollMode = sf->IsSmoothScroll(aOptions.mBehavior)
3939 ? ScrollMode::SmoothMsd
3940 : ScrollMode::Instant;
3941
3942 sf->ScrollByCSSPixels(scrollDelta, scrollMode,
3943 mozilla::ScrollOrigin::Relative);
3944 }
3945 }
3946
ScrollByLines(int32_t numLines,const ScrollOptions & aOptions)3947 void nsGlobalWindowInner::ScrollByLines(int32_t numLines,
3948 const ScrollOptions& aOptions) {
3949 FlushPendingNotifications(FlushType::Layout);
3950 nsIScrollableFrame* sf = GetScrollFrame();
3951 if (sf) {
3952 // It seems like it would make more sense for ScrollByLines to use
3953 // SMOOTH mode, but tests seem to depend on the synchronous behaviour.
3954 // Perhaps Web content does too.
3955 ScrollMode scrollMode = sf->IsSmoothScroll(aOptions.mBehavior)
3956 ? ScrollMode::SmoothMsd
3957 : ScrollMode::Instant;
3958
3959 sf->ScrollBy(nsIntPoint(0, numLines), ScrollUnit::LINES, scrollMode);
3960 }
3961 }
3962
ScrollByPages(int32_t numPages,const ScrollOptions & aOptions)3963 void nsGlobalWindowInner::ScrollByPages(int32_t numPages,
3964 const ScrollOptions& aOptions) {
3965 FlushPendingNotifications(FlushType::Layout);
3966 nsIScrollableFrame* sf = GetScrollFrame();
3967 if (sf) {
3968 // It seems like it would make more sense for ScrollByPages to use
3969 // SMOOTH mode, but tests seem to depend on the synchronous behaviour.
3970 // Perhaps Web content does too.
3971 ScrollMode scrollMode = sf->IsSmoothScroll(aOptions.mBehavior)
3972 ? ScrollMode::SmoothMsd
3973 : ScrollMode::Instant;
3974
3975 sf->ScrollBy(nsIntPoint(0, numPages), ScrollUnit::PAGES, scrollMode);
3976 }
3977 }
3978
MozScrollSnap()3979 void nsGlobalWindowInner::MozScrollSnap() {
3980 FlushPendingNotifications(FlushType::Layout);
3981 nsIScrollableFrame* sf = GetScrollFrame();
3982 if (sf) {
3983 sf->ScrollSnap();
3984 }
3985 }
3986
ClearTimeout(int32_t aHandle)3987 void nsGlobalWindowInner::ClearTimeout(int32_t aHandle) {
3988 DebuggerNotificationDispatch(this, DebuggerNotificationType::ClearTimeout);
3989
3990 if (aHandle > 0) {
3991 mTimeoutManager->ClearTimeout(aHandle, Timeout::Reason::eTimeoutOrInterval);
3992 }
3993 }
3994
ClearInterval(int32_t aHandle)3995 void nsGlobalWindowInner::ClearInterval(int32_t aHandle) {
3996 DebuggerNotificationDispatch(this, DebuggerNotificationType::ClearInterval);
3997
3998 if (aHandle > 0) {
3999 mTimeoutManager->ClearTimeout(aHandle, Timeout::Reason::eTimeoutOrInterval);
4000 }
4001 }
4002
SetResizable(bool aResizable) const4003 void nsGlobalWindowInner::SetResizable(bool aResizable) const {
4004 // nop
4005 }
4006
CaptureEvents()4007 void nsGlobalWindowInner::CaptureEvents() {
4008 if (mDoc) {
4009 mDoc->WarnOnceAbout(DeprecatedOperations::eUseOfCaptureEvents);
4010 }
4011 }
4012
ReleaseEvents()4013 void nsGlobalWindowInner::ReleaseEvents() {
4014 if (mDoc) {
4015 mDoc->WarnOnceAbout(DeprecatedOperations::eUseOfReleaseEvents);
4016 }
4017 }
4018
Open(const nsAString & aUrl,const nsAString & aName,const nsAString & aOptions,ErrorResult & aError)4019 Nullable<WindowProxyHolder> nsGlobalWindowInner::Open(const nsAString& aUrl,
4020 const nsAString& aName,
4021 const nsAString& aOptions,
4022 ErrorResult& aError) {
4023 FORWARD_TO_OUTER_OR_THROW(OpenOuter, (aUrl, aName, aOptions, aError), aError,
4024 nullptr);
4025 }
4026
OpenDialog(JSContext * aCx,const nsAString & aUrl,const nsAString & aName,const nsAString & aOptions,const Sequence<JS::Value> & aExtraArgument,ErrorResult & aError)4027 Nullable<WindowProxyHolder> nsGlobalWindowInner::OpenDialog(
4028 JSContext* aCx, const nsAString& aUrl, const nsAString& aName,
4029 const nsAString& aOptions, const Sequence<JS::Value>& aExtraArgument,
4030 ErrorResult& aError) {
4031 FORWARD_TO_OUTER_OR_THROW(
4032 OpenDialogOuter, (aCx, aUrl, aName, aOptions, aExtraArgument, aError),
4033 aError, nullptr);
4034 }
4035
GetFrames(ErrorResult & aError)4036 WindowProxyHolder nsGlobalWindowInner::GetFrames(ErrorResult& aError) {
4037 FORWARD_TO_OUTER_OR_THROW(GetFramesOuter, (), aError, Window());
4038 }
4039
PostMessageMoz(JSContext * aCx,JS::Handle<JS::Value> aMessage,const nsAString & aTargetOrigin,JS::Handle<JS::Value> aTransfer,nsIPrincipal & aSubjectPrincipal,ErrorResult & aError)4040 void nsGlobalWindowInner::PostMessageMoz(JSContext* aCx,
4041 JS::Handle<JS::Value> aMessage,
4042 const nsAString& aTargetOrigin,
4043 JS::Handle<JS::Value> aTransfer,
4044 nsIPrincipal& aSubjectPrincipal,
4045 ErrorResult& aError) {
4046 FORWARD_TO_OUTER_OR_THROW(
4047 PostMessageMozOuter,
4048 (aCx, aMessage, aTargetOrigin, aTransfer, aSubjectPrincipal, aError),
4049 aError, );
4050 }
4051
PostMessageMoz(JSContext * aCx,JS::Handle<JS::Value> aMessage,const nsAString & aTargetOrigin,const Sequence<JSObject * > & aTransfer,nsIPrincipal & aSubjectPrincipal,ErrorResult & aRv)4052 void nsGlobalWindowInner::PostMessageMoz(JSContext* aCx,
4053 JS::Handle<JS::Value> aMessage,
4054 const nsAString& aTargetOrigin,
4055 const Sequence<JSObject*>& aTransfer,
4056 nsIPrincipal& aSubjectPrincipal,
4057 ErrorResult& aRv) {
4058 JS::Rooted<JS::Value> transferArray(aCx, JS::UndefinedValue());
4059
4060 aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransfer,
4061 &transferArray);
4062 if (NS_WARN_IF(aRv.Failed())) {
4063 return;
4064 }
4065
4066 PostMessageMoz(aCx, aMessage, aTargetOrigin, transferArray, aSubjectPrincipal,
4067 aRv);
4068 }
4069
PostMessageMoz(JSContext * aCx,JS::Handle<JS::Value> aMessage,const WindowPostMessageOptions & aOptions,nsIPrincipal & aSubjectPrincipal,ErrorResult & aRv)4070 void nsGlobalWindowInner::PostMessageMoz(
4071 JSContext* aCx, JS::Handle<JS::Value> aMessage,
4072 const WindowPostMessageOptions& aOptions, nsIPrincipal& aSubjectPrincipal,
4073 ErrorResult& aRv) {
4074 JS::Rooted<JS::Value> transferArray(aCx, JS::UndefinedValue());
4075
4076 aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(
4077 aCx, aOptions.mTransfer, &transferArray);
4078 if (NS_WARN_IF(aRv.Failed())) {
4079 return;
4080 }
4081
4082 PostMessageMoz(aCx, aMessage, aOptions.mTargetOrigin, transferArray,
4083 aSubjectPrincipal, aRv);
4084 }
4085
Close(CallerType aCallerType,ErrorResult & aError)4086 void nsGlobalWindowInner::Close(CallerType aCallerType, ErrorResult& aError) {
4087 FORWARD_TO_OUTER_OR_THROW(CloseOuter, (aCallerType == CallerType::System),
4088 aError, );
4089 }
4090
Close()4091 nsresult nsGlobalWindowInner::Close() {
4092 FORWARD_TO_OUTER(Close, (), NS_ERROR_UNEXPECTED);
4093 }
4094
IsInModalState()4095 bool nsGlobalWindowInner::IsInModalState() {
4096 FORWARD_TO_OUTER(IsInModalState, (), false);
4097 }
4098
4099 // static
NotifyDOMWindowDestroyed(nsGlobalWindowInner * aWindow)4100 void nsGlobalWindowInner::NotifyDOMWindowDestroyed(
4101 nsGlobalWindowInner* aWindow) {
4102 nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
4103 if (observerService) {
4104 observerService->NotifyObservers(ToSupports(aWindow),
4105 DOM_WINDOW_DESTROYED_TOPIC, nullptr);
4106 }
4107 }
4108
NotifyWindowIDDestroyed(const char * aTopic)4109 void nsGlobalWindowInner::NotifyWindowIDDestroyed(const char* aTopic) {
4110 nsCOMPtr<nsIRunnable> runnable =
4111 new WindowDestroyedEvent(this, mWindowID, aTopic);
4112 Dispatch(TaskCategory::Other, runnable.forget());
4113 }
4114
4115 // static
NotifyDOMWindowFrozen(nsGlobalWindowInner * aWindow)4116 void nsGlobalWindowInner::NotifyDOMWindowFrozen(nsGlobalWindowInner* aWindow) {
4117 if (aWindow) {
4118 nsCOMPtr<nsIObserverService> observerService =
4119 services::GetObserverService();
4120 if (observerService) {
4121 observerService->NotifyObservers(ToSupports(aWindow),
4122 DOM_WINDOW_FROZEN_TOPIC, nullptr);
4123 }
4124 }
4125 }
4126
4127 // static
NotifyDOMWindowThawed(nsGlobalWindowInner * aWindow)4128 void nsGlobalWindowInner::NotifyDOMWindowThawed(nsGlobalWindowInner* aWindow) {
4129 if (aWindow) {
4130 nsCOMPtr<nsIObserverService> observerService =
4131 services::GetObserverService();
4132 if (observerService) {
4133 observerService->NotifyObservers(ToSupports(aWindow),
4134 DOM_WINDOW_THAWED_TOPIC, nullptr);
4135 }
4136 }
4137 }
4138
GetFrameElement(nsIPrincipal & aSubjectPrincipal,ErrorResult & aError)4139 Element* nsGlobalWindowInner::GetFrameElement(nsIPrincipal& aSubjectPrincipal,
4140 ErrorResult& aError) {
4141 FORWARD_TO_OUTER_OR_THROW(GetFrameElement, (aSubjectPrincipal), aError,
4142 nullptr);
4143 }
4144
GetRealFrameElement(ErrorResult & aError)4145 Element* nsGlobalWindowInner::GetRealFrameElement(ErrorResult& aError) {
4146 FORWARD_TO_OUTER_OR_THROW(GetFrameElement, (), aError, nullptr);
4147 }
4148
UpdateCommands(const nsAString & anAction,Selection * aSel,int16_t aReason)4149 void nsGlobalWindowInner::UpdateCommands(const nsAString& anAction,
4150 Selection* aSel, int16_t aReason) {
4151 if (GetOuterWindowInternal()) {
4152 GetOuterWindowInternal()->UpdateCommands(anAction, aSel, aReason);
4153 }
4154 }
4155
GetSelection(ErrorResult & aError)4156 Selection* nsGlobalWindowInner::GetSelection(ErrorResult& aError) {
4157 FORWARD_TO_OUTER_OR_THROW(GetSelectionOuter, (), aError, nullptr);
4158 }
4159
Find(const nsAString & aString,bool aCaseSensitive,bool aBackwards,bool aWrapAround,bool aWholeWord,bool aSearchInFrames,bool aShowDialog,ErrorResult & aError)4160 bool nsGlobalWindowInner::Find(const nsAString& aString, bool aCaseSensitive,
4161 bool aBackwards, bool aWrapAround,
4162 bool aWholeWord, bool aSearchInFrames,
4163 bool aShowDialog, ErrorResult& aError) {
4164 FORWARD_TO_OUTER_OR_THROW(FindOuter,
4165 (aString, aCaseSensitive, aBackwards, aWrapAround,
4166 aWholeWord, aSearchInFrames, aShowDialog, aError),
4167 aError, false);
4168 }
4169
GetOrigin(nsAString & aOrigin)4170 void nsGlobalWindowInner::GetOrigin(nsAString& aOrigin) {
4171 nsContentUtils::GetUTFOrigin(GetPrincipal(), aOrigin);
4172 }
4173
Atob(const nsAString & aAsciiBase64String,nsAString & aBinaryData,ErrorResult & aError)4174 void nsGlobalWindowInner::Atob(const nsAString& aAsciiBase64String,
4175 nsAString& aBinaryData, ErrorResult& aError) {
4176 aError = nsContentUtils::Atob(aAsciiBase64String, aBinaryData);
4177 }
4178
Btoa(const nsAString & aBinaryData,nsAString & aAsciiBase64String,ErrorResult & aError)4179 void nsGlobalWindowInner::Btoa(const nsAString& aBinaryData,
4180 nsAString& aAsciiBase64String,
4181 ErrorResult& aError) {
4182 aError = nsContentUtils::Btoa(aBinaryData, aAsciiBase64String);
4183 }
4184
4185 //*****************************************************************************
4186 // EventTarget
4187 //*****************************************************************************
4188
GetOwnerGlobalForBindingsInternal()4189 nsPIDOMWindowOuter* nsGlobalWindowInner::GetOwnerGlobalForBindingsInternal() {
4190 return nsPIDOMWindowOuter::GetFromCurrentInner(this);
4191 }
4192
DispatchEvent(Event & aEvent,CallerType aCallerType,ErrorResult & aRv)4193 bool nsGlobalWindowInner::DispatchEvent(Event& aEvent, CallerType aCallerType,
4194 ErrorResult& aRv) {
4195 if (!IsCurrentInnerWindow()) {
4196 NS_WARNING(
4197 "DispatchEvent called on non-current inner window, dropping. "
4198 "Please check the window in the caller instead.");
4199 aRv.Throw(NS_ERROR_FAILURE);
4200 return false;
4201 }
4202
4203 if (!mDoc) {
4204 aRv.Throw(NS_ERROR_FAILURE);
4205 return false;
4206 }
4207
4208 // Obtain a presentation shell
4209 RefPtr<nsPresContext> presContext = mDoc->GetPresContext();
4210
4211 nsEventStatus status = nsEventStatus_eIgnore;
4212 nsresult rv = EventDispatcher::DispatchDOMEvent(
4213 ToSupports(this), nullptr, &aEvent, presContext, &status);
4214 bool retval = !aEvent.DefaultPrevented(aCallerType);
4215 if (NS_FAILED(rv)) {
4216 aRv.Throw(rv);
4217 }
4218 return retval;
4219 }
4220
4221 mozilla::Maybe<mozilla::dom::EventCallbackDebuggerNotificationType>
GetDebuggerNotificationType() const4222 nsGlobalWindowInner::GetDebuggerNotificationType() const {
4223 return mozilla::Some(
4224 mozilla::dom::EventCallbackDebuggerNotificationType::Global);
4225 }
4226
ComputeDefaultWantsUntrusted(ErrorResult & aRv)4227 bool nsGlobalWindowInner::ComputeDefaultWantsUntrusted(ErrorResult& aRv) {
4228 return !nsContentUtils::IsChromeDoc(mDoc);
4229 }
4230
GetOrCreateListenerManager()4231 EventListenerManager* nsGlobalWindowInner::GetOrCreateListenerManager() {
4232 if (!mListenerManager) {
4233 mListenerManager =
4234 new EventListenerManager(static_cast<EventTarget*>(this));
4235 }
4236
4237 return mListenerManager;
4238 }
4239
GetExistingListenerManager() const4240 EventListenerManager* nsGlobalWindowInner::GetExistingListenerManager() const {
4241 return mListenerManager;
4242 }
4243
4244 mozilla::dom::DebuggerNotificationManager*
GetOrCreateDebuggerNotificationManager()4245 nsGlobalWindowInner::GetOrCreateDebuggerNotificationManager() {
4246 if (!mDebuggerNotificationManager) {
4247 mDebuggerNotificationManager = new DebuggerNotificationManager(this);
4248 }
4249
4250 return mDebuggerNotificationManager;
4251 }
4252
4253 mozilla::dom::DebuggerNotificationManager*
GetExistingDebuggerNotificationManager()4254 nsGlobalWindowInner::GetExistingDebuggerNotificationManager() {
4255 return mDebuggerNotificationManager;
4256 }
4257
4258 //*****************************************************************************
4259 // nsGlobalWindowInner::nsPIDOMWindow
4260 //*****************************************************************************
4261
Location()4262 Location* nsGlobalWindowInner::Location() {
4263 if (!mLocation) {
4264 mLocation = new dom::Location(this, GetBrowsingContext());
4265 }
4266
4267 return mLocation;
4268 }
4269
MaybeUpdateTouchState()4270 void nsGlobalWindowInner::MaybeUpdateTouchState() {
4271 if (mMayHaveTouchEventListener) {
4272 nsCOMPtr<nsIObserverService> observerService =
4273 services::GetObserverService();
4274
4275 if (observerService) {
4276 observerService->NotifyObservers(static_cast<nsIDOMWindow*>(this),
4277 DOM_TOUCH_LISTENER_ADDED, nullptr);
4278 }
4279 }
4280 }
4281
EnableGamepadUpdates()4282 void nsGlobalWindowInner::EnableGamepadUpdates() {
4283 if (mHasGamepad) {
4284 RefPtr<GamepadManager> gamepadManager(GamepadManager::GetService());
4285 if (gamepadManager) {
4286 gamepadManager->AddListener(this);
4287 }
4288 }
4289 }
4290
DisableGamepadUpdates()4291 void nsGlobalWindowInner::DisableGamepadUpdates() {
4292 if (mHasGamepad) {
4293 RefPtr<GamepadManager> gamepadManager(GamepadManager::GetService());
4294 if (gamepadManager) {
4295 gamepadManager->RemoveListener(this);
4296 }
4297 }
4298 }
4299
EnableVRUpdates()4300 void nsGlobalWindowInner::EnableVRUpdates() {
4301 // We need to create a VREventObserver before we can either detect XR runtimes
4302 // or start an XR session
4303 if (!mVREventObserver && (mHasXRSession || mXRRuntimeDetectionInFlight)) {
4304 // Assert that we are not creating the observer while IsDying() as
4305 // that would result in a leak. VREventObserver holds a RefPtr to
4306 // this nsGlobalWindowInner and would prevent it from being deallocated.
4307 MOZ_ASSERT(!IsDying(),
4308 "Creating a VREventObserver for an nsGlobalWindow that is "
4309 "dying would cause it to leak.");
4310 mVREventObserver = new VREventObserver(this);
4311 }
4312 // If the content has an XR session, then we need to tell
4313 // VREventObserver that there is VR activity.
4314 if (mHasXRSession) {
4315 nsPIDOMWindowOuter* outer = GetOuterWindow();
4316 if (outer && !outer->IsBackground()) {
4317 StartVRActivity();
4318 }
4319 }
4320 }
4321
DisableVRUpdates()4322 void nsGlobalWindowInner::DisableVRUpdates() {
4323 if (mVREventObserver) {
4324 mVREventObserver->DisconnectFromOwner();
4325 mVREventObserver = nullptr;
4326 }
4327 }
4328
ResetVRTelemetry(bool aUpdate)4329 void nsGlobalWindowInner::ResetVRTelemetry(bool aUpdate) {
4330 if (mVREventObserver) {
4331 mVREventObserver->UpdateSpentTimeIn2DTelemetry(aUpdate);
4332 }
4333 }
4334
StartVRActivity()4335 void nsGlobalWindowInner::StartVRActivity() {
4336 /**
4337 * If the content has an XR session, tell
4338 * the VREventObserver that the window is accessing
4339 * VR devices.
4340 *
4341 * It's possible to have a VREventObserver without
4342 * and XR session, if we are using it to get updates
4343 * about XR runtime enumeration. In this case,
4344 * we would not tell the VREventObserver that
4345 * we are accessing VR devices.
4346 */
4347 if (mVREventObserver && mHasXRSession) {
4348 mVREventObserver->StartActivity();
4349 }
4350 }
4351
StopVRActivity()4352 void nsGlobalWindowInner::StopVRActivity() {
4353 /**
4354 * If the content has an XR session, tell
4355 * the VReventObserver that the window is no longer
4356 * accessing VR devices. This does not stop the
4357 * XR session itself, which may be resumed with
4358 * EnableVRUpdates.
4359 * It's possible to have a VREventObserver without
4360 * and XR session, if we are using it to get updates
4361 * about XR runtime enumeration. In this case,
4362 * we would not tell the VREventObserver that
4363 * we ending an activity that accesses VR devices.
4364 */
4365 if (mVREventObserver && mHasXRSession) {
4366 mVREventObserver->StopActivity();
4367 }
4368 }
4369
SetFocusedElement(Element * aElement,uint32_t aFocusMethod,bool aNeedsFocus)4370 void nsGlobalWindowInner::SetFocusedElement(Element* aElement,
4371 uint32_t aFocusMethod,
4372 bool aNeedsFocus) {
4373 if (aElement && aElement->GetComposedDoc() != mDoc) {
4374 NS_WARNING("Trying to set focus to a node from a wrong document");
4375 return;
4376 }
4377
4378 if (IsDying()) {
4379 NS_ASSERTION(!aElement, "Trying to focus cleaned up window!");
4380 aElement = nullptr;
4381 aNeedsFocus = false;
4382 }
4383 if (mFocusedElement != aElement) {
4384 UpdateCanvasFocus(false, aElement);
4385 mFocusedElement = aElement;
4386 // TODO: Maybe this should be set on refocus too?
4387 mFocusMethod = aFocusMethod & nsIFocusManager::METHOD_MASK;
4388 }
4389
4390 if (mFocusedElement) {
4391 // if a node was focused by a keypress, turn on focus rings for the
4392 // window.
4393 if (mFocusMethod & nsIFocusManager::FLAG_BYKEY) {
4394 mUnknownFocusMethodShouldShowOutline = true;
4395 mFocusByKeyOccurred = true;
4396 } else if (nsFocusManager::GetFocusMoveActionCause(mFocusMethod) !=
4397 widget::InputContextAction::CAUSE_UNKNOWN) {
4398 mUnknownFocusMethodShouldShowOutline = false;
4399 } else if (aFocusMethod & nsIFocusManager::FLAG_NOSHOWRING) {
4400 // If we get focused via script, and script has explicitly opted out of
4401 // outlines via FLAG_NOSHOWRING, we don't want to make a refocus start
4402 // showing outlines.
4403 mUnknownFocusMethodShouldShowOutline = false;
4404 }
4405 }
4406
4407 if (aNeedsFocus) {
4408 mNeedsFocus = aNeedsFocus;
4409 }
4410 }
4411
GetFocusMethod()4412 uint32_t nsGlobalWindowInner::GetFocusMethod() { return mFocusMethod; }
4413
ShouldShowFocusRing()4414 bool nsGlobalWindowInner::ShouldShowFocusRing() {
4415 if (mFocusByKeyOccurred &&
4416 StaticPrefs::browser_display_always_show_rings_after_key_focus()) {
4417 return true;
4418 }
4419
4420 nsCOMPtr<nsPIWindowRoot> root = GetTopWindowRoot();
4421 return root && root->ShowFocusRings();
4422 }
4423
TakeFocus(bool aFocus,uint32_t aFocusMethod)4424 bool nsGlobalWindowInner::TakeFocus(bool aFocus, uint32_t aFocusMethod) {
4425 if (IsDying()) {
4426 return false;
4427 }
4428
4429 if (aFocus) {
4430 mFocusMethod = aFocusMethod & nsIFocusManager::METHOD_MASK;
4431 }
4432
4433 if (mHasFocus != aFocus) {
4434 mHasFocus = aFocus;
4435 UpdateCanvasFocus(true, mFocusedElement);
4436 }
4437
4438 // if mNeedsFocus is true, then the document has not yet received a
4439 // document-level focus event. If there is a root content node, then return
4440 // true to tell the calling focus manager that a focus event is expected. If
4441 // there is no root content node, the document hasn't loaded enough yet, or
4442 // there isn't one and there is no point in firing a focus event.
4443 if (aFocus && mNeedsFocus && mDoc && mDoc->GetRootElement() != nullptr) {
4444 mNeedsFocus = false;
4445 return true;
4446 }
4447
4448 mNeedsFocus = false;
4449 return false;
4450 }
4451
SetReadyForFocus()4452 void nsGlobalWindowInner::SetReadyForFocus() {
4453 bool oldNeedsFocus = mNeedsFocus;
4454 mNeedsFocus = false;
4455
4456 nsFocusManager* fm = nsFocusManager::GetFocusManager();
4457 if (fm) {
4458 fm->WindowShown(GetOuterWindow(), oldNeedsFocus);
4459 }
4460 }
4461
PageHidden()4462 void nsGlobalWindowInner::PageHidden() {
4463 // the window is being hidden, so tell the focus manager that the frame is
4464 // no longer valid. Use the persisted field to determine if the document
4465 // is being destroyed.
4466
4467 nsFocusManager* fm = nsFocusManager::GetFocusManager();
4468 if (fm) {
4469 fm->WindowHidden(GetOuterWindow(), nsFocusManager::GenerateFocusActionId());
4470 }
4471
4472 mNeedsFocus = true;
4473 }
4474
4475 class HashchangeCallback : public Runnable {
4476 public:
HashchangeCallback(const nsAString & aOldURL,const nsAString & aNewURL,nsGlobalWindowInner * aWindow)4477 HashchangeCallback(const nsAString& aOldURL, const nsAString& aNewURL,
4478 nsGlobalWindowInner* aWindow)
4479 : mozilla::Runnable("HashchangeCallback"), mWindow(aWindow) {
4480 MOZ_ASSERT(mWindow);
4481 mOldURL.Assign(aOldURL);
4482 mNewURL.Assign(aNewURL);
4483 }
4484
Run()4485 NS_IMETHOD Run() override {
4486 MOZ_ASSERT(NS_IsMainThread(), "Should be called on the main thread.");
4487 return mWindow->FireHashchange(mOldURL, mNewURL);
4488 }
4489
4490 private:
4491 nsString mOldURL;
4492 nsString mNewURL;
4493 RefPtr<nsGlobalWindowInner> mWindow;
4494 };
4495
DispatchAsyncHashchange(nsIURI * aOldURI,nsIURI * aNewURI)4496 nsresult nsGlobalWindowInner::DispatchAsyncHashchange(nsIURI* aOldURI,
4497 nsIURI* aNewURI) {
4498 // Make sure that aOldURI and aNewURI are identical up to the '#', and that
4499 // their hashes are different.
4500 bool equal = false;
4501 NS_ENSURE_STATE(NS_SUCCEEDED(aOldURI->EqualsExceptRef(aNewURI, &equal)) &&
4502 equal);
4503 nsAutoCString oldHash, newHash;
4504 bool oldHasHash, newHasHash;
4505 NS_ENSURE_STATE(NS_SUCCEEDED(aOldURI->GetRef(oldHash)) &&
4506 NS_SUCCEEDED(aNewURI->GetRef(newHash)) &&
4507 NS_SUCCEEDED(aOldURI->GetHasRef(&oldHasHash)) &&
4508 NS_SUCCEEDED(aNewURI->GetHasRef(&newHasHash)) &&
4509 (oldHasHash != newHasHash || !oldHash.Equals(newHash)));
4510
4511 nsAutoCString oldSpec, newSpec;
4512 nsresult rv = aOldURI->GetSpec(oldSpec);
4513 NS_ENSURE_SUCCESS(rv, rv);
4514 rv = aNewURI->GetSpec(newSpec);
4515 NS_ENSURE_SUCCESS(rv, rv);
4516
4517 NS_ConvertUTF8toUTF16 oldWideSpec(oldSpec);
4518 NS_ConvertUTF8toUTF16 newWideSpec(newSpec);
4519
4520 nsCOMPtr<nsIRunnable> callback =
4521 new HashchangeCallback(oldWideSpec, newWideSpec, this);
4522 return Dispatch(TaskCategory::Other, callback.forget());
4523 }
4524
FireHashchange(const nsAString & aOldURL,const nsAString & aNewURL)4525 nsresult nsGlobalWindowInner::FireHashchange(const nsAString& aOldURL,
4526 const nsAString& aNewURL) {
4527 // Don't do anything if the window is frozen.
4528 if (IsFrozen()) {
4529 return NS_OK;
4530 }
4531
4532 // Get a presentation shell for use in creating the hashchange event.
4533 NS_ENSURE_STATE(IsCurrentInnerWindow());
4534
4535 HashChangeEventInit init;
4536 init.mNewURL = aNewURL;
4537 init.mOldURL = aOldURL;
4538
4539 RefPtr<HashChangeEvent> event =
4540 HashChangeEvent::Constructor(this, u"hashchange"_ns, init);
4541
4542 event->SetTrusted(true);
4543
4544 ErrorResult rv;
4545 DispatchEvent(*event, rv);
4546 return rv.StealNSResult();
4547 }
4548
DispatchSyncPopState()4549 nsresult nsGlobalWindowInner::DispatchSyncPopState() {
4550 NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
4551 "Must be safe to run script here.");
4552
4553 // Bail if the window is frozen.
4554 if (IsFrozen()) {
4555 return NS_OK;
4556 }
4557
4558 // Get the document's pending state object -- it contains the data we're
4559 // going to send along with the popstate event. The object is serialized
4560 // using structured clone.
4561 nsCOMPtr<nsIVariant> stateObj;
4562 nsresult rv = mDoc->GetStateObject(getter_AddRefs(stateObj));
4563 NS_ENSURE_SUCCESS(rv, rv);
4564
4565 AutoJSAPI jsapi;
4566 bool result = jsapi.Init(this);
4567 NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
4568
4569 JSContext* cx = jsapi.cx();
4570 JS::Rooted<JS::Value> stateJSValue(cx, JS::NullValue());
4571 result = stateObj ? VariantToJsval(cx, stateObj, &stateJSValue) : true;
4572 NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
4573
4574 RootedDictionary<PopStateEventInit> init(cx);
4575 init.mState = stateJSValue;
4576
4577 RefPtr<PopStateEvent> event =
4578 PopStateEvent::Constructor(this, u"popstate"_ns, init);
4579 event->SetTrusted(true);
4580 event->SetTarget(this);
4581
4582 ErrorResult err;
4583 DispatchEvent(*event, err);
4584 return err.StealNSResult();
4585 }
4586
4587 //-------------------------------------------------------
4588 // Tells the HTMLFrame/CanvasFrame that is now has focus
UpdateCanvasFocus(bool aFocusChanged,nsIContent * aNewContent)4589 void nsGlobalWindowInner::UpdateCanvasFocus(bool aFocusChanged,
4590 nsIContent* aNewContent) {
4591 // this is called from the inner window so use GetDocShell
4592 nsIDocShell* docShell = GetDocShell();
4593 if (!docShell) return;
4594
4595 bool editable;
4596 docShell->GetEditable(&editable);
4597 if (editable) return;
4598
4599 PresShell* presShell = docShell->GetPresShell();
4600 if (!presShell || !mDoc) {
4601 return;
4602 }
4603
4604 Element* rootElement = mDoc->GetRootElement();
4605 if (rootElement) {
4606 if ((mHasFocus || aFocusChanged) &&
4607 (mFocusedElement == rootElement || aNewContent == rootElement)) {
4608 nsCanvasFrame* canvasFrame = presShell->GetCanvasFrame();
4609 if (canvasFrame) {
4610 canvasFrame->SetHasFocus(mHasFocus && rootElement == aNewContent);
4611 }
4612 }
4613 } else {
4614 // XXXbz I would expect that there is never a canvasFrame in this case...
4615 nsCanvasFrame* canvasFrame = presShell->GetCanvasFrame();
4616 if (canvasFrame) {
4617 canvasFrame->SetHasFocus(false);
4618 }
4619 }
4620 }
4621
GetComputedStyle(Element & aElt,const nsAString & aPseudoElt,ErrorResult & aError)4622 already_AddRefed<nsICSSDeclaration> nsGlobalWindowInner::GetComputedStyle(
4623 Element& aElt, const nsAString& aPseudoElt, ErrorResult& aError) {
4624 return GetComputedStyleHelper(aElt, aPseudoElt, false, aError);
4625 }
4626
4627 already_AddRefed<nsICSSDeclaration>
GetDefaultComputedStyle(Element & aElt,const nsAString & aPseudoElt,ErrorResult & aError)4628 nsGlobalWindowInner::GetDefaultComputedStyle(Element& aElt,
4629 const nsAString& aPseudoElt,
4630 ErrorResult& aError) {
4631 return GetComputedStyleHelper(aElt, aPseudoElt, true, aError);
4632 }
4633
GetComputedStyleHelper(Element & aElt,const nsAString & aPseudoElt,bool aDefaultStylesOnly,ErrorResult & aError)4634 already_AddRefed<nsICSSDeclaration> nsGlobalWindowInner::GetComputedStyleHelper(
4635 Element& aElt, const nsAString& aPseudoElt, bool aDefaultStylesOnly,
4636 ErrorResult& aError) {
4637 FORWARD_TO_OUTER_OR_THROW(GetComputedStyleHelperOuter,
4638 (aElt, aPseudoElt, aDefaultStylesOnly), aError,
4639 nullptr);
4640 }
4641
GetSessionStorage(ErrorResult & aError)4642 Storage* nsGlobalWindowInner::GetSessionStorage(ErrorResult& aError) {
4643 nsIPrincipal* principal = GetPrincipal();
4644 nsIPrincipal* storagePrincipal = GetEffectiveStoragePrincipal();
4645 BrowsingContext* browsingContext = GetBrowsingContext();
4646
4647 if (!principal || !storagePrincipal || !browsingContext ||
4648 !Storage::StoragePrefIsEnabled()) {
4649 return nullptr;
4650 }
4651
4652 if (mSessionStorage) {
4653 MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
4654 ("nsGlobalWindowInner %p has %p sessionStorage", this,
4655 mSessionStorage.get()));
4656 bool canAccess =
4657 principal->Subsumes(mSessionStorage->Principal()) &&
4658 storagePrincipal->Subsumes(mSessionStorage->StoragePrincipal());
4659 if (!canAccess) {
4660 mSessionStorage = nullptr;
4661 }
4662 }
4663
4664 if (!mSessionStorage) {
4665 nsString documentURI;
4666 if (mDoc) {
4667 aError = mDoc->GetDocumentURI(documentURI);
4668 if (NS_WARN_IF(aError.Failed())) {
4669 return nullptr;
4670 }
4671 }
4672
4673 // If the document has the sandboxed origin flag set
4674 // don't allow access to sessionStorage.
4675 if (!mDoc) {
4676 aError.Throw(NS_ERROR_FAILURE);
4677 return nullptr;
4678 }
4679
4680 if (mDoc->GetSandboxFlags() & SANDBOXED_ORIGIN) {
4681 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
4682 return nullptr;
4683 }
4684
4685 uint32_t rejectedReason = 0;
4686 StorageAccess access = StorageAllowedForWindow(this, &rejectedReason);
4687
4688 // SessionStorage is an ephemeral per-tab per-origin storage that only lives
4689 // as long as the tab is open, although it may survive browser restarts
4690 // thanks to the session store. So we interpret storage access differently
4691 // than we would for persistent per-origin storage like LocalStorage and so
4692 // it may be okay to provide SessionStorage even when we receive a value of
4693 // eDeny.
4694 //
4695 // ContentBlocking::ShouldAllowAccessFor will return false for 3 main
4696 // reasons.
4697 //
4698 // 1. Cookies are entirely blocked due to a per-origin permission
4699 // (nsICookiePermission::ACCESS_DENY for the top-level principal or this
4700 // window's principal) or the very broad BEHAVIOR_REJECT. This will return
4701 // eDeny with a reason of STATE_COOKIES_BLOCKED_BY_PERMISSION or
4702 // STATE_COOKIES_BLOCKED_ALL.
4703 //
4704 // 2. Third-party cookies are limited via BEHAVIOR_REJECT_FOREIGN and
4705 // BEHAVIOR_LIMIT_FOREIGN and this is a third-party window. This will return
4706 // eDeny with a reason of STATE_COOKIES_BLOCKED_FOREIGN.
4707 //
4708 // 3. Tracking protection (BEHAVIOR_REJECT_TRACKER and
4709 // BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN) is in effect and
4710 // IsThirdPartyTrackingResourceWindow() returned true and there wasn't a
4711 // permission that allows it. This will return ePartitionTrackersOrDeny with
4712 // a reason of STATE_COOKIES_BLOCKED_TRACKER or
4713 // STATE_COOKIES_BLOCKED_SOCIALTRACKER.
4714 //
4715 // In the 1st case, the user has explicitly indicated that they don't want
4716 // to allow any storage to the origin or all origins and so we throw an
4717 // error and deny access to SessionStorage. In the 2nd case, a legacy
4718 // decision reasoned that there's no harm in providing SessionStorage
4719 // because the information is not durable and cannot escape the current tab.
4720 // The rationale is similar for the 3rd case.
4721 if (access == StorageAccess::eDeny &&
4722 rejectedReason !=
4723 nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN) {
4724 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
4725 return nullptr;
4726 }
4727
4728 const RefPtr<SessionStorageManager> storageManager =
4729 browsingContext->GetSessionStorageManager();
4730 if (!storageManager) {
4731 aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
4732 return nullptr;
4733 }
4734
4735 RefPtr<Storage> storage;
4736 aError = storageManager->CreateStorage(this, principal, storagePrincipal,
4737 documentURI, IsPrivateBrowsing(),
4738 getter_AddRefs(storage));
4739 if (aError.Failed()) {
4740 return nullptr;
4741 }
4742
4743 mSessionStorage = storage;
4744 MOZ_ASSERT(mSessionStorage);
4745
4746 MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
4747 ("nsGlobalWindowInner %p tried to get a new sessionStorage %p",
4748 this, mSessionStorage.get()));
4749
4750 if (!mSessionStorage) {
4751 aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
4752 return nullptr;
4753 }
4754 }
4755
4756 MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug,
4757 ("nsGlobalWindowInner %p returns %p sessionStorage", this,
4758 mSessionStorage.get()));
4759
4760 return mSessionStorage;
4761 }
4762
GetLocalStorage(ErrorResult & aError)4763 Storage* nsGlobalWindowInner::GetLocalStorage(ErrorResult& aError) {
4764 if (!Storage::StoragePrefIsEnabled()) {
4765 return nullptr;
4766 }
4767
4768 // LocalStorage needs to be exposed in every context except for sandboxes and
4769 // NullPrincipals (data: URLs, for instance). But we need to keep data
4770 // separate in some scenarios: private-browsing and partitioned trackers.
4771 // In private-browsing, LocalStorage keeps data in memory, and it shares
4772 // StorageEvents just with other origins in the same private-browsing
4773 // environment.
4774 // For Partitioned Trackers, we expose a partitioned LocalStorage, which
4775 // doesn't share data with other contexts, and it's just in memory.
4776 // Partitioned localStorage is available only for trackers listed in the
4777 // privacy.restrict3rdpartystorage.partitionedHosts pref. See
4778 // nsContentUtils::IsURIInPrefList to know the syntax for the pref value.
4779 // This is a temporary web-compatibility hack.
4780
4781 StorageAccess access = StorageAllowedForWindow(this);
4782
4783 // We allow partitioned localStorage only to some hosts.
4784 bool isolated = false;
4785 if (ShouldPartitionStorage(access)) {
4786 if (!mDoc) {
4787 access = StorageAccess::eDeny;
4788 } else if (!StoragePartitioningEnabled(access, mDoc->CookieJarSettings())) {
4789 static const char* kPrefName =
4790 "privacy.restrict3rdpartystorage.partitionedHosts";
4791
4792 bool isInList = false;
4793 mDoc->NodePrincipal()->IsURIInPrefList(kPrefName, &isInList);
4794 if (!isInList) {
4795 access = StorageAccess::eDeny;
4796 } else {
4797 isolated = true;
4798 }
4799 }
4800 }
4801
4802 if (access == StorageAccess::eDeny) {
4803 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
4804 return nullptr;
4805 }
4806
4807 nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
4808 if (mDoc) {
4809 cookieJarSettings = mDoc->CookieJarSettings();
4810 } else {
4811 cookieJarSettings = net::CookieJarSettings::GetBlockingAll();
4812 }
4813
4814 // Note that this behavior is observable: if we grant storage permission to a
4815 // tracker, we pass from the partitioned LocalStorage (or a partitioned cookie
4816 // jar) to the 'normal' one. The previous data is lost and the 2
4817 // window.localStorage objects, before and after the permission granted, will
4818 // be different.
4819 if (mLocalStorage) {
4820 if ((mLocalStorage->Type() == (isolated ? Storage::ePartitionedLocalStorage
4821 : Storage::eLocalStorage)) &&
4822 (mLocalStorage->StoragePrincipal() == GetEffectiveStoragePrincipal())) {
4823 return mLocalStorage;
4824 }
4825
4826 // storage needs change
4827 mLocalStorage = nullptr;
4828 }
4829
4830 MOZ_ASSERT(!mLocalStorage);
4831
4832 if (!isolated) {
4833 RefPtr<Storage> storage;
4834
4835 if (NextGenLocalStorageEnabled()) {
4836 aError = LSObject::CreateForWindow(this, getter_AddRefs(storage));
4837 } else {
4838 nsresult rv;
4839 nsCOMPtr<nsIDOMStorageManager> storageManager =
4840 do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv);
4841 if (NS_FAILED(rv)) {
4842 aError.Throw(rv);
4843 return nullptr;
4844 }
4845
4846 nsString documentURI;
4847 if (mDoc) {
4848 aError = mDoc->GetDocumentURI(documentURI);
4849 if (NS_WARN_IF(aError.Failed())) {
4850 return nullptr;
4851 }
4852 }
4853
4854 nsIPrincipal* principal = GetPrincipal();
4855 if (!principal) {
4856 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
4857 return nullptr;
4858 }
4859
4860 nsIPrincipal* storagePrincipal = GetEffectiveStoragePrincipal();
4861 if (!storagePrincipal) {
4862 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
4863 return nullptr;
4864 }
4865
4866 aError = storageManager->CreateStorage(this, principal, storagePrincipal,
4867 documentURI, IsPrivateBrowsing(),
4868 getter_AddRefs(storage));
4869 }
4870
4871 if (aError.Failed()) {
4872 return nullptr;
4873 }
4874
4875 mLocalStorage = storage;
4876 } else {
4877 nsresult rv;
4878 nsCOMPtr<nsIDOMSessionStorageManager> storageManager =
4879 do_GetService("@mozilla.org/dom/sessionStorage-manager;1", &rv);
4880 if (NS_FAILED(rv)) {
4881 aError.Throw(rv);
4882 return nullptr;
4883 }
4884
4885 nsIPrincipal* principal = GetPrincipal();
4886 if (!principal) {
4887 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
4888 return nullptr;
4889 }
4890
4891 nsIPrincipal* storagePrincipal = GetEffectiveStoragePrincipal();
4892 if (!storagePrincipal) {
4893 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
4894 return nullptr;
4895 }
4896
4897 RefPtr<SessionStorageCache> cache;
4898 if (isolated) {
4899 cache = new SessionStorageCache();
4900 } else {
4901 // This will clone the session storage if it exists.
4902 rv = storageManager->GetSessionStorageCache(principal, storagePrincipal,
4903 &cache);
4904 if (NS_FAILED(rv)) {
4905 aError.Throw(rv);
4906 return nullptr;
4907 }
4908 }
4909
4910 mLocalStorage =
4911 new PartitionedLocalStorage(this, principal, storagePrincipal, cache);
4912 }
4913
4914 MOZ_ASSERT(mLocalStorage);
4915 MOZ_ASSERT(
4916 mLocalStorage->Type() ==
4917 (isolated ? Storage::ePartitionedLocalStorage : Storage::eLocalStorage));
4918 return mLocalStorage;
4919 }
4920
GetIndexedDB(ErrorResult & aError)4921 IDBFactory* nsGlobalWindowInner::GetIndexedDB(ErrorResult& aError) {
4922 if (!mIndexedDB) {
4923 // This may keep mIndexedDB null without setting an error.
4924 auto res = IDBFactory::CreateForWindow(this);
4925 if (res.isErr()) {
4926 aError = res.unwrapErr();
4927 } else {
4928 mIndexedDB = res.unwrap();
4929 }
4930 }
4931
4932 return mIndexedDB;
4933 }
4934
4935 //*****************************************************************************
4936 // nsGlobalWindowInner::nsIInterfaceRequestor
4937 //*****************************************************************************
4938
4939 NS_IMETHODIMP
GetInterface(const nsIID & aIID,void ** aSink)4940 nsGlobalWindowInner::GetInterface(const nsIID& aIID, void** aSink) {
4941 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
4942 NS_ENSURE_TRUE(outer, NS_ERROR_NOT_INITIALIZED);
4943
4944 nsresult rv = outer->GetInterfaceInternal(aIID, aSink);
4945 if (rv == NS_ERROR_NO_INTERFACE) {
4946 return QueryInterface(aIID, aSink);
4947 }
4948 return rv;
4949 }
4950
GetInterface(JSContext * aCx,JS::Handle<JS::Value> aIID,JS::MutableHandle<JS::Value> aRetval,ErrorResult & aError)4951 void nsGlobalWindowInner::GetInterface(JSContext* aCx,
4952 JS::Handle<JS::Value> aIID,
4953 JS::MutableHandle<JS::Value> aRetval,
4954 ErrorResult& aError) {
4955 dom::GetInterface(aCx, this, aIID, aRetval, aError);
4956 }
4957
GetCaches(ErrorResult & aRv)4958 already_AddRefed<CacheStorage> nsGlobalWindowInner::GetCaches(
4959 ErrorResult& aRv) {
4960 if (!mCacheStorage) {
4961 bool forceTrustedOrigin =
4962 GetBrowsingContext() &&
4963 GetBrowsingContext()->Top()->GetServiceWorkersTestingEnabled();
4964 mCacheStorage = CacheStorage::CreateOnMainThread(
4965 cache::DEFAULT_NAMESPACE, this, GetEffectiveStoragePrincipal(),
4966 forceTrustedOrigin, aRv);
4967 }
4968
4969 RefPtr<CacheStorage> ref = mCacheStorage;
4970 return ref.forget();
4971 }
4972
FireOfflineStatusEventIfChanged()4973 void nsGlobalWindowInner::FireOfflineStatusEventIfChanged() {
4974 if (!IsCurrentInnerWindow()) return;
4975
4976 // Don't fire an event if the status hasn't changed
4977 if (mWasOffline == NS_IsOffline()) {
4978 return;
4979 }
4980
4981 mWasOffline = !mWasOffline;
4982
4983 nsAutoString name;
4984 if (mWasOffline) {
4985 name.AssignLiteral("offline");
4986 } else {
4987 name.AssignLiteral("online");
4988 }
4989 nsContentUtils::DispatchTrustedEvent(mDoc, static_cast<EventTarget*>(this),
4990 name, CanBubble::eNo, Cancelable::eNo);
4991 }
4992
4993 nsGlobalWindowInner::SlowScriptResponse
ShowSlowScriptDialog(JSContext * aCx,const nsString & aAddonId,const double aDuration)4994 nsGlobalWindowInner::ShowSlowScriptDialog(JSContext* aCx,
4995 const nsString& aAddonId,
4996 const double aDuration) {
4997 nsresult rv;
4998
4999 if (Preferences::GetBool("dom.always_stop_slow_scripts")) {
5000 return KillSlowScript;
5001 }
5002
5003 // If it isn't safe to run script, then it isn't safe to bring up the prompt
5004 // (since that spins the event loop). In that (rare) case, we just kill the
5005 // script and report a warning.
5006 if (!nsContentUtils::IsSafeToRunScript()) {
5007 JS::WarnASCII(aCx, "A long running script was terminated");
5008 return KillSlowScript;
5009 }
5010
5011 // If our document is not active, just kill the script: we've been unloaded
5012 if (!HasActiveDocument()) {
5013 return KillSlowScript;
5014 }
5015
5016 // Check if we should offer the option to debug
5017 JS::AutoFilename filename;
5018 unsigned lineno;
5019 // Computing the line number can be very expensive (see bug 1330231 for
5020 // example), and we don't use the line number anywhere except than in the
5021 // parent process, so we avoid computing it elsewhere. This gives us most of
5022 // the wins we are interested in, since the source of the slowness here is
5023 // minified scripts which is more common in Web content that is loaded in the
5024 // content process.
5025 unsigned* linenop = XRE_IsParentProcess() ? &lineno : nullptr;
5026 bool hasFrame = JS::DescribeScriptedCaller(aCx, &filename, linenop);
5027
5028 // Record the slow script event if we haven't done so already for this inner
5029 // window (which represents a particular page to the user).
5030 if (!mHasHadSlowScript) {
5031 Telemetry::Accumulate(Telemetry::SLOW_SCRIPT_PAGE_COUNT, 1);
5032 }
5033 mHasHadSlowScript = true;
5034
5035 // Override the cursor to something that we're sure the user can see.
5036 SetCursor("auto"_ns, IgnoreErrors());
5037
5038 if (XRE_IsContentProcess() && ProcessHangMonitor::Get()) {
5039 ProcessHangMonitor::SlowScriptAction action;
5040 RefPtr<ProcessHangMonitor> monitor = ProcessHangMonitor::Get();
5041 nsIDocShell* docShell = GetDocShell();
5042 nsCOMPtr<nsIBrowserChild> child =
5043 docShell ? docShell->GetBrowserChild() : nullptr;
5044 action =
5045 monitor->NotifySlowScript(child, filename.get(), aAddonId, aDuration);
5046 if (action == ProcessHangMonitor::Terminate) {
5047 return KillSlowScript;
5048 }
5049
5050 if (action == ProcessHangMonitor::StartDebugger) {
5051 // Spin a nested event loop so that the debugger in the parent can fetch
5052 // any information it needs. Once the debugger has started, return to the
5053 // script.
5054 RefPtr<nsGlobalWindowOuter> outer = GetOuterWindowInternal();
5055 outer->EnterModalState();
5056 SpinEventLoopUntil(
5057 [&]() { return monitor->IsDebuggerStartupComplete(); });
5058 outer->LeaveModalState();
5059 return ContinueSlowScript;
5060 }
5061
5062 return ContinueSlowScriptAndKeepNotifying;
5063 }
5064
5065 // Reached only on non-e10s - once per slow script dialog.
5066 // On e10s - we probe once at ProcessHangsMonitor.jsm
5067 Telemetry::Accumulate(Telemetry::SLOW_SCRIPT_NOTICE_COUNT, 1);
5068
5069 // Get the nsIPrompt interface from the docshell
5070 nsCOMPtr<nsIDocShell> ds = GetDocShell();
5071 NS_ENSURE_TRUE(ds, KillSlowScript);
5072 nsCOMPtr<nsIPrompt> prompt = do_GetInterface(ds);
5073 NS_ENSURE_TRUE(prompt, KillSlowScript);
5074
5075 // Prioritize the SlowScriptDebug interface over JSD1.
5076 nsCOMPtr<nsISlowScriptDebugCallback> debugCallback;
5077
5078 if (hasFrame) {
5079 const char* debugCID = "@mozilla.org/dom/slow-script-debug;1";
5080 nsCOMPtr<nsISlowScriptDebug> debugService = do_GetService(debugCID, &rv);
5081 if (NS_SUCCEEDED(rv)) {
5082 debugService->GetActivationHandler(getter_AddRefs(debugCallback));
5083 }
5084 }
5085
5086 bool failed = false;
5087 auto getString = [&](const char* name,
5088 nsContentUtils::PropertiesFile propFile =
5089 nsContentUtils::eDOM_PROPERTIES) {
5090 nsAutoString result;
5091 nsresult rv = nsContentUtils::GetLocalizedString(propFile, name, result);
5092
5093 // GetStringFromName can return NS_OK and still give nullptr string
5094 failed = failed || NS_FAILED(rv) || result.IsEmpty();
5095 return result;
5096 };
5097
5098 bool isAddonScript = !aAddonId.IsEmpty();
5099 bool showDebugButton = debugCallback && !isAddonScript;
5100
5101 // Get localizable strings
5102
5103 nsAutoString title, checkboxMsg, debugButton, msg;
5104 if (isAddonScript) {
5105 title = getString("KillAddonScriptTitle");
5106 checkboxMsg = getString("KillAddonScriptGlobalMessage");
5107
5108 auto appName =
5109 getString("brandShortName", nsContentUtils::eBRAND_PROPERTIES);
5110
5111 nsCOMPtr<nsIAddonPolicyService> aps =
5112 do_GetService("@mozilla.org/addons/policy-service;1");
5113 nsString addonName;
5114 if (!aps || NS_FAILED(aps->GetExtensionName(aAddonId, addonName))) {
5115 addonName = aAddonId;
5116 }
5117
5118 rv = nsContentUtils::FormatLocalizedString(
5119 msg, nsContentUtils::eDOM_PROPERTIES, "KillAddonScriptMessage",
5120 addonName, appName);
5121
5122 failed = failed || NS_FAILED(rv);
5123 } else {
5124 title = getString("KillScriptTitle");
5125 checkboxMsg = getString("DontAskAgain");
5126
5127 if (showDebugButton) {
5128 debugButton = getString("DebugScriptButton");
5129 msg = getString("KillScriptWithDebugMessage");
5130 } else {
5131 msg = getString("KillScriptMessage");
5132 }
5133 }
5134
5135 auto stopButton = getString("StopScriptButton");
5136 auto waitButton = getString("WaitForScriptButton");
5137
5138 if (failed) {
5139 NS_ERROR("Failed to get localized strings.");
5140 return ContinueSlowScript;
5141 }
5142
5143 // Append file and line number information, if available
5144 if (filename.get()) {
5145 nsAutoString scriptLocation;
5146 // We want to drop the middle part of too-long locations. We'll
5147 // define "too-long" as longer than 60 UTF-16 code units. Just
5148 // have to be a bit careful about unpaired surrogates.
5149 NS_ConvertUTF8toUTF16 filenameUTF16(filename.get());
5150 if (filenameUTF16.Length() > 60) {
5151 // XXXbz Do we need to insert any bidi overrides here?
5152 size_t cutStart = 30;
5153 size_t cutLength = filenameUTF16.Length() - 60;
5154 MOZ_ASSERT(cutLength > 0);
5155 if (NS_IS_LOW_SURROGATE(filenameUTF16[cutStart])) {
5156 // Don't truncate before the low surrogate, in case it's preceded by a
5157 // high surrogate and forms a single Unicode character. Instead, just
5158 // include the low surrogate.
5159 ++cutStart;
5160 --cutLength;
5161 }
5162 if (NS_IS_LOW_SURROGATE(filenameUTF16[cutStart + cutLength])) {
5163 // Likewise, don't drop a trailing low surrogate here. We want to
5164 // increase cutLength, since it might be 0 already so we can't very well
5165 // decrease it.
5166 ++cutLength;
5167 }
5168
5169 // Insert U+2026 HORIZONTAL ELLIPSIS
5170 filenameUTF16.ReplaceLiteral(cutStart, cutLength, u"\x2026");
5171 }
5172 rv = nsContentUtils::FormatLocalizedString(
5173 scriptLocation, nsContentUtils::eDOM_PROPERTIES, "KillScriptLocation",
5174 filenameUTF16);
5175
5176 if (NS_SUCCEEDED(rv)) {
5177 msg.AppendLiteral("\n\n");
5178 msg.Append(scriptLocation);
5179 msg.Append(':');
5180 msg.AppendInt(lineno);
5181 }
5182 }
5183
5184 uint32_t buttonFlags = nsIPrompt::BUTTON_POS_1_DEFAULT +
5185 (nsIPrompt::BUTTON_TITLE_IS_STRING *
5186 (nsIPrompt::BUTTON_POS_0 + nsIPrompt::BUTTON_POS_1));
5187
5188 // Add a third button if necessary.
5189 if (showDebugButton)
5190 buttonFlags += nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_2;
5191
5192 bool checkboxValue = false;
5193 int32_t buttonPressed = 0; // In case the user exits dialog by clicking X.
5194 {
5195 // Null out the operation callback while we're re-entering JS here.
5196 AutoDisableJSInterruptCallback disabler(aCx);
5197
5198 // Open the dialog.
5199 rv = prompt->ConfirmEx(
5200 title.get(), msg.get(), buttonFlags, waitButton.get(), stopButton.get(),
5201 debugButton.get(), checkboxMsg.get(), &checkboxValue, &buttonPressed);
5202 }
5203
5204 if (buttonPressed == 0) {
5205 if (checkboxValue && !isAddonScript && NS_SUCCEEDED(rv))
5206 return AlwaysContinueSlowScript;
5207 return ContinueSlowScript;
5208 }
5209
5210 if (buttonPressed == 2) {
5211 MOZ_RELEASE_ASSERT(debugCallback);
5212
5213 rv = debugCallback->HandleSlowScriptDebug(this);
5214 return NS_SUCCEEDED(rv) ? ContinueSlowScript : KillSlowScript;
5215 }
5216
5217 JS_ClearPendingException(aCx);
5218
5219 return KillSlowScript;
5220 }
5221
Observe(nsISupports * aSubject,const char * aTopic,const char16_t * aData)5222 nsresult nsGlobalWindowInner::Observe(nsISupports* aSubject, const char* aTopic,
5223 const char16_t* aData) {
5224 if (!nsCRT::strcmp(aTopic, NS_IOSERVICE_OFFLINE_STATUS_TOPIC)) {
5225 if (!IsFrozen()) {
5226 // Fires an offline status event if the offline status has changed
5227 FireOfflineStatusEventIfChanged();
5228 }
5229 return NS_OK;
5230 }
5231
5232 if (!nsCRT::strcmp(aTopic, MEMORY_PRESSURE_OBSERVER_TOPIC)) {
5233 if (mPerformance) {
5234 mPerformance->MemoryPressure();
5235 }
5236 RemoveReportRecords();
5237 return NS_OK;
5238 }
5239
5240 if (!nsCRT::strcmp(aTopic, "offline-cache-update-added")) {
5241 if (mApplicationCache) return NS_OK;
5242
5243 // Instantiate the application object now. It observes update belonging to
5244 // this window's document and correctly updates the applicationCache object
5245 // state.
5246 nsCOMPtr<nsIObserver> observer = GetApplicationCache();
5247 if (observer) observer->Observe(aSubject, aTopic, aData);
5248
5249 return NS_OK;
5250 }
5251
5252 if (!nsCRT::strcmp(aTopic, PERMISSION_CHANGED_TOPIC)) {
5253 nsCOMPtr<nsIPermission> perm(do_QueryInterface(aSubject));
5254 if (!perm) {
5255 // A null permission indicates that the entire permission list
5256 // was cleared.
5257 MOZ_ASSERT(!nsCRT::strcmp(aData, u"cleared"));
5258 UpdatePermissions();
5259 return NS_OK;
5260 }
5261
5262 nsAutoCString type;
5263 perm->GetType(type);
5264 if (type == "autoplay-media"_ns) {
5265 UpdateAutoplayPermission();
5266 } else if (type == "shortcuts"_ns) {
5267 UpdateShortcutsPermission();
5268 } else if (type == "popup"_ns) {
5269 UpdatePopupPermission();
5270 }
5271
5272 if (!mDoc) {
5273 return NS_OK;
5274 }
5275
5276 RefPtr<PermissionDelegateHandler> permDelegateHandler =
5277 mDoc->GetPermissionDelegateHandler();
5278
5279 if (permDelegateHandler) {
5280 permDelegateHandler->UpdateDelegatedPermission(type);
5281 }
5282
5283 return NS_OK;
5284 }
5285
5286 if (!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
5287 MOZ_ASSERT(!NS_strcmp(aData, u"intl.accept_languages"));
5288
5289 // The user preferred languages have changed, we need to fire an event on
5290 // Window object and invalidate the cache for navigator.languages. It is
5291 // done for every change which can be a waste of cycles but those should be
5292 // fairly rare.
5293 // We MUST invalidate navigator.languages before sending the event in the
5294 // very likely situation where an event handler will try to read its value.
5295
5296 if (mNavigator) {
5297 Navigator_Binding::ClearCachedLanguageValue(mNavigator);
5298 Navigator_Binding::ClearCachedLanguagesValue(mNavigator);
5299 }
5300
5301 // The event has to be dispatched only to the current inner window.
5302 if (!IsCurrentInnerWindow()) {
5303 return NS_OK;
5304 }
5305
5306 RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
5307 event->InitEvent(u"languagechange"_ns, false, false);
5308 event->SetTrusted(true);
5309
5310 ErrorResult rv;
5311 DispatchEvent(*event, rv);
5312 return rv.StealNSResult();
5313 }
5314
5315 NS_WARNING("unrecognized topic in nsGlobalWindowInner::Observe");
5316 return NS_ERROR_FAILURE;
5317 }
5318
ObserveStorageNotification(StorageEvent * aEvent,const char16_t * aStorageType,bool aPrivateBrowsing)5319 void nsGlobalWindowInner::ObserveStorageNotification(
5320 StorageEvent* aEvent, const char16_t* aStorageType, bool aPrivateBrowsing) {
5321 MOZ_ASSERT(aEvent);
5322
5323 // The private browsing check must be done here again because this window
5324 // could have changed its state before the notification check and now. This
5325 // happens in case this window did have a docShell at that time.
5326 if (aPrivateBrowsing != IsPrivateBrowsing()) {
5327 return;
5328 }
5329
5330 // LocalStorage can only exist on an inner window, and we don't want to
5331 // generate events on frozen or otherwise-navigated-away from windows.
5332 // (Actually, this code used to try and buffer events for frozen windows,
5333 // but it never worked, so we've removed it. See bug 1285898.)
5334 if (!IsCurrentInnerWindow() || IsFrozen()) {
5335 return;
5336 }
5337
5338 nsIPrincipal* principal = GetPrincipal();
5339 if (!principal) {
5340 return;
5341 }
5342
5343 bool fireMozStorageChanged = false;
5344 nsAutoString eventType;
5345 eventType.AssignLiteral("storage");
5346
5347 if (!NS_strcmp(aStorageType, u"sessionStorage")) {
5348 RefPtr<Storage> changingStorage = aEvent->GetStorageArea();
5349 MOZ_ASSERT(changingStorage);
5350
5351 bool check = false;
5352
5353 if (const RefPtr<SessionStorageManager> storageManager =
5354 GetBrowsingContext()->GetSessionStorageManager()) {
5355 nsresult rv = storageManager->CheckStorage(GetEffectiveStoragePrincipal(),
5356 changingStorage, &check);
5357 if (NS_FAILED(rv)) {
5358 return;
5359 }
5360 }
5361
5362 if (!check) {
5363 // This storage event is not coming from our storage or is coming
5364 // from a different docshell, i.e. it is a clone, ignore this event.
5365 return;
5366 }
5367
5368 MOZ_LOG(
5369 gDOMLeakPRLogInner, LogLevel::Debug,
5370 ("nsGlobalWindowInner %p with sessionStorage %p passing event from %p",
5371 this, mSessionStorage.get(), changingStorage.get()));
5372
5373 fireMozStorageChanged = mSessionStorage == changingStorage;
5374 if (fireMozStorageChanged) {
5375 eventType.AssignLiteral("MozSessionStorageChanged");
5376 }
5377 }
5378
5379 else {
5380 MOZ_ASSERT(!NS_strcmp(aStorageType, u"localStorage"));
5381
5382 nsIPrincipal* storagePrincipal = GetEffectiveStoragePrincipal();
5383 if (!storagePrincipal) {
5384 return;
5385 }
5386
5387 MOZ_DIAGNOSTIC_ASSERT(StorageUtils::PrincipalsEqual(aEvent->GetPrincipal(),
5388 storagePrincipal));
5389
5390 fireMozStorageChanged =
5391 mLocalStorage && mLocalStorage == aEvent->GetStorageArea();
5392
5393 if (fireMozStorageChanged) {
5394 eventType.AssignLiteral("MozLocalStorageChanged");
5395 }
5396 }
5397
5398 // Clone the storage event included in the observer notification. We want
5399 // to dispatch clones rather than the original event.
5400 IgnoredErrorResult error;
5401 RefPtr<StorageEvent> clonedEvent =
5402 CloneStorageEvent(eventType, aEvent, error);
5403 if (error.Failed() || !clonedEvent) {
5404 return;
5405 }
5406
5407 clonedEvent->SetTrusted(true);
5408
5409 if (fireMozStorageChanged) {
5410 WidgetEvent* internalEvent = clonedEvent->WidgetEventPtr();
5411 internalEvent->mFlags.mOnlyChromeDispatch = true;
5412 }
5413
5414 DispatchEvent(*clonedEvent);
5415 }
5416
CloneStorageEvent(const nsAString & aType,const RefPtr<StorageEvent> & aEvent,ErrorResult & aRv)5417 already_AddRefed<StorageEvent> nsGlobalWindowInner::CloneStorageEvent(
5418 const nsAString& aType, const RefPtr<StorageEvent>& aEvent,
5419 ErrorResult& aRv) {
5420 StorageEventInit dict;
5421
5422 dict.mBubbles = aEvent->Bubbles();
5423 dict.mCancelable = aEvent->Cancelable();
5424 aEvent->GetKey(dict.mKey);
5425 aEvent->GetOldValue(dict.mOldValue);
5426 aEvent->GetNewValue(dict.mNewValue);
5427 aEvent->GetUrl(dict.mUrl);
5428
5429 RefPtr<Storage> storageArea = aEvent->GetStorageArea();
5430
5431 RefPtr<Storage> storage;
5432
5433 // If null, this is a localStorage event received by IPC.
5434 if (!storageArea) {
5435 storage = GetLocalStorage(aRv);
5436 if (!NextGenLocalStorageEnabled()) {
5437 if (aRv.Failed() || !storage) {
5438 return nullptr;
5439 }
5440
5441 if (storage->Type() == Storage::eLocalStorage) {
5442 RefPtr<LocalStorage> localStorage =
5443 static_cast<LocalStorage*>(storage.get());
5444
5445 // We must apply the current change to the 'local' localStorage.
5446 localStorage->ApplyEvent(aEvent);
5447 }
5448 }
5449 } else if (storageArea->Type() == Storage::eSessionStorage) {
5450 storage = GetSessionStorage(aRv);
5451 } else {
5452 MOZ_ASSERT(storageArea->Type() == Storage::eLocalStorage);
5453 storage = GetLocalStorage(aRv);
5454 }
5455
5456 if (aRv.Failed() || !storage) {
5457 return nullptr;
5458 }
5459
5460 if (storage->Type() == Storage::ePartitionedLocalStorage) {
5461 // This error message is not exposed.
5462 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
5463 return nullptr;
5464 }
5465
5466 MOZ_ASSERT(storage);
5467 MOZ_ASSERT_IF(storageArea, storage->IsForkOf(storageArea));
5468
5469 dict.mStorageArea = storage;
5470
5471 RefPtr<StorageEvent> event = StorageEvent::Constructor(this, aType, dict);
5472 return event.forget();
5473 }
5474
Suspend(bool aIncludeSubWindows)5475 void nsGlobalWindowInner::Suspend(bool aIncludeSubWindows) {
5476 MOZ_ASSERT(NS_IsMainThread());
5477
5478 // We can only safely suspend windows that are the current inner window. If
5479 // its not the current inner, then we are in one of two different cases.
5480 // Either we are in the bfcache or we are doomed window that is going away.
5481 // When a window becomes inactive we purposely avoid placing already suspended
5482 // windows into the bfcache. It only expects windows suspended due to the
5483 // Freeze() method which occurs while the window is still the current inner.
5484 // So we must not call Suspend() on bfcache windows at this point or this
5485 // invariant will be broken. If the window is doomed there is no point in
5486 // suspending it since it will soon be gone.
5487 if (!IsCurrentInnerWindow()) {
5488 return;
5489 }
5490
5491 // All in-process descendants are also suspended. This ensure mSuspendDepth
5492 // is set properly and the timers are properly canceled for each in-process
5493 // descendant.
5494 if (aIncludeSubWindows) {
5495 CallOnInProcessDescendants(&nsGlobalWindowInner::Suspend, false);
5496 }
5497
5498 mSuspendDepth += 1;
5499 if (mSuspendDepth != 1) {
5500 return;
5501 }
5502
5503 if (mWindowGlobalChild) {
5504 mWindowGlobalChild->BlockBFCacheFor(BFCacheStatus::SUSPENDED);
5505 }
5506
5507 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
5508 if (ac) {
5509 for (uint32_t i = 0; i < mEnabledSensors.Length(); i++)
5510 ac->RemoveWindowListener(mEnabledSensors[i], this);
5511 }
5512 DisableGamepadUpdates();
5513 DisableVRUpdates();
5514
5515 SuspendWorkersForWindow(*this);
5516
5517 for (RefPtr<mozilla::dom::SharedWorker> pinnedWorker :
5518 mSharedWorkers.ForwardRange()) {
5519 pinnedWorker->Suspend();
5520 }
5521
5522 SuspendIdleRequests();
5523
5524 mTimeoutManager->Suspend();
5525
5526 // Suspend all of the AudioContexts for this window
5527 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
5528 mAudioContexts[i]->SuspendFromChrome();
5529 }
5530 }
5531
Resume(bool aIncludeSubWindows)5532 void nsGlobalWindowInner::Resume(bool aIncludeSubWindows) {
5533 MOZ_ASSERT(NS_IsMainThread());
5534
5535 // We can only safely resume a window if its the current inner window. If
5536 // its not the current inner, then we are in one of two different cases.
5537 // Either we are in the bfcache or we are doomed window that is going away.
5538 // If a window is suspended when it becomes inactive we purposely do not
5539 // put it in the bfcache, so Resume should never be needed in that case.
5540 // If the window is doomed then there is no point in resuming it.
5541 if (!IsCurrentInnerWindow()) {
5542 return;
5543 }
5544
5545 // Resume all in-process descendants. This restores timers recursively
5546 // canceled in Suspend() and ensures all in-process descendants have the
5547 // correct mSuspendDepth.
5548 if (aIncludeSubWindows) {
5549 CallOnInProcessDescendants(&nsGlobalWindowInner::Resume, false);
5550 }
5551
5552 if (mSuspendDepth == 0) {
5553 // Ignore if the window is not suspended.
5554 return;
5555 }
5556
5557 mSuspendDepth -= 1;
5558
5559 if (mSuspendDepth != 0) {
5560 return;
5561 }
5562
5563 // We should not be able to resume a frozen window. It must be Thaw()'d
5564 // first.
5565 MOZ_ASSERT(mFreezeDepth == 0);
5566
5567 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
5568 if (ac) {
5569 for (uint32_t i = 0; i < mEnabledSensors.Length(); i++)
5570 ac->AddWindowListener(mEnabledSensors[i], this);
5571 }
5572 EnableGamepadUpdates();
5573 EnableVRUpdates();
5574
5575 // Resume all of the AudioContexts for this window
5576 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
5577 mAudioContexts[i]->ResumeFromChrome();
5578 }
5579
5580 mTimeoutManager->Resume();
5581
5582 ResumeIdleRequests();
5583
5584 // Resume all of the workers for this window. We must do this
5585 // after timeouts since workers may have queued events that can trigger
5586 // a setTimeout().
5587 ResumeWorkersForWindow(*this);
5588
5589 for (RefPtr<mozilla::dom::SharedWorker> pinnedWorker :
5590 mSharedWorkers.ForwardRange()) {
5591 pinnedWorker->Resume();
5592 }
5593
5594 if (mWindowGlobalChild) {
5595 mWindowGlobalChild->UnblockBFCacheFor(BFCacheStatus::SUSPENDED);
5596 }
5597 }
5598
IsSuspended() const5599 bool nsGlobalWindowInner::IsSuspended() const {
5600 MOZ_ASSERT(NS_IsMainThread());
5601 return mSuspendDepth != 0;
5602 }
5603
Freeze(bool aIncludeSubWindows)5604 void nsGlobalWindowInner::Freeze(bool aIncludeSubWindows) {
5605 MOZ_ASSERT(NS_IsMainThread());
5606 Suspend(aIncludeSubWindows);
5607 FreezeInternal(aIncludeSubWindows);
5608 }
5609
FreezeInternal(bool aIncludeSubWindows)5610 void nsGlobalWindowInner::FreezeInternal(bool aIncludeSubWindows) {
5611 MOZ_ASSERT(NS_IsMainThread());
5612 MOZ_DIAGNOSTIC_ASSERT(IsCurrentInnerWindow());
5613 MOZ_DIAGNOSTIC_ASSERT(IsSuspended());
5614
5615 HintIsLoading(false);
5616
5617 if (aIncludeSubWindows) {
5618 CallOnInProcessChildren(&nsGlobalWindowInner::FreezeInternal,
5619 aIncludeSubWindows);
5620 }
5621
5622 mFreezeDepth += 1;
5623 MOZ_ASSERT(mSuspendDepth >= mFreezeDepth);
5624 if (mFreezeDepth != 1) {
5625 return;
5626 }
5627
5628 FreezeWorkersForWindow(*this);
5629
5630 for (RefPtr<mozilla::dom::SharedWorker> pinnedWorker :
5631 mSharedWorkers.ForwardRange()) {
5632 pinnedWorker->Freeze();
5633 }
5634
5635 mTimeoutManager->Freeze();
5636 if (mClientSource) {
5637 mClientSource->Freeze();
5638 }
5639
5640 NotifyDOMWindowFrozen(this);
5641 }
5642
Thaw(bool aIncludeSubWindows)5643 void nsGlobalWindowInner::Thaw(bool aIncludeSubWindows) {
5644 MOZ_ASSERT(NS_IsMainThread());
5645 ThawInternal(aIncludeSubWindows);
5646 Resume(aIncludeSubWindows);
5647 }
5648
ThawInternal(bool aIncludeSubWindows)5649 void nsGlobalWindowInner::ThawInternal(bool aIncludeSubWindows) {
5650 MOZ_ASSERT(NS_IsMainThread());
5651 MOZ_DIAGNOSTIC_ASSERT(IsCurrentInnerWindow());
5652 MOZ_DIAGNOSTIC_ASSERT(IsSuspended());
5653
5654 if (aIncludeSubWindows) {
5655 CallOnInProcessChildren(&nsGlobalWindowInner::ThawInternal,
5656 aIncludeSubWindows);
5657 }
5658
5659 MOZ_ASSERT(mFreezeDepth != 0);
5660 mFreezeDepth -= 1;
5661 MOZ_ASSERT(mSuspendDepth >= mFreezeDepth);
5662 if (mFreezeDepth != 0) {
5663 return;
5664 }
5665
5666 if (mClientSource) {
5667 mClientSource->Thaw();
5668 }
5669 mTimeoutManager->Thaw();
5670
5671 ThawWorkersForWindow(*this);
5672
5673 for (RefPtr<mozilla::dom::SharedWorker> pinnedWorker :
5674 mSharedWorkers.ForwardRange()) {
5675 pinnedWorker->Thaw();
5676 }
5677
5678 NotifyDOMWindowThawed(this);
5679 }
5680
IsFrozen() const5681 bool nsGlobalWindowInner::IsFrozen() const {
5682 MOZ_ASSERT(NS_IsMainThread());
5683 bool frozen = mFreezeDepth != 0;
5684 MOZ_ASSERT_IF(frozen, IsSuspended());
5685 return frozen;
5686 }
5687
SyncStateFromParentWindow()5688 void nsGlobalWindowInner::SyncStateFromParentWindow() {
5689 // This method should only be called on an inner window that has been
5690 // assigned to an outer window already.
5691 MOZ_ASSERT(IsCurrentInnerWindow());
5692 nsPIDOMWindowOuter* outer = GetOuterWindow();
5693 MOZ_ASSERT(outer);
5694
5695 // Attempt to find our parent windows.
5696 nsCOMPtr<Element> frame = outer->GetFrameElementInternal();
5697 nsPIDOMWindowOuter* parentOuter =
5698 frame ? frame->OwnerDoc()->GetWindow() : nullptr;
5699 nsGlobalWindowInner* parentInner =
5700 parentOuter
5701 ? nsGlobalWindowInner::Cast(parentOuter->GetCurrentInnerWindow())
5702 : nullptr;
5703
5704 // If our outer is in a modal state, but our parent is not in a modal
5705 // state, then we must apply the suspend directly. If our parent is
5706 // in a modal state then we should get the suspend automatically
5707 // via the parentSuspendDepth application below.
5708 if ((!parentInner || !parentInner->IsInModalState()) && IsInModalState()) {
5709 Suspend();
5710 }
5711
5712 uint32_t parentFreezeDepth = parentInner ? parentInner->mFreezeDepth : 0;
5713 uint32_t parentSuspendDepth = parentInner ? parentInner->mSuspendDepth : 0;
5714
5715 // Since every Freeze() calls Suspend(), the suspend count must
5716 // be equal or greater to the freeze count.
5717 MOZ_ASSERT(parentFreezeDepth <= parentSuspendDepth);
5718
5719 // First apply the Freeze() calls.
5720 for (uint32_t i = 0; i < parentFreezeDepth; ++i) {
5721 Freeze();
5722 }
5723
5724 // Now apply only the number of Suspend() calls to reach the target
5725 // suspend count after applying the Freeze() calls.
5726 for (uint32_t i = 0; i < (parentSuspendDepth - parentFreezeDepth); ++i) {
5727 Suspend();
5728 }
5729 }
5730
5731 template <typename Method, typename... Args>
CallOnInProcessDescendantsInternal(BrowsingContext * aBrowsingContext,bool aChildOnly,Method aMethod,Args &&...aArgs)5732 CallState nsGlobalWindowInner::CallOnInProcessDescendantsInternal(
5733 BrowsingContext* aBrowsingContext, bool aChildOnly, Method aMethod,
5734 Args&&... aArgs) {
5735 MOZ_ASSERT(NS_IsMainThread());
5736 MOZ_ASSERT(aBrowsingContext);
5737
5738 CallState state = CallState::Continue;
5739 for (const RefPtr<BrowsingContext>& bc : aBrowsingContext->Children()) {
5740 if (nsCOMPtr<nsPIDOMWindowOuter> pWin = bc->GetDOMWindow()) {
5741 auto* win = nsGlobalWindowOuter::Cast(pWin);
5742 if (nsGlobalWindowInner* inner = win->GetCurrentInnerWindowInternal()) {
5743 // Call the descendant method using our helper CallDescendant() template
5744 // method. This allows us to handle both void returning methods and
5745 // methods that return CallState explicitly. For void returning methods
5746 // we assume CallState::Continue.
5747 typedef decltype((inner->*aMethod)(aArgs...)) returnType;
5748 state = CallDescendant<returnType>(inner, aMethod, aArgs...);
5749
5750 if (state == CallState::Stop) {
5751 return state;
5752 }
5753 }
5754 }
5755
5756 if (!aChildOnly) {
5757 state = CallOnInProcessDescendantsInternal(bc.get(), aChildOnly, aMethod,
5758 aArgs...);
5759 if (state == CallState::Stop) {
5760 return state;
5761 }
5762 }
5763 }
5764
5765 return state;
5766 }
5767
GetClientInfo() const5768 Maybe<ClientInfo> nsGlobalWindowInner::GetClientInfo() const {
5769 MOZ_ASSERT(NS_IsMainThread());
5770 if (mDoc && mDoc->IsStaticDocument()) {
5771 if (Maybe<ClientInfo> info = mDoc->GetOriginalDocument()->GetClientInfo()) {
5772 return info;
5773 }
5774 }
5775
5776 Maybe<ClientInfo> clientInfo;
5777 if (mClientSource) {
5778 clientInfo.emplace(mClientSource->Info());
5779 }
5780 return clientInfo;
5781 }
5782
GetClientState() const5783 Maybe<ClientState> nsGlobalWindowInner::GetClientState() const {
5784 MOZ_ASSERT(NS_IsMainThread());
5785 if (mDoc && mDoc->IsStaticDocument()) {
5786 if (Maybe<ClientState> state =
5787 mDoc->GetOriginalDocument()->GetClientState()) {
5788 return state;
5789 }
5790 }
5791
5792 Maybe<ClientState> clientState;
5793 if (mClientSource) {
5794 Result<ClientState, ErrorResult> res = mClientSource->SnapshotState();
5795 if (res.isOk()) {
5796 clientState.emplace(res.unwrap());
5797 } else {
5798 res.unwrapErr().SuppressException();
5799 }
5800 }
5801 return clientState;
5802 }
5803
GetController() const5804 Maybe<ServiceWorkerDescriptor> nsGlobalWindowInner::GetController() const {
5805 MOZ_ASSERT(NS_IsMainThread());
5806 if (mDoc && mDoc->IsStaticDocument()) {
5807 if (Maybe<ServiceWorkerDescriptor> controller =
5808 mDoc->GetOriginalDocument()->GetController()) {
5809 return controller;
5810 }
5811 }
5812
5813 Maybe<ServiceWorkerDescriptor> controller;
5814 if (mClientSource) {
5815 controller = mClientSource->GetController();
5816 }
5817 return controller;
5818 }
5819
SetCsp(nsIContentSecurityPolicy * aCsp)5820 void nsGlobalWindowInner::SetCsp(nsIContentSecurityPolicy* aCsp) {
5821 if (!mClientSource) {
5822 return;
5823 }
5824 mClientSource->SetCsp(aCsp);
5825 // Also cache the CSP within the document
5826 mDoc->SetCsp(aCsp);
5827
5828 if (mWindowGlobalChild) {
5829 mWindowGlobalChild->SendSetClientInfo(mClientSource->Info().ToIPC());
5830 }
5831 }
5832
SetPreloadCsp(nsIContentSecurityPolicy * aPreloadCsp)5833 void nsGlobalWindowInner::SetPreloadCsp(nsIContentSecurityPolicy* aPreloadCsp) {
5834 if (!mClientSource) {
5835 return;
5836 }
5837 mClientSource->SetPreloadCsp(aPreloadCsp);
5838 // Also cache the preload CSP within the document
5839 mDoc->SetPreloadCsp(aPreloadCsp);
5840
5841 if (mWindowGlobalChild) {
5842 mWindowGlobalChild->SendSetClientInfo(mClientSource->Info().ToIPC());
5843 }
5844 }
5845
GetCsp()5846 nsIContentSecurityPolicy* nsGlobalWindowInner::GetCsp() {
5847 if (mDoc) {
5848 return mDoc->GetCsp();
5849 }
5850
5851 // If the window is partially torn down and has its document nulled out,
5852 // we query the CSP we snapshot in FreeInnerObjects.
5853 if (mDocumentCsp) {
5854 return mDocumentCsp;
5855 }
5856 return nullptr;
5857 }
5858
GetOrCreateServiceWorker(const ServiceWorkerDescriptor & aDescriptor)5859 RefPtr<ServiceWorker> nsGlobalWindowInner::GetOrCreateServiceWorker(
5860 const ServiceWorkerDescriptor& aDescriptor) {
5861 MOZ_ASSERT(NS_IsMainThread());
5862 RefPtr<ServiceWorker> ref;
5863 ForEachEventTargetObject([&](DOMEventTargetHelper* aTarget, bool* aDoneOut) {
5864 RefPtr<ServiceWorker> sw = do_QueryObject(aTarget);
5865 if (!sw || !sw->Descriptor().Matches(aDescriptor)) {
5866 return;
5867 }
5868
5869 ref = std::move(sw);
5870 *aDoneOut = true;
5871 });
5872
5873 if (!ref) {
5874 ref = ServiceWorker::Create(this, aDescriptor);
5875 }
5876
5877 return ref;
5878 }
5879
5880 RefPtr<mozilla::dom::ServiceWorkerRegistration>
GetServiceWorkerRegistration(const mozilla::dom::ServiceWorkerRegistrationDescriptor & aDescriptor) const5881 nsGlobalWindowInner::GetServiceWorkerRegistration(
5882 const mozilla::dom::ServiceWorkerRegistrationDescriptor& aDescriptor)
5883 const {
5884 MOZ_ASSERT(NS_IsMainThread());
5885 RefPtr<ServiceWorkerRegistration> ref;
5886 ForEachEventTargetObject([&](DOMEventTargetHelper* aTarget, bool* aDoneOut) {
5887 RefPtr<ServiceWorkerRegistration> swr = do_QueryObject(aTarget);
5888 if (!swr || !swr->MatchesDescriptor(aDescriptor)) {
5889 return;
5890 }
5891
5892 ref = std::move(swr);
5893 *aDoneOut = true;
5894 });
5895 return ref;
5896 }
5897
5898 RefPtr<ServiceWorkerRegistration>
GetOrCreateServiceWorkerRegistration(const ServiceWorkerRegistrationDescriptor & aDescriptor)5899 nsGlobalWindowInner::GetOrCreateServiceWorkerRegistration(
5900 const ServiceWorkerRegistrationDescriptor& aDescriptor) {
5901 MOZ_ASSERT(NS_IsMainThread());
5902 RefPtr<ServiceWorkerRegistration> ref =
5903 GetServiceWorkerRegistration(aDescriptor);
5904 if (!ref) {
5905 ref = ServiceWorkerRegistration::CreateForMainThread(this, aDescriptor);
5906 }
5907 return ref;
5908 }
5909
FireDelayedDOMEvents(bool aIncludeSubWindows)5910 nsresult nsGlobalWindowInner::FireDelayedDOMEvents(bool aIncludeSubWindows) {
5911 if (mApplicationCache) {
5912 static_cast<nsDOMOfflineResourceList*>(mApplicationCache.get())
5913 ->FirePendingEvents();
5914 }
5915
5916 // Fires an offline status event if the offline status has changed
5917 FireOfflineStatusEventIfChanged();
5918
5919 if (!aIncludeSubWindows) {
5920 return NS_OK;
5921 }
5922
5923 nsCOMPtr<nsIDocShell> docShell = GetDocShell();
5924 if (docShell) {
5925 int32_t childCount = 0;
5926 docShell->GetInProcessChildCount(&childCount);
5927
5928 // Take a copy of the current children so that modifications to
5929 // the child list don't affect to the iteration.
5930 AutoTArray<nsCOMPtr<nsIDocShellTreeItem>, 8> children;
5931 for (int32_t i = 0; i < childCount; ++i) {
5932 nsCOMPtr<nsIDocShellTreeItem> childShell;
5933 docShell->GetInProcessChildAt(i, getter_AddRefs(childShell));
5934 if (childShell) {
5935 children.AppendElement(childShell);
5936 }
5937 }
5938
5939 for (nsCOMPtr<nsIDocShellTreeItem> childShell : children) {
5940 if (nsCOMPtr<nsPIDOMWindowOuter> pWin = childShell->GetWindow()) {
5941 auto* win = nsGlobalWindowOuter::Cast(pWin);
5942 win->FireDelayedDOMEvents(true);
5943 }
5944 }
5945 }
5946
5947 return NS_OK;
5948 }
5949
5950 //*****************************************************************************
5951 // nsGlobalWindowInner: Window Control Functions
5952 //*****************************************************************************
5953
GetInProcessParentInternal()5954 nsPIDOMWindowOuter* nsGlobalWindowInner::GetInProcessParentInternal() {
5955 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
5956 if (!outer) {
5957 // No outer window available!
5958 return nullptr;
5959 }
5960 return outer->GetInProcessParentInternal();
5961 }
5962
GetTopLevelAntiTrackingPrincipal()5963 nsIPrincipal* nsGlobalWindowInner::GetTopLevelAntiTrackingPrincipal() {
5964 nsPIDOMWindowOuter* outerWindow = GetOuterWindowInternal();
5965 if (!outerWindow) {
5966 return nullptr;
5967 }
5968
5969 nsPIDOMWindowOuter* topLevelOuterWindow =
5970 GetBrowsingContext()->Top()->GetDOMWindow();
5971 if (!topLevelOuterWindow) {
5972 return nullptr;
5973 }
5974
5975 bool stopAtOurLevel =
5976 mDoc && mDoc->CookieJarSettings()->GetCookieBehavior() ==
5977 nsICookieService::BEHAVIOR_REJECT_TRACKER;
5978
5979 if (stopAtOurLevel && topLevelOuterWindow == outerWindow) {
5980 return nullptr;
5981 }
5982
5983 nsPIDOMWindowInner* topLevelInnerWindow =
5984 topLevelOuterWindow->GetCurrentInnerWindow();
5985 if (NS_WARN_IF(!topLevelInnerWindow)) {
5986 return nullptr;
5987 }
5988
5989 nsIPrincipal* topLevelPrincipal =
5990 nsGlobalWindowInner::Cast(topLevelInnerWindow)->GetPrincipal();
5991 if (NS_WARN_IF(!topLevelPrincipal)) {
5992 return nullptr;
5993 }
5994
5995 return topLevelPrincipal;
5996 }
5997
GetTopLevelStorageAreaPrincipal()5998 nsIPrincipal* nsGlobalWindowInner::GetTopLevelStorageAreaPrincipal() {
5999 if (mDoc && (mDoc->StorageAccessSandboxed())) {
6000 // Storage access is disabled
6001 return nullptr;
6002 }
6003
6004 BrowsingContext* parent = GetBrowsingContext()->GetParent();
6005 nsPIDOMWindowOuter* outerWindow = parent ? parent->GetDOMWindow() : nullptr;
6006 if (!outerWindow) {
6007 // No outer window available!
6008 return nullptr;
6009 }
6010
6011 if (!outerWindow->GetBrowsingContext()->IsTop()) {
6012 return nullptr;
6013 }
6014
6015 nsPIDOMWindowInner* innerWindow = outerWindow->GetCurrentInnerWindow();
6016 if (NS_WARN_IF(!innerWindow)) {
6017 return nullptr;
6018 }
6019
6020 nsIPrincipal* parentPrincipal =
6021 nsGlobalWindowInner::Cast(innerWindow)->GetPrincipal();
6022 if (NS_WARN_IF(!parentPrincipal)) {
6023 return nullptr;
6024 }
6025
6026 return parentPrincipal;
6027 }
6028
6029 //*****************************************************************************
6030 // nsGlobalWindowInner: Timeout Functions
6031 //*****************************************************************************
6032
6033 class WindowScriptTimeoutHandler final : public ScriptTimeoutHandler {
6034 public:
6035 NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(WindowScriptTimeoutHandler,ScriptTimeoutHandler)6036 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(WindowScriptTimeoutHandler,
6037 ScriptTimeoutHandler)
6038
6039 WindowScriptTimeoutHandler(JSContext* aCx, nsIGlobalObject* aGlobal,
6040 const nsAString& aExpression)
6041 : ScriptTimeoutHandler(aCx, aGlobal, aExpression),
6042 mInitiatingScript(ScriptLoader::GetActiveScript(aCx)) {}
6043
6044 MOZ_CAN_RUN_SCRIPT virtual bool Call(const char* aExecutionReason) override;
6045
6046 private:
6047 virtual ~WindowScriptTimeoutHandler() = default;
6048
6049 // Initiating script for use when evaluating mExpr on the main thread.
6050 RefPtr<LoadedScript> mInitiatingScript;
6051 };
6052
NS_IMPL_CYCLE_COLLECTION_INHERITED(WindowScriptTimeoutHandler,ScriptTimeoutHandler,mInitiatingScript)6053 NS_IMPL_CYCLE_COLLECTION_INHERITED(WindowScriptTimeoutHandler,
6054 ScriptTimeoutHandler, mInitiatingScript)
6055
6056 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WindowScriptTimeoutHandler)
6057 NS_INTERFACE_MAP_END_INHERITING(ScriptTimeoutHandler)
6058
6059 NS_IMPL_ADDREF_INHERITED(WindowScriptTimeoutHandler, ScriptTimeoutHandler)
6060 NS_IMPL_RELEASE_INHERITED(WindowScriptTimeoutHandler, ScriptTimeoutHandler)
6061
6062 bool WindowScriptTimeoutHandler::Call(const char* aExecutionReason) {
6063 // New script entry point required, due to the "Create a script" sub-step
6064 // of
6065 // http://www.whatwg.org/specs/web-apps/current-work/#timer-initialisation-steps
6066 nsAutoMicroTask mt;
6067 AutoEntryScript aes(mGlobal, aExecutionReason, true);
6068 JS::CompileOptions options(aes.cx());
6069 options.setFileAndLine(mFileName.get(), mLineNo);
6070 options.setNoScriptRval(true);
6071 options.setIntroductionType("domTimer");
6072 JS::Rooted<JSObject*> global(aes.cx(), mGlobal->GetGlobalJSObject());
6073 {
6074 JSExecutionContext exec(aes.cx(), global, options);
6075 nsresult rv = exec.Compile(mExpr);
6076
6077 JS::Rooted<JSScript*> script(aes.cx(), exec.MaybeGetScript());
6078 if (script) {
6079 if (mInitiatingScript) {
6080 mInitiatingScript->AssociateWithScript(script);
6081 }
6082
6083 rv = exec.ExecScript();
6084 }
6085
6086 if (rv == NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW_UNCATCHABLE) {
6087 return false;
6088 }
6089 }
6090
6091 return true;
6092 };
6093
InnerForSetTimeoutOrInterval(ErrorResult & aError)6094 nsGlobalWindowInner* nsGlobalWindowInner::InnerForSetTimeoutOrInterval(
6095 ErrorResult& aError) {
6096 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
6097 nsGlobalWindowInner* currentInner =
6098 outer ? outer->GetCurrentInnerWindowInternal() : this;
6099
6100 // If forwardTo is not the window with an active document then we want the
6101 // call to setTimeout/Interval to be a noop, so return null but don't set an
6102 // error.
6103 return HasActiveDocument() ? currentInner : nullptr;
6104 }
6105
SetTimeout(JSContext * aCx,Function & aFunction,int32_t aTimeout,const Sequence<JS::Value> & aArguments,ErrorResult & aError)6106 int32_t nsGlobalWindowInner::SetTimeout(JSContext* aCx, Function& aFunction,
6107 int32_t aTimeout,
6108 const Sequence<JS::Value>& aArguments,
6109 ErrorResult& aError) {
6110 return SetTimeoutOrInterval(aCx, aFunction, aTimeout, aArguments, false,
6111 aError);
6112 }
6113
SetTimeout(JSContext * aCx,const nsAString & aHandler,int32_t aTimeout,const Sequence<JS::Value> &,ErrorResult & aError)6114 int32_t nsGlobalWindowInner::SetTimeout(JSContext* aCx,
6115 const nsAString& aHandler,
6116 int32_t aTimeout,
6117 const Sequence<JS::Value>& /* unused */,
6118 ErrorResult& aError) {
6119 return SetTimeoutOrInterval(aCx, aHandler, aTimeout, false, aError);
6120 }
6121
SetInterval(JSContext * aCx,Function & aFunction,const int32_t aTimeout,const Sequence<JS::Value> & aArguments,ErrorResult & aError)6122 int32_t nsGlobalWindowInner::SetInterval(JSContext* aCx, Function& aFunction,
6123 const int32_t aTimeout,
6124 const Sequence<JS::Value>& aArguments,
6125 ErrorResult& aError) {
6126 return SetTimeoutOrInterval(aCx, aFunction, aTimeout, aArguments, true,
6127 aError);
6128 }
6129
SetInterval(JSContext * aCx,const nsAString & aHandler,const int32_t aTimeout,const Sequence<JS::Value> &,ErrorResult & aError)6130 int32_t nsGlobalWindowInner::SetInterval(
6131 JSContext* aCx, const nsAString& aHandler, const int32_t aTimeout,
6132 const Sequence<JS::Value>& /* unused */, ErrorResult& aError) {
6133 return SetTimeoutOrInterval(aCx, aHandler, aTimeout, true, aError);
6134 }
6135
SetTimeoutOrInterval(JSContext * aCx,Function & aFunction,int32_t aTimeout,const Sequence<JS::Value> & aArguments,bool aIsInterval,ErrorResult & aError)6136 int32_t nsGlobalWindowInner::SetTimeoutOrInterval(
6137 JSContext* aCx, Function& aFunction, int32_t aTimeout,
6138 const Sequence<JS::Value>& aArguments, bool aIsInterval,
6139 ErrorResult& aError) {
6140 nsGlobalWindowInner* inner = InnerForSetTimeoutOrInterval(aError);
6141 if (!inner) {
6142 return -1;
6143 }
6144
6145 if (inner != this) {
6146 RefPtr<nsGlobalWindowInner> innerRef(inner);
6147 return innerRef->SetTimeoutOrInterval(aCx, aFunction, aTimeout, aArguments,
6148 aIsInterval, aError);
6149 }
6150
6151 DebuggerNotificationDispatch(
6152 this, aIsInterval ? DebuggerNotificationType::SetInterval
6153 : DebuggerNotificationType::SetTimeout);
6154
6155 if (!GetContextInternal() || !HasJSGlobal()) {
6156 // This window was already closed, or never properly initialized,
6157 // don't let a timer be scheduled on such a window.
6158 aError.Throw(NS_ERROR_NOT_INITIALIZED);
6159 return 0;
6160 }
6161
6162 nsTArray<JS::Heap<JS::Value>> args;
6163 if (!args.AppendElements(aArguments, fallible)) {
6164 aError.Throw(NS_ERROR_OUT_OF_MEMORY);
6165 return 0;
6166 }
6167
6168 RefPtr<TimeoutHandler> handler =
6169 new CallbackTimeoutHandler(aCx, this, &aFunction, std::move(args));
6170
6171 int32_t result;
6172 aError =
6173 mTimeoutManager->SetTimeout(handler, aTimeout, aIsInterval,
6174 Timeout::Reason::eTimeoutOrInterval, &result);
6175 return result;
6176 }
6177
SetTimeoutOrInterval(JSContext * aCx,const nsAString & aHandler,int32_t aTimeout,bool aIsInterval,ErrorResult & aError)6178 int32_t nsGlobalWindowInner::SetTimeoutOrInterval(JSContext* aCx,
6179 const nsAString& aHandler,
6180 int32_t aTimeout,
6181 bool aIsInterval,
6182 ErrorResult& aError) {
6183 nsGlobalWindowInner* inner = InnerForSetTimeoutOrInterval(aError);
6184 if (!inner) {
6185 return -1;
6186 }
6187
6188 if (inner != this) {
6189 RefPtr<nsGlobalWindowInner> innerRef(inner);
6190 return innerRef->SetTimeoutOrInterval(aCx, aHandler, aTimeout, aIsInterval,
6191 aError);
6192 }
6193
6194 DebuggerNotificationDispatch(
6195 this, aIsInterval ? DebuggerNotificationType::SetInterval
6196 : DebuggerNotificationType::SetTimeout);
6197
6198 if (!GetContextInternal() || !HasJSGlobal()) {
6199 // This window was already closed, or never properly initialized,
6200 // don't let a timer be scheduled on such a window.
6201 aError.Throw(NS_ERROR_NOT_INITIALIZED);
6202 return 0;
6203 }
6204
6205 bool allowEval = false;
6206 aError = CSPEvalChecker::CheckForWindow(aCx, this, aHandler, &allowEval);
6207 if (NS_WARN_IF(aError.Failed()) || !allowEval) {
6208 return 0;
6209 }
6210
6211 RefPtr<TimeoutHandler> handler =
6212 new WindowScriptTimeoutHandler(aCx, this, aHandler);
6213
6214 int32_t result;
6215 aError =
6216 mTimeoutManager->SetTimeout(handler, aTimeout, aIsInterval,
6217 Timeout::Reason::eTimeoutOrInterval, &result);
6218 return result;
6219 }
6220
GetTimeoutReasonString(Timeout * aTimeout)6221 static const char* GetTimeoutReasonString(Timeout* aTimeout) {
6222 switch (aTimeout->mReason) {
6223 case Timeout::Reason::eTimeoutOrInterval:
6224 if (aTimeout->mIsInterval) {
6225 return "setInterval handler";
6226 }
6227 return "setTimeout handler";
6228 case Timeout::Reason::eIdleCallbackTimeout:
6229 return "setIdleCallback handler (timed out)";
6230 default:
6231 MOZ_CRASH("Unexpected enum value");
6232 return "";
6233 }
6234 }
6235
RunTimeoutHandler(Timeout * aTimeout,nsIScriptContext * aScx)6236 bool nsGlobalWindowInner::RunTimeoutHandler(Timeout* aTimeout,
6237 nsIScriptContext* aScx) {
6238 // Hold on to the timeout in case mExpr or mFunObj releases its
6239 // doc.
6240 // XXXbz Our caller guarantees it'll hold on to the timeout (because
6241 // we're MOZ_CAN_RUN_SCRIPT), so we can probably stop doing that...
6242 RefPtr<Timeout> timeout = aTimeout;
6243 Timeout* last_running_timeout = mTimeoutManager->BeginRunningTimeout(timeout);
6244 timeout->mRunning = true;
6245
6246 // Push this timeout's popup control state, which should only be
6247 // enabled the first time a timeout fires that was created while
6248 // popups were enabled and with a delay less than
6249 // "dom.disable_open_click_delay".
6250 AutoPopupStatePusher popupStatePusher(timeout->mPopupState);
6251
6252 // Clear the timeout's popup state, if any, to prevent interval
6253 // timeouts from repeatedly opening poups.
6254 timeout->mPopupState = PopupBlocker::openAbused;
6255
6256 uint32_t nestingLevel = TimeoutManager::GetNestingLevel();
6257 TimeoutManager::SetNestingLevel(timeout->mNestingLevel);
6258
6259 const char* reason = GetTimeoutReasonString(timeout);
6260
6261 nsCString str;
6262 if (profiler_can_accept_markers()) {
6263 TimeDuration originalInterval = timeout->When() - timeout->SubmitTime();
6264 str.Append(reason);
6265 str.Append(" with interval ");
6266 str.AppendInt(int(originalInterval.ToMilliseconds()));
6267 str.Append("ms: ");
6268 nsCString handlerDescription;
6269 timeout->mScriptHandler->GetDescription(handlerDescription);
6270 str.Append(handlerDescription);
6271 }
6272 AUTO_PROFILER_MARKER_TEXT("setTimeout callback", DOM,
6273 MarkerOptions(MarkerStack::TakeBacktrace(
6274 timeout->TakeProfilerBacktrace()),
6275 MarkerInnerWindowId(mWindowID)),
6276 str);
6277
6278 bool abortIntervalHandler;
6279 {
6280 RefPtr<TimeoutHandler> handler(timeout->mScriptHandler);
6281
6282 CallbackDebuggerNotificationGuard guard(
6283 this, timeout->mIsInterval
6284 ? DebuggerNotificationType::SetIntervalCallback
6285 : DebuggerNotificationType::SetTimeoutCallback);
6286 abortIntervalHandler = !handler->Call(reason);
6287 }
6288
6289 // If we received an uncatchable exception, do not schedule the timeout again.
6290 // This allows the slow script dialog to break easy DoS attacks like
6291 // setInterval(function() { while(1); }, 100);
6292 if (abortIntervalHandler) {
6293 // If it wasn't an interval timer to begin with, this does nothing. If it
6294 // was, we'll treat it as a timeout that we just ran and discard it when
6295 // we return.
6296 timeout->mIsInterval = false;
6297 }
6298
6299 // We ignore any failures from calling EvaluateString() on the context or
6300 // Call() on a Function here since we're in a loop
6301 // where we're likely to be running timeouts whose OS timers
6302 // didn't fire in time and we don't want to not fire those timers
6303 // now just because execution of one timer failed. We can't
6304 // propagate the error to anyone who cares about it from this
6305 // point anyway, and the script context should have already reported
6306 // the script error in the usual way - so we just drop it.
6307
6308 TimeoutManager::SetNestingLevel(nestingLevel);
6309
6310 mTimeoutManager->EndRunningTimeout(last_running_timeout);
6311 timeout->mRunning = false;
6312
6313 return timeout->mCleared;
6314 }
6315
6316 //*****************************************************************************
6317 // nsGlobalWindowInner: Helper Functions
6318 //*****************************************************************************
6319
GetTreeOwner()6320 already_AddRefed<nsIDocShellTreeOwner> nsGlobalWindowInner::GetTreeOwner() {
6321 FORWARD_TO_OUTER(GetTreeOwner, (), nullptr);
6322 }
6323
6324 already_AddRefed<nsIWebBrowserChrome>
GetWebBrowserChrome()6325 nsGlobalWindowInner::GetWebBrowserChrome() {
6326 nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
6327
6328 nsCOMPtr<nsIWebBrowserChrome> browserChrome = do_GetInterface(treeOwner);
6329 return browserChrome.forget();
6330 }
6331
GetScrollFrame()6332 nsIScrollableFrame* nsGlobalWindowInner::GetScrollFrame() {
6333 FORWARD_TO_OUTER(GetScrollFrame, (), nullptr);
6334 }
6335
IsPrivateBrowsing()6336 bool nsGlobalWindowInner::IsPrivateBrowsing() {
6337 nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(GetDocShell());
6338 return loadContext && loadContext->UsePrivateBrowsing();
6339 }
6340
FlushPendingNotifications(FlushType aType)6341 void nsGlobalWindowInner::FlushPendingNotifications(FlushType aType) {
6342 if (mDoc) {
6343 mDoc->FlushPendingNotifications(aType);
6344 }
6345 }
6346
EnableDeviceSensor(uint32_t aType)6347 void nsGlobalWindowInner::EnableDeviceSensor(uint32_t aType) {
6348 bool alreadyEnabled = false;
6349 for (uint32_t i = 0; i < mEnabledSensors.Length(); i++) {
6350 if (mEnabledSensors[i] == aType) {
6351 alreadyEnabled = true;
6352 break;
6353 }
6354 }
6355
6356 mEnabledSensors.AppendElement(aType);
6357
6358 if (alreadyEnabled) {
6359 return;
6360 }
6361
6362 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
6363 if (ac) {
6364 ac->AddWindowListener(aType, this);
6365 }
6366 }
6367
DisableDeviceSensor(uint32_t aType)6368 void nsGlobalWindowInner::DisableDeviceSensor(uint32_t aType) {
6369 int32_t doomedElement = -1;
6370 int32_t listenerCount = 0;
6371 for (uint32_t i = 0; i < mEnabledSensors.Length(); i++) {
6372 if (mEnabledSensors[i] == aType) {
6373 doomedElement = i;
6374 listenerCount++;
6375 }
6376 }
6377
6378 if (doomedElement == -1) {
6379 return;
6380 }
6381
6382 mEnabledSensors.RemoveElementAt(doomedElement);
6383
6384 if (listenerCount > 1) {
6385 return;
6386 }
6387
6388 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
6389 if (ac) {
6390 ac->RemoveWindowListener(aType, this);
6391 }
6392 }
6393
6394 #if defined(MOZ_WIDGET_ANDROID)
EnableOrientationChangeListener()6395 void nsGlobalWindowInner::EnableOrientationChangeListener() {
6396 if (!nsContentUtils::ShouldResistFingerprinting(GetDocShell()) &&
6397 !mOrientationChangeObserver) {
6398 mOrientationChangeObserver = MakeUnique<WindowOrientationObserver>(this);
6399 }
6400 }
6401
DisableOrientationChangeListener()6402 void nsGlobalWindowInner::DisableOrientationChangeListener() {
6403 mOrientationChangeObserver = nullptr;
6404 }
6405 #endif
6406
SetHasGamepadEventListener(bool aHasGamepad)6407 void nsGlobalWindowInner::SetHasGamepadEventListener(
6408 bool aHasGamepad /* = true*/) {
6409 mHasGamepad = aHasGamepad;
6410 if (aHasGamepad) {
6411 EnableGamepadUpdates();
6412 }
6413 }
6414
NotifyDetectXRRuntimesCompleted()6415 void nsGlobalWindowInner::NotifyDetectXRRuntimesCompleted() {
6416 if (!mXRRuntimeDetectionInFlight) {
6417 return;
6418 }
6419 mXRRuntimeDetectionInFlight = false;
6420 if (mXRPermissionRequestInFlight) {
6421 return;
6422 }
6423 gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
6424 bool supported = vm->RuntimeSupportsVR();
6425 if (!supported) {
6426 // A VR runtime was not installed; we can suppress
6427 // the permission prompt
6428 OnXRPermissionRequestCancel();
6429 return;
6430 }
6431 // A VR runtime was found. Display a permission prompt before
6432 // allowing it to be accessed.
6433 // Connect to the VRManager in order to receive the runtime
6434 // detection results.
6435 mXRPermissionRequestInFlight = true;
6436 RefPtr<XRPermissionRequest> request =
6437 new XRPermissionRequest(this, WindowID());
6438 Unused << NS_WARN_IF(NS_FAILED(request->Start()));
6439 }
6440
RequestXRPermission()6441 void nsGlobalWindowInner::RequestXRPermission() {
6442 if (IsDying()) {
6443 // Do not proceed if the window is dying, as that will result
6444 // in leaks of objects that get re-allocated after FreeInnerObjects
6445 // has been called, including mVREventObserver.
6446 return;
6447 }
6448 if (mXRPermissionGranted) {
6449 // Don't prompt redundantly once permission to
6450 // access XR devices has been granted.
6451 OnXRPermissionRequestAllow();
6452 return;
6453 }
6454 if (mXRRuntimeDetectionInFlight || mXRPermissionRequestInFlight) {
6455 // Don't allow multiple simultaneous permissions requests;
6456 return;
6457 }
6458 // Before displaying a permission prompt, detect
6459 // if there is any VR runtime installed.
6460 gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
6461 mXRRuntimeDetectionInFlight = true;
6462 EnableVRUpdates();
6463 vm->DetectRuntimes();
6464 }
6465
OnXRPermissionRequestAllow()6466 void nsGlobalWindowInner::OnXRPermissionRequestAllow() {
6467 mXRPermissionRequestInFlight = false;
6468 if (IsDying()) {
6469 // The window may have started dying while the permission request
6470 // is in flight.
6471 // Do not proceed if the window is dying, as that will result
6472 // in leaks of objects that get re-allocated after FreeInnerObjects
6473 // has been called, including mNavigator.
6474 return;
6475 }
6476 mXRPermissionGranted = true;
6477
6478 NotifyHasXRSession();
6479
6480 dom::Navigator* nav = Navigator();
6481 MOZ_ASSERT(nav != nullptr);
6482 nav->OnXRPermissionRequestAllow();
6483 }
6484
OnXRPermissionRequestCancel()6485 void nsGlobalWindowInner::OnXRPermissionRequestCancel() {
6486 mXRPermissionRequestInFlight = false;
6487 if (IsDying()) {
6488 // The window may have started dying while the permission request
6489 // is in flight.
6490 // Do not proceed if the window is dying, as that will result
6491 // in leaks of objects that get re-allocated after FreeInnerObjects
6492 // has been called, including mNavigator.
6493 return;
6494 }
6495 dom::Navigator* nav = Navigator();
6496 MOZ_ASSERT(nav != nullptr);
6497 nav->OnXRPermissionRequestCancel();
6498 }
6499
EventListenerAdded(nsAtom * aType)6500 void nsGlobalWindowInner::EventListenerAdded(nsAtom* aType) {
6501 if (aType == nsGkAtoms::onvrdisplayactivate ||
6502 aType == nsGkAtoms::onvrdisplayconnect ||
6503 aType == nsGkAtoms::onvrdisplaydeactivate ||
6504 aType == nsGkAtoms::onvrdisplaydisconnect ||
6505 aType == nsGkAtoms::onvrdisplaypresentchange) {
6506 RequestXRPermission();
6507 }
6508
6509 if (aType == nsGkAtoms::onvrdisplayactivate) {
6510 mHasVRDisplayActivateEvents = true;
6511 }
6512
6513 if ((aType == nsGkAtoms::onunload || aType == nsGkAtoms::onbeforeunload) &&
6514 mWindowGlobalChild) {
6515 if (++mUnloadOrBeforeUnloadListenerCount == 1) {
6516 mWindowGlobalChild->BlockBFCacheFor(BFCacheStatus::UNLOAD_LISTENER);
6517 }
6518 if (aType == nsGkAtoms::onbeforeunload &&
6519 (!mDoc || !(mDoc->GetSandboxFlags() & SANDBOXED_MODALS))) {
6520 mWindowGlobalChild->BeforeUnloadAdded();
6521 MOZ_ASSERT(mWindowGlobalChild->BeforeUnloadListeners() > 0);
6522 }
6523 }
6524
6525 // We need to initialize localStorage in order to receive notifications.
6526 if (aType == nsGkAtoms::onstorage) {
6527 ErrorResult rv;
6528 GetLocalStorage(rv);
6529 rv.SuppressException();
6530
6531 if (NextGenLocalStorageEnabled() && mLocalStorage &&
6532 mLocalStorage->Type() == Storage::eLocalStorage) {
6533 auto object = static_cast<LSObject*>(mLocalStorage.get());
6534
6535 Unused << NS_WARN_IF(NS_FAILED(object->EnsureObserver()));
6536 }
6537 }
6538 }
6539
EventListenerRemoved(nsAtom * aType)6540 void nsGlobalWindowInner::EventListenerRemoved(nsAtom* aType) {
6541 if ((aType == nsGkAtoms::onunload || aType == nsGkAtoms::onbeforeunload) &&
6542 mWindowGlobalChild) {
6543 MOZ_ASSERT(mUnloadOrBeforeUnloadListenerCount > 0);
6544 if (--mUnloadOrBeforeUnloadListenerCount == 0) {
6545 mWindowGlobalChild->UnblockBFCacheFor(BFCacheStatus::UNLOAD_LISTENER);
6546 }
6547 if (aType == nsGkAtoms::onbeforeunload &&
6548 (!mDoc || !(mDoc->GetSandboxFlags() & SANDBOXED_MODALS))) {
6549 mWindowGlobalChild->BeforeUnloadRemoved();
6550 MOZ_ASSERT(mWindowGlobalChild->BeforeUnloadListeners() >= 0);
6551 }
6552 }
6553
6554 if (aType == nsGkAtoms::onstorage) {
6555 if (NextGenLocalStorageEnabled() && mLocalStorage &&
6556 mLocalStorage->Type() == Storage::eLocalStorage &&
6557 // The remove event is fired even if this isn't the last listener, so
6558 // only remove if there are no other listeners left.
6559 mListenerManager &&
6560 !mListenerManager->HasListenersFor(nsGkAtoms::onstorage)) {
6561 auto object = static_cast<LSObject*>(mLocalStorage.get());
6562
6563 object->DropObserver();
6564 }
6565 }
6566 }
6567
NotifyHasXRSession()6568 void nsGlobalWindowInner::NotifyHasXRSession() {
6569 if (IsDying()) {
6570 // Do not proceed if the window is dying, as that will result
6571 // in leaks of objects that get re-allocated after FreeInnerObjects
6572 // has been called, including mVREventObserver.
6573 return;
6574 }
6575 if (mWindowGlobalChild && !mHasXRSession) {
6576 mWindowGlobalChild->BlockBFCacheFor(BFCacheStatus::HAS_USED_VR);
6577 }
6578 mHasXRSession = true;
6579 EnableVRUpdates();
6580 }
6581
HasUsedVR() const6582 bool nsGlobalWindowInner::HasUsedVR() const {
6583 // Returns true only if content has enumerated and activated
6584 // XR devices. Detection of XR runtimes without activation
6585 // will not cause true to be returned.
6586 return mHasXRSession;
6587 }
6588
IsVRContentDetected() const6589 bool nsGlobalWindowInner::IsVRContentDetected() const {
6590 // Returns true only if the content will respond to
6591 // the VRDisplayActivate event.
6592 return mHasVRDisplayActivateEvents;
6593 }
6594
IsVRContentPresenting() const6595 bool nsGlobalWindowInner::IsVRContentPresenting() const {
6596 for (const auto& display : mVRDisplays) {
6597 if (display->IsAnyPresenting(gfx::kVRGroupAll)) {
6598 return true;
6599 }
6600 }
6601 return false;
6602 }
6603
AddSizeOfIncludingThis(nsWindowSizes & aWindowSizes) const6604 void nsGlobalWindowInner::AddSizeOfIncludingThis(
6605 nsWindowSizes& aWindowSizes) const {
6606 aWindowSizes.mDOMSizes.mDOMOtherSize +=
6607 aWindowSizes.mState.mMallocSizeOf(this);
6608 aWindowSizes.mDOMSizes.mDOMOtherSize +=
6609 nsIGlobalObject::ShallowSizeOfExcludingThis(
6610 aWindowSizes.mState.mMallocSizeOf);
6611
6612 EventListenerManager* elm = GetExistingListenerManager();
6613 if (elm) {
6614 aWindowSizes.mDOMSizes.mDOMOtherSize +=
6615 elm->SizeOfIncludingThis(aWindowSizes.mState.mMallocSizeOf);
6616 aWindowSizes.mDOMEventListenersCount += elm->ListenerCount();
6617 }
6618 if (mDoc) {
6619 // Multiple global windows can share a document. So only measure the
6620 // document if it (a) doesn't have a global window, or (b) it's the
6621 // primary document for the window.
6622 if (!mDoc->GetInnerWindow() || mDoc->GetInnerWindow() == this) {
6623 mDoc->DocAddSizeOfIncludingThis(aWindowSizes);
6624 }
6625 }
6626
6627 if (mNavigator) {
6628 aWindowSizes.mDOMSizes.mDOMOtherSize +=
6629 mNavigator->SizeOfIncludingThis(aWindowSizes.mState.mMallocSizeOf);
6630 }
6631
6632 ForEachEventTargetObject([&](DOMEventTargetHelper* et, bool* aDoneOut) {
6633 if (nsCOMPtr<nsISizeOfEventTarget> iSizeOf = do_QueryObject(et)) {
6634 aWindowSizes.mDOMSizes.mDOMEventTargetsSize +=
6635 iSizeOf->SizeOfEventTargetIncludingThis(
6636 aWindowSizes.mState.mMallocSizeOf);
6637 }
6638 if (EventListenerManager* elm = et->GetExistingListenerManager()) {
6639 aWindowSizes.mDOMEventListenersCount += elm->ListenerCount();
6640 }
6641 ++aWindowSizes.mDOMEventTargetsCount;
6642 });
6643
6644 if (mPerformance) {
6645 aWindowSizes.mDOMSizes.mDOMPerformanceUserEntries =
6646 mPerformance->SizeOfUserEntries(aWindowSizes.mState.mMallocSizeOf);
6647 aWindowSizes.mDOMSizes.mDOMPerformanceResourceEntries =
6648 mPerformance->SizeOfResourceEntries(aWindowSizes.mState.mMallocSizeOf);
6649 aWindowSizes.mDOMSizes.mDOMPerformanceEventEntries =
6650 mPerformance->SizeOfEventEntries(aWindowSizes.mState.mMallocSizeOf);
6651 }
6652 }
6653
RegisterDataDocumentForMemoryReporting(Document * aDocument)6654 void nsGlobalWindowInner::RegisterDataDocumentForMemoryReporting(
6655 Document* aDocument) {
6656 aDocument->SetAddedToMemoryReportAsDataDocument();
6657 mDataDocumentsForMemoryReporting.AppendElement(
6658 do_GetWeakReference(aDocument));
6659 }
6660
UnregisterDataDocumentForMemoryReporting(Document * aDocument)6661 void nsGlobalWindowInner::UnregisterDataDocumentForMemoryReporting(
6662 Document* aDocument) {
6663 nsWeakPtr doc = do_GetWeakReference(aDocument);
6664 MOZ_ASSERT(mDataDocumentsForMemoryReporting.Contains(doc));
6665 mDataDocumentsForMemoryReporting.RemoveElement(doc);
6666 }
6667
CollectDOMSizesForDataDocuments(nsWindowSizes & aSize) const6668 void nsGlobalWindowInner::CollectDOMSizesForDataDocuments(
6669 nsWindowSizes& aSize) const {
6670 for (const nsWeakPtr& ptr : mDataDocumentsForMemoryReporting) {
6671 if (nsCOMPtr<Document> doc = do_QueryReferent(ptr)) {
6672 doc->DocAddSizeOfIncludingThis(aSize);
6673 }
6674 }
6675 }
6676
AddGamepad(GamepadHandle aHandle,Gamepad * aGamepad)6677 void nsGlobalWindowInner::AddGamepad(GamepadHandle aHandle, Gamepad* aGamepad) {
6678 // Create the index we will present to content based on which indices are
6679 // already taken, as required by the spec.
6680 // https://w3c.github.io/gamepad/gamepad.html#widl-Gamepad-index
6681 int index = 0;
6682 while (mGamepadIndexSet.Contains(index)) {
6683 ++index;
6684 }
6685 mGamepadIndexSet.Put(index);
6686 aGamepad->SetIndex(index);
6687 mGamepads.InsertOrUpdate(aHandle, RefPtr{aGamepad});
6688 }
6689
RemoveGamepad(GamepadHandle aHandle)6690 void nsGlobalWindowInner::RemoveGamepad(GamepadHandle aHandle) {
6691 RefPtr<Gamepad> gamepad;
6692 if (!mGamepads.Get(aHandle, getter_AddRefs(gamepad))) {
6693 return;
6694 }
6695 // Free up the index we were using so it can be reused
6696 mGamepadIndexSet.Remove(gamepad->Index());
6697 mGamepads.Remove(aHandle);
6698 }
6699
GetGamepads(nsTArray<RefPtr<Gamepad>> & aGamepads)6700 void nsGlobalWindowInner::GetGamepads(nsTArray<RefPtr<Gamepad>>& aGamepads) {
6701 aGamepads.Clear();
6702
6703 // navigator.getGamepads() always returns an empty array when
6704 // privacy.resistFingerprinting is true.
6705 if (nsContentUtils::ShouldResistFingerprinting(GetDocShell())) {
6706 return;
6707 }
6708
6709 // mGamepads.Count() may not be sufficient, but it's not harmful.
6710 aGamepads.SetCapacity(mGamepads.Count());
6711 for (const auto& entry : mGamepads) {
6712 Gamepad* gamepad = entry.GetWeak();
6713 aGamepads.EnsureLengthAtLeast(gamepad->Index() + 1);
6714 aGamepads[gamepad->Index()] = gamepad;
6715 }
6716 }
6717
GetGamepad(GamepadHandle aHandle)6718 already_AddRefed<Gamepad> nsGlobalWindowInner::GetGamepad(
6719 GamepadHandle aHandle) {
6720 RefPtr<Gamepad> gamepad;
6721
6722 if (mGamepads.Get(aHandle, getter_AddRefs(gamepad))) {
6723 return gamepad.forget();
6724 }
6725
6726 return nullptr;
6727 }
6728
SetHasSeenGamepadInput(bool aHasSeen)6729 void nsGlobalWindowInner::SetHasSeenGamepadInput(bool aHasSeen) {
6730 mHasSeenGamepadInput = aHasSeen;
6731 }
6732
HasSeenGamepadInput()6733 bool nsGlobalWindowInner::HasSeenGamepadInput() { return mHasSeenGamepadInput; }
6734
SyncGamepadState()6735 void nsGlobalWindowInner::SyncGamepadState() {
6736 if (mHasSeenGamepadInput) {
6737 RefPtr<GamepadManager> gamepadManager(GamepadManager::GetService());
6738 for (const auto& entry : mGamepads) {
6739 gamepadManager->SyncGamepadState(entry.GetKey(), this, entry.GetWeak());
6740 }
6741 }
6742 }
6743
StopGamepadHaptics()6744 void nsGlobalWindowInner::StopGamepadHaptics() {
6745 if (mHasSeenGamepadInput) {
6746 RefPtr<GamepadManager> gamepadManager(GamepadManager::GetService());
6747 gamepadManager->StopHaptics();
6748 }
6749 }
6750
UpdateVRDisplays(nsTArray<RefPtr<mozilla::dom::VRDisplay>> & aDevices)6751 bool nsGlobalWindowInner::UpdateVRDisplays(
6752 nsTArray<RefPtr<mozilla::dom::VRDisplay>>& aDevices) {
6753 VRDisplay::UpdateVRDisplays(mVRDisplays, this);
6754 aDevices = mVRDisplays.Clone();
6755 return true;
6756 }
6757
NotifyActiveVRDisplaysChanged()6758 void nsGlobalWindowInner::NotifyActiveVRDisplaysChanged() {
6759 if (mNavigator) {
6760 mNavigator->NotifyActiveVRDisplaysChanged();
6761 }
6762 }
6763
NotifyPresentationGenerationChanged(uint32_t aDisplayID)6764 void nsGlobalWindowInner::NotifyPresentationGenerationChanged(
6765 uint32_t aDisplayID) {
6766 for (const auto& display : mVRDisplays) {
6767 if (display->DisplayId() == aDisplayID) {
6768 display->OnPresentationGenerationChanged();
6769 }
6770 }
6771 }
6772
DispatchVRDisplayActivate(uint32_t aDisplayID,mozilla::dom::VRDisplayEventReason aReason)6773 void nsGlobalWindowInner::DispatchVRDisplayActivate(
6774 uint32_t aDisplayID, mozilla::dom::VRDisplayEventReason aReason) {
6775 // Ensure that our list of displays is up to date
6776 VRDisplay::UpdateVRDisplays(mVRDisplays, this);
6777
6778 // Search for the display identified with aDisplayID and fire the
6779 // event if found.
6780 for (const auto& display : mVRDisplays) {
6781 if (display->DisplayId() == aDisplayID) {
6782 if (aReason != VRDisplayEventReason::Navigation &&
6783 display->IsAnyPresenting(gfx::kVRGroupContent)) {
6784 // We only want to trigger this event if nobody is presenting to the
6785 // display already or when a page is loaded by navigating away
6786 // from a page with an active VR Presentation.
6787 continue;
6788 }
6789
6790 VRDisplayEventInit init;
6791 init.mBubbles = false;
6792 init.mCancelable = false;
6793 init.mDisplay = display;
6794 init.mReason.Construct(aReason);
6795
6796 RefPtr<VRDisplayEvent> event =
6797 VRDisplayEvent::Constructor(this, u"vrdisplayactivate"_ns, init);
6798 // vrdisplayactivate is a trusted event, allowing VRDisplay.requestPresent
6799 // to be used in response to link traversal, user request (chrome UX), and
6800 // HMD mounting detection sensors.
6801 event->SetTrusted(true);
6802 // VRDisplay.requestPresent normally requires a user gesture; however, an
6803 // exception is made to allow it to be called in response to
6804 // vrdisplayactivate during VR link traversal.
6805 display->StartHandlingVRNavigationEvent();
6806 DispatchEvent(*event);
6807 display->StopHandlingVRNavigationEvent();
6808 // Once we dispatch the event, we must not access any members as an event
6809 // listener can do anything, including closing windows.
6810 return;
6811 }
6812 }
6813 }
6814
DispatchVRDisplayDeactivate(uint32_t aDisplayID,mozilla::dom::VRDisplayEventReason aReason)6815 void nsGlobalWindowInner::DispatchVRDisplayDeactivate(
6816 uint32_t aDisplayID, mozilla::dom::VRDisplayEventReason aReason) {
6817 // Ensure that our list of displays is up to date
6818 VRDisplay::UpdateVRDisplays(mVRDisplays, this);
6819
6820 // Search for the display identified with aDisplayID and fire the
6821 // event if found.
6822 for (const auto& display : mVRDisplays) {
6823 if (display->DisplayId() == aDisplayID && display->IsPresenting()) {
6824 // We only want to trigger this event to content that is presenting to
6825 // the display already.
6826
6827 VRDisplayEventInit init;
6828 init.mBubbles = false;
6829 init.mCancelable = false;
6830 init.mDisplay = display;
6831 init.mReason.Construct(aReason);
6832
6833 RefPtr<VRDisplayEvent> event =
6834 VRDisplayEvent::Constructor(this, u"vrdisplaydeactivate"_ns, init);
6835 event->SetTrusted(true);
6836 DispatchEvent(*event);
6837 // Once we dispatch the event, we must not access any members as an event
6838 // listener can do anything, including closing windows.
6839 return;
6840 }
6841 }
6842 }
6843
DispatchVRDisplayConnect(uint32_t aDisplayID)6844 void nsGlobalWindowInner::DispatchVRDisplayConnect(uint32_t aDisplayID) {
6845 // Ensure that our list of displays is up to date
6846 VRDisplay::UpdateVRDisplays(mVRDisplays, this);
6847
6848 // Search for the display identified with aDisplayID and fire the
6849 // event if found.
6850 for (const auto& display : mVRDisplays) {
6851 if (display->DisplayId() == aDisplayID) {
6852 // Fire event even if not presenting to the display.
6853 VRDisplayEventInit init;
6854 init.mBubbles = false;
6855 init.mCancelable = false;
6856 init.mDisplay = display;
6857 // VRDisplayEvent.reason is not set for vrdisplayconnect
6858
6859 RefPtr<VRDisplayEvent> event =
6860 VRDisplayEvent::Constructor(this, u"vrdisplayconnect"_ns, init);
6861 event->SetTrusted(true);
6862 DispatchEvent(*event);
6863 // Once we dispatch the event, we must not access any members as an event
6864 // listener can do anything, including closing windows.
6865 return;
6866 }
6867 }
6868 }
6869
DispatchVRDisplayDisconnect(uint32_t aDisplayID)6870 void nsGlobalWindowInner::DispatchVRDisplayDisconnect(uint32_t aDisplayID) {
6871 // Ensure that our list of displays is up to date
6872 VRDisplay::UpdateVRDisplays(mVRDisplays, this);
6873
6874 // Search for the display identified with aDisplayID and fire the
6875 // event if found.
6876 for (const auto& display : mVRDisplays) {
6877 if (display->DisplayId() == aDisplayID) {
6878 // Fire event even if not presenting to the display.
6879 VRDisplayEventInit init;
6880 init.mBubbles = false;
6881 init.mCancelable = false;
6882 init.mDisplay = display;
6883 // VRDisplayEvent.reason is not set for vrdisplaydisconnect
6884
6885 RefPtr<VRDisplayEvent> event =
6886 VRDisplayEvent::Constructor(this, u"vrdisplaydisconnect"_ns, init);
6887 event->SetTrusted(true);
6888 DispatchEvent(*event);
6889 // Once we dispatch the event, we must not access any members as an event
6890 // listener can do anything, including closing windows.
6891 return;
6892 }
6893 }
6894 }
6895
DispatchVRDisplayPresentChange(uint32_t aDisplayID)6896 void nsGlobalWindowInner::DispatchVRDisplayPresentChange(uint32_t aDisplayID) {
6897 // Ensure that our list of displays is up to date
6898 VRDisplay::UpdateVRDisplays(mVRDisplays, this);
6899
6900 // Search for the display identified with aDisplayID and fire the
6901 // event if found.
6902 for (const auto& display : mVRDisplays) {
6903 if (display->DisplayId() == aDisplayID) {
6904 // Fire event even if not presenting to the display.
6905 VRDisplayEventInit init;
6906 init.mBubbles = false;
6907 init.mCancelable = false;
6908 init.mDisplay = display;
6909 // VRDisplayEvent.reason is not set for vrdisplaypresentchange
6910 RefPtr<VRDisplayEvent> event =
6911 VRDisplayEvent::Constructor(this, u"vrdisplaypresentchange"_ns, init);
6912 event->SetTrusted(true);
6913 DispatchEvent(*event);
6914 // Once we dispatch the event, we must not access any members as an event
6915 // listener can do anything, including closing windows.
6916 return;
6917 }
6918 }
6919 }
6920
6921 enum WindowState {
6922 // These constants need to match the constants in Window.webidl
6923 STATE_MAXIMIZED = 1,
6924 STATE_MINIMIZED = 2,
6925 STATE_NORMAL = 3,
6926 STATE_FULLSCREEN = 4
6927 };
6928
WindowState()6929 uint16_t nsGlobalWindowInner::WindowState() {
6930 nsCOMPtr<nsIWidget> widget = GetMainWidget();
6931
6932 int32_t mode = widget ? widget->SizeMode() : 0;
6933
6934 switch (mode) {
6935 case nsSizeMode_Minimized:
6936 return STATE_MINIMIZED;
6937 case nsSizeMode_Maximized:
6938 return STATE_MAXIMIZED;
6939 case nsSizeMode_Fullscreen:
6940 return STATE_FULLSCREEN;
6941 case nsSizeMode_Normal:
6942 return STATE_NORMAL;
6943 default:
6944 NS_WARNING("Illegal window state for this chrome window");
6945 break;
6946 }
6947
6948 return STATE_NORMAL;
6949 }
6950
IsFullyOccluded()6951 bool nsGlobalWindowInner::IsFullyOccluded() {
6952 nsCOMPtr<nsIWidget> widget = GetMainWidget();
6953 return widget && widget->IsFullyOccluded();
6954 }
6955
Maximize()6956 void nsGlobalWindowInner::Maximize() {
6957 nsCOMPtr<nsIWidget> widget = GetMainWidget();
6958
6959 if (widget) {
6960 widget->SetSizeMode(nsSizeMode_Maximized);
6961 }
6962 }
6963
Minimize()6964 void nsGlobalWindowInner::Minimize() {
6965 nsCOMPtr<nsIWidget> widget = GetMainWidget();
6966
6967 if (widget) {
6968 widget->SetSizeMode(nsSizeMode_Minimized);
6969 }
6970 }
6971
Restore()6972 void nsGlobalWindowInner::Restore() {
6973 nsCOMPtr<nsIWidget> widget = GetMainWidget();
6974
6975 if (widget) {
6976 widget->SetSizeMode(nsSizeMode_Normal);
6977 }
6978 }
6979
GetWorkspaceID(nsAString & workspaceID)6980 void nsGlobalWindowInner::GetWorkspaceID(nsAString& workspaceID) {
6981 nsCOMPtr<nsIWidget> widget = GetMainWidget();
6982
6983 workspaceID.Truncate();
6984 if (widget) {
6985 return widget->GetWorkspaceID(workspaceID);
6986 }
6987 }
6988
MoveToWorkspace(const nsAString & workspaceID)6989 void nsGlobalWindowInner::MoveToWorkspace(const nsAString& workspaceID) {
6990 nsCOMPtr<nsIWidget> widget = GetMainWidget();
6991
6992 if (widget) {
6993 widget->MoveToWorkspace(workspaceID);
6994 }
6995 }
6996
GetAttention(ErrorResult & aResult)6997 void nsGlobalWindowInner::GetAttention(ErrorResult& aResult) {
6998 return GetAttentionWithCycleCount(-1, aResult);
6999 }
7000
GetAttentionWithCycleCount(int32_t aCycleCount,ErrorResult & aError)7001 void nsGlobalWindowInner::GetAttentionWithCycleCount(int32_t aCycleCount,
7002 ErrorResult& aError) {
7003 nsCOMPtr<nsIWidget> widget = GetMainWidget();
7004
7005 if (widget) {
7006 aError = widget->GetAttention(aCycleCount);
7007 }
7008 }
7009
PromiseDocumentFlushed(PromiseDocumentFlushedCallback & aCallback,ErrorResult & aError)7010 already_AddRefed<Promise> nsGlobalWindowInner::PromiseDocumentFlushed(
7011 PromiseDocumentFlushedCallback& aCallback, ErrorResult& aError) {
7012 MOZ_RELEASE_ASSERT(IsChromeWindow());
7013
7014 if (!IsCurrentInnerWindow()) {
7015 aError.Throw(NS_ERROR_FAILURE);
7016 return nullptr;
7017 }
7018
7019 if (!mDoc || mIteratingDocumentFlushedResolvers) {
7020 aError.Throw(NS_ERROR_FAILURE);
7021 return nullptr;
7022 }
7023
7024 PresShell* presShell = mDoc->GetPresShell();
7025 if (!presShell) {
7026 aError.Throw(NS_ERROR_FAILURE);
7027 return nullptr;
7028 }
7029
7030 // We need to associate the lifetime of the Promise to the lifetime
7031 // of the caller's global. That way, if the window we're observing
7032 // refresh driver ticks on goes away before our observer is fired,
7033 // we can still resolve the Promise.
7034 nsIGlobalObject* global = GetIncumbentGlobal();
7035 if (!global) {
7036 aError.Throw(NS_ERROR_FAILURE);
7037 return nullptr;
7038 }
7039
7040 RefPtr<Promise> resultPromise = Promise::Create(global, aError);
7041 if (aError.Failed()) {
7042 return nullptr;
7043 }
7044
7045 UniquePtr<PromiseDocumentFlushedResolver> flushResolver(
7046 new PromiseDocumentFlushedResolver(resultPromise, aCallback));
7047
7048 if (!presShell->NeedStyleFlush() && !presShell->NeedLayoutFlush()) {
7049 flushResolver->Call();
7050 return resultPromise.forget();
7051 }
7052
7053 if (!TryToObserveRefresh()) {
7054 aError.Throw(NS_ERROR_FAILURE);
7055 return nullptr;
7056 }
7057
7058 mDocumentFlushedResolvers.AppendElement(std::move(flushResolver));
7059 return resultPromise.forget();
7060 }
7061
TryToObserveRefresh()7062 bool nsGlobalWindowInner::TryToObserveRefresh() {
7063 if (mObservingRefresh) {
7064 return true;
7065 }
7066
7067 if (!mDoc) {
7068 return false;
7069 }
7070
7071 nsPresContext* pc = mDoc->GetPresContext();
7072 if (!pc) {
7073 return false;
7074 }
7075
7076 mObservingRefresh = true;
7077 auto observer = MakeRefPtr<ManagedPostRefreshObserver>(
7078 pc, [win = RefPtr{this}](bool aWasCanceled) {
7079 if (win->MaybeCallDocumentFlushedResolvers(
7080 /* aUntilExhaustion = */ aWasCanceled)) {
7081 return ManagedPostRefreshObserver::Unregister::No;
7082 }
7083 win->mObservingRefresh = false;
7084 return ManagedPostRefreshObserver::Unregister::Yes;
7085 });
7086 pc->RegisterManagedPostRefreshObserver(observer.get());
7087 return mObservingRefresh;
7088 }
7089
CallDocumentFlushedResolvers(bool aUntilExhaustion)7090 void nsGlobalWindowInner::CallDocumentFlushedResolvers(bool aUntilExhaustion) {
7091 while (true) {
7092 {
7093 // To coalesce MicroTask checkpoints inside callback call, enclose the
7094 // inner loop with nsAutoMicroTask, and perform a MicroTask checkpoint
7095 // after the loop.
7096 nsAutoMicroTask mt;
7097
7098 mIteratingDocumentFlushedResolvers = true;
7099
7100 auto resolvers = std::move(mDocumentFlushedResolvers);
7101 for (const auto& resolver : resolvers) {
7102 resolver->Call();
7103 }
7104
7105 mIteratingDocumentFlushedResolvers = false;
7106 }
7107
7108 // Leaving nsAutoMicroTask above will perform MicroTask checkpoint, and
7109 // Promise callbacks there may create mDocumentFlushedResolvers items.
7110
7111 // If there's no new resolvers, or we're not exhausting the queue, there's
7112 // nothing to do (we'll keep observing if there's any new observer).
7113 //
7114 // Otherwise, keep looping to call all promises. This case can happen while
7115 // destroying the window. This violates the constraint that the
7116 // promiseDocumentFlushed callback only ever run when no flush is needed,
7117 // but it's necessary to resolve the Promise returned by that.
7118 if (!aUntilExhaustion || mDocumentFlushedResolvers.IsEmpty()) {
7119 break;
7120 }
7121 }
7122 }
7123
MaybeCallDocumentFlushedResolvers(bool aUntilExhaustion)7124 bool nsGlobalWindowInner::MaybeCallDocumentFlushedResolvers(
7125 bool aUntilExhaustion) {
7126 MOZ_ASSERT(mDoc);
7127
7128 PresShell* presShell = mDoc->GetPresShell();
7129 if (!presShell || aUntilExhaustion) {
7130 CallDocumentFlushedResolvers(/* aUntilExhaustion = */ true);
7131 return false;
7132 }
7133
7134 if (presShell->NeedStyleFlush() || presShell->NeedLayoutFlush()) {
7135 // By the time our observer fired, something has already invalidated
7136 // style or layout - or perhaps we're still in the middle of a flush that
7137 // was interrupted. In either case, we'll wait until the next refresh driver
7138 // tick instead and try again.
7139 return true;
7140 }
7141
7142 CallDocumentFlushedResolvers(/* aUntilExhaustion = */ false);
7143 return !mDocumentFlushedResolvers.IsEmpty();
7144 }
7145
GetWindowRoot(mozilla::ErrorResult & aError)7146 already_AddRefed<nsWindowRoot> nsGlobalWindowInner::GetWindowRoot(
7147 mozilla::ErrorResult& aError) {
7148 FORWARD_TO_OUTER_OR_THROW(GetWindowRootOuter, (), aError, nullptr);
7149 }
7150
SetCursor(const nsACString & aCursor,ErrorResult & aError)7151 void nsGlobalWindowInner::SetCursor(const nsACString& aCursor,
7152 ErrorResult& aError) {
7153 FORWARD_TO_OUTER_OR_THROW(SetCursorOuter, (aCursor, aError), aError, );
7154 }
7155
7156 NS_IMETHODIMP
GetBrowserDOMWindow(nsIBrowserDOMWindow ** aBrowserWindow)7157 nsGlobalWindowInner::GetBrowserDOMWindow(nsIBrowserDOMWindow** aBrowserWindow) {
7158 MOZ_RELEASE_ASSERT(IsChromeWindow());
7159
7160 ErrorResult rv;
7161 NS_IF_ADDREF(*aBrowserWindow = GetBrowserDOMWindow(rv));
7162 return rv.StealNSResult();
7163 }
7164
GetBrowserDOMWindow(ErrorResult & aError)7165 nsIBrowserDOMWindow* nsGlobalWindowInner::GetBrowserDOMWindow(
7166 ErrorResult& aError) {
7167 FORWARD_TO_OUTER_OR_THROW(GetBrowserDOMWindowOuter, (), aError, nullptr);
7168 }
7169
SetBrowserDOMWindow(nsIBrowserDOMWindow * aBrowserWindow,ErrorResult & aError)7170 void nsGlobalWindowInner::SetBrowserDOMWindow(
7171 nsIBrowserDOMWindow* aBrowserWindow, ErrorResult& aError) {
7172 FORWARD_TO_OUTER_OR_THROW(SetBrowserDOMWindowOuter, (aBrowserWindow),
7173 aError, );
7174 }
7175
NotifyDefaultButtonLoaded(Element & aDefaultButton,ErrorResult & aError)7176 void nsGlobalWindowInner::NotifyDefaultButtonLoaded(Element& aDefaultButton,
7177 ErrorResult& aError) {
7178 #ifdef MOZ_XUL
7179 // Don't snap to a disabled button.
7180 nsCOMPtr<nsIDOMXULControlElement> xulControl = aDefaultButton.AsXULControl();
7181 if (!xulControl) {
7182 aError.Throw(NS_ERROR_FAILURE);
7183 return;
7184 }
7185 bool disabled;
7186 aError = xulControl->GetDisabled(&disabled);
7187 if (aError.Failed() || disabled) {
7188 return;
7189 }
7190
7191 // Get the button rect in screen coordinates.
7192 nsIFrame* frame = aDefaultButton.GetPrimaryFrame();
7193 if (!frame) {
7194 aError.Throw(NS_ERROR_FAILURE);
7195 return;
7196 }
7197 LayoutDeviceIntRect buttonRect = LayoutDeviceIntRect::FromAppUnitsToNearest(
7198 frame->GetScreenRectInAppUnits(),
7199 frame->PresContext()->AppUnitsPerDevPixel());
7200
7201 // Get the widget rect in screen coordinates.
7202 nsIWidget* widget = GetNearestWidget();
7203 if (!widget) {
7204 aError.Throw(NS_ERROR_FAILURE);
7205 return;
7206 }
7207 LayoutDeviceIntRect widgetRect = widget->GetScreenBounds();
7208
7209 // Convert the buttonRect coordinates from screen to the widget.
7210 buttonRect -= widgetRect.TopLeft();
7211 nsresult rv = widget->OnDefaultButtonLoaded(buttonRect);
7212 if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) {
7213 aError.Throw(rv);
7214 }
7215 #else
7216 aError.Throw(NS_ERROR_NOT_IMPLEMENTED);
7217 #endif
7218 }
7219
MessageManager()7220 ChromeMessageBroadcaster* nsGlobalWindowInner::MessageManager() {
7221 MOZ_ASSERT(IsChromeWindow());
7222 if (!mChromeFields.mMessageManager) {
7223 RefPtr<ChromeMessageBroadcaster> globalMM =
7224 nsFrameMessageManager::GetGlobalMessageManager();
7225 mChromeFields.mMessageManager = new ChromeMessageBroadcaster(globalMM);
7226 }
7227 return mChromeFields.mMessageManager;
7228 }
7229
GetGroupMessageManager(const nsAString & aGroup)7230 ChromeMessageBroadcaster* nsGlobalWindowInner::GetGroupMessageManager(
7231 const nsAString& aGroup) {
7232 MOZ_ASSERT(IsChromeWindow());
7233
7234 return mChromeFields.mGroupMessageManagers
7235 .LookupOrInsertWith(
7236 aGroup,
7237 [&] {
7238 return MakeAndAddRef<ChromeMessageBroadcaster>(MessageManager());
7239 })
7240 .get();
7241 }
7242
InitWasOffline()7243 void nsGlobalWindowInner::InitWasOffline() { mWasOffline = NS_IsOffline(); }
7244
7245 #if defined(MOZ_WIDGET_ANDROID)
Orientation(CallerType aCallerType) const7246 int16_t nsGlobalWindowInner::Orientation(CallerType aCallerType) const {
7247 return nsContentUtils::ResistFingerprinting(aCallerType)
7248 ? 0
7249 : WindowOrientationObserver::OrientationAngle();
7250 }
7251 #endif
7252
GetConsole(JSContext * aCx,ErrorResult & aRv)7253 already_AddRefed<Console> nsGlobalWindowInner::GetConsole(JSContext* aCx,
7254 ErrorResult& aRv) {
7255 if (!mConsole) {
7256 mConsole = Console::Create(aCx, this, aRv);
7257 if (NS_WARN_IF(aRv.Failed())) {
7258 return nullptr;
7259 }
7260 }
7261
7262 RefPtr<Console> console = mConsole;
7263 return console.forget();
7264 }
7265
IsSecureContext() const7266 bool nsGlobalWindowInner::IsSecureContext() const {
7267 JS::Realm* realm = js::GetNonCCWObjectRealm(GetWrapperPreserveColor());
7268 return JS::GetIsSecureContext(realm);
7269 }
7270
GetExternal(ErrorResult & aRv)7271 External* nsGlobalWindowInner::GetExternal(ErrorResult& aRv) {
7272 if (!mExternal) {
7273 mExternal = new dom::External(ToSupports(this));
7274 }
7275
7276 return mExternal;
7277 }
7278
GetSidebar(OwningExternalOrWindowProxy & aResult,ErrorResult & aRv)7279 void nsGlobalWindowInner::GetSidebar(OwningExternalOrWindowProxy& aResult,
7280 ErrorResult& aRv) {
7281 // First check for a named frame named "sidebar"
7282 RefPtr<BrowsingContext> domWindow = GetChildWindow(u"sidebar"_ns);
7283 if (domWindow) {
7284 aResult.SetAsWindowProxy() = std::move(domWindow);
7285 return;
7286 }
7287
7288 RefPtr<External> external = GetExternal(aRv);
7289 if (external) {
7290 aResult.SetAsExternal() = external;
7291 }
7292 }
7293
ClearDocumentDependentSlots(JSContext * aCx)7294 void nsGlobalWindowInner::ClearDocumentDependentSlots(JSContext* aCx) {
7295 // If JSAPI OOMs here, there is basically nothing we can do to recover safely.
7296 if (!Window_Binding::ClearCachedDocumentValue(aCx, this) ||
7297 !Window_Binding::ClearCachedPerformanceValue(aCx, this)) {
7298 MOZ_CRASH("Unhandlable OOM while clearing document dependent slots.");
7299 }
7300 }
7301
7302 /* static */
CreateNamedPropertiesObject(JSContext * aCx,JS::Handle<JSObject * > aProto)7303 JSObject* nsGlobalWindowInner::CreateNamedPropertiesObject(
7304 JSContext* aCx, JS::Handle<JSObject*> aProto) {
7305 return WindowNamedPropertiesHandler::Create(aCx, aProto);
7306 }
7307
RedefineProperty(JSContext * aCx,const char * aPropName,JS::Handle<JS::Value> aValue,ErrorResult & aError)7308 void nsGlobalWindowInner::RedefineProperty(JSContext* aCx,
7309 const char* aPropName,
7310 JS::Handle<JS::Value> aValue,
7311 ErrorResult& aError) {
7312 JS::Rooted<JSObject*> thisObj(aCx, GetWrapperPreserveColor());
7313 if (!thisObj) {
7314 aError.Throw(NS_ERROR_UNEXPECTED);
7315 return;
7316 }
7317
7318 if (!JS_WrapObject(aCx, &thisObj) ||
7319 !JS_DefineProperty(aCx, thisObj, aPropName, aValue, JSPROP_ENUMERATE)) {
7320 aError.Throw(NS_ERROR_FAILURE);
7321 }
7322 }
7323
7324 template <typename T>
GetReplaceableWindowCoord(JSContext * aCx,nsGlobalWindowInner::WindowCoordGetter<T> aGetter,JS::MutableHandle<JS::Value> aRetval,CallerType aCallerType,ErrorResult & aError)7325 void nsGlobalWindowInner::GetReplaceableWindowCoord(
7326 JSContext* aCx, nsGlobalWindowInner::WindowCoordGetter<T> aGetter,
7327 JS::MutableHandle<JS::Value> aRetval, CallerType aCallerType,
7328 ErrorResult& aError) {
7329 T coord = (this->*aGetter)(aCallerType, aError);
7330 if (!aError.Failed() && !ToJSValue(aCx, coord, aRetval)) {
7331 aError.Throw(NS_ERROR_FAILURE);
7332 }
7333 }
7334
7335 template <typename T>
SetReplaceableWindowCoord(JSContext * aCx,nsGlobalWindowInner::WindowCoordSetter<T> aSetter,JS::Handle<JS::Value> aValue,const char * aPropName,CallerType aCallerType,ErrorResult & aError)7336 void nsGlobalWindowInner::SetReplaceableWindowCoord(
7337 JSContext* aCx, nsGlobalWindowInner::WindowCoordSetter<T> aSetter,
7338 JS::Handle<JS::Value> aValue, const char* aPropName, CallerType aCallerType,
7339 ErrorResult& aError) {
7340 /*
7341 * If caller is not chrome and the user has not explicitly exempted the site,
7342 * just treat this the way we would an IDL replaceable property.
7343 */
7344 nsGlobalWindowOuter* outer = GetOuterWindowInternal();
7345 if (!outer || !outer->CanMoveResizeWindows(aCallerType) ||
7346 mBrowsingContext->IsFrame()) {
7347 RedefineProperty(aCx, aPropName, aValue, aError);
7348 return;
7349 }
7350
7351 T value;
7352 if (!ValueToPrimitive<T, eDefault>(aCx, aValue, aPropName, &value)) {
7353 aError.Throw(NS_ERROR_UNEXPECTED);
7354 return;
7355 }
7356
7357 if (nsContentUtils::ShouldResistFingerprinting(GetDocShell())) {
7358 bool innerWidthSpecified = false;
7359 bool innerHeightSpecified = false;
7360 bool outerWidthSpecified = false;
7361 bool outerHeightSpecified = false;
7362
7363 if (strcmp(aPropName, "innerWidth") == 0) {
7364 innerWidthSpecified = true;
7365 } else if (strcmp(aPropName, "innerHeight") == 0) {
7366 innerHeightSpecified = true;
7367 } else if (strcmp(aPropName, "outerWidth") == 0) {
7368 outerWidthSpecified = true;
7369 } else if (strcmp(aPropName, "outerHeight") == 0) {
7370 outerHeightSpecified = true;
7371 }
7372
7373 if (innerWidthSpecified || innerHeightSpecified || outerWidthSpecified ||
7374 outerHeightSpecified) {
7375 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = outer->GetTreeOwnerWindow();
7376 nsCOMPtr<nsIScreen> screen;
7377 nsCOMPtr<nsIScreenManager> screenMgr(
7378 do_GetService("@mozilla.org/gfx/screenmanager;1"));
7379 int32_t winLeft = 0;
7380 int32_t winTop = 0;
7381 int32_t winWidth = 0;
7382 int32_t winHeight = 0;
7383 double scale = 1.0;
7384
7385 if (treeOwnerAsWin && screenMgr) {
7386 // Acquire current window size.
7387 treeOwnerAsWin->GetUnscaledDevicePixelsPerCSSPixel(&scale);
7388 treeOwnerAsWin->GetPositionAndSize(&winLeft, &winTop, &winWidth,
7389 &winHeight);
7390 winLeft = NSToIntRound(winHeight / scale);
7391 winTop = NSToIntRound(winWidth / scale);
7392 winWidth = NSToIntRound(winWidth / scale);
7393 winHeight = NSToIntRound(winHeight / scale);
7394
7395 // Acquire content window size.
7396 CSSSize contentSize;
7397 outer->GetInnerSize(contentSize);
7398
7399 screenMgr->ScreenForRect(winLeft, winTop, winWidth, winHeight,
7400 getter_AddRefs(screen));
7401
7402 if (screen) {
7403 int32_t roundedValue = std::round(value);
7404 int32_t* targetContentWidth = nullptr;
7405 int32_t* targetContentHeight = nullptr;
7406 int32_t screenWidth = 0;
7407 int32_t screenHeight = 0;
7408 int32_t chromeWidth = 0;
7409 int32_t chromeHeight = 0;
7410 int32_t inputWidth = 0;
7411 int32_t inputHeight = 0;
7412 int32_t unused = 0;
7413
7414 // Get screen dimensions (in device pixels)
7415 screen->GetAvailRect(&unused, &unused, &screenWidth, &screenHeight);
7416 // Convert them to CSS pixels
7417 screenWidth = NSToIntRound(screenWidth / scale);
7418 screenHeight = NSToIntRound(screenHeight / scale);
7419
7420 // Calculate the chrome UI size.
7421 chromeWidth = winWidth - contentSize.width;
7422 chromeHeight = winHeight - contentSize.height;
7423
7424 if (innerWidthSpecified || outerWidthSpecified) {
7425 inputWidth = value;
7426 targetContentWidth = &roundedValue;
7427 targetContentHeight = &unused;
7428 } else if (innerHeightSpecified || outerHeightSpecified) {
7429 inputHeight = value;
7430 targetContentWidth = &unused;
7431 targetContentHeight = &roundedValue;
7432 }
7433
7434 nsContentUtils::CalcRoundedWindowSizeForResistingFingerprinting(
7435 chromeWidth, chromeHeight, screenWidth, screenHeight, inputWidth,
7436 inputHeight, outerWidthSpecified, outerHeightSpecified,
7437 targetContentWidth, targetContentHeight);
7438 value = T(roundedValue);
7439 }
7440 }
7441 }
7442 }
7443
7444 (this->*aSetter)(value, aCallerType, aError);
7445 }
7446
FireOnNewGlobalObject()7447 void nsGlobalWindowInner::FireOnNewGlobalObject() {
7448 // AutoEntryScript required to invoke debugger hook, which is a
7449 // Gecko-specific concept at present.
7450 AutoEntryScript aes(this, "nsGlobalWindowInner report new global");
7451 JS::Rooted<JSObject*> global(aes.cx(), GetWrapper());
7452 JS_FireOnNewGlobalObject(aes.cx(), global);
7453 }
7454
7455 #if defined(_WINDOWS_) && !defined(MOZ_WRAPPED_WINDOWS_H)
7456 # pragma message( \
7457 "wrapper failure reason: " MOZ_WINDOWS_WRAPPER_DISABLED_REASON)
7458 # error "Never include unwrapped windows.h in this file!"
7459 #endif
7460
CreateImageBitmap(const ImageBitmapSource & aImage,ErrorResult & aRv)7461 already_AddRefed<Promise> nsGlobalWindowInner::CreateImageBitmap(
7462 const ImageBitmapSource& aImage, ErrorResult& aRv) {
7463 return ImageBitmap::Create(this, aImage, Nothing(), aRv);
7464 }
7465
CreateImageBitmap(const ImageBitmapSource & aImage,int32_t aSx,int32_t aSy,int32_t aSw,int32_t aSh,ErrorResult & aRv)7466 already_AddRefed<Promise> nsGlobalWindowInner::CreateImageBitmap(
7467 const ImageBitmapSource& aImage, int32_t aSx, int32_t aSy, int32_t aSw,
7468 int32_t aSh, ErrorResult& aRv) {
7469 return ImageBitmap::Create(this, aImage,
7470 Some(gfx::IntRect(aSx, aSy, aSw, aSh)), aRv);
7471 }
7472
Dispatch(TaskCategory aCategory,already_AddRefed<nsIRunnable> && aRunnable)7473 nsresult nsGlobalWindowInner::Dispatch(
7474 TaskCategory aCategory, already_AddRefed<nsIRunnable>&& aRunnable) {
7475 MOZ_RELEASE_ASSERT(NS_IsMainThread());
7476 if (GetDocGroup()) {
7477 return GetDocGroup()->Dispatch(aCategory, std::move(aRunnable));
7478 }
7479 return DispatcherTrait::Dispatch(aCategory, std::move(aRunnable));
7480 }
7481
EventTargetFor(TaskCategory aCategory) const7482 nsISerialEventTarget* nsGlobalWindowInner::EventTargetFor(
7483 TaskCategory aCategory) const {
7484 MOZ_RELEASE_ASSERT(NS_IsMainThread());
7485 if (GetDocGroup()) {
7486 return GetDocGroup()->EventTargetFor(aCategory);
7487 }
7488 return DispatcherTrait::EventTargetFor(aCategory);
7489 }
7490
AbstractMainThreadFor(TaskCategory aCategory)7491 AbstractThread* nsGlobalWindowInner::AbstractMainThreadFor(
7492 TaskCategory aCategory) {
7493 MOZ_RELEASE_ASSERT(NS_IsMainThread());
7494 if (GetDocGroup()) {
7495 return GetDocGroup()->AbstractMainThreadFor(aCategory);
7496 }
7497 return DispatcherTrait::AbstractMainThreadFor(aCategory);
7498 }
7499
GetPaintWorklet(ErrorResult & aRv)7500 Worklet* nsGlobalWindowInner::GetPaintWorklet(ErrorResult& aRv) {
7501 if (!mPaintWorklet) {
7502 nsIPrincipal* principal = GetPrincipal();
7503 if (!principal) {
7504 aRv.Throw(NS_ERROR_FAILURE);
7505 return nullptr;
7506 }
7507
7508 mPaintWorklet = PaintWorkletImpl::CreateWorklet(this, principal);
7509 }
7510
7511 return mPaintWorklet;
7512 }
7513
GetRegionalPrefsLocales(nsTArray<nsString> & aLocales)7514 void nsGlobalWindowInner::GetRegionalPrefsLocales(
7515 nsTArray<nsString>& aLocales) {
7516 MOZ_ASSERT(mozilla::intl::LocaleService::GetInstance());
7517
7518 AutoTArray<nsCString, 10> rpLocales;
7519 mozilla::intl::LocaleService::GetInstance()->GetRegionalPrefsLocales(
7520 rpLocales);
7521
7522 for (const auto& loc : rpLocales) {
7523 aLocales.AppendElement(NS_ConvertUTF8toUTF16(loc));
7524 }
7525 }
7526
GetWebExposedLocales(nsTArray<nsString> & aLocales)7527 void nsGlobalWindowInner::GetWebExposedLocales(nsTArray<nsString>& aLocales) {
7528 MOZ_ASSERT(mozilla::intl::LocaleService::GetInstance());
7529
7530 AutoTArray<nsCString, 10> rpLocales;
7531 mozilla::intl::LocaleService::GetInstance()->GetWebExposedLocales(rpLocales);
7532
7533 for (const auto& loc : rpLocales) {
7534 aLocales.AppendElement(NS_ConvertUTF8toUTF16(loc));
7535 }
7536 }
7537
GetIntlUtils(ErrorResult & aError)7538 IntlUtils* nsGlobalWindowInner::GetIntlUtils(ErrorResult& aError) {
7539 if (!mIntlUtils) {
7540 mIntlUtils = new IntlUtils(this);
7541 }
7542
7543 return mIntlUtils;
7544 }
7545
StoreSharedWorker(SharedWorker * aSharedWorker)7546 void nsGlobalWindowInner::StoreSharedWorker(SharedWorker* aSharedWorker) {
7547 MOZ_ASSERT(aSharedWorker);
7548 MOZ_ASSERT(!mSharedWorkers.Contains(aSharedWorker));
7549
7550 mSharedWorkers.AppendElement(aSharedWorker);
7551 }
7552
ForgetSharedWorker(SharedWorker * aSharedWorker)7553 void nsGlobalWindowInner::ForgetSharedWorker(SharedWorker* aSharedWorker) {
7554 MOZ_ASSERT(aSharedWorker);
7555 MOZ_ASSERT(mSharedWorkers.Contains(aSharedWorker));
7556
7557 mSharedWorkers.RemoveElement(aSharedWorker);
7558 }
7559
StorageAccessPermissionGranted()7560 void nsGlobalWindowInner::StorageAccessPermissionGranted() {
7561 PropagateStorageAccessPermissionGrantedToWorkers(*this);
7562
7563 // If we have a partitioned localStorage, it's time to replace it with a real
7564 // one in order to receive notifications.
7565
7566 if (mLocalStorage) {
7567 IgnoredErrorResult error;
7568 GetLocalStorage(error);
7569 if (NS_WARN_IF(error.Failed())) {
7570 return;
7571 }
7572
7573 MOZ_ASSERT(mLocalStorage &&
7574 mLocalStorage->Type() == Storage::eLocalStorage);
7575
7576 if (NextGenLocalStorageEnabled() && mListenerManager &&
7577 mListenerManager->HasListenersFor(nsGkAtoms::onstorage)) {
7578 auto object = static_cast<LSObject*>(mLocalStorage.get());
7579
7580 object->EnsureObserver();
7581 }
7582 }
7583
7584 // Reset the IndexedDB factory.
7585 mIndexedDB = nullptr;
7586
7587 // Reset DOM Cache
7588 mCacheStorage = nullptr;
7589
7590 // Reset the active storage principal
7591 if (mDoc) {
7592 mDoc->ClearActiveStoragePrincipal();
7593 if (mWindowGlobalChild) {
7594 // XXX(farre): This is a bit backwards, but clearing the storage
7595 // principal might make us end up with a new effective storage
7596 // principal on the child side than on the parent side, which
7597 // means that we need to sync it. See bug 1705359.
7598 mWindowGlobalChild->SetDocumentPrincipal(
7599 mDoc->NodePrincipal(), mDoc->EffectiveStoragePrincipal());
7600 }
7601 }
7602 }
7603
GetContentMediaController()7604 ContentMediaController* nsGlobalWindowInner::GetContentMediaController() {
7605 if (mContentMediaController) {
7606 return mContentMediaController;
7607 }
7608 if (!mBrowsingContext) {
7609 return nullptr;
7610 }
7611
7612 mContentMediaController = new ContentMediaController(mBrowsingContext->Id());
7613 return mContentMediaController;
7614 }
7615
SetScrollMarks(const nsTArray<uint32_t> & aScrollMarks)7616 void nsGlobalWindowInner::SetScrollMarks(
7617 const nsTArray<uint32_t>& aScrollMarks) {
7618 mScrollMarks.Assign(aScrollMarks);
7619
7620 // Mark the scrollbar for repainting.
7621 if (mDoc) {
7622 PresShell* presShell = mDoc->GetPresShell();
7623 if (presShell) {
7624 nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollable();
7625 if (sf) {
7626 sf->InvalidateVerticalScrollbar();
7627 }
7628 }
7629 }
7630 }
7631
7632 /* static */
Create(nsGlobalWindowOuter * aOuterWindow,bool aIsChrome,WindowGlobalChild * aActor)7633 already_AddRefed<nsGlobalWindowInner> nsGlobalWindowInner::Create(
7634 nsGlobalWindowOuter* aOuterWindow, bool aIsChrome,
7635 WindowGlobalChild* aActor) {
7636 RefPtr<nsGlobalWindowInner> window =
7637 new nsGlobalWindowInner(aOuterWindow, aActor);
7638 if (aIsChrome) {
7639 window->mIsChrome = true;
7640 window->mCleanMessageManager = true;
7641 }
7642
7643 if (aActor) {
7644 aActor->InitWindowGlobal(window);
7645 }
7646
7647 window->InitWasOffline();
7648 return window.forget();
7649 }
7650
GetDocumentURI() const7651 nsIURI* nsPIDOMWindowInner::GetDocumentURI() const {
7652 return mDoc ? mDoc->GetDocumentURI() : mDocumentURI.get();
7653 }
7654
GetDocBaseURI() const7655 nsIURI* nsPIDOMWindowInner::GetDocBaseURI() const {
7656 return mDoc ? mDoc->GetDocBaseURI() : mDocBaseURI.get();
7657 }
7658
GetWindowContext() const7659 mozilla::dom::WindowContext* nsPIDOMWindowInner::GetWindowContext() const {
7660 return mWindowGlobalChild ? mWindowGlobalChild->WindowContext() : nullptr;
7661 }
7662
RemoveFromBFCacheSync()7663 bool nsPIDOMWindowInner::RemoveFromBFCacheSync() {
7664 if (Document* doc = GetExtantDoc()) {
7665 return doc->RemoveFromBFCacheSync();
7666 }
7667 return false;
7668 }
7669
MaybeCreateDoc()7670 void nsPIDOMWindowInner::MaybeCreateDoc() {
7671 // XXX: Forward to outer?
7672 MOZ_ASSERT(!mDoc);
7673 if (nsIDocShell* docShell = GetDocShell()) {
7674 // Note that |document| here is the same thing as our mDoc, but we
7675 // don't have to explicitly set the member variable because the docshell
7676 // has already called SetNewDocument().
7677 nsCOMPtr<Document> document = docShell->GetDocument();
7678 Unused << document;
7679 }
7680 }
7681
GetDocGroup() const7682 mozilla::dom::DocGroup* nsPIDOMWindowInner::GetDocGroup() const {
7683 Document* doc = GetExtantDoc();
7684 if (doc) {
7685 return doc->GetDocGroup();
7686 }
7687 return nullptr;
7688 }
7689
7690 mozilla::dom::BrowsingContextGroup*
GetBrowsingContextGroup() const7691 nsPIDOMWindowInner::GetBrowsingContextGroup() const {
7692 return mBrowsingContext ? mBrowsingContext->Group() : nullptr;
7693 }
7694
AsGlobal()7695 nsIGlobalObject* nsPIDOMWindowInner::AsGlobal() {
7696 return nsGlobalWindowInner::Cast(this);
7697 }
7698
AsGlobal() const7699 const nsIGlobalObject* nsPIDOMWindowInner::AsGlobal() const {
7700 return nsGlobalWindowInner::Cast(this);
7701 }
7702
SaveStorageAccessPermissionGranted()7703 void nsPIDOMWindowInner::SaveStorageAccessPermissionGranted() {
7704 mStorageAccessPermissionGranted = true;
7705
7706 nsGlobalWindowInner::Cast(this)->StorageAccessPermissionGranted();
7707 }
7708
HasStorageAccessPermissionGranted()7709 bool nsPIDOMWindowInner::HasStorageAccessPermissionGranted() {
7710 return mStorageAccessPermissionGranted;
7711 }
7712
nsPIDOMWindowInner(nsPIDOMWindowOuter * aOuterWindow,WindowGlobalChild * aActor)7713 nsPIDOMWindowInner::nsPIDOMWindowInner(nsPIDOMWindowOuter* aOuterWindow,
7714 WindowGlobalChild* aActor)
7715 : mMutationBits(0),
7716 mIsDocumentLoaded(false),
7717 mIsHandlingResizeEvent(false),
7718 mMayHavePaintEventListener(false),
7719 mMayHaveTouchEventListener(false),
7720 mMayHaveSelectionChangeEventListener(false),
7721 mMayHaveMouseEnterLeaveEventListener(false),
7722 mMayHavePointerEnterLeaveEventListener(false),
7723 mMayHaveBeforeInputEventListenerForTelemetry(false),
7724 mMutationObserverHasObservedNodeForTelemetry(false),
7725 mOuterWindow(aOuterWindow),
7726 mWindowID(0),
7727 mHasNotifiedGlobalCreated(false),
7728 mMarkedCCGeneration(0),
7729 mHasTriedToCacheTopInnerWindow(false),
7730 mNumOfIndexedDBDatabases(0),
7731 mNumOfOpenWebSockets(0),
7732 mEvent(nullptr),
7733 mStorageAccessPermissionGranted(false),
7734 mWindowGlobalChild(aActor),
7735 mWasSuspendedByGroup(false) {
7736 MOZ_ASSERT(aOuterWindow);
7737 mBrowsingContext = aOuterWindow->GetBrowsingContext();
7738
7739 if (mWindowGlobalChild) {
7740 mWindowID = aActor->InnerWindowId();
7741
7742 MOZ_ASSERT(mWindowGlobalChild->BrowsingContext() == mBrowsingContext);
7743 } else {
7744 mWindowID = nsContentUtils::GenerateWindowId();
7745 }
7746 }
7747
7748 nsPIDOMWindowInner::~nsPIDOMWindowInner() = default;
7749
7750 #undef FORWARD_TO_OUTER
7751 #undef FORWARD_TO_OUTER_OR_THROW
7752 #undef FORWARD_TO_OUTER_VOID
7753