ClientSource.cpp (23170B)
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 "ClientSource.h" 8 9 #include "ClientManager.h" 10 #include "ClientManagerChild.h" 11 #include "ClientPrincipalUtils.h" 12 #include "ClientSourceChild.h" 13 #include "ClientState.h" 14 #include "ClientValidation.h" 15 #include "mozilla/SchedulerGroup.h" 16 #include "mozilla/StaticPrefs_privacy.h" 17 #include "mozilla/StorageAccess.h" 18 #include "mozilla/Try.h" 19 #include "mozilla/dom/BlobURLProtocolHandler.h" 20 #include "mozilla/dom/ClientIPCTypes.h" 21 #include "mozilla/dom/DOMMozPromiseRequestHolder.h" 22 #include "mozilla/dom/JSExecutionManager.h" 23 #include "mozilla/dom/MessageEvent.h" 24 #include "mozilla/dom/MessageEventBinding.h" 25 #include "mozilla/dom/Navigator.h" 26 #include "mozilla/dom/PolicyContainer.h" 27 #include "mozilla/dom/ServiceWorker.h" 28 #include "mozilla/dom/ServiceWorkerContainer.h" 29 #include "mozilla/dom/ServiceWorkerManager.h" 30 #include "mozilla/dom/ServiceWorkerUtils.h" 31 #include "mozilla/dom/WorkerRef.h" 32 #include "mozilla/dom/WorkerScope.h" 33 #include "mozilla/dom/ipc/StructuredCloneData.h" 34 #include "mozilla/ipc/BackgroundUtils.h" 35 #include "nsContentUtils.h" 36 #include "nsFocusManager.h" 37 #include "nsIContentSecurityPolicy.h" 38 #include "nsIDocShell.h" 39 #include "nsPIDOMWindow.h" 40 41 namespace mozilla::dom { 42 43 using mozilla::dom::ipc::StructuredCloneData; 44 using mozilla::ipc::CSPInfo; 45 using mozilla::ipc::CSPToCSPInfo; 46 using mozilla::ipc::PrincipalInfo; 47 using mozilla::ipc::PrincipalInfoToPrincipal; 48 49 void ClientSource::Shutdown() { 50 NS_ASSERT_OWNINGTHREAD(ClientSource); 51 if (IsShutdown()) { 52 return; 53 } 54 55 ShutdownThing(); 56 57 mManager = nullptr; 58 } 59 60 void ClientSource::ExecutionReady(const ClientSourceExecutionReadyArgs& aArgs) { 61 // Fast fail if we don't understand this particular principal/URL combination. 62 // This can happen since we use MozURL for validation which does not handle 63 // some of the more obscure internal principal/url combinations. Normal 64 // content pages will pass this check. 65 if (NS_WARN_IF(!ClientIsValidCreationURL(mClientInfo.PrincipalInfo(), 66 aArgs.url()))) { 67 Shutdown(); 68 return; 69 } 70 71 mClientInfo.SetURL(aArgs.url()); 72 mClientInfo.SetFrameType(aArgs.frameType()); 73 MaybeExecute([aArgs](PClientSourceChild* aActor) { 74 aActor->SendExecutionReady(aArgs); 75 }); 76 } 77 78 Result<ClientState, ErrorResult> ClientSource::SnapshotWindowState() { 79 MOZ_ASSERT(NS_IsMainThread()); 80 81 nsPIDOMWindowInner* window = GetInnerWindow(); 82 if (!window || !window->IsCurrentInnerWindow() || 83 !window->HasActiveDocument()) { 84 return ClientState(ClientWindowState(VisibilityState::Hidden, TimeStamp(), 85 StorageAccess::eDeny, false)); 86 } 87 88 Document* doc = window->GetExtantDoc(); 89 ErrorResult rv; 90 if (NS_WARN_IF(!doc)) { 91 rv.ThrowInvalidStateError("Document not active"); 92 return Err(std::move(rv)); 93 } 94 95 bool focused = doc->HasFocus(rv); 96 if (NS_WARN_IF(rv.Failed())) { 97 return Err(std::move(rv)); 98 } 99 100 StorageAccess storage = StorageAllowedForDocument(doc); 101 102 return ClientState(ClientWindowState(doc->VisibilityState(), 103 doc->LastFocusTime(), storage, focused)); 104 } 105 106 WorkerPrivate* ClientSource::GetWorkerPrivate() const { 107 NS_ASSERT_OWNINGTHREAD(ClientSource); 108 if (!mOwner.is<WorkerPrivate*>()) { 109 return nullptr; 110 } 111 return mOwner.as<WorkerPrivate*>(); 112 } 113 114 nsIDocShell* ClientSource::GetDocShell() const { 115 NS_ASSERT_OWNINGTHREAD(ClientSource); 116 if (!mOwner.is<nsCOMPtr<nsIDocShell>>()) { 117 return nullptr; 118 } 119 return mOwner.as<nsCOMPtr<nsIDocShell>>(); 120 } 121 122 nsIGlobalObject* ClientSource::GetGlobal() const { 123 NS_ASSERT_OWNINGTHREAD(ClientSource); 124 nsPIDOMWindowInner* win = GetInnerWindow(); 125 if (win) { 126 return win->AsGlobal(); 127 } 128 129 WorkerPrivate* wp = GetWorkerPrivate(); 130 if (wp) { 131 return wp->GlobalScope(); 132 } 133 134 // Note, ClientSource objects attached to docshell for conceptual 135 // initial about:blank will get nullptr here. The caller should 136 // use MaybeCreateIntitialDocument() to create the window before 137 // GetGlobal() if it wants this before. 138 139 return nullptr; 140 } 141 142 // We want to be explicit about possible invalid states and 143 // return them as errors. 144 Result<bool, ErrorResult> ClientSource::MaybeCreateInitialDocument() { 145 // If there is not even a docshell, we do not expect to have a document 146 nsIDocShell* docshell = GetDocShell(); 147 if (!docshell) { 148 return false; 149 } 150 151 // Force the creation of the initial document if it does not yet exist. 152 if (!docshell->GetDocument()) { 153 ErrorResult rv; 154 rv.ThrowInvalidStateError("No document available."); 155 return Err(std::move(rv)); 156 } 157 158 return true; 159 } 160 161 ClientSource::ClientSource(ClientManager* aManager, 162 nsISerialEventTarget* aEventTarget, 163 const ClientSourceConstructorArgs& aArgs) 164 : mManager(aManager), 165 mEventTarget(aEventTarget), 166 mOwner(AsVariant(Nothing())), 167 mClientInfo(aArgs.id(), aArgs.agentClusterId(), aArgs.type(), 168 aArgs.principalInfo(), aArgs.creationTime(), aArgs.url(), 169 aArgs.frameType()) { 170 MOZ_ASSERT(mManager); 171 MOZ_ASSERT(mEventTarget); 172 } 173 174 void ClientSource::Activate(PClientManagerChild* aActor) { 175 NS_ASSERT_OWNINGTHREAD(ClientSource); 176 MOZ_ASSERT(!GetActor()); 177 178 if (IsShutdown()) { 179 return; 180 } 181 182 // Fast fail if we don't understand this particular kind of PrincipalInfo. 183 // This can happen since we use MozURL for validation which does not handle 184 // some of the more obscure internal principal/url combinations. Normal 185 // content pages will pass this check. 186 if (NS_WARN_IF(!ClientIsValidPrincipalInfo(mClientInfo.PrincipalInfo()))) { 187 Shutdown(); 188 return; 189 } 190 191 ClientSourceConstructorArgs args( 192 mClientInfo.Id(), Nothing(), mClientInfo.Type(), 193 mClientInfo.PrincipalInfo(), mClientInfo.CreationTime(), VoidCString(), 194 FrameType::None); 195 RefPtr<ClientSourceChild> actor = new ClientSourceChild(args); 196 if (!aActor->SendPClientSourceConstructor(actor, args)) { 197 Shutdown(); 198 return; 199 } 200 201 ActivateThing(actor); 202 } 203 204 ClientSource::~ClientSource() { Shutdown(); } 205 206 nsPIDOMWindowInner* ClientSource::GetInnerWindow() const { 207 NS_ASSERT_OWNINGTHREAD(ClientSource); 208 if (!mOwner.is<RefPtr<nsPIDOMWindowInner>>()) { 209 return nullptr; 210 } 211 return mOwner.as<RefPtr<nsPIDOMWindowInner>>(); 212 } 213 214 void ClientSource::WorkerExecutionReady(WorkerPrivate* aWorkerPrivate) { 215 MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate); 216 aWorkerPrivate->AssertIsOnWorkerThread(); 217 218 if (IsShutdown()) { 219 return; 220 } 221 222 // Its safe to store the WorkerPrivate* here because the ClientSource 223 // is explicitly destroyed by WorkerPrivate before exiting its run loop. 224 // 225 // We need to initialize our owner before the call to 226 // ServiceWorkersStorageAllowedForGlobal below which will use SnapshotState to 227 // retrieve our StorageAccess. 228 MOZ_DIAGNOSTIC_ASSERT(mOwner.is<Nothing>()); 229 mOwner = AsVariant(aWorkerPrivate); 230 231 // A client without access to storage should never be controlled by 232 // a service worker. Check this here in case we were controlled before 233 // execution ready. We can't reliably determine what our storage policy 234 // is before execution ready, unfortunately. 235 if (mController.isSome()) { 236 MOZ_DIAGNOSTIC_ASSERT( 237 ServiceWorkersStorageAllowedForGlobal(aWorkerPrivate->GlobalScope()) || 238 StringBeginsWith(aWorkerPrivate->ScriptURL(), u"blob:"_ns)); 239 } 240 241 ClientSourceExecutionReadyArgs args(aWorkerPrivate->GetLocationInfo().mHref, 242 FrameType::None); 243 244 ExecutionReady(args); 245 } 246 247 nsresult ClientSource::WindowExecutionReady(nsPIDOMWindowInner* aInnerWindow) { 248 MOZ_ASSERT(NS_IsMainThread()); 249 MOZ_DIAGNOSTIC_ASSERT(aInnerWindow); 250 MOZ_ASSERT(aInnerWindow->IsCurrentInnerWindow()); 251 MOZ_ASSERT(aInnerWindow->HasActiveDocument()); 252 253 if (IsShutdown()) { 254 return NS_OK; 255 } 256 257 Document* doc = aInnerWindow->GetExtantDoc(); 258 NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED); 259 260 nsIURI* uri = doc->GetOriginalURI(); 261 NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED); 262 263 // Don't use nsAutoCString here since IPC requires a full nsCString anyway. 264 nsCString spec; 265 nsresult rv = uri->GetSpec(spec); 266 NS_ENSURE_SUCCESS(rv, rv); 267 268 // A client without access to storage should never be controlled by 269 // a service worker. Check this here in case we were controlled before 270 // execution ready. We can't reliably determine what our storage policy 271 // is before execution ready, unfortunately. 272 // 273 // Note, explicitly avoid checking storage policy for windows that inherit 274 // service workers from their parent. If a user opens a controlled window 275 // and then blocks storage, that window will continue to be controlled by 276 // the SW until the window is closed. Any about:blank or blob URL should 277 // continue to inherit the SW as well. We need to avoid triggering the 278 // assertion in this corner case. 279 #ifdef DEBUG 280 if (mController.isSome()) { 281 bool isAboutBlankURL = spec.LowerCaseEqualsLiteral("about:blank"); 282 bool isBlobURL = StringBeginsWith(spec, "blob:"_ns); 283 bool isStorageAllowed = 284 ServiceWorkersStorageAllowedForGlobal(aInnerWindow->AsGlobal()); 285 MOZ_ASSERT(isAboutBlankURL || isBlobURL || isStorageAllowed); 286 } 287 #endif 288 289 nsPIDOMWindowOuter* outer = aInnerWindow->GetOuterWindow(); 290 NS_ENSURE_TRUE(outer, NS_ERROR_UNEXPECTED); 291 292 FrameType frameType = FrameType::Top_level; 293 if (!outer->GetBrowsingContext()->IsTop()) { 294 frameType = FrameType::Nested; 295 } else if (outer->GetBrowsingContext()->HadOriginalOpener()) { 296 frameType = FrameType::Auxiliary; 297 } 298 299 // We should either be setting a window execution ready for the 300 // first time or setting the same window execution ready again. 301 // The secondary calls are due to initial about:blank replacement. 302 MOZ_DIAGNOSTIC_ASSERT(mOwner.is<Nothing>() || 303 mOwner.is<nsCOMPtr<nsIDocShell>>() || 304 GetInnerWindow() == aInnerWindow); 305 306 // This creates a cycle with the window. It is broken when 307 // nsGlobalWindow::FreeInnerObjects() deletes the ClientSource. 308 mOwner = AsVariant(RefPtr<nsPIDOMWindowInner>(aInnerWindow)); 309 310 ClientSourceExecutionReadyArgs args(spec, frameType); 311 ExecutionReady(args); 312 313 return NS_OK; 314 } 315 316 nsresult ClientSource::DocShellExecutionReady(nsIDocShell* aDocShell) { 317 MOZ_ASSERT(NS_IsMainThread()); 318 MOZ_DIAGNOSTIC_ASSERT(aDocShell); 319 320 if (IsShutdown()) { 321 return NS_OK; 322 } 323 324 nsPIDOMWindowOuter* outer = aDocShell->GetWindow(); 325 if (NS_WARN_IF(!outer)) { 326 return NS_ERROR_UNEXPECTED; 327 } 328 329 // Note: We don't assert storage access for a controlled client. If 330 // the about:blank actually gets used then WindowExecutionReady() will 331 // get called which asserts storage access. 332 333 // TODO: dedupe this with WindowExecutionReady 334 FrameType frameType = FrameType::Top_level; 335 if (!outer->GetBrowsingContext()->IsTop()) { 336 frameType = FrameType::Nested; 337 } else if (outer->GetBrowsingContext()->HadOriginalOpener()) { 338 frameType = FrameType::Auxiliary; 339 } 340 341 MOZ_DIAGNOSTIC_ASSERT(mOwner.is<Nothing>()); 342 343 // This creates a cycle with the docshell. It is broken when 344 // nsDocShell::Destroy() deletes the ClientSource. 345 mOwner = AsVariant(nsCOMPtr<nsIDocShell>(aDocShell)); 346 347 ClientSourceExecutionReadyArgs args("about:blank"_ns, frameType); 348 ExecutionReady(args); 349 350 return NS_OK; 351 } 352 353 void ClientSource::Freeze() { 354 MaybeExecute([](PClientSourceChild* aActor) { aActor->SendFreeze(); }); 355 } 356 357 void ClientSource::Thaw() { 358 MaybeExecute([](PClientSourceChild* aActor) { aActor->SendThaw(); }); 359 } 360 361 void ClientSource::EvictFromBFCache() { 362 if (nsCOMPtr<nsPIDOMWindowInner> win = GetInnerWindow()) { 363 win->RemoveFromBFCacheSync(); 364 } else if (WorkerPrivate* vp = GetWorkerPrivate()) { 365 vp->EvictFromBFCache(); 366 } 367 } 368 369 RefPtr<ClientOpPromise> ClientSource::EvictFromBFCacheOp() { 370 EvictFromBFCache(); 371 return ClientOpPromise::CreateAndResolve(CopyableErrorResult(), __func__); 372 } 373 374 const ClientInfo& ClientSource::Info() const { return mClientInfo; } 375 376 void ClientSource::WorkerSyncPing(WorkerPrivate* aWorkerPrivate) { 377 NS_ASSERT_OWNINGTHREAD(ClientSource); 378 MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate); 379 380 if (IsShutdown()) { 381 return; 382 } 383 384 // We need to make sure the mainthread is unblocked. 385 AutoYieldJSThreadExecution yield; 386 387 MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate == mManager->GetWorkerPrivate()); 388 aWorkerPrivate->AssertIsOnWorkerThread(); 389 MOZ_DIAGNOSTIC_ASSERT(GetActor()); 390 391 GetActor()->SendWorkerSyncPing(); 392 } 393 394 void ClientSource::SetController( 395 const ServiceWorkerDescriptor& aServiceWorker) { 396 NS_ASSERT_OWNINGTHREAD(ClientSource); 397 398 // We should never have a cross-origin controller. Since this would be 399 // same-origin policy violation we do a full release assertion here. 400 MOZ_RELEASE_ASSERT(ClientMatchPrincipalInfo(mClientInfo.PrincipalInfo(), 401 aServiceWorker.PrincipalInfo())); 402 403 // A client without access to storage should never be controlled a 404 // a service worker. If we are already execution ready with a real 405 // window or worker, then verify assert the storage policy is correct. 406 // 407 // Note, explicitly avoid checking storage policy for clients that inherit 408 // service workers from their parent. This basically means blob: URLs 409 // and about:blank windows. 410 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED 411 if (GetInnerWindow()) { 412 bool IsAboutBlankURL = Info().URL().LowerCaseEqualsLiteral("about:blank"); 413 bool IsBlobURL = StringBeginsWith(Info().URL(), "blob:"_ns); 414 bool IsStorageAllowed = ServiceWorkersStorageAllowedForGlobal(GetGlobal()); 415 MOZ_DIAGNOSTIC_ASSERT(IsAboutBlankURL || IsBlobURL || IsStorageAllowed); 416 } else if (GetWorkerPrivate()) { 417 MOZ_DIAGNOSTIC_ASSERT( 418 ServiceWorkersStorageAllowedForGlobal(GetGlobal()) || 419 StringBeginsWith(GetWorkerPrivate()->ScriptURL(), u"blob:"_ns)); 420 } 421 #endif 422 423 if (mController.isSome() && mController.ref() == aServiceWorker) { 424 return; 425 } 426 427 mController.reset(); 428 mController.emplace(aServiceWorker); 429 430 RefPtr<ServiceWorkerContainer> swc; 431 nsPIDOMWindowInner* window = GetInnerWindow(); 432 if (window) { 433 swc = window->Navigator()->ServiceWorker(); 434 } 435 436 // TODO: Also self.navigator.serviceWorker on workers when its exposed there 437 438 if (swc && nsContentUtils::IsSafeToRunScript()) { 439 swc->ControllerChanged(IgnoreErrors()); 440 } 441 } 442 443 RefPtr<ClientOpPromise> ClientSource::Control( 444 const ClientControlledArgs& aArgs) { 445 NS_ASSERT_OWNINGTHREAD(ClientSource); 446 447 // Determine if the client is allowed to be controlled. Currently we 448 // prevent service workers from controlling clients that cannot access 449 // storage. We exempt this restriction for local URL clients, like 450 // about:blank and blob:, since access to service workers is dictated by their 451 // parent. 452 // 453 // Note, we default to allowing the client to be controlled in the case 454 // where we are not execution ready yet. This can only happen if the 455 // the non-subresource load is intercepted by a service worker. Since 456 // ServiceWorkerInterceptController() uses StorageAllowedForChannel() 457 // it should be fine to accept these control messages. 458 // 459 // Its also fine to default to allowing ClientSource attached to a docshell 460 // to be controlled. These clients represent inital about:blank windows 461 // that do not have an inner window created yet. We explicitly allow initial 462 // about:blank. 463 bool controlAllowed = true; 464 if (GetInnerWindow()) { 465 // Local URL windows and windows with access to storage can be controlled. 466 bool isAboutBlankURL = Info().URL().LowerCaseEqualsLiteral("about:blank"); 467 bool isBlobURL = StringBeginsWith(Info().URL(), "blob:"_ns); 468 bool isStorageAllowed = ServiceWorkersStorageAllowedForGlobal(GetGlobal()); 469 controlAllowed = isAboutBlankURL || isBlobURL || isStorageAllowed; 470 } else if (GetWorkerPrivate()) { 471 // Local URL workers and workers with access to storage can be controlled. 472 controlAllowed = 473 ServiceWorkersStorageAllowedForGlobal(GetGlobal()) || 474 StringBeginsWith(GetWorkerPrivate()->ScriptURL(), u"blob:"_ns); 475 } 476 477 if (NS_WARN_IF(!controlAllowed)) { 478 CopyableErrorResult rv; 479 rv.ThrowInvalidStateError("Client cannot be controlled"); 480 return ClientOpPromise::CreateAndReject(rv, __func__); 481 } 482 483 SetController(ServiceWorkerDescriptor(aArgs.serviceWorker())); 484 485 return ClientOpPromise::CreateAndResolve(CopyableErrorResult(), __func__); 486 } 487 488 void ClientSource::InheritController( 489 const ServiceWorkerDescriptor& aServiceWorker) { 490 NS_ASSERT_OWNINGTHREAD(ClientSource); 491 492 // Tell the parent-side ClientManagerService that the controller was 493 // inherited. This is necessary for clients.matchAll() to work properly. 494 // In parent-side intercept mode this will also note the inheritance in 495 // the parent-side SWM. 496 MaybeExecute([aServiceWorker](PClientSourceChild* aActor) { 497 aActor->SendInheritController(ClientControlledArgs(aServiceWorker.ToIPC())); 498 }); 499 500 // Finally, record the new controller in our local ClientSource for any 501 // immediate synchronous access. 502 SetController(aServiceWorker); 503 } 504 505 const Maybe<ServiceWorkerDescriptor>& ClientSource::GetController() const { 506 return mController; 507 } 508 509 void ClientSource::NoteDOMContentLoaded() { 510 MaybeExecute( 511 [](PClientSourceChild* aActor) { aActor->SendNoteDOMContentLoaded(); }); 512 } 513 514 RefPtr<ClientOpPromise> ClientSource::Focus(const ClientFocusArgs& aArgs) { 515 NS_ASSERT_OWNINGTHREAD(ClientSource); 516 517 if (mClientInfo.Type() != ClientType::Window) { 518 CopyableErrorResult rv; 519 rv.ThrowNotSupportedError("Not a Window client"); 520 return ClientOpPromise::CreateAndReject(rv, __func__); 521 } 522 nsCOMPtr<nsPIDOMWindowOuter> outer; 523 nsPIDOMWindowInner* inner = GetInnerWindow(); 524 if (inner) { 525 outer = inner->GetOuterWindow(); 526 } else { 527 nsIDocShell* docshell = GetDocShell(); 528 if (docshell) { 529 outer = docshell->GetWindow(); 530 } 531 } 532 533 if (!outer) { 534 CopyableErrorResult rv; 535 rv.ThrowInvalidStateError("Browsing context discarded"); 536 return ClientOpPromise::CreateAndReject(rv, __func__); 537 } 538 539 MOZ_ASSERT(NS_IsMainThread()); 540 nsFocusManager::FocusWindow(outer, aArgs.callerType()); 541 542 Result<ClientState, ErrorResult> state = SnapshotState(); 543 if (state.isErr()) { 544 return ClientOpPromise::CreateAndReject( 545 CopyableErrorResult(state.unwrapErr()), __func__); 546 } 547 548 return ClientOpPromise::CreateAndResolve(state.inspect().ToIPC(), __func__); 549 } 550 551 RefPtr<ClientOpPromise> ClientSource::PostMessage( 552 const ClientPostMessageArgs& aArgs) { 553 NS_ASSERT_OWNINGTHREAD(ClientSource); 554 555 // (This only returns a global for windows or workers.) 556 nsIGlobalObject* global = GetGlobal(); 557 if (global) { 558 const RefPtr<ServiceWorkerContainer> container = 559 global->GetServiceWorkerContainer(); 560 MOZ_ASSERT_DEBUG_OR_FUZZING(container); 561 562 // Note, EvictFromBFCache() may delete the ClientSource object 563 // when bfcache lives in the child process. 564 EvictFromBFCache(); 565 container->ReceiveMessage(aArgs); 566 return ClientOpPromise::CreateAndResolve(CopyableErrorResult(), __func__); 567 } 568 569 CopyableErrorResult rv; 570 rv.ThrowInvalidStateError("Global discarded"); 571 return ClientOpPromise::CreateAndReject(rv, __func__); 572 } 573 574 RefPtr<ClientOpPromise> ClientSource::GetInfoAndState( 575 const ClientGetInfoAndStateArgs& aArgs) { 576 Result<ClientState, ErrorResult> state = SnapshotState(); 577 if (state.isErr()) { 578 return ClientOpPromise::CreateAndReject( 579 CopyableErrorResult(state.unwrapErr()), __func__); 580 } 581 582 return ClientOpPromise::CreateAndResolve( 583 ClientInfoAndState(mClientInfo.ToIPC(), state.inspect().ToIPC()), 584 __func__); 585 } 586 587 Result<ClientState, ErrorResult> ClientSource::SnapshotState() { 588 NS_ASSERT_OWNINGTHREAD(ClientSource); 589 590 if (mClientInfo.Type() == ClientType::Window) { 591 // If there is a docshell, try to create a document, too. 592 MOZ_TRY(MaybeCreateInitialDocument()); 593 // SnapshotWindowState can deal with a missing inner window 594 return SnapshotWindowState(); 595 } 596 597 WorkerPrivate* workerPrivate = GetWorkerPrivate(); 598 if (!workerPrivate) { 599 ErrorResult rv; 600 rv.ThrowInvalidStateError("Worker terminated"); 601 return Err(std::move(rv)); 602 } 603 604 return ClientState(ClientWorkerState(workerPrivate->StorageAccess())); 605 } 606 607 nsISerialEventTarget* ClientSource::EventTarget() const { return mEventTarget; } 608 609 void ClientSource::SetPolicyContainer(nsIPolicyContainer* aPolicyContainer) { 610 NS_ASSERT_OWNINGTHREAD(ClientSource); 611 if (!aPolicyContainer) { 612 return; 613 } 614 615 mozilla::ipc::PolicyContainerArgs policyContainerArgs; 616 PolicyContainer::ToArgs(PolicyContainer::Cast(aPolicyContainer), 617 policyContainerArgs); 618 619 SetPolicyContainerArgs(policyContainerArgs); 620 } 621 622 void ClientSource::SetPreloadCsp(nsIContentSecurityPolicy* aPreloadCsp) { 623 NS_ASSERT_OWNINGTHREAD(ClientSource); 624 if (!aPreloadCsp) { 625 return; 626 } 627 628 CSPInfo cspPreloadInfo; 629 nsresult rv = CSPToCSPInfo(aPreloadCsp, &cspPreloadInfo); 630 if (NS_WARN_IF(NS_FAILED(rv))) { 631 return; 632 } 633 mClientInfo.SetPreloadCspInfo(cspPreloadInfo); 634 } 635 636 void ClientSource::SetPolicyContainerArgs( 637 const mozilla::ipc::PolicyContainerArgs& aPolicyContainer) { 638 NS_ASSERT_OWNINGTHREAD(ClientSource); 639 mClientInfo.SetPolicyContainerArgs(aPolicyContainer); 640 } 641 642 const Maybe<mozilla::ipc::PolicyContainerArgs>& 643 ClientSource::GetPolicyContainerArgs() { 644 NS_ASSERT_OWNINGTHREAD(ClientSource); 645 return mClientInfo.GetPolicyContainerArgs(); 646 } 647 648 void ClientSource::Traverse(nsCycleCollectionTraversalCallback& aCallback, 649 const char* aName, uint32_t aFlags) { 650 if (mOwner.is<RefPtr<nsPIDOMWindowInner>>()) { 651 ImplCycleCollectionTraverse( 652 aCallback, mOwner.as<RefPtr<nsPIDOMWindowInner>>(), aName, aFlags); 653 } else if (mOwner.is<nsCOMPtr<nsIDocShell>>()) { 654 ImplCycleCollectionTraverse(aCallback, mOwner.as<nsCOMPtr<nsIDocShell>>(), 655 aName, aFlags); 656 } 657 } 658 659 void ClientSource::NoteCalledRegisterForServiceWorkerScope( 660 const nsACString& aScope) { 661 if (mRegisteringScopeList.Contains(aScope)) { 662 return; 663 } 664 mRegisteringScopeList.AppendElement(aScope); 665 } 666 667 bool ClientSource::CalledRegisterForServiceWorkerScope( 668 const nsACString& aScope) { 669 return mRegisteringScopeList.Contains(aScope); 670 } 671 672 nsIPrincipal* ClientSource::GetPrincipal() { 673 MOZ_ASSERT(NS_IsMainThread()); 674 675 // We only create the principal if necessary because creating a principal is 676 // expensive. 677 if (!mPrincipal) { 678 auto principalOrErr = Info().GetPrincipal(); 679 nsCOMPtr<nsIPrincipal> prin = 680 principalOrErr.isOk() ? principalOrErr.unwrap() : nullptr; 681 682 mPrincipal.emplace(prin); 683 } 684 685 return mPrincipal.ref(); 686 } 687 688 } // namespace mozilla::dom