|  | // Copyright 2013 The Chromium Authors | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "content/browser/service_worker/service_worker_host.h" | 
|  |  | 
|  | #include <utility> | 
|  |  | 
|  | #include "base/bind.h" | 
|  | #include "base/memory/ptr_util.h" | 
|  | #include "content/browser/broadcast_channel/broadcast_channel_provider.h" | 
|  | #include "content/browser/broadcast_channel/broadcast_channel_service.h" | 
|  | #include "content/browser/buckets/bucket_manager.h" | 
|  | #include "content/browser/code_cache/generated_code_cache_context.h" | 
|  | #include "content/browser/renderer_host/code_cache_host_impl.h" | 
|  | #include "content/browser/renderer_host/render_process_host_impl.h" | 
|  | #include "content/browser/service_worker/service_worker_consts.h" | 
|  | #include "content/browser/service_worker/service_worker_context_core.h" | 
|  | #include "content/browser/service_worker/service_worker_version.h" | 
|  | #include "content/browser/webtransport/web_transport_connector_impl.h" | 
|  | #include "content/public/browser/browser_context.h" | 
|  | #include "content/public/browser/browser_thread.h" | 
|  | #include "content/public/browser/permission_controller.h" | 
|  | #include "content/public/browser/render_process_host.h" | 
|  | #include "content/public/browser/storage_partition.h" | 
|  | #include "content/public/common/child_process_host.h" | 
|  | #include "content/public/common/origin_util.h" | 
|  | #include "mojo/public/cpp/bindings/message.h" | 
|  | #include "third_party/blink/public/common/features.h" | 
|  | #include "third_party/blink/public/common/messaging/message_port_channel.h" | 
|  | #include "third_party/blink/public/common/storage_key/storage_key.h" | 
|  | #include "third_party/blink/public/mojom/service_worker/service_worker_client.mojom.h" | 
|  |  | 
|  | namespace content { | 
|  |  | 
|  | ServiceWorkerHost::ServiceWorkerHost( | 
|  | mojo::PendingAssociatedReceiver<blink::mojom::ServiceWorkerContainerHost> | 
|  | host_receiver, | 
|  | ServiceWorkerVersion* version, | 
|  | base::WeakPtr<ServiceWorkerContextCore> context) | 
|  | : version_(version), | 
|  | broker_(this), | 
|  | container_host_(std::make_unique<content::ServiceWorkerContainerHost>( | 
|  | std::move(context))), | 
|  | host_receiver_(container_host_.get(), std::move(host_receiver)) { | 
|  | DCHECK_CURRENTLY_ON(BrowserThread::UI); | 
|  | DCHECK(version_); | 
|  |  | 
|  | container_host_->set_service_worker_host(this); | 
|  | container_host_->UpdateUrls( | 
|  | version_->script_url(), | 
|  | url::Origin::Create(version_->key().top_level_site().GetURL()), | 
|  | version_->key()); | 
|  | } | 
|  |  | 
|  | ServiceWorkerHost::~ServiceWorkerHost() { | 
|  | DCHECK_CURRENTLY_ON(BrowserThread::UI); | 
|  |  | 
|  | // Explicitly destroy the ServiceWorkerContainerHost to release | 
|  | // ServiceWorkerObjectHosts and ServiceWorkerRegistrationObjectHosts owned by | 
|  | // that. Otherwise, this destructor can trigger their Mojo connection error | 
|  | // handlers, which would call back into halfway destroyed |this|. This is | 
|  | // because they are associated with the ServiceWorker interface, which can be | 
|  | // destroyed while in this destructor (|version_|'s |event_dispatcher_|). | 
|  | // See https://crbug.com/854993. | 
|  | container_host_.reset(); | 
|  | } | 
|  |  | 
|  | void ServiceWorkerHost::CompleteStartWorkerPreparation( | 
|  | int process_id, | 
|  | mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker> broker_receiver, | 
|  | mojo::PendingRemote<service_manager::mojom::InterfaceProvider> | 
|  | interface_provider_remote) { | 
|  | DCHECK_CURRENTLY_ON(BrowserThread::UI); | 
|  | DCHECK_EQ(ChildProcessHost::kInvalidUniqueID, worker_process_id_); | 
|  | DCHECK_NE(ChildProcessHost::kInvalidUniqueID, process_id); | 
|  | worker_process_id_ = process_id; | 
|  | broker_receiver_.Bind(std::move(broker_receiver)); | 
|  | remote_interfaces_.Bind(std::move(interface_provider_remote)); | 
|  | } | 
|  |  | 
|  | void ServiceWorkerHost::CreateWebTransportConnector( | 
|  | mojo::PendingReceiver<blink::mojom::WebTransportConnector> receiver) { | 
|  | DCHECK_CURRENTLY_ON(BrowserThread::UI); | 
|  | mojo::MakeSelfOwnedReceiver( | 
|  | std::make_unique<WebTransportConnectorImpl>( | 
|  | worker_process_id_, /*frame=*/nullptr, version_->key().origin(), | 
|  | GetNetworkIsolationKey()), | 
|  | std::move(receiver)); | 
|  | } | 
|  |  | 
|  | void ServiceWorkerHost::BindCacheStorage( | 
|  | mojo::PendingReceiver<blink::mojom::CacheStorage> receiver) { | 
|  | DCHECK_CURRENTLY_ON(BrowserThread::UI); | 
|  | version_->embedded_worker()->BindCacheStorage(std::move(receiver)); | 
|  | } | 
|  |  | 
|  | #if !BUILDFLAG(IS_ANDROID) | 
|  | void ServiceWorkerHost::BindHidService( | 
|  | mojo::PendingReceiver<blink::mojom::HidService> receiver) { | 
|  | DCHECK_CURRENTLY_ON(BrowserThread::UI); | 
|  | version_->embedded_worker()->BindHidService(version_->key().origin(), | 
|  | std::move(receiver)); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | net::NetworkIsolationKey ServiceWorkerHost::GetNetworkIsolationKey() const { | 
|  | // TODO(https://crbug.com/1147281): This is the NetworkIsolationKey of a | 
|  | // top-level browsing context, which shouldn't be use for ServiceWorkers used | 
|  | // in iframes. | 
|  | return net::NetworkIsolationKey::ToDoUseTopFrameOriginAsWell( | 
|  | version_->key().origin()); | 
|  | } | 
|  |  | 
|  | const base::UnguessableToken& ServiceWorkerHost::GetReportingSource() const { | 
|  | return version_->reporting_source(); | 
|  | } | 
|  |  | 
|  | StoragePartition* ServiceWorkerHost::GetStoragePartition() const { | 
|  | // It is possible that the RenderProcessHost is gone but we receive a request | 
|  | // before we had the opportunity to Detach because the disconnect handler | 
|  | // wasn't run yet. In such cases it is is safe to ignore these messages since | 
|  | // we are about to stop the service worker. | 
|  | auto* process = | 
|  | RenderProcessHost::FromID(version_->embedded_worker()->process_id()); | 
|  | if (process == nullptr) | 
|  | return nullptr; | 
|  |  | 
|  | return process->GetStoragePartition(); | 
|  | } | 
|  |  | 
|  | void ServiceWorkerHost::CreateCodeCacheHost( | 
|  | mojo::PendingReceiver<blink::mojom::CodeCacheHost> receiver) { | 
|  | auto embedded_worker_status = version_->embedded_worker()->status(); | 
|  | // Due to IPC races it is possible that we receive code cache host requests | 
|  | // when the worker is stopping. For ex: | 
|  | // 1) Browser starts trying to stop, sends the Stop() IPC. | 
|  | // 2) Renderer sends a CreateCodeCacheHost() IPC. | 
|  | // 3) Renderer gets the Stop() IPC and realize it should try to stop the | 
|  | // worker. | 
|  | // Given the worker is stopping it is safe to ignore these messages. | 
|  | if (embedded_worker_status == EmbeddedWorkerStatus::STOPPING) | 
|  | return; | 
|  |  | 
|  | // Create a new CodeCacheHostImpl and bind it to the given receiver. | 
|  | StoragePartition* storage_partition = GetStoragePartition(); | 
|  | if (!storage_partition) { | 
|  | return; | 
|  | } | 
|  | if (!code_cache_host_receivers_) { | 
|  | code_cache_host_receivers_ = | 
|  | std::make_unique<CodeCacheHostImpl::ReceiverSet>( | 
|  | storage_partition->GetGeneratedCodeCacheContext()); | 
|  | } | 
|  | code_cache_host_receivers_->Add(version_->embedded_worker()->process_id(), | 
|  | GetNetworkIsolationKey(), | 
|  | std::move(receiver)); | 
|  | } | 
|  |  | 
|  | void ServiceWorkerHost::CreateBroadcastChannelProvider( | 
|  | mojo::PendingReceiver<blink::mojom::BroadcastChannelProvider> receiver) { | 
|  | auto* storage_partition_impl = | 
|  | static_cast<StoragePartitionImpl*>(GetStoragePartition()); | 
|  | if (!storage_partition_impl) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | auto* broadcast_channel_service = | 
|  | storage_partition_impl->GetBroadcastChannelService(); | 
|  | broadcast_channel_service->AddReceiver( | 
|  | std::make_unique<BroadcastChannelProvider>(broadcast_channel_service, | 
|  | version()->key()), | 
|  | std::move(receiver)); | 
|  | } | 
|  |  | 
|  | void ServiceWorkerHost::CreateBucketManagerHost( | 
|  | mojo::PendingReceiver<blink::mojom::BucketManagerHost> receiver) { | 
|  | static_cast<StoragePartitionImpl*>(GetStoragePartition()) | 
|  | ->GetBucketManager() | 
|  | ->BindReceiver(GetWeakPtr(), std::move(receiver), | 
|  | mojo::GetBadMessageCallback()); | 
|  | } | 
|  |  | 
|  | base::WeakPtr<ServiceWorkerHost> ServiceWorkerHost::GetWeakPtr() { | 
|  | DCHECK_CURRENTLY_ON(BrowserThread::UI); | 
|  | return weak_factory_.GetWeakPtr(); | 
|  | } | 
|  |  | 
|  | void ServiceWorkerHost::ReportNoBinderForInterface(const std::string& error) { | 
|  | broker_receiver_.ReportBadMessage(error + " for the service worker scope"); | 
|  | } | 
|  |  | 
|  | blink::StorageKey ServiceWorkerHost::GetBucketStorageKey() { | 
|  | return version_->key(); | 
|  | } | 
|  |  | 
|  | blink::mojom::PermissionStatus ServiceWorkerHost::GetPermissionStatus( | 
|  | blink::PermissionType permission_type) { | 
|  | auto* process = | 
|  | RenderProcessHost::FromID(version_->embedded_worker()->process_id()); | 
|  | if (!process) | 
|  | return blink::mojom::PermissionStatus::DENIED; | 
|  |  | 
|  | return process->GetBrowserContext() | 
|  | ->GetPermissionController() | 
|  | ->GetPermissionStatusForWorker(permission_type, process, | 
|  | GetBucketStorageKey().origin()); | 
|  | } | 
|  |  | 
|  | void ServiceWorkerHost::BindCacheStorageForBucket( | 
|  | const storage::BucketInfo& bucket, | 
|  | mojo::PendingReceiver<blink::mojom::CacheStorage> receiver) { | 
|  | // TODO(estade): pass the bucket in order to support non-default buckets. | 
|  | version_->embedded_worker()->BindCacheStorage(std::move(receiver)); | 
|  | } | 
|  |  | 
|  | }  // namespace content |