WindowGlobalChild.cpp (34798B)
1 /* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ 2 /* vim: set sw=2 ts=8 et tw=80 ft=cpp : */ 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 "mozilla/dom/WindowGlobalChild.h" 8 9 #include "GeckoProfiler.h" 10 #include "mozilla/AntiTrackingUtils.h" 11 #include "mozilla/ClearOnShutdown.h" 12 #include "mozilla/PresShell.h" 13 #include "mozilla/ScopeExit.h" 14 #include "mozilla/dom/BrowserBridgeChild.h" 15 #include "mozilla/dom/BrowserChild.h" 16 #include "mozilla/dom/BrowsingContext.h" 17 #include "mozilla/dom/BrowsingContextGroup.h" 18 #include "mozilla/dom/CloseWatcherManager.h" 19 #include "mozilla/dom/ContentChild.h" 20 #include "mozilla/dom/ContentParent.h" 21 #include "mozilla/dom/IdentityCredential.h" 22 #include "mozilla/dom/InProcessChild.h" 23 #include "mozilla/dom/InProcessParent.h" 24 #include "mozilla/dom/JSActorService.h" 25 #include "mozilla/dom/JSWindowActorBinding.h" 26 #include "mozilla/dom/JSWindowActorChild.h" 27 #include "mozilla/dom/MozFrameLoaderOwnerBinding.h" 28 #include "mozilla/dom/SecurityPolicyViolationEvent.h" 29 #include "mozilla/dom/SessionStoreRestoreData.h" 30 #include "mozilla/dom/WindowContext.h" 31 #include "mozilla/dom/WindowGlobalActorsBinding.h" 32 #include "mozilla/dom/WindowGlobalParent.h" 33 #include "mozilla/dom/nsMixedContentBlocker.h" 34 #include "mozilla/ipc/Endpoint.h" 35 #include "nsContentUtils.h" 36 #include "nsDocShell.h" 37 #include "nsFocusManager.h" 38 #include "nsFrameLoader.h" 39 #include "nsFrameLoaderOwner.h" 40 #include "nsGlobalWindowInner.h" 41 #include "nsIHttpChannelInternal.h" 42 #include "nsIURIMutator.h" 43 #include "nsNetUtil.h" 44 #include "nsQueryObject.h" 45 #include "nsScriptSecurityManager.h" 46 #include "nsSerializationHelper.h" 47 #include "nsURLHelper.h" 48 49 using namespace mozilla::ipc; 50 using namespace mozilla::dom::ipc; 51 52 namespace mozilla::dom { 53 54 WindowGlobalChild::WindowGlobalChild(dom::WindowContext* aWindowContext, 55 nsIPrincipal* aPrincipal, 56 nsIURI* aDocumentURI) 57 : mWindowContext(aWindowContext), 58 mDocumentPrincipal(aPrincipal), 59 mDocumentURI(aDocumentURI) { 60 MOZ_DIAGNOSTIC_ASSERT(mWindowContext); 61 MOZ_DIAGNOSTIC_ASSERT(mDocumentPrincipal); 62 MOZ_DIAGNOSTIC_ASSERT(mDocumentPrincipal->GetIsLocalIpAddress() == 63 mWindowContext->IsLocalIP()); 64 65 if (!mDocumentURI) { 66 NS_NewURI(getter_AddRefs(mDocumentURI), "about:blank"); 67 } 68 69 // Registers a DOM Window with the profiler. It re-registers the same Inner 70 // Window ID with different URIs because when a Browsing context is first 71 // loaded, the first url loaded in it will be about:blank. This call keeps the 72 // first non-about:blank registration of window and discards the previous one. 73 uint64_t embedderInnerWindowID = 0; 74 if (BrowsingContext()->GetParent()) { 75 embedderInnerWindowID = BrowsingContext()->GetEmbedderInnerWindowId(); 76 } 77 profiler_register_page( 78 BrowsingContext()->BrowserId(), InnerWindowId(), 79 nsContentUtils::TruncatedURLForDisplay(aDocumentURI, 1024), 80 embedderInnerWindowID, BrowsingContext()->UsePrivateBrowsing()); 81 } 82 83 void VerifyStoragePrincipalMatchesDocumentPrincipal(WindowGlobalInit aInit) { 84 // WindowGlobalParent::CreateDisconnected performs similar checks in 85 // SetDocumentStoragePrincipal. If they fail, the parent process crashes. 86 // Let's ensure we crash in content instead, and assert each condition 87 // separately to find out what fails. See bug 2003449. 88 nsCString noSuffix, storageNoSuffix; 89 aInit.principal()->GetOriginNoSuffix(noSuffix); 90 aInit.storagePrincipal()->GetOriginNoSuffix(storageNoSuffix); 91 MOZ_RELEASE_ASSERT(noSuffix == storageNoSuffix); 92 MOZ_RELEASE_ASSERT( 93 aInit.principal()->OriginAttributesRef().EqualsIgnoringPartitionKey( 94 aInit.storagePrincipal()->OriginAttributesRef())); 95 } 96 97 already_AddRefed<WindowGlobalChild> WindowGlobalChild::Create( 98 nsGlobalWindowInner* aWindow) { 99 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED 100 // Opener policy is set when we start to load a document. Here, we ensure we 101 // have set the correct Opener policy so that it will be available in the 102 // parent process through window global child. 103 nsCOMPtr<nsIChannel> chan = aWindow->GetDocument()->GetChannel(); 104 nsCOMPtr<nsILoadInfo> loadInfo = chan ? chan->LoadInfo() : nullptr; 105 nsCOMPtr<nsIHttpChannelInternal> httpChan = do_QueryInterface(chan); 106 nsILoadInfo::CrossOriginOpenerPolicy policy; 107 if (httpChan && 108 loadInfo->GetExternalContentPolicyType() == 109 ExtContentPolicy::TYPE_DOCUMENT && 110 NS_SUCCEEDED(httpChan->GetCrossOriginOpenerPolicy(&policy))) { 111 MOZ_DIAGNOSTIC_ASSERT(policy == 112 aWindow->GetBrowsingContext()->GetOpenerPolicy()); 113 } 114 #endif 115 116 WindowGlobalInit init = WindowGlobalActor::WindowInitializer(aWindow); 117 RefPtr<WindowGlobalChild> wgc = CreateDisconnected(init); 118 119 // Send the link constructor over PBrowser, or link over PInProcess. 120 if (XRE_IsParentProcess()) { 121 InProcessChild* ipChild = InProcessChild::Singleton(); 122 InProcessParent* ipParent = InProcessParent::Singleton(); 123 if (!ipChild || !ipParent) { 124 return nullptr; 125 } 126 127 ManagedEndpoint<PWindowGlobalParent> endpoint = 128 ipChild->OpenPWindowGlobalEndpoint(wgc); 129 ipParent->BindPWindowGlobalEndpoint(std::move(endpoint), 130 wgc->WindowContext()->Canonical()); 131 } else { 132 RefPtr<BrowserChild> browserChild = 133 BrowserChild::GetFrom(static_cast<mozIDOMWindow*>(aWindow)); 134 MOZ_ASSERT(browserChild); 135 136 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED 137 dom::BrowsingContext* bc = aWindow->GetBrowsingContext(); 138 #endif 139 140 MOZ_DIAGNOSTIC_ASSERT(bc->AncestorsAreCurrent()); 141 MOZ_DIAGNOSTIC_ASSERT(bc->IsInProcess()); 142 143 VerifyStoragePrincipalMatchesDocumentPrincipal(init); 144 145 ManagedEndpoint<PWindowGlobalParent> endpoint = 146 browserChild->OpenPWindowGlobalEndpoint(wgc); 147 browserChild->SendNewWindowGlobal(std::move(endpoint), init); 148 } 149 150 wgc->Init(); 151 wgc->InitWindowGlobal(aWindow); 152 return wgc.forget(); 153 } 154 155 already_AddRefed<WindowGlobalChild> WindowGlobalChild::CreateDisconnected( 156 const WindowGlobalInit& aInit) { 157 RefPtr<dom::BrowsingContext> browsingContext = 158 dom::BrowsingContext::Get(aInit.context().mBrowsingContextId); 159 160 RefPtr<dom::WindowContext> windowContext = 161 dom::WindowContext::GetById(aInit.context().mInnerWindowId); 162 MOZ_RELEASE_ASSERT(!windowContext, "Creating duplicate WindowContext"); 163 164 // Create our new WindowContext 165 if (XRE_IsParentProcess()) { 166 windowContext = WindowGlobalParent::CreateDisconnected(aInit); 167 } else { 168 dom::WindowContext::FieldValues fields = aInit.context().mFields; 169 windowContext = new dom::WindowContext( 170 browsingContext, aInit.context().mInnerWindowId, 171 aInit.context().mOuterWindowId, std::move(fields)); 172 } 173 174 RefPtr<WindowGlobalChild> windowChild = new WindowGlobalChild( 175 windowContext, aInit.principal(), aInit.documentURI()); 176 windowContext->mIsInProcess = true; 177 windowContext->mWindowGlobalChild = windowChild; 178 return windowChild.forget(); 179 } 180 181 void WindowGlobalChild::Init() { 182 MOZ_ASSERT(mWindowContext->mWindowGlobalChild == this); 183 mWindowContext->Init(); 184 } 185 186 void WindowGlobalChild::InitWindowGlobal(nsGlobalWindowInner* aWindow) { 187 mWindowGlobal = aWindow; 188 } 189 190 void WindowGlobalChild::OnNewDocument(Document* aDocument) { 191 MOZ_RELEASE_ASSERT(mWindowGlobal); 192 MOZ_RELEASE_ASSERT(aDocument); 193 194 // Send a series of messages to update document-specific state on 195 // WindowGlobalParent, when we change documents on an existing WindowGlobal. 196 // This data is also all sent when we construct a WindowGlobal, so anything 197 // added here should also be added to WindowGlobalActor::WindowInitializer. 198 199 // FIXME: Perhaps these should be combined into a smaller number of messages? 200 SendSetIsInitialDocument(aDocument->IsInitialDocument()); 201 SetDocumentURI(aDocument->GetDocumentURI()); 202 SetDocumentPrincipal(aDocument->NodePrincipal(), 203 aDocument->EffectiveStoragePrincipal()); 204 205 nsCOMPtr<nsITransportSecurityInfo> securityInfo; 206 if (nsCOMPtr<nsIChannel> channel = aDocument->GetChannel()) { 207 channel->GetSecurityInfo(getter_AddRefs(securityInfo)); 208 } 209 SendUpdateDocumentSecurityInfo(securityInfo); 210 211 SendUpdateDocumentCspSettings(aDocument->GetBlockAllMixedContent(false), 212 aDocument->GetUpgradeInsecureRequests(false)); 213 SendUpdateSandboxFlags(aDocument->GetSandboxFlags()); 214 215 net::CookieJarSettingsArgs csArgs; 216 net::CookieJarSettings::Cast(aDocument->CookieJarSettings()) 217 ->Serialize(csArgs); 218 if (!SendUpdateCookieJarSettings(csArgs)) { 219 NS_WARNING( 220 "Failed to update document's cookie jar settings on the " 221 "WindowGlobalParent"); 222 } 223 224 SendUpdateHttpsOnlyStatus(aDocument->HttpsOnlyStatus()); 225 226 // Update window context fields for the newly loaded Document. 227 WindowContext::Transaction txn; 228 txn.SetCookieBehavior( 229 Some(aDocument->CookieJarSettings()->GetCookieBehavior())); 230 txn.SetIsOnContentBlockingAllowList( 231 aDocument->CookieJarSettings()->GetIsOnContentBlockingAllowList()); 232 txn.SetIsThirdPartyWindow(aDocument->HasThirdPartyChannel()); 233 txn.SetIsThirdPartyTrackingResourceWindow( 234 nsContentUtils::IsThirdPartyTrackingResourceWindow(mWindowGlobal)); 235 txn.SetIsSecureContext(mWindowGlobal->IsSecureContext()); 236 if (auto policy = aDocument->GetEmbedderPolicy()) { 237 txn.SetEmbedderPolicy(*policy); 238 } 239 txn.SetShouldResistFingerprinting(aDocument->ShouldResistFingerprinting( 240 RFPTarget::IsAlwaysEnabledForPrecompute)); 241 txn.SetOverriddenFingerprintingSettings( 242 aDocument->GetOverriddenFingerprintingSettings()); 243 244 if (nsCOMPtr<nsIChannel> channel = aDocument->GetChannel()) { 245 nsCOMPtr<nsILoadInfo> loadInfo(channel->LoadInfo()); 246 txn.SetIsOriginalFrameSource(loadInfo->GetOriginalFrameSrcLoad()); 247 248 nsILoadInfo::StoragePermissionState storageAccess = 249 loadInfo->GetStoragePermission(); 250 txn.SetUsingStorageAccess( 251 storageAccess == nsILoadInfo::HasStoragePermission || 252 storageAccess == nsILoadInfo::StoragePermissionAllowListed); 253 } else { 254 txn.SetIsOriginalFrameSource(false); 255 } 256 257 // Init Mixed Content Fields 258 nsCOMPtr<nsIURI> innerDocURI = 259 NS_GetInnermostURI(aDocument->GetDocumentURI()); 260 if (innerDocURI) { 261 txn.SetIsSecure( 262 innerDocURI->SchemeIs("https") || 263 nsMixedContentBlocker::IsPotentiallyTrustworthyOnion(innerDocURI)); 264 } 265 266 MOZ_DIAGNOSTIC_ASSERT(mDocumentPrincipal->GetIsLocalIpAddress() == 267 mWindowContext->IsLocalIP()); 268 269 MOZ_ALWAYS_SUCCEEDS(txn.Commit(mWindowContext)); 270 } 271 272 /* static */ 273 already_AddRefed<WindowGlobalChild> WindowGlobalChild::GetByInnerWindowId( 274 uint64_t aInnerWindowId) { 275 if (RefPtr<dom::WindowContext> context = 276 dom::WindowContext::GetById(aInnerWindowId)) { 277 return do_AddRef(context->GetWindowGlobalChild()); 278 } 279 return nullptr; 280 } 281 282 dom::BrowsingContext* WindowGlobalChild::BrowsingContext() { 283 return mWindowContext->GetBrowsingContext(); 284 } 285 286 Nullable<WindowProxyHolder> WindowGlobalChild::GetContentWindow() { 287 if (IsCurrentGlobal()) { 288 return WindowProxyHolder(BrowsingContext()); 289 } 290 return nullptr; 291 } 292 293 uint64_t WindowGlobalChild::InnerWindowId() { 294 return mWindowContext->InnerWindowId(); 295 } 296 297 uint64_t WindowGlobalChild::OuterWindowId() { 298 return mWindowContext->OuterWindowId(); 299 } 300 301 bool WindowGlobalChild::IsCurrentGlobal() { 302 return CanSend() && mWindowGlobal->IsCurrentInnerWindow(); 303 } 304 305 already_AddRefed<WindowGlobalParent> WindowGlobalChild::GetParentActor() { 306 if (!CanSend()) { 307 return nullptr; 308 } 309 IProtocol* otherSide = InProcessChild::ParentActorFor(this); 310 return do_AddRef(static_cast<WindowGlobalParent*>(otherSide)); 311 } 312 313 already_AddRefed<BrowserChild> WindowGlobalChild::GetBrowserChild() { 314 if (IsInProcess() || !CanSend()) { 315 return nullptr; 316 } 317 return do_AddRef(static_cast<BrowserChild*>(Manager())); 318 } 319 320 uint64_t WindowGlobalChild::ContentParentId() { 321 if (XRE_IsParentProcess()) { 322 return 0; 323 } 324 return ContentChild::GetSingleton()->GetID(); 325 } 326 327 // A WindowGlobalChild is the root in its process if it has no parent, or its 328 // embedder is in a different process. 329 bool WindowGlobalChild::IsProcessRoot() { 330 if (!BrowsingContext()->GetParent()) { 331 return true; 332 } 333 334 return !BrowsingContext()->GetEmbedderElement(); 335 } 336 337 // When a "beforeunload" handler is added, it's recorded to be able to know when 338 // dispatching "beforeunload" is needed. 339 void WindowGlobalChild::BeforeUnloadAdded() { 340 // Don't bother notifying the parent if we don't have an IPC link open. 341 if (mBeforeUnloadListeners == 0 && CanSend()) { 342 (void)mWindowContext->SetNeedsBeforeUnload(true); 343 } 344 345 mBeforeUnloadListeners++; 346 MOZ_ASSERT(mBeforeUnloadListeners > 0); 347 } 348 349 // This is the inverse of `BeforeUnloadAdded`, making sure that "beforeunload" 350 // isn't dispatched if all "beforeunload" handlers have been removed. 351 void WindowGlobalChild::BeforeUnloadRemoved() { 352 mBeforeUnloadListeners--; 353 MOZ_ASSERT(mBeforeUnloadListeners >= 0); 354 355 if (mBeforeUnloadListeners == 0) { 356 (void)mWindowContext->SetNeedsBeforeUnload(false); 357 } 358 } 359 360 // This is very similar to what is done for "beforeunload" and uses the same 361 // state to keep track, but is only ever used for a top level window. It's used 362 // to be able to track when a "navigate" event needs to be dispatched to the top 363 // level window's navigation object, which needs to happen right after a 364 // "beforeunload" event for that window would be dispatched, regardless of if it 365 // is. 366 void WindowGlobalChild::NavigateAdded() { 367 if (!BrowsingContext()->IsTop()) { 368 return; 369 } 370 BeforeUnloadAdded(); 371 } 372 373 // The inverse of `NavigateAdded`, again only ever used for a top level window. 374 void WindowGlobalChild::NavigateRemoved() { 375 if (!BrowsingContext()->IsTop()) { 376 return; 377 } 378 BeforeUnloadRemoved(); 379 } 380 381 void WindowGlobalChild::Destroy() { 382 JSActorWillDestroy(); 383 384 mWindowContext->Discard(); 385 386 // Perform async IPC shutdown unless we're not in-process, and our 387 // BrowserChild is in the process of being destroyed, which will destroy 388 // us as well. 389 RefPtr<BrowserChild> browserChild = GetBrowserChild(); 390 if (!browserChild || !browserChild->IsDestroyed()) { 391 SendDestroy(); 392 } 393 } 394 395 mozilla::ipc::IPCResult WindowGlobalChild::RecvMakeFrameLocal( 396 const MaybeDiscarded<dom::BrowsingContext>& aFrameContext, 397 uint64_t aPendingSwitchId) { 398 MOZ_DIAGNOSTIC_ASSERT(XRE_IsContentProcess()); 399 400 MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug, 401 ("RecvMakeFrameLocal ID=%" PRIx64, aFrameContext.ContextId())); 402 403 if (NS_WARN_IF(aFrameContext.IsNullOrDiscarded())) { 404 return IPC_OK(); 405 } 406 dom::BrowsingContext* frameContext = aFrameContext.get(); 407 408 RefPtr<Element> embedderElt = frameContext->GetEmbedderElement(); 409 if (NS_WARN_IF(!embedderElt)) { 410 return IPC_OK(); 411 } 412 413 if (NS_WARN_IF(embedderElt->GetOwnerGlobal() != GetWindowGlobal())) { 414 return IPC_OK(); 415 } 416 417 RefPtr<nsFrameLoaderOwner> flo = do_QueryObject(embedderElt); 418 MOZ_DIAGNOSTIC_ASSERT(flo, "Embedder must be a nsFrameLoaderOwner"); 419 420 // Trigger a process switch into the current process. 421 RemotenessOptions options; 422 options.mRemoteType = NOT_REMOTE_TYPE; 423 options.mPendingSwitchID.Construct(aPendingSwitchId); 424 options.mSwitchingInProgressLoad = true; 425 flo->ChangeRemoteness(options, IgnoreErrors()); 426 return IPC_OK(); 427 } 428 429 mozilla::ipc::IPCResult WindowGlobalChild::RecvMakeFrameRemote( 430 const MaybeDiscarded<dom::BrowsingContext>& aFrameContext, 431 ManagedEndpoint<PBrowserBridgeChild>&& aEndpoint, const TabId& aTabId, 432 const LayersId& aLayersId, MakeFrameRemoteResolver&& aResolve) { 433 MOZ_DIAGNOSTIC_ASSERT(XRE_IsContentProcess()); 434 435 MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug, 436 ("RecvMakeFrameRemote ID=%" PRIx64, aFrameContext.ContextId())); 437 438 if (!aLayersId.IsValid()) { 439 return IPC_FAIL(this, "Received an invalid LayersId"); 440 } 441 442 // Resolve the promise when this function exits, as we'll have fully unloaded 443 // at that point. 444 auto scopeExit = MakeScopeExit([&] { aResolve(true); }); 445 446 // Get a BrowsingContext if we're not null or discarded. We don't want to 447 // early-return before we connect the BrowserBridgeChild, as otherwise we'll 448 // never break the channel in the parent. 449 RefPtr<dom::BrowsingContext> frameContext; 450 if (!aFrameContext.IsDiscarded()) { 451 frameContext = aFrameContext.get(); 452 } 453 454 // Immediately construct the BrowserBridgeChild so we can destroy it cleanly 455 // if the process switch fails. 456 RefPtr<BrowserBridgeChild> bridge = 457 new BrowserBridgeChild(frameContext, aTabId, aLayersId); 458 RefPtr<BrowserChild> manager = GetBrowserChild(); 459 if (NS_WARN_IF( 460 !manager->BindPBrowserBridgeEndpoint(std::move(aEndpoint), bridge))) { 461 return IPC_OK(); 462 } 463 464 // Synchronously delete de actor here rather than using SendBeginDestroy(), as 465 // we haven't initialized it yet. 466 auto deleteBridge = 467 MakeScopeExit([&] { BrowserBridgeChild::Send__delete__(bridge); }); 468 469 // Immediately tear down the actor if we don't have a valid FrameContext. 470 if (NS_WARN_IF(aFrameContext.IsNullOrDiscarded())) { 471 return IPC_OK(); 472 } 473 474 RefPtr<Element> embedderElt = frameContext->GetEmbedderElement(); 475 if (NS_WARN_IF(!embedderElt)) { 476 return IPC_OK(); 477 } 478 479 if (NS_WARN_IF(embedderElt->GetOwnerGlobal() != GetWindowGlobal())) { 480 return IPC_OK(); 481 } 482 483 RefPtr<nsFrameLoaderOwner> flo = do_QueryObject(embedderElt); 484 MOZ_DIAGNOSTIC_ASSERT(flo, "Embedder must be a nsFrameLoaderOwner"); 485 486 // Trgger a process switch into the specified process. 487 IgnoredErrorResult rv; 488 flo->ChangeRemotenessWithBridge(bridge, rv); 489 if (NS_WARN_IF(rv.Failed())) { 490 return IPC_OK(); 491 } 492 493 // Everything succeeded, so don't delete the bridge. 494 deleteBridge.release(); 495 496 return IPC_OK(); 497 } 498 499 mozilla::ipc::IPCResult WindowGlobalChild::RecvDrawSnapshot( 500 const Maybe<IntRect>& aRect, const float& aScale, 501 const nscolor& aBackgroundColor, const uint32_t& aFlags, 502 DrawSnapshotResolver&& aResolve) { 503 aResolve(gfx::PaintFragment::Record(BrowsingContext(), aRect, aScale, 504 aBackgroundColor, 505 (gfx::CrossProcessPaintFlags)aFlags)); 506 return IPC_OK(); 507 } 508 509 mozilla::ipc::IPCResult 510 WindowGlobalChild::RecvSaveStorageAccessPermissionGranted() { 511 nsCOMPtr<nsPIDOMWindowInner> inner = GetWindowGlobal(); 512 if (inner) { 513 inner->SaveStorageAccessPermissionGranted(); 514 } 515 516 return IPC_OK(); 517 } 518 519 mozilla::ipc::IPCResult WindowGlobalChild::RecvDispatchSecurityPolicyViolation( 520 const nsString& aViolationEventJSON) { 521 nsGlobalWindowInner* window = GetWindowGlobal(); 522 if (!window) { 523 return IPC_OK(); 524 } 525 526 Document* doc = window->GetDocument(); 527 if (!doc) { 528 return IPC_OK(); 529 } 530 531 SecurityPolicyViolationEventInit violationEvent; 532 if (!violationEvent.Init(aViolationEventJSON)) { 533 return IPC_OK(); 534 } 535 536 RefPtr<Event> event = SecurityPolicyViolationEvent::Constructor( 537 doc, u"securitypolicyviolation"_ns, violationEvent); 538 event->SetTrusted(true); 539 doc->DispatchEvent(*event, IgnoreErrors()); 540 return IPC_OK(); 541 } 542 543 mozilla::ipc::IPCResult WindowGlobalChild::RecvAddBlockedFrameNodeByClassifier( 544 const MaybeDiscardedBrowsingContext& aNode) { 545 if (aNode.IsNullOrDiscarded()) { 546 return IPC_OK(); 547 } 548 549 nsGlobalWindowInner* window = GetWindowGlobal(); 550 if (!window) { 551 return IPC_OK(); 552 } 553 554 Document* doc = window->GetDocument(); 555 if (!doc) { 556 return IPC_OK(); 557 } 558 559 MOZ_ASSERT(aNode.get()->GetEmbedderElement()->OwnerDoc() == doc); 560 doc->AddBlockedNodeByClassifier(aNode.get()->GetEmbedderElement()); 561 return IPC_OK(); 562 } 563 564 mozilla::ipc::IPCResult WindowGlobalChild::RecvResetScalingZoom() { 565 if (Document* doc = mWindowGlobal->GetExtantDoc()) { 566 if (PresShell* ps = doc->GetPresShell()) { 567 ps->SetResolutionAndScaleTo(1.0, 568 ResolutionChangeOrigin::MainThreadAdjustment); 569 } 570 } 571 return IPC_OK(); 572 } 573 574 mozilla::ipc::IPCResult WindowGlobalChild::RecvRestoreDocShellState( 575 const dom::sessionstore::DocShellRestoreState& aState, 576 RestoreDocShellStateResolver&& aResolve) { 577 if (mWindowGlobal) { 578 SessionStoreUtils::RestoreDocShellState(mWindowGlobal->GetDocShell(), 579 aState); 580 } 581 aResolve(true); 582 return IPC_OK(); 583 } 584 585 mozilla::ipc::IPCResult WindowGlobalChild::RecvRestoreTabContent( 586 dom::SessionStoreRestoreData* aData, RestoreTabContentResolver&& aResolve) { 587 aData->RestoreInto(BrowsingContext()); 588 aResolve(true); 589 return IPC_OK(); 590 } 591 592 IPCResult WindowGlobalChild::RecvRawMessage( 593 const JSActorMessageMeta& aMeta, JSIPCValue&& aData, 594 const UniquePtr<ClonedMessageData>& aStack) { 595 UniquePtr<StructuredCloneData> stack; 596 if (aStack) { 597 stack = MakeUnique<StructuredCloneData>(); 598 stack->BorrowFromClonedMessageData(*aStack); 599 } 600 ReceiveRawMessage(aMeta, std::move(aData), std::move(stack)); 601 return IPC_OK(); 602 } 603 604 IPCResult WindowGlobalChild::RecvNotifyPermissionChange(const nsCString& aType, 605 uint32_t aPermission) { 606 nsCOMPtr<nsIObserverService> observerService = services::GetObserverService(); 607 NS_ENSURE_TRUE(observerService, 608 IPC_FAIL(this, "Failed to get observer service")); 609 nsPIDOMWindowInner* notifyTarget = 610 static_cast<nsPIDOMWindowInner*>(this->GetWindowGlobal()); 611 observerService->NotifyObservers(notifyTarget, "perm-changed-notify-only", 612 NS_ConvertUTF8toUTF16(aType).get()); 613 // We only need to handle the revoked permission case here. The permission 614 // grant case is handled via the Storage Access API code. 615 if (this->GetWindowGlobal() && 616 this->GetWindowGlobal()->UsingStorageAccess() && 617 aPermission != nsIPermissionManager::ALLOW_ACTION) { 618 this->GetWindowGlobal()->SaveStorageAccessPermissionRevoked(); 619 } 620 return IPC_OK(); 621 } 622 623 IPCResult WindowGlobalChild::RecvProcessCloseRequest( 624 const MaybeDiscarded<dom::BrowsingContext>& aFocused) { 625 RefPtr<nsFocusManager> focusManager = nsFocusManager::GetFocusManager(); 626 RefPtr<dom::BrowsingContext> focusedContext = 627 focusManager ? focusManager->GetFocusedBrowsingContext() : nullptr; 628 MOZ_ASSERT(focusedContext, "Cannot find focused context"); 629 // Only the currently focused context's CloseWatcher should be processed. 630 if (RefPtr<Document> doc = focusedContext->GetExtantDocument()) { 631 RefPtr<nsPIDOMWindowInner> win = doc->GetInnerWindow(); 632 if (win && win->IsFullyActive()) { 633 RefPtr manager = win->EnsureCloseWatcherManager(); 634 manager->ProcessCloseRequest(); 635 } 636 } 637 return IPC_OK(); 638 } 639 640 void WindowGlobalChild::SetDocumentURI(nsIURI* aDocumentURI) { 641 // Registers a DOM Window with the profiler. It re-registers the same Inner 642 // Window ID with different URIs because when a Browsing context is first 643 // loaded, the first url loaded in it will be about:blank. This call keeps the 644 // first non-about:blank registration of window and discards the previous one. 645 uint64_t embedderInnerWindowID = 0; 646 if (BrowsingContext()->GetParent()) { 647 embedderInnerWindowID = BrowsingContext()->GetEmbedderInnerWindowId(); 648 } 649 profiler_register_page( 650 BrowsingContext()->BrowserId(), InnerWindowId(), 651 nsContentUtils::TruncatedURLForDisplay(aDocumentURI, 1024), 652 embedderInnerWindowID, BrowsingContext()->UsePrivateBrowsing()); 653 654 nsCOMPtr<nsIURI> principalURI = mDocumentPrincipal->GetURI(); 655 if (mDocumentPrincipal->GetIsNullPrincipal()) { 656 if (nsCOMPtr<nsIPrincipal> precursor = 657 mDocumentPrincipal->GetPrecursorPrincipal()) { 658 principalURI = precursor->GetURI(); 659 } 660 } 661 662 MOZ_DIAGNOSTIC_ASSERT(!nsScriptSecurityManager::IsHttpOrHttpsAndCrossOrigin( 663 principalURI, aDocumentURI), 664 "Setting DocumentURI with a different origin " 665 "than principal URI"); 666 667 mDocumentURI = aDocumentURI; 668 SendUpdateDocumentURI(WrapNotNull(aDocumentURI)); 669 } 670 671 void WindowGlobalChild::SetDocumentPrincipal( 672 nsIPrincipal* aNewDocumentPrincipal, 673 nsIPrincipal* aNewDocumentStoragePrincipal) { 674 MOZ_ASSERT(mDocumentPrincipal->Equals(aNewDocumentPrincipal)); 675 mDocumentPrincipal = aNewDocumentPrincipal; 676 SendUpdateDocumentPrincipal(aNewDocumentPrincipal, 677 aNewDocumentStoragePrincipal); 678 } 679 680 const nsACString& WindowGlobalChild::GetRemoteType() const { 681 if (XRE_IsContentProcess()) { 682 return ContentChild::GetSingleton()->GetRemoteType(); 683 } 684 685 return NOT_REMOTE_TYPE; 686 } 687 688 already_AddRefed<JSWindowActorChild> WindowGlobalChild::GetActor( 689 JSContext* aCx, const nsACString& aName, ErrorResult& aRv) { 690 return JSActorManager::GetActor(aCx, aName, aRv) 691 .downcast<JSWindowActorChild>(); 692 } 693 694 already_AddRefed<JSWindowActorChild> WindowGlobalChild::GetExistingActor( 695 const nsACString& aName) { 696 return JSActorManager::GetExistingActor(aName).downcast<JSWindowActorChild>(); 697 } 698 699 already_AddRefed<JSActor> WindowGlobalChild::InitJSActor( 700 JS::Handle<JSObject*> aMaybeActor, const nsACString& aName, 701 ErrorResult& aRv) { 702 RefPtr<JSWindowActorChild> actor; 703 if (aMaybeActor.get()) { 704 aRv = UNWRAP_OBJECT(JSWindowActorChild, aMaybeActor.get(), actor); 705 if (aRv.Failed()) { 706 return nullptr; 707 } 708 } else { 709 actor = new JSWindowActorChild(); 710 } 711 712 MOZ_RELEASE_ASSERT(!actor->GetManager(), 713 "mManager was already initialized once!"); 714 actor->Init(aName, this); 715 return actor.forget(); 716 } 717 718 void WindowGlobalChild::ActorDestroy(ActorDestroyReason aWhy) { 719 MOZ_ASSERT(nsContentUtils::IsSafeToRunScript(), 720 "Destroying WindowGlobalChild can run script"); 721 722 // If our WindowContext hasn't been marked as discarded yet, ensure it's 723 // marked as discarded at this point. 724 mWindowContext->Discard(); 725 726 profiler_unregister_page(InnerWindowId()); 727 728 // Destroy our JSActors, and reject any pending queries. 729 JSActorDidDestroy(); 730 } 731 732 bool WindowGlobalChild::IsSameOriginWith( 733 const dom::WindowContext* aOther) const { 734 if (aOther == WindowContext()) { 735 return true; 736 } 737 738 MOZ_DIAGNOSTIC_ASSERT(WindowContext()->Group() == aOther->Group()); 739 if (nsGlobalWindowInner* otherWin = aOther->GetInnerWindow()) { 740 return mDocumentPrincipal->Equals(otherWin->GetPrincipal()); 741 } 742 return false; 743 } 744 745 bool WindowGlobalChild::SameOriginWithTop() { 746 return IsSameOriginWith(WindowContext()->TopWindowContext()); 747 } 748 749 // For historical context, see: 750 // 751 // Bug 13871: Prevent frameset spoofing 752 // Bug 103638: Targets with same name in different windows open in wrong 753 // window with javascript 754 // Bug 408052: Adopt "ancestor" frame navigation policy 755 // Bug 1570207: Refactor logic to rely on BrowsingContextGroups to enforce 756 // origin attribute isolation 757 // Bug 1810619: Crash at null in nsDocShell::ValidateOrigin 758 bool WindowGlobalChild::CanNavigate(dom::BrowsingContext* aTarget, 759 bool aConsiderOpener) { 760 MOZ_DIAGNOSTIC_ASSERT(WindowContext()->Group() == aTarget->Group(), 761 "A WindowGlobalChild should never try to navigate a " 762 "BrowsingContext from another group"); 763 764 auto isFileScheme = [](nsIPrincipal* aPrincipal) -> bool { 765 // NOTE: This code previously checked for a file scheme using 766 // `nsIPrincipal::GetURI()` combined with `NS_GetInnermostURI`. We no longer 767 // use GetURI, as it has been deprecated, and it makes more sense to take 768 // advantage of the pre-computed origin, which will already use the 769 // innermost URI (bug 1810619) 770 nsAutoCString origin, scheme; 771 return NS_SUCCEEDED(aPrincipal->GetOriginNoSuffix(origin)) && 772 NS_SUCCEEDED(net_ExtractURLScheme(origin, scheme)) && 773 scheme == "file"_ns; 774 }; 775 776 // A frame can navigate itself and its own root. 777 if (aTarget == BrowsingContext() || aTarget == BrowsingContext()->Top()) { 778 return true; 779 } 780 781 // If the target frame doesn't yet have a WindowContext, start checking 782 // principals from its direct ancestor instead. It would inherit its principal 783 // from this document upon creation. 784 dom::WindowContext* initialWc = aTarget->GetCurrentWindowContext(); 785 if (!initialWc) { 786 initialWc = aTarget->GetParentWindowContext(); 787 } 788 789 // A frame can navigate any frame with a same-origin ancestor. 790 bool isFileDocument = isFileScheme(DocumentPrincipal()); 791 for (dom::WindowContext* wc = initialWc; wc; 792 wc = wc->GetParentWindowContext()) { 793 dom::WindowGlobalChild* wgc = wc->GetWindowGlobalChild(); 794 if (!wgc) { 795 continue; // out-of process, so not same-origin. 796 } 797 798 if (DocumentPrincipal()->Equals(wgc->DocumentPrincipal())) { 799 return true; 800 } 801 802 // Not strictly equal, special case if both are file: URIs. 803 // 804 // file: URIs are considered the same domain for the purpose of frame 805 // navigation, regardless of script accessibility (bug 420425). 806 if (isFileDocument && isFileScheme(wgc->DocumentPrincipal())) { 807 return true; 808 } 809 } 810 811 // If the target is a top-level document, a frame can navigate it if it can 812 // navigate its opener. 813 if (aConsiderOpener && !aTarget->GetParent()) { 814 if (RefPtr<dom::BrowsingContext> opener = aTarget->GetOpener()) { 815 return CanNavigate(opener, false); 816 } 817 } 818 819 return false; 820 } 821 822 // FindWithName follows the rules for choosing a browsing context, 823 // with the exception of sandboxing for iframes. The implementation 824 // for arbitrarily choosing between two browsing contexts with the 825 // same name is as follows: 826 // 827 // 1) The start browsing context, i.e. 'this' 828 // 2) Descendants in insertion order 829 // 3) The parent 830 // 4) Siblings and their children, both in insertion order 831 // 5) After this we iteratively follow the parent chain, repeating 3 832 // and 4 until 833 // 6) If there is no parent, consider all other top level browsing 834 // contexts and their children, both in insertion order 835 // 836 // See 837 // https://html.spec.whatwg.org/multipage/browsers.html#the-rules-for-choosing-a-browsing-context-given-a-browsing-context-name 838 dom::BrowsingContext* WindowGlobalChild::FindBrowsingContextWithName( 839 const nsAString& aName, bool aUseEntryGlobalForAccessCheck) { 840 RefPtr<WindowGlobalChild> requestingContext = this; 841 if (aUseEntryGlobalForAccessCheck) { 842 if (nsGlobalWindowInner* caller = nsContentUtils::EntryInnerWindow()) { 843 if (caller->GetBrowsingContextGroup() == WindowContext()->Group()) { 844 requestingContext = caller->GetWindowGlobalChild(); 845 } else { 846 MOZ_RELEASE_ASSERT(caller->GetPrincipal()->IsSystemPrincipal(), 847 "caller must be either same-group or system"); 848 } 849 } 850 } 851 MOZ_ASSERT(requestingContext, "must have a requestingContext"); 852 853 dom::BrowsingContext* found = nullptr; 854 if (aName.IsEmpty()) { 855 // You can't find a browsing context with an empty name. 856 found = nullptr; 857 } else if (aName.LowerCaseEqualsLiteral("_blank")) { 858 // Just return null. Caller must handle creating a new window with 859 // a blank name. 860 found = nullptr; 861 } else if (nsContentUtils::IsSpecialName(aName)) { 862 found = BrowsingContext()->FindWithSpecialName(aName, *requestingContext); 863 } else if (dom::BrowsingContext* child = 864 BrowsingContext()->FindWithNameInSubtree(aName, 865 requestingContext)) { 866 found = child; 867 } else { 868 dom::WindowContext* current = WindowContext(); 869 870 do { 871 Span<RefPtr<dom::BrowsingContext>> siblings; 872 dom::WindowContext* parent = current->GetParentWindowContext(); 873 874 if (!parent) { 875 // We've reached the root of the tree, consider browsing 876 // contexts in the same browsing context group. 877 siblings = WindowContext()->Group()->Toplevels(); 878 } else if (dom::BrowsingContext* bc = parent->GetBrowsingContext(); 879 bc && bc->NameEquals(aName) && 880 requestingContext->CanNavigate(bc) && bc->IsTargetable()) { 881 found = bc; 882 break; 883 } else { 884 siblings = parent->NonSyntheticChildren(); 885 } 886 887 for (dom::BrowsingContext* sibling : siblings) { 888 if (sibling == current->GetBrowsingContext()) { 889 continue; 890 } 891 892 if (dom::BrowsingContext* relative = 893 sibling->FindWithNameInSubtree(aName, requestingContext)) { 894 found = relative; 895 // Breaks the outer loop 896 parent = nullptr; 897 break; 898 } 899 } 900 901 current = parent; 902 } while (current); 903 } 904 905 // Helpers should perform access control checks, which means that we 906 // only need to assert that we can access found. 907 MOZ_DIAGNOSTIC_ASSERT(!found || requestingContext->CanNavigate(found)); 908 909 return found; 910 } 911 912 void WindowGlobalChild::UnblockBFCacheFor(BFCacheStatus aStatus) { 913 SendUpdateBFCacheStatus(0, aStatus); 914 } 915 916 void WindowGlobalChild::BlockBFCacheFor(BFCacheStatus aStatus) { 917 SendUpdateBFCacheStatus(aStatus, 0); 918 } 919 920 WindowGlobalChild::~WindowGlobalChild() = default; 921 922 JSObject* WindowGlobalChild::WrapObject(JSContext* aCx, 923 JS::Handle<JSObject*> aGivenProto) { 924 return WindowGlobalChild_Binding::Wrap(aCx, this, aGivenProto); 925 } 926 927 nsISupports* WindowGlobalChild::GetParentObject() { 928 return xpc::NativeGlobal(xpc::PrivilegedJunkScope()); 929 } 930 931 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(WindowGlobalChild) 932 933 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(WindowGlobalChild) 934 NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindowGlobal) 935 NS_IMPL_CYCLE_COLLECTION_UNLINK(mContainerFeaturePolicy) 936 NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindowContext) 937 tmp->UnlinkManager(); 938 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER 939 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR 940 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 941 942 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(WindowGlobalChild) 943 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindowGlobal) 944 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContainerFeaturePolicy) 945 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindowContext) 946 if (!tmp->IsInProcess()) { 947 CycleCollectionNoteChild(cb, static_cast<BrowserChild*>(tmp->Manager()), 948 "Manager()"); 949 } 950 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 951 952 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WindowGlobalChild) 953 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 954 NS_INTERFACE_MAP_ENTRY(nsISupports) 955 NS_INTERFACE_MAP_END 956 957 NS_IMPL_CYCLE_COLLECTING_ADDREF(WindowGlobalChild) 958 NS_IMPL_CYCLE_COLLECTING_RELEASE(WindowGlobalChild) 959 960 } // namespace mozilla::dom