ClientManagerService.cpp (24163B)
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 "ClientManagerService.h" 8 9 #include "ClientHandleParent.h" 10 #include "ClientManagerParent.h" 11 #include "ClientNavigateOpParent.h" 12 #include "ClientOpenWindowUtils.h" 13 #include "ClientPrincipalUtils.h" 14 #include "ClientSourceParent.h" 15 #include "jsfriendapi.h" 16 #include "mozilla/ClearOnShutdown.h" 17 #include "mozilla/MozPromise.h" 18 #include "mozilla/SchedulerGroup.h" 19 #include "mozilla/ScopeExit.h" 20 #include "mozilla/dom/ContentParent.h" 21 #include "mozilla/dom/ServiceWorkerManager.h" 22 #include "mozilla/dom/ServiceWorkerUtils.h" 23 #include "mozilla/ipc/BackgroundParent.h" 24 #include "mozilla/ipc/PBackgroundSharedTypes.h" 25 #include "nsIAsyncShutdown.h" 26 #include "nsIXULRuntime.h" 27 #include "nsProxyRelease.h" 28 29 namespace mozilla::dom { 30 31 using mozilla::ipc::AssertIsOnBackgroundThread; 32 using mozilla::ipc::PrincipalInfo; 33 34 namespace { 35 36 ClientManagerService* sClientManagerServiceInstance = nullptr; 37 bool sClientManagerServiceShutdownRegistered = false; 38 39 class ClientShutdownBlocker final : public nsIAsyncShutdownBlocker { 40 RefPtr<GenericPromise::Private> mPromise; 41 42 ~ClientShutdownBlocker() = default; 43 44 public: 45 explicit ClientShutdownBlocker(GenericPromise::Private* aPromise) 46 : mPromise(aPromise) { 47 MOZ_DIAGNOSTIC_ASSERT(mPromise); 48 } 49 50 NS_IMETHOD 51 GetName(nsAString& aNameOut) override { 52 aNameOut = nsLiteralString( 53 u"ClientManagerService: start destroying IPC actors early"); 54 return NS_OK; 55 } 56 57 NS_IMETHOD 58 BlockShutdown(nsIAsyncShutdownClient* aClient) override { 59 mPromise->Resolve(true, __func__); 60 aClient->RemoveBlocker(this); 61 return NS_OK; 62 } 63 64 NS_IMETHOD 65 GetState(nsIPropertyBag**) override { return NS_OK; } 66 67 NS_DECL_ISUPPORTS 68 }; 69 70 NS_IMPL_ISUPPORTS(ClientShutdownBlocker, nsIAsyncShutdownBlocker) 71 72 // Helper function the resolves a MozPromise when we detect that the browser 73 // has begun to shutdown. 74 RefPtr<GenericPromise> OnShutdown() { 75 RefPtr<GenericPromise::Private> ref = new GenericPromise::Private(__func__); 76 77 nsCOMPtr<nsIRunnable> r = 78 NS_NewRunnableFunction("ClientManagerServer::OnShutdown", [ref]() { 79 nsCOMPtr<nsIAsyncShutdownService> svc = 80 services::GetAsyncShutdownService(); 81 if (!svc) { 82 ref->Resolve(true, __func__); 83 return; 84 } 85 86 nsCOMPtr<nsIAsyncShutdownClient> phase; 87 MOZ_ALWAYS_SUCCEEDS(svc->GetXpcomWillShutdown(getter_AddRefs(phase))); 88 if (!phase) { 89 ref->Resolve(true, __func__); 90 return; 91 } 92 93 nsCOMPtr<nsIAsyncShutdownBlocker> blocker = 94 new ClientShutdownBlocker(ref); 95 nsresult rv = 96 phase->AddBlocker(blocker, NS_LITERAL_STRING_FROM_CSTRING(__FILE__), 97 __LINE__, u"ClientManagerService shutdown"_ns); 98 99 if (NS_FAILED(rv)) { 100 ref->Resolve(true, __func__); 101 return; 102 } 103 }); 104 105 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget())); 106 107 return ref; 108 } 109 110 } // anonymous namespace 111 112 ClientManagerService::FutureClientSourceParent::FutureClientSourceParent( 113 const IPCClientInfo& aClientInfo) 114 : mPrincipalInfo(aClientInfo.principalInfo()), mAssociated(false) {} 115 116 ClientManagerService::ClientManagerService() : mShutdown(false) { 117 AssertIsOnBackgroundThread(); 118 119 // Only register one shutdown handler at a time. If a previous service 120 // instance did this, but shutdown has not come, then we can avoid 121 // doing it again. 122 if (!sClientManagerServiceShutdownRegistered) { 123 sClientManagerServiceShutdownRegistered = true; 124 125 // While the ClientManagerService will be gracefully terminated as windows 126 // and workers are naturally killed, this can cause us to do extra work 127 // relatively late in the shutdown process. To avoid this we eagerly begin 128 // shutdown at the first sign it has begun. Since we handle normal shutdown 129 // gracefully we don't really need to block anything here. We just begin 130 // destroying our IPC actors immediately. 131 OnShutdown()->Then(GetCurrentSerialEventTarget(), __func__, []() { 132 // Look up the latest service instance, if it exists. This may 133 // be different from the instance that registered the shutdown 134 // handler. 135 RefPtr<ClientManagerService> svc = ClientManagerService::GetInstance(); 136 if (svc) { 137 svc->Shutdown(); 138 } 139 }); 140 } 141 } 142 143 ClientManagerService::~ClientManagerService() { 144 AssertIsOnBackgroundThread(); 145 MOZ_DIAGNOSTIC_ASSERT(mSourceTable.Count() == 0); 146 MOZ_DIAGNOSTIC_ASSERT(mManagerList.IsEmpty()); 147 148 MOZ_DIAGNOSTIC_ASSERT(sClientManagerServiceInstance == this); 149 sClientManagerServiceInstance = nullptr; 150 } 151 152 void ClientManagerService::Shutdown() { 153 AssertIsOnBackgroundThread(); 154 MOZ_DIAGNOSTIC_ASSERT(sClientManagerServiceShutdownRegistered); 155 156 // If many ClientManagerService are created and destroyed quickly we can 157 // in theory get more than one shutdown listener calling us. 158 if (mShutdown) { 159 return; 160 } 161 mShutdown = true; 162 163 // Begin destroying our various manager actors which will in turn destroy 164 // all source, handle, and operation actors. 165 for (auto actor : 166 CopyableAutoTArray<ClientManagerParent*, 16>(mManagerList)) { 167 (void)PClientManagerParent::Send__delete__(actor); 168 } 169 170 // Destroying manager actors should've also destroyed all source actors, so 171 // the only sources left will be future sources, which need to be aborted. 172 for (auto& entry : mSourceTable) { 173 MOZ_RELEASE_ASSERT(entry.GetData().is<FutureClientSourceParent>()); 174 CopyableErrorResult rv; 175 rv.ThrowInvalidStateError("Client creation aborted."); 176 entry.GetModifiableData() 177 ->as<FutureClientSourceParent>() 178 .RejectPromiseIfExists(rv); 179 } 180 mSourceTable.Clear(); 181 } 182 183 ClientSourceParent* ClientManagerService::MaybeUnwrapAsExistingSource( 184 const SourceTableEntry& aEntry) const { 185 AssertIsOnBackgroundThread(); 186 187 if (aEntry.is<FutureClientSourceParent>()) { 188 return nullptr; 189 } 190 191 return aEntry.as<ClientSourceParent*>(); 192 } 193 194 ClientSourceParent* ClientManagerService::FindExistingSource( 195 const nsID& aID, const PrincipalInfo& aPrincipalInfo) const { 196 AssertIsOnBackgroundThread(); 197 198 auto entry = mSourceTable.Lookup(aID); 199 200 if (!entry) { 201 return nullptr; 202 } 203 204 ClientSourceParent* source = MaybeUnwrapAsExistingSource(entry.Data()); 205 206 if (!source || NS_WARN_IF(!ClientMatchPrincipalInfo( 207 source->Info().PrincipalInfo(), aPrincipalInfo))) { 208 return nullptr; 209 } 210 return source; 211 } 212 213 // static 214 already_AddRefed<ClientManagerService> 215 ClientManagerService::GetOrCreateInstance() { 216 AssertIsOnBackgroundThread(); 217 218 if (!sClientManagerServiceInstance) { 219 sClientManagerServiceInstance = new ClientManagerService(); 220 } 221 222 RefPtr<ClientManagerService> ref(sClientManagerServiceInstance); 223 return ref.forget(); 224 } 225 226 // static 227 already_AddRefed<ClientManagerService> ClientManagerService::GetInstance() { 228 AssertIsOnBackgroundThread(); 229 230 if (!sClientManagerServiceInstance) { 231 return nullptr; 232 } 233 234 RefPtr<ClientManagerService> ref(sClientManagerServiceInstance); 235 return ref.forget(); 236 } 237 238 namespace { 239 240 bool IsNullPrincipalInfo(const PrincipalInfo& aPrincipalInfo) { 241 return aPrincipalInfo.type() == PrincipalInfo::TNullPrincipalInfo; 242 } 243 244 bool AreBothNullPrincipals(const PrincipalInfo& aPrincipalInfo1, 245 const PrincipalInfo& aPrincipalInfo2) { 246 return IsNullPrincipalInfo(aPrincipalInfo1) && 247 IsNullPrincipalInfo(aPrincipalInfo2); 248 } 249 250 } // anonymous namespace 251 252 bool ClientManagerService::AddSource(ClientSourceParent* aSource) { 253 AssertIsOnBackgroundThread(); 254 MOZ_ASSERT(aSource); 255 256 auto entry = mSourceTable.Lookup(aSource->Info().Id()); 257 if (entry) { 258 // Do not permit overwriting an existing ClientSource with the same 259 // UUID. This would allow a spoofed ClientParentSource actor to 260 // intercept postMessage() intended for the real actor. 261 if (entry.Data().is<ClientSourceParent*>()) { 262 return false; 263 } 264 FutureClientSourceParent& placeHolder = 265 entry.Data().as<FutureClientSourceParent>(); 266 267 const PrincipalInfo& placeHolderPrincipalInfo = placeHolder.PrincipalInfo(); 268 const PrincipalInfo& sourcePrincipalInfo = aSource->Info().PrincipalInfo(); 269 270 // The placeholder FutureClientSourceParent's PrincipalInfo must match the 271 // real ClientSourceParent's PrincipalInfo. The only exception is if both 272 // are null principals (two null principals are considered unequal). 273 if (!AreBothNullPrincipals(placeHolderPrincipalInfo, sourcePrincipalInfo) && 274 NS_WARN_IF(!ClientMatchPrincipalInfo(placeHolderPrincipalInfo, 275 sourcePrincipalInfo))) { 276 return false; 277 } 278 279 placeHolder.ResolvePromiseIfExists(); 280 *entry = AsVariant(aSource); 281 return true; 282 } 283 if (!mSourceTable.WithEntryHandle(aSource->Info().Id(), 284 [&aSource](auto&& entry) { 285 if (NS_WARN_IF(entry.HasEntry())) { 286 return false; 287 } 288 entry.Insert(AsVariant(aSource)); 289 return true; 290 })) { 291 return false; 292 } 293 return true; 294 } 295 296 bool ClientManagerService::RemoveSource(ClientSourceParent* aSource) { 297 AssertIsOnBackgroundThread(); 298 MOZ_ASSERT(aSource); 299 auto entry = mSourceTable.Lookup(aSource->Info().Id()); 300 if (NS_WARN_IF(!entry)) { 301 return false; 302 } 303 entry.Remove(); 304 return true; 305 } 306 307 bool ClientManagerService::ExpectFutureSource( 308 const IPCClientInfo& aClientInfo) { 309 AssertIsOnBackgroundThread(); 310 311 if (!mSourceTable.WithEntryHandle( 312 aClientInfo.id(), [&aClientInfo](auto&& entry) { 313 // Prevent overwrites. 314 if (entry.HasEntry()) { 315 return false; 316 } 317 entry.Insert(SourceTableEntry( 318 VariantIndex<0>(), FutureClientSourceParent(aClientInfo))); 319 return true; 320 })) { 321 return false; 322 } 323 324 return true; 325 } 326 327 void ClientManagerService::ForgetFutureSource( 328 const IPCClientInfo& aClientInfo) { 329 AssertIsOnBackgroundThread(); 330 331 auto entry = mSourceTable.Lookup(aClientInfo.id()); 332 333 if (entry) { 334 if (entry.Data().is<ClientSourceParent*>()) { 335 return; 336 } 337 338 // For non-e10s case, ClientChannelHelperParent will be freed before real 339 // ClientSourceParnet be created. In the end this methoed will be called to 340 // release the FutureClientSourceParent. That means a ClientHandle operation 341 // which waits for the FutureClientSourceParent will have no chance to 342 // connect to the ClientSourceParent. So the FutureClientSourceParent should 343 // be keep in this case. 344 // IsAssociated() makes sure there is a ClientHandle operation associated 345 // with it. 346 // More details please refer 347 // https://bugzilla.mozilla.org/show_bug.cgi?id=1730350#c2 348 if (!XRE_IsE10sParentProcess() && 349 entry.Data().as<FutureClientSourceParent>().IsAssociated()) { 350 return; 351 } 352 353 CopyableErrorResult rv; 354 rv.ThrowInvalidStateError("Client creation aborted."); 355 entry.Data().as<FutureClientSourceParent>().RejectPromiseIfExists(rv); 356 357 entry.Remove(); 358 } 359 } 360 361 RefPtr<SourcePromise> ClientManagerService::FindSource( 362 const nsID& aID, const PrincipalInfo& aPrincipalInfo) { 363 AssertIsOnBackgroundThread(); 364 365 auto entry = mSourceTable.Lookup(aID); 366 if (!entry) { 367 CopyableErrorResult rv; 368 rv.ThrowInvalidStateError("Unknown client."); 369 return SourcePromise::CreateAndReject(rv, __func__); 370 } 371 372 if (entry.Data().is<FutureClientSourceParent>()) { 373 entry.Data().as<FutureClientSourceParent>().SetAsAssociated(); 374 return entry.Data().as<FutureClientSourceParent>().Promise(); 375 } 376 377 ClientSourceParent* source = entry.Data().as<ClientSourceParent*>(); 378 if (NS_WARN_IF(!ClientMatchPrincipalInfo(source->Info().PrincipalInfo(), 379 aPrincipalInfo))) { 380 CopyableErrorResult rv; 381 rv.ThrowInvalidStateError("Unknown client."); 382 return SourcePromise::CreateAndReject(rv, __func__); 383 } 384 385 return SourcePromise::CreateAndResolve(true, __func__); 386 } 387 388 void ClientManagerService::AddManager(ClientManagerParent* aManager) { 389 AssertIsOnBackgroundThread(); 390 MOZ_DIAGNOSTIC_ASSERT(aManager); 391 MOZ_ASSERT(!mManagerList.Contains(aManager)); 392 mManagerList.AppendElement(aManager); 393 394 // If shutdown has already begun then immediately destroy the actor. 395 if (mShutdown) { 396 (void)PClientManagerParent::Send__delete__(aManager); 397 } 398 } 399 400 void ClientManagerService::RemoveManager(ClientManagerParent* aManager) { 401 AssertIsOnBackgroundThread(); 402 MOZ_DIAGNOSTIC_ASSERT(aManager); 403 DebugOnly<bool> removed = mManagerList.RemoveElement(aManager); 404 MOZ_ASSERT(removed); 405 } 406 407 RefPtr<ClientOpPromise> ClientManagerService::Navigate( 408 ThreadsafeContentParentHandle* aOriginContent, 409 const ClientNavigateArgs& aArgs) { 410 ClientSourceParent* source = 411 FindExistingSource(aArgs.target().id(), aArgs.target().principalInfo()); 412 if (!source) { 413 CopyableErrorResult rv; 414 rv.ThrowInvalidStateError("Unknown client"); 415 return ClientOpPromise::CreateAndReject(rv, __func__); 416 } 417 418 const IPCServiceWorkerDescriptor& serviceWorker = aArgs.serviceWorker(); 419 420 // Per https://w3c.github.io/ServiceWorker/#dom-windowclient-navigate step 4, 421 // if the service worker does not control the client, reject with a TypeError. 422 const Maybe<ServiceWorkerDescriptor>& controller = source->GetController(); 423 if (controller.isNothing() || 424 controller.ref().Scope() != serviceWorker.scope() || 425 controller.ref().Id() != serviceWorker.id()) { 426 CopyableErrorResult rv; 427 rv.ThrowTypeError("Client is not controlled by this Service Worker"); 428 return ClientOpPromise::CreateAndReject(rv, __func__); 429 } 430 431 PClientManagerParent* manager = source->Manager(); 432 MOZ_DIAGNOSTIC_ASSERT(manager); 433 434 // This is safe to do because the ClientSourceChild cannot directly delete 435 // itself. Instead it sends a Teardown message to the parent which then 436 // calls delete. That means we can be sure that we are not racing with 437 // source destruction here. 438 ClientNavigateOpConstructorArgs args(WrapNotNull(source), aArgs.url(), 439 aArgs.baseURL()); 440 441 RefPtr<ClientOpPromise::Private> promise = 442 new ClientOpPromise::Private(__func__); 443 444 ClientNavigateOpParent* op = new ClientNavigateOpParent(args, promise); 445 PClientNavigateOpParent* result = 446 manager->SendPClientNavigateOpConstructor(op, args); 447 if (!result) { 448 CopyableErrorResult rv; 449 rv.ThrowInvalidStateError("Client is aborted"); 450 promise->Reject(rv, __func__); 451 } 452 453 return promise; 454 } 455 456 namespace { 457 458 class PromiseListHolder final { 459 RefPtr<ClientOpPromise::Private> mResultPromise; 460 nsTArray<RefPtr<ClientOpPromise>> mPromiseList; 461 nsTArray<ClientInfoAndState> mResultList; 462 uint32_t mOutstandingPromiseCount; 463 464 void ProcessSuccess(const ClientInfoAndState& aResult) { 465 mResultList.AppendElement(aResult); 466 ProcessCompletion(); 467 } 468 469 void ProcessCompletion() { 470 MOZ_DIAGNOSTIC_ASSERT(mOutstandingPromiseCount > 0); 471 mOutstandingPromiseCount -= 1; 472 MaybeFinish(); 473 } 474 475 ~PromiseListHolder() = default; 476 477 public: 478 PromiseListHolder() 479 : mResultPromise(new ClientOpPromise::Private(__func__)), 480 mOutstandingPromiseCount(0) {} 481 482 RefPtr<ClientOpPromise> GetResultPromise() { 483 RefPtr<PromiseListHolder> kungFuDeathGrip = this; 484 return mResultPromise->Then( 485 GetCurrentSerialEventTarget(), __func__, 486 [kungFuDeathGrip](const ClientOpPromise::ResolveOrRejectValue& aValue) { 487 return ClientOpPromise::CreateAndResolveOrReject(aValue, __func__); 488 }); 489 } 490 491 void AddPromise(RefPtr<ClientOpPromise>&& aPromise) { 492 mPromiseList.AppendElement(std::move(aPromise)); 493 MOZ_DIAGNOSTIC_ASSERT(mPromiseList.LastElement()); 494 mOutstandingPromiseCount += 1; 495 496 RefPtr<PromiseListHolder> self(this); 497 mPromiseList.LastElement()->Then( 498 GetCurrentSerialEventTarget(), __func__, 499 [self](const ClientOpResult& aResult) { 500 // TODO: This is pretty clunky. Try to figure out a better 501 // wait for MatchAll() and Claim() to share this code 502 // even though they expect different return values. 503 if (aResult.type() == ClientOpResult::TClientInfoAndState) { 504 self->ProcessSuccess(aResult.get_ClientInfoAndState()); 505 } else { 506 self->ProcessCompletion(); 507 } 508 }, 509 [self](const CopyableErrorResult& aResult) { 510 self->ProcessCompletion(); 511 }); 512 } 513 514 void MaybeFinish() { 515 if (!mOutstandingPromiseCount) { 516 mResultPromise->Resolve(CopyableTArray(mResultList.Clone()), __func__); 517 } 518 } 519 520 NS_INLINE_DECL_REFCOUNTING(PromiseListHolder) 521 }; 522 523 } // anonymous namespace 524 525 RefPtr<ClientOpPromise> ClientManagerService::MatchAll( 526 ThreadsafeContentParentHandle* aOriginContent, 527 const ClientMatchAllArgs& aArgs) { 528 AssertIsOnBackgroundThread(); 529 530 ServiceWorkerDescriptor swd(aArgs.serviceWorker()); 531 const PrincipalInfo& principalInfo = swd.PrincipalInfo(); 532 533 RefPtr<PromiseListHolder> promiseList = new PromiseListHolder(); 534 535 for (const auto& entry : mSourceTable) { 536 ClientSourceParent* source = MaybeUnwrapAsExistingSource(entry.GetData()); 537 538 if (!source || source->IsFrozen() || !source->ExecutionReady()) { 539 continue; 540 } 541 542 if (aArgs.type() != ClientType::All && 543 source->Info().Type() != aArgs.type()) { 544 continue; 545 } 546 547 if (!ClientMatchPrincipalInfo(source->Info().PrincipalInfo(), 548 principalInfo)) { 549 continue; 550 } 551 552 if (!aArgs.includeUncontrolled()) { 553 const Maybe<ServiceWorkerDescriptor>& controller = 554 source->GetController(); 555 if (controller.isNothing()) { 556 continue; 557 } 558 559 if (controller.ref().Id() != swd.Id() || 560 controller.ref().Scope() != swd.Scope()) { 561 continue; 562 } 563 } 564 565 promiseList->AddPromise(source->StartOp(ClientGetInfoAndStateArgs( 566 source->Info().Id(), source->Info().PrincipalInfo()))); 567 } 568 569 // Maybe finish the promise now in case we didn't find any matching clients. 570 promiseList->MaybeFinish(); 571 572 return promiseList->GetResultPromise(); 573 } 574 575 namespace { 576 577 RefPtr<ClientOpPromise> ClaimOnMainThread( 578 const ClientInfo& aClientInfo, const ServiceWorkerDescriptor& aDescriptor) { 579 RefPtr<ClientOpPromise::Private> promise = 580 new ClientOpPromise::Private(__func__); 581 582 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction( 583 __func__, [promise, clientInfo = std::move(aClientInfo), 584 desc = std::move(aDescriptor)]() { 585 auto scopeExit = MakeScopeExit([&] { 586 // This will truncate the URLs if they have embedded nulls, if that 587 // can happen, but for // our purposes here that's OK. 588 nsPrintfCString err( 589 "Service worker at <%s> can't claim Client at <%s>", 590 desc.ScriptURL().get(), clientInfo.URL().get()); 591 CopyableErrorResult rv; 592 rv.ThrowInvalidStateError(err); 593 promise->Reject(rv, __func__); 594 }); 595 596 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); 597 NS_ENSURE_TRUE_VOID(swm); 598 599 RefPtr<GenericErrorResultPromise> inner = 600 swm->MaybeClaimClient(clientInfo, desc); 601 inner->Then( 602 GetMainThreadSerialEventTarget(), __func__, 603 [promise](bool aResult) { 604 promise->Resolve(CopyableErrorResult(), __func__); 605 }, 606 [promise](const CopyableErrorResult& aRv) { 607 promise->Reject(aRv, __func__); 608 }); 609 610 scopeExit.release(); 611 }); 612 613 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget())); 614 615 return promise; 616 } 617 618 } // anonymous namespace 619 620 RefPtr<ClientOpPromise> ClientManagerService::Claim( 621 ThreadsafeContentParentHandle* aOriginContent, 622 const ClientClaimArgs& aArgs) { 623 AssertIsOnBackgroundThread(); 624 625 const IPCServiceWorkerDescriptor& serviceWorker = aArgs.serviceWorker(); 626 const PrincipalInfo& principalInfo = serviceWorker.principalInfo(); 627 628 RefPtr<PromiseListHolder> promiseList = new PromiseListHolder(); 629 630 for (const auto& entry : mSourceTable) { 631 ClientSourceParent* source = MaybeUnwrapAsExistingSource(entry.GetData()); 632 633 if (!source) { 634 continue; 635 } 636 637 if (!ClientMatchPrincipalInfo(source->Info().PrincipalInfo(), 638 principalInfo)) { 639 continue; 640 } 641 642 const Maybe<ServiceWorkerDescriptor>& controller = source->GetController(); 643 if (controller.isSome() && 644 controller.ref().Scope() == serviceWorker.scope() && 645 controller.ref().Id() == serviceWorker.id()) { 646 continue; 647 } 648 649 // TODO: This logic to determine if a service worker should control 650 // a particular client should be moved to the ServiceWorkerManager. 651 // This can't happen until the SWM is moved to the parent process, 652 // though. 653 if (!source->ExecutionReady() || 654 source->Info().Type() == ClientType::Serviceworker || 655 source->Info().URL().Find(serviceWorker.scope()) != 0) { 656 continue; 657 } 658 659 if (source->IsFrozen()) { 660 (void)source->SendEvictFromBFCache(); 661 continue; 662 } 663 664 promiseList->AddPromise(ClaimOnMainThread( 665 source->Info(), ServiceWorkerDescriptor(serviceWorker))); 666 } 667 668 // Maybe finish the promise now in case we didn't find any matching clients. 669 promiseList->MaybeFinish(); 670 671 return promiseList->GetResultPromise(); 672 } 673 674 RefPtr<ClientOpPromise> ClientManagerService::GetInfoAndState( 675 ThreadsafeContentParentHandle* aOriginContent, 676 const ClientGetInfoAndStateArgs& aArgs) { 677 ClientSourceParent* source = 678 FindExistingSource(aArgs.id(), aArgs.principalInfo()); 679 680 if (!source) { 681 CopyableErrorResult rv; 682 rv.ThrowInvalidStateError("Unknown client"); 683 return ClientOpPromise::CreateAndReject(rv, __func__); 684 } 685 686 if (!source->ExecutionReady()) { 687 RefPtr<ClientManagerService> self = this; 688 689 // rejection ultimately converted to `undefined` in Clients::Get 690 return source->ExecutionReadyPromise()->Then( 691 GetCurrentSerialEventTarget(), __func__, 692 [self = std::move(self), aArgs]() -> RefPtr<ClientOpPromise> { 693 ClientSourceParent* source = 694 self->FindExistingSource(aArgs.id(), aArgs.principalInfo()); 695 696 if (!source) { 697 CopyableErrorResult rv; 698 rv.ThrowInvalidStateError("Unknown client"); 699 return ClientOpPromise::CreateAndReject(rv, __func__); 700 } 701 702 return source->StartOp(aArgs); 703 }); 704 } 705 706 return source->StartOp(aArgs); 707 } 708 709 RefPtr<ClientOpPromise> ClientManagerService::OpenWindow( 710 ThreadsafeContentParentHandle* aOriginContent, 711 const ClientOpenWindowArgs& aArgs) { 712 return InvokeAsync(GetMainThreadSerialEventTarget(), __func__, 713 [originContent = RefPtr{aOriginContent}, aArgs]() { 714 return ClientOpenWindow(originContent, aArgs); 715 }); 716 } 717 718 bool ClientManagerService::HasWindow( 719 const Maybe<ContentParentId>& aContentParentId, 720 const PrincipalInfo& aPrincipalInfo, const nsID& aClientId) { 721 AssertIsOnBackgroundThread(); 722 723 ClientSourceParent* source = FindExistingSource(aClientId, aPrincipalInfo); 724 if (!source) { 725 return false; 726 } 727 728 if (!source->ExecutionReady()) { 729 return false; 730 } 731 732 if (source->Info().Type() != ClientType::Window) { 733 return false; 734 } 735 736 if (aContentParentId && !source->IsOwnedByProcess(aContentParentId.value())) { 737 return false; 738 } 739 740 return true; 741 } 742 743 } // namespace mozilla::dom