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