blob: 7ee9e0f243a9f0c6bd2a4212549d0ef486813276 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/renderer/service_worker/service_worker_provider_context.h"
#include <set>
#include <utility>
#include <vector>
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/stl_util.h"
#include "base/task/post_task.h"
#include "base/threading/thread_task_runner_handle.h"
#include "content/child/child_thread_impl.h"
#include "content/child/thread_safe_sender.h"
#include "content/public/common/service_names.mojom.h"
#include "content/renderer/service_worker/controller_service_worker_connector.h"
#include "content/renderer/service_worker/service_worker_subresource_loader.h"
#include "content/renderer/service_worker/web_service_worker_impl.h"
#include "content/renderer/service_worker/web_service_worker_registration_impl.h"
#include "content/renderer/worker_thread_registry.h"
#include "mojo/public/cpp/bindings/strong_associated_binding.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h"
#include "services/service_manager/public/cpp/connector.h"
#include "services/service_manager/public/cpp/interface_provider.h"
#include "third_party/blink/public/common/service_worker/service_worker_utils.h"
#include "third_party/blink/public/mojom/service_worker/service_worker_object.mojom.h"
#include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
namespace content {
namespace {
void CreateSubresourceLoaderFactoryForProviderContext(
mojom::ServiceWorkerContainerHostPtrInfo container_host_info,
mojom::ControllerServiceWorkerPtrInfo controller_ptr_info,
const std::string& client_id,
std::unique_ptr<network::SharedURLLoaderFactoryInfo> fallback_factory_info,
mojom::ControllerServiceWorkerConnectorRequest connector_request,
network::mojom::URLLoaderFactoryRequest request,
scoped_refptr<base::SequencedTaskRunner> task_runner) {
mojom::ControllerServiceWorkerPtr controller_ptr;
controller_ptr.Bind(std::move(controller_ptr_info));
auto connector = base::MakeRefCounted<ControllerServiceWorkerConnector>(
std::move(container_host_info), std::move(controller_ptr), client_id);
connector->AddBinding(std::move(connector_request));
ServiceWorkerSubresourceLoaderFactory::Create(
std::move(connector),
network::SharedURLLoaderFactory::Create(std::move(fallback_factory_info)),
std::move(request), std::move(task_runner));
}
} // namespace
// For service worker clients.
ServiceWorkerProviderContext::ServiceWorkerProviderContext(
int provider_id,
blink::mojom::ServiceWorkerProviderType provider_type,
mojom::ServiceWorkerContainerAssociatedRequest request,
mojom::ServiceWorkerContainerHostAssociatedPtrInfo host_ptr_info,
mojom::ControllerServiceWorkerInfoPtr controller_info,
scoped_refptr<network::SharedURLLoaderFactory> fallback_loader_factory)
: provider_type_(provider_type),
provider_id_(provider_id),
main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
binding_(this, std::move(request)),
weak_factory_(this) {
container_host_.Bind(std::move(host_ptr_info));
state_for_client_ = std::make_unique<ServiceWorkerProviderStateForClient>(
std::move(fallback_loader_factory));
// Set up the URL loader factory for sending subresource requests to
// the controller.
if (controller_info) {
SetController(std::move(controller_info),
std::vector<blink::mojom::WebFeature>(),
false /* should_notify_controllerchange */);
}
}
// For service worker execution contexts.
ServiceWorkerProviderContext::ServiceWorkerProviderContext(
int provider_id,
mojom::ServiceWorkerContainerAssociatedRequest request,
mojom::ServiceWorkerContainerHostAssociatedPtrInfo host_ptr_info)
: provider_type_(
blink::mojom::ServiceWorkerProviderType::kForServiceWorker),
provider_id_(provider_id),
main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
binding_(this, std::move(request)),
weak_factory_(this) {
container_host_.Bind(std::move(host_ptr_info));
}
ServiceWorkerProviderContext::~ServiceWorkerProviderContext() = default;
blink::mojom::ServiceWorkerObjectInfoPtr
ServiceWorkerProviderContext::TakeController() {
DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
DCHECK(state_for_client_);
return std::move(state_for_client_->controller);
}
int64_t ServiceWorkerProviderContext::GetControllerVersionId() const {
DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
DCHECK(state_for_client_);
return state_for_client_->controller_version_id;
}
blink::mojom::ControllerServiceWorkerMode
ServiceWorkerProviderContext::IsControlledByServiceWorker() const {
DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
DCHECK(state_for_client_);
return state_for_client_->controller_mode;
}
network::mojom::URLLoaderFactory*
ServiceWorkerProviderContext::GetSubresourceLoaderFactory() {
if (!blink::ServiceWorkerUtils::IsServicificationEnabled())
return nullptr;
DCHECK(state_for_client_);
auto* state = state_for_client_.get();
if (!state->controller_endpoint && !state->controller_connector) {
// No controller is attached.
return nullptr;
}
if (state->controller_mode !=
blink::mojom::ControllerServiceWorkerMode::kControlled) {
// The controller does not exist or has no fetch event handler.
return nullptr;
}
if (!state->subresource_loader_factory) {
DCHECK(!state->controller_connector);
DCHECK(state->controller_endpoint);
// Create a SubresourceLoaderFactory on a background thread to avoid
// extra contention on the main thread.
auto task_runner = base::CreateSequencedTaskRunnerWithTraits(
{base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
task_runner->PostTask(
FROM_HERE,
base::BindOnce(&CreateSubresourceLoaderFactoryForProviderContext,
CloneContainerHostPtrInfo(),
std::move(state->controller_endpoint), state->client_id,
state->fallback_loader_factory->Clone(),
mojo::MakeRequest(&state->controller_connector),
mojo::MakeRequest(&state->subresource_loader_factory),
task_runner));
}
return state->subresource_loader_factory.get();
}
mojom::ServiceWorkerContainerHost*
ServiceWorkerProviderContext::container_host() const {
DCHECK_EQ(blink::mojom::ServiceWorkerProviderType::kForWindow,
provider_type_);
return container_host_.get();
}
const std::set<blink::mojom::WebFeature>&
ServiceWorkerProviderContext::used_features() const {
DCHECK(state_for_client_);
return state_for_client_->used_features;
}
const std::string& ServiceWorkerProviderContext::client_id() const {
DCHECK(state_for_client_);
return state_for_client_->client_id;
}
void ServiceWorkerProviderContext::SetWebServiceWorkerProvider(
base::WeakPtr<WebServiceWorkerProviderImpl> provider) {
DCHECK(state_for_client_);
state_for_client_->web_service_worker_provider = provider;
}
void ServiceWorkerProviderContext::RegisterWorkerClient(
mojom::ServiceWorkerWorkerClientPtr client) {
DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
DCHECK(state_for_client_);
client.set_connection_error_handler(base::BindOnce(
&ServiceWorkerProviderContext::UnregisterWorkerFetchContext,
base::Unretained(this), client.get()));
state_for_client_->worker_clients.push_back(std::move(client));
}
void ServiceWorkerProviderContext::CloneWorkerClientRegistry(
mojom::ServiceWorkerWorkerClientRegistryRequest request) {
DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
DCHECK(state_for_client_);
state_for_client_->worker_client_registry_bindings.AddBinding(
this, std::move(request));
}
mojom::ServiceWorkerContainerHostPtrInfo
ServiceWorkerProviderContext::CloneContainerHostPtrInfo() {
DCHECK(blink::ServiceWorkerUtils::IsServicificationEnabled());
DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
DCHECK(state_for_client_);
mojom::ServiceWorkerContainerHostPtrInfo container_host_ptr_info;
container_host_->CloneContainerHost(
mojo::MakeRequest(&container_host_ptr_info));
return container_host_ptr_info;
}
scoped_refptr<WebServiceWorkerRegistrationImpl>
ServiceWorkerProviderContext::GetOrCreateServiceWorkerRegistrationObject(
blink::mojom::ServiceWorkerRegistrationObjectInfoPtr info) {
DCHECK_EQ(blink::mojom::ServiceWorkerProviderType::kForWindow,
provider_type_);
DCHECK(state_for_client_);
auto found = state_for_client_->registrations_.find(info->registration_id);
if (found != state_for_client_->registrations_.end()) {
found->second->AttachForServiceWorkerClient(std::move(info));
return found->second;
}
return WebServiceWorkerRegistrationImpl::CreateForServiceWorkerClient(
std::move(info), weak_factory_.GetWeakPtr());
}
scoped_refptr<WebServiceWorkerImpl>
ServiceWorkerProviderContext::GetOrCreateServiceWorkerObject(
blink::mojom::ServiceWorkerObjectInfoPtr info) {
DCHECK_EQ(blink::mojom::ServiceWorkerProviderType::kForWindow,
provider_type_);
DCHECK(state_for_client_);
if (!info)
return nullptr;
auto found = state_for_client_->workers_.find(info->version_id);
if (found != state_for_client_->workers_.end()) {
return found->second;
}
return WebServiceWorkerImpl::CreateForServiceWorkerClient(
std::move(info), weak_factory_.GetWeakPtr());
}
void ServiceWorkerProviderContext::OnNetworkProviderDestroyed() {
container_host_.reset();
}
void ServiceWorkerProviderContext::PingContainerHost(
base::OnceClosure callback) {
DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
container_host_->Ping(std::move(callback));
}
void ServiceWorkerProviderContext::DispatchNetworkQuiet() {
DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
ServiceWorkerProviderStateForClient* state = state_for_client_.get();
DCHECK(state);
// In non-S13nSW, this hint isn't needed because the browser process
// sees all requests and schedules update at a convenient time.
if (!blink::ServiceWorkerUtils::IsServicificationEnabled())
return;
if (state->controller_mode ==
blink::mojom::ControllerServiceWorkerMode::kNoController) {
return;
}
container_host_->HintToUpdateServiceWorker();
}
void ServiceWorkerProviderContext::UnregisterWorkerFetchContext(
mojom::ServiceWorkerWorkerClient* client) {
DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
DCHECK(state_for_client_);
base::EraseIf(
state_for_client_->worker_clients,
[client](const mojom::ServiceWorkerWorkerClientPtr& client_ptr) {
return client_ptr.get() == client;
});
}
void ServiceWorkerProviderContext::SetController(
mojom::ControllerServiceWorkerInfoPtr controller_info,
const std::vector<blink::mojom::WebFeature>& used_features,
bool should_notify_controllerchange) {
DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
ServiceWorkerProviderStateForClient* state = state_for_client_.get();
DCHECK(state);
state->controller = std::move(controller_info->object_info);
state->controller_version_id =
state->controller ? state->controller->version_id
: blink::mojom::kInvalidServiceWorkerVersionId;
// The client id should never change once set.
DCHECK(state->client_id.empty() ||
state->client_id == controller_info->client_id);
state->client_id = controller_info->client_id;
DCHECK((controller_info->mode ==
blink::mojom::ControllerServiceWorkerMode::kNoController &&
!state->controller) ||
(controller_info->mode !=
blink::mojom::ControllerServiceWorkerMode::kNoController &&
state->controller));
state->controller_mode = controller_info->mode;
state->controller_endpoint = std::move(controller_info->endpoint);
// Propagate the controller to workers related to this provider.
if (state->controller) {
DCHECK_NE(blink::mojom::kInvalidServiceWorkerVersionId,
state->controller->version_id);
for (const auto& worker : state->worker_clients) {
// This is a Mojo interface call to the (dedicated or shared) worker
// thread.
worker->OnControllerChanged(state->controller_mode);
}
}
for (blink::mojom::WebFeature feature : used_features)
state->used_features.insert(feature);
// S13nServiceWorker:
// Reset connector state for subresource loader factory if necessary.
if (CanCreateSubresourceLoaderFactory()) {
DCHECK(blink::ServiceWorkerUtils::IsServicificationEnabled());
// There could be four patterns:
// (A) Had a controller, and got a new controller.
// (B) Had a controller, and lost the controller.
// (C) Didn't have a controller, and got a new controller.
// (D) Didn't have a controller, and lost the controller (nothing to do).
if (state->controller_connector) {
// Used to have a controller at least once and have created a
// subresource loader factory before (if no subresource factory was
// created before, then the right controller, if any, will be used when
// the factory is created in GetSubresourceLoaderFactory, so there's
// nothing to do here).
// Update the connector's controller so that subsequent resource requests
// will get the new controller in case (A)/(C), or fallback to the network
// in case (B). Inflight requests that are already dispatched may just use
// the existing controller or may use the new controller settings
// depending on when the request is actually passed to the factory (this
// part is inherently racy).
state->controller_connector->UpdateController(
mojom::ControllerServiceWorkerPtr(
std::move(state->controller_endpoint)));
}
}
// The WebServiceWorkerProviderImpl might not exist yet because the document
// has not yet been created (as WebServiceWorkerImpl is created for a
// ServiceWorkerContainer). In that case, once it's created it will still get
// the controller from |this| via WebServiceWorkerProviderImpl::SetClient().
if (state->web_service_worker_provider) {
state->web_service_worker_provider->SetController(
std::move(state->controller), state->used_features,
should_notify_controllerchange);
}
}
void ServiceWorkerProviderContext::PostMessageToClient(
blink::mojom::ServiceWorkerObjectInfoPtr source,
blink::TransferableMessage message) {
DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
ServiceWorkerProviderStateForClient* state = state_for_client_.get();
DCHECK(state);
if (state->web_service_worker_provider) {
state->web_service_worker_provider->PostMessageToClient(std::move(source),
std::move(message));
}
}
void ServiceWorkerProviderContext::AddServiceWorkerRegistrationObject(
int64_t registration_id,
WebServiceWorkerRegistrationImpl* registration) {
DCHECK(state_for_client_);
DCHECK(
!base::ContainsKey(state_for_client_->registrations_, registration_id));
state_for_client_->registrations_[registration_id] = registration;
}
void ServiceWorkerProviderContext::RemoveServiceWorkerRegistrationObject(
int64_t registration_id) {
DCHECK(state_for_client_);
DCHECK(base::ContainsKey(state_for_client_->registrations_, registration_id));
state_for_client_->registrations_.erase(registration_id);
}
bool ServiceWorkerProviderContext::
ContainsServiceWorkerRegistrationObjectForTesting(int64_t registration_id) {
DCHECK(state_for_client_);
return base::ContainsKey(state_for_client_->registrations_, registration_id);
}
void ServiceWorkerProviderContext::AddServiceWorkerObject(
int64_t version_id,
WebServiceWorkerImpl* worker) {
DCHECK(state_for_client_);
DCHECK(!base::ContainsKey(state_for_client_->workers_, version_id));
state_for_client_->workers_[version_id] = worker;
}
void ServiceWorkerProviderContext::RemoveServiceWorkerObject(
int64_t version_id) {
DCHECK(state_for_client_);
DCHECK(base::ContainsKey(state_for_client_->workers_, version_id));
state_for_client_->workers_.erase(version_id);
}
bool ServiceWorkerProviderContext::ContainsServiceWorkerObjectForTesting(
int64_t version_id) {
DCHECK(state_for_client_);
return base::ContainsKey(state_for_client_->workers_, version_id);
}
void ServiceWorkerProviderContext::CountFeature(
blink::mojom::WebFeature feature) {
DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
DCHECK(state_for_client_);
ServiceWorkerProviderStateForClient* state = state_for_client_.get();
// ServiceWorkerProviderContext keeps track of features in order to propagate
// it to WebServiceWorkerProviderClient, which actually records the
// UseCounter.
state->used_features.insert(feature);
if (state->web_service_worker_provider) {
state->web_service_worker_provider->CountFeature(feature);
}
}
bool ServiceWorkerProviderContext::CanCreateSubresourceLoaderFactory() const {
// Expected that it is called only for clients.
DCHECK(state_for_client_);
// |state_for_client_->fallback_loader_factory| could be null in unit tests.
return (blink::ServiceWorkerUtils::IsServicificationEnabled() &&
state_for_client_->fallback_loader_factory);
}
void ServiceWorkerProviderContext::DestructOnMainThread() const {
if (!main_thread_task_runner_->RunsTasksInCurrentSequence() &&
main_thread_task_runner_->DeleteSoon(FROM_HERE, this)) {
return;
}
delete this;
}
} // namespace content