nsGlobalWindowOuter.cpp (260625B)
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 "nsGlobalWindowOuter.h" 8 9 #include <algorithm> 10 11 #include "mozilla/Assertions.h" 12 #include "mozilla/ScopeExit.h" 13 #include "nsDeviceContext.h" 14 #include "nsGlobalWindowInner.h" 15 16 // Local Includes 17 #include "Navigator.h" 18 #include "WindowDestroyedEvent.h" 19 #include "WindowNamedPropertiesHandler.h" 20 #include "mozilla/AntiTrackingUtils.h" 21 #include "mozilla/IntegerPrintfMacros.h" 22 #include "mozilla/Result.h" 23 #include "mozilla/StorageAccessAPIHelper.h" 24 #include "mozilla/dom/AutoPrintEventDispatcher.h" 25 #include "mozilla/dom/BindingUtils.h" 26 #include "mozilla/dom/BrowserChild.h" 27 #include "mozilla/dom/BrowsingContextBinding.h" 28 #include "mozilla/dom/CanonicalBrowsingContext.h" 29 #include "mozilla/dom/ContentChild.h" 30 #include "mozilla/dom/ContentFrameMessageManager.h" 31 #include "mozilla/dom/DocumentInlines.h" 32 #include "mozilla/dom/DocumentPictureInPicture.h" 33 #include "mozilla/dom/EventTarget.h" 34 #include "mozilla/dom/HTMLIFrameElement.h" 35 #include "mozilla/dom/LSObject.h" 36 #include "mozilla/dom/LocalStorage.h" 37 #include "mozilla/dom/MaybeCrossOriginObject.h" 38 #include "mozilla/dom/Performance.h" 39 #include "mozilla/dom/ProxyHandlerUtils.h" 40 #include "mozilla/dom/Storage.h" 41 #include "mozilla/dom/StorageEvent.h" 42 #include "mozilla/dom/StorageEventBinding.h" 43 #include "mozilla/dom/StorageNotifierService.h" 44 #include "mozilla/dom/StorageUtils.h" 45 #include "mozilla/dom/Timeout.h" 46 #include "mozilla/dom/TimeoutHandler.h" 47 #include "mozilla/dom/TimeoutManager.h" 48 #include "mozilla/dom/UserActivation.h" 49 #include "mozilla/dom/WindowContext.h" 50 #include "mozilla/dom/WindowFeatures.h" // WindowFeatures 51 #include "mozilla/dom/WindowGlobalChild.h" 52 #include "mozilla/dom/WindowProxyHolder.h" 53 #include "mozilla/intl/LocaleService.h" 54 #include "nsArrayUtils.h" 55 #include "nsBaseCommandController.h" 56 #include "nsContentSecurityManager.h" 57 #include "nsDOMJSUtils.h" 58 #include "nsDOMNavigationTiming.h" 59 #include "nsDocShellLoadState.h" 60 #include "nsError.h" 61 #include "nsFrameSelection.h" 62 #include "nsGlobalWindowOuter.h" 63 #include "nsHistory.h" 64 #include "nsICookieService.h" 65 #include "nsIDOMStorageManager.h" 66 #include "nsIDocShellTreeOwner.h" 67 #include "nsIInterfaceRequestorUtils.h" 68 #include "nsILoadGroup.h" 69 #include "nsIPermissionManager.h" 70 #include "nsIScriptContext.h" 71 #include "nsISecureBrowserUI.h" 72 #include "nsISizeOfEventTarget.h" 73 #include "nsIWebProgressListener.h" 74 #include "nsNetUtil.h" 75 #include "nsPrintfCString.h" 76 #include "nsScreen.h" 77 #include "nsVariant.h" 78 #include "nsWindowMemoryReporter.h" 79 #include "nsWindowSizes.h" 80 #include "nsWindowWatcher.h" 81 82 // Helper Classes 83 #include "js/CallAndConstruct.h" // JS::Call 84 #include "js/PropertyAndElement.h" // JS_DefineObject, JS_GetProperty 85 #include "js/PropertySpec.h" 86 #include "js/RealmIterators.h" 87 #include "js/Wrapper.h" 88 #include "js/friend/StackLimits.h" // js::AutoCheckRecursionLimit 89 #include "js/friend/WindowProxy.h" // js::IsWindowProxy, js::SetWindowProxy 90 #include "jsapi.h" 91 #include "jsfriendapi.h" 92 #include "mozilla/Likely.h" 93 #include "mozilla/Preferences.h" 94 #include "mozilla/SchedulerGroup.h" 95 #include "mozilla/SpinEventLoopUntil.h" 96 #include "mozilla/Sprintf.h" 97 #include "mozilla/dom/ScriptSettings.h" 98 #include "nsJSEnvironment.h" 99 #include "nsJSUtils.h" 100 #include "nsLayoutUtils.h" 101 #include "nsReadableUtils.h" 102 103 // Other Classes 104 #include "AudioChannelService.h" 105 #include "PostMessageEvent.h" 106 #include "mozilla/Attributes.h" 107 #include "mozilla/Components.h" 108 #include "mozilla/Debug.h" 109 #include "mozilla/EventListenerManager.h" 110 #include "mozilla/MouseEvents.h" 111 #include "mozilla/PresShell.h" 112 #include "mozilla/ProcessHangMonitor.h" 113 #include "mozilla/StaticPrefs_dom.h" 114 #include "mozilla/StaticPrefs_fission.h" 115 #include "mozilla/StaticPrefs_full_screen_api.h" 116 #include "mozilla/StaticPrefs_print.h" 117 #include "mozilla/ThrottledEventQueue.h" 118 #include "mozilla/dom/BarProps.h" 119 #include "mozilla/dom/DocGroup.h" 120 #include "mozilla/dom/ToJSValue.h" 121 #include "mozilla/dom/WorkerCommon.h" 122 #include "mozilla/net/CookieJarSettings.h" 123 #include "nsAboutProtocolUtils.h" 124 #include "nsCCUncollectableMarker.h" 125 #include "nsCharTraits.h" // NS_IS_HIGH/LOW_SURROGATE 126 #include "nsJSPrincipals.h" 127 #include "nsLayoutStatics.h" 128 129 // Interfaces Needed 130 #include "Crypto.h" 131 #include "mozilla/EventDispatcher.h" 132 #include "mozilla/EventStateManager.h" 133 #include "mozilla/ScrollContainerFrame.h" 134 #include "mozilla/dom/CustomEvent.h" 135 #include "mozilla/dom/Document.h" 136 #include "nsCSSProps.h" 137 #include "nsCanvasFrame.h" 138 #include "nsComputedDOMStyle.h" 139 #include "nsContentUtils.h" 140 #include "nsDOMCID.h" 141 #include "nsDOMString.h" 142 #include "nsDOMWindowUtils.h" 143 #include "nsFocusManager.h" 144 #include "nsGlobalWindowCommands.h" 145 #include "nsIAppWindow.h" 146 #include "nsIBaseWindow.h" 147 #include "nsIClassifiedChannel.h" 148 #include "nsIContent.h" 149 #include "nsIControllers.h" 150 #include "nsIDeviceSensors.h" 151 #include "nsIDocShell.h" 152 #include "nsIDocumentViewer.h" 153 #include "nsIFrame.h" 154 #include "nsILoadContext.h" 155 #include "nsIObserverService.h" 156 #include "nsIPrompt.h" 157 #include "nsIPromptFactory.h" 158 #include "nsIPromptService.h" 159 #include "nsISHistory.h" 160 #include "nsIScreenManager.h" 161 #include "nsIScriptError.h" 162 #include "nsIURIFixup.h" 163 #include "nsIURIMutator.h" 164 #include "nsIWebBrowserChrome.h" 165 #include "nsIWebBrowserFind.h" // For window.find() 166 #include "nsIWebNavigation.h" 167 #include "nsIWidget.h" 168 #include "nsIWidgetListener.h" 169 #include "nsIWindowWatcher.h" 170 #include "nsIWritablePropertyBag2.h" 171 #include "nsIXULRuntime.h" 172 #include "nsPIWindowWatcher.h" 173 #include "nsQueryObject.h" 174 #include "nsServiceManagerUtils.h" 175 #include "nsThreadUtils.h" 176 #include "xpcprivate.h" 177 178 #ifdef NS_PRINTING 179 # include "nsIPrintSettings.h" 180 # include "nsIPrintSettingsService.h" 181 # include "nsIWebBrowserPrint.h" 182 #endif 183 184 #include "AccessCheck.h" 185 #include "FxRWindowManager.h" 186 #include "VRShMem.h" 187 #include "gfxVR.h" 188 #include "mozilla/BasePrincipal.h" 189 #include "mozilla/DOMEventTargetHelper.h" 190 #include "mozilla/GlobalKeyListener.h" 191 #include "mozilla/Logging.h" 192 #include "mozilla/ProfilerMarkers.h" 193 #include "mozilla/Services.h" 194 #include "mozilla/dom/AudioContext.h" 195 #include "mozilla/dom/BrowsingContextGroup.h" 196 #include "mozilla/dom/Console.h" 197 #include "mozilla/dom/Element.h" 198 #include "mozilla/dom/Fetch.h" 199 #include "mozilla/dom/FunctionBinding.h" 200 #include "mozilla/dom/Gamepad.h" 201 #include "mozilla/dom/GamepadManager.h" 202 #include "mozilla/dom/HashChangeEvent.h" 203 #include "mozilla/dom/IDBFactory.h" 204 #include "mozilla/dom/ImageBitmap.h" 205 #include "mozilla/dom/ImageBitmapBinding.h" 206 #include "mozilla/dom/IntlUtils.h" 207 #include "mozilla/dom/Location.h" 208 #include "mozilla/dom/MediaQueryList.h" 209 #include "mozilla/dom/MessageChannel.h" 210 #include "mozilla/dom/NavigatorBinding.h" 211 #include "mozilla/dom/PopStateEvent.h" 212 #include "mozilla/dom/PopupBlockedEvent.h" 213 #include "mozilla/dom/PrimitiveConversions.h" 214 #include "mozilla/dom/Promise.h" 215 #include "mozilla/dom/RedirectBlockedEvent.h" 216 #include "mozilla/dom/Selection.h" 217 #include "mozilla/dom/ServiceWorkerRegistration.h" 218 #include "mozilla/dom/VRDisplay.h" 219 #include "mozilla/dom/VRDisplayEvent.h" 220 #include "mozilla/dom/VRDisplayEventBinding.h" 221 #include "mozilla/dom/VREventObserver.h" 222 #include "mozilla/dom/WebIDLGlobalNameHash.h" 223 #include "mozilla/dom/WindowBinding.h" 224 #include "mozilla/dom/Worklet.h" 225 #include "mozilla/dom/cache/CacheStorage.h" 226 #include "mozilla/extensions/WebExtensionPolicy.h" 227 #include "mozilla/glean/DomMetrics.h" 228 #include "nsFrameLoader.h" 229 #include "nsFrameLoaderOwner.h" 230 #include "nsHTMLDocument.h" 231 #include "nsIArray.h" 232 #include "nsIBrowserChild.h" 233 #include "nsIDOMXULCommandDispatcher.h" 234 #include "nsIDragService.h" 235 #include "nsNetCID.h" 236 #include "nsRefreshDriver.h" 237 #include "nsSandboxFlags.h" 238 #include "nsWindowRoot.h" 239 #include "nsWrapperCacheInlines.h" 240 #include "nsXPCOMCID.h" 241 #include "nsXULControllers.h" 242 #include "prenv.h" 243 #include "prrng.h" 244 245 #ifdef MOZ_WEBSPEECH 246 # include "mozilla/dom/SpeechSynthesis.h" 247 #endif 248 249 #ifdef ANDROID 250 # include <android/log.h> 251 #endif 252 253 #ifdef XP_WIN 254 # include <process.h> 255 # define getpid _getpid 256 #else 257 # include <unistd.h> // for getpid() 258 #endif 259 260 using namespace mozilla; 261 using namespace mozilla::dom; 262 using namespace mozilla::dom::ipc; 263 using mozilla::BasePrincipal; 264 using mozilla::OriginAttributes; 265 using mozilla::TimeStamp; 266 using mozilla::layout::RemotePrintJobChild; 267 268 static inline nsGlobalWindowInner* GetCurrentInnerWindowInternal( 269 const nsGlobalWindowOuter* aOuter) { 270 return nsGlobalWindowInner::Cast(aOuter->GetCurrentInnerWindow()); 271 } 272 273 #define FORWARD_TO_INNER(method, args, err_rval) \ 274 PR_BEGIN_MACRO \ 275 if (!mInnerWindow) { \ 276 NS_WARNING("No inner window available!"); \ 277 return err_rval; \ 278 } \ 279 return GetCurrentInnerWindowInternal(this)->method args; \ 280 PR_END_MACRO 281 282 #define FORWARD_TO_INNER_WITH_STRONG_REF(method, args, err_rval) \ 283 PR_BEGIN_MACRO \ 284 if (!mInnerWindow) { \ 285 NS_WARNING("No inner window available!"); \ 286 return err_rval; \ 287 } \ 288 RefPtr<nsGlobalWindowInner> inner = GetCurrentInnerWindowInternal(this); \ 289 return inner->method args; \ 290 PR_END_MACRO 291 292 #define FORWARD_TO_INNER_VOID(method, args) \ 293 PR_BEGIN_MACRO \ 294 if (!mInnerWindow) { \ 295 NS_WARNING("No inner window available!"); \ 296 return; \ 297 } \ 298 GetCurrentInnerWindowInternal(this)->method args; \ 299 return; \ 300 PR_END_MACRO 301 302 // Same as FORWARD_TO_INNER, but this will create a fresh inner if an 303 // inner doesn't already exists. 304 #define FORWARD_TO_INNER_CREATE(method, args, err_rval) \ 305 PR_BEGIN_MACRO \ 306 if (!mInnerWindow) { \ 307 if (mIsClosed) { \ 308 return err_rval; \ 309 } \ 310 nsCOMPtr<Document> kungFuDeathGrip = GetDoc(); \ 311 (void)kungFuDeathGrip; \ 312 if (!mInnerWindow) { \ 313 return err_rval; \ 314 } \ 315 } \ 316 return GetCurrentInnerWindowInternal(this)->method args; \ 317 PR_END_MACRO 318 319 static LazyLogModule gDOMLeakPRLogOuter("DOMLeakOuter"); 320 extern LazyLogModule gPageCacheLog; 321 322 #ifdef DEBUG 323 static LazyLogModule gDocShellAndDOMWindowLeakLogging( 324 "DocShellAndDOMWindowLeak"); 325 #endif 326 327 nsGlobalWindowOuter::OuterWindowByIdTable* 328 nsGlobalWindowOuter::sOuterWindowsById = nullptr; 329 330 /* static */ 331 nsPIDOMWindowOuter* nsPIDOMWindowOuter::GetFromCurrentInner( 332 nsPIDOMWindowInner* aInner) { 333 if (!aInner) { 334 return nullptr; 335 } 336 337 nsPIDOMWindowOuter* outer = aInner->GetOuterWindow(); 338 if (!outer || outer->GetCurrentInnerWindow() != aInner) { 339 return nullptr; 340 } 341 342 return outer; 343 } 344 345 //***************************************************************************** 346 // nsOuterWindowProxy: Outer Window Proxy 347 //***************************************************************************** 348 349 // Give OuterWindowProxyClass 2 reserved slots, like the other wrappers, so 350 // JSObject::swap can swap it with CrossCompartmentWrappers without requiring 351 // malloc. 352 // 353 // We store the nsGlobalWindowOuter* in our first slot. 354 // 355 // We store our holder weakmap in the second slot. 356 const JSClass OuterWindowProxyClass = PROXY_CLASS_DEF( 357 "Proxy", JSCLASS_HAS_RESERVED_SLOTS(2)); /* additional class flags */ 358 359 static const size_t OUTER_WINDOW_SLOT = 0; 360 static const size_t HOLDER_WEAKMAP_SLOT = 1; 361 362 class nsOuterWindowProxy : public MaybeCrossOriginObject<js::Wrapper> { 363 using Base = MaybeCrossOriginObject<js::Wrapper>; 364 365 public: 366 constexpr nsOuterWindowProxy() : Base(0) {} 367 368 bool finalizeInBackground(const JS::Value& priv) const override { 369 return false; 370 } 371 372 // Standard internal methods 373 /** 374 * Implementation of [[GetOwnProperty]] as defined at 375 * https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-getownproperty 376 * 377 * "proxy" is the WindowProxy object involved. It may not be same-compartment 378 * with cx. 379 */ 380 bool getOwnPropertyDescriptor( 381 JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, 382 JS::MutableHandle<Maybe<JS::PropertyDescriptor>> desc) const override; 383 384 /* 385 * Implementation of the same-origin case of 386 * <https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-getownproperty>. 387 */ 388 bool definePropertySameOrigin(JSContext* cx, JS::Handle<JSObject*> proxy, 389 JS::Handle<jsid> id, 390 JS::Handle<JS::PropertyDescriptor> desc, 391 JS::ObjectOpResult& result) const override; 392 393 /** 394 * Implementation of [[OwnPropertyKeys]] as defined at 395 * 396 * https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-ownpropertykeys 397 * 398 * "proxy" is the WindowProxy object involved. It may not be same-compartment 399 * with cx. 400 */ 401 bool ownPropertyKeys(JSContext* cx, JS::Handle<JSObject*> proxy, 402 JS::MutableHandleVector<jsid> props) const override; 403 /** 404 * Implementation of [[Delete]] as defined at 405 * https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-delete 406 * 407 * "proxy" is the WindowProxy object involved. It may not be same-compartment 408 * with cx. 409 */ 410 bool delete_(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, 411 JS::ObjectOpResult& result) const override; 412 413 /** 414 * Implementaton of hook for superclass getPrototype() method. 415 */ 416 JSObject* getSameOriginPrototype(JSContext* cx) const override; 417 418 /** 419 * Implementation of [[HasProperty]] internal method as defined at 420 * https://tc39.github.io/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-hasproperty-p 421 * 422 * "proxy" is the WindowProxy object involved. It may not be same-compartment 423 * with cx. 424 * 425 * Note that the HTML spec does not define an override for this internal 426 * method, so we just want the "normal object" behavior. We have to override 427 * it, because js::Wrapper also overrides, with "not normal" behavior. 428 */ 429 bool has(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, 430 bool* bp) const override; 431 432 /** 433 * Implementation of [[Get]] internal method as defined at 434 * <https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-get>. 435 * 436 * "proxy" is the WindowProxy object involved. It may or may not be 437 * same-compartment with "cx". 438 * 439 * "receiver" is the receiver ("this") for the get. It will be 440 * same-compartment with "cx". 441 * 442 * "vp" is the return value. It will be same-compartment with "cx". 443 */ 444 bool get(JSContext* cx, JS::Handle<JSObject*> proxy, 445 JS::Handle<JS::Value> receiver, JS::Handle<jsid> id, 446 JS::MutableHandle<JS::Value> vp) const override; 447 448 /** 449 * Implementation of [[Set]] internal method as defined at 450 * <https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-set>. 451 * 452 * "proxy" is the WindowProxy object involved. It may or may not be 453 * same-compartment with "cx". 454 * 455 * "v" is the value being set. It will be same-compartment with "cx". 456 * 457 * "receiver" is the receiver ("this") for the set. It will be 458 * same-compartment with "cx". 459 */ 460 bool set(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, 461 JS::Handle<JS::Value> v, JS::Handle<JS::Value> receiver, 462 JS::ObjectOpResult& result) const override; 463 464 // SpiderMonkey extensions 465 /** 466 * Implementation of SpiderMonkey extension which just checks whether this 467 * object has the property. Basically Object.getOwnPropertyDescriptor(obj, 468 * prop) !== undefined. but does not require reifying the descriptor. 469 * 470 * We have to override this because js::Wrapper overrides it, but we want 471 * different behavior from js::Wrapper. 472 * 473 * "proxy" is the WindowProxy object involved. It may not be same-compartment 474 * with cx. 475 */ 476 bool hasOwn(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, 477 bool* bp) const override; 478 479 /** 480 * Implementation of SpiderMonkey extension which is used as a fast path for 481 * enumerating. 482 * 483 * We have to override this because js::Wrapper overrides it, but we want 484 * different behavior from js::Wrapper. 485 * 486 * "proxy" is the WindowProxy object involved. It may not be same-compartment 487 * with cx. 488 */ 489 bool getOwnEnumerablePropertyKeys( 490 JSContext* cx, JS::Handle<JSObject*> proxy, 491 JS::MutableHandleVector<jsid> props) const override; 492 493 /** 494 * Hook used by SpiderMonkey to implement Object.prototype.toString. 495 */ 496 const char* className(JSContext* cx, 497 JS::Handle<JSObject*> wrapper) const override; 498 499 void finalize(JS::GCContext* gcx, JSObject* proxy) const override; 500 size_t objectMoved(JSObject* proxy, JSObject* old) const override; 501 502 bool isCallable(JSObject* obj) const override { return false; } 503 bool isConstructor(JSObject* obj) const override { return false; } 504 505 static const nsOuterWindowProxy singleton; 506 507 static nsGlobalWindowOuter* GetOuterWindow(JSObject* proxy) { 508 nsGlobalWindowOuter* outerWindow = 509 nsGlobalWindowOuter::FromSupports(static_cast<nsISupports*>( 510 js::GetProxyReservedSlot(proxy, OUTER_WINDOW_SLOT).toPrivate())); 511 return outerWindow; 512 } 513 514 protected: 515 // False return value means we threw an exception. True return value 516 // but false "found" means we didn't have a subframe at that index. 517 bool GetSubframeWindow(JSContext* cx, JS::Handle<JSObject*> proxy, 518 JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp, 519 bool& found) const; 520 521 // Returns a non-null window only if id is an index and we have a 522 // window at that index. 523 Nullable<WindowProxyHolder> GetSubframeWindow(JSContext* cx, 524 JS::Handle<JSObject*> proxy, 525 JS::Handle<jsid> id) const; 526 527 bool AppendIndexedPropertyNames(JSObject* proxy, 528 JS::MutableHandleVector<jsid> props) const; 529 530 using MaybeCrossOriginObjectMixins::EnsureHolder; 531 bool EnsureHolder(JSContext* cx, JS::Handle<JSObject*> proxy, 532 JS::MutableHandle<JSObject*> holder) const override; 533 534 // Helper method for creating a special "print" method that allows printing 535 // our PDF-viewer documents even if you're not same-origin with them. 536 // 537 // aProxy must be our nsOuterWindowProxy. It will not be same-compartment 538 // with aCx, since we only use this on the different-origin codepath! 539 // 540 // Can return true without filling in aDesc, which corresponds to not exposing 541 // a "print" method. 542 static bool MaybeGetPDFJSPrintMethod( 543 JSContext* cx, JS::Handle<JSObject*> proxy, 544 JS::MutableHandle<Maybe<JS::PropertyDescriptor>> desc); 545 546 // The actual "print" method we use for the PDFJS case. 547 static bool PDFJSPrintMethod(JSContext* cx, unsigned argc, JS::Value* vp); 548 549 // Helper method to get the pre-PDF-viewer-messing-with-it principal from an 550 // inner window. Will return null if this is not a PDF-viewer inner or if the 551 // principal could not be found for some reason. 552 static already_AddRefed<nsIPrincipal> GetNoPDFJSPrincipal( 553 nsGlobalWindowInner* inner); 554 }; 555 556 const char* nsOuterWindowProxy::className(JSContext* cx, 557 JS::Handle<JSObject*> proxy) const { 558 MOZ_ASSERT(js::IsProxy(proxy)); 559 560 if (!IsPlatformObjectSameOrigin(cx, proxy)) { 561 return "Object"; 562 } 563 564 return "Window"; 565 } 566 567 void nsOuterWindowProxy::finalize(JS::GCContext* gcx, JSObject* proxy) const { 568 nsGlobalWindowOuter* outerWindow = GetOuterWindow(proxy); 569 if (outerWindow) { 570 outerWindow->ClearWrapper(proxy); 571 BrowsingContext* bc = outerWindow->GetBrowsingContext(); 572 if (bc) { 573 bc->ClearWindowProxy(); 574 } 575 576 // Ideally we would use OnFinalize here, but it's possible that 577 // EnsureScriptEnvironment will later be called on the window, and we don't 578 // want to create a new script object in that case. Therefore, we need to 579 // write a non-null value that will reliably crash when dereferenced. 580 outerWindow->PoisonOuterWindowProxy(proxy); 581 } 582 } 583 584 bool nsOuterWindowProxy::getOwnPropertyDescriptor( 585 JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, 586 JS::MutableHandle<Maybe<JS::PropertyDescriptor>> desc) const { 587 // First check for indexed access. This is 588 // https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-getownproperty 589 // step 2, mostly. 590 JS::Rooted<JS::Value> subframe(cx); 591 bool found; 592 if (!GetSubframeWindow(cx, proxy, id, &subframe, found)) { 593 return false; 594 } 595 if (found) { 596 // Step 2.4. 597 598 desc.set(Some(JS::PropertyDescriptor::Data( 599 subframe, { 600 JS::PropertyAttribute::Configurable, 601 JS::PropertyAttribute::Enumerable, 602 }))); 603 return true; 604 } 605 606 bool isSameOrigin = IsPlatformObjectSameOrigin(cx, proxy); 607 608 // If we did not find a subframe, we could still have an indexed property 609 // access. In that case we should throw a SecurityError in the cross-origin 610 // case. 611 if (!isSameOrigin && IsArrayIndex(GetArrayIndexFromId(id))) { 612 // Step 2.5.2. 613 return ReportCrossOriginDenial(cx, id, "access"_ns); 614 } 615 616 // Step 2.5.1 is handled via the forwarding to js::Wrapper; it saves us an 617 // IsArrayIndex(GetArrayIndexFromId(id)) here. We'll never have a property on 618 // the Window whose name is an index, because our defineProperty doesn't pass 619 // those on to the Window. 620 621 // Step 3. 622 if (isSameOrigin) { 623 if (StaticPrefs::dom_missing_prop_counters_enabled() && id.isAtom()) { 624 Window_Binding::CountMaybeMissingProperty(proxy, id); 625 } 626 627 // Fall through to js::Wrapper. 628 { // Scope for JSAutoRealm while we are dealing with js::Wrapper. 629 // When forwarding to js::Wrapper, we should just enter the Realm of proxy 630 // for now. That's what js::Wrapper expects, and since we're same-origin 631 // anyway this is not changing any security behavior. 632 JSAutoRealm ar(cx, proxy); 633 JS_MarkCrossZoneId(cx, id); 634 bool ok = js::Wrapper::getOwnPropertyDescriptor(cx, proxy, id, desc); 635 if (!ok) { 636 return false; 637 } 638 639 #if 0 640 // See https://github.com/tc39/ecma262/issues/672 for more information. 641 if (desc.isSome() && 642 !IsNonConfigurableReadonlyPrimitiveGlobalProp(cx, id)) { 643 (*desc).setConfigurable(true); 644 } 645 #endif 646 } 647 648 // Now wrap our descriptor back into the Realm that asked for it. 649 return JS_WrapPropertyDescriptor(cx, desc); 650 } 651 652 // Step 4. 653 if (!CrossOriginGetOwnPropertyHelper(cx, proxy, id, desc)) { 654 return false; 655 } 656 657 // Step 5 658 if (desc.isSome()) { 659 return true; 660 } 661 662 // Non-spec step for the PDF viewer's window.print(). This comes before we 663 // check for named subframes, because in the same-origin case print() would 664 // shadow those. 665 if (id == GetJSIDByIndex(cx, XPCJSContext::IDX_PRINT)) { 666 if (!MaybeGetPDFJSPrintMethod(cx, proxy, desc)) { 667 return false; 668 } 669 670 if (desc.isSome()) { 671 return true; 672 } 673 } 674 675 // Step 6 -- check for named subframes. 676 if (id.isString()) { 677 nsAutoJSString name; 678 if (!name.init(cx, id.toString())) { 679 return false; 680 } 681 nsGlobalWindowOuter* win = GetOuterWindow(proxy); 682 if (RefPtr<BrowsingContext> childDOMWin = win->GetChildWindow(name)) { 683 JS::Rooted<JS::Value> childValue(cx); 684 if (!ToJSValue(cx, WindowProxyHolder(childDOMWin), &childValue)) { 685 return false; 686 } 687 desc.set(Some(JS::PropertyDescriptor::Data( 688 childValue, {JS::PropertyAttribute::Configurable}))); 689 return true; 690 } 691 } 692 693 // And step 7. 694 return CrossOriginPropertyFallback(cx, proxy, id, desc); 695 } 696 697 bool nsOuterWindowProxy::definePropertySameOrigin( 698 JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, 699 JS::Handle<JS::PropertyDescriptor> desc, JS::ObjectOpResult& result) const { 700 if (IsArrayIndex(GetArrayIndexFromId(id))) { 701 // Spec says to Reject whether this is a supported index or not, 702 // since we have no indexed setter or indexed creator. It is up 703 // to the caller to decide whether to throw a TypeError. 704 return result.failCantDefineWindowElement(); 705 } 706 707 JS::ObjectOpResult ourResult; 708 bool ok = js::Wrapper::defineProperty(cx, proxy, id, desc, ourResult); 709 if (!ok) { 710 return false; 711 } 712 713 if (!ourResult.ok()) { 714 // It's possible that this failed because the page got the existing 715 // descriptor (which we force to claim to be configurable) and then tried to 716 // redefine the property with the descriptor it got but a different value. 717 // We want to allow this case to succeed, so check for it and if we're in 718 // that case try again but now with an attempt to define a non-configurable 719 // property. 720 if (!desc.hasConfigurable() || !desc.configurable()) { 721 // The incoming descriptor was not explicitly marked "configurable: true", 722 // so it failed for some other reason. Just propagate that reason out. 723 result = ourResult; 724 return true; 725 } 726 727 JS::Rooted<Maybe<JS::PropertyDescriptor>> existingDesc(cx); 728 ok = js::Wrapper::getOwnPropertyDescriptor(cx, proxy, id, &existingDesc); 729 if (!ok) { 730 return false; 731 } 732 if (existingDesc.isNothing() || existingDesc->configurable()) { 733 // We have no existing property, or its descriptor is already configurable 734 // (on the Window itself, where things really can be non-configurable). 735 // So we failed for some other reason, which we should propagate out. 736 result = ourResult; 737 return true; 738 } 739 740 JS::Rooted<JS::PropertyDescriptor> updatedDesc(cx, desc); 741 updatedDesc.setConfigurable(false); 742 743 JS::ObjectOpResult ourNewResult; 744 ok = js::Wrapper::defineProperty(cx, proxy, id, updatedDesc, ourNewResult); 745 if (!ok) { 746 return false; 747 } 748 749 if (!ourNewResult.ok()) { 750 // Twiddling the configurable flag didn't help. Just return this failure 751 // out to the caller. 752 result = ourNewResult; 753 return true; 754 } 755 } 756 757 #if 0 758 // See https://github.com/tc39/ecma262/issues/672 for more information. 759 if (desc.hasConfigurable() && !desc.configurable() && 760 !IsNonConfigurableReadonlyPrimitiveGlobalProp(cx, id)) { 761 // Give callers a way to detect that they failed to "really" define a 762 // non-configurable property. 763 result.failCantDefineWindowNonConfigurable(); 764 return true; 765 } 766 #endif 767 768 result.succeed(); 769 return true; 770 } 771 772 bool nsOuterWindowProxy::ownPropertyKeys( 773 JSContext* cx, JS::Handle<JSObject*> proxy, 774 JS::MutableHandleVector<jsid> props) const { 775 // Just our indexed stuff followed by our "normal" own property names. 776 if (!AppendIndexedPropertyNames(proxy, props)) { 777 return false; 778 } 779 780 if (IsPlatformObjectSameOrigin(cx, proxy)) { 781 // When forwarding to js::Wrapper, we should just enter the Realm of proxy 782 // for now. That's what js::Wrapper expects, and since we're same-origin 783 // anyway this is not changing any security behavior. 784 JS::RootedVector<jsid> innerProps(cx); 785 { // Scope for JSAutoRealm so we can mark the ids once we exit it 786 JSAutoRealm ar(cx, proxy); 787 if (!js::Wrapper::ownPropertyKeys(cx, proxy, &innerProps)) { 788 return false; 789 } 790 } 791 for (auto& id : innerProps) { 792 JS_MarkCrossZoneId(cx, id); 793 } 794 return js::AppendUnique(cx, props, innerProps); 795 } 796 797 // In the cross-origin case we purposefully exclude subframe names from the 798 // list of property names we report here. 799 JS::Rooted<JSObject*> holder(cx); 800 if (!EnsureHolder(cx, proxy, &holder)) { 801 return false; 802 } 803 804 JS::RootedVector<jsid> crossOriginProps(cx); 805 if (!js::GetPropertyKeys(cx, holder, 806 JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, 807 &crossOriginProps) || 808 !js::AppendUnique(cx, props, crossOriginProps)) { 809 return false; 810 } 811 812 // Add the "print" property if needed. 813 nsGlobalWindowOuter* outer = GetOuterWindow(proxy); 814 nsGlobalWindowInner* inner = 815 nsGlobalWindowInner::Cast(outer->GetCurrentInnerWindow()); 816 if (inner) { 817 nsCOMPtr<nsIPrincipal> targetPrincipal = GetNoPDFJSPrincipal(inner); 818 if (targetPrincipal && 819 nsContentUtils::SubjectPrincipal(cx)->Equals(targetPrincipal)) { 820 JS::RootedVector<jsid> printProp(cx); 821 if (!printProp.append(GetJSIDByIndex(cx, XPCJSContext::IDX_PRINT)) || 822 !js::AppendUnique(cx, props, printProp)) { 823 return false; 824 } 825 } 826 } 827 828 return xpc::AppendCrossOriginWhitelistedPropNames(cx, props); 829 } 830 831 bool nsOuterWindowProxy::delete_(JSContext* cx, JS::Handle<JSObject*> proxy, 832 JS::Handle<jsid> id, 833 JS::ObjectOpResult& result) const { 834 if (!IsPlatformObjectSameOrigin(cx, proxy)) { 835 return ReportCrossOriginDenial(cx, id, "delete"_ns); 836 } 837 838 if (!GetSubframeWindow(cx, proxy, id).IsNull()) { 839 // Fail (which means throw if strict, else return false). 840 return result.failCantDeleteWindowElement(); 841 } 842 843 if (IsArrayIndex(GetArrayIndexFromId(id))) { 844 // Indexed, but not supported. Spec says return true. 845 return result.succeed(); 846 } 847 848 // We're same-origin, so it should be safe to enter the Realm of "proxy". 849 // Let's do that, just in case, to avoid cross-compartment issues in our 850 // js::Wrapper caller.. 851 JSAutoRealm ar(cx, proxy); 852 JS_MarkCrossZoneId(cx, id); 853 return js::Wrapper::delete_(cx, proxy, id, result); 854 } 855 856 JSObject* nsOuterWindowProxy::getSameOriginPrototype(JSContext* cx) const { 857 return Window_Binding::GetProtoObjectHandle(cx); 858 } 859 860 bool nsOuterWindowProxy::has(JSContext* cx, JS::Handle<JSObject*> proxy, 861 JS::Handle<jsid> id, bool* bp) const { 862 // We could just directly forward this method to js::BaseProxyHandler, but 863 // that involves reifying the actual property descriptor, which might be more 864 // work than we have to do for has() on the Window. 865 866 if (!IsPlatformObjectSameOrigin(cx, proxy)) { 867 // In the cross-origin case we only have own properties. Just call hasOwn 868 // directly. 869 return hasOwn(cx, proxy, id, bp); 870 } 871 872 if (!GetSubframeWindow(cx, proxy, id).IsNull()) { 873 *bp = true; 874 return true; 875 } 876 877 // Just to be safe in terms of compartment asserts, enter the Realm of 878 // "proxy". We're same-origin with it, so this should be safe. 879 JSAutoRealm ar(cx, proxy); 880 JS_MarkCrossZoneId(cx, id); 881 return js::Wrapper::has(cx, proxy, id, bp); 882 } 883 884 bool nsOuterWindowProxy::hasOwn(JSContext* cx, JS::Handle<JSObject*> proxy, 885 JS::Handle<jsid> id, bool* bp) const { 886 // We could just directly forward this method to js::BaseProxyHandler, but 887 // that involves reifying the actual property descriptor, which might be more 888 // work than we have to do for hasOwn() on the Window. 889 890 if (!IsPlatformObjectSameOrigin(cx, proxy)) { 891 // Avoiding reifying the property descriptor here would require duplicating 892 // a bunch of "is this property exposed cross-origin" logic, which is 893 // probably not worth it. Just forward this along to the base 894 // implementation. 895 // 896 // It's very important to not forward this to js::Wrapper, because that will 897 // not do the right security and cross-origin checks and will pass through 898 // the call to the Window. 899 // 900 // The BaseProxyHandler code is OK with this happening without entering the 901 // compartment of "proxy". 902 return js::BaseProxyHandler::hasOwn(cx, proxy, id, bp); 903 } 904 905 if (!GetSubframeWindow(cx, proxy, id).IsNull()) { 906 *bp = true; 907 return true; 908 } 909 910 // Just to be safe in terms of compartment asserts, enter the Realm of 911 // "proxy". We're same-origin with it, so this should be safe. 912 JSAutoRealm ar(cx, proxy); 913 JS_MarkCrossZoneId(cx, id); 914 return js::Wrapper::hasOwn(cx, proxy, id, bp); 915 } 916 917 bool nsOuterWindowProxy::get(JSContext* cx, JS::Handle<JSObject*> proxy, 918 JS::Handle<JS::Value> receiver, 919 JS::Handle<jsid> id, 920 JS::MutableHandle<JS::Value> vp) const { 921 if (id == GetJSIDByIndex(cx, XPCJSContext::IDX_WRAPPED_JSOBJECT) && 922 xpc::AccessCheck::isChrome(js::GetContextCompartment(cx))) { 923 vp.set(JS::ObjectValue(*proxy)); 924 return MaybeWrapValue(cx, vp); 925 } 926 927 if (!IsPlatformObjectSameOrigin(cx, proxy)) { 928 return CrossOriginGet(cx, proxy, receiver, id, vp); 929 } 930 931 bool found; 932 if (!GetSubframeWindow(cx, proxy, id, vp, found)) { 933 return false; 934 } 935 936 if (found) { 937 return true; 938 } 939 940 if (StaticPrefs::dom_missing_prop_counters_enabled() && id.isAtom()) { 941 Window_Binding::CountMaybeMissingProperty(proxy, id); 942 } 943 944 { // Scope for JSAutoRealm 945 // Enter "proxy"'s Realm. We're in the same-origin case, so this should be 946 // safe. 947 JSAutoRealm ar(cx, proxy); 948 949 JS_MarkCrossZoneId(cx, id); 950 951 JS::Rooted<JS::Value> wrappedReceiver(cx, receiver); 952 if (!MaybeWrapValue(cx, &wrappedReceiver)) { 953 return false; 954 } 955 956 // Fall through to js::Wrapper. 957 if (!js::Wrapper::get(cx, proxy, wrappedReceiver, id, vp)) { 958 return false; 959 } 960 } 961 962 // Make sure our return value is in the caller compartment. 963 return MaybeWrapValue(cx, vp); 964 } 965 966 bool nsOuterWindowProxy::set(JSContext* cx, JS::Handle<JSObject*> proxy, 967 JS::Handle<jsid> id, JS::Handle<JS::Value> v, 968 JS::Handle<JS::Value> receiver, 969 JS::ObjectOpResult& result) const { 970 if (!IsPlatformObjectSameOrigin(cx, proxy)) { 971 return CrossOriginSet(cx, proxy, id, v, receiver, result); 972 } 973 974 if (IsArrayIndex(GetArrayIndexFromId(id))) { 975 // Reject the set. It's up to the caller to decide whether to throw a 976 // TypeError. If the caller is strict mode JS code, it'll throw. 977 return result.failReadOnly(); 978 } 979 980 // Do the rest in the Realm of "proxy", since we're in the same-origin case. 981 JSAutoRealm ar(cx, proxy); 982 JS::Rooted<JS::Value> wrappedArg(cx, v); 983 if (!MaybeWrapValue(cx, &wrappedArg)) { 984 return false; 985 } 986 JS::Rooted<JS::Value> wrappedReceiver(cx, receiver); 987 if (!MaybeWrapValue(cx, &wrappedReceiver)) { 988 return false; 989 } 990 991 JS_MarkCrossZoneId(cx, id); 992 993 return js::Wrapper::set(cx, proxy, id, wrappedArg, wrappedReceiver, result); 994 } 995 996 bool nsOuterWindowProxy::getOwnEnumerablePropertyKeys( 997 JSContext* cx, JS::Handle<JSObject*> proxy, 998 JS::MutableHandleVector<jsid> props) const { 999 // We could just stop overring getOwnEnumerablePropertyKeys and let our 1000 // superclasses deal (by falling back on the BaseProxyHandler implementation 1001 // that uses a combination of ownPropertyKeys and getOwnPropertyDescriptor to 1002 // only return the enumerable ones. But maybe there's value in having 1003 // somewhat faster for-in iteration on Window objects... 1004 1005 // Like ownPropertyKeys, our indexed stuff followed by our "normal" enumerable 1006 // own property names. 1007 if (!AppendIndexedPropertyNames(proxy, props)) { 1008 return false; 1009 } 1010 1011 if (!IsPlatformObjectSameOrigin(cx, proxy)) { 1012 // All the cross-origin properties other than the indexed props are 1013 // non-enumerable, so we're done here. 1014 return true; 1015 } 1016 1017 // When forwarding to js::Wrapper, we should just enter the Realm of proxy 1018 // for now. That's what js::Wrapper expects, and since we're same-origin 1019 // anyway this is not changing any security behavior. 1020 JS::RootedVector<jsid> innerProps(cx); 1021 { // Scope for JSAutoRealm so we can mark the ids once we exit it. 1022 JSAutoRealm ar(cx, proxy); 1023 if (!js::Wrapper::getOwnEnumerablePropertyKeys(cx, proxy, &innerProps)) { 1024 return false; 1025 } 1026 } 1027 1028 for (auto& id : innerProps) { 1029 JS_MarkCrossZoneId(cx, id); 1030 } 1031 1032 return js::AppendUnique(cx, props, innerProps); 1033 } 1034 1035 bool nsOuterWindowProxy::GetSubframeWindow(JSContext* cx, 1036 JS::Handle<JSObject*> proxy, 1037 JS::Handle<jsid> id, 1038 JS::MutableHandle<JS::Value> vp, 1039 bool& found) const { 1040 Nullable<WindowProxyHolder> frame = GetSubframeWindow(cx, proxy, id); 1041 if (frame.IsNull()) { 1042 found = false; 1043 return true; 1044 } 1045 1046 found = true; 1047 return WrapObject(cx, frame.Value(), vp); 1048 } 1049 1050 Nullable<WindowProxyHolder> nsOuterWindowProxy::GetSubframeWindow( 1051 JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id) const { 1052 uint32_t index = GetArrayIndexFromId(id); 1053 if (!IsArrayIndex(index)) { 1054 return nullptr; 1055 } 1056 1057 nsGlobalWindowOuter* win = GetOuterWindow(proxy); 1058 return win->IndexedGetterOuter(index); 1059 } 1060 1061 bool nsOuterWindowProxy::AppendIndexedPropertyNames( 1062 JSObject* proxy, JS::MutableHandleVector<jsid> props) const { 1063 uint32_t length = GetOuterWindow(proxy)->Length(); 1064 MOZ_ASSERT(int32_t(length) >= 0); 1065 if (!props.reserve(props.length() + length)) { 1066 return false; 1067 } 1068 for (int32_t i = 0; i < int32_t(length); ++i) { 1069 if (!props.append(JS::PropertyKey::Int(i))) { 1070 return false; 1071 } 1072 } 1073 1074 return true; 1075 } 1076 1077 bool nsOuterWindowProxy::EnsureHolder( 1078 JSContext* cx, JS::Handle<JSObject*> proxy, 1079 JS::MutableHandle<JSObject*> holder) const { 1080 return EnsureHolder(cx, proxy, HOLDER_WEAKMAP_SLOT, 1081 Window_Binding::sCrossOriginProperties, holder); 1082 } 1083 1084 size_t nsOuterWindowProxy::objectMoved(JSObject* obj, JSObject* old) const { 1085 nsGlobalWindowOuter* outerWindow = GetOuterWindow(obj); 1086 if (outerWindow) { 1087 outerWindow->UpdateWrapper(obj, old); 1088 BrowsingContext* bc = outerWindow->GetBrowsingContext(); 1089 if (bc) { 1090 bc->UpdateWindowProxy(obj, old); 1091 } 1092 } 1093 return 0; 1094 } 1095 1096 enum { PDFJS_SLOT_CALLEE = 0 }; 1097 1098 // static 1099 bool nsOuterWindowProxy::MaybeGetPDFJSPrintMethod( 1100 JSContext* cx, JS::Handle<JSObject*> proxy, 1101 JS::MutableHandle<Maybe<JS::PropertyDescriptor>> desc) { 1102 MOZ_ASSERT(proxy); 1103 MOZ_ASSERT(!desc.isSome()); 1104 1105 nsGlobalWindowOuter* outer = GetOuterWindow(proxy); 1106 nsGlobalWindowInner* inner = 1107 nsGlobalWindowInner::Cast(outer->GetCurrentInnerWindow()); 1108 if (!inner) { 1109 // No print method to expose. 1110 return true; 1111 } 1112 1113 nsCOMPtr<nsIPrincipal> targetPrincipal = GetNoPDFJSPrincipal(inner); 1114 if (!targetPrincipal) { 1115 // Nothing special to be done. 1116 return true; 1117 } 1118 1119 if (!nsContentUtils::SubjectPrincipal(cx)->Equals(targetPrincipal)) { 1120 // Not our origin's PDF document. 1121 return true; 1122 } 1123 1124 // Get the function we plan to actually call. 1125 JS::Rooted<JSObject*> innerObj(cx, inner->GetGlobalJSObject()); 1126 if (!innerObj) { 1127 // Really should not happen, but ok, let's just return. 1128 return true; 1129 } 1130 1131 JS::Rooted<JS::Value> targetFunc(cx); 1132 { 1133 JSAutoRealm ar(cx, innerObj); 1134 if (!JS_GetProperty(cx, innerObj, "print", &targetFunc)) { 1135 return false; 1136 } 1137 } 1138 1139 if (!targetFunc.isObject()) { 1140 // Who knows what's going on. Just return. 1141 return true; 1142 } 1143 1144 // The Realm of cx is the realm our caller is in and the realm we 1145 // should create our function in. Note that we can't use the 1146 // standard XPConnect function forwarder machinery because our 1147 // "this" is cross-origin, so we have to do thus by hand. 1148 1149 // Make sure targetFunc is wrapped into the right compartment. 1150 if (!MaybeWrapValue(cx, &targetFunc)) { 1151 return false; 1152 } 1153 1154 JSFunction* fun = 1155 js::NewFunctionWithReserved(cx, PDFJSPrintMethod, 0, 0, "print"); 1156 if (!fun) { 1157 return false; 1158 } 1159 1160 JS::Rooted<JSObject*> funObj(cx, JS_GetFunctionObject(fun)); 1161 js::SetFunctionNativeReserved(funObj, PDFJS_SLOT_CALLEE, targetFunc); 1162 1163 // { value: <print>, writable: true, enumerable: true, configurable: true } 1164 // because that's what it would have been in the same-origin case without 1165 // the PDF viewer messing with things. 1166 desc.set(Some(JS::PropertyDescriptor::Data( 1167 JS::ObjectValue(*funObj), 1168 {JS::PropertyAttribute::Configurable, JS::PropertyAttribute::Enumerable, 1169 JS::PropertyAttribute::Writable}))); 1170 return true; 1171 } 1172 1173 // static 1174 bool nsOuterWindowProxy::PDFJSPrintMethod(JSContext* cx, unsigned argc, 1175 JS::Value* vp) { 1176 JS::CallArgs args = CallArgsFromVp(argc, vp); 1177 1178 JS::Rooted<JSObject*> realCallee( 1179 cx, &js::GetFunctionNativeReserved(&args.callee(), PDFJS_SLOT_CALLEE) 1180 .toObject()); 1181 // Unchecked unwrap, because we want to extract the thing we really had 1182 // before. 1183 realCallee = js::UncheckedUnwrap(realCallee); 1184 1185 JS::Rooted<JS::Value> thisv(cx, args.thisv()); 1186 if (thisv.isNullOrUndefined()) { 1187 // Replace it with the global of our stashed callee, simulating the 1188 // global-assuming behavior of DOM methods. 1189 JS::Rooted<JSObject*> global(cx, JS::GetNonCCWObjectGlobal(realCallee)); 1190 if (!MaybeWrapObject(cx, &global)) { 1191 return false; 1192 } 1193 thisv.setObject(*global); 1194 } else if (!thisv.isObject()) { 1195 return ThrowInvalidThis(cx, args, false, prototypes::id::Window); 1196 } 1197 1198 // We want to do an UncheckedUnwrap here, because we're going to directly 1199 // examine the principal of the inner window, if we have an inner window. 1200 JS::Rooted<JSObject*> unwrappedObj(cx, 1201 js::UncheckedUnwrap(&thisv.toObject())); 1202 nsGlobalWindowInner* inner = nullptr; 1203 { 1204 // Do the unwrap in the Realm of the object we're looking at. 1205 JSAutoRealm ar(cx, unwrappedObj); 1206 UNWRAP_MAYBE_CROSS_ORIGIN_OBJECT(Window, &unwrappedObj, inner, cx); 1207 } 1208 if (!inner) { 1209 return ThrowInvalidThis(cx, args, false, prototypes::id::Window); 1210 } 1211 1212 nsIPrincipal* callerPrincipal = nsContentUtils::SubjectPrincipal(cx); 1213 if (!callerPrincipal->SubsumesConsideringDomain(inner->GetPrincipal())) { 1214 // Check whether it's a PDF viewer from our origin. 1215 nsCOMPtr<nsIPrincipal> pdfPrincipal = GetNoPDFJSPrincipal(inner); 1216 if (!pdfPrincipal || !callerPrincipal->Equals(pdfPrincipal)) { 1217 // Security error. 1218 return ThrowInvalidThis(cx, args, true, prototypes::id::Window); 1219 } 1220 } 1221 1222 // Go ahead and enter the Realm of our real callee to call it. We'll pass it 1223 // our "thisv", just in case someone grabs a "print" method off one PDF 1224 // document and .call()s it on another one. 1225 { 1226 JSAutoRealm ar(cx, realCallee); 1227 if (!MaybeWrapValue(cx, &thisv)) { 1228 return false; 1229 } 1230 1231 // Don't bother passing through the args; they will get ignored anyway. 1232 1233 if (!JS::Call(cx, thisv, realCallee, JS::HandleValueArray::empty(), 1234 args.rval())) { 1235 return false; 1236 } 1237 } 1238 1239 // Wrap the return value (not that there should be any!) into the right 1240 // compartment. 1241 return MaybeWrapValue(cx, args.rval()); 1242 } 1243 1244 // static 1245 already_AddRefed<nsIPrincipal> nsOuterWindowProxy::GetNoPDFJSPrincipal( 1246 nsGlobalWindowInner* inner) { 1247 if (!nsContentUtils::IsPDFJS(inner->GetPrincipal())) { 1248 return nullptr; 1249 } 1250 1251 if (Document* doc = inner->GetExtantDoc()) { 1252 if (nsCOMPtr<nsIPropertyBag2> propBag = 1253 do_QueryInterface(doc->GetChannel())) { 1254 nsCOMPtr<nsIPrincipal> principal( 1255 do_GetProperty(propBag, u"noPDFJSPrincipal"_ns)); 1256 return principal.forget(); 1257 } 1258 } 1259 return nullptr; 1260 } 1261 1262 const nsOuterWindowProxy nsOuterWindowProxy::singleton; 1263 1264 class nsChromeOuterWindowProxy : public nsOuterWindowProxy { 1265 public: 1266 constexpr nsChromeOuterWindowProxy() = default; 1267 1268 const char* className(JSContext* cx, 1269 JS::Handle<JSObject*> wrapper) const override; 1270 1271 static const nsChromeOuterWindowProxy singleton; 1272 }; 1273 1274 const char* nsChromeOuterWindowProxy::className( 1275 JSContext* cx, JS::Handle<JSObject*> proxy) const { 1276 MOZ_ASSERT(js::IsProxy(proxy)); 1277 1278 return "ChromeWindow"; 1279 } 1280 1281 const nsChromeOuterWindowProxy nsChromeOuterWindowProxy::singleton; 1282 1283 static JSObject* NewOuterWindowProxy(JSContext* cx, 1284 JS::Handle<JSObject*> global, 1285 bool isChrome) { 1286 MOZ_ASSERT(JS_IsGlobalObject(global)); 1287 1288 JSAutoRealm ar(cx, global); 1289 1290 js::WrapperOptions options; 1291 options.setClass(&OuterWindowProxyClass); 1292 JSObject* obj = 1293 js::Wrapper::New(cx, global, 1294 isChrome ? &nsChromeOuterWindowProxy::singleton 1295 : &nsOuterWindowProxy::singleton, 1296 options); 1297 MOZ_ASSERT_IF(obj, js::IsWindowProxy(obj)); 1298 return obj; 1299 } 1300 1301 //***************************************************************************** 1302 //*** nsGlobalWindowOuter: Object Management 1303 //***************************************************************************** 1304 1305 nsGlobalWindowOuter::nsGlobalWindowOuter(uint64_t aWindowID) 1306 : nsPIDOMWindowOuter(aWindowID), 1307 mFullscreenHasChangedDuringProcessing(false), 1308 mForceFullScreenInWidget(false), 1309 mIsClosed(false), 1310 mInClose(false), 1311 mHavePendingClose(false), 1312 mBlockScriptedClosingFlag(false), 1313 mWasOffline(false), 1314 mCreatingInnerWindow(false), 1315 mIsChrome(false), 1316 mAllowScriptsToClose(false), 1317 mTopLevelOuterContentWindow(false), 1318 mDelayedPrintUntilAfterLoad(false), 1319 mDelayedCloseForPrinting(false), 1320 mShouldDelayPrintUntilAfterLoad(false), 1321 #ifdef DEBUG 1322 mSerial(0), 1323 mSetOpenerWindowCalled(false), 1324 #endif 1325 mCleanedUp(false), 1326 mCanSkipCCGeneration(0), 1327 mAutoActivateVRDisplayID(0) { 1328 AssertIsOnMainThread(); 1329 SetIsOnMainThread(); 1330 nsLayoutStatics::AddRef(); 1331 1332 // Initialize the PRCList (this). 1333 PR_INIT_CLIST(this); 1334 1335 // |this| is an outer window. Outer windows start out frozen and 1336 // remain frozen until they get an inner window. 1337 MOZ_ASSERT(IsFrozen()); 1338 1339 // We could have failed the first time through trying 1340 // to create the entropy collector, so we should 1341 // try to get one until we succeed. 1342 1343 #ifdef DEBUG 1344 mSerial = nsContentUtils::InnerOrOuterWindowCreated(); 1345 1346 MOZ_LOG(gDocShellAndDOMWindowLeakLogging, LogLevel::Info, 1347 ("++DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p]\n", 1348 nsContentUtils::GetCurrentInnerOrOuterWindowCount(), 1349 static_cast<void*>(ToCanonicalSupports(this)), getpid(), mSerial, 1350 nullptr)); 1351 #endif 1352 1353 MOZ_LOG(gDOMLeakPRLogOuter, LogLevel::Debug, 1354 ("DOMWINDOW %p created outer=nullptr", this)); 1355 1356 // Add ourselves to the outer windows list. 1357 MOZ_ASSERT(sOuterWindowsById, "Outer Windows hash table must be created!"); 1358 1359 // |this| is an outer window, add to the outer windows list. 1360 MOZ_ASSERT(!sOuterWindowsById->Contains(mWindowID), 1361 "This window shouldn't be in the hash table yet!"); 1362 // We seem to see crashes in release builds because of null 1363 // |sOuterWindowsById|. 1364 if (sOuterWindowsById) { 1365 sOuterWindowsById->InsertOrUpdate(mWindowID, this); 1366 } 1367 } 1368 1369 #ifdef DEBUG 1370 1371 /* static */ 1372 void nsGlobalWindowOuter::AssertIsOnMainThread() { 1373 MOZ_ASSERT(NS_IsMainThread()); 1374 } 1375 1376 #endif // DEBUG 1377 1378 /* static */ 1379 void nsGlobalWindowOuter::Init() { 1380 AssertIsOnMainThread(); 1381 1382 NS_ASSERTION(gDOMLeakPRLogOuter, 1383 "gDOMLeakPRLogOuter should have been initialized!"); 1384 1385 sOuterWindowsById = new OuterWindowByIdTable(); 1386 } 1387 1388 nsGlobalWindowOuter::~nsGlobalWindowOuter() { 1389 AssertIsOnMainThread(); 1390 1391 if (sOuterWindowsById) { 1392 sOuterWindowsById->Remove(mWindowID); 1393 } 1394 1395 nsContentUtils::InnerOrOuterWindowDestroyed(); 1396 1397 #ifdef DEBUG 1398 if (MOZ_LOG_TEST(gDocShellAndDOMWindowLeakLogging, LogLevel::Info)) { 1399 nsAutoCString url; 1400 if (mLastOpenedURI) { 1401 url = mLastOpenedURI->GetSpecOrDefault(); 1402 1403 // Data URLs can be very long, so truncate to avoid flooding the log. 1404 const uint32_t maxURLLength = 1000; 1405 if (url.Length() > maxURLLength) { 1406 url.Truncate(maxURLLength); 1407 } 1408 } 1409 1410 MOZ_LOG( 1411 gDocShellAndDOMWindowLeakLogging, LogLevel::Info, 1412 ("--DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p] [url = " 1413 "%s]\n", 1414 nsContentUtils::GetCurrentInnerOrOuterWindowCount(), 1415 static_cast<void*>(ToCanonicalSupports(this)), getpid(), mSerial, 1416 nullptr, url.get())); 1417 } 1418 #endif 1419 1420 MOZ_LOG(gDOMLeakPRLogOuter, LogLevel::Debug, 1421 ("DOMWINDOW %p destroyed", this)); 1422 1423 JSObject* proxy = GetWrapperMaybeDead(); 1424 if (proxy) { 1425 if (mBrowsingContext && mBrowsingContext->GetUnbarrieredWindowProxy()) { 1426 nsGlobalWindowOuter* outer = nsOuterWindowProxy::GetOuterWindow( 1427 mBrowsingContext->GetUnbarrieredWindowProxy()); 1428 // Check that the current WindowProxy object corresponds to this 1429 // nsGlobalWindowOuter, because we don't want to clear the WindowProxy if 1430 // we've replaced it with a cross-process WindowProxy. 1431 if (outer == this) { 1432 mBrowsingContext->ClearWindowProxy(); 1433 } 1434 } 1435 js::SetProxyReservedSlot(proxy, OUTER_WINDOW_SLOT, 1436 JS::PrivateValue(nullptr)); 1437 } 1438 1439 // An outer window is destroyed with inner windows still possibly 1440 // alive, iterate through the inner windows and null out their 1441 // back pointer to this outer, and pull them out of the list of 1442 // inner windows. 1443 // 1444 // Our linked list of inner windows both contains (an nsGlobalWindowOuter), 1445 // and our inner windows (nsGlobalWindowInners). This means that we need to 1446 // use PRCList*. We can then compare that PRCList* to `this` to see if its an 1447 // inner or outer window. 1448 PRCList* w; 1449 while ((w = PR_LIST_HEAD(this)) != this) { 1450 PR_REMOVE_AND_INIT_LINK(w); 1451 } 1452 1453 DropOuterWindowDocs(); 1454 1455 // Outer windows are always supposed to call CleanUp before letting themselves 1456 // be destroyed. 1457 MOZ_ASSERT(mCleanedUp); 1458 1459 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID); 1460 if (ac) ac->RemoveWindowAsListener(this); 1461 1462 nsLayoutStatics::Release(); 1463 } 1464 1465 // static 1466 void nsGlobalWindowOuter::ShutDown() { 1467 AssertIsOnMainThread(); 1468 1469 delete sOuterWindowsById; 1470 sOuterWindowsById = nullptr; 1471 } 1472 1473 void nsGlobalWindowOuter::DropOuterWindowDocs() { 1474 MOZ_ASSERT_IF(mDoc, !mDoc->EventHandlingSuppressed()); 1475 mDoc = nullptr; 1476 mSuspendedDocs.Clear(); 1477 } 1478 1479 void nsGlobalWindowOuter::CleanUp() { 1480 // Guarantee idempotence. 1481 if (mCleanedUp) return; 1482 mCleanedUp = true; 1483 1484 StartDying(); 1485 1486 mWindowUtils = nullptr; 1487 1488 ClearControllers(); 1489 1490 mContext = nullptr; // Forces Release 1491 mChromeEventHandler = nullptr; // Forces Release 1492 mParentTarget = nullptr; 1493 mMessageManager = nullptr; 1494 1495 mArguments = nullptr; 1496 } 1497 1498 void nsGlobalWindowOuter::ClearControllers() { 1499 if (mControllers) { 1500 uint32_t count; 1501 mControllers->GetControllerCount(&count); 1502 1503 while (count--) { 1504 nsCOMPtr<nsIController> controller; 1505 mControllers->GetControllerAt(count, getter_AddRefs(controller)); 1506 if (nsCOMPtr<nsBaseCommandController> context = 1507 do_QueryInterface(controller)) { 1508 context->SetContext(nullptr); 1509 } 1510 } 1511 1512 mControllers = nullptr; 1513 } 1514 } 1515 1516 //***************************************************************************** 1517 // nsGlobalWindowOuter::nsISupports 1518 //***************************************************************************** 1519 1520 // QueryInterface implementation for nsGlobalWindowOuter 1521 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGlobalWindowOuter) 1522 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 1523 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, EventTarget) 1524 NS_INTERFACE_MAP_ENTRY(nsIDOMWindow) 1525 NS_INTERFACE_MAP_ENTRY(nsIGlobalObject) 1526 NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObject) 1527 NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal) 1528 NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget) 1529 NS_INTERFACE_MAP_ENTRY(nsPIDOMWindowOuter) 1530 NS_INTERFACE_MAP_ENTRY(mozIDOMWindowProxy) 1531 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) 1532 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) 1533 NS_INTERFACE_MAP_END 1534 1535 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGlobalWindowOuter) 1536 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGlobalWindowOuter) 1537 1538 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGlobalWindowOuter) 1539 if (tmp->IsBlackForCC(false)) { 1540 if (nsCCUncollectableMarker::InGeneration(tmp->mCanSkipCCGeneration)) { 1541 return true; 1542 } 1543 tmp->mCanSkipCCGeneration = nsCCUncollectableMarker::sGeneration; 1544 if (EventListenerManager* elm = tmp->GetExistingListenerManager()) { 1545 elm->MarkForCC(); 1546 } 1547 return true; 1548 } 1549 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END 1550 1551 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsGlobalWindowOuter) 1552 return tmp->IsBlackForCC(true); 1553 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END 1554 1555 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsGlobalWindowOuter) 1556 return tmp->IsBlackForCC(false); 1557 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END 1558 1559 NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalWindowOuter) 1560 1561 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindowOuter) 1562 if (MOZ_UNLIKELY(cb.WantDebugInfo())) { 1563 char name[512]; 1564 nsAutoCString uri; 1565 if (tmp->mDoc && tmp->mDoc->GetDocumentURI()) { 1566 uri = tmp->mDoc->GetDocumentURI()->GetSpecOrDefault(); 1567 } 1568 SprintfLiteral(name, "nsGlobalWindowOuter # %" PRIu64 " outer %s", 1569 tmp->mWindowID, uri.get()); 1570 cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name); 1571 } else { 1572 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsGlobalWindowOuter, tmp->mRefCnt.get()) 1573 } 1574 1575 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext) 1576 1577 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mControllers) 1578 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mArguments) 1579 1580 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStorage) 1581 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSuspendedDocs) 1582 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPrincipal) 1583 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentCookiePrincipal) 1584 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentStoragePrincipal) 1585 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPartitionedPrincipal) 1586 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDoc) 1587 1588 // Traverse stuff from nsPIDOMWindow 1589 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeEventHandler) 1590 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentTarget) 1591 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageManager) 1592 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameElement) 1593 1594 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocShell) 1595 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowsingContext) 1596 1597 tmp->TraverseObjectsInGlobal(cb); 1598 1599 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeFields.mBrowserDOMWindow) 1600 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1601 1602 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindowOuter) 1603 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE 1604 if (sOuterWindowsById) { 1605 sOuterWindowsById->Remove(tmp->mWindowID); 1606 } 1607 1608 NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext) 1609 1610 NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers) 1611 NS_IMPL_CYCLE_COLLECTION_UNLINK(mArguments) 1612 1613 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalStorage) 1614 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSuspendedDocs) 1615 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPrincipal) 1616 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentCookiePrincipal) 1617 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentStoragePrincipal) 1618 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPartitionedPrincipal) 1619 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDoc) 1620 1621 // Unlink stuff from nsPIDOMWindow 1622 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeEventHandler) 1623 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParentTarget) 1624 NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageManager) 1625 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameElement) 1626 1627 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell) 1628 if (tmp->mBrowsingContext) { 1629 if (tmp->mBrowsingContext->GetUnbarrieredWindowProxy()) { 1630 nsGlobalWindowOuter* outer = nsOuterWindowProxy::GetOuterWindow( 1631 tmp->mBrowsingContext->GetUnbarrieredWindowProxy()); 1632 // Check that the current WindowProxy object corresponds to this 1633 // nsGlobalWindowOuter, because we don't want to clear the WindowProxy if 1634 // we've replaced it with a cross-process WindowProxy. 1635 if (outer == tmp) { 1636 tmp->mBrowsingContext->ClearWindowProxy(); 1637 } 1638 } 1639 tmp->mBrowsingContext = nullptr; 1640 } 1641 1642 tmp->UnlinkObjectsInGlobal(); 1643 1644 if (tmp->IsChromeWindow()) { 1645 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeFields.mBrowserDOMWindow) 1646 } 1647 1648 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER 1649 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1650 1651 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGlobalWindowOuter) 1652 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER 1653 NS_IMPL_CYCLE_COLLECTION_TRACE_END 1654 1655 bool nsGlobalWindowOuter::IsBlackForCC(bool aTracingNeeded) { 1656 if (!nsCCUncollectableMarker::sGeneration) { 1657 return false; 1658 } 1659 1660 // Unlike most wrappers, the outer window wrapper is not a wrapper for 1661 // the outer window. Instead, the outer window wrapper holds the inner 1662 // window binding object, which in turn holds the nsGlobalWindowInner, which 1663 // has a strong reference to the nsGlobalWindowOuter. We're using the 1664 // mInnerWindow pointer as a flag for that whole chain. 1665 return (nsCCUncollectableMarker::InGeneration(GetMarkedCCGeneration()) || 1666 (mInnerWindow && HasKnownLiveWrapper())) && 1667 (!aTracingNeeded || HasNothingToTrace(ToSupports(this))); 1668 } 1669 1670 //***************************************************************************** 1671 // nsGlobalWindowOuter::nsIScriptGlobalObject 1672 //***************************************************************************** 1673 1674 bool nsGlobalWindowOuter::ShouldResistFingerprinting(RFPTarget aTarget) const { 1675 if (mDoc) { 1676 return mDoc->ShouldResistFingerprinting(aTarget); 1677 } 1678 return nsContentUtils::ShouldResistFingerprinting( 1679 "If we do not have a document then we do not have any context" 1680 "to make an informed RFP choice, so we fall back to the global pref", 1681 aTarget); 1682 } 1683 1684 OriginTrials nsGlobalWindowOuter::Trials() const { 1685 return mInnerWindow ? nsGlobalWindowInner::Cast(mInnerWindow)->Trials() 1686 : OriginTrials(); 1687 } 1688 1689 FontFaceSet* nsGlobalWindowOuter::GetFonts() { 1690 if (mDoc) { 1691 return mDoc->Fonts(); 1692 } 1693 return nullptr; 1694 } 1695 1696 nsresult nsGlobalWindowOuter::EnsureScriptEnvironment() { 1697 if (GetWrapperPreserveColor()) { 1698 return NS_OK; 1699 } 1700 1701 NS_ENSURE_STATE(!mCleanedUp); 1702 1703 NS_ASSERTION(!GetCurrentInnerWindowInternal(this), 1704 "No cached wrapper, but we have an inner window?"); 1705 NS_ASSERTION(!mContext, "Will overwrite mContext!"); 1706 1707 // If this window is an [i]frame, don't bother GC'ing when the frame's context 1708 // is destroyed since a GC will happen when the frameset or host document is 1709 // destroyed anyway. 1710 mContext = new nsJSContext(mBrowsingContext->IsTop(), this); 1711 return NS_OK; 1712 } 1713 1714 nsIScriptContext* nsGlobalWindowOuter::GetScriptContext() { return mContext; } 1715 1716 bool nsGlobalWindowOuter::WouldReuseInnerWindow(Document* aNewDocument) { 1717 // We reuse the inner window when: 1718 // a. The current document is transient, i.e. a temporary placeholder while 1719 // an async load is ongoing. This is equivalent to the uncommitted initial 1720 // document. 1721 // b. At least one of the following conditions are true: 1722 // -- The new document is the same as the old document. This means that we're 1723 // getting called from document.open(). 1724 // -- The new document has the same origin as what we have loaded right now. 1725 1726 if (!mDoc || !aNewDocument) { 1727 return false; 1728 } 1729 1730 if (!mDoc->IsUncommittedInitialDocument()) { 1731 return false; 1732 } 1733 1734 #ifdef DEBUG 1735 { 1736 nsCOMPtr<nsIURI> uri; 1737 NS_GetURIWithoutRef(mDoc->GetDocumentURI(), getter_AddRefs(uri)); 1738 NS_ASSERTION(NS_IsAboutBlank(uri), "How'd this happen?"); 1739 } 1740 #endif 1741 1742 // Great, we're the original document, check for one of the other 1743 // conditions. 1744 1745 if (mDoc == aNewDocument) { 1746 return true; 1747 } 1748 1749 if (aNewDocument->IsStaticDocument()) { 1750 return false; 1751 } 1752 1753 if (BasePrincipal::Cast(mDoc->NodePrincipal()) 1754 ->FastEqualsConsideringDomain(aNewDocument->NodePrincipal())) { 1755 // The origin is the same. 1756 return true; 1757 } 1758 1759 return false; 1760 } 1761 1762 void nsGlobalWindowOuter::SetInitialPrincipal( 1763 nsIPrincipal* aNewWindowPrincipal) { 1764 // We should never create windows with an expanded principal. 1765 // If we have a system principal, make sure we're not using it for a content 1766 // docshell. 1767 // NOTE: Please keep this logic in sync with 1768 // nsAppShellService::JustCreateTopWindow 1769 if (nsContentUtils::IsExpandedPrincipal(aNewWindowPrincipal) || 1770 (aNewWindowPrincipal->IsSystemPrincipal() && 1771 GetBrowsingContext()->IsContent())) { 1772 aNewWindowPrincipal = nullptr; 1773 } 1774 1775 MOZ_ASSERT(mDoc, "Some document should've been eagerly created"); 1776 1777 // Bail if the existing document is (a) not initial 1778 if (!mDoc->IsUncommittedInitialDocument()) return; 1779 // or (b) already has the correct principal. 1780 if (mDoc->NodePrincipal() == aNewWindowPrincipal) return; 1781 1782 #ifdef DEBUG 1783 // The current document should be a dummy and therefore have a null principal 1784 bool isNullPrincipal; 1785 MOZ_ASSERT(NS_SUCCEEDED( 1786 mDoc->NodePrincipal()->GetIsNullPrincipal(&isNullPrincipal)) && 1787 isNullPrincipal); 1788 #endif 1789 1790 // Use the subject (or system) principal as the storage principal too until 1791 // the new window finishes navigating and gets a real storage principal. 1792 nsDocShell::Cast(GetDocShell()) 1793 ->CreateAboutBlankDocumentViewer( 1794 aNewWindowPrincipal, aNewWindowPrincipal, mDoc->GetPolicyContainer(), 1795 mDoc->GetDocBaseURI(), 1796 /* aIsInitialDocument */ true, mDoc->GetEmbedderPolicy()); 1797 1798 if (mDoc) { 1799 MOZ_ASSERT(mDoc->IsInitialDocument(), 1800 "document should be initial document"); 1801 } 1802 1803 RefPtr<PresShell> presShell = GetDocShell()->GetPresShell(); 1804 if (presShell && !presShell->DidInitialize()) { 1805 // Ensure that if someone plays with this document they will get 1806 // layout happening. 1807 presShell->Initialize(); 1808 } 1809 } 1810 1811 #define WINDOWSTATEHOLDER_IID \ 1812 {0x0b917c3e, 0xbd50, 0x4683, {0xaf, 0xc9, 0xc7, 0x81, 0x07, 0xae, 0x33, 0x26}} 1813 1814 class WindowStateHolder final : public nsISupports { 1815 public: 1816 NS_INLINE_DECL_STATIC_IID(WINDOWSTATEHOLDER_IID) 1817 NS_DECL_ISUPPORTS 1818 1819 explicit WindowStateHolder(nsGlobalWindowInner* aWindow); 1820 1821 nsGlobalWindowInner* GetInnerWindow() { return mInnerWindow; } 1822 1823 void DidRestoreWindow() { 1824 mInnerWindow = nullptr; 1825 mInnerWindowReflector = nullptr; 1826 } 1827 1828 protected: 1829 ~WindowStateHolder(); 1830 1831 nsGlobalWindowInner* mInnerWindow; 1832 // We hold onto this to make sure the inner window doesn't go away. The outer 1833 // window ends up recalculating it anyway. 1834 JS::PersistentRooted<JSObject*> mInnerWindowReflector; 1835 }; 1836 1837 WindowStateHolder::WindowStateHolder(nsGlobalWindowInner* aWindow) 1838 : mInnerWindow(aWindow), 1839 mInnerWindowReflector(RootingCx(), aWindow->GetWrapper()) { 1840 MOZ_ASSERT(aWindow, "null window"); 1841 1842 aWindow->Suspend(); 1843 1844 // When a global goes into the bfcache, we disable script. 1845 xpc::Scriptability::Get(mInnerWindowReflector).SetWindowAllowsScript(false); 1846 } 1847 1848 WindowStateHolder::~WindowStateHolder() { 1849 if (mInnerWindow) { 1850 // This window was left in the bfcache and is now going away. We need to 1851 // free it up. 1852 // Note that FreeInnerObjects may already have been called on the 1853 // inner window if its outer has already had SetDocShell(null) 1854 // called. 1855 mInnerWindow->FreeInnerObjects(); 1856 } 1857 } 1858 1859 NS_IMPL_ISUPPORTS(WindowStateHolder, WindowStateHolder) 1860 1861 bool nsGlobalWindowOuter::ComputeIsSecureContext(Document* aDocument, 1862 SecureContextFlags aFlags) { 1863 nsCOMPtr<nsIPrincipal> principal = aDocument->NodePrincipal(); 1864 if (principal->IsSystemPrincipal()) { 1865 return true; 1866 } 1867 1868 // Implement https://w3c.github.io/webappsec-secure-contexts/#settings-object 1869 // With some modifications to allow for aFlags. 1870 1871 bool hadNonSecureContextCreator = false; 1872 1873 if (WindowContext* parentWindow = 1874 GetBrowsingContext()->GetParentWindowContext()) { 1875 hadNonSecureContextCreator = !parentWindow->GetIsSecureContext(); 1876 } 1877 1878 if (hadNonSecureContextCreator) { 1879 return false; 1880 } 1881 1882 if (nsContentUtils::HttpsStateIsModern(aDocument) || 1883 nsContentUtils::DocumentHasOnionURI(aDocument)) { 1884 return true; 1885 } 1886 1887 if (principal->GetIsNullPrincipal()) { 1888 // If the NullPrincipal has a valid precursor URI we want to use it to 1889 // construct the principal otherwise we fall back to the original document 1890 // URI. 1891 nsCOMPtr<nsIPrincipal> precursorPrin = principal->GetPrecursorPrincipal(); 1892 nsCOMPtr<nsIURI> uri = precursorPrin ? precursorPrin->GetURI() : nullptr; 1893 if (!uri) { 1894 uri = aDocument->GetOriginalURI(); 1895 } 1896 // IsOriginPotentiallyTrustworthy doesn't care about origin attributes so 1897 // it doesn't actually matter what we use here, but reusing the document 1898 // principal's attributes is convenient. 1899 const OriginAttributes& attrs = principal->OriginAttributesRef(); 1900 // CreateContentPrincipal correctly gets a useful principal for blob: and 1901 // other URI_INHERITS_SECURITY_CONTEXT URIs. 1902 principal = BasePrincipal::CreateContentPrincipal(uri, attrs); 1903 if (NS_WARN_IF(!principal)) { 1904 return false; 1905 } 1906 } 1907 1908 return principal->GetIsOriginPotentiallyTrustworthy(); 1909 } 1910 1911 static bool InitializeLegacyNetscapeObject(JSContext* aCx, 1912 JS::Handle<JSObject*> aGlobal) { 1913 JSAutoRealm ar(aCx, aGlobal); 1914 1915 // Note: MathJax depends on window.netscape being exposed. See bug 791526. 1916 JS::Rooted<JSObject*> obj(aCx); 1917 obj = JS_DefineObject(aCx, aGlobal, "netscape", nullptr); 1918 NS_ENSURE_TRUE(obj, false); 1919 1920 obj = JS_DefineObject(aCx, obj, "security", nullptr); 1921 NS_ENSURE_TRUE(obj, false); 1922 1923 return true; 1924 } 1925 1926 struct MOZ_STACK_CLASS CompartmentFinderState { 1927 explicit CompartmentFinderState(nsIPrincipal* aPrincipal) 1928 : principal(aPrincipal), compartment(nullptr) {} 1929 1930 // Input: we look for a compartment which is same-origin with the 1931 // given principal. 1932 nsIPrincipal* principal; 1933 1934 // Output: We set this member if we find a compartment. 1935 JS::Compartment* compartment; 1936 }; 1937 1938 static JS::CompartmentIterResult FindSameOriginCompartment( 1939 JSContext* aCx, void* aData, JS::Compartment* aCompartment) { 1940 auto* data = static_cast<CompartmentFinderState*>(aData); 1941 MOZ_ASSERT(!data->compartment, "Why are we getting called?"); 1942 1943 // If this compartment is not safe to share across globals, don't do 1944 // anything with it; in particular we should not be getting a 1945 // CompartmentPrivate from such a compartment, because it may be in 1946 // the middle of being collected and its CompartmentPrivate may no 1947 // longer be valid. 1948 if (!js::IsSharableCompartment(aCompartment)) { 1949 return JS::CompartmentIterResult::KeepGoing; 1950 } 1951 1952 auto* compartmentPrivate = xpc::CompartmentPrivate::Get(aCompartment); 1953 if (!compartmentPrivate || 1954 !compartmentPrivate->CanShareCompartmentWith(data->principal)) { 1955 // Can't reuse this one, keep going. 1956 return JS::CompartmentIterResult::KeepGoing; 1957 } 1958 1959 // We have a winner! 1960 data->compartment = aCompartment; 1961 return JS::CompartmentIterResult::Stop; 1962 } 1963 1964 static JS::RealmCreationOptions& SelectZone( 1965 JSContext* aCx, nsIPrincipal* aPrincipal, nsGlobalWindowInner* aNewInner, 1966 JS::RealmCreationOptions& aOptions) { 1967 // Use the shared system compartment for chrome windows. 1968 if (aPrincipal->IsSystemPrincipal()) { 1969 return aOptions.setExistingCompartment(xpc::PrivilegedJunkScope()); 1970 } 1971 1972 BrowsingContext* bc = aNewInner->GetBrowsingContext(); 1973 if (bc->IsTop()) { 1974 // We're a toplevel load. Use a new zone. This way, when we do 1975 // zone-based compartment sharing we won't share compartments 1976 // across navigations. 1977 return aOptions.setNewCompartmentAndZone(); 1978 } 1979 1980 // Find the in-process ancestor highest in the hierarchy. 1981 nsGlobalWindowInner* ancestor = nullptr; 1982 for (WindowContext* wc = bc->GetParentWindowContext(); wc; 1983 wc = wc->GetParentWindowContext()) { 1984 if (nsGlobalWindowInner* win = wc->GetInnerWindow()) { 1985 ancestor = win; 1986 } 1987 } 1988 1989 // If we have an ancestor window, use its zone. 1990 if (ancestor && ancestor->GetGlobalJSObject()) { 1991 JS::Zone* zone = JS::GetObjectZone(ancestor->GetGlobalJSObject()); 1992 // Now try to find an existing compartment that's same-origin 1993 // with our principal. 1994 CompartmentFinderState data(aPrincipal); 1995 JS_IterateCompartmentsInZone(aCx, zone, &data, FindSameOriginCompartment); 1996 if (data.compartment) { 1997 return aOptions.setExistingCompartment(data.compartment); 1998 } 1999 return aOptions.setNewCompartmentInExistingZone( 2000 ancestor->GetGlobalJSObject()); 2001 } 2002 2003 return aOptions.setNewCompartmentAndZone(); 2004 } 2005 2006 /** 2007 * Create a new global object that will be used for an inner window. 2008 * Return the native global and an nsISupports 'holder' that can be used 2009 * to manage the lifetime of it. 2010 */ 2011 static nsresult CreateNativeGlobalForInner( 2012 JSContext* aCx, nsGlobalWindowInner* aNewInner, Document* aDocument, 2013 JS::MutableHandle<JSObject*> aGlobal, bool aIsSecureContext, 2014 bool aDefineSharedArrayBufferConstructor) { 2015 MOZ_ASSERT(aCx); 2016 MOZ_ASSERT(aNewInner); 2017 2018 nsCOMPtr<nsIURI> uri = aDocument->GetDocumentURI(); 2019 nsCOMPtr<nsIPrincipal> principal = aDocument->NodePrincipal(); 2020 MOZ_ASSERT(principal); 2021 2022 // DOMWindow with nsEP is not supported, we have to make sure 2023 // no one creates one accidentally. 2024 nsCOMPtr<nsIExpandedPrincipal> nsEP = do_QueryInterface(principal); 2025 MOZ_RELEASE_ASSERT(!nsEP, "DOMWindow with nsEP is not supported"); 2026 2027 JS::RealmOptions options; 2028 JS::RealmCreationOptions& creationOptions = options.creationOptions(); 2029 2030 SelectZone(aCx, principal, aNewInner, creationOptions); 2031 2032 // Define the SharedArrayBuffer global constructor property only if shared 2033 // memory may be used and structured-cloned (e.g. through postMessage). 2034 // 2035 // When the global constructor property isn't defined, the SharedArrayBuffer 2036 // constructor can still be reached through Web Assembly. Omitting the global 2037 // property just prevents feature-tests from being misled. See bug 1624266. 2038 creationOptions.setDefineSharedArrayBufferConstructor( 2039 aDefineSharedArrayBufferConstructor); 2040 2041 xpc::InitGlobalObjectOptions( 2042 options, principal->IsSystemPrincipal(), aIsSecureContext, 2043 aDocument->ShouldResistFingerprinting(RFPTarget::JSDateTimeUTC), 2044 aDocument->ShouldResistFingerprinting(RFPTarget::JSMathFdlibm), 2045 aDocument->ShouldResistFingerprinting(RFPTarget::JSLocale), 2046 aDocument->GetBrowsingContext()->Top()->GetLanguageOverride(), 2047 aDocument->GetBrowsingContext()->Top()->GetTimezoneOverride()); 2048 2049 // Determine if we need the Components object. 2050 bool needComponents = principal->IsSystemPrincipal(); 2051 uint32_t flags = needComponents ? 0 : xpc::OMIT_COMPONENTS_OBJECT; 2052 flags |= xpc::DONT_FIRE_ONNEWGLOBALHOOK; 2053 2054 if (!Window_Binding::Wrap(aCx, aNewInner, aNewInner, options, 2055 nsJSPrincipals::get(principal), aGlobal) || 2056 !xpc::InitGlobalObject(aCx, aGlobal, flags)) { 2057 return NS_ERROR_FAILURE; 2058 } 2059 2060 MOZ_ASSERT(aNewInner->GetWrapperPreserveColor() == aGlobal); 2061 2062 // Set the location information for the new global, so that tools like 2063 // about:memory may use that information 2064 xpc::SetLocationForGlobal(aGlobal, uri); 2065 2066 if (!InitializeLegacyNetscapeObject(aCx, aGlobal)) { 2067 return NS_ERROR_FAILURE; 2068 } 2069 2070 return NS_OK; 2071 } 2072 2073 nsresult nsGlobalWindowOuter::SetNewDocument(Document* aDocument, 2074 nsISupports* aState, 2075 bool aForceReuseInnerWindow, 2076 WindowGlobalChild* aActor) { 2077 MOZ_ASSERT(mDocumentPrincipal == nullptr, 2078 "mDocumentPrincipal prematurely set!"); 2079 MOZ_ASSERT(mDocumentCookiePrincipal == nullptr, 2080 "mDocumentCookiePrincipal prematurely set!"); 2081 MOZ_ASSERT(mDocumentStoragePrincipal == nullptr, 2082 "mDocumentStoragePrincipal prematurely set!"); 2083 MOZ_ASSERT(mDocumentPartitionedPrincipal == nullptr, 2084 "mDocumentPartitionedPrincipal prematurely set!"); 2085 MOZ_ASSERT(aDocument); 2086 2087 // Bail out early if we're in process of closing down the window. 2088 NS_ENSURE_STATE(!mCleanedUp); 2089 2090 NS_ASSERTION(!GetCurrentInnerWindow() || 2091 GetCurrentInnerWindow()->GetExtantDoc() == mDoc, 2092 "Uh, mDoc doesn't match the current inner window " 2093 "document!"); 2094 bool wouldReuseInnerWindow = WouldReuseInnerWindow(aDocument); 2095 if (aForceReuseInnerWindow && !wouldReuseInnerWindow && mDoc && 2096 mDoc->NodePrincipal() != aDocument->NodePrincipal()) { 2097 NS_ERROR("Attempted forced inner window reuse while changing principal"); 2098 return NS_ERROR_UNEXPECTED; 2099 } 2100 2101 if (!mBrowsingContext->AncestorsAreCurrent()) { 2102 return NS_ERROR_NOT_AVAILABLE; 2103 } 2104 2105 RefPtr<Document> oldDoc = mDoc; 2106 MOZ_RELEASE_ASSERT(oldDoc != aDocument); 2107 2108 AutoJSAPI jsapi; 2109 jsapi.Init(); 2110 JSContext* cx = jsapi.cx(); 2111 2112 // Check if we're anywhere near the stack limit before we reach the 2113 // transplanting code, since it has no good way to handle errors. This uses 2114 // the untrusted script limit, which is not strictly necessary since no 2115 // actual script should run. 2116 js::AutoCheckRecursionLimit recursion(cx); 2117 if (!recursion.checkConservativeDontReport(cx)) { 2118 NS_WARNING("Overrecursion in SetNewDocument"); 2119 return NS_ERROR_FAILURE; 2120 } 2121 2122 if (!mDoc) { 2123 // First document load. 2124 2125 // Get our private root. If it is equal to us, then we need to 2126 // attach our global key bindings that handles browser scrolling 2127 // and other browser commands. 2128 nsPIDOMWindowOuter* privateRoot = GetPrivateRoot(); 2129 2130 if (privateRoot == this) { 2131 RootWindowGlobalKeyListener::AttachKeyHandler(mChromeEventHandler); 2132 } 2133 } 2134 2135 MaybeResetWindowName(aDocument); 2136 2137 /* No mDocShell means we're already been partially closed down. When that 2138 happens, setting status isn't a big requirement, so don't. (Doesn't happen 2139 under normal circumstances, but bug 49615 describes a case.) */ 2140 2141 nsContentUtils::AddScriptRunner( 2142 NewRunnableMethod("nsGlobalWindowOuter::ClearStatus", this, 2143 &nsGlobalWindowOuter::ClearStatus)); 2144 2145 // Sometimes, WouldReuseInnerWindow() returns true even if there's no inner 2146 // window (see bug 776497). Be safe. 2147 bool reUseInnerWindow = (aForceReuseInnerWindow || wouldReuseInnerWindow) && 2148 GetCurrentInnerWindowInternal(this); 2149 2150 nsresult rv; 2151 2152 // We set mDoc even though this is an outer window to avoid 2153 // having to *always* reach into the inner window to find the 2154 // document. 2155 mDoc = aDocument; 2156 2157 nsDocShell::Cast(mDocShell)->MaybeRestoreWindowName(); 2158 2159 // We drop the print request for the old document on the floor, it never made 2160 // it. We don't close the window here either even if we were asked to. 2161 mShouldDelayPrintUntilAfterLoad = true; 2162 mDelayedCloseForPrinting = false; 2163 mDelayedPrintUntilAfterLoad = false; 2164 2165 // Take this opportunity to clear mSuspendedDocs. Our old inner window is now 2166 // responsible for unsuspending it. 2167 mSuspendedDocs.Clear(); 2168 2169 #ifdef DEBUG 2170 mLastOpenedURI = aDocument->GetDocumentURI(); 2171 #endif 2172 2173 RefPtr<nsGlobalWindowInner> currentInner = 2174 GetCurrentInnerWindowInternal(this); 2175 2176 if (currentInner && currentInner->mNavigator) { 2177 currentInner->mNavigator->OnNavigation(); 2178 } 2179 2180 RefPtr<nsGlobalWindowInner> newInnerWindow; 2181 bool createdInnerWindow = false; 2182 2183 bool thisChrome = IsChromeWindow(); 2184 2185 nsCOMPtr<WindowStateHolder> wsh = do_QueryInterface(aState); 2186 NS_ASSERTION(!aState || wsh, 2187 "What kind of weird state are you giving me here?"); 2188 2189 bool doomCurrentInner = false; 2190 2191 // Only non-gray (i.e. exposed to JS) objects should be assigned to 2192 // newInnerGlobal. 2193 JS::Rooted<JSObject*> newInnerGlobal(cx); 2194 if (reUseInnerWindow) { 2195 // We're reusing the current inner window. 2196 NS_ASSERTION(!currentInner->IsFrozen(), 2197 "We should never be reusing a shared inner window"); 2198 newInnerWindow = currentInner; 2199 newInnerGlobal = currentInner->GetWrapper(); 2200 2201 // We're reusing the inner window, but this still counts as a navigation, 2202 // so all expandos and such defined on the outer window should go away. 2203 // Force all Xray wrappers to be recomputed. 2204 JS::Rooted<JSObject*> rootedObject(cx, GetWrapper()); 2205 if (!JS_RefreshCrossCompartmentWrappers(cx, rootedObject)) { 2206 return NS_ERROR_FAILURE; 2207 } 2208 2209 // Inner windows are only reused for same-origin principals, but the 2210 // principals don't necessarily match exactly. Update the principal on the 2211 // realm to match the new document. NB: We don't just call 2212 // currentInner->RefreshRealmPrincipals() here because we haven't yet set 2213 // its mDoc to aDocument. 2214 JS::Realm* realm = js::GetNonCCWObjectRealm(newInnerGlobal); 2215 #ifdef DEBUG 2216 bool sameOrigin = false; 2217 nsIPrincipal* existing = nsJSPrincipals::get(JS::GetRealmPrincipals(realm)); 2218 aDocument->NodePrincipal()->Equals(existing, &sameOrigin); 2219 MOZ_ASSERT(sameOrigin); 2220 #endif 2221 JS::SetRealmPrincipals(realm, 2222 nsJSPrincipals::get(aDocument->NodePrincipal())); 2223 } else { 2224 if (aState) { 2225 newInnerWindow = wsh->GetInnerWindow(); 2226 newInnerGlobal = newInnerWindow->GetWrapper(); 2227 } else { 2228 newInnerWindow = nsGlobalWindowInner::Create(this, thisChrome, aActor); 2229 if (StaticPrefs::dom_timeout_defer_during_load() && 2230 !aDocument->NodePrincipal()->IsURIInPrefList( 2231 "dom.timeout.defer_during_load.force-disable")) { 2232 // ensure the initial loading state is known 2233 newInnerWindow->SetActiveLoadingState( 2234 aDocument->GetReadyStateEnum() == 2235 Document::ReadyState::READYSTATE_LOADING); 2236 } 2237 2238 // The outer window is automatically treated as frozen when we 2239 // null out the inner window. As a result, initializing classes 2240 // on the new inner won't end up reaching into the old inner 2241 // window for classes etc. 2242 // 2243 // [This happens with Object.prototype when XPConnect creates 2244 // a temporary global while initializing classes; the reason 2245 // being that xpconnect creates the temp global w/o a parent 2246 // and proto, which makes the JS engine look up classes in 2247 // cx->globalObject, i.e. this outer window]. 2248 2249 mInnerWindow = nullptr; 2250 2251 mCreatingInnerWindow = true; 2252 2253 // The SharedArrayBuffer global constructor property should not be present 2254 // in a fresh global object when shared memory objects aren't allowed 2255 // (because COOP/COEP support isn't enabled, or because COOP/COEP don't 2256 // act to isolate this page to a separate process). 2257 2258 // Every script context we are initialized with must create a 2259 // new global. 2260 rv = CreateNativeGlobalForInner( 2261 cx, newInnerWindow, aDocument, &newInnerGlobal, 2262 ComputeIsSecureContext(aDocument), 2263 newInnerWindow->IsSharedMemoryAllowedInternal( 2264 aDocument->NodePrincipal())); 2265 NS_ASSERTION( 2266 NS_SUCCEEDED(rv) && newInnerGlobal && 2267 newInnerWindow->GetWrapperPreserveColor() == newInnerGlobal, 2268 "Failed to get script global"); 2269 2270 mCreatingInnerWindow = false; 2271 createdInnerWindow = true; 2272 2273 NS_ENSURE_SUCCESS(rv, rv); 2274 } 2275 2276 if (currentInner && currentInner->GetWrapperPreserveColor()) { 2277 // Don't free objects on our current inner window if it's going to be 2278 // held in the bfcache. 2279 if (!currentInner->IsFrozen()) { 2280 doomCurrentInner = true; 2281 } 2282 } 2283 2284 mInnerWindow = newInnerWindow; 2285 MOZ_ASSERT(mInnerWindow); 2286 mInnerWindow->TryToCacheTopInnerWindow(); 2287 2288 if (!GetWrapperPreserveColor()) { 2289 JS::Rooted<JSObject*> outer( 2290 cx, NewOuterWindowProxy(cx, newInnerGlobal, thisChrome)); 2291 NS_ENSURE_TRUE(outer, NS_ERROR_FAILURE); 2292 2293 mBrowsingContext->CleanUpDanglingRemoteOuterWindowProxies(cx, &outer); 2294 MOZ_ASSERT(js::IsWindowProxy(outer)); 2295 2296 js::SetProxyReservedSlot(outer, OUTER_WINDOW_SLOT, 2297 JS::PrivateValue(ToSupports(this))); 2298 2299 // Inform the nsJSContext, which is the canonical holder of the outer. 2300 mContext->SetWindowProxy(outer); 2301 2302 SetWrapper(mContext->GetWindowProxy()); 2303 } else { 2304 JS::Rooted<JSObject*> outerObject( 2305 cx, NewOuterWindowProxy(cx, newInnerGlobal, thisChrome)); 2306 if (!outerObject) { 2307 NS_ERROR("out of memory"); 2308 return NS_ERROR_FAILURE; 2309 } 2310 2311 JS::Rooted<JSObject*> obj(cx, GetWrapper()); 2312 2313 MOZ_ASSERT(js::IsWindowProxy(obj)); 2314 2315 js::SetProxyReservedSlot(obj, OUTER_WINDOW_SLOT, 2316 JS::PrivateValue(nullptr)); 2317 js::SetProxyReservedSlot(outerObject, OUTER_WINDOW_SLOT, 2318 JS::PrivateValue(nullptr)); 2319 js::SetProxyReservedSlot(obj, HOLDER_WEAKMAP_SLOT, JS::UndefinedValue()); 2320 2321 outerObject = xpc::TransplantObjectNukingXrayWaiver(cx, obj, outerObject); 2322 2323 if (!outerObject) { 2324 mBrowsingContext->ClearWindowProxy(); 2325 NS_ERROR("unable to transplant wrappers, probably OOM"); 2326 return NS_ERROR_FAILURE; 2327 } 2328 2329 js::SetProxyReservedSlot(outerObject, OUTER_WINDOW_SLOT, 2330 JS::PrivateValue(ToSupports(this))); 2331 2332 SetWrapper(outerObject); 2333 2334 MOZ_ASSERT(JS::GetNonCCWObjectGlobal(outerObject) == newInnerGlobal); 2335 2336 // Inform the nsJSContext, which is the canonical holder of the outer. 2337 mContext->SetWindowProxy(outerObject); 2338 } 2339 2340 // Enter the new global's realm. 2341 JSAutoRealm ar(cx, GetWrapperPreserveColor()); 2342 2343 { 2344 JS::Rooted<JSObject*> outer(cx, GetWrapperPreserveColor()); 2345 js::SetWindowProxy(cx, newInnerGlobal, outer); 2346 mBrowsingContext->SetWindowProxy(outer); 2347 } 2348 2349 // Set scriptability based on the state of the WindowContext. 2350 WindowContext* wc = mInnerWindow->GetWindowContext(); 2351 bool allow = 2352 wc ? wc->CanExecuteScripts() : mBrowsingContext->CanExecuteScripts(); 2353 xpc::Scriptability::Get(GetWrapperPreserveColor()) 2354 .SetWindowAllowsScript(allow); 2355 2356 if (!aState) { 2357 // Get the "window" property once so it will be cached on our inner. We 2358 // have to do this here, not in binding code, because this has to happen 2359 // after we've created the outer window proxy and stashed it in the outer 2360 // nsGlobalWindowOuter, so GetWrapperPreserveColor() on that outer 2361 // nsGlobalWindowOuter doesn't return null and 2362 // nsGlobalWindowOuter::OuterObject works correctly. 2363 JS::Rooted<JS::Value> unused(cx); 2364 if (!JS_GetProperty(cx, newInnerGlobal, "window", &unused)) { 2365 NS_ERROR("can't create the 'window' property"); 2366 return NS_ERROR_FAILURE; 2367 } 2368 2369 // And same thing for the "self" property. 2370 if (!JS_GetProperty(cx, newInnerGlobal, "self", &unused)) { 2371 NS_ERROR("can't create the 'self' property"); 2372 return NS_ERROR_FAILURE; 2373 } 2374 } 2375 } 2376 2377 JSAutoRealm ar(cx, GetWrapperPreserveColor()); 2378 2379 if (!aState && !reUseInnerWindow) { 2380 // Loading a new page and creating a new inner window, *not* 2381 // restoring from session history. 2382 2383 // Now that both the the inner and outer windows are initialized 2384 // let the script context do its magic to hook them together. 2385 MOZ_ASSERT(mContext->GetWindowProxy() == GetWrapperPreserveColor()); 2386 #ifdef DEBUG 2387 JS::Rooted<JSObject*> rootedJSObject(cx, GetWrapperPreserveColor()); 2388 JS::Rooted<JSObject*> proto1(cx), proto2(cx); 2389 JS_GetPrototype(cx, rootedJSObject, &proto1); 2390 JS_GetPrototype(cx, newInnerGlobal, &proto2); 2391 NS_ASSERTION(proto1 == proto2, 2392 "outer and inner globals should have the same prototype"); 2393 #endif 2394 2395 mInnerWindow->SyncStateFromParentWindow(); 2396 } 2397 2398 // Add an extra ref in case we release mContext during GC. 2399 nsCOMPtr<nsIScriptContext> kungFuDeathGrip(mContext); 2400 2401 // Make sure the inner's document is set correctly before we call 2402 // SetScriptGlobalObject, because that might try to examine document-dependent 2403 // state. Unfortunately, we can't do some of the other clearing/resetting 2404 // work we do below until after SetScriptGlobalObject(), because it might 2405 // depend on the document having the right scope object. 2406 if (aState) { 2407 MOZ_RELEASE_ASSERT(newInnerWindow->mDoc == aDocument); 2408 } else { 2409 if (reUseInnerWindow) { 2410 MOZ_RELEASE_ASSERT(newInnerWindow->mDoc != aDocument); 2411 } 2412 newInnerWindow->mDoc = aDocument; 2413 } 2414 2415 aDocument->SetScriptGlobalObject(newInnerWindow); 2416 2417 MOZ_RELEASE_ASSERT(newInnerWindow->mDoc == aDocument); 2418 2419 if (mBrowsingContext->IsTopContent()) { 2420 net::CookieJarSettings::Cast(aDocument->CookieJarSettings()) 2421 ->SetTopLevelWindowContextId(aDocument->InnerWindowID()); 2422 } 2423 2424 newInnerWindow->RefreshReduceTimerPrecisionCallerType(); 2425 2426 if (!aState) { 2427 if (reUseInnerWindow) { 2428 // The StorageAccess state may have changed. Invalidate the cached 2429 // StorageAllowed field, so that the next call to StorageAllowedForWindow 2430 // recomputes it. 2431 newInnerWindow->ClearStorageAllowedCache(); 2432 2433 // The storage objects contain the URL of the window. We have to 2434 // recreate them when the innerWindow is reused. 2435 newInnerWindow->mLocalStorage = nullptr; 2436 newInnerWindow->mSessionStorage = nullptr; 2437 newInnerWindow->mPerformance = nullptr; 2438 2439 // This must be called after nullifying the internal objects because 2440 // here we could recreate them, calling the getter methods, and store 2441 // them into the JS slots. If we nullify them after, the slot values and 2442 // the objects will be out of sync. 2443 newInnerWindow->ClearDocumentDependentSlots(cx); 2444 } else { 2445 newInnerWindow->InitDocumentDependentState(cx); 2446 2447 // Initialize DOM classes etc on the inner window. 2448 JS::Rooted<JSObject*> obj(cx, newInnerGlobal); 2449 rv = kungFuDeathGrip->InitClasses(obj); 2450 NS_ENSURE_SUCCESS(rv, rv); 2451 } 2452 2453 // When replacing an initial about:blank document we call 2454 // ExecutionReady again to update the client creation URL. 2455 rv = newInnerWindow->ExecutionReady(); 2456 NS_ENSURE_SUCCESS(rv, rv); 2457 2458 if (mArguments) { 2459 newInnerWindow->DefineArgumentsProperty(mArguments); 2460 mArguments = nullptr; 2461 } 2462 2463 // Give the new inner window our chrome event handler (since it 2464 // doesn't have one). 2465 newInnerWindow->mChromeEventHandler = mChromeEventHandler; 2466 } 2467 2468 if (!aState && reUseInnerWindow) { 2469 // Notify our WindowGlobalChild that it has a new document. If `aState` was 2470 // passed, we're restoring the window from the BFCache, so the document 2471 // hasn't changed. 2472 // If we didn't have a window global child before, then initializing 2473 // it will have set all the required state, so we don't need to do 2474 // it again. 2475 mInnerWindow->GetWindowGlobalChild()->OnNewDocument(aDocument); 2476 } 2477 2478 // Update the current window for our BrowsingContext. 2479 RefPtr<BrowsingContext> bc = GetBrowsingContext(); 2480 2481 if (bc->IsOwnedByProcess()) { 2482 MOZ_ALWAYS_SUCCEEDS(bc->SetCurrentInnerWindowId(mInnerWindow->WindowID())); 2483 } 2484 2485 // We no longer need the old inner window. Start its destruction if 2486 // its not being reused and clear our reference. 2487 if (doomCurrentInner) { 2488 currentInner->FreeInnerObjects(); 2489 } 2490 currentInner = nullptr; 2491 2492 // We wait to fire the debugger hook until the window is all set up and hooked 2493 // up with the outer. See bug 969156. 2494 if (createdInnerWindow) { 2495 nsContentUtils::AddScriptRunner(NewRunnableMethod( 2496 "nsGlobalWindowInner::FireOnNewGlobalObject", newInnerWindow, 2497 &nsGlobalWindowInner::FireOnNewGlobalObject)); 2498 } 2499 2500 if (!newInnerWindow->mHasNotifiedGlobalCreated && mDoc) { 2501 // We should probably notify, except if we have the initial about:blank 2502 // in a chrome docshell, defer notification until the first non-initial 2503 // document. 2504 const bool isAboutBlankInChromeDocshell = [&] { 2505 if (!mDocShell) { 2506 return false; 2507 } 2508 2509 RefPtr<BrowsingContext> bc = mDocShell->GetBrowsingContext(); 2510 if (!bc || bc->GetType() != BrowsingContext::Type::Chrome) { 2511 return false; 2512 } 2513 2514 return mDoc->IsInitialDocument(); 2515 }(); 2516 2517 if (!isAboutBlankInChromeDocshell) { 2518 newInnerWindow->mHasNotifiedGlobalCreated = true; 2519 nsContentUtils::AddScriptRunner(NewRunnableMethod( 2520 "nsGlobalWindowOuter::DispatchDOMWindowCreated", this, 2521 &nsGlobalWindowOuter::DispatchDOMWindowCreated)); 2522 } 2523 } 2524 2525 PreloadLocalStorage(); 2526 2527 // Do this here rather than in say the Document constructor, since 2528 // we need a WindowContext available. 2529 mDoc->InitUseCounters(); 2530 2531 return NS_OK; 2532 } 2533 2534 /* static */ 2535 void nsGlobalWindowOuter::PrepareForProcessChange(JSObject* aProxy) { 2536 JS::Rooted<JSObject*> localProxy(RootingCx(), aProxy); 2537 MOZ_ASSERT(js::IsWindowProxy(localProxy)); 2538 2539 RefPtr<nsGlobalWindowOuter> outerWindow = 2540 nsOuterWindowProxy::GetOuterWindow(localProxy); 2541 if (!outerWindow) { 2542 return; 2543 } 2544 2545 AutoJSAPI jsapi; 2546 jsapi.Init(); 2547 JSContext* cx = jsapi.cx(); 2548 2549 JSAutoRealm ar(cx, localProxy); 2550 2551 // Clear out existing references from the browsing context and outer window to 2552 // the proxy, and from the proxy to the outer window. These references will 2553 // become invalid once the proxy is transplanted. Clearing the window proxy 2554 // from the browsing context is also necessary to indicate that it is for an 2555 // out of process window. 2556 outerWindow->ClearWrapper(localProxy); 2557 RefPtr<BrowsingContext> bc = outerWindow->GetBrowsingContext(); 2558 MOZ_ASSERT(bc); 2559 MOZ_ASSERT(bc->GetWindowProxy() == localProxy); 2560 bc->ClearWindowProxy(); 2561 js::SetProxyReservedSlot(localProxy, OUTER_WINDOW_SLOT, 2562 JS::PrivateValue(nullptr)); 2563 js::SetProxyReservedSlot(localProxy, HOLDER_WEAKMAP_SLOT, 2564 JS::UndefinedValue()); 2565 2566 // Create a new remote outer window proxy, and transplant to it. 2567 JS::Rooted<JSObject*> remoteProxy(cx); 2568 2569 if (!mozilla::dom::GetRemoteOuterWindowProxy(cx, bc, localProxy, 2570 &remoteProxy)) { 2571 MOZ_CRASH("PrepareForProcessChange GetRemoteOuterWindowProxy"); 2572 } 2573 2574 if (!xpc::TransplantObjectNukingXrayWaiver(cx, localProxy, remoteProxy)) { 2575 MOZ_CRASH("PrepareForProcessChange TransplantObject"); 2576 } 2577 } 2578 2579 void nsGlobalWindowOuter::PreloadLocalStorage() { 2580 if (!Storage::StoragePrefIsEnabled()) { 2581 return; 2582 } 2583 2584 if (IsChromeWindow()) { 2585 return; 2586 } 2587 2588 nsIPrincipal* principal = GetPrincipal(); 2589 nsIPrincipal* storagePrincipal = GetEffectiveStoragePrincipal(); 2590 if (!principal || !storagePrincipal) { 2591 return; 2592 } 2593 2594 nsresult rv; 2595 2596 nsCOMPtr<nsIDOMStorageManager> storageManager = 2597 do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv); 2598 if (NS_FAILED(rv)) { 2599 return; 2600 } 2601 2602 // private browsing windows do not persist local storage to disk so we should 2603 // only try to precache storage when we're not a private browsing window. 2604 if (!principal->GetIsInPrivateBrowsing()) { 2605 RefPtr<Storage> storage; 2606 rv = storageManager->PrecacheStorage(principal, storagePrincipal, 2607 getter_AddRefs(storage)); 2608 if (NS_SUCCEEDED(rv)) { 2609 mLocalStorage = storage; 2610 } 2611 } 2612 } 2613 2614 void nsGlobalWindowOuter::DispatchDOMWindowCreated() { 2615 if (!mDoc) { 2616 return; 2617 } 2618 2619 // Fire DOMWindowCreated at chrome event listeners 2620 nsContentUtils::DispatchChromeEvent(mDoc, mDoc, u"DOMWindowCreated"_ns, 2621 CanBubble::eYes, Cancelable::eNo); 2622 2623 nsCOMPtr<nsIObserverService> observerService = 2624 mozilla::services::GetObserverService(); 2625 2626 // The event dispatching could possibly cause docshell destory, and 2627 // consequently cause mDoc to be set to nullptr by DropOuterWindowDocs(), 2628 // so check it again here. 2629 if (observerService && mDoc) { 2630 nsAutoString origin; 2631 nsIPrincipal* principal = mDoc->NodePrincipal(); 2632 nsContentUtils::GetWebExposedOriginSerialization(principal, origin); 2633 observerService->NotifyObservers(static_cast<nsIDOMWindow*>(this), 2634 principal->IsSystemPrincipal() 2635 ? "chrome-document-global-created" 2636 : "content-document-global-created", 2637 origin.get()); 2638 } 2639 } 2640 2641 void nsGlobalWindowOuter::ClearStatus() { SetStatusOuter(u""_ns); } 2642 2643 void nsGlobalWindowOuter::SetDocShell(nsDocShell* aDocShell) { 2644 MOZ_ASSERT(aDocShell); 2645 2646 if (aDocShell == mDocShell) { 2647 return; 2648 } 2649 2650 mDocShell = aDocShell; 2651 mBrowsingContext = aDocShell->GetBrowsingContext(); 2652 2653 RefPtr<BrowsingContext> parentContext = mBrowsingContext->GetParent(); 2654 2655 MOZ_RELEASE_ASSERT(!parentContext || 2656 GetBrowsingContextGroup() == parentContext->Group()); 2657 2658 mTopLevelOuterContentWindow = mBrowsingContext->IsTopContent(); 2659 2660 // Get our enclosing chrome shell and retrieve its global window impl, so 2661 // that we can do some forwarding to the chrome document. 2662 RefPtr<EventTarget> chromeEventHandler; 2663 mDocShell->GetChromeEventHandler(getter_AddRefs(chromeEventHandler)); 2664 mChromeEventHandler = chromeEventHandler; 2665 if (!mChromeEventHandler) { 2666 // We have no chrome event handler. If we have a parent, 2667 // get our chrome event handler from the parent. If 2668 // we don't have a parent, then we need to make a new 2669 // window root object that will function as a chrome event 2670 // handler and receive all events that occur anywhere inside 2671 // our window. 2672 nsCOMPtr<nsPIDOMWindowOuter> parentWindow = GetInProcessParent(); 2673 if (parentWindow.get() != this) { 2674 mChromeEventHandler = parentWindow->GetChromeEventHandler(); 2675 } else { 2676 mChromeEventHandler = NS_NewWindowRoot(this); 2677 mIsRootOuterWindow = true; 2678 } 2679 } 2680 2681 SetIsBackgroundInternal(!mBrowsingContext->IsActive()); 2682 } 2683 2684 void nsGlobalWindowOuter::DetachFromDocShell(bool aIsBeingDiscarded) { 2685 // DetachFromDocShell means the window is being torn down. Drop our 2686 // reference to the script context, allowing it to be deleted 2687 // later. Meanwhile, keep our weak reference to the script object 2688 // so that it can be retrieved later (until it is finalized by the JS GC). 2689 2690 // Call FreeInnerObjects on all inner windows, not just the current 2691 // one, since some could be held by WindowStateHolder objects that 2692 // are GC-owned. 2693 RefPtr<nsGlobalWindowInner> inner; 2694 for (PRCList* node = PR_LIST_HEAD(this); node != this; 2695 node = PR_NEXT_LINK(inner)) { 2696 // This cast is safe because `node != this`. Non-this nodes are inner 2697 // windows. 2698 inner = static_cast<nsGlobalWindowInner*>(node); 2699 MOZ_ASSERT(!inner->mOuterWindow || inner->mOuterWindow == this); 2700 inner->FreeInnerObjects(); 2701 } 2702 2703 // Don't report that we were detached to the nsWindowMemoryReporter, as it 2704 // only tracks inner windows. 2705 2706 NotifyWindowIDDestroyed("outer-window-destroyed"); 2707 2708 nsGlobalWindowInner* currentInner = GetCurrentInnerWindowInternal(this); 2709 2710 if (currentInner) { 2711 NS_ASSERTION(mDoc, "Must have doc!"); 2712 2713 // Remember the document's principal and URI. 2714 mDocumentPrincipal = mDoc->NodePrincipal(); 2715 mDocumentCookiePrincipal = mDoc->EffectiveCookiePrincipal(); 2716 mDocumentStoragePrincipal = mDoc->EffectiveStoragePrincipal(); 2717 mDocumentPartitionedPrincipal = mDoc->PartitionedPrincipal(); 2718 mDocumentURI = mDoc->GetDocumentURI(); 2719 2720 // Release our document reference 2721 DropOuterWindowDocs(); 2722 } 2723 2724 ClearControllers(); 2725 2726 mChromeEventHandler = nullptr; // force release now 2727 2728 if (mContext) { 2729 // When we're about to destroy a top level content window 2730 // (for example a tab), we trigger a full GC by passing null as the last 2731 // param. We also trigger a full GC for chrome windows. 2732 nsJSContext::PokeGC(JS::GCReason::SET_DOC_SHELL, 2733 (mTopLevelOuterContentWindow || mIsChrome) 2734 ? nullptr 2735 : GetWrapperPreserveColor()); 2736 mContext = nullptr; 2737 } 2738 2739 if (aIsBeingDiscarded) { 2740 // If our BrowsingContext is being discarded, make a note that our current 2741 // inner window was active at the time it went away. 2742 if (nsGlobalWindowInner* currentInner = 2743 GetCurrentInnerWindowInternal(this)) { 2744 currentInner->SetWasCurrentInnerWindow(); 2745 } 2746 } 2747 2748 mDocShell = nullptr; 2749 mBrowsingContext->ClearDocShell(); 2750 2751 CleanUp(); 2752 } 2753 2754 void nsGlobalWindowOuter::UpdateParentTarget() { 2755 // NOTE: This method is nearly identical to 2756 // nsGlobalWindowInner::UpdateParentTarget(). IF YOU UPDATE THIS METHOD, 2757 // UPDATE THE OTHER ONE TOO! The one difference is that this method updates 2758 // mMessageManager as well, which inner windows don't have. 2759 2760 // Try to get our frame element's tab child global (its in-process message 2761 // manager). If that fails, fall back to the chrome event handler's tab 2762 // child global, and if it doesn't have one, just use the chrome event 2763 // handler itself. 2764 2765 nsCOMPtr<Element> frameElement = GetFrameElementInternal(); 2766 mMessageManager = nsContentUtils::TryGetBrowserChildGlobal(frameElement); 2767 2768 if (!mMessageManager) { 2769 nsGlobalWindowOuter* topWin = GetInProcessScriptableTopInternal(); 2770 if (topWin) { 2771 frameElement = topWin->GetFrameElementInternal(); 2772 mMessageManager = nsContentUtils::TryGetBrowserChildGlobal(frameElement); 2773 } 2774 } 2775 2776 if (!mMessageManager) { 2777 mMessageManager = 2778 nsContentUtils::TryGetBrowserChildGlobal(mChromeEventHandler); 2779 } 2780 2781 if (mMessageManager) { 2782 mParentTarget = mMessageManager; 2783 } else { 2784 mParentTarget = mChromeEventHandler; 2785 } 2786 } 2787 2788 EventTarget* nsGlobalWindowOuter::GetTargetForEventTargetChain() { 2789 return GetCurrentInnerWindowInternal(this); 2790 } 2791 2792 void nsGlobalWindowOuter::GetEventTargetParent(EventChainPreVisitor& aVisitor) { 2793 MOZ_CRASH("The outer window should not be part of an event path"); 2794 } 2795 2796 bool nsGlobalWindowOuter::ShouldPromptToBlockDialogs() { 2797 if (!nsContentUtils::GetCurrentJSContext()) { 2798 return false; // non-scripted caller. 2799 } 2800 2801 BrowsingContextGroup* group = GetBrowsingContextGroup(); 2802 if (!group) { 2803 return true; 2804 } 2805 2806 return group->DialogsAreBeingAbused(); 2807 } 2808 2809 bool nsGlobalWindowOuter::AreDialogsEnabled() { 2810 BrowsingContextGroup* group = mBrowsingContext->Group(); 2811 if (!group) { 2812 NS_ERROR("AreDialogsEnabled() called without a browsing context group?"); 2813 return false; 2814 } 2815 2816 // Dialogs are blocked if the content viewer is hidden 2817 if (mDocShell) { 2818 nsCOMPtr<nsIDocumentViewer> viewer; 2819 mDocShell->GetDocViewer(getter_AddRefs(viewer)); 2820 2821 bool isHidden; 2822 viewer->GetIsHidden(&isHidden); 2823 if (isHidden) { 2824 return false; 2825 } 2826 } 2827 2828 // Dialogs are also blocked if the document is sandboxed with SANDBOXED_MODALS 2829 // (or if we have no document, of course). Which document? Who knows; the 2830 // spec is daft. See <https://github.com/whatwg/html/issues/1206>. For now 2831 // just go ahead and check mDoc, since in everything except edge cases in 2832 // which a frame is allow-same-origin but not allow-scripts and is being poked 2833 // at by some other window this should be the right thing anyway. 2834 if (!mDoc || (mDoc->GetSandboxFlags() & SANDBOXED_MODALS)) { 2835 return false; 2836 } 2837 2838 return group->GetAreDialogsEnabled(); 2839 } 2840 2841 void nsGlobalWindowOuter::DisableDialogs() { 2842 BrowsingContextGroup* group = mBrowsingContext->Group(); 2843 if (!group) { 2844 NS_ERROR("DisableDialogs() called without a browsing context group?"); 2845 return; 2846 } 2847 2848 if (group) { 2849 group->SetAreDialogsEnabled(false); 2850 } 2851 } 2852 2853 void nsGlobalWindowOuter::EnableDialogs() { 2854 BrowsingContextGroup* group = mBrowsingContext->Group(); 2855 if (!group) { 2856 NS_ERROR("EnableDialogs() called without a browsing context group?"); 2857 return; 2858 } 2859 2860 if (group) { 2861 group->SetAreDialogsEnabled(true); 2862 } 2863 } 2864 2865 nsresult nsGlobalWindowOuter::PostHandleEvent(EventChainPostVisitor& aVisitor) { 2866 MOZ_CRASH("The outer window should not be part of an event path"); 2867 } 2868 2869 void nsGlobalWindowOuter::PoisonOuterWindowProxy(JSObject* aObject) { 2870 if (aObject == GetWrapperMaybeDead()) { 2871 PoisonWrapper(); 2872 } 2873 } 2874 2875 nsresult nsGlobalWindowOuter::SetArguments(nsIArray* aArguments) { 2876 nsresult rv; 2877 2878 // We've now mostly separated them, but the difference is still opaque to 2879 // nsWindowWatcher (the caller of SetArguments in this little back-and-forth 2880 // embedding waltz we do here). 2881 // 2882 // So we need to demultiplex the two cases here. 2883 nsGlobalWindowInner* currentInner = GetCurrentInnerWindowInternal(this); 2884 2885 mArguments = aArguments; 2886 rv = currentInner->DefineArgumentsProperty(aArguments); 2887 NS_ENSURE_SUCCESS(rv, rv); 2888 2889 return NS_OK; 2890 } 2891 2892 //***************************************************************************** 2893 // nsGlobalWindowOuter::nsIScriptObjectPrincipal 2894 //***************************************************************************** 2895 2896 nsIPrincipal* nsGlobalWindowOuter::GetPrincipal() { 2897 if (mDoc) { 2898 // If we have a document, get the principal from the document 2899 return mDoc->NodePrincipal(); 2900 } 2901 2902 if (mDocumentPrincipal) { 2903 return mDocumentPrincipal; 2904 } 2905 2906 // If we don't have a principal and we don't have a document we 2907 // ask the parent window for the principal. This can happen when 2908 // loading a frameset that has a <frame src="javascript:xxx">, in 2909 // that case the global window is used in JS before we've loaded 2910 // a document into the window. 2911 2912 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal = 2913 do_QueryInterface(GetInProcessParentInternal()); 2914 2915 if (objPrincipal) { 2916 return objPrincipal->GetPrincipal(); 2917 } 2918 2919 return nullptr; 2920 } 2921 2922 nsIPrincipal* nsGlobalWindowOuter::GetEffectiveCookiePrincipal() { 2923 if (mDoc) { 2924 // If we have a document, get the principal from the document 2925 return mDoc->EffectiveCookiePrincipal(); 2926 } 2927 2928 if (mDocumentCookiePrincipal) { 2929 return mDocumentCookiePrincipal; 2930 } 2931 2932 // If we don't have a cookie principal and we don't have a document we ask 2933 // the parent window for the cookie principal. 2934 2935 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal = 2936 do_QueryInterface(GetInProcessParentInternal()); 2937 2938 if (objPrincipal) { 2939 return objPrincipal->GetEffectiveCookiePrincipal(); 2940 } 2941 2942 return nullptr; 2943 } 2944 2945 nsIPrincipal* nsGlobalWindowOuter::GetEffectiveStoragePrincipal() { 2946 if (mDoc) { 2947 // If we have a document, get the principal from the document 2948 return mDoc->EffectiveStoragePrincipal(); 2949 } 2950 2951 if (mDocumentStoragePrincipal) { 2952 return mDocumentStoragePrincipal; 2953 } 2954 2955 // If we don't have a storage principal and we don't have a document we ask 2956 // the parent window for the storage principal. 2957 2958 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal = 2959 do_QueryInterface(GetInProcessParentInternal()); 2960 2961 if (objPrincipal) { 2962 return objPrincipal->GetEffectiveStoragePrincipal(); 2963 } 2964 2965 return nullptr; 2966 } 2967 2968 nsIPrincipal* nsGlobalWindowOuter::PartitionedPrincipal() { 2969 if (mDoc) { 2970 // If we have a document, get the principal from the document 2971 return mDoc->PartitionedPrincipal(); 2972 } 2973 2974 if (mDocumentPartitionedPrincipal) { 2975 return mDocumentPartitionedPrincipal; 2976 } 2977 2978 // If we don't have a partitioned principal and we don't have a document we 2979 // ask the parent window for the partitioned principal. 2980 2981 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal = 2982 do_QueryInterface(GetInProcessParentInternal()); 2983 2984 if (objPrincipal) { 2985 return objPrincipal->PartitionedPrincipal(); 2986 } 2987 2988 return nullptr; 2989 } 2990 2991 //***************************************************************************** 2992 // nsGlobalWindowOuter::nsIDOMWindow 2993 //***************************************************************************** 2994 2995 Element* nsPIDOMWindowOuter::GetFrameElementInternal() const { 2996 return mFrameElement; 2997 } 2998 2999 void nsPIDOMWindowOuter::SetFrameElementInternal(Element* aFrameElement) { 3000 mFrameElement = aFrameElement; 3001 } 3002 3003 Navigator* nsGlobalWindowOuter::GetNavigator() { 3004 FORWARD_TO_INNER(Navigator, (), nullptr); 3005 } 3006 3007 nsScreen* nsGlobalWindowOuter::GetScreen() { 3008 FORWARD_TO_INNER(Screen, (), nullptr); 3009 } 3010 3011 void nsPIDOMWindowOuter::ActivateMediaComponents() { 3012 if (!ShouldDelayMediaFromStart()) { 3013 return; 3014 } 3015 MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug, 3016 ("nsPIDOMWindowOuter, ActiveMediaComponents, " 3017 "no longer to delay media from start, this = %p\n", 3018 this)); 3019 if (BrowsingContext* bc = GetBrowsingContext()) { 3020 (void)bc->Top()->SetShouldDelayMediaFromStart(false); 3021 } 3022 NotifyResumingDelayedMedia(); 3023 } 3024 3025 bool nsPIDOMWindowOuter::ShouldDelayMediaFromStart() const { 3026 BrowsingContext* bc = GetBrowsingContext(); 3027 return bc && bc->Top()->GetShouldDelayMediaFromStart(); 3028 } 3029 3030 void nsPIDOMWindowOuter::NotifyResumingDelayedMedia() { 3031 RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate(); 3032 if (service) { 3033 service->NotifyResumingDelayedMedia(this); 3034 } 3035 } 3036 3037 bool nsPIDOMWindowOuter::GetAudioMuted() const { 3038 BrowsingContext* bc = GetBrowsingContext(); 3039 return bc && bc->Top()->GetMuted(); 3040 } 3041 3042 void nsPIDOMWindowOuter::RefreshMediaElementsVolume() { 3043 RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate(); 3044 if (service) { 3045 // TODO: RefreshAgentsVolume can probably be simplified further. 3046 service->RefreshAgentsVolume(this, 1.0f, GetAudioMuted()); 3047 } 3048 } 3049 3050 mozilla::dom::BrowsingContextGroup* 3051 nsPIDOMWindowOuter::GetBrowsingContextGroup() const { 3052 return mBrowsingContext ? mBrowsingContext->Group() : nullptr; 3053 } 3054 3055 Nullable<WindowProxyHolder> nsGlobalWindowOuter::GetParentOuter() { 3056 BrowsingContext* bc = GetBrowsingContext(); 3057 return bc ? bc->GetParent(IgnoreErrors()) : nullptr; 3058 } 3059 3060 /** 3061 * GetInProcessScriptableParent used to be called when a script read 3062 * window.parent. Under Fission, that is now handled by 3063 * BrowsingContext::GetParent, and the result is a WindowProxyHolder rather than 3064 * an actual global window. This method still exists for legacy callers which 3065 * relied on the old logic, and require in-process windows. However, it only 3066 * works correctly when no out-of-process frames exist between this window and 3067 * the top-level window, so it should not be used in new code. 3068 * 3069 * In contrast to GetRealParent, GetInProcessScriptableParent respects <iframe 3070 * mozbrowser> boundaries, so if |this| is contained by an <iframe 3071 * mozbrowser>, we will return |this| as its own parent. 3072 */ 3073 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetInProcessScriptableParent() { 3074 if (!mDocShell) { 3075 return nullptr; 3076 } 3077 3078 if (BrowsingContext* parentBC = GetBrowsingContext()->GetParent()) { 3079 if (nsCOMPtr<nsPIDOMWindowOuter> parent = parentBC->GetDOMWindow()) { 3080 return parent; 3081 } 3082 } 3083 return this; 3084 } 3085 3086 /** 3087 * Behavies identically to GetInProcessScriptableParent extept that it returns 3088 * null if GetInProcessScriptableParent would return this window. 3089 */ 3090 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetInProcessScriptableParentOrNull() { 3091 nsPIDOMWindowOuter* parent = GetInProcessScriptableParent(); 3092 return (nsGlobalWindowOuter::Cast(parent) == this) ? nullptr : parent; 3093 } 3094 3095 already_AddRefed<nsPIDOMWindowOuter> nsGlobalWindowOuter::GetInProcessParent() { 3096 if (!mDocShell) { 3097 return nullptr; 3098 } 3099 3100 if (auto* parentBC = GetBrowsingContext()->GetParent()) { 3101 if (auto* parent = parentBC->GetDOMWindow()) { 3102 return do_AddRef(parent); 3103 } 3104 } 3105 return do_AddRef(this); 3106 } 3107 3108 static nsresult GetTopImpl(nsGlobalWindowOuter* aWin, nsIURI* aURIBeingLoaded, 3109 nsPIDOMWindowOuter** aTop, bool aScriptable, 3110 bool aExcludingExtensionAccessibleContentFrames) { 3111 *aTop = nullptr; 3112 3113 MOZ_ASSERT_IF(aExcludingExtensionAccessibleContentFrames, !aScriptable); 3114 3115 // Walk up the parent chain. 3116 3117 nsCOMPtr<nsPIDOMWindowOuter> prevParent = aWin; 3118 nsCOMPtr<nsPIDOMWindowOuter> parent = aWin; 3119 do { 3120 if (!parent) { 3121 break; 3122 } 3123 3124 prevParent = parent; 3125 3126 if (aScriptable) { 3127 parent = parent->GetInProcessScriptableParent(); 3128 } else { 3129 parent = parent->GetInProcessParent(); 3130 } 3131 3132 if (aExcludingExtensionAccessibleContentFrames) { 3133 if (auto* p = nsGlobalWindowOuter::Cast(parent)) { 3134 nsGlobalWindowInner* currentInner = GetCurrentInnerWindowInternal(p); 3135 nsIURI* uri = prevParent->GetDocumentURI(); 3136 if (!uri) { 3137 // If our parent doesn't have a URI yet, we have a document that is in 3138 // the process of being loaded. In that case, our caller is 3139 // responsible for passing in the URI for the document that is being 3140 // loaded, so we fall back to using that URI here. 3141 uri = aURIBeingLoaded; 3142 } 3143 3144 if (currentInner && uri) { 3145 // If we find an inner window, we better find the uri for the current 3146 // window we're looking at. If we can't find it directly, it is the 3147 // responsibility of our caller to provide it to us. 3148 MOZ_DIAGNOSTIC_ASSERT(uri); 3149 3150 // If the new parent has permission to load the current page, we're 3151 // at a moz-extension:// frame which has a host permission that allows 3152 // it to load the document that we've loaded. In that case, stop at 3153 // this frame and consider it the top-level frame. 3154 // 3155 // Note that it's possible for the set of URIs accepted by 3156 // AddonAllowsLoad() to change at runtime, but we don't need to cache 3157 // the result of this check, since the important consumer of this code 3158 // (which is nsIHttpChannelInternal.topWindowURI) already caches the 3159 // result after computing it the first time. 3160 if (BasePrincipal::Cast(p->GetPrincipal()) 3161 ->AddonAllowsLoad(uri, true)) { 3162 parent = prevParent; 3163 break; 3164 } 3165 } 3166 } 3167 } 3168 3169 } while (parent != prevParent); 3170 3171 if (parent) { 3172 parent.swap(*aTop); 3173 } 3174 3175 return NS_OK; 3176 } 3177 3178 /** 3179 * GetInProcessScriptableTop used to be called when a script read window.top. 3180 * Under Fission, that is now handled by BrowsingContext::Top, and the result is 3181 * a WindowProxyHolder rather than an actual global window. This method still 3182 * exists for legacy callers which relied on the old logic, and require 3183 * in-process windows. However, it only works correctly when no out-of-process 3184 * frames exist between this window and the top-level window, so it should not 3185 * be used in new code. 3186 * 3187 * In contrast to GetRealTop, GetInProcessScriptableTop respects <iframe 3188 * mozbrowser> boundaries. If we encounter a window owned by an <iframe 3189 * mozbrowser> while walking up the window hierarchy, we'll stop and return that 3190 * window. 3191 */ 3192 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetInProcessScriptableTop() { 3193 nsCOMPtr<nsPIDOMWindowOuter> window; 3194 GetTopImpl(this, /* aURIBeingLoaded = */ nullptr, getter_AddRefs(window), 3195 /* aScriptable = */ true, 3196 /* aExcludingExtensionAccessibleContentFrames = */ false); 3197 return window.get(); 3198 } 3199 3200 already_AddRefed<nsPIDOMWindowOuter> nsGlobalWindowOuter::GetInProcessTop() { 3201 nsCOMPtr<nsPIDOMWindowOuter> window; 3202 GetTopImpl(this, /* aURIBeingLoaded = */ nullptr, getter_AddRefs(window), 3203 /* aScriptable = */ false, 3204 /* aExcludingExtensionAccessibleContentFrames = */ false); 3205 return window.forget(); 3206 } 3207 3208 already_AddRefed<nsPIDOMWindowOuter> 3209 nsGlobalWindowOuter::GetTopExcludingExtensionAccessibleContentFrames( 3210 nsIURI* aURIBeingLoaded) { 3211 // There is a parent-process equivalent of this in DocumentLoadListener.cpp 3212 // GetTopWindowExcludingExtensionAccessibleContentFrames 3213 nsCOMPtr<nsPIDOMWindowOuter> window; 3214 GetTopImpl(this, aURIBeingLoaded, getter_AddRefs(window), 3215 /* aScriptable = */ false, 3216 /* aExcludingExtensionAccessibleContentFrames = */ true); 3217 return window.forget(); 3218 } 3219 3220 void nsGlobalWindowOuter::GetContentOuter(JSContext* aCx, 3221 JS::MutableHandle<JSObject*> aRetval, 3222 CallerType aCallerType, 3223 ErrorResult& aError) { 3224 RefPtr<BrowsingContext> content = GetContentInternal(aCallerType, aError); 3225 if (aError.Failed()) { 3226 return; 3227 } 3228 3229 if (!content) { 3230 aRetval.set(nullptr); 3231 return; 3232 } 3233 3234 JS::Rooted<JS::Value> val(aCx); 3235 if (!ToJSValue(aCx, WindowProxyHolder{content}, &val)) { 3236 aError.Throw(NS_ERROR_UNEXPECTED); 3237 return; 3238 } 3239 3240 MOZ_ASSERT(val.isObjectOrNull()); 3241 aRetval.set(val.toObjectOrNull()); 3242 } 3243 3244 already_AddRefed<BrowsingContext> nsGlobalWindowOuter::GetContentInternal( 3245 CallerType aCallerType, ErrorResult& aError) { 3246 // First check for a named frame named "content" 3247 if (RefPtr<BrowsingContext> named = GetChildWindow(u"content"_ns)) { 3248 return named.forget(); 3249 } 3250 3251 // If we're in the parent process, and being called by system code, `content` 3252 // should return the current primary content frame (if it's in-process). 3253 // 3254 // We return `nullptr` if the current primary content frame is out-of-process, 3255 // rather than a remote window proxy, as that is the existing behaviour as of 3256 // bug 1597437. 3257 if (XRE_IsParentProcess() && aCallerType == CallerType::System) { 3258 nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner(); 3259 if (!treeOwner) { 3260 aError.Throw(NS_ERROR_FAILURE); 3261 return nullptr; 3262 } 3263 3264 nsCOMPtr<nsIDocShellTreeItem> primaryContent; 3265 treeOwner->GetPrimaryContentShell(getter_AddRefs(primaryContent)); 3266 if (!primaryContent) { 3267 return nullptr; 3268 } 3269 3270 return do_AddRef(primaryContent->GetBrowsingContext()); 3271 } 3272 3273 // For legacy untrusted callers we always return the same value as 3274 // `window.top` 3275 if (mDoc && aCallerType != CallerType::System) { 3276 mDoc->WarnOnceAbout(DeprecatedOperations::eWindowContentUntrusted); 3277 } 3278 3279 MOZ_ASSERT(mBrowsingContext->IsContent()); 3280 return do_AddRef(mBrowsingContext->Top()); 3281 } 3282 3283 nsresult nsGlobalWindowOuter::GetPrompter(nsIPrompt** aPrompt) { 3284 if (!mDocShell) return NS_ERROR_FAILURE; 3285 3286 nsCOMPtr<nsIPrompt> prompter(do_GetInterface(mDocShell)); 3287 NS_ENSURE_TRUE(prompter, NS_ERROR_NO_INTERFACE); 3288 3289 prompter.forget(aPrompt); 3290 return NS_OK; 3291 } 3292 3293 bool nsGlobalWindowOuter::GetClosedOuter() { 3294 // If someone called close(), or if we don't have a docshell, we're closed. 3295 return mIsClosed || !mDocShell; 3296 } 3297 3298 bool nsGlobalWindowOuter::Closed() { return GetClosedOuter(); } 3299 3300 Nullable<WindowProxyHolder> nsGlobalWindowOuter::IndexedGetterOuter( 3301 uint32_t aIndex) { 3302 BrowsingContext* bc = GetBrowsingContext(); 3303 NS_ENSURE_TRUE(bc, nullptr); 3304 3305 BrowsingContext* child = bc->NonSyntheticLightDOMChildAt(aIndex); 3306 3307 if (child) { 3308 return WindowProxyHolder(child); 3309 } 3310 return nullptr; 3311 } 3312 3313 nsIControllers* nsGlobalWindowOuter::GetControllersOuter(ErrorResult& aError) { 3314 if (!mControllers) { 3315 mControllers = new nsXULControllers(); 3316 if (!mControllers) { 3317 aError.Throw(NS_ERROR_FAILURE); 3318 return nullptr; 3319 } 3320 3321 // Add in the default controller 3322 RefPtr<nsBaseCommandController> commandController = 3323 nsBaseCommandController::CreateWindowController(); 3324 if (!commandController) { 3325 aError.Throw(NS_ERROR_FAILURE); 3326 return nullptr; 3327 } 3328 3329 mControllers->InsertControllerAt(0, commandController); 3330 commandController->SetContext(this); 3331 } 3332 3333 return mControllers; 3334 } 3335 3336 nsresult nsGlobalWindowOuter::GetControllers(nsIControllers** aResult) { 3337 FORWARD_TO_INNER(GetControllers, (aResult), NS_ERROR_UNEXPECTED); 3338 } 3339 3340 already_AddRefed<BrowsingContext> 3341 nsGlobalWindowOuter::GetOpenerBrowsingContext() { 3342 RefPtr<BrowsingContext> opener = GetBrowsingContext()->GetOpener(); 3343 MOZ_DIAGNOSTIC_ASSERT(!opener || 3344 opener->Group() == GetBrowsingContext()->Group()); 3345 if (!opener || opener->Group() != GetBrowsingContext()->Group()) { 3346 return nullptr; 3347 } 3348 3349 // Catch the case where we're chrome but the opener is not... 3350 if (nsContentUtils::LegacyIsCallerChromeOrNativeCode() && 3351 GetPrincipal() == nsContentUtils::GetSystemPrincipal()) { 3352 auto* openerWin = nsGlobalWindowOuter::Cast(opener->GetDOMWindow()); 3353 if (!openerWin || 3354 openerWin->GetPrincipal() != nsContentUtils::GetSystemPrincipal()) { 3355 return nullptr; 3356 } 3357 } 3358 3359 return opener.forget(); 3360 } 3361 3362 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetSameProcessOpener() { 3363 if (RefPtr<BrowsingContext> opener = GetOpenerBrowsingContext()) { 3364 return opener->GetDOMWindow(); 3365 } 3366 return nullptr; 3367 } 3368 3369 Nullable<WindowProxyHolder> nsGlobalWindowOuter::GetOpenerWindowOuter() { 3370 if (RefPtr<BrowsingContext> opener = GetOpenerBrowsingContext()) { 3371 return WindowProxyHolder(std::move(opener)); 3372 } 3373 return nullptr; 3374 } 3375 3376 Nullable<WindowProxyHolder> nsGlobalWindowOuter::GetOpener() { 3377 return GetOpenerWindowOuter(); 3378 } 3379 3380 void nsGlobalWindowOuter::GetStatusOuter(nsAString& aStatus) { 3381 aStatus = mStatus; 3382 } 3383 3384 void nsGlobalWindowOuter::SetStatusOuter(const nsAString& aStatus) { 3385 mStatus = aStatus; 3386 3387 // We don't support displaying window.status in the UI, so there's nothing 3388 // left to do here. 3389 } 3390 3391 void nsGlobalWindowOuter::GetNameOuter(nsAString& aName) { 3392 if (mDocShell) { 3393 mDocShell->GetName(aName); 3394 } 3395 } 3396 3397 void nsGlobalWindowOuter::SetNameOuter(const nsAString& aName, 3398 mozilla::ErrorResult& aError) { 3399 if (mDocShell) { 3400 aError = mDocShell->SetName(aName); 3401 } 3402 } 3403 3404 // NOTE: The idea of this function is that it should return the same as 3405 // nsPresContext::CSSToDeviceScale() if it was in aWindow synchronously. For 3406 // that, we use the UnscaledDevicePixelsPerCSSPixel() (which contains the device 3407 // scale and the OS zoom scale) and then account for the browsing context full 3408 // zoom. See the declaration of this function for context about why this is 3409 // needed. 3410 CSSToLayoutDeviceScale nsGlobalWindowOuter::CSSToDevScaleForBaseWindow( 3411 nsIBaseWindow* aWindow) { 3412 MOZ_ASSERT(aWindow); 3413 auto scale = aWindow->UnscaledDevicePixelsPerCSSPixel(); 3414 if (mBrowsingContext) { 3415 scale.scale *= mBrowsingContext->FullZoom(); 3416 } 3417 return scale; 3418 } 3419 3420 nsresult nsGlobalWindowOuter::GetInnerSize(CSSSize& aSize) { 3421 if (mDoc && mDoc->IsTopLevelContentDocument() && 3422 nsLayoutUtils::ShouldHandleMetaViewport(mDoc)) { 3423 // Window.inner{Width,Height} depend on minimum-scale size and to get the 3424 // up-to-date minimum-scale size we need to flush layout. 3425 FlushPendingNotifications(FlushType::Layout); 3426 } else { 3427 EnsureSizeAndPositionUpToDate(); 3428 } 3429 3430 NS_ENSURE_STATE(mDocShell); 3431 3432 RefPtr<PresShell> presShell = mDocShell->GetPresShell(); 3433 if (!presShell) { 3434 aSize = {}; 3435 return NS_OK; 3436 } 3437 3438 // Whether or not the css viewport has been overridden, we can get the 3439 // correct value by looking at the visible area of the presContext. 3440 presShell->FlushDelayedResize(); 3441 3442 nsPresContext* pc = presShell->GetPresContext(); 3443 if (NS_WARN_IF(!pc)) { 3444 aSize = {}; 3445 return NS_OK; 3446 } 3447 3448 nsSize innerSize = presShell->GetInnerSize(); 3449 if (pc->GetDynamicToolbarState() == DynamicToolbarState::Collapsed) { 3450 innerSize = nsLayoutUtils::ExpandHeightForViewportUnits(pc, innerSize); 3451 } 3452 3453 aSize = CSSPixel::FromAppUnits(innerSize); 3454 3455 switch (StaticPrefs::dom_innerSize_rounding()) { 3456 case 1: 3457 aSize.width = std::roundf(aSize.width); 3458 aSize.height = std::roundf(aSize.height); 3459 break; 3460 case 2: 3461 aSize.width = std::truncf(aSize.width); 3462 aSize.height = std::truncf(aSize.height); 3463 break; 3464 default: 3465 break; 3466 } 3467 3468 return NS_OK; 3469 } 3470 3471 double nsGlobalWindowOuter::GetInnerWidthOuter(ErrorResult& aError) { 3472 CSSSize size; 3473 aError = GetInnerSize(size); 3474 return size.width; 3475 } 3476 3477 nsresult nsGlobalWindowOuter::GetInnerWidth(double* aInnerWidth) { 3478 FORWARD_TO_INNER_WITH_STRONG_REF(GetInnerWidth, (aInnerWidth), 3479 NS_ERROR_UNEXPECTED); 3480 } 3481 3482 double nsGlobalWindowOuter::GetInnerHeightOuter(ErrorResult& aError) { 3483 CSSSize size; 3484 aError = GetInnerSize(size); 3485 return size.height; 3486 } 3487 3488 nsresult nsGlobalWindowOuter::GetInnerHeight(double* aInnerHeight) { 3489 FORWARD_TO_INNER_WITH_STRONG_REF(GetInnerHeight, (aInnerHeight), 3490 NS_ERROR_UNEXPECTED); 3491 } 3492 3493 CSSIntSize nsGlobalWindowOuter::GetOuterSize(CallerType aCallerType, 3494 ErrorResult& aError) { 3495 if (nsIGlobalObject::ShouldResistFingerprinting(aCallerType, 3496 RFPTarget::WindowOuterSize)) { 3497 if (BrowsingContext* bc = GetBrowsingContext()) { 3498 return bc->Top()->GetTopInnerSizeForRFP(); 3499 } 3500 return {}; 3501 } 3502 3503 // Windows showing documents in RDM panes and any subframes within them 3504 // return the simulated device size. 3505 if (mDoc) { 3506 Maybe<CSSIntSize> deviceSize = GetRDMDeviceSize(*mDoc); 3507 if (deviceSize.isSome()) { 3508 return *deviceSize; 3509 } 3510 } 3511 3512 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow(); 3513 if (!treeOwnerAsWin) { 3514 aError.Throw(NS_ERROR_FAILURE); 3515 return {}; 3516 } 3517 3518 return RoundedToInt(treeOwnerAsWin->GetSize() / 3519 CSSToDevScaleForBaseWindow(treeOwnerAsWin)); 3520 } 3521 3522 int32_t nsGlobalWindowOuter::GetOuterWidthOuter(CallerType aCallerType, 3523 ErrorResult& aError) { 3524 return GetOuterSize(aCallerType, aError).width; 3525 } 3526 3527 int32_t nsGlobalWindowOuter::GetOuterHeightOuter(CallerType aCallerType, 3528 ErrorResult& aError) { 3529 return GetOuterSize(aCallerType, aError).height; 3530 } 3531 3532 CSSPoint nsGlobalWindowOuter::ScreenEdgeSlop() { 3533 if (NS_WARN_IF(!mDocShell)) { 3534 return {}; 3535 } 3536 RefPtr<nsPresContext> pc = mDocShell->GetPresContext(); 3537 if (NS_WARN_IF(!pc)) { 3538 return {}; 3539 } 3540 nsCOMPtr<nsIWidget> widget = GetMainWidget(); 3541 if (NS_WARN_IF(!widget)) { 3542 return {}; 3543 } 3544 LayoutDeviceIntPoint pt = widget->GetScreenEdgeSlop(); 3545 auto auPoint = 3546 LayoutDeviceIntPoint::ToAppUnits(pt, pc->AppUnitsPerDevPixel()); 3547 return CSSPoint::FromAppUnits(auPoint); 3548 } 3549 3550 CSSIntPoint nsGlobalWindowOuter::GetScreenXY(CallerType aCallerType, 3551 ErrorResult& aError) { 3552 // When resisting fingerprinting, always return (0,0) 3553 if (nsIGlobalObject::ShouldResistFingerprinting(aCallerType, 3554 RFPTarget::WindowScreenXY)) { 3555 return CSSIntPoint(0, 0); 3556 } 3557 3558 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow(); 3559 if (!treeOwnerAsWin) { 3560 aError.Throw(NS_ERROR_FAILURE); 3561 return CSSIntPoint(0, 0); 3562 } 3563 3564 LayoutDeviceIntPoint windowPos; 3565 aError = treeOwnerAsWin->GetPosition(&windowPos.x.value, &windowPos.y.value); 3566 3567 RefPtr<nsPresContext> presContext = mDocShell->GetPresContext(); 3568 if (!presContext) { 3569 // XXX Fishy LayoutDevice to CSS conversion? 3570 return CSSIntPoint(windowPos.x, windowPos.y); 3571 } 3572 3573 nsDeviceContext* context = presContext->DeviceContext(); 3574 auto windowPosAppUnits = LayoutDeviceIntPoint::ToAppUnits( 3575 windowPos, context->AppUnitsPerDevPixel()); 3576 return CSSIntPoint::FromAppUnitsRounded(windowPosAppUnits); 3577 } 3578 3579 int32_t nsGlobalWindowOuter::GetScreenXOuter(CallerType aCallerType, 3580 ErrorResult& aError) { 3581 return GetScreenXY(aCallerType, aError).x; 3582 } 3583 3584 nsRect nsGlobalWindowOuter::GetInnerScreenRect() { 3585 if (!mDocShell) { 3586 return nsRect(); 3587 } 3588 3589 EnsureSizeAndPositionUpToDate(); 3590 3591 if (!mDocShell) { 3592 return nsRect(); 3593 } 3594 3595 PresShell* presShell = mDocShell->GetPresShell(); 3596 if (!presShell) { 3597 return nsRect(); 3598 } 3599 nsIFrame* rootFrame = presShell->GetRootFrame(); 3600 if (!rootFrame) { 3601 return nsRect(); 3602 } 3603 3604 return rootFrame->GetScreenRectInAppUnits(); 3605 } 3606 3607 Maybe<CSSIntSize> nsGlobalWindowOuter::GetRDMDeviceSize( 3608 const Document& aDocument) { 3609 // RDM device size should reflect the simulated device resolution, and 3610 // be independent of any full zoom or resolution zoom applied to the 3611 // content. To get this value, we get the "unscaled" browser child size, 3612 // and divide by the full zoom. "Unscaled" in this case means unscaled 3613 // from device to screen but it has been affected (multiplied) by the 3614 // full zoom and we need to compensate for that. 3615 MOZ_RELEASE_ASSERT(NS_IsMainThread()); 3616 3617 // Bug 1576256: This does not work for cross-process subframes. 3618 const Document* topInProcessContentDoc = 3619 aDocument.GetTopLevelContentDocumentIfSameProcess(); 3620 BrowsingContext* bc = topInProcessContentDoc 3621 ? topInProcessContentDoc->GetBrowsingContext() 3622 : nullptr; 3623 if (bc && bc->InRDMPane()) { 3624 nsIDocShell* docShell = topInProcessContentDoc->GetDocShell(); 3625 if (docShell) { 3626 nsPresContext* presContext = docShell->GetPresContext(); 3627 if (presContext) { 3628 nsCOMPtr<nsIBrowserChild> child = docShell->GetBrowserChild(); 3629 if (child) { 3630 // We intentionally use GetFullZoom here instead of 3631 // GetDeviceFullZoom, because the unscaledInnerSize is based 3632 // on the full zoom and not the device full zoom (which is 3633 // rounded to result in integer device pixels). 3634 float zoom = presContext->GetFullZoom(); 3635 BrowserChild* bc = static_cast<BrowserChild*>(child.get()); 3636 CSSSize unscaledSize = bc->GetUnscaledInnerSize(); 3637 return Some(CSSIntSize(gfx::RoundedToInt(unscaledSize / zoom))); 3638 } 3639 } 3640 } 3641 } 3642 return Nothing(); 3643 } 3644 3645 float nsGlobalWindowOuter::GetMozInnerScreenXOuter(CallerType aCallerType) { 3646 // When resisting fingerprinting, always return 0. 3647 if (nsIGlobalObject::ShouldResistFingerprinting( 3648 aCallerType, RFPTarget::WindowInnerScreenXY)) { 3649 return 0.0; 3650 } 3651 3652 nsRect r = GetInnerScreenRect(); 3653 return nsPresContext::AppUnitsToFloatCSSPixels(r.x); 3654 } 3655 3656 float nsGlobalWindowOuter::GetMozInnerScreenYOuter(CallerType aCallerType) { 3657 // Return 0 to prevent fingerprinting. 3658 if (nsIGlobalObject::ShouldResistFingerprinting( 3659 aCallerType, RFPTarget::WindowInnerScreenXY)) { 3660 return 0.0; 3661 } 3662 3663 nsRect r = GetInnerScreenRect(); 3664 return nsPresContext::AppUnitsToFloatCSSPixels(r.y); 3665 } 3666 3667 int32_t nsGlobalWindowOuter::GetScreenYOuter(CallerType aCallerType, 3668 ErrorResult& aError) { 3669 return GetScreenXY(aCallerType, aError).y; 3670 } 3671 3672 // NOTE: Arguments to this function should have values scaled to 3673 // CSS pixels, not device pixels. 3674 void nsGlobalWindowOuter::CheckSecurityWidthAndHeight(int32_t* aWidth, 3675 int32_t* aHeight, 3676 CallerType aCallerType) { 3677 if (aCallerType != CallerType::System) { 3678 // if attempting to resize the window, hide any open popups 3679 nsContentUtils::HidePopupsInDocument(mDoc); 3680 } 3681 3682 // This one is easy. Just ensure the variable is greater than 100; 3683 if ((aWidth && *aWidth < 100) || (aHeight && *aHeight < 100)) { 3684 // Check security state for use in determing window dimensions 3685 3686 if (aCallerType != CallerType::System) { 3687 // sec check failed 3688 if (aWidth && *aWidth < 100) { 3689 *aWidth = 100; 3690 } 3691 if (aHeight && *aHeight < 100) { 3692 *aHeight = 100; 3693 } 3694 } 3695 } 3696 } 3697 3698 // NOTE: Arguments to this function should have values in app units 3699 void nsGlobalWindowOuter::SetCSSViewportWidthAndHeight(nscoord aInnerWidth, 3700 nscoord aInnerHeight) { 3701 RefPtr<nsPresContext> presContext = mDocShell->GetPresContext(); 3702 3703 nsRect shellArea = presContext->GetVisibleArea(); 3704 shellArea.SetHeight(aInnerHeight); 3705 shellArea.SetWidth(aInnerWidth); 3706 3707 // FIXME(emilio): This doesn't seem to be ok, this doesn't reflow or 3708 // anything... Should go through PresShell::ResizeReflow. 3709 // 3710 // But I don't think this can be reached by content, as we don't allow to set 3711 // inner{Width,Height}. 3712 presContext->SetVisibleArea(shellArea); 3713 } 3714 3715 // NOTE: Arguments to this function should have values scaled to 3716 // CSS pixels, not device pixels. 3717 void nsGlobalWindowOuter::CheckSecurityLeftAndTop(int32_t* aLeft, int32_t* aTop, 3718 CallerType aCallerType) { 3719 // This one is harder. We have to get the screen size and window dimensions. 3720 3721 // Check security state for use in determing window dimensions 3722 3723 if (aCallerType != CallerType::System) { 3724 // if attempting to move the window, hide any open popups 3725 nsContentUtils::HidePopupsInDocument(mDoc); 3726 3727 if (nsGlobalWindowOuter* rootWindow = 3728 nsGlobalWindowOuter::Cast(GetPrivateRoot())) { 3729 rootWindow->FlushPendingNotifications(FlushType::Layout); 3730 } 3731 3732 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow(); 3733 3734 RefPtr<nsScreen> screen = GetScreen(); 3735 3736 if (treeOwnerAsWin && screen) { 3737 CSSToLayoutDeviceScale scale = CSSToDevScaleForBaseWindow(treeOwnerAsWin); 3738 CSSIntRect winRect = 3739 CSSIntRect::Round(treeOwnerAsWin->GetPositionAndSize() / scale); 3740 3741 // Get the screen dimensions 3742 // XXX This should use nsIScreenManager once it's fully fleshed out. 3743 int32_t screenLeft = screen->AvailLeft(); 3744 int32_t screenWidth = screen->AvailWidth(); 3745 int32_t screenHeight = screen->AvailHeight(); 3746 #if defined(XP_MACOSX) 3747 /* The mac's coordinate system is different from the assumed Windows' 3748 system. It offsets by the height of the menubar so that a window 3749 placed at (0,0) will be entirely visible. Unfortunately that 3750 correction is made elsewhere (in Widget) and the meaning of 3751 the Avail... coordinates is overloaded. Here we allow a window 3752 to be placed at (0,0) because it does make sense to do so. 3753 */ 3754 int32_t screenTop = screen->Top(); 3755 #else 3756 int32_t screenTop = screen->AvailTop(); 3757 #endif 3758 3759 if (aLeft) { 3760 if (screenLeft + screenWidth < *aLeft + winRect.width) 3761 *aLeft = screenLeft + screenWidth - winRect.width; 3762 if (screenLeft > *aLeft) *aLeft = screenLeft; 3763 } 3764 if (aTop) { 3765 if (screenTop + screenHeight < *aTop + winRect.height) 3766 *aTop = screenTop + screenHeight - winRect.height; 3767 if (screenTop > *aTop) *aTop = screenTop; 3768 } 3769 } else { 3770 if (aLeft) *aLeft = 0; 3771 if (aTop) *aTop = 0; 3772 } 3773 } 3774 } 3775 3776 int32_t nsGlobalWindowOuter::GetScrollBoundaryOuter(Side aSide) { 3777 FlushPendingNotifications(FlushType::Layout); 3778 if (ScrollContainerFrame* sf = GetScrollContainerFrame()) { 3779 return nsPresContext::AppUnitsToIntCSSPixels( 3780 sf->GetScrollRange().Edge(aSide)); 3781 } 3782 return 0; 3783 } 3784 3785 CSSPoint nsGlobalWindowOuter::GetScrollXY(bool aDoFlush) { 3786 if (aDoFlush) { 3787 FlushPendingNotifications(FlushType::Layout); 3788 } else { 3789 EnsureSizeAndPositionUpToDate(); 3790 } 3791 3792 ScrollContainerFrame* sf = GetScrollContainerFrame(); 3793 if (!sf) { 3794 return CSSIntPoint(0, 0); 3795 } 3796 3797 nsPoint scrollPos = sf->GetScrollPosition(); 3798 if (scrollPos != nsPoint(0, 0) && !aDoFlush) { 3799 // Oh, well. This is the expensive case -- the window is scrolled and we 3800 // didn't actually flush yet. Repeat, but with a flush, since the content 3801 // may get shorter and hence our scroll position may decrease. 3802 return GetScrollXY(true); 3803 } 3804 3805 return CSSPoint::FromAppUnits(scrollPos); 3806 } 3807 3808 double nsGlobalWindowOuter::GetScrollXOuter() { return GetScrollXY(false).x; } 3809 3810 double nsGlobalWindowOuter::GetScrollYOuter() { return GetScrollXY(false).y; } 3811 3812 uint32_t nsGlobalWindowOuter::Length() { 3813 BrowsingContext* bc = GetBrowsingContext(); 3814 NS_ENSURE_TRUE(bc, 0); 3815 return bc->NonSyntheticLightDOMChildrenCount(); 3816 } 3817 3818 Nullable<WindowProxyHolder> nsGlobalWindowOuter::GetTopOuter() { 3819 BrowsingContext* bc = GetBrowsingContext(); 3820 return bc ? bc->GetTop(IgnoreErrors()) : nullptr; 3821 } 3822 3823 already_AddRefed<BrowsingContext> nsGlobalWindowOuter::GetChildWindow( 3824 const nsAString& aName) { 3825 NS_ENSURE_TRUE(mBrowsingContext, nullptr); 3826 NS_ENSURE_TRUE(mInnerWindow, nullptr); 3827 NS_ENSURE_TRUE(mInnerWindow->GetWindowGlobalChild(), nullptr); 3828 3829 return do_AddRef(mBrowsingContext->FindChildWithName( 3830 aName, *mInnerWindow->GetWindowGlobalChild())); 3831 } 3832 3833 bool nsGlobalWindowOuter::DispatchCustomEvent( 3834 const nsAString& aEventName, ChromeOnlyDispatch aChromeOnlyDispatch) { 3835 bool defaultActionEnabled = true; 3836 3837 if (aChromeOnlyDispatch == ChromeOnlyDispatch::eYes) { 3838 nsContentUtils::DispatchEventOnlyToChrome(mDoc, this, aEventName, 3839 CanBubble::eYes, Cancelable::eYes, 3840 &defaultActionEnabled); 3841 } else { 3842 nsContentUtils::DispatchTrustedEvent(mDoc, this, aEventName, 3843 CanBubble::eYes, Cancelable::eYes, 3844 &defaultActionEnabled); 3845 } 3846 3847 return defaultActionEnabled; 3848 } 3849 3850 bool nsGlobalWindowOuter::WindowExists(const nsAString& aName, 3851 bool aForceNoOpener, 3852 bool aLookForCallerOnJSStack) { 3853 MOZ_ASSERT(mDocShell, "Must have docshell"); 3854 3855 if (aForceNoOpener) { 3856 return aName.LowerCaseEqualsLiteral("_self") || 3857 aName.LowerCaseEqualsLiteral("_top") || 3858 aName.LowerCaseEqualsLiteral("_parent"); 3859 } 3860 3861 if (WindowGlobalChild* wgc = mInnerWindow->GetWindowGlobalChild()) { 3862 return wgc->FindBrowsingContextWithName(aName, aLookForCallerOnJSStack); 3863 } 3864 return false; 3865 } 3866 3867 already_AddRefed<nsIWidget> nsGlobalWindowOuter::GetMainWidget() { 3868 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow(); 3869 3870 nsCOMPtr<nsIWidget> widget; 3871 3872 if (treeOwnerAsWin) { 3873 treeOwnerAsWin->GetMainWidget(getter_AddRefs(widget)); 3874 } 3875 3876 return widget.forget(); 3877 } 3878 3879 nsIWidget* nsGlobalWindowOuter::GetNearestWidget() const { 3880 nsIDocShell* docShell = GetDocShell(); 3881 if (!docShell) { 3882 return nullptr; 3883 } 3884 PresShell* presShell = docShell->GetPresShell(); 3885 if (!presShell) { 3886 return nullptr; 3887 } 3888 nsIFrame* rootFrame = presShell->GetRootFrame(); 3889 if (!rootFrame) { 3890 return nullptr; 3891 } 3892 return rootFrame->GetNearestWidget(); 3893 } 3894 3895 void nsGlobalWindowOuter::SetFullscreenOuter(bool aFullscreen, 3896 mozilla::ErrorResult& aError) { 3897 aError = 3898 SetFullscreenInternal(FullscreenReason::ForFullscreenMode, aFullscreen); 3899 } 3900 3901 nsresult nsGlobalWindowOuter::SetFullScreen(bool aFullscreen) { 3902 return SetFullscreenInternal(FullscreenReason::ForFullscreenMode, 3903 aFullscreen); 3904 } 3905 3906 static void FinishDOMFullscreenChange(Document* aDoc, bool aInDOMFullscreen) { 3907 if (aInDOMFullscreen) { 3908 // Ask the document to handle any pending DOM fullscreen change. 3909 if (!Document::HandlePendingFullscreenRequests(aDoc)) { 3910 // If we don't end up having anything in fullscreen, 3911 // async request exiting fullscreen. 3912 Document::AsyncExitFullscreen(aDoc); 3913 } 3914 } else { 3915 // If the window is leaving fullscreen state, also ask the document 3916 // to exit from DOM Fullscreen. 3917 Document::ExitFullscreenInDocTree(aDoc); 3918 } 3919 } 3920 3921 struct FullscreenTransitionDuration { 3922 // The unit of the durations is millisecond 3923 uint16_t mFadeIn = 0; 3924 uint16_t mFadeOut = 0; 3925 bool IsSuppressed() const { return mFadeIn == 0 && mFadeOut == 0; } 3926 }; 3927 3928 static void GetFullscreenTransitionDuration( 3929 bool aEnterFullscreen, FullscreenTransitionDuration* aDuration) { 3930 const char* pref = aEnterFullscreen 3931 ? "full-screen-api.transition-duration.enter" 3932 : "full-screen-api.transition-duration.leave"; 3933 nsAutoCString prefValue; 3934 Preferences::GetCString(pref, prefValue); 3935 if (!prefValue.IsEmpty()) { 3936 sscanf(prefValue.get(), "%hu%hu", &aDuration->mFadeIn, 3937 &aDuration->mFadeOut); 3938 } 3939 } 3940 3941 class FullscreenTransitionTask : public Runnable { 3942 public: 3943 FullscreenTransitionTask(const FullscreenTransitionDuration& aDuration, 3944 nsGlobalWindowOuter* aWindow, bool aFullscreen, 3945 nsIWidget* aWidget, nsISupports* aTransitionData) 3946 : mozilla::Runnable("FullscreenTransitionTask"), 3947 mWindow(aWindow), 3948 mWidget(aWidget), 3949 mTransitionData(aTransitionData), 3950 mDuration(aDuration), 3951 mStage(eBeforeToggle), 3952 mFullscreen(aFullscreen) {} 3953 3954 NS_IMETHOD Run() override; 3955 3956 private: 3957 ~FullscreenTransitionTask() override = default; 3958 3959 /** 3960 * The flow of fullscreen transition: 3961 * 3962 * parent process | child process 3963 * ---------------------------------------------------------------- 3964 * 3965 * | request/exit fullscreen 3966 * <-----| 3967 * BeforeToggle stage | 3968 * | 3969 * ToggleFullscreen stage *1 |-----> 3970 * | HandleFullscreenRequests 3971 * | 3972 * <-----| MozAfterPaint event 3973 * AfterToggle stage *2 | 3974 * | 3975 * End stage | 3976 * 3977 * Note we also start a timer at *1 so that if we don't get MozAfterPaint 3978 * from the child process in time, we continue going to *2. 3979 */ 3980 enum Stage { 3981 // BeforeToggle stage happens before we enter or leave fullscreen 3982 // state. In this stage, the task triggers the pre-toggle fullscreen 3983 // transition on the widget. 3984 eBeforeToggle, 3985 // ToggleFullscreen stage actually executes the fullscreen toggle, 3986 // and wait for the next paint on the content to continue. 3987 eToggleFullscreen, 3988 // AfterToggle stage happens after we toggle the fullscreen state. 3989 // In this stage, the task triggers the post-toggle fullscreen 3990 // transition on the widget. 3991 eAfterToggle, 3992 // End stage is triggered after the final transition finishes. 3993 eEnd 3994 }; 3995 3996 class Observer final : public nsIObserver, public nsINamed { 3997 public: 3998 NS_DECL_ISUPPORTS 3999 NS_DECL_NSIOBSERVER 4000 NS_DECL_NSINAMED 4001 4002 explicit Observer(FullscreenTransitionTask* aTask) : mTask(aTask) {} 4003 4004 private: 4005 ~Observer() = default; 4006 4007 RefPtr<FullscreenTransitionTask> mTask; 4008 }; 4009 4010 static const char* const kPaintedTopic; 4011 4012 RefPtr<nsGlobalWindowOuter> mWindow; 4013 nsCOMPtr<nsIWidget> mWidget; 4014 nsCOMPtr<nsITimer> mTimer; 4015 nsCOMPtr<nsISupports> mTransitionData; 4016 4017 TimeStamp mFullscreenChangeStartTime; 4018 FullscreenTransitionDuration mDuration; 4019 Stage mStage; 4020 bool mFullscreen; 4021 }; 4022 4023 const char* const FullscreenTransitionTask::kPaintedTopic = 4024 "fullscreen-painted"; 4025 4026 NS_IMETHODIMP 4027 FullscreenTransitionTask::Run() { 4028 Stage stage = mStage; 4029 mStage = Stage(mStage + 1); 4030 if (MOZ_UNLIKELY(mWidget->Destroyed())) { 4031 // If the widget has been destroyed before we get here, don't try to 4032 // do anything more. Just let it go and release ourselves. 4033 NS_WARNING("The widget to fullscreen has been destroyed"); 4034 mWindow->mIsInFullScreenTransition = false; 4035 return NS_OK; 4036 } 4037 if (stage == eBeforeToggle) { 4038 PROFILER_MARKER_UNTYPED("Fullscreen transition start", DOM); 4039 4040 mWindow->mIsInFullScreenTransition = true; 4041 4042 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); 4043 NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE); 4044 obs->NotifyObservers(nullptr, "fullscreen-transition-start", nullptr); 4045 4046 mWidget->PerformFullscreenTransition(nsIWidget::eBeforeFullscreenToggle, 4047 mDuration.mFadeIn, mTransitionData, 4048 this); 4049 } else if (stage == eToggleFullscreen) { 4050 PROFILER_MARKER_UNTYPED("Fullscreen toggle start", DOM); 4051 mFullscreenChangeStartTime = TimeStamp::Now(); 4052 // Toggle the fullscreen state on the widget 4053 if (!mWindow->SetWidgetFullscreen(FullscreenReason::ForFullscreenAPI, 4054 mFullscreen, mWidget)) { 4055 // Fail to setup the widget, call FinishFullscreenChange to 4056 // complete fullscreen change directly. 4057 mWindow->FinishFullscreenChange(mFullscreen); 4058 } 4059 // Set observer for the next content paint. 4060 nsCOMPtr<nsIObserver> observer = new Observer(this); 4061 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); 4062 obs->AddObserver(observer, kPaintedTopic, false); 4063 // There are several edge cases where we may never get the paint 4064 // notification, including: 4065 // 1. the window/tab is closed before the next paint; 4066 // 2. the user has switched to another tab before we get here. 4067 // Completely fixing those cases seems to be tricky, and since they 4068 // should rarely happen, it probably isn't worth to fix. Hence we 4069 // simply add a timeout here to ensure we never hang forever. 4070 // In addition, if the page is complicated or the machine is less 4071 // powerful, layout could take a long time, in which case, staying 4072 // in black screen for that long could hurt user experience even 4073 // more than exposing an intermediate state. 4074 uint32_t timeout = 4075 Preferences::GetUint("full-screen-api.transition.timeout", 1000); 4076 NS_NewTimerWithObserver(getter_AddRefs(mTimer), observer, timeout, 4077 nsITimer::TYPE_ONE_SHOT); 4078 } else if (stage == eAfterToggle) { 4079 glean::dom::fullscreen_transition_black.AccumulateRawDuration( 4080 TimeStamp::Now() - mFullscreenChangeStartTime); 4081 mWidget->PerformFullscreenTransition(nsIWidget::eAfterFullscreenToggle, 4082 mDuration.mFadeOut, mTransitionData, 4083 this); 4084 } else if (stage == eEnd) { 4085 PROFILER_MARKER_UNTYPED("Fullscreen transition end", DOM); 4086 4087 mWindow->mIsInFullScreenTransition = false; 4088 4089 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); 4090 NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE); 4091 obs->NotifyObservers(nullptr, "fullscreen-transition-end", nullptr); 4092 4093 mWidget->CleanupFullscreenTransition(); 4094 } 4095 return NS_OK; 4096 } 4097 4098 NS_IMPL_ISUPPORTS(FullscreenTransitionTask::Observer, nsIObserver, nsINamed) 4099 4100 NS_IMETHODIMP 4101 FullscreenTransitionTask::Observer::Observe(nsISupports* aSubject, 4102 const char* aTopic, 4103 const char16_t* aData) { 4104 bool shouldContinue = false; 4105 if (strcmp(aTopic, FullscreenTransitionTask::kPaintedTopic) == 0) { 4106 nsCOMPtr<nsPIDOMWindowInner> win(do_QueryInterface(aSubject)); 4107 nsCOMPtr<nsIWidget> widget = 4108 win ? nsGlobalWindowInner::Cast(win)->GetMainWidget() : nullptr; 4109 if (widget == mTask->mWidget) { 4110 // The paint notification arrives first. Cancel the timer. 4111 mTask->mTimer->Cancel(); 4112 shouldContinue = true; 4113 PROFILER_MARKER_UNTYPED("Fullscreen toggle end", DOM); 4114 } 4115 } else { 4116 #ifdef DEBUG 4117 MOZ_ASSERT(strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC) == 0, 4118 "Should only get fullscreen-painted or timer-callback"); 4119 nsCOMPtr<nsITimer> timer(do_QueryInterface(aSubject)); 4120 MOZ_ASSERT(timer && timer == mTask->mTimer, 4121 "Should only trigger this with the timer the task created"); 4122 #endif 4123 shouldContinue = true; 4124 PROFILER_MARKER_UNTYPED("Fullscreen toggle timeout", DOM); 4125 } 4126 if (shouldContinue) { 4127 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); 4128 obs->RemoveObserver(this, kPaintedTopic); 4129 mTask->mTimer = nullptr; 4130 mTask->Run(); 4131 } 4132 return NS_OK; 4133 } 4134 4135 NS_IMETHODIMP 4136 FullscreenTransitionTask::Observer::GetName(nsACString& aName) { 4137 aName.AssignLiteral("FullscreenTransitionTask"); 4138 return NS_OK; 4139 } 4140 4141 static bool MakeWidgetFullscreen(nsGlobalWindowOuter* aWindow, 4142 FullscreenReason aReason, bool aFullscreen) { 4143 nsCOMPtr<nsIWidget> widget = aWindow->GetMainWidget(); 4144 if (!widget) { 4145 return false; 4146 } 4147 4148 FullscreenTransitionDuration duration; 4149 bool performTransition = false; 4150 nsCOMPtr<nsISupports> transitionData; 4151 if (aReason == FullscreenReason::ForFullscreenAPI) { 4152 GetFullscreenTransitionDuration(aFullscreen, &duration); 4153 if (!duration.IsSuppressed()) { 4154 performTransition = widget->PrepareForFullscreenTransition( 4155 getter_AddRefs(transitionData)); 4156 } 4157 } 4158 4159 if (!performTransition) { 4160 return aWindow->SetWidgetFullscreen(aReason, aFullscreen, widget); 4161 } 4162 4163 nsCOMPtr<nsIRunnable> task = new FullscreenTransitionTask( 4164 duration, aWindow, aFullscreen, widget, transitionData); 4165 task->Run(); 4166 return true; 4167 } 4168 4169 nsresult nsGlobalWindowOuter::ProcessWidgetFullscreenRequest( 4170 FullscreenReason aReason, bool aFullscreen) { 4171 mInProcessFullscreenRequest.emplace(aReason, aFullscreen); 4172 4173 // Prevent chrome documents which are still loading from resizing 4174 // the window after we set fullscreen mode. 4175 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow(); 4176 nsCOMPtr<nsIAppWindow> appWin(do_GetInterface(treeOwnerAsWin)); 4177 if (aFullscreen && appWin) { 4178 appWin->SetIntrinsicallySized(false); 4179 } 4180 4181 // Sometimes we don't want the top-level widget to actually go fullscreen: 4182 // - in the B2G desktop client, we don't want the emulated screen dimensions 4183 // to appear to increase when entering fullscreen mode; we just want the 4184 // content to fill the entire client area of the emulator window. 4185 // - in FxR Desktop, we don't want fullscreen to take over the monitor, but 4186 // instead we want fullscreen to fill the FxR window in the the headset. 4187 if (!StaticPrefs::full_screen_api_ignore_widgets() && 4188 !mForceFullScreenInWidget) { 4189 if (MakeWidgetFullscreen(this, aReason, aFullscreen)) { 4190 // The rest of code for switching fullscreen is in nsGlobalWindowOuter:: 4191 // FinishFullscreenChange() which will be called after sizemodechange 4192 // event is dispatched. 4193 return NS_OK; 4194 } 4195 } 4196 4197 #if defined(NIGHTLY_BUILD) && defined(XP_WIN) 4198 if (FxRWindowManager::GetInstance()->IsFxRWindow(mWindowID)) { 4199 mozilla::gfx::VRShMem shmem(nullptr, true /*aRequiresMutex*/); 4200 shmem.SendFullscreenState(mWindowID, aFullscreen); 4201 } 4202 #endif // NIGHTLY_BUILD && XP_WIN 4203 FinishFullscreenChange(aFullscreen); 4204 return NS_OK; 4205 } 4206 4207 nsresult nsGlobalWindowOuter::SetFullscreenInternal(FullscreenReason aReason, 4208 bool aFullscreen) { 4209 MOZ_ASSERT(nsContentUtils::IsSafeToRunScript(), 4210 "Requires safe to run script as it " 4211 "may call FinishDOMFullscreenChange"); 4212 4213 NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE); 4214 4215 MOZ_ASSERT( 4216 aReason != FullscreenReason::ForForceExitFullscreen || !aFullscreen, 4217 "FullscreenReason::ForForceExitFullscreen can " 4218 "only be used with exiting fullscreen"); 4219 4220 // Only chrome can change our fullscreen mode. Otherwise, the state 4221 // can only be changed for DOM fullscreen. 4222 if (aReason == FullscreenReason::ForFullscreenMode && 4223 !nsContentUtils::LegacyIsCallerChromeOrNativeCode()) { 4224 return NS_OK; 4225 } 4226 4227 // Element.requestFullscreen() is already blocked, but also block 4228 // fullscreening for other callers, especially the chrome window. 4229 if (GetBrowsingContext()->Top()->GetIsDocumentPiP()) { 4230 return NS_OK; 4231 } 4232 4233 // SetFullscreen needs to be called on the root window, so get that 4234 // via the DocShell tree, and if we are not already the root, 4235 // call SetFullscreen on that window instead. 4236 nsCOMPtr<nsIDocShellTreeItem> rootItem; 4237 mDocShell->GetInProcessRootTreeItem(getter_AddRefs(rootItem)); 4238 nsCOMPtr<nsPIDOMWindowOuter> window = 4239 rootItem ? rootItem->GetWindow() : nullptr; 4240 if (!window) return NS_ERROR_FAILURE; 4241 if (rootItem != mDocShell) 4242 return window->SetFullscreenInternal(aReason, aFullscreen); 4243 4244 // make sure we don't try to set full screen on a non-chrome window, 4245 // which might happen in embedding world 4246 if (mDocShell->ItemType() != nsIDocShellTreeItem::typeChrome) 4247 return NS_ERROR_FAILURE; 4248 4249 // FullscreenReason::ForForceExitFullscreen can only be used with exiting 4250 // fullscreen 4251 MOZ_ASSERT_IF( 4252 mFullscreen.isSome(), 4253 mFullscreen.value() != FullscreenReason::ForForceExitFullscreen); 4254 4255 // If we are already in full screen mode, just return, we don't care about the 4256 // reason here, because, 4257 // - If we are in fullscreen mode due to browser fullscreen mode, requesting 4258 // DOM fullscreen does not change anything. 4259 // - If we are in fullscreen mode due to DOM fullscreen, requesting browser 4260 // fullscreen should not change anything, either. Note that we should not 4261 // update reason to ForFullscreenMode, otherwise the subsequent DOM 4262 // fullscreen exit will be ignored and user will be confused. And ideally 4263 // this should never happen as `window.fullscreen` returns `true` for DOM 4264 // fullscreen as well. 4265 if (mFullscreen.isSome() == aFullscreen) { 4266 // How come we get browser fullscreen request while we are already in DOM 4267 // fullscreen? 4268 MOZ_ASSERT_IF(aFullscreen && aReason == FullscreenReason::ForFullscreenMode, 4269 mFullscreen.value() != FullscreenReason::ForFullscreenAPI); 4270 return NS_OK; 4271 } 4272 4273 // Note that although entering DOM fullscreen could also cause 4274 // consequential calls to this method, those calls will be skipped 4275 // at the condition above. 4276 if (aReason == FullscreenReason::ForFullscreenMode) { 4277 if (!aFullscreen && mFullscreen && 4278 mFullscreen.value() == FullscreenReason::ForFullscreenAPI) { 4279 // If we are exiting fullscreen mode, but we actually didn't 4280 // entered browser fullscreen mode, the fullscreen state was only for 4281 // the Fullscreen API. Change the reason here so that we can 4282 // perform transition for it. 4283 aReason = FullscreenReason::ForFullscreenAPI; 4284 } 4285 } else { 4286 // If we are exiting from DOM fullscreen while we initially make 4287 // the window fullscreen because of browser fullscreen mode, don't restore 4288 // the window. But we still need to exit the DOM fullscreen state. 4289 if (!aFullscreen && mFullscreen && 4290 mFullscreen.value() == FullscreenReason::ForFullscreenMode) { 4291 // If there is a in-process fullscreen request, FinishDOMFullscreenChange 4292 // will be called when the request is finished. 4293 if (!mInProcessFullscreenRequest.isSome()) { 4294 FinishDOMFullscreenChange(mDoc, false); 4295 } 4296 return NS_OK; 4297 } 4298 } 4299 4300 // Set this before so if widget sends an event indicating its 4301 // gone full screen, the state trap above works. 4302 if (aFullscreen) { 4303 mFullscreen.emplace(aReason); 4304 } else { 4305 mFullscreen.reset(); 4306 } 4307 4308 // If we are in process of fullscreen request, only keep the latest fullscreen 4309 // state, we will sync up later while the processing request is finished. 4310 if (mInProcessFullscreenRequest.isSome()) { 4311 mFullscreenHasChangedDuringProcessing = true; 4312 return NS_OK; 4313 } 4314 4315 return ProcessWidgetFullscreenRequest(aReason, aFullscreen); 4316 } 4317 4318 // Support a per-window, dynamic equivalent of enabling 4319 // full-screen-api.ignore-widgets 4320 void nsGlobalWindowOuter::ForceFullScreenInWidget() { 4321 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess()); 4322 4323 mForceFullScreenInWidget = true; 4324 } 4325 4326 bool nsGlobalWindowOuter::SetWidgetFullscreen(FullscreenReason aReason, 4327 bool aIsFullscreen, 4328 nsIWidget* aWidget) { 4329 MOZ_ASSERT(this == GetInProcessTopInternal(), 4330 "Only topmost window should call this"); 4331 MOZ_ASSERT(!GetFrameElementInternal(), "Content window should not call this"); 4332 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); 4333 4334 if (!NS_WARN_IF(!IsChromeWindow())) { 4335 if (!NS_WARN_IF(mChromeFields.mFullscreenPresShell)) { 4336 if (PresShell* presShell = mDocShell->GetPresShell()) { 4337 if (nsRefreshDriver* rd = presShell->GetRefreshDriver()) { 4338 mChromeFields.mFullscreenPresShell = do_GetWeakReference(presShell); 4339 MOZ_ASSERT(mChromeFields.mFullscreenPresShell); 4340 rd->SetIsResizeSuppressed(); 4341 rd->Freeze(); 4342 } 4343 } 4344 } 4345 } 4346 nsresult rv = aReason == FullscreenReason::ForFullscreenMode 4347 ? 4348 // If we enter fullscreen for fullscreen mode, we want 4349 // the native system behavior. 4350 aWidget->MakeFullScreenWithNativeTransition(aIsFullscreen) 4351 : aWidget->MakeFullScreen(aIsFullscreen); 4352 return NS_SUCCEEDED(rv); 4353 } 4354 4355 /* virtual */ 4356 void nsGlobalWindowOuter::FullscreenWillChange(bool aIsFullscreen) { 4357 if (!mInProcessFullscreenRequest.isSome()) { 4358 // If there is no in-process fullscreen request, the fullscreen state change 4359 // is triggered from the OS directly, e.g. user use built-in window button 4360 // to enter/exit fullscreen on macOS. 4361 mInProcessFullscreenRequest.emplace(FullscreenReason::ForFullscreenMode, 4362 aIsFullscreen); 4363 if (mFullscreen.isSome() != aIsFullscreen) { 4364 if (aIsFullscreen) { 4365 mFullscreen.emplace(FullscreenReason::ForFullscreenMode); 4366 } else { 4367 mFullscreen.reset(); 4368 } 4369 } else { 4370 // It is possible that FullscreenWillChange is notified with current 4371 // fullscreen state, e.g. browser goes into fullscreen when widget 4372 // fullscreen is prevented, and then user triggers fullscreen from the OS 4373 // directly again. 4374 MOZ_ASSERT(StaticPrefs::full_screen_api_ignore_widgets() || 4375 mForceFullScreenInWidget, 4376 "This should only happen when widget fullscreen is prevented"); 4377 } 4378 } 4379 if (aIsFullscreen) { 4380 DispatchCustomEvent(u"willenterfullscreen"_ns, ChromeOnlyDispatch::eYes); 4381 } else { 4382 DispatchCustomEvent(u"willexitfullscreen"_ns, ChromeOnlyDispatch::eYes); 4383 } 4384 } 4385 4386 /* virtual */ 4387 void nsGlobalWindowOuter::FinishFullscreenChange(bool aIsFullscreen) { 4388 mozilla::Maybe<FullscreenRequest> currentInProcessRequest = 4389 std::move(mInProcessFullscreenRequest); 4390 if (!mFullscreenHasChangedDuringProcessing && 4391 aIsFullscreen != mFullscreen.isSome()) { 4392 NS_WARNING("Failed to toggle fullscreen state of the widget"); 4393 // We failed to make the widget enter fullscreen. 4394 // Stop further changes and restore the state. 4395 if (!aIsFullscreen) { 4396 mFullscreen.reset(); 4397 } else { 4398 #ifndef XP_MACOSX 4399 MOZ_ASSERT_UNREACHABLE("Failed to exit fullscreen?"); 4400 #endif 4401 // Restore fullscreen state with FullscreenReason::ForFullscreenAPI reason 4402 // in order to make subsequent DOM fullscreen exit request can exit 4403 // browser fullscreen mode. 4404 mFullscreen.emplace(FullscreenReason::ForFullscreenAPI); 4405 } 4406 return; 4407 } 4408 4409 // Note that we must call this to toggle the DOM fullscreen state 4410 // of the document before dispatching the "fullscreen" event, so 4411 // that the chrome can distinguish between browser fullscreen mode 4412 // and DOM fullscreen. 4413 FinishDOMFullscreenChange(mDoc, aIsFullscreen); 4414 4415 // dispatch a "fullscreen" DOM event so that XUL apps can 4416 // respond visually if we are kicked into full screen mode 4417 DispatchCustomEvent(u"fullscreen"_ns, ChromeOnlyDispatch::eYes); 4418 4419 if (!NS_WARN_IF(!IsChromeWindow())) { 4420 if (RefPtr<PresShell> presShell = 4421 do_QueryReferent(mChromeFields.mFullscreenPresShell)) { 4422 if (nsRefreshDriver* rd = presShell->GetRefreshDriver()) { 4423 rd->Thaw(); 4424 } 4425 mChromeFields.mFullscreenPresShell = nullptr; 4426 } 4427 } 4428 4429 // If fullscreen state has changed during processing fullscreen request, we 4430 // need to ensure widget matches our latest fullscreen state here. 4431 if (mFullscreenHasChangedDuringProcessing) { 4432 mFullscreenHasChangedDuringProcessing = false; 4433 // Widget doesn't care about the reason that makes it entering/exiting 4434 // fullscreen, so here we just need to ensure the fullscreen state is 4435 // matched. 4436 if (aIsFullscreen != mFullscreen.isSome()) { 4437 // If we end up need to exit fullscreen, use the same reason that brings 4438 // us into fullscreen mode, so that we will perform the same fullscreen 4439 // transistion effect for exiting. 4440 ProcessWidgetFullscreenRequest( 4441 mFullscreen.isSome() ? mFullscreen.value() 4442 : currentInProcessRequest.value().mReason, 4443 mFullscreen.isSome()); 4444 } 4445 } 4446 } 4447 4448 /* virtual */ 4449 void nsGlobalWindowOuter::MacFullscreenMenubarOverlapChanged( 4450 mozilla::DesktopCoord aOverlapAmount) { 4451 ErrorResult res; 4452 RefPtr<Event> domEvent = 4453 mDoc->CreateEvent(u"CustomEvent"_ns, CallerType::System, res); 4454 if (res.Failed()) { 4455 return; 4456 } 4457 4458 AutoJSAPI jsapi; 4459 jsapi.Init(); 4460 JSContext* cx = jsapi.cx(); 4461 JSAutoRealm ar(cx, GetWrapperPreserveColor()); 4462 4463 JS::Rooted<JS::Value> detailValue(cx); 4464 if (!ToJSValue(cx, aOverlapAmount, &detailValue)) { 4465 return; 4466 } 4467 4468 CustomEvent* customEvent = static_cast<CustomEvent*>(domEvent.get()); 4469 customEvent->InitCustomEvent(cx, u"MacFullscreenMenubarRevealUpdate"_ns, 4470 /* aCanBubble = */ true, 4471 /* aCancelable = */ true, detailValue); 4472 domEvent->SetTrusted(true); 4473 domEvent->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true; 4474 4475 nsCOMPtr<EventTarget> target = this; 4476 domEvent->SetTarget(target); 4477 4478 target->DispatchEvent(*domEvent, CallerType::System, IgnoreErrors()); 4479 } 4480 4481 bool nsGlobalWindowOuter::Fullscreen() const { 4482 NS_ENSURE_TRUE(mDocShell, mFullscreen.isSome()); 4483 4484 // Get the fullscreen value of the root window, to always have the value 4485 // accurate, even when called from content. 4486 nsCOMPtr<nsIDocShellTreeItem> rootItem; 4487 mDocShell->GetInProcessRootTreeItem(getter_AddRefs(rootItem)); 4488 if (rootItem == mDocShell) { 4489 if (!XRE_IsContentProcess()) { 4490 // We are the root window. Return our internal value. 4491 return mFullscreen.isSome(); 4492 } 4493 if (nsCOMPtr<nsIWidget> widget = GetNearestWidget()) { 4494 // We are in content process, figure out the value from 4495 // the sizemode of the puppet widget. 4496 return widget->SizeMode() == nsSizeMode_Fullscreen; 4497 } 4498 return false; 4499 } 4500 4501 nsCOMPtr<nsPIDOMWindowOuter> window = rootItem->GetWindow(); 4502 NS_ENSURE_TRUE(window, mFullscreen.isSome()); 4503 4504 return nsGlobalWindowOuter::Cast(window)->Fullscreen(); 4505 } 4506 4507 bool nsGlobalWindowOuter::GetFullscreenOuter() { return Fullscreen(); } 4508 4509 bool nsGlobalWindowOuter::GetFullScreen() { 4510 FORWARD_TO_INNER(GetFullScreen, (), false); 4511 } 4512 4513 void nsGlobalWindowOuter::EnsureReflowFlushAndPaint() { 4514 NS_ASSERTION(mDocShell, 4515 "EnsureReflowFlushAndPaint() called with no " 4516 "docshell!"); 4517 4518 if (!mDocShell) return; 4519 4520 RefPtr<PresShell> presShell = mDocShell->GetPresShell(); 4521 if (!presShell) { 4522 return; 4523 } 4524 4525 // Flush pending reflows. 4526 if (mDoc) { 4527 mDoc->FlushPendingNotifications(FlushType::Layout); 4528 } 4529 4530 // Unsuppress painting. 4531 presShell->UnsuppressPainting(); 4532 } 4533 4534 // static 4535 void nsGlobalWindowOuter::MakeMessageWithPrincipal( 4536 nsAString& aOutMessage, nsIPrincipal* aSubjectPrincipal, bool aUseHostPort, 4537 const char* aNullMessage, const char* aContentMessage, 4538 const char* aFallbackMessage) { 4539 MOZ_ASSERT(aSubjectPrincipal); 4540 4541 aOutMessage.Truncate(); 4542 4543 // Try to get a host from the running principal -- this will do the 4544 // right thing for javascript: and data: documents. 4545 4546 nsAutoCString contentDesc; 4547 4548 if (aSubjectPrincipal->GetIsNullPrincipal()) { 4549 nsContentUtils::GetLocalizedString( 4550 nsContentUtils::eCOMMON_DIALOG_PROPERTIES, aNullMessage, aOutMessage); 4551 } else { 4552 auto* addonPolicy = BasePrincipal::Cast(aSubjectPrincipal)->AddonPolicy(); 4553 if (addonPolicy) { 4554 nsContentUtils::FormatLocalizedString( 4555 aOutMessage, nsContentUtils::eCOMMON_DIALOG_PROPERTIES, 4556 aContentMessage, addonPolicy->Name()); 4557 } else { 4558 nsresult rv = NS_ERROR_FAILURE; 4559 if (aUseHostPort) { 4560 nsCOMPtr<nsIURI> uri = aSubjectPrincipal->GetURI(); 4561 if (uri) { 4562 rv = uri->GetDisplayHostPort(contentDesc); 4563 } 4564 } 4565 if (!aUseHostPort || NS_FAILED(rv)) { 4566 rv = aSubjectPrincipal->GetExposablePrePath(contentDesc); 4567 } 4568 if (NS_SUCCEEDED(rv) && !contentDesc.IsEmpty()) { 4569 NS_ConvertUTF8toUTF16 ucsPrePath(contentDesc); 4570 nsContentUtils::FormatLocalizedString( 4571 aOutMessage, nsContentUtils::eCOMMON_DIALOG_PROPERTIES, 4572 aContentMessage, ucsPrePath); 4573 } 4574 } 4575 } 4576 4577 if (aOutMessage.IsEmpty()) { 4578 // We didn't find a host so use the generic heading 4579 nsContentUtils::GetLocalizedString( 4580 nsContentUtils::eCOMMON_DIALOG_PROPERTIES, aFallbackMessage, 4581 aOutMessage); 4582 } 4583 4584 // Just in case 4585 if (aOutMessage.IsEmpty()) { 4586 NS_WARNING( 4587 "could not get ScriptDlgGenericHeading string from string bundle"); 4588 aOutMessage.AssignLiteral("[Script]"); 4589 } 4590 } 4591 4592 bool nsGlobalWindowOuter::CanMoveResizeWindows(CallerType aCallerType, 4593 bool aIsMove, 4594 ErrorResult& aError) { 4595 if (mBrowsingContext->IsSubframe()) { 4596 return false; 4597 } 4598 4599 // When called from chrome, we can avoid the following checks. 4600 if (aCallerType != CallerType::System) { 4601 // Don't allow scripts to move or resize windows that were not opened by a 4602 // script. 4603 if (!mBrowsingContext->GetTopLevelCreatedByWebContent()) { 4604 return false; 4605 } 4606 4607 if (!CanSetProperty("dom.disable_window_move_resize")) { 4608 return false; 4609 } 4610 4611 // Ignore the request if we have more than one tab in the window. 4612 if (mBrowsingContext->Top()->HasSiblings()) { 4613 return false; 4614 } 4615 } 4616 4617 if (mDocShell) { 4618 bool allow; 4619 nsresult rv = mDocShell->GetAllowWindowControl(&allow); 4620 if (NS_SUCCEEDED(rv) && !allow) return false; 4621 } 4622 4623 if (mBrowsingContext->GetIsDocumentPiP()) { 4624 // https://wicg.github.io/document-picture-in-picture/#positioning 4625 if (aIsMove) { 4626 nsLiteralString errorMsg( 4627 u"Picture-in-Picture windows cannot be moved by script."); 4628 nsContentUtils::ReportToConsoleNonLocalized( 4629 errorMsg, nsIScriptError::warningFlag, "Window"_ns, GetDocument()); 4630 return false; 4631 } 4632 4633 // https://wicg.github.io/document-picture-in-picture/#resizing-the-pip-window 4634 WindowContext* wc = mInnerWindow->GetWindowContext(); 4635 if (!wc || !wc->ConsumeTransientUserGestureActivation()) { 4636 aError.ThrowNotAllowedError( 4637 "Resizing a Picture-in-Picture window requires transient activation"); 4638 return false; 4639 } 4640 } 4641 4642 if (nsGlobalWindowInner::sMouseDown && 4643 !nsGlobalWindowInner::sDragServiceDisabled) { 4644 nsCOMPtr<nsIDragService> ds = 4645 do_GetService("@mozilla.org/widget/dragservice;1"); 4646 if (ds) { 4647 nsGlobalWindowInner::sDragServiceDisabled = true; 4648 ds->Suppress(); 4649 } 4650 } 4651 return true; 4652 } 4653 4654 bool nsGlobalWindowOuter::AlertOrConfirm(bool aAlert, const nsAString& aMessage, 4655 nsIPrincipal& aSubjectPrincipal, 4656 ErrorResult& aError) { 4657 // XXX This method is very similar to nsGlobalWindowOuter::Prompt, make 4658 // sure any modifications here don't need to happen over there! 4659 if (!AreDialogsEnabled()) { 4660 // Just silently return. In the case of alert(), the return value is 4661 // ignored. In the case of confirm(), returning false is the same thing as 4662 // would happen if the user cancels. 4663 return false; 4664 } 4665 4666 // Reset popup state while opening a modal dialog, and firing events 4667 // about the dialog, to prevent the current state from being active 4668 // the whole time a modal dialog is open. 4669 AutoPopupStatePusher popupStatePusher(PopupBlocker::openAbused, true); 4670 4671 // Before bringing up the window, unsuppress painting and flush 4672 // pending reflows. 4673 EnsureReflowFlushAndPaint(); 4674 4675 nsAutoString title; 4676 MakeMessageWithPrincipal(title, &aSubjectPrincipal, false, 4677 "ScriptDlgNullPrincipalHeading", "ScriptDlgHeading", 4678 "ScriptDlgGenericHeading"); 4679 4680 // Remove non-terminating null characters from the 4681 // string. See bug #310037. 4682 nsAutoString final; 4683 nsContentUtils::StripNullChars(aMessage, final); 4684 nsContentUtils::PlatformToDOMLineBreaks(final); 4685 4686 nsresult rv; 4687 nsCOMPtr<nsIPromptFactory> promptFac = 4688 do_GetService("@mozilla.org/prompter;1", &rv); 4689 if (NS_FAILED(rv)) { 4690 aError.Throw(rv); 4691 return false; 4692 } 4693 4694 nsCOMPtr<nsIPrompt> prompt; 4695 aError = 4696 promptFac->GetPrompt(this, NS_GET_IID(nsIPrompt), getter_AddRefs(prompt)); 4697 if (aError.Failed()) { 4698 return false; 4699 } 4700 4701 // Always allow content modal prompts for alert and confirm. 4702 if (nsCOMPtr<nsIWritablePropertyBag2> promptBag = do_QueryInterface(prompt)) { 4703 promptBag->SetPropertyAsUint32(u"modalType"_ns, 4704 nsIPrompt::MODAL_TYPE_CONTENT); 4705 } 4706 4707 bool result = false; 4708 nsAutoSyncOperation sync(mDoc, SyncOperationBehavior::eSuspendInput); 4709 if (ShouldPromptToBlockDialogs()) { 4710 bool disallowDialog = false; 4711 nsAutoString label; 4712 MakeMessageWithPrincipal( 4713 label, &aSubjectPrincipal, true, "ScriptDialogLabelNullPrincipal", 4714 "ScriptDialogLabelContentPrincipal", "ScriptDialogLabelNullPrincipal"); 4715 4716 aError = aAlert 4717 ? prompt->AlertCheck(title.get(), final.get(), label.get(), 4718 &disallowDialog) 4719 : prompt->ConfirmCheck(title.get(), final.get(), label.get(), 4720 &disallowDialog, &result); 4721 4722 if (disallowDialog) { 4723 DisableDialogs(); 4724 } 4725 } else { 4726 aError = aAlert ? prompt->Alert(title.get(), final.get()) 4727 : prompt->Confirm(title.get(), final.get(), &result); 4728 } 4729 4730 return result; 4731 } 4732 4733 void nsGlobalWindowOuter::AlertOuter(const nsAString& aMessage, 4734 nsIPrincipal& aSubjectPrincipal, 4735 ErrorResult& aError) { 4736 AlertOrConfirm(/* aAlert = */ true, aMessage, aSubjectPrincipal, aError); 4737 } 4738 4739 bool nsGlobalWindowOuter::ConfirmOuter(const nsAString& aMessage, 4740 nsIPrincipal& aSubjectPrincipal, 4741 ErrorResult& aError) { 4742 return AlertOrConfirm(/* aAlert = */ false, aMessage, aSubjectPrincipal, 4743 aError); 4744 } 4745 4746 void nsGlobalWindowOuter::PromptOuter(const nsAString& aMessage, 4747 const nsAString& aInitial, 4748 nsAString& aReturn, 4749 nsIPrincipal& aSubjectPrincipal, 4750 ErrorResult& aError) { 4751 // XXX This method is very similar to nsGlobalWindowOuter::AlertOrConfirm, 4752 // make sure any modifications here don't need to happen over there! 4753 SetDOMStringToNull(aReturn); 4754 4755 if (!AreDialogsEnabled()) { 4756 // Return null, as if the user just canceled the prompt. 4757 return; 4758 } 4759 4760 // Reset popup state while opening a modal dialog, and firing events 4761 // about the dialog, to prevent the current state from being active 4762 // the whole time a modal dialog is open. 4763 AutoPopupStatePusher popupStatePusher(PopupBlocker::openAbused, true); 4764 4765 // Before bringing up the window, unsuppress painting and flush 4766 // pending reflows. 4767 EnsureReflowFlushAndPaint(); 4768 4769 nsAutoString title; 4770 MakeMessageWithPrincipal(title, &aSubjectPrincipal, false, 4771 "ScriptDlgNullPrincipalHeading", "ScriptDlgHeading", 4772 "ScriptDlgGenericHeading"); 4773 4774 // Remove non-terminating null characters from the 4775 // string. See bug #310037. 4776 nsAutoString fixedMessage, fixedInitial; 4777 nsContentUtils::StripNullChars(aMessage, fixedMessage); 4778 nsContentUtils::PlatformToDOMLineBreaks(fixedMessage); 4779 nsContentUtils::StripNullChars(aInitial, fixedInitial); 4780 4781 nsresult rv; 4782 nsCOMPtr<nsIPromptFactory> promptFac = 4783 do_GetService("@mozilla.org/prompter;1", &rv); 4784 if (NS_FAILED(rv)) { 4785 aError.Throw(rv); 4786 return; 4787 } 4788 4789 nsCOMPtr<nsIPrompt> prompt; 4790 aError = 4791 promptFac->GetPrompt(this, NS_GET_IID(nsIPrompt), getter_AddRefs(prompt)); 4792 if (aError.Failed()) { 4793 return; 4794 } 4795 4796 // Always allow content modal prompts for prompt. 4797 if (nsCOMPtr<nsIWritablePropertyBag2> promptBag = do_QueryInterface(prompt)) { 4798 promptBag->SetPropertyAsUint32(u"modalType"_ns, 4799 nsIPrompt::MODAL_TYPE_CONTENT); 4800 } 4801 4802 // Pass in the default value, if any. 4803 char16_t* inoutValue = ToNewUnicode(fixedInitial); 4804 bool disallowDialog = false; 4805 4806 nsAutoString label; 4807 label.SetIsVoid(true); 4808 if (ShouldPromptToBlockDialogs()) { 4809 nsContentUtils::GetLocalizedString( 4810 nsContentUtils::eCOMMON_DIALOG_PROPERTIES, "ScriptDialogLabel", label); 4811 } 4812 4813 nsAutoSyncOperation sync(mDoc, SyncOperationBehavior::eSuspendInput); 4814 bool ok; 4815 aError = prompt->Prompt(title.get(), fixedMessage.get(), &inoutValue, 4816 label.IsVoid() ? nullptr : label.get(), 4817 &disallowDialog, &ok); 4818 4819 if (disallowDialog) { 4820 DisableDialogs(); 4821 } 4822 4823 // XXX Doesn't this leak inoutValue? 4824 if (aError.Failed()) { 4825 return; 4826 } 4827 4828 nsString outValue; 4829 outValue.Adopt(inoutValue); 4830 if (ok && inoutValue) { 4831 aReturn = std::move(outValue); 4832 } 4833 } 4834 4835 void nsGlobalWindowOuter::FocusOuter(CallerType aCallerType, 4836 bool aFromOtherProcess, 4837 uint64_t aActionId) { 4838 RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager(); 4839 if (MOZ_UNLIKELY(!fm)) { 4840 return; 4841 } 4842 4843 auto [canFocus, isActive] = GetBrowsingContext()->CanFocusCheck(aCallerType); 4844 if (aFromOtherProcess) { 4845 // We trust that the check passed in a process that's, in principle, 4846 // untrusted, because we don't have the required caller context available 4847 // here. Also, the worst that the other process can do in this case is to 4848 // raise a window it's not supposed to be allowed to raise. 4849 // https://bugzilla.mozilla.org/show_bug.cgi?id=1677899 4850 MOZ_ASSERT(XRE_IsContentProcess(), 4851 "Parent should not trust other processes."); 4852 canFocus = true; 4853 } 4854 4855 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow(); 4856 if (treeOwnerAsWin && (canFocus || isActive)) { 4857 bool isEnabled = true; 4858 if (NS_SUCCEEDED(treeOwnerAsWin->GetEnabled(&isEnabled)) && !isEnabled) { 4859 NS_WARNING("Should not try to set the focus on a disabled window"); 4860 return; 4861 } 4862 } 4863 4864 if (!mDocShell) { 4865 return; 4866 } 4867 4868 // If the window has a child frame focused, clear the focus. This 4869 // ensures that focus will be in this frame and not in a child. 4870 if (nsIContent* content = GetFocusedElement()) { 4871 if (HTMLIFrameElement::FromNode(content)) { 4872 fm->ClearFocus(this); 4873 } 4874 } 4875 4876 RefPtr<BrowsingContext> parent; 4877 BrowsingContext* bc = GetBrowsingContext(); 4878 if (bc) { 4879 parent = bc->GetParent(); 4880 if (!parent && XRE_IsParentProcess()) { 4881 parent = bc->Canonical()->GetParentCrossChromeBoundary(); 4882 } 4883 } 4884 if (parent) { 4885 if (!parent->IsInProcess()) { 4886 if (isActive) { 4887 OwningNonNull<nsGlobalWindowOuter> kungFuDeathGrip(*this); 4888 fm->WindowRaised(kungFuDeathGrip, aActionId); 4889 } else { 4890 ContentChild* contentChild = ContentChild::GetSingleton(); 4891 MOZ_ASSERT(contentChild); 4892 contentChild->SendFinalizeFocusOuter(bc, canFocus, aCallerType); 4893 } 4894 return; 4895 } 4896 4897 MOZ_ASSERT(mDoc, "Call chain should have ensured document creation."); 4898 if (mDoc) { 4899 if (Element* frame = mDoc->GetEmbedderElement()) { 4900 nsContentUtils::RequestFrameFocus(*frame, canFocus, aCallerType); 4901 } 4902 } 4903 return; 4904 } 4905 4906 if (canFocus) { 4907 // if there is no parent, this must be a toplevel window, so raise the 4908 // window if canFocus is true. If this is a child process, the raise 4909 // window request will get forwarded to the parent by the puppet widget. 4910 OwningNonNull<nsGlobalWindowOuter> kungFuDeathGrip(*this); 4911 fm->RaiseWindow(kungFuDeathGrip, aCallerType, aActionId); 4912 } 4913 } 4914 4915 nsresult nsGlobalWindowOuter::Focus(CallerType aCallerType) { 4916 FORWARD_TO_INNER(Focus, (aCallerType), NS_ERROR_UNEXPECTED); 4917 } 4918 4919 void nsGlobalWindowOuter::BlurOuter(CallerType aCallerType) { 4920 if (!GetBrowsingContext()->CanBlurCheck(aCallerType)) { 4921 return; 4922 } 4923 4924 nsCOMPtr<nsIWebBrowserChrome> chrome = GetWebBrowserChrome(); 4925 if (chrome) { 4926 chrome->Blur(); 4927 } 4928 } 4929 4930 void nsGlobalWindowOuter::StopOuter(ErrorResult& aError) { 4931 // IsNavigationAllowed checks are usually done in nsDocShell directly, 4932 // however nsDocShell::Stop has a bunch of internal users that would fail 4933 // the IsNavigationAllowed check. 4934 if (!mDocShell || !nsDocShell::Cast(mDocShell)->IsNavigationAllowed()) { 4935 return; 4936 } 4937 4938 nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell)); 4939 if (webNav) { 4940 aError = webNav->Stop(nsIWebNavigation::STOP_ALL); 4941 } 4942 } 4943 4944 void nsGlobalWindowOuter::PrintOuter(ErrorResult& aError) { 4945 if (!AreDialogsEnabled()) { 4946 // Per spec, silently return. https://github.com/whatwg/html/commit/21a1de1 4947 return; 4948 } 4949 4950 // Printing is disabled, silently return. 4951 if (!StaticPrefs::print_enabled()) { 4952 return; 4953 } 4954 4955 // If we're loading, queue the print for later. This is a special-case that 4956 // only applies to the window.print() call, for compat with other engines and 4957 // pre-existing behavior. 4958 if (mShouldDelayPrintUntilAfterLoad) { 4959 if (nsIDocShell* docShell = GetDocShell()) { 4960 if (docShell->GetBusyFlags() & nsIDocShell::BUSY_FLAGS_PAGE_LOADING) { 4961 mDelayedPrintUntilAfterLoad = true; 4962 return; 4963 } 4964 } 4965 } 4966 4967 #ifdef NS_PRINTING 4968 RefPtr<BrowsingContext> top = 4969 mBrowsingContext ? mBrowsingContext->Top() : nullptr; 4970 if (NS_WARN_IF(top && top->GetIsPrinting())) { 4971 return; 4972 } 4973 4974 if (top) { 4975 (void)top->SetIsPrinting(true); 4976 } 4977 4978 auto unset = MakeScopeExit([&] { 4979 if (top) { 4980 (void)top->SetIsPrinting(false); 4981 } 4982 }); 4983 4984 const bool forPreview = !StaticPrefs::print_always_print_silent() && 4985 !StaticPrefs::print_prefer_system_dialog(); 4986 Print(nullptr, nullptr, nullptr, nullptr, IsPreview(forPreview), 4987 IsForWindowDotPrint::Yes, nullptr, nullptr, aError); 4988 #endif 4989 } 4990 4991 class MOZ_RAII AutoModalState { 4992 public: 4993 explicit AutoModalState(nsGlobalWindowOuter& aWin) 4994 : mModalStateWin(aWin.EnterModalState()) {} 4995 4996 ~AutoModalState() { 4997 if (mModalStateWin) { 4998 mModalStateWin->LeaveModalState(); 4999 } 5000 } 5001 5002 RefPtr<nsGlobalWindowOuter> mModalStateWin; 5003 }; 5004 5005 Nullable<WindowProxyHolder> nsGlobalWindowOuter::Print( 5006 nsIPrintSettings* aPrintSettings, RemotePrintJobChild* aRemotePrintJob, 5007 nsIWebProgressListener* aListener, nsIDocShell* aDocShellToCloneInto, 5008 IsPreview aIsPreview, IsForWindowDotPrint aForWindowDotPrint, 5009 PrintPreviewResolver&& aPrintPreviewCallback, 5010 RefPtr<BrowsingContext>* aCachedBrowsingContext, ErrorResult& aError) { 5011 #ifdef NS_PRINTING 5012 nsCOMPtr<nsIPrintSettingsService> printSettingsService = 5013 do_GetService("@mozilla.org/gfx/printsettings-service;1"); 5014 if (!printSettingsService) { 5015 // we currently return here in headless mode - should we? 5016 aError.ThrowNotSupportedError("No print settings service"); 5017 return nullptr; 5018 } 5019 5020 nsCOMPtr<nsIPrintSettings> ps = aPrintSettings; 5021 if (!ps) { 5022 // We shouldn't need this once bug 1776169 is fixed. 5023 printSettingsService->GetDefaultPrintSettingsForPrinting( 5024 getter_AddRefs(ps)); 5025 } 5026 5027 RefPtr<Document> docToPrint = mDoc; 5028 if (NS_WARN_IF(!docToPrint)) { 5029 aError.ThrowNotSupportedError("Document is gone"); 5030 return nullptr; 5031 } 5032 5033 RefPtr<BrowsingContext> sourceBC = docToPrint->GetBrowsingContext(); 5034 MOZ_DIAGNOSTIC_ASSERT(sourceBC); 5035 if (!sourceBC) { 5036 aError.ThrowNotSupportedError("No browsing context for source document"); 5037 return nullptr; 5038 } 5039 5040 nsAutoSyncOperation sync(docToPrint, SyncOperationBehavior::eAllowInput); 5041 AutoModalState modalState(*this); 5042 5043 nsCOMPtr<nsIDocumentViewer> viewer; 5044 RefPtr<BrowsingContext> bc; 5045 bool hasPrintCallbacks = false; 5046 bool wasStaticDocument = docToPrint->IsStaticDocument(); 5047 bool usingCachedBrowsingContext = false; 5048 if (aCachedBrowsingContext && *aCachedBrowsingContext) { 5049 MOZ_ASSERT(!wasStaticDocument, 5050 "Why pass in non-empty aCachedBrowsingContext if original " 5051 "document is already static?"); 5052 if (!wasStaticDocument) { 5053 // The passed in document is not a static clone and the caller passed in a 5054 // static clone to reuse, so swap it in. 5055 docToPrint = (*aCachedBrowsingContext)->GetDocument(); 5056 MOZ_ASSERT(docToPrint); 5057 MOZ_ASSERT(docToPrint->IsStaticDocument()); 5058 wasStaticDocument = true; 5059 usingCachedBrowsingContext = true; 5060 } 5061 } 5062 if (wasStaticDocument) { 5063 if (aForWindowDotPrint == IsForWindowDotPrint::Yes) { 5064 aError.ThrowNotSupportedError( 5065 "Calling print() from a print preview is unsupported, did you intend " 5066 "to call printPreview() instead?"); 5067 return nullptr; 5068 } 5069 if (usingCachedBrowsingContext) { 5070 bc = docToPrint->GetBrowsingContext(); 5071 } else { 5072 // We're already a print preview window, just reuse our browsing context / 5073 // content viewer. 5074 bc = sourceBC; 5075 } 5076 nsCOMPtr<nsIDocShell> docShell = bc->GetDocShell(); 5077 if (!docShell) { 5078 aError.ThrowNotSupportedError("No docshell"); 5079 return nullptr; 5080 } 5081 // We could handle this if needed. 5082 if (aDocShellToCloneInto && aDocShellToCloneInto != docShell) { 5083 aError.ThrowNotSupportedError( 5084 "We don't handle cloning a print preview doc into a different " 5085 "docshell"); 5086 return nullptr; 5087 } 5088 docShell->GetDocViewer(getter_AddRefs(viewer)); 5089 MOZ_DIAGNOSTIC_ASSERT(viewer); 5090 } else { 5091 if (aDocShellToCloneInto) { 5092 // Ensure the content viewer is created if needed. 5093 (void)aDocShellToCloneInto->GetDocument(); 5094 bc = aDocShellToCloneInto->GetBrowsingContext(); 5095 } else { 5096 AutoNoJSAPI nojsapi; 5097 auto printKind = aForWindowDotPrint == IsForWindowDotPrint::Yes 5098 ? PrintKind::WindowDotPrint 5099 : PrintKind::InternalPrint; 5100 // For PrintKind::WindowDotPrint, this call will not only make the parent 5101 // process create a CanonicalBrowsingContext for the returned `bc`, but 5102 // it will also make the parent process initiate the print/print preview. 5103 // See the handling of OPEN_PRINT_BROWSER in browser.js. 5104 aError = OpenInternal(""_ns, u""_ns, u""_ns, 5105 false, // aDialog 5106 true, // aCalledNoScript 5107 false, // aDoJSFixups 5108 true, // aNavigate 5109 nullptr, // No args 5110 nullptr, // aLoadState 5111 false, // aForceNoOpener 5112 printKind, getter_AddRefs(bc)); 5113 if (NS_WARN_IF(aError.Failed())) { 5114 return nullptr; 5115 } 5116 if (aCachedBrowsingContext) { 5117 MOZ_ASSERT(!*aCachedBrowsingContext); 5118 *aCachedBrowsingContext = bc; 5119 } 5120 } 5121 if (!bc) { 5122 aError.ThrowNotAllowedError("No browsing context"); 5123 return nullptr; 5124 } 5125 5126 (void)bc->Top()->SetIsPrinting(true); 5127 nsCOMPtr<nsIDocShell> cloneDocShell = bc->GetDocShell(); 5128 MOZ_DIAGNOSTIC_ASSERT(cloneDocShell); 5129 cloneDocShell->GetDocViewer(getter_AddRefs(viewer)); 5130 MOZ_DIAGNOSTIC_ASSERT(viewer); 5131 if (!viewer) { 5132 aError.ThrowNotSupportedError("Didn't end up with a content viewer"); 5133 return nullptr; 5134 } 5135 5136 if (bc != sourceBC) { 5137 MOZ_ASSERT(bc->IsTopContent()); 5138 // If we are cloning from a document in a different BrowsingContext, we 5139 // need to make sure to copy over our opener policy information from that 5140 // BrowsingContext. In the case where the source is an iframe, this 5141 // information needs to be copied from the toplevel source 5142 // BrowsingContext, as we may be making a static clone of a single 5143 // subframe. 5144 MOZ_ALWAYS_SUCCEEDS( 5145 bc->SetOpenerPolicy(sourceBC->Top()->GetOpenerPolicy())); 5146 MOZ_DIAGNOSTIC_ASSERT(bc->Group() == sourceBC->Group()); 5147 } 5148 5149 if (RefPtr<Document> doc = viewer->GetDocument()) { 5150 if (doc->IsShowing()) { 5151 // We're going to drop this document on the floor, in the SetDocument 5152 // call below. Make sure to run OnPageHide() to keep state consistent 5153 // and avoids assertions in the document destructor. 5154 doc->OnPageHide(false, nullptr); 5155 } 5156 } 5157 5158 AutoPrintEventDispatcher dispatcher(*docToPrint); 5159 5160 nsAutoScriptBlocker blockScripts; 5161 RefPtr<Document> clone = docToPrint->CreateStaticClone( 5162 cloneDocShell, viewer, ps, &hasPrintCallbacks); 5163 if (!clone) { 5164 aError.ThrowNotSupportedError("Clone operation for printing failed"); 5165 return nullptr; 5166 } 5167 } 5168 5169 nsCOMPtr<nsIWebBrowserPrint> webBrowserPrint = do_QueryInterface(viewer); 5170 if (!webBrowserPrint) { 5171 aError.ThrowNotSupportedError( 5172 "Content viewer didn't implement nsIWebBrowserPrint"); 5173 return nullptr; 5174 } 5175 bool closeWindowAfterPrint; 5176 if (wasStaticDocument) { 5177 // Here the document was a static clone to begin with that this code did not 5178 // create, so we should not clean it up. 5179 // The exception is if we're using the passed-in aCachedBrowsingContext, in 5180 // which case this is the second print with this static document clone that 5181 // we created the first time through, and we are responsible for cleaning it 5182 // up. There's also an exception if we're directly using the system print 5183 // dialog rather than our preview panel, because in this case the preview 5184 // will not take care of cleaning up the cloned doc. 5185 closeWindowAfterPrint = 5186 usingCachedBrowsingContext || StaticPrefs::print_prefer_system_dialog(); 5187 } else { 5188 // In this case the document was not a static clone, so we made a static 5189 // clone for printing purposes and must clean it up after the print is done. 5190 // The exception is if aCachedBrowsingContext is non-NULL, meaning the 5191 // caller is intending to print this document again, so we need to defer the 5192 // cleanup until after the second print. 5193 closeWindowAfterPrint = !aCachedBrowsingContext; 5194 } 5195 webBrowserPrint->SetCloseWindowAfterPrint(closeWindowAfterPrint); 5196 5197 // For window.print(), we postpone making these calls until the round-trip to 5198 // the parent process (triggered by the OpenInternal call above) calls us 5199 // again. Only a call from the parent can provide a valid nsPrintSettings 5200 // object and RemotePrintJobChild object. 5201 if (aForWindowDotPrint == IsForWindowDotPrint::No) { 5202 if (aIsPreview == IsPreview::Yes) { 5203 aError = webBrowserPrint->PrintPreview(ps, aListener, 5204 std::move(aPrintPreviewCallback)); 5205 if (aError.Failed()) { 5206 return nullptr; 5207 } 5208 } else { 5209 // Historically we've eaten this error. 5210 webBrowserPrint->Print(ps, aRemotePrintJob, aListener); 5211 } 5212 } 5213 5214 // Check whether we're in a case where we need to block in order for 5215 // window.print() to function properly: 5216 const bool shouldBlock = [&] { 5217 if (aForWindowDotPrint == IsForWindowDotPrint::No) { 5218 // We're not doing window.print; no need to block. 5219 return false; 5220 } 5221 5222 // When window.print() spawns a print dialog (either our own tab-modal 5223 // dialog or the system-print dialog), we usually want window.print() to 5224 // block until the print dialog is hidden. But we can't really do that if 5225 // we have print callbacks (mozPrintCallback), because we are inside a sync 5226 // operation, and we want to run microtasks / etc that the print callbacks 5227 // may create. It is really awkward to have this subtle behavior 5228 // difference... 5229 if (aIsPreview == IsPreview::Yes || 5230 StaticPrefs::print_prefer_system_dialog()) { 5231 return !hasPrintCallbacks; 5232 } 5233 5234 // We also want to allow window.print() to block for fuzzing, so that 5235 // fuzzers can test either behavior without needing to interact with a 5236 // dialog. 5237 return StaticPrefs::dom_window_print_fuzzing_block_while_printing(); 5238 }(); 5239 5240 if (shouldBlock) { 5241 SpinEventLoopUntil("nsGlobalWindowOuter::Print"_ns, 5242 [&] { return bc->IsDiscarded(); }); 5243 } 5244 5245 return WindowProxyHolder(std::move(bc)); 5246 #else 5247 return nullptr; 5248 #endif // NS_PRINTING 5249 } 5250 5251 void nsGlobalWindowOuter::MoveToOuter(int32_t aXPos, int32_t aYPos, 5252 CallerType aCallerType, 5253 ErrorResult& aError) { 5254 /* 5255 * If caller is not chrome and the user has not explicitly exempted the site, 5256 * prevent window.moveTo() by exiting early 5257 */ 5258 5259 if (!CanMoveResizeWindows(aCallerType, true, aError)) { 5260 return; 5261 } 5262 5263 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow(); 5264 if (!treeOwnerAsWin) { 5265 aError.Throw(NS_ERROR_FAILURE); 5266 return; 5267 } 5268 5269 // We need to do the same transformation GetScreenXY does. 5270 RefPtr<nsPresContext> presContext = mDocShell->GetPresContext(); 5271 if (!presContext) { 5272 return; 5273 } 5274 5275 CSSIntPoint cssPos(aXPos, aYPos); 5276 CheckSecurityLeftAndTop(&cssPos.x.value, &cssPos.y.value, aCallerType); 5277 5278 nsDeviceContext* context = presContext->DeviceContext(); 5279 5280 auto devPos = LayoutDeviceIntPoint::FromAppUnitsRounded( 5281 CSSIntPoint::ToAppUnits(cssPos), context->AppUnitsPerDevPixel()); 5282 5283 aError = treeOwnerAsWin->SetPosition(devPos.x, devPos.y); 5284 CheckForDPIChange(); 5285 } 5286 5287 void nsGlobalWindowOuter::MoveByOuter(int32_t aXDif, int32_t aYDif, 5288 CallerType aCallerType, 5289 ErrorResult& aError) { 5290 /* 5291 * If caller is not chrome and the user has not explicitly exempted the site, 5292 * prevent window.moveBy() by exiting early 5293 */ 5294 5295 if (!CanMoveResizeWindows(aCallerType, true, aError)) { 5296 return; 5297 } 5298 5299 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow(); 5300 if (!treeOwnerAsWin) { 5301 aError.Throw(NS_ERROR_FAILURE); 5302 return; 5303 } 5304 5305 // To do this correctly we have to convert what we get from GetPosition 5306 // into CSS pixels, add the arguments, do the security check, and 5307 // then convert back to device pixels for the call to SetPosition. 5308 5309 int32_t x, y; 5310 aError = treeOwnerAsWin->GetPosition(&x, &y); 5311 if (aError.Failed()) { 5312 return; 5313 } 5314 5315 auto cssScale = CSSToDevScaleForBaseWindow(treeOwnerAsWin); 5316 CSSIntPoint cssPos = RoundedToInt(treeOwnerAsWin->GetPosition() / cssScale); 5317 5318 cssPos.x += aXDif; 5319 cssPos.y += aYDif; 5320 5321 CheckSecurityLeftAndTop(&cssPos.x.value, &cssPos.y.value, aCallerType); 5322 5323 LayoutDeviceIntPoint newDevPos = RoundedToInt(cssPos * cssScale); 5324 aError = treeOwnerAsWin->SetPosition(newDevPos.x, newDevPos.y); 5325 5326 CheckForDPIChange(); 5327 } 5328 5329 nsresult nsGlobalWindowOuter::MoveBy(int32_t aXDif, int32_t aYDif) { 5330 ErrorResult rv; 5331 MoveByOuter(aXDif, aYDif, CallerType::System, rv); 5332 5333 return rv.StealNSResult(); 5334 } 5335 5336 void nsGlobalWindowOuter::ResizeToOuter(int32_t aWidth, int32_t aHeight, 5337 CallerType aCallerType, 5338 ErrorResult& aError) { 5339 /* 5340 * If caller is not chrome and the user has not explicitly exempted the site, 5341 * prevent window.resizeTo() by exiting early 5342 */ 5343 5344 if (!CanMoveResizeWindows(aCallerType, false, aError)) { 5345 return; 5346 } 5347 5348 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow(); 5349 if (!treeOwnerAsWin) { 5350 aError.Throw(NS_ERROR_FAILURE); 5351 return; 5352 } 5353 5354 CSSIntSize cssSize(aWidth, aHeight); 5355 5356 if (mBrowsingContext->GetIsDocumentPiP()) { 5357 if (Maybe<CSSIntRect> screen = 5358 DocumentPictureInPicture::GetScreenRect(this)) { 5359 CSSIntSize maxSize = 5360 DocumentPictureInPicture::CalcMaxDimensions(screen.value()); 5361 cssSize.width = std::min(cssSize.width, maxSize.width); 5362 cssSize.height = std::min(cssSize.height, maxSize.height); 5363 } else { 5364 aError.Throw(NS_ERROR_FAILURE); 5365 return; 5366 } 5367 } 5368 5369 CheckSecurityWidthAndHeight(&cssSize.width, &cssSize.height, aCallerType); 5370 5371 LayoutDeviceIntSize devSize = 5372 RoundedToInt(cssSize * CSSToDevScaleForBaseWindow(treeOwnerAsWin)); 5373 aError = treeOwnerAsWin->SetSize(devSize.width, devSize.height, true); 5374 5375 CheckForDPIChange(); 5376 } 5377 5378 void nsGlobalWindowOuter::ResizeByOuter(int32_t aWidthDif, int32_t aHeightDif, 5379 CallerType aCallerType, 5380 ErrorResult& aError) { 5381 /* 5382 * If caller is not chrome and the user has not explicitly exempted the site, 5383 * prevent window.resizeBy() by exiting early 5384 */ 5385 5386 if (!CanMoveResizeWindows(aCallerType, false, aError)) { 5387 return; 5388 } 5389 5390 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow(); 5391 if (!treeOwnerAsWin) { 5392 aError.Throw(NS_ERROR_FAILURE); 5393 return; 5394 } 5395 5396 LayoutDeviceIntSize size = treeOwnerAsWin->GetSize(); 5397 5398 // To do this correctly we have to convert what we got from GetSize 5399 // into CSS pixels, add the arguments, do the security check, and 5400 // then convert back to device pixels for the call to SetSize. 5401 5402 auto scale = CSSToDevScaleForBaseWindow(treeOwnerAsWin); 5403 CSSIntSize cssSize = RoundedToInt(size / scale); 5404 5405 cssSize.width += aWidthDif; 5406 cssSize.height += aHeightDif; 5407 5408 if (mBrowsingContext->GetIsDocumentPiP()) { 5409 if (Maybe<CSSIntRect> screen = 5410 DocumentPictureInPicture::GetScreenRect(this)) { 5411 CSSIntSize maxSize = 5412 DocumentPictureInPicture::CalcMaxDimensions(screen.value()); 5413 cssSize.width = std::min(cssSize.width, maxSize.width); 5414 cssSize.height = std::min(cssSize.height, maxSize.height); 5415 } else { 5416 aError.Throw(NS_ERROR_FAILURE); 5417 return; 5418 } 5419 } 5420 5421 CheckSecurityWidthAndHeight(&cssSize.width, &cssSize.height, aCallerType); 5422 5423 LayoutDeviceIntSize newDevSize = RoundedToInt(cssSize * scale); 5424 5425 aError = treeOwnerAsWin->SetSize(newDevSize.width, newDevSize.height, true); 5426 5427 CheckForDPIChange(); 5428 } 5429 5430 void nsGlobalWindowOuter::SizeToContentOuter( 5431 const SizeToContentConstraints& aConstraints, ErrorResult& aError) { 5432 if (!mDocShell) { 5433 return; 5434 } 5435 5436 if (mBrowsingContext->IsSubframe()) { 5437 return; 5438 } 5439 5440 // The content viewer does a check to make sure that it's a content 5441 // viewer for a toplevel docshell. 5442 nsCOMPtr<nsIDocumentViewer> viewer; 5443 mDocShell->GetDocViewer(getter_AddRefs(viewer)); 5444 if (!viewer) { 5445 return aError.Throw(NS_ERROR_FAILURE); 5446 } 5447 5448 auto contentSize = viewer->GetContentSize( 5449 aConstraints.mMaxWidth, aConstraints.mMaxHeight, aConstraints.mPrefWidth); 5450 if (!contentSize) { 5451 return aError.Throw(NS_ERROR_FAILURE); 5452 } 5453 5454 nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner(); 5455 if (!treeOwner) { 5456 return aError.Throw(NS_ERROR_FAILURE); 5457 } 5458 5459 // Don't use DevToCSSIntPixelsForBaseWindow() nor 5460 // CSSToDevIntPixelsForBaseWindow() here because contentSize is comes from 5461 // nsIDocumentViewer::GetContentSize() and it's computed with nsPresContext so 5462 // that we need to work with nsPresContext here too. 5463 RefPtr<nsPresContext> presContext = viewer->GetPresContext(); 5464 MOZ_ASSERT( 5465 presContext, 5466 "Should be non-nullptr if nsIDocumentViewer::GetContentSize() succeeded"); 5467 CSSIntSize cssSize = *contentSize; 5468 5469 LayoutDeviceIntSize newDevSize( 5470 presContext->CSSPixelsToDevPixels(cssSize.width), 5471 presContext->CSSPixelsToDevPixels(cssSize.height)); 5472 5473 nsCOMPtr<nsIDocShell> docShell = mDocShell; 5474 aError = 5475 treeOwner->SizeShellTo(docShell, newDevSize.width, newDevSize.height); 5476 } 5477 5478 already_AddRefed<nsPIWindowRoot> nsGlobalWindowOuter::GetTopWindowRoot() { 5479 nsPIDOMWindowOuter* piWin = GetPrivateRoot(); 5480 if (!piWin) { 5481 return nullptr; 5482 } 5483 5484 nsCOMPtr<nsPIWindowRoot> window = 5485 do_QueryInterface(piWin->GetChromeEventHandler()); 5486 return window.forget(); 5487 } 5488 5489 void nsGlobalWindowOuter::FirePopupBlockedEvent( 5490 nsIURI* aPopupURI, const nsAString& aPopupWindowName, 5491 const nsAString& aPopupWindowFeatures) { 5492 Document* doc = GetDoc(); 5493 if (!doc) { 5494 return; 5495 } 5496 5497 // Fire a "DOMPopupBlocked" event so that the UI can hear about 5498 // blocked popups. 5499 PopupBlockedEventInit init; 5500 init.mBubbles = true; 5501 init.mCancelable = true; 5502 // XXX: This is a different object, but webidl requires an inner window here 5503 // now. 5504 init.mRequestingWindow = GetCurrentInnerWindowInternal(this); 5505 init.mPopupWindowURI = aPopupURI; 5506 init.mPopupWindowName = aPopupWindowName; 5507 init.mPopupWindowFeatures = aPopupWindowFeatures; 5508 5509 RefPtr<PopupBlockedEvent> event = 5510 PopupBlockedEvent::Constructor(doc, u"DOMPopupBlocked"_ns, init); 5511 event->SetTrusted(true); 5512 5513 doc->DispatchEvent(*event); 5514 } 5515 5516 void nsGlobalWindowOuter::FireRedirectBlockedEvent(nsIURI* aRedirectURI) { 5517 Document* doc = GetDoc(); 5518 if (!doc) { 5519 return; 5520 } 5521 5522 // Fire a "DOMRedirectBlocked" event so that the UI can hear about 5523 // blocked redirects. 5524 RedirectBlockedEventInit init; 5525 init.mBubbles = true; 5526 init.mCancelable = true; 5527 init.mRequestingWindow = GetCurrentInnerWindowInternal(this); 5528 init.mRedirectURI = aRedirectURI; 5529 5530 RefPtr<RedirectBlockedEvent> event = 5531 RedirectBlockedEvent::Constructor(doc, u"DOMRedirectBlocked"_ns, init); 5532 event->SetTrusted(true); 5533 event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true; 5534 5535 doc->DispatchEvent(*event); 5536 } 5537 5538 // static 5539 bool nsGlobalWindowOuter::CanSetProperty(const char* aPrefName) { 5540 // Chrome can set any property. 5541 if (nsContentUtils::LegacyIsCallerChromeOrNativeCode()) { 5542 return true; 5543 } 5544 5545 // If the pref is set to true, we can not set the property 5546 // and vice versa. 5547 return !Preferences::GetBool(aPrefName, true); 5548 } 5549 5550 Nullable<WindowProxyHolder> nsGlobalWindowOuter::OpenOuter( 5551 const nsAString& aUrl, const nsAString& aName, const nsAString& aOptions, 5552 ErrorResult& aError) { 5553 RefPtr<BrowsingContext> bc; 5554 NS_ConvertUTF16toUTF8 url(aUrl); 5555 nsresult rv = OpenJS(url, aName, aOptions, getter_AddRefs(bc)); 5556 if (rv == NS_ERROR_MALFORMED_URI) { 5557 aError.ThrowSyntaxError("Unable to open a window with invalid URL '"_ns + 5558 url + "'."_ns); 5559 return nullptr; 5560 } 5561 5562 // XXX Is it possible that some internal errors are thrown here? 5563 aError = rv; 5564 5565 if (!bc) { 5566 return nullptr; 5567 } 5568 return WindowProxyHolder(std::move(bc)); 5569 } 5570 5571 nsresult nsGlobalWindowOuter::Open(const nsACString& aUrl, 5572 const nsAString& aName, 5573 const nsAString& aOptions, 5574 nsDocShellLoadState* aLoadState, 5575 bool aForceNoOpener, 5576 BrowsingContext** _retval) { 5577 return OpenInternal(aUrl, aName, aOptions, 5578 false, // aDialog 5579 true, // aCalledNoScript 5580 false, // aDoJSFixups 5581 true, // aNavigate 5582 nullptr, // No args 5583 aLoadState, aForceNoOpener, PrintKind::None, _retval); 5584 } 5585 5586 nsresult nsGlobalWindowOuter::OpenJS(const nsACString& aUrl, 5587 const nsAString& aName, 5588 const nsAString& aOptions, 5589 BrowsingContext** _retval) { 5590 return OpenInternal(aUrl, aName, aOptions, 5591 false, // aDialog 5592 false, // aCalledNoScript 5593 true, // aDoJSFixups 5594 true, // aNavigate 5595 nullptr, // No args 5596 nullptr, // aLoadState 5597 false, // aForceNoOpener 5598 PrintKind::None, _retval); 5599 } 5600 5601 // like Open, but attaches to the new window any extra parameters past 5602 // [features] as a JS property named "arguments" 5603 nsresult nsGlobalWindowOuter::OpenDialog(const nsACString& aUrl, 5604 const nsAString& aName, 5605 const nsAString& aOptions, 5606 nsIArray* aArguments, 5607 BrowsingContext** _retval) { 5608 return OpenInternal(aUrl, aName, aOptions, 5609 true, // aDialog 5610 true, // aCalledNoScript 5611 false, // aDoJSFixups 5612 true, // aNavigate 5613 aArguments, // Arguments 5614 nullptr, // aLoadState 5615 false, // aForceNoOpener 5616 PrintKind::None, _retval); 5617 } 5618 5619 // Like Open, but passes aNavigate=false. 5620 /* virtual */ 5621 nsresult nsGlobalWindowOuter::OpenNoNavigate(const nsACString& aUrl, 5622 const nsAString& aName, 5623 const nsAString& aOptions, 5624 BrowsingContext** _retval) { 5625 return OpenInternal(aUrl, aName, aOptions, 5626 false, // aDialog 5627 true, // aCalledNoScript 5628 false, // aDoJSFixups 5629 false, // aNavigate 5630 nullptr, // No args 5631 nullptr, // aLoadState 5632 false, // aForceNoOpener 5633 PrintKind::None, _retval); 5634 } 5635 5636 Nullable<WindowProxyHolder> nsGlobalWindowOuter::OpenDialogOuter( 5637 JSContext* aCx, const nsAString& aUrl, const nsAString& aName, 5638 const nsAString& aOptions, const Sequence<JS::Value>& aExtraArgument, 5639 ErrorResult& aError) { 5640 nsCOMPtr<nsIJSArgArray> argvArray; 5641 aError = 5642 NS_CreateJSArgv(aCx, aExtraArgument.Length(), aExtraArgument.Elements(), 5643 getter_AddRefs(argvArray)); 5644 if (aError.Failed()) { 5645 return nullptr; 5646 } 5647 5648 RefPtr<BrowsingContext> dialog; 5649 aError = OpenInternal(NS_ConvertUTF16toUTF8(aUrl), aName, aOptions, 5650 true, // aDialog 5651 false, // aCalledNoScript 5652 false, // aDoJSFixups 5653 true, // aNavigate 5654 argvArray, // Arguments 5655 nullptr, // aLoadState 5656 false, // aForceNoOpener 5657 PrintKind::None, getter_AddRefs(dialog)); 5658 if (!dialog) { 5659 return nullptr; 5660 } 5661 return WindowProxyHolder(std::move(dialog)); 5662 } 5663 5664 WindowProxyHolder nsGlobalWindowOuter::GetFramesOuter() { 5665 RefPtr<nsPIDOMWindowOuter> frames(this); 5666 FlushPendingNotifications(FlushType::ContentAndNotify); 5667 return WindowProxyHolder(mBrowsingContext); 5668 } 5669 5670 /* static */ 5671 bool nsGlobalWindowOuter::GatherPostMessageData( 5672 JSContext* aCx, const nsAString& aTargetOrigin, BrowsingContext** aSource, 5673 nsAString& aOrigin, nsIURI** aTargetOriginURI, 5674 nsIPrincipal** aCallerPrincipal, nsGlobalWindowInner** aCallerInnerWindow, 5675 nsIURI** aCallerURI, Maybe<nsID>* aCallerAgentClusterId, 5676 nsACString* aScriptLocation, ErrorResult& aError) { 5677 // 5678 // Window.postMessage is an intentional subversion of the same-origin policy. 5679 // As such, this code must be particularly careful in the information it 5680 // exposes to calling code. 5681 // 5682 // http://www.whatwg.org/specs/web-apps/current-work/multipage/section-crossDocumentMessages.html 5683 // 5684 5685 // First, get the caller's window 5686 RefPtr<nsGlobalWindowInner> callerInnerWin = 5687 nsContentUtils::IncumbentInnerWindow(); 5688 nsIPrincipal* callerPrin; 5689 if (callerInnerWin) { 5690 RefPtr<Document> doc = callerInnerWin->GetExtantDoc(); 5691 if (!doc) { 5692 return false; 5693 } 5694 NS_IF_ADDREF(*aCallerURI = doc->GetDocumentURI()); 5695 5696 // Compute the caller's origin either from its principal or, in the case the 5697 // principal doesn't carry a URI (e.g. the system principal), the caller's 5698 // document. We must get this now instead of when the event is created and 5699 // dispatched, because ultimately it is the identity of the calling window 5700 // *now* that determines who sent the message (and not an identity which 5701 // might have changed due to intervening navigations). 5702 callerPrin = callerInnerWin->GetPrincipal(); 5703 } else { 5704 // In case the global is not a window, it can be a sandbox, and the 5705 // sandbox's principal can be used for the security check. 5706 nsIGlobalObject* global = GetIncumbentGlobal(); 5707 NS_ASSERTION(global, "Why is there no global object?"); 5708 callerPrin = global->PrincipalOrNull(); 5709 if (callerPrin) { 5710 BasePrincipal::Cast(callerPrin)->GetScriptLocation(*aScriptLocation); 5711 } 5712 } 5713 if (!callerPrin) { 5714 return false; 5715 } 5716 5717 // if the principal has a URI, use that to generate the origin 5718 if (!callerPrin->IsSystemPrincipal()) { 5719 nsAutoCString webExposedOriginSerialization; 5720 callerPrin->GetWebExposedOriginSerialization(webExposedOriginSerialization); 5721 CopyUTF8toUTF16(webExposedOriginSerialization, aOrigin); 5722 } else if (callerInnerWin) { 5723 if (!*aCallerURI) { 5724 return false; 5725 } 5726 // otherwise use the URI of the document to generate origin 5727 nsContentUtils::GetWebExposedOriginSerialization(*aCallerURI, aOrigin); 5728 } else { 5729 // in case of a sandbox with a system principal origin can be empty 5730 if (!callerPrin->IsSystemPrincipal()) { 5731 return false; 5732 } 5733 } 5734 NS_IF_ADDREF(*aCallerPrincipal = callerPrin); 5735 5736 // "/" indicates same origin as caller, "*" indicates no specific origin is 5737 // required. 5738 if (!aTargetOrigin.EqualsASCII("/") && !aTargetOrigin.EqualsASCII("*")) { 5739 nsCOMPtr<nsIURI> targetOriginURI; 5740 if (NS_FAILED(NS_NewURI(getter_AddRefs(targetOriginURI), aTargetOrigin))) { 5741 aError.Throw(NS_ERROR_DOM_SYNTAX_ERR); 5742 return false; 5743 } 5744 5745 nsresult rv = NS_MutateURI(targetOriginURI) 5746 .SetUserPass(""_ns) 5747 .SetPathQueryRef(""_ns) 5748 .Finalize(aTargetOriginURI); 5749 if (NS_FAILED(rv)) { 5750 return false; 5751 } 5752 } 5753 5754 if (!nsContentUtils::IsCallerChrome() && callerInnerWin && 5755 callerInnerWin->GetOuterWindowInternal()) { 5756 NS_ADDREF(*aSource = callerInnerWin->GetOuterWindowInternal() 5757 ->GetBrowsingContext()); 5758 } else { 5759 *aSource = nullptr; 5760 } 5761 5762 if (aCallerAgentClusterId && callerInnerWin && 5763 callerInnerWin->GetDocGroup()) { 5764 *aCallerAgentClusterId = 5765 Some(callerInnerWin->GetDocGroup()->AgentClusterId()); 5766 } 5767 5768 callerInnerWin.forget(aCallerInnerWindow); 5769 5770 return true; 5771 } 5772 5773 bool nsGlobalWindowOuter::GetPrincipalForPostMessage( 5774 const nsAString& aTargetOrigin, nsIURI* aTargetOriginURI, 5775 nsIPrincipal* aCallerPrincipal, nsIPrincipal& aSubjectPrincipal, 5776 nsIPrincipal** aProvidedPrincipal) { 5777 // 5778 // Window.postMessage is an intentional subversion of the same-origin policy. 5779 // As such, this code must be particularly careful in the information it 5780 // exposes to calling code. 5781 // 5782 // http://www.whatwg.org/specs/web-apps/current-work/multipage/section-crossDocumentMessages.html 5783 // 5784 5785 // Convert the provided origin string into a URI for comparison purposes. 5786 nsCOMPtr<nsIPrincipal> providedPrincipal; 5787 5788 if (aTargetOrigin.EqualsASCII("/")) { 5789 providedPrincipal = aCallerPrincipal; 5790 } 5791 // "*" indicates no specific origin is required. 5792 else if (!aTargetOrigin.EqualsASCII("*")) { 5793 OriginAttributes attrs = aSubjectPrincipal.OriginAttributesRef(); 5794 if (aSubjectPrincipal.IsSystemPrincipal()) { 5795 auto principal = BasePrincipal::Cast(GetPrincipal()); 5796 5797 if (attrs != principal->OriginAttributesRef()) { 5798 nsAutoCString targetURL; 5799 nsAutoCString sourceOrigin; 5800 nsAutoCString targetOrigin; 5801 5802 if (NS_FAILED(principal->GetAsciiSpec(targetURL)) || 5803 NS_FAILED(principal->GetOrigin(targetOrigin)) || 5804 NS_FAILED(aSubjectPrincipal.GetOrigin(sourceOrigin))) { 5805 NS_WARNING("Failed to get source and target origins"); 5806 return false; 5807 } 5808 5809 nsContentUtils::LogSimpleConsoleError( 5810 NS_ConvertUTF8toUTF16(nsPrintfCString( 5811 R"(Attempting to post a message to window with url "%s" and )" 5812 R"(origin "%s" from a system principal scope with mismatched )" 5813 R"(origin "%s".)", 5814 targetURL.get(), targetOrigin.get(), sourceOrigin.get())), 5815 "DOM"_ns, !!principal->PrivateBrowsingId(), 5816 principal->IsSystemPrincipal()); 5817 5818 attrs = principal->OriginAttributesRef(); 5819 } 5820 } 5821 5822 // Create a nsIPrincipal inheriting the app/browser attributes from the 5823 // caller. 5824 providedPrincipal = 5825 BasePrincipal::CreateContentPrincipal(aTargetOriginURI, attrs); 5826 if (NS_WARN_IF(!providedPrincipal)) { 5827 return false; 5828 } 5829 } else { 5830 // We still need to check the originAttributes if the target origin is '*'. 5831 // But we will ingore the FPD here since the FPDs are possible to be 5832 // different. 5833 auto principal = BasePrincipal::Cast(GetPrincipal()); 5834 NS_ENSURE_TRUE(principal, false); 5835 5836 OriginAttributes targetAttrs = principal->OriginAttributesRef(); 5837 OriginAttributes sourceAttrs = aSubjectPrincipal.OriginAttributesRef(); 5838 // We have to exempt the check of OA if the subject prioncipal is a system 5839 // principal since there are many tests try to post messages to content from 5840 // chrome with a mismatch OA. For example, using the ContentTask.spawn() to 5841 // post a message into a private browsing window. The injected code in 5842 // ContentTask.spawn() will be executed under the system principal and the 5843 // OA of the system principal mismatches with the OA of a private browsing 5844 // window. 5845 MOZ_DIAGNOSTIC_ASSERT(aSubjectPrincipal.IsSystemPrincipal() || 5846 sourceAttrs.EqualsIgnoringFPD(targetAttrs)); 5847 5848 // If 'privacy.firstparty.isolate.block_post_message' is true, we will block 5849 // postMessage across different first party domains. 5850 if (OriginAttributes::IsBlockPostMessageForFPI() && 5851 !aSubjectPrincipal.IsSystemPrincipal() && 5852 sourceAttrs.mFirstPartyDomain != targetAttrs.mFirstPartyDomain) { 5853 return false; 5854 } 5855 } 5856 5857 providedPrincipal.forget(aProvidedPrincipal); 5858 return true; 5859 } 5860 5861 void nsGlobalWindowOuter::PostMessageMozOuter(JSContext* aCx, 5862 JS::Handle<JS::Value> aMessage, 5863 const nsAString& aTargetOrigin, 5864 JS::Handle<JS::Value> aTransfer, 5865 nsIPrincipal& aSubjectPrincipal, 5866 ErrorResult& aError) { 5867 RefPtr<BrowsingContext> sourceBc; 5868 nsAutoString origin; 5869 nsCOMPtr<nsIURI> targetOriginURI; 5870 nsCOMPtr<nsIPrincipal> callerPrincipal; 5871 RefPtr<nsGlobalWindowInner> callerInnerWindow; 5872 nsCOMPtr<nsIURI> callerURI; 5873 Maybe<nsID> callerAgentClusterId = Nothing(); 5874 nsAutoCString scriptLocation; 5875 if (!GatherPostMessageData( 5876 aCx, aTargetOrigin, getter_AddRefs(sourceBc), origin, 5877 getter_AddRefs(targetOriginURI), getter_AddRefs(callerPrincipal), 5878 getter_AddRefs(callerInnerWindow), getter_AddRefs(callerURI), 5879 &callerAgentClusterId, &scriptLocation, aError)) { 5880 return; 5881 } 5882 5883 nsCOMPtr<nsIPrincipal> providedPrincipal; 5884 if (!GetPrincipalForPostMessage(aTargetOrigin, targetOriginURI, 5885 callerPrincipal, aSubjectPrincipal, 5886 getter_AddRefs(providedPrincipal))) { 5887 return; 5888 } 5889 5890 // Create and asynchronously dispatch a runnable which will handle actual DOM 5891 // event creation and dispatch. 5892 RefPtr<PostMessageEvent> event = new PostMessageEvent( 5893 sourceBc, origin, this, providedPrincipal, 5894 callerInnerWindow ? callerInnerWindow->WindowID() : 0, callerURI, 5895 scriptLocation, callerAgentClusterId); 5896 5897 JS::CloneDataPolicy clonePolicy; 5898 5899 if (GetDocGroup() && callerAgentClusterId.isSome() && 5900 GetDocGroup()->AgentClusterId().Equals(callerAgentClusterId.value())) { 5901 clonePolicy.allowIntraClusterClonableSharedObjects(); 5902 } 5903 5904 if (callerInnerWindow && callerInnerWindow->IsSharedMemoryAllowed()) { 5905 clonePolicy.allowSharedMemoryObjects(); 5906 } 5907 5908 event->Write(aCx, aMessage, aTransfer, clonePolicy, aError); 5909 if (NS_WARN_IF(aError.Failed())) { 5910 return; 5911 } 5912 5913 event->DispatchToTargetThread(aError); 5914 } 5915 5916 class nsCloseEvent : public Runnable { 5917 RefPtr<nsGlobalWindowOuter> mWindow; 5918 bool mIndirect; 5919 5920 nsCloseEvent(nsGlobalWindowOuter* aWindow, bool aIndirect) 5921 : mozilla::Runnable("nsCloseEvent"), 5922 mWindow(aWindow), 5923 mIndirect(aIndirect) {} 5924 5925 public: 5926 static nsresult PostCloseEvent(nsGlobalWindowOuter* aWindow, bool aIndirect) { 5927 nsCOMPtr<nsIRunnable> ev = new nsCloseEvent(aWindow, aIndirect); 5928 return aWindow->Dispatch(ev.forget()); 5929 } 5930 5931 NS_IMETHOD Run() override { 5932 if (mWindow) { 5933 if (mIndirect) { 5934 return PostCloseEvent(mWindow, false); 5935 } 5936 mWindow->ReallyCloseWindow(); 5937 } 5938 return NS_OK; 5939 } 5940 }; 5941 5942 bool nsGlobalWindowOuter::CanClose() { 5943 if (mIsChrome) { 5944 nsCOMPtr<nsIBrowserDOMWindow> bwin = GetBrowserDOMWindow(); 5945 5946 bool canClose = true; 5947 if (bwin && NS_SUCCEEDED(bwin->CanClose(&canClose))) { 5948 return canClose; 5949 } 5950 } 5951 5952 if (!mDocShell) { 5953 return true; 5954 } 5955 5956 nsCOMPtr<nsIDocumentViewer> viewer; 5957 mDocShell->GetDocViewer(getter_AddRefs(viewer)); 5958 if (viewer) { 5959 bool canClose; 5960 nsresult rv = viewer->PermitUnload(&canClose); 5961 // PermitUnload can destroy the docshell. 5962 if (!mDocShell || mDocShell->IsBeingDestroyed()) { 5963 return true; 5964 } 5965 if (NS_SUCCEEDED(rv) && !canClose) { 5966 return false; 5967 } 5968 } 5969 5970 // If we still have to print, we delay the closing until print has happened. 5971 if (mShouldDelayPrintUntilAfterLoad && mDelayedPrintUntilAfterLoad) { 5972 mDelayedCloseForPrinting = true; 5973 return false; 5974 } 5975 5976 return true; 5977 } 5978 5979 void nsGlobalWindowOuter::CloseOuter(bool aTrustedCaller) { 5980 if (!mDocShell || IsInModalState() || mBrowsingContext->IsSubframe()) { 5981 // window.close() is called on a frame in a frameset, on a window 5982 // that's already closed, or on a window for which there's 5983 // currently a modal dialog open. Ignore such calls. 5984 return; 5985 } 5986 5987 if (mHavePendingClose) { 5988 // We're going to be closed anyway; do nothing since we don't want 5989 // to double-close 5990 return; 5991 } 5992 5993 if (mBlockScriptedClosingFlag) { 5994 // A script's popup has been blocked and we don't want 5995 // the window to be closed directly after this event, 5996 // so the user can see that there was a blocked popup. 5997 return; 5998 } 5999 6000 // Don't allow scripts from content to close non-neterror windows that 6001 // were not opened by script. 6002 if (mDoc) { 6003 nsAutoString url; 6004 nsresult rv = mDoc->GetURL(url); 6005 NS_ENSURE_SUCCESS_VOID(rv); 6006 6007 RefPtr<ChildSHistory> csh = 6008 nsDocShell::Cast(mDocShell)->GetSessionHistory(); 6009 6010 if (!StringBeginsWith(url, u"about:neterror"_ns) && 6011 // we want about:torconnect pages to be able to close themselves after 6012 // bootstrap 6013 !StringBeginsWith(url, u"about:torconnect"_ns) && 6014 !mBrowsingContext->GetTopLevelCreatedByWebContent() && 6015 !aTrustedCaller && csh && csh->Count() > 1) { 6016 bool allowClose = 6017 mAllowScriptsToClose || 6018 Preferences::GetBool("dom.allow_scripts_to_close_windows", true); 6019 if (!allowClose) { 6020 // We're blocking the close operation 6021 // report localized error msg in JS console 6022 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, 6023 "DOM Window"_ns, 6024 mDoc, // Better name for the category? 6025 nsContentUtils::eDOM_PROPERTIES, 6026 "WindowCloseByScriptBlockedWarning"); 6027 6028 return; 6029 } 6030 } 6031 } 6032 6033 if (!mInClose && !mIsClosed && !CanClose()) { 6034 return; 6035 } 6036 6037 // Fire a DOM event notifying listeners that this window is about to 6038 // be closed. The tab UI code may choose to cancel the default 6039 // action for this event, if so, we won't actually close the window 6040 // (since the tab UI code will close the tab in stead). Sure, this 6041 // could be abused by content code, but do we care? I don't think 6042 // so... 6043 6044 bool wasInClose = mInClose; 6045 mInClose = true; 6046 6047 if (!DispatchCustomEvent(u"DOMWindowClose"_ns, ChromeOnlyDispatch::eYes)) { 6048 // Someone chose to prevent the default action for this event, if 6049 // so, let's not close this window after all... 6050 6051 mInClose = wasInClose; 6052 return; 6053 } 6054 6055 FinalClose(); 6056 } 6057 6058 nsresult nsGlobalWindowOuter::Close() { 6059 CloseOuter(/* aTrustedCaller = */ true); 6060 return NS_OK; 6061 } 6062 6063 void nsGlobalWindowOuter::ForceClose() { 6064 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); 6065 6066 if (mBrowsingContext->IsSubframe() || !mDocShell) { 6067 // This may be a frame in a frameset, or a window that's already closed. 6068 // Ignore such calls. 6069 return; 6070 } 6071 6072 if (mHavePendingClose) { 6073 // We're going to be closed anyway; do nothing since we don't want 6074 // to double-close 6075 return; 6076 } 6077 6078 mInClose = true; 6079 6080 DispatchCustomEvent(u"DOMWindowClose"_ns, ChromeOnlyDispatch::eYes); 6081 6082 FinalClose(); 6083 } 6084 6085 void nsGlobalWindowOuter::FinalClose() { 6086 // Flag that we were closed. 6087 mIsClosed = true; 6088 6089 if (!mBrowsingContext->IsDiscarded()) { 6090 MOZ_ALWAYS_SUCCEEDS(mBrowsingContext->SetClosed(true)); 6091 } 6092 6093 // If we get here from CloseOuter then it means that the parent process is 6094 // going to close our window for us. It's just important to set mIsClosed. 6095 if (XRE_GetProcessType() == GeckoProcessType_Content) { 6096 return; 6097 } 6098 6099 // This stuff is non-sensical but incredibly fragile. The reasons for the 6100 // behavior here don't make sense today and may not have ever made sense, 6101 // but various bits of frontend code break when you change them. If you need 6102 // to fix up this behavior, feel free to. It's a righteous task, but involves 6103 // wrestling with various download manager tests, frontend code, and possible 6104 // broken addons. The chrome tests in toolkit/mozapps/downloads are a good 6105 // testing ground. 6106 // 6107 // In particular, if some inner of |win| is the entry global, we must 6108 // complete _two_ round-trips to the event loop before the call to 6109 // ReallyCloseWindow. This allows setTimeout handlers that are set after 6110 // FinalClose() is called to run before the window is torn down. 6111 nsCOMPtr<nsPIDOMWindowInner> entryWindow = 6112 do_QueryInterface(GetEntryGlobal()); 6113 bool indirect = entryWindow && entryWindow->GetOuterWindow() == this; 6114 if (NS_FAILED(nsCloseEvent::PostCloseEvent(this, indirect))) { 6115 ReallyCloseWindow(); 6116 } else { 6117 mHavePendingClose = true; 6118 } 6119 } 6120 6121 void nsGlobalWindowOuter::ReallyCloseWindow() { 6122 // Make sure we never reenter this method. 6123 mHavePendingClose = true; 6124 6125 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow(); 6126 if (!treeOwnerAsWin) { 6127 return; 6128 } 6129 6130 treeOwnerAsWin->Destroy(); 6131 CleanUp(); 6132 } 6133 6134 void nsGlobalWindowOuter::SuppressEventHandling() { 6135 if (mSuppressEventHandlingDepth == 0) { 6136 if (BrowsingContext* bc = GetBrowsingContext()) { 6137 bc->PreOrderWalk([&](BrowsingContext* aBC) { 6138 if (nsCOMPtr<nsPIDOMWindowOuter> win = aBC->GetDOMWindow()) { 6139 if (RefPtr<Document> doc = win->GetExtantDoc()) { 6140 mSuspendedDocs.AppendElement(doc); 6141 // Note: Document::SuppressEventHandling will also automatically 6142 // suppress event handling for any in-process sub-documents. 6143 // However, since we need to deal with cases where remote 6144 // BrowsingContexts may be interleaved with in-process ones, we 6145 // still need to walk the entire tree ourselves. This may be 6146 // slightly redundant in some cases, but since event handling 6147 // suppressions maintain a count of current blockers, it does not 6148 // cause any problems. 6149 doc->SuppressEventHandling(); 6150 } 6151 } 6152 }); 6153 } 6154 } 6155 mSuppressEventHandlingDepth++; 6156 } 6157 6158 void nsGlobalWindowOuter::UnsuppressEventHandling() { 6159 MOZ_ASSERT(mSuppressEventHandlingDepth != 0); 6160 mSuppressEventHandlingDepth--; 6161 6162 if (mSuppressEventHandlingDepth == 0 && mSuspendedDocs.Length()) { 6163 RefPtr<Document> currentDoc = GetExtantDoc(); 6164 bool fireEvent = currentDoc == mSuspendedDocs[0]; 6165 nsTArray<RefPtr<Document>> suspendedDocs = std::move(mSuspendedDocs); 6166 for (const auto& doc : suspendedDocs) { 6167 doc->UnsuppressEventHandlingAndFireEvents(fireEvent); 6168 } 6169 } 6170 } 6171 6172 nsGlobalWindowOuter* nsGlobalWindowOuter::EnterModalState() { 6173 // GetInProcessScriptableTop, not GetInProcessTop, so that EnterModalState 6174 // works properly with <iframe mozbrowser>. 6175 nsGlobalWindowOuter* topWin = GetInProcessScriptableTopInternal(); 6176 6177 if (!topWin) { 6178 NS_ERROR("Uh, EnterModalState() called w/o a reachable top window?"); 6179 return nullptr; 6180 } 6181 6182 // If there is an active ESM in this window, clear it. Otherwise, this can 6183 // cause a problem if a modal state is entered during a mouseup event. 6184 EventStateManager* activeESM = static_cast<EventStateManager*>( 6185 EventStateManager::GetActiveEventStateManager()); 6186 if (activeESM && activeESM->GetPresContext()) { 6187 PresShell* activePresShell = activeESM->GetPresContext()->GetPresShell(); 6188 if (activePresShell && (nsContentUtils::ContentIsCrossDocDescendantOf( 6189 activePresShell->GetDocument(), mDoc) || 6190 nsContentUtils::ContentIsCrossDocDescendantOf( 6191 mDoc, activePresShell->GetDocument()))) { 6192 EventStateManager::ClearGlobalActiveContent(activeESM); 6193 6194 PresShell::ReleaseCapturingContent(); 6195 6196 if (activePresShell) { 6197 RefPtr<nsFrameSelection> frameSelection = 6198 activePresShell->FrameSelection(); 6199 frameSelection->SetDragState(false); 6200 } 6201 } 6202 } 6203 6204 // If there are any drag and drop operations in flight, try to end them. 6205 nsCOMPtr<nsIDragService> ds = 6206 do_GetService("@mozilla.org/widget/dragservice;1"); 6207 if (ds && topWin->GetDocShell()) { 6208 if (PresShell* presShell = topWin->GetDocShell()->GetPresShell()) { 6209 if (RefPtr<nsIWidget> widget = presShell->GetRootWidget()) { 6210 if (nsCOMPtr<nsIDragSession> session = ds->GetCurrentSession(widget)) { 6211 session->EndDragSession(true, 0); 6212 } 6213 } 6214 } 6215 } 6216 6217 // Clear the capturing content if it is under topDoc. 6218 // Usually the activeESM check above does that, but there are cases when 6219 // we don't have activeESM, or it is for different document. 6220 Document* topDoc = topWin->GetExtantDoc(); 6221 nsIContent* capturingContent = PresShell::GetCapturingContent(); 6222 if (capturingContent && topDoc && 6223 nsContentUtils::ContentIsCrossDocDescendantOf(capturingContent, topDoc)) { 6224 PresShell::ReleaseCapturingContent(); 6225 } 6226 6227 if (topWin->mModalStateDepth == 0) { 6228 topWin->SuppressEventHandling(); 6229 6230 if (nsGlobalWindowInner* inner = GetCurrentInnerWindowInternal(topWin)) { 6231 inner->Suspend(); 6232 } 6233 } 6234 topWin->mModalStateDepth++; 6235 return topWin; 6236 } 6237 6238 void nsGlobalWindowOuter::LeaveModalState() { 6239 { 6240 nsGlobalWindowOuter* topWin = GetInProcessScriptableTopInternal(); 6241 if (!topWin) { 6242 NS_WARNING("Uh, LeaveModalState() called w/o a reachable top window?"); 6243 return; 6244 } 6245 6246 if (topWin != this) { 6247 MOZ_ASSERT(IsSuspended()); 6248 return topWin->LeaveModalState(); 6249 } 6250 } 6251 6252 MOZ_ASSERT(mModalStateDepth != 0); 6253 MOZ_ASSERT(IsSuspended()); 6254 mModalStateDepth--; 6255 6256 nsGlobalWindowInner* inner = GetCurrentInnerWindowInternal(this); 6257 if (mModalStateDepth == 0) { 6258 if (inner) { 6259 inner->Resume(); 6260 } 6261 6262 UnsuppressEventHandling(); 6263 } 6264 6265 // Remember the time of the last dialog quit. 6266 if (auto* bcg = GetBrowsingContextGroup()) { 6267 bcg->SetLastDialogQuitTime(TimeStamp::Now()); 6268 } 6269 6270 if (mModalStateDepth == 0) { 6271 RefPtr<Event> event = NS_NewDOMEvent(inner, nullptr, nullptr); 6272 event->InitEvent(u"endmodalstate"_ns, true, false); 6273 event->SetTrusted(true); 6274 event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true; 6275 DispatchEvent(*event); 6276 } 6277 } 6278 6279 bool nsGlobalWindowOuter::IsInModalState() { 6280 nsGlobalWindowOuter* topWin = GetInProcessScriptableTopInternal(); 6281 6282 if (!topWin) { 6283 // IsInModalState() getting called w/o a reachable top window is a bit 6284 // iffy, but valid enough not to make noise about it. See bug 404828 6285 return false; 6286 } 6287 6288 return topWin->mModalStateDepth != 0; 6289 } 6290 6291 void nsGlobalWindowOuter::NotifyWindowIDDestroyed(const char* aTopic) { 6292 nsCOMPtr<nsIRunnable> runnable = 6293 new WindowDestroyedEvent(this, mWindowID, aTopic); 6294 Dispatch(runnable.forget()); 6295 } 6296 6297 Element* nsGlobalWindowOuter::GetFrameElement(nsIPrincipal& aSubjectPrincipal) { 6298 // Per HTML5, the frameElement getter returns null in cross-origin situations. 6299 Element* element = GetFrameElement(); 6300 if (!element) { 6301 return nullptr; 6302 } 6303 6304 if (!aSubjectPrincipal.SubsumesConsideringDomain(element->NodePrincipal())) { 6305 return nullptr; 6306 } 6307 6308 return element; 6309 } 6310 6311 Element* nsGlobalWindowOuter::GetFrameElement() { 6312 if (!mBrowsingContext || mBrowsingContext->IsTop()) { 6313 return nullptr; 6314 } 6315 return mBrowsingContext->GetEmbedderElement(); 6316 } 6317 6318 namespace { 6319 class ChildCommandDispatcher : public Runnable { 6320 public: 6321 ChildCommandDispatcher(nsPIWindowRoot* aRoot, nsIBrowserChild* aBrowserChild, 6322 nsPIDOMWindowOuter* aWindow, const nsAString& aAction) 6323 : mozilla::Runnable("ChildCommandDispatcher"), 6324 mRoot(aRoot), 6325 mBrowserChild(aBrowserChild), 6326 mWindow(aWindow), 6327 mAction(aAction) {} 6328 6329 NS_IMETHOD Run() override { 6330 AutoTArray<nsCString, 70> enabledCommands, disabledCommands; 6331 mRoot->GetEnabledDisabledCommands(enabledCommands, disabledCommands); 6332 if (enabledCommands.Length() || disabledCommands.Length()) { 6333 BrowserChild* bc = static_cast<BrowserChild*>(mBrowserChild.get()); 6334 bc->SendEnableDisableCommands(mWindow->GetBrowsingContext(), mAction, 6335 enabledCommands, disabledCommands); 6336 } 6337 6338 return NS_OK; 6339 } 6340 6341 private: 6342 nsCOMPtr<nsPIWindowRoot> mRoot; 6343 nsCOMPtr<nsIBrowserChild> mBrowserChild; 6344 nsCOMPtr<nsPIDOMWindowOuter> mWindow; 6345 nsString mAction; 6346 }; 6347 6348 class CommandDispatcher : public Runnable { 6349 public: 6350 CommandDispatcher(nsIDOMXULCommandDispatcher* aDispatcher, 6351 const nsAString& aAction) 6352 : mozilla::Runnable("CommandDispatcher"), 6353 mDispatcher(aDispatcher), 6354 mAction(aAction) {} 6355 6356 // TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230, bug 1535398) 6357 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD Run() override { 6358 return mDispatcher->UpdateCommands(mAction); 6359 } 6360 6361 const nsCOMPtr<nsIDOMXULCommandDispatcher> mDispatcher; 6362 nsString mAction; 6363 }; 6364 } // anonymous namespace 6365 6366 void nsGlobalWindowOuter::UpdateCommands(const nsAString& anAction) { 6367 // If this is a child process, redirect to the parent process. 6368 if (nsIDocShell* docShell = GetDocShell()) { 6369 if (nsCOMPtr<nsIBrowserChild> child = docShell->GetBrowserChild()) { 6370 nsCOMPtr<nsPIWindowRoot> root = GetTopWindowRoot(); 6371 if (root) { 6372 nsContentUtils::AddScriptRunner( 6373 new ChildCommandDispatcher(root, child, this, anAction)); 6374 } 6375 return; 6376 } 6377 } 6378 6379 nsPIDOMWindowOuter* rootWindow = GetPrivateRoot(); 6380 if (!rootWindow) { 6381 return; 6382 } 6383 6384 Document* doc = rootWindow->GetExtantDoc(); 6385 6386 if (!doc) { 6387 return; 6388 } 6389 6390 // Retrieve the command dispatcher and call updateCommands on it. 6391 nsIDOMXULCommandDispatcher* xulCommandDispatcher = 6392 doc->GetCommandDispatcher(); 6393 if (xulCommandDispatcher) { 6394 nsContentUtils::AddScriptRunner( 6395 new CommandDispatcher(xulCommandDispatcher, anAction)); 6396 } 6397 } 6398 6399 Selection* nsGlobalWindowOuter::GetSelectionOuter() { 6400 if (!mDocShell) { 6401 return nullptr; 6402 } 6403 6404 PresShell* presShell = mDocShell->GetPresShell(); 6405 if (!presShell) { 6406 // Force layout of the containing frame. 6407 // layout/reftests/selection/modify-range.html goes 6408 // through here. 6409 EnsureSizeAndPositionUpToDate(); 6410 if (!mDocShell) { 6411 return nullptr; 6412 } 6413 presShell = mDocShell->GetPresShell(); 6414 if (!presShell) { 6415 return nullptr; 6416 } 6417 } 6418 return presShell->GetCurrentSelection(SelectionType::eNormal); 6419 } 6420 6421 already_AddRefed<Selection> nsGlobalWindowOuter::GetSelection() { 6422 RefPtr<Selection> selection = GetSelectionOuter(); 6423 return selection.forget(); 6424 } 6425 6426 bool nsGlobalWindowOuter::FindOuter(const nsAString& aString, 6427 bool aCaseSensitive, bool aBackwards, 6428 bool aWrapAround, bool aWholeWord, 6429 bool aSearchInFrames, bool aShowDialog, 6430 ErrorResult& aError) { 6431 (void)aShowDialog; 6432 6433 nsCOMPtr<nsIWebBrowserFind> finder(do_GetInterface(mDocShell)); 6434 if (!finder) { 6435 aError.Throw(NS_ERROR_NOT_AVAILABLE); 6436 return false; 6437 } 6438 6439 // Set the options of the search 6440 aError = finder->SetSearchString(aString); 6441 if (aError.Failed()) { 6442 return false; 6443 } 6444 finder->SetMatchCase(aCaseSensitive); 6445 finder->SetFindBackwards(aBackwards); 6446 finder->SetWrapFind(aWrapAround); 6447 finder->SetEntireWord(aWholeWord); 6448 finder->SetSearchFrames(aSearchInFrames); 6449 6450 // the nsIWebBrowserFind is initialized to use this window 6451 // as the search root, but uses focus to set the current search 6452 // frame. If we're being called from JS (as here), this window 6453 // should be the current search frame. 6454 nsCOMPtr<nsIWebBrowserFindInFrames> framesFinder(do_QueryInterface(finder)); 6455 if (framesFinder) { 6456 framesFinder->SetRootSearchFrame(this); // paranoia 6457 framesFinder->SetCurrentSearchFrame(this); 6458 } 6459 6460 if (aString.IsEmpty()) { 6461 return false; 6462 } 6463 6464 // Launch the search with the passed in search string 6465 bool didFind = false; 6466 aError = finder->FindNext(&didFind); 6467 return didFind; 6468 } 6469 6470 //***************************************************************************** 6471 // EventTarget 6472 //***************************************************************************** 6473 6474 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetOwnerGlobalForBindingsInternal() { 6475 return this; 6476 } 6477 6478 nsIGlobalObject* nsGlobalWindowOuter::GetOwnerGlobal() const { 6479 return GetCurrentInnerWindowInternal(this); 6480 } 6481 6482 bool nsGlobalWindowOuter::DispatchEvent(Event& aEvent, CallerType aCallerType, 6483 ErrorResult& aRv) { 6484 FORWARD_TO_INNER(DispatchEvent, (aEvent, aCallerType, aRv), false); 6485 } 6486 6487 bool nsGlobalWindowOuter::ComputeDefaultWantsUntrusted(ErrorResult& aRv) { 6488 // It's OK that we just return false here on failure to create an 6489 // inner. GetOrCreateListenerManager() will likewise fail, and then 6490 // we won't be adding any listeners anyway. 6491 FORWARD_TO_INNER_CREATE(ComputeDefaultWantsUntrusted, (aRv), false); 6492 } 6493 6494 EventListenerManager* nsGlobalWindowOuter::GetOrCreateListenerManager() { 6495 FORWARD_TO_INNER_CREATE(GetOrCreateListenerManager, (), nullptr); 6496 } 6497 6498 EventListenerManager* nsGlobalWindowOuter::GetExistingListenerManager() const { 6499 FORWARD_TO_INNER(GetExistingListenerManager, (), nullptr); 6500 } 6501 6502 //***************************************************************************** 6503 // nsGlobalWindowOuter::nsPIDOMWindow 6504 //***************************************************************************** 6505 6506 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetPrivateParent() { 6507 nsCOMPtr<nsPIDOMWindowOuter> parent = GetInProcessParent(); 6508 6509 if (this == parent) { 6510 nsCOMPtr<nsIContent> chromeElement(do_QueryInterface(mChromeEventHandler)); 6511 if (!chromeElement) 6512 return nullptr; // This is ok, just means a null parent. 6513 6514 Document* doc = chromeElement->GetComposedDoc(); 6515 if (!doc) return nullptr; // This is ok, just means a null parent. 6516 6517 return doc->GetWindow(); 6518 } 6519 6520 return parent; 6521 } 6522 6523 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetPrivateRoot() { 6524 nsCOMPtr<nsPIDOMWindowOuter> top = GetInProcessTop(); 6525 6526 nsCOMPtr<nsIContent> chromeElement(do_QueryInterface(mChromeEventHandler)); 6527 if (chromeElement) { 6528 Document* doc = chromeElement->GetComposedDoc(); 6529 if (doc) { 6530 nsCOMPtr<nsPIDOMWindowOuter> parent = doc->GetWindow(); 6531 if (parent) { 6532 top = parent->GetInProcessTop(); 6533 } 6534 } 6535 } 6536 6537 return top; 6538 } 6539 6540 // This has a caller in Windows-only code (nsNativeAppSupportWin). 6541 Location* nsGlobalWindowOuter::GetLocation() { 6542 // This method can be called on the outer window as well. 6543 FORWARD_TO_INNER(Location, (), nullptr); 6544 } 6545 6546 void nsGlobalWindowOuter::SetIsBackground(bool aIsBackground) { 6547 const bool changed = aIsBackground != IsBackground(); 6548 SetIsBackgroundInternal(aIsBackground); 6549 6550 nsGlobalWindowInner* inner = GetCurrentInnerWindowInternal(this); 6551 if (!inner) { 6552 return; 6553 } 6554 6555 if (changed) { 6556 inner->UpdateBackgroundState(); 6557 if (aIsBackground) { 6558 // Notify gamepadManager we are at the background window, 6559 // we need to stop vibrate. 6560 // Stop the vr telemery time spent when it switches to 6561 // the background window. 6562 inner->StopGamepadHaptics(); 6563 inner->StopVRActivity(); 6564 return; 6565 } 6566 } 6567 // FIXME: Why doing this even if not changed? 6568 inner->SyncGamepadState(); 6569 inner->StartVRActivity(); 6570 } 6571 6572 void nsGlobalWindowOuter::SetIsBackgroundInternal(bool aIsBackground) { 6573 mIsBackground = aIsBackground; 6574 } 6575 6576 void nsGlobalWindowOuter::SetChromeEventHandler( 6577 EventTarget* aChromeEventHandler) { 6578 SetChromeEventHandlerInternal(aChromeEventHandler); 6579 // update the chrome event handler on all our inner windows 6580 RefPtr<nsGlobalWindowInner> inner; 6581 for (PRCList* node = PR_LIST_HEAD(this); node != this; 6582 node = PR_NEXT_LINK(inner)) { 6583 // This cast is only safe if `node != this`, as nsGlobalWindowOuter is also 6584 // in the list. 6585 inner = static_cast<nsGlobalWindowInner*>(node); 6586 NS_ASSERTION(!inner->mOuterWindow || inner->mOuterWindow == this, 6587 "bad outer window pointer"); 6588 inner->SetChromeEventHandlerInternal(aChromeEventHandler); 6589 } 6590 } 6591 6592 void nsGlobalWindowOuter::SetFocusedElement(Element* aElement, 6593 uint32_t aFocusMethod, 6594 bool aNeedsFocus) { 6595 FORWARD_TO_INNER_VOID(SetFocusedElement, 6596 (aElement, aFocusMethod, aNeedsFocus)); 6597 } 6598 6599 uint32_t nsGlobalWindowOuter::GetFocusMethod() { 6600 FORWARD_TO_INNER(GetFocusMethod, (), 0); 6601 } 6602 6603 bool nsGlobalWindowOuter::ShouldShowFocusRing() { 6604 FORWARD_TO_INNER(ShouldShowFocusRing, (), false); 6605 } 6606 6607 bool nsGlobalWindowOuter::TakeFocus(bool aFocus, uint32_t aFocusMethod) { 6608 FORWARD_TO_INNER(TakeFocus, (aFocus, aFocusMethod), false); 6609 } 6610 6611 void nsGlobalWindowOuter::SetReadyForFocus() { 6612 FORWARD_TO_INNER_VOID(SetReadyForFocus, ()); 6613 } 6614 6615 void nsGlobalWindowOuter::PageHidden(bool aIsEnteringBFCacheInParent) { 6616 FORWARD_TO_INNER_VOID(PageHidden, (aIsEnteringBFCacheInParent)); 6617 } 6618 6619 already_AddRefed<nsDOMCSSDeclaration> 6620 nsGlobalWindowOuter::GetComputedStyleHelperOuter(Element& aElt, 6621 const nsAString& aPseudoElt, 6622 bool aDefaultStylesOnly, 6623 ErrorResult& aRv) { 6624 if (!mDoc) { 6625 return nullptr; 6626 } 6627 6628 RefPtr<nsDOMCSSDeclaration> compStyle = NS_NewComputedDOMStyle( 6629 &aElt, aPseudoElt, mDoc, 6630 aDefaultStylesOnly ? nsComputedDOMStyle::StyleType::DefaultOnly 6631 : nsComputedDOMStyle::StyleType::All, 6632 aRv); 6633 6634 return compStyle.forget(); 6635 } 6636 6637 //***************************************************************************** 6638 // nsGlobalWindowOuter::nsIInterfaceRequestor 6639 //***************************************************************************** 6640 6641 nsresult nsGlobalWindowOuter::GetInterfaceInternal(const nsIID& aIID, 6642 void** aSink) { 6643 NS_ENSURE_ARG_POINTER(aSink); 6644 *aSink = nullptr; 6645 6646 if (aIID.Equals(NS_GET_IID(nsIWebNavigation))) { 6647 nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell)); 6648 webNav.forget(aSink); 6649 } else if (aIID.Equals(NS_GET_IID(nsIDocShell))) { 6650 nsCOMPtr<nsIDocShell> docShell = mDocShell; 6651 docShell.forget(aSink); 6652 } 6653 #ifdef NS_PRINTING 6654 else if (aIID.Equals(NS_GET_IID(nsIWebBrowserPrint))) { 6655 if (mDocShell) { 6656 nsCOMPtr<nsIDocumentViewer> viewer; 6657 mDocShell->GetDocViewer(getter_AddRefs(viewer)); 6658 if (viewer) { 6659 nsCOMPtr<nsIWebBrowserPrint> webBrowserPrint(do_QueryInterface(viewer)); 6660 webBrowserPrint.forget(aSink); 6661 } 6662 } 6663 } 6664 #endif 6665 else if (aIID.Equals(NS_GET_IID(nsILoadContext))) { 6666 nsCOMPtr<nsILoadContext> loadContext(do_QueryInterface(mDocShell)); 6667 loadContext.forget(aSink); 6668 } 6669 6670 return *aSink ? NS_OK : NS_ERROR_NO_INTERFACE; 6671 } 6672 6673 NS_IMETHODIMP 6674 nsGlobalWindowOuter::GetInterface(const nsIID& aIID, void** aSink) { 6675 nsresult rv = GetInterfaceInternal(aIID, aSink); 6676 if (rv == NS_ERROR_NO_INTERFACE) { 6677 return QueryInterface(aIID, aSink); 6678 } 6679 return rv; 6680 } 6681 6682 bool nsGlobalWindowOuter::IsSuspended() const { 6683 MOZ_ASSERT(NS_IsMainThread()); 6684 // No inner means we are effectively suspended 6685 if (!mInnerWindow) { 6686 return true; 6687 } 6688 return nsGlobalWindowInner::Cast(mInnerWindow)->IsSuspended(); 6689 } 6690 6691 bool nsGlobalWindowOuter::IsFrozen() const { 6692 MOZ_ASSERT(NS_IsMainThread()); 6693 // No inner means we are effectively frozen 6694 if (!mInnerWindow) { 6695 return true; 6696 } 6697 return nsGlobalWindowInner::Cast(mInnerWindow)->IsFrozen(); 6698 } 6699 6700 nsresult nsGlobalWindowOuter::FireDelayedDOMEvents(bool aIncludeSubWindows) { 6701 FORWARD_TO_INNER(FireDelayedDOMEvents, (aIncludeSubWindows), 6702 NS_ERROR_UNEXPECTED); 6703 } 6704 6705 //***************************************************************************** 6706 // nsGlobalWindowOuter: Window Control Functions 6707 //***************************************************************************** 6708 6709 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetInProcessParentInternal() { 6710 nsCOMPtr<nsPIDOMWindowOuter> parent = GetInProcessParent(); 6711 6712 if (parent && parent != this) { 6713 return parent; 6714 } 6715 6716 return nullptr; 6717 } 6718 6719 void nsGlobalWindowOuter::UnblockScriptedClosing() { 6720 mBlockScriptedClosingFlag = false; 6721 } 6722 6723 class AutoUnblockScriptClosing { 6724 private: 6725 RefPtr<nsGlobalWindowOuter> mWin; 6726 6727 public: 6728 explicit AutoUnblockScriptClosing(nsGlobalWindowOuter* aWin) : mWin(aWin) { 6729 MOZ_ASSERT(mWin); 6730 } 6731 ~AutoUnblockScriptClosing() { 6732 void (nsGlobalWindowOuter::*run)() = 6733 &nsGlobalWindowOuter::UnblockScriptedClosing; 6734 nsCOMPtr<nsIRunnable> caller = NewRunnableMethod( 6735 "AutoUnblockScriptClosing::~AutoUnblockScriptClosing", mWin, run); 6736 mWin->Dispatch(caller.forget()); 6737 } 6738 }; 6739 6740 nsresult nsGlobalWindowOuter::OpenInternal( 6741 const nsACString& aUrl, const nsAString& aName, const nsAString& aOptions, 6742 bool aDialog, bool aCalledNoScript, bool aDoJSFixups, bool aNavigate, 6743 nsIArray* aArguments, nsDocShellLoadState* aLoadState, bool aForceNoOpener, 6744 PrintKind aPrintKind, BrowsingContext** aReturn) { 6745 mozilla::Maybe<AutoUnblockScriptClosing> closeUnblocker; 6746 6747 // Calls to window.open from script should navigate. 6748 MOZ_ASSERT(aCalledNoScript || aNavigate); 6749 6750 *aReturn = nullptr; 6751 6752 nsCOMPtr<nsIWebBrowserChrome> chrome = GetWebBrowserChrome(); 6753 if (!chrome) { 6754 // No chrome means we don't want to go through with this open call 6755 // -- see nsIWindowWatcher.idl 6756 return NS_ERROR_NOT_AVAILABLE; 6757 } 6758 6759 NS_ASSERTION(mDocShell, "Must have docshell here"); 6760 6761 NS_ConvertUTF16toUTF8 optionsUtf8(aOptions); 6762 6763 WindowFeatures features; 6764 if (!features.Tokenize(optionsUtf8)) { 6765 return NS_ERROR_FAILURE; 6766 } 6767 6768 bool forceNoOpener = aForceNoOpener; 6769 if (features.Exists("noopener")) { 6770 forceNoOpener = features.GetBool("noopener"); 6771 features.Remove("noopener"); 6772 } 6773 6774 bool forceNoReferrer = false; 6775 if (features.Exists("noreferrer")) { 6776 forceNoReferrer = features.GetBool("noreferrer"); 6777 if (forceNoReferrer) { 6778 // noreferrer implies noopener 6779 forceNoOpener = true; 6780 } 6781 features.Remove("noreferrer"); 6782 } 6783 6784 nsAutoCString options; 6785 features.Stringify(options); 6786 6787 // If noopener is force-enabled for the current document, then set noopener to 6788 // true, and clear the name to "_blank". 6789 nsAutoString windowName(aName); 6790 if (nsDocShell::Cast(GetDocShell())->NoopenerForceEnabled() && 6791 aPrintKind == PrintKind::None) { 6792 MOZ_DIAGNOSTIC_ASSERT(aNavigate, 6793 "cannot OpenNoNavigate if noopener is force-enabled"); 6794 6795 forceNoOpener = true; 6796 windowName = u"_blank"_ns; 6797 } 6798 6799 bool windowExists = WindowExists(windowName, forceNoOpener, !aCalledNoScript); 6800 6801 // XXXbz When this gets fixed to not use LegacyIsCallerNativeCode() 6802 // (indirectly) maybe we can nix the AutoJSAPI usage OnLinkClickEvent::Run. 6803 // But note that if you change this to GetEntryGlobal(), say, then 6804 // OnLinkClickEvent::Run will need a full-blown AutoEntryScript. (Bug 1930445) 6805 const bool checkForPopup = [&]() { 6806 if (aDialog) { 6807 return false; 6808 } 6809 if (windowExists) { 6810 return false; 6811 } 6812 if (aLoadState && aLoadState->IsFormSubmission()) { 6813 return true; 6814 } 6815 return !nsContentUtils::LegacyIsCallerChromeOrNativeCode(); 6816 }(); 6817 6818 nsCOMPtr<nsIURI> uri; 6819 6820 // It's important to do this security check before determining whether this 6821 // window opening should be blocked, to ensure that we don't 6822 // FirePopupBlockedEvent for a window opening that wouldn't have 6823 // succeeded in the first place. 6824 if (!aUrl.IsEmpty()) { 6825 // It's safe to skip the security check below if we're a dialog because 6826 // window.openDialog is not callable from content script. See bug 56851. 6827 // 6828 // If we're not navigating, we assume that whoever *does* navigate the 6829 // window will do a security check of their own. 6830 auto result = 6831 URIfromURLAndMaybeDoSecurityCheck(aUrl, !aDialog && aNavigate); 6832 if (result.isErr()) { 6833 return result.unwrapErr(); 6834 } 6835 6836 uri = result.unwrap(); 6837 } else if (mDoc) { 6838 mDoc->SetUseCounter(eUseCounter_custom_WindowOpenEmptyUrl); 6839 } 6840 6841 UserActivation::Modifiers modifiers; 6842 mBrowsingContext->GetUserActivationModifiersForPopup(&modifiers); 6843 6844 // Need to create loadState before the user activation is consumed in 6845 // BrowsingContext::RevisePopupAbuseLevel() below. 6846 RefPtr<nsDocShellLoadState> loadState = aLoadState; 6847 if (!loadState && aNavigate && uri) { 6848 loadState = nsWindowWatcher::CreateLoadState(uri, this, aDoJSFixups); 6849 } 6850 6851 PopupBlocker::PopupControlState abuseLevel = 6852 PopupBlocker::GetPopupControlState(); 6853 if (checkForPopup) { 6854 abuseLevel = mBrowsingContext->RevisePopupAbuseLevel(abuseLevel); 6855 if (abuseLevel >= PopupBlocker::openBlocked) { 6856 if (!aCalledNoScript) { 6857 // If script in some other window is doing a window.open on us and 6858 // it's being blocked, then it's OK to close us afterwards, probably. 6859 // But if we're doing a window.open on ourselves and block the popup, 6860 // prevent this window from closing until after this script terminates 6861 // so that whatever popup blocker UI the app has will be visible. 6862 nsCOMPtr<nsPIDOMWindowInner> entryWindow = 6863 do_QueryInterface(GetEntryGlobal()); 6864 // Note that entryWindow can be null here if some JS component was the 6865 // place where script was entered for this JS execution. 6866 if (entryWindow && entryWindow->GetOuterWindow() == this) { 6867 mBlockScriptedClosingFlag = true; 6868 closeUnblocker.emplace(this); 6869 } 6870 } 6871 6872 FirePopupBlockedEvent(uri, windowName, aOptions); 6873 return aDoJSFixups ? NS_OK : NS_ERROR_FAILURE; 6874 } 6875 } 6876 6877 // Per https://github.com/whatwg/html/pull/10547, we should always consume 6878 // user activation when opening a new window, even if the popup blocker is 6879 // disabled or the website has popup permission. 6880 if (!windowExists && mDoc) { 6881 mDoc->ConsumeTransientUserGestureActivation(); 6882 } 6883 6884 RefPtr<BrowsingContext> domReturn; 6885 6886 nsresult rv = NS_OK; 6887 nsCOMPtr<nsIWindowWatcher> wwatch = 6888 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv); 6889 NS_ENSURE_TRUE(wwatch, rv); 6890 6891 NS_ConvertUTF16toUTF8 name(windowName); 6892 6893 nsCOMPtr<nsPIWindowWatcher> pwwatch(do_QueryInterface(wwatch)); 6894 NS_ENSURE_STATE(pwwatch); 6895 6896 MOZ_ASSERT_IF(checkForPopup, abuseLevel < PopupBlocker::openBlocked); 6897 // At this point we should know for a fact that if checkForPopup then 6898 // abuseLevel < PopupBlocker::openBlocked, so we could just check for 6899 // abuseLevel == PopupBlocker::openControlled. But let's be defensive just in 6900 // case and treat anything that fails the above assert as a spam popup too, if 6901 // it ever happens. 6902 bool isPopupSpamWindow = 6903 checkForPopup && (abuseLevel >= PopupBlocker::openControlled); 6904 6905 const auto wwPrintKind = [&] { 6906 switch (aPrintKind) { 6907 case PrintKind::None: 6908 return nsPIWindowWatcher::PRINT_NONE; 6909 case PrintKind::InternalPrint: 6910 return nsPIWindowWatcher::PRINT_INTERNAL; 6911 case PrintKind::WindowDotPrint: 6912 return nsPIWindowWatcher::PRINT_WINDOW_DOT_PRINT; 6913 } 6914 MOZ_ASSERT_UNREACHABLE("Wat"); 6915 return nsPIWindowWatcher::PRINT_NONE; 6916 }(); 6917 6918 { 6919 // Reset popup state while opening a window to prevent the 6920 // current state from being active the whole time a modal 6921 // dialog is open. 6922 AutoPopupStatePusher popupStatePusher(PopupBlocker::openAbused, true); 6923 6924 if (!aCalledNoScript) { 6925 // We asserted at the top of this function that aNavigate is true for 6926 // !aCalledNoScript. 6927 rv = pwwatch->OpenWindow2(this, uri, name, options, modifiers, 6928 /* aCalledFromScript = */ true, aDialog, 6929 aNavigate, aArguments, isPopupSpamWindow, 6930 forceNoOpener, forceNoReferrer, wwPrintKind, 6931 loadState, getter_AddRefs(domReturn)); 6932 } else { 6933 // Force a system caller here so that the window watcher won't screw us 6934 // up. We do NOT want this case looking at the JS context on the stack 6935 // when searching. Compare comments on 6936 // nsIDOMWindow::OpenWindow and nsIWindowWatcher::OpenWindow. 6937 6938 // Note: Because nsWindowWatcher is so broken, it's actually important 6939 // that we don't force a system caller here, because that screws it up 6940 // when it tries to compute the caller principal to associate with dialog 6941 // arguments. That whole setup just really needs to be rewritten. :-( 6942 AutoNoJSAPI nojsapi; 6943 rv = pwwatch->OpenWindow2(this, uri, name, options, modifiers, 6944 /* aCalledFromScript = */ false, aDialog, 6945 aNavigate, aArguments, isPopupSpamWindow, 6946 forceNoOpener, forceNoReferrer, wwPrintKind, 6947 loadState, getter_AddRefs(domReturn)); 6948 } 6949 } 6950 6951 NS_ENSURE_SUCCESS(rv, rv); 6952 6953 // success! 6954 6955 if (!aCalledNoScript && !windowExists && uri && !forceNoOpener) { 6956 MaybeAllowStorageForOpenedWindow(uri); 6957 } 6958 6959 if (domReturn && aDoJSFixups) { 6960 nsPIDOMWindowOuter* outer = domReturn->GetDOMWindow(); 6961 if (outer && !nsGlobalWindowOuter::Cast(outer)->IsChromeWindow()) { 6962 // A new non-chrome window was created from a call to 6963 // window.open() from JavaScript, make sure there's a document in 6964 // the new window. We do this by simply asking the new window for 6965 // its document, this will synchronously create an empty document 6966 // if there is no document in the window. 6967 // XXXbz should this just use EnsureInnerWindow()? 6968 6969 // Force document creation. 6970 nsCOMPtr<Document> doc = outer->GetDoc(); 6971 (void)doc; 6972 } 6973 } 6974 6975 domReturn.forget(aReturn); 6976 return NS_OK; 6977 } 6978 6979 void nsGlobalWindowOuter::MaybeAllowStorageForOpenedWindow(nsIURI* aURI) { 6980 nsGlobalWindowInner* inner = GetCurrentInnerWindowInternal(this); 6981 if (NS_WARN_IF(!inner)) { 6982 return; 6983 } 6984 6985 // Don't trigger the heuristic for third-party trackers. 6986 if (StaticPrefs:: 6987 privacy_restrict3rdpartystorage_heuristic_exclude_third_party_trackers() && 6988 nsContentUtils::IsThirdPartyTrackingResourceWindow(inner)) { 6989 return; 6990 } 6991 6992 // No 3rd party URL/window. 6993 if (!AntiTrackingUtils::IsThirdPartyWindow(inner, aURI)) { 6994 return; 6995 } 6996 6997 Document* doc = inner->GetDoc(); 6998 if (!doc) { 6999 return; 7000 } 7001 nsCOMPtr<nsIPrincipal> principal = BasePrincipal::CreateContentPrincipal( 7002 aURI, doc->NodePrincipal()->OriginAttributesRef()); 7003 7004 // We don't care when the asynchronous work finishes here. 7005 // Without e10s or fission enabled this is run in the parent process. 7006 if (XRE_IsParentProcess()) { 7007 (void)StorageAccessAPIHelper::AllowAccessForOnParentProcess( 7008 principal, GetBrowsingContext(), ContentBlockingNotifier::eOpener); 7009 } else { 7010 (void)StorageAccessAPIHelper::AllowAccessForOnChildProcess( 7011 principal, GetBrowsingContext(), ContentBlockingNotifier::eOpener); 7012 } 7013 } 7014 7015 //***************************************************************************** 7016 // nsGlobalWindowOuter: Helper Functions 7017 //***************************************************************************** 7018 7019 already_AddRefed<nsIDocShellTreeOwner> nsPIDOMWindowOuter::GetTreeOwner() { 7020 // If there's no docShellAsItem, this window must have been closed, 7021 // in that case there is no tree owner. 7022 7023 if (!mDocShell) { 7024 return nullptr; 7025 } 7026 7027 nsCOMPtr<nsIDocShellTreeOwner> treeOwner; 7028 mDocShell->GetTreeOwner(getter_AddRefs(treeOwner)); 7029 return treeOwner.forget(); 7030 } 7031 7032 already_AddRefed<nsIBaseWindow> nsPIDOMWindowOuter::GetTreeOwnerWindow() { 7033 nsCOMPtr<nsIDocShellTreeOwner> treeOwner; 7034 7035 // If there's no mDocShell, this window must have been closed, 7036 // in that case there is no tree owner. 7037 7038 if (mDocShell) { 7039 mDocShell->GetTreeOwner(getter_AddRefs(treeOwner)); 7040 } 7041 7042 nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(treeOwner); 7043 return baseWindow.forget(); 7044 } 7045 7046 already_AddRefed<nsIWebBrowserChrome> 7047 nsPIDOMWindowOuter::GetWebBrowserChrome() { 7048 nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner(); 7049 7050 nsCOMPtr<nsIWebBrowserChrome> browserChrome = do_GetInterface(treeOwner); 7051 return browserChrome.forget(); 7052 } 7053 7054 ScrollContainerFrame* nsGlobalWindowOuter::GetScrollContainerFrame() { 7055 if (!mDocShell) { 7056 return nullptr; 7057 } 7058 7059 PresShell* presShell = mDocShell->GetPresShell(); 7060 if (presShell) { 7061 return presShell->GetRootScrollContainerFrame(); 7062 } 7063 return nullptr; 7064 } 7065 7066 Result<already_AddRefed<nsIURI>, nsresult> 7067 nsGlobalWindowOuter::URIfromURLAndMaybeDoSecurityCheck(const nsACString& aURL, 7068 bool aSecurityCheck) { 7069 nsCOMPtr<nsPIDOMWindowInner> sourceWindow = 7070 do_QueryInterface(GetEntryGlobal()); 7071 if (!sourceWindow) { 7072 sourceWindow = GetCurrentInnerWindow(); 7073 } 7074 7075 // Resolve the baseURI, which could be relative to the calling window. 7076 // 7077 // Note the algorithm to get the base URI should match the one 7078 // used to actually kick off the load in nsWindowWatcher.cpp. 7079 nsCOMPtr<Document> doc = sourceWindow->GetDoc(); 7080 nsIURI* baseURI = nullptr; 7081 auto encoding = UTF_8_ENCODING; // default to utf-8 7082 if (doc) { 7083 baseURI = doc->GetDocBaseURI(); 7084 encoding = doc->GetDocumentCharacterSet(); 7085 } 7086 nsCOMPtr<nsIURI> uri; 7087 nsresult rv = NS_NewURI(getter_AddRefs(uri), aURL, encoding, baseURI); 7088 if (NS_WARN_IF(NS_FAILED(rv))) { 7089 return Err(NS_ERROR_DOM_SYNTAX_ERR); 7090 } 7091 7092 if (aSecurityCheck) { 7093 AutoJSContext cx; 7094 nsGlobalWindowInner* sourceWin = nsGlobalWindowInner::Cast(sourceWindow); 7095 JSAutoRealm ar(cx, sourceWin->GetGlobalJSObject()); 7096 7097 if (NS_FAILED(nsContentUtils::GetSecurityManager()->CheckLoadURIFromScript( 7098 cx, uri))) { 7099 return Err(NS_ERROR_FAILURE); 7100 } 7101 } 7102 7103 return uri.forget(); 7104 } 7105 7106 void nsGlobalWindowOuter::FlushPendingNotifications(FlushType aType) { 7107 if (mDoc) { 7108 mDoc->FlushPendingNotifications(aType); 7109 } 7110 } 7111 7112 void nsGlobalWindowOuter::EnsureSizeAndPositionUpToDate() { 7113 // If we're a subframe, make sure our size is up to date. Make sure to go 7114 // through the document chain rather than the window chain to not flush on 7115 // detached iframes, see bug 1545516. 7116 if (mDoc && mDoc->StyleOrLayoutObservablyDependsOnParentDocumentLayout()) { 7117 RefPtr<Document> parent = mDoc->GetInProcessParentDocument(); 7118 parent->FlushPendingNotifications(FlushType::Layout); 7119 } 7120 } 7121 7122 already_AddRefed<nsISupports> nsGlobalWindowOuter::SaveWindowState() { 7123 MOZ_ASSERT(!mozilla::SessionHistoryInParent()); 7124 7125 if (!mContext || !GetWrapperPreserveColor()) { 7126 // The window may be getting torn down; don't bother saving state. 7127 return nullptr; 7128 } 7129 7130 nsGlobalWindowInner* inner = GetCurrentInnerWindowInternal(this); 7131 NS_ASSERTION(inner, "No inner window to save"); 7132 7133 if (WindowContext* wc = inner->GetWindowContext()) { 7134 MOZ_ASSERT(!wc->GetWindowStateSaved()); 7135 (void)wc->SetWindowStateSaved(true); 7136 } 7137 7138 // Don't do anything else to this inner window! After this point, all 7139 // calls to SetTimeoutOrInterval will create entries in the timeout 7140 // list that will only run after this window has come out of the bfcache. 7141 // Also, while we're frozen, we won't dispatch online/offline events 7142 // to the page. 7143 inner->Freeze(); 7144 7145 nsCOMPtr<nsISupports> state = new WindowStateHolder(inner); 7146 7147 MOZ_LOG(gPageCacheLog, LogLevel::Debug, 7148 ("saving window state, state = %p", (void*)state)); 7149 7150 return state.forget(); 7151 } 7152 7153 nsresult nsGlobalWindowOuter::RestoreWindowState(nsISupports* aState) { 7154 MOZ_ASSERT(!mozilla::SessionHistoryInParent()); 7155 7156 if (!mContext || !GetWrapperPreserveColor()) { 7157 // The window may be getting torn down; don't bother restoring state. 7158 return NS_OK; 7159 } 7160 7161 nsCOMPtr<WindowStateHolder> holder = do_QueryInterface(aState); 7162 NS_ENSURE_TRUE(holder, NS_ERROR_FAILURE); 7163 7164 MOZ_LOG(gPageCacheLog, LogLevel::Debug, 7165 ("restoring window state, state = %p", (void*)holder)); 7166 7167 // And we're ready to go! 7168 nsGlobalWindowInner* inner = GetCurrentInnerWindowInternal(this); 7169 7170 // if a link is focused, refocus with the FLAG_SHOWRING flag set. This makes 7171 // it easy to tell which link was last clicked when going back a page. 7172 RefPtr<Element> focusedElement = inner->GetFocusedElement(); 7173 if (nsContentUtils::ContentIsLink(focusedElement)) { 7174 if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) { 7175 fm->SetFocus(focusedElement, nsIFocusManager::FLAG_NOSCROLL | 7176 nsIFocusManager::FLAG_SHOWRING); 7177 } 7178 } 7179 7180 if (WindowContext* wc = inner->GetWindowContext()) { 7181 MOZ_ASSERT(wc->GetWindowStateSaved()); 7182 (void)wc->SetWindowStateSaved(false); 7183 } 7184 7185 inner->Thaw(); 7186 7187 holder->DidRestoreWindow(); 7188 7189 return NS_OK; 7190 } 7191 7192 void nsGlobalWindowOuter::AddSizeOfIncludingThis( 7193 nsWindowSizes& aWindowSizes) const { 7194 aWindowSizes.mDOMSizes.mDOMOtherSize += 7195 aWindowSizes.mState.mMallocSizeOf(this); 7196 } 7197 7198 uint32_t nsGlobalWindowOuter::GetAutoActivateVRDisplayID() { 7199 uint32_t retVal = mAutoActivateVRDisplayID; 7200 mAutoActivateVRDisplayID = 0; 7201 return retVal; 7202 } 7203 7204 void nsGlobalWindowOuter::SetAutoActivateVRDisplayID( 7205 uint32_t aAutoActivateVRDisplayID) { 7206 mAutoActivateVRDisplayID = aAutoActivateVRDisplayID; 7207 } 7208 7209 already_AddRefed<nsWindowRoot> nsGlobalWindowOuter::GetWindowRootOuter() { 7210 nsCOMPtr<nsPIWindowRoot> root = GetTopWindowRoot(); 7211 return root.forget().downcast<nsWindowRoot>(); 7212 } 7213 7214 nsIDOMWindowUtils* nsGlobalWindowOuter::WindowUtils() { 7215 if (!mWindowUtils) { 7216 mWindowUtils = new nsDOMWindowUtils(this); 7217 } 7218 return mWindowUtils; 7219 } 7220 7221 bool nsGlobalWindowOuter::IsInSyncOperation() { 7222 return GetExtantDoc() && GetExtantDoc()->IsInSyncOperation(); 7223 } 7224 7225 // Note: This call will lock the cursor, it will not change as it moves. 7226 // To unlock, the cursor must be set back to Auto. 7227 void nsGlobalWindowOuter::SetCursorOuter(const nsACString& aCursor, 7228 ErrorResult& aError) { 7229 auto cursor = StyleCursorKind::Auto; 7230 if (!Servo_CursorKind_Parse(&aCursor, &cursor)) { 7231 // FIXME: It's a bit weird that this doesn't throw but stuff below does, but 7232 // matches previous behavior so... 7233 return; 7234 } 7235 7236 RefPtr<nsPresContext> presContext; 7237 if (mDocShell) { 7238 presContext = mDocShell->GetPresContext(); 7239 } 7240 7241 if (presContext) { 7242 PresShell* presShell = mDocShell->GetPresShell(); 7243 if (!presShell) { 7244 aError.Throw(NS_ERROR_FAILURE); 7245 return; 7246 } 7247 7248 // Need root widget. 7249 nsIWidget* widget = presShell->GetRootWidget(); 7250 if (!widget) { 7251 aError.Throw(NS_ERROR_FAILURE); 7252 return; 7253 } 7254 7255 // Call esm and set cursor. 7256 aError = presContext->EventStateManager()->SetCursor( 7257 cursor, nullptr, {}, Nothing(), widget, true); 7258 } 7259 } 7260 7261 nsIBrowserDOMWindow* nsGlobalWindowOuter::GetBrowserDOMWindow() { 7262 MOZ_RELEASE_ASSERT(IsChromeWindow()); 7263 return mChromeFields.mBrowserDOMWindow; 7264 } 7265 7266 void nsGlobalWindowOuter::SetBrowserDOMWindowOuter( 7267 nsIBrowserDOMWindow* aBrowserWindow) { 7268 MOZ_ASSERT(IsChromeWindow()); 7269 mChromeFields.mBrowserDOMWindow = aBrowserWindow; 7270 } 7271 7272 ChromeMessageBroadcaster* nsGlobalWindowOuter::GetMessageManager() { 7273 if (!mInnerWindow) { 7274 NS_WARNING("No inner window available!"); 7275 return nullptr; 7276 } 7277 return GetCurrentInnerWindowInternal(this)->MessageManager(); 7278 } 7279 7280 ChromeMessageBroadcaster* nsGlobalWindowOuter::GetGroupMessageManager( 7281 const nsAString& aGroup) { 7282 if (!mInnerWindow) { 7283 NS_WARNING("No inner window available!"); 7284 return nullptr; 7285 } 7286 return GetCurrentInnerWindowInternal(this)->GetGroupMessageManager(aGroup); 7287 } 7288 7289 void nsGlobalWindowOuter::InitWasOffline() { mWasOffline = NS_IsOffline(); } 7290 7291 #if defined(_WINDOWS_) && !defined(MOZ_WRAPPED_WINDOWS_H) 7292 # pragma message( \ 7293 "wrapper failure reason: " MOZ_WINDOWS_WRAPPER_DISABLED_REASON) 7294 # error "Never include unwrapped windows.h in this file!" 7295 #endif 7296 7297 // Helper called by methods that move/resize the window, 7298 // to ensure the presContext (if any) is aware of resolution 7299 // change that may happen in multi-monitor configuration. 7300 void nsGlobalWindowOuter::CheckForDPIChange() { 7301 if (mDocShell) { 7302 RefPtr<nsPresContext> presContext = mDocShell->GetPresContext(); 7303 if (presContext) { 7304 if (presContext->DeviceContext()->CheckDPIChange()) { 7305 presContext->UIResolutionChanged(); 7306 } 7307 } 7308 } 7309 } 7310 7311 nsresult nsGlobalWindowOuter::Dispatch( 7312 already_AddRefed<nsIRunnable>&& aRunnable) const { 7313 MOZ_RELEASE_ASSERT(NS_IsMainThread()); 7314 return NS_DispatchToCurrentThread(std::move(aRunnable)); 7315 } 7316 7317 nsISerialEventTarget* nsGlobalWindowOuter::SerialEventTarget() const { 7318 MOZ_RELEASE_ASSERT(NS_IsMainThread()); 7319 return GetMainThreadSerialEventTarget(); 7320 } 7321 7322 void nsGlobalWindowOuter::MaybeResetWindowName(Document* aNewDocument) { 7323 MOZ_ASSERT(aNewDocument); 7324 7325 if (!StaticPrefs::privacy_window_name_update_enabled()) { 7326 return; 7327 } 7328 7329 const LoadingSessionHistoryInfo* info = 7330 nsDocShell::Cast(mDocShell)->GetLoadingSessionHistoryInfo(); 7331 if (!info || info->mForceMaybeResetName.isNothing()) { 7332 // We only reset the window name for the top-level content as well as 7333 // storing in session entries. 7334 if (!GetBrowsingContext()->IsTopContent()) { 7335 return; 7336 } 7337 7338 // Following implements https://html.spec.whatwg.org/#history-traversal: 7339 // Step 4.2. Check if the loading document has a different origin than the 7340 // previous document. 7341 7342 // We don't need to do anything if we haven't loaded a non-initial document. 7343 if (!GetBrowsingContext()->GetHasLoadedNonInitialDocument()) { 7344 return; 7345 } 7346 7347 // If we have an existing document, directly check the document prinicpals 7348 // with the new document to know if it is cross-origin. 7349 // 7350 // Note that there will be an issue of initial document handling in Fission 7351 // when running the WPT unset_context_name-1.html. In the test, the first 7352 // about:blank page would be loaded with the principal of the testing domain 7353 // in Fission and the window.name will be set there. Then, The window.name 7354 // won't be reset after navigating to the testing page because the principal 7355 // is the same. But, it won't be the case for non-Fission mode that the 7356 // first about:blank will be loaded with a null principal and the 7357 // window.name will be reset when loading the test page. 7358 if (mDoc && mDoc->NodePrincipal()->Equals(aNewDocument->NodePrincipal())) { 7359 return; 7360 } 7361 7362 // If we don't have an existing document, and if it's not the initial 7363 // about:blank, we could be loading a document because of the 7364 // process-switching. In this case, this should be a cross-origin 7365 // navigation. 7366 } else if (!info->mForceMaybeResetName.ref()) { 7367 return; 7368 } 7369 7370 // Step 4.2.2 Store the window.name into all session history entries that have 7371 // the same origin as the previous document. 7372 nsDocShell::Cast(mDocShell)->StoreWindowNameToSHEntries(); 7373 7374 // Step 4.2.3 Clear the window.name if the browsing context is the top-level 7375 // content and doesn't have an opener. 7376 7377 // We need to reset the window name in case of a cross-origin navigation, 7378 // without an opener. 7379 RefPtr<BrowsingContext> opener = GetOpenerBrowsingContext(); 7380 if (opener) { 7381 return; 7382 } 7383 7384 (void)mBrowsingContext->SetName(EmptyString()); 7385 } 7386 7387 nsGlobalWindowOuter::TemporarilyDisableDialogs::TemporarilyDisableDialogs( 7388 BrowsingContext* aBC) { 7389 BrowsingContextGroup* group = aBC->Group(); 7390 if (!group) { 7391 NS_ERROR( 7392 "nsGlobalWindowOuter::TemporarilyDisableDialogs called without a " 7393 "browsing context group?"); 7394 return; 7395 } 7396 7397 if (group) { 7398 mGroup = group; 7399 mSavedDialogsEnabled = group->GetAreDialogsEnabled(); 7400 group->SetAreDialogsEnabled(false); 7401 } 7402 } 7403 7404 nsGlobalWindowOuter::TemporarilyDisableDialogs::~TemporarilyDisableDialogs() { 7405 if (mGroup) { 7406 mGroup->SetAreDialogsEnabled(mSavedDialogsEnabled); 7407 } 7408 } 7409 7410 /* static */ 7411 already_AddRefed<nsGlobalWindowOuter> nsGlobalWindowOuter::Create( 7412 nsDocShell* aDocShell, bool aIsChrome) { 7413 uint64_t outerWindowID = aDocShell->GetOuterWindowID(); 7414 RefPtr<nsGlobalWindowOuter> window = new nsGlobalWindowOuter(outerWindowID); 7415 if (aIsChrome) { 7416 window->mIsChrome = true; 7417 } 7418 window->SetDocShell(aDocShell); 7419 7420 window->InitWasOffline(); 7421 return window.forget(); 7422 } 7423 7424 nsIURI* nsPIDOMWindowOuter::GetDocumentURI() const { 7425 return mDoc ? mDoc->GetDocumentURI() : mDocumentURI.get(); 7426 } 7427 7428 void nsPIDOMWindowOuter::MaybeCreateDoc() { 7429 MOZ_ASSERT(!mDoc); 7430 if (nsIDocShell* docShell = GetDocShell()) { 7431 // Note that |document| here is the same thing as our mDoc, but we 7432 // don't have to explicitly set the member variable because the docshell 7433 // has already called SetNewDocument(). 7434 nsCOMPtr<Document> document = docShell->GetDocument(); 7435 (void)document; 7436 } 7437 } 7438 7439 void nsPIDOMWindowOuter::SetChromeEventHandlerInternal( 7440 EventTarget* aChromeEventHandler) { 7441 // Out-of-line so we don't need to include ContentFrameMessageManager.h in 7442 // nsPIDOMWindow.h. 7443 mChromeEventHandler = aChromeEventHandler; 7444 7445 // mParentTarget and mMessageManager will be set when the next event is 7446 // dispatched or someone asks for our message manager. 7447 mParentTarget = nullptr; 7448 mMessageManager = nullptr; 7449 } 7450 7451 mozilla::dom::DocGroup* nsPIDOMWindowOuter::GetDocGroup() const { 7452 Document* doc = GetExtantDoc(); 7453 if (doc) { 7454 return doc->GetDocGroup(); 7455 } 7456 return nullptr; 7457 } 7458 7459 nsPIDOMWindowOuter::nsPIDOMWindowOuter(uint64_t aWindowID) 7460 : mFrameElement(nullptr), 7461 mModalStateDepth(0), 7462 mSuppressEventHandlingDepth(0), 7463 mIsBackground(false), 7464 mIsRootOuterWindow(false), 7465 mInnerWindow(nullptr), 7466 mWindowID(aWindowID), 7467 mMarkedCCGeneration(0) {} 7468 7469 nsPIDOMWindowOuter::~nsPIDOMWindowOuter() = default;