blob: 688213976978d3be53f0588121727754d632f96b [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/threading/thread_task_runner_handle.h"
#include "content/child/child_thread_impl.h"
#include "content/child/thread_safe_sender.h"
#include "content/common/service_worker/service_worker_utils.h"
#include "content/public/common/service_names.mojom.h"
#include "content/public/common/url_loader_factory.mojom.h"
#include "content/public/renderer/child_url_loader_factory_getter.h"
#include "content/renderer/service_worker/controller_service_worker_connector.h"
#include "content/renderer/service_worker/service_worker_dispatcher.h"
#include "content/renderer/service_worker/service_worker_handle_reference.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/service_manager/public/cpp/connector.h"
#include "services/service_manager/public/cpp/interface_provider.h"
#include "third_party/WebKit/public/platform/modules/serviceworker/service_worker_object.mojom.h"
#include "third_party/WebKit/public/platform/modules/serviceworker/service_worker_registration.mojom.h"
namespace content {
// Holds state for service worker clients.
struct ServiceWorkerProviderContext::ControlleeState {
explicit ControlleeState(
scoped_refptr<ChildURLLoaderFactoryGetter> default_loader_factory_getter)
: default_loader_factory_getter(
std::move(default_loader_factory_getter)) {}
~ControlleeState() = default;
// |controller| will be set by SetController() and taken by TakeController().
std::unique_ptr<ServiceWorkerHandleReference> controller;
// Keeps version id of the current controller service worker object.
int64_t controller_version_id = blink::mojom::kInvalidServiceWorkerVersionId;
// S13nServiceWorker:
// Used to intercept requests from the controllee and dispatch them
// as events to the controller ServiceWorker. This is reset when a new
// controller is set.
mojom::URLLoaderFactoryPtr subresource_loader_factory;
// S13nServiceWorker:
// Used when we create |subresource_loader_factory|.
scoped_refptr<ChildURLLoaderFactoryGetter> default_loader_factory_getter;
// Tracks feature usage for UseCounter.
std::set<uint32_t> used_features;
// Corresponds to a ServiceWorkerContainer. We notify it when
// ServiceWorkerContainer#controller should be changed.
base::WeakPtr<WebServiceWorkerProviderImpl> web_service_worker_provider;
// Keeps ServiceWorkerWorkerClient pointers of dedicated or shared workers
// which are associated with the ServiceWorkerProviderContext.
// - If this ServiceWorkerProviderContext is for a Document, then
// |worker_clients| contains all its dedicated workers.
// - If this ServiceWorkerProviderContext is for a SharedWorker (technically
// speaking, for its shadow page), then |worker_clients| has one element:
// the shared worker.
std::vector<mojom::ServiceWorkerWorkerClientPtr> worker_clients;
// S13nServiceWorker
// Used in |subresource_loader_factory| to get the connection to the
// controller service worker. Kept here in order to call
// OnContainerHostConnectionClosed when container_host_ for the
// provider is reset.
scoped_refptr<ControllerServiceWorkerConnector> controller_connector;
// For service worker clients. Map from registration id to JavaScript
// ServiceWorkerRegistration object.
std::map<int64_t, WebServiceWorkerRegistrationImpl*> registrations_;
};
// Holds state for service worker execution contexts.
struct ServiceWorkerProviderContext::ControllerState {
ControllerState() = default;
~ControllerState() = default;
// |registration->host_ptr_info| will be taken by
// ServiceWorkerProviderContext::TakeRegistrationForServiceWorkerGlobalScope()
// means after that |registration| will be in a half-way taken state.
// TODO(leonhsl): To avoid the half-way taken state mentioned above, make
// ServiceWorkerProviderContext::TakeRegistrationForServiceWorkerGlobalScope()
// take/reset all information of |registration|, |installing|, |waiting| and
// |active| all at once.
blink::mojom::ServiceWorkerRegistrationObjectInfoPtr registration;
std::unique_ptr<ServiceWorkerHandleReference> installing;
std::unique_ptr<ServiceWorkerHandleReference> waiting;
std::unique_ptr<ServiceWorkerHandleReference> active;
};
ServiceWorkerProviderContext::ServiceWorkerProviderContext(
int provider_id,
ServiceWorkerProviderType provider_type,
mojom::ServiceWorkerContainerAssociatedRequest request,
mojom::ServiceWorkerContainerHostAssociatedPtrInfo host_ptr_info,
ServiceWorkerDispatcher* dispatcher,
scoped_refptr<ChildURLLoaderFactoryGetter> default_loader_factory_getter)
: 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));
if (provider_type == SERVICE_WORKER_PROVIDER_FOR_CONTROLLER) {
controller_state_ = std::make_unique<ControllerState>();
} else {
controllee_state_ = std::make_unique<ControlleeState>(
std::move(default_loader_factory_getter));
}
// |dispatcher| may be null in tests.
// TODO(falken): Figure out how to make a dispatcher in tests.
if (dispatcher)
dispatcher->AddProviderContext(this);
}
ServiceWorkerProviderContext::~ServiceWorkerProviderContext() {
if (ServiceWorkerDispatcher* dispatcher =
ServiceWorkerDispatcher::GetThreadSpecificInstance()) {
// Remove this context from the dispatcher living on the main thread.
dispatcher->RemoveProviderContext(this);
}
}
void ServiceWorkerProviderContext::SetRegistrationForServiceWorkerGlobalScope(
blink::mojom::ServiceWorkerRegistrationObjectInfoPtr registration,
scoped_refptr<ThreadSafeSender> sender) {
DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
ControllerState* state = controller_state_.get();
DCHECK(state);
DCHECK(!state->registration);
DCHECK(!state->installing && !state->waiting && !state->active);
state->installing = ServiceWorkerHandleReference::Adopt(
std::move(registration->installing), sender);
state->waiting = ServiceWorkerHandleReference::Adopt(
std::move(registration->waiting), sender);
state->active = ServiceWorkerHandleReference::Adopt(
std::move(registration->active), sender);
state->registration = std::move(registration);
}
scoped_refptr<WebServiceWorkerRegistrationImpl>
ServiceWorkerProviderContext::TakeRegistrationForServiceWorkerGlobalScope(
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner) {
DCHECK_EQ(SERVICE_WORKER_PROVIDER_FOR_CONTROLLER, provider_type_);
ControllerState* state = controller_state_.get();
DCHECK(state);
DCHECK(state->registration);
DCHECK(state->registration->host_ptr_info.is_valid());
DCHECK_NE(state->registration->registration_id,
blink::mojom::kInvalidServiceWorkerRegistrationId);
blink::mojom::ServiceWorkerRegistrationObjectInfoPtr info =
std::move(state->registration);
if (state->installing)
info->installing = state->installing->GetInfo();
else
info->installing = blink::mojom::ServiceWorkerObjectInfo::New();
if (state->waiting)
info->waiting = state->waiting->GetInfo();
else
info->waiting = blink::mojom::ServiceWorkerObjectInfo::New();
if (state->active)
info->active = state->active->GetInfo();
else
info->active = blink::mojom::ServiceWorkerObjectInfo::New();
ServiceWorkerDispatcher* dispatcher =
ServiceWorkerDispatcher::GetThreadSpecificInstance();
DCHECK(dispatcher);
std::unique_ptr<ServiceWorkerHandleReference> installing =
ServiceWorkerHandleReference::Create(std::move(info->installing),
dispatcher->thread_safe_sender());
std::unique_ptr<ServiceWorkerHandleReference> waiting =
ServiceWorkerHandleReference::Create(std::move(info->waiting),
dispatcher->thread_safe_sender());
std::unique_ptr<ServiceWorkerHandleReference> active =
ServiceWorkerHandleReference::Create(std::move(info->active),
dispatcher->thread_safe_sender());
DCHECK(info->request.is_pending());
scoped_refptr<WebServiceWorkerRegistrationImpl> registration =
WebServiceWorkerRegistrationImpl::CreateForServiceWorkerGlobalScope(
std::move(info), std::move(io_task_runner));
registration->SetInstalling(
dispatcher->GetOrCreateServiceWorker(std::move(installing)));
registration->SetWaiting(
dispatcher->GetOrCreateServiceWorker(std::move(waiting)));
registration->SetActive(
dispatcher->GetOrCreateServiceWorker(std::move(active)));
return registration;
}
std::unique_ptr<ServiceWorkerHandleReference>
ServiceWorkerProviderContext::TakeController() {
DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
DCHECK(controllee_state_);
return std::move(controllee_state_->controller);
}
int64_t ServiceWorkerProviderContext::GetControllerVersionId() {
DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
DCHECK(controllee_state_);
return controllee_state_->controller_version_id;
}
mojom::URLLoaderFactory*
ServiceWorkerProviderContext::subresource_loader_factory() {
DCHECK(controllee_state_);
return controllee_state_->subresource_loader_factory.get();
}
mojom::ServiceWorkerContainerHost*
ServiceWorkerProviderContext::container_host() const {
DCHECK_EQ(SERVICE_WORKER_PROVIDER_FOR_WINDOW, provider_type_);
return container_host_.get();
}
void ServiceWorkerProviderContext::CountFeature(uint32_t feature) {
// ServiceWorkerProviderContext keeps track of features in order to propagate
// it to WebServiceWorkerProviderClient, which actually records the
// UseCounter.
DCHECK(controllee_state_);
controllee_state_->used_features.insert(feature);
}
const std::set<uint32_t>& ServiceWorkerProviderContext::used_features() const {
DCHECK(controllee_state_);
return controllee_state_->used_features;
}
void ServiceWorkerProviderContext::SetWebServiceWorkerProvider(
base::WeakPtr<WebServiceWorkerProviderImpl> provider) {
DCHECK(controllee_state_);
controllee_state_->web_service_worker_provider = provider;
}
mojom::ServiceWorkerWorkerClientRequest
ServiceWorkerProviderContext::CreateWorkerClientRequest() {
DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
DCHECK(controllee_state_);
mojom::ServiceWorkerWorkerClientPtr client;
mojom::ServiceWorkerWorkerClientRequest request = mojo::MakeRequest(&client);
client.set_connection_error_handler(base::BindOnce(
&ServiceWorkerProviderContext::UnregisterWorkerFetchContext,
base::Unretained(this), client.get()));
controllee_state_->worker_clients.push_back(std::move(client));
return request;
}
mojom::ServiceWorkerContainerHostPtrInfo
ServiceWorkerProviderContext::CloneContainerHostPtrInfo() {
DCHECK(ServiceWorkerUtils::IsServicificationEnabled());
DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
DCHECK(controllee_state_);
mojom::ServiceWorkerContainerHostPtrInfo container_host_ptr_info;
container_host_->CloneForWorker(mojo::MakeRequest(&container_host_ptr_info));
return container_host_ptr_info;
}
scoped_refptr<WebServiceWorkerRegistrationImpl>
ServiceWorkerProviderContext::GetOrCreateRegistrationForServiceWorkerClient(
blink::mojom::ServiceWorkerRegistrationObjectInfoPtr info) {
DCHECK_EQ(SERVICE_WORKER_PROVIDER_FOR_WINDOW, provider_type_);
DCHECK(controllee_state_);
ServiceWorkerDispatcher* dispatcher =
ServiceWorkerDispatcher::GetThreadSpecificInstance();
DCHECK(dispatcher);
std::unique_ptr<ServiceWorkerHandleReference> installing =
ServiceWorkerHandleReference::Adopt(std::move(info->installing),
dispatcher->thread_safe_sender());
std::unique_ptr<ServiceWorkerHandleReference> waiting =
ServiceWorkerHandleReference::Adopt(std::move(info->waiting),
dispatcher->thread_safe_sender());
std::unique_ptr<ServiceWorkerHandleReference> active =
ServiceWorkerHandleReference::Adopt(std::move(info->active),
dispatcher->thread_safe_sender());
auto found = controllee_state_->registrations_.find(info->registration_id);
if (found != controllee_state_->registrations_.end()) {
DCHECK(!info->request.is_pending());
found->second->AttachForServiceWorkerClient(std::move(info));
return found->second;
}
DCHECK(info->request.is_pending());
// WebServiceWorkerRegistrationImpl constructor calls
// AddServiceWorkerRegistration to add itself into
// |controllee_state_->registrations_|.
scoped_refptr<WebServiceWorkerRegistrationImpl> registration =
WebServiceWorkerRegistrationImpl::CreateForServiceWorkerClient(
std::move(info), weak_factory_.GetWeakPtr());
registration->SetInstalling(
dispatcher->GetOrCreateServiceWorker(std::move(installing)));
registration->SetWaiting(
dispatcher->GetOrCreateServiceWorker(std::move(waiting)));
registration->SetActive(
dispatcher->GetOrCreateServiceWorker(std::move(active)));
return registration;
}
void ServiceWorkerProviderContext::OnNetworkProviderDestroyed() {
container_host_.reset();
if (controllee_state_ && controllee_state_->controller_connector)
controllee_state_->controller_connector->OnContainerHostConnectionClosed();
}
void ServiceWorkerProviderContext::UnregisterWorkerFetchContext(
mojom::ServiceWorkerWorkerClient* client) {
DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
DCHECK(controllee_state_);
base::EraseIf(
controllee_state_->worker_clients,
[client](const mojom::ServiceWorkerWorkerClientPtr& client_ptr) {
return client_ptr.get() == client;
});
}
void ServiceWorkerProviderContext::SetController(
blink::mojom::ServiceWorkerObjectInfoPtr controller,
const std::vector<blink::mojom::WebFeature>& used_features,
bool should_notify_controllerchange) {
DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
ControlleeState* state = controllee_state_.get();
DCHECK(state);
DCHECK(!state->controller || state->controller->handle_id() !=
blink::mojom::kInvalidServiceWorkerHandleId);
ServiceWorkerDispatcher* dispatcher =
ServiceWorkerDispatcher::GetThreadSpecificInstance();
state->controller_version_id = controller->version_id;
state->controller = ServiceWorkerHandleReference::Adopt(
std::move(controller), dispatcher->thread_safe_sender());
// Propagate the controller to workers related to this provider.
if (state->controller) {
for (const auto& worker : state->worker_clients) {
// This is a Mojo interface call to the (dedicated or shared) worker
// thread.
worker->SetControllerServiceWorker(state->controller->version_id());
}
}
for (blink::mojom::WebFeature feature : used_features)
state->used_features.insert(static_cast<uint32_t>(feature));
// S13nServiceWorker
// Set up the URL loader factory for sending URL requests to the controller.
if (!ServiceWorkerUtils::IsServicificationEnabled() || !state->controller) {
state->controller_connector = nullptr;
state->subresource_loader_factory = nullptr;
} else {
blink::mojom::BlobRegistryPtr blob_registry_ptr;
ChildThreadImpl::current()->GetConnector()->BindInterface(
mojom::kBrowserServiceName, mojo::MakeRequest(&blob_registry_ptr));
auto blob_registry = base::MakeRefCounted<
base::RefCountedData<blink::mojom::BlobRegistryPtr>>();
blob_registry->data = std::move(blob_registry_ptr);
state->controller_connector =
base::MakeRefCounted<ControllerServiceWorkerConnector>(
container_host_.get());
mojo::MakeStrongBinding(
std::make_unique<ServiceWorkerSubresourceLoaderFactory>(
state->controller_connector, state->default_loader_factory_getter,
state->controller->url().GetOrigin(), std::move(blob_registry)),
mojo::MakeRequest(&state->subresource_loader_factory));
}
// 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,
const base::string16& message,
std::vector<mojo::ScopedMessagePipeHandle> message_pipes) {
DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
ControlleeState* state = controllee_state_.get();
DCHECK(state);
if (state->web_service_worker_provider) {
state->web_service_worker_provider->PostMessageToClient(
std::move(source), message, std::move(message_pipes));
}
}
void ServiceWorkerProviderContext::AddServiceWorkerRegistration(
int64_t registration_id,
WebServiceWorkerRegistrationImpl* registration) {
DCHECK(controllee_state_);
DCHECK(
!base::ContainsKey(controllee_state_->registrations_, registration_id));
controllee_state_->registrations_[registration_id] = registration;
}
void ServiceWorkerProviderContext::RemoveServiceWorkerRegistration(
int64_t registration_id) {
DCHECK(controllee_state_);
DCHECK(base::ContainsKey(controllee_state_->registrations_, registration_id));
controllee_state_->registrations_.erase(registration_id);
}
bool ServiceWorkerProviderContext::ContainsServiceWorkerRegistrationForTesting(
int64_t registration_id) {
DCHECK(controllee_state_);
return base::ContainsKey(controllee_state_->registrations_, registration_id);
}
void ServiceWorkerProviderContext::DestructOnMainThread() const {
if (!main_thread_task_runner_->RunsTasksInCurrentSequence() &&
main_thread_task_runner_->DeleteSoon(FROM_HERE, this)) {
return;
}
delete this;
}
} // namespace content