blob: 5dbd903ae796931e94f07f23b6fe95776b4002e9 [file] [log] [blame]
// 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/functional/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/file_system_access/file_system_access_error.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_container_host.h"
#include "content/browser/service_worker/service_worker_context_core.h"
#include "content/browser/service_worker/service_worker_version.h"
#include "content/browser/websockets/websocket_connector_impl.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/child_process_host.h"
#include "content/public/browser/permission_controller.h"
#include "content/public/browser/permission_descriptor_util.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/content_client.h"
#include "content/public/common/origin_util.h"
#include "mojo/public/cpp/bindings/message.h"
#include "storage/browser/blob/blob_url_store_impl.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)
: worker_process_id_(ChildProcessHost::kInvalidUniqueID),
version_(&version),
token_(blink::ServiceWorkerToken()),
broker_(this),
container_host_(
std::make_unique<content::ServiceWorkerContainerHostForServiceWorker>(
std::move(context),
this,
version_->script_url(),
version_->key())),
host_receiver_(container_host_.get(), std::move(host_receiver)) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
}
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(),
GetNetworkAnonymizationKey()),
std::move(receiver));
}
void ServiceWorkerHost::CreateWebSocketConnector(
mojo::PendingReceiver<blink::mojom::WebSocketConnector> receiver) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
const blink::StorageKey& storage_key = version_->key();
mojo::MakeSelfOwnedReceiver(
std::make_unique<WebSocketConnectorImpl>(
worker_process_id_, IPC::mojom::kRoutingIdNone, storage_key.origin(),
storage_key.ToPartialNetIsolationInfo()),
std::move(receiver));
}
void ServiceWorkerHost::BindCacheStorage(
mojo::PendingReceiver<blink::mojom::CacheStorage> receiver) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
version_->embedded_worker()->BindCacheStorage(
std::move(receiver),
storage::BucketLocator::ForDefaultBucket(version_->key()));
}
void ServiceWorkerHost::GetSandboxedFileSystemForBucket(
const storage::BucketInfo& bucket,
const std::vector<std::string>& directory_path_components,
blink::mojom::FileSystemAccessManager::GetSandboxedFileSystemCallback
callback) {
auto* process = GetProcessHost();
if (process) {
process->GetSandboxedFileSystemForBucket(bucket.ToBucketLocator(),
directory_path_components,
std::move(callback));
} else {
std::move(callback).Run(
file_system_access_error::FromStatus(
blink::mojom::FileSystemAccessStatus::kInvalidState,
"Process gone."),
{});
}
}
#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
void ServiceWorkerHost::BindUsbService(
mojo::PendingReceiver<blink::mojom::WebUsbService> receiver) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (container_host_->top_frame_origin().opaque()) {
// Service worker should not be available to a window/worker client whose
// origin is opaque according to Service Worker specification. However, this
// can possibly be triggered by a compromised renderer, so reject it and
// report a bad mojo message.
mojo::ReportBadMessage(
"WebUSB is not allowed for the service worker scope when the top-level "
"frame has an opaque origin.");
return;
}
version_->embedded_worker()->BindUsbService(
container_host_->top_frame_origin(), std::move(receiver));
}
net::NetworkIsolationKey ServiceWorkerHost::GetNetworkIsolationKey() const {
return version_->key().ToPartialNetIsolationInfo().network_isolation_key();
}
net::NetworkAnonymizationKey ServiceWorkerHost::GetNetworkAnonymizationKey()
const {
return version_->key()
.ToPartialNetIsolationInfo()
.network_anonymization_key();
}
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 = GetProcessHost();
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 == blink::EmbeddedWorkerStatus::kStopping) {
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(),
GetBucketStorageKey(), 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::CreateBlobUrlStoreProvider(
mojo::PendingReceiver<blink::mojom::BlobURLStore> receiver) {
auto* storage_partition_impl =
static_cast<StoragePartitionImpl*>(GetStoragePartition());
if (!storage_partition_impl) {
return;
}
storage_partition_impl->GetBlobUrlRegistry()->AddReceiver(
version()->key(), version()->key().origin(),
GetProcessHost()->GetDeprecatedID(), std::move(receiver),
/*context_type_for_debugging=*/"Service Worker",
base::BindRepeating(
[](base::WeakPtr<ServiceWorkerHost> host) -> std::string {
if (!host) {
return "destroyed ServiceWorkerHost";
}
return host->version()->key().GetDebugString();
},
weak_factory_.GetWeakPtr()),
// Storage access can only be granted to dedicated workers.
base::BindRepeating([]() -> bool { return false; }),
!(GetContentClient()->browser()->IsBlobUrlPartitioningEnabled(
GetProcessHost()->GetBrowserContext())));
}
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 = GetProcessHost();
if (!process) {
return blink::mojom::PermissionStatus::DENIED;
}
return process->GetBrowserContext()
->GetPermissionController()
->GetPermissionStatusForWorker(
content::PermissionDescriptorUtil::
CreatePermissionDescriptorForPermissionType(permission_type),
process, GetBucketStorageKey().origin());
}
void ServiceWorkerHost::BindCacheStorageForBucket(
const storage::BucketInfo& bucket,
mojo::PendingReceiver<blink::mojom::CacheStorage> receiver) {
version_->embedded_worker()->BindCacheStorage(std::move(receiver),
bucket.ToBucketLocator());
}
storage::BucketClientInfo ServiceWorkerHost::GetBucketClientInfo() const {
return storage::BucketClientInfo{worker_process_id(), token()};
}
RenderProcessHost* ServiceWorkerHost::GetProcessHost() const {
return RenderProcessHost::FromID(version_->embedded_worker()->process_id());
}
void ServiceWorkerHost::BindAIManager(
mojo::PendingReceiver<blink::mojom::AIManager> receiver) {
auto* process = GetProcessHost();
if (process) {
GetContentClient()->browser()->BindAIManager(process->GetBrowserContext(),
this, /*rfh=*/nullptr,
std::move(receiver));
}
}
} // namespace content