blob: 1ccdae92a08b234444b4897602d2e434c54881be [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/browser/devtools/service_worker_devtools_agent_host.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/memory/ptr_util.h"
#include "base/strings/stringprintf.h"
#include "content/browser/devtools/devtools_renderer_channel.h"
#include "content/browser/devtools/devtools_session.h"
#include "content/browser/devtools/protocol/fetch_handler.h"
#include "content/browser/devtools/protocol/inspector_handler.h"
#include "content/browser/devtools/protocol/network_handler.h"
#include "content/browser/devtools/protocol/protocol.h"
#include "content/browser/devtools/protocol/schema_handler.h"
#include "content/browser/devtools/protocol/target_handler.h"
#include "content/browser/devtools/service_worker_devtools_manager.h"
#include "content/browser/service_worker/service_worker_context_wrapper.h"
#include "content/browser/service_worker/service_worker_version.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"
namespace content {
namespace {
void TerminateServiceWorkerOnCoreThread(
scoped_refptr<ServiceWorkerContextWrapper> context,
int64_t version_id) {
if (ServiceWorkerVersion* version = context->GetLiveVersion(version_id))
version->StopWorker(base::DoNothing());
}
void SetDevToolsAttachedOnCoreThread(
scoped_refptr<ServiceWorkerContextWrapper> context,
int64_t version_id,
bool attached) {
if (ServiceWorkerVersion* version = context->GetLiveVersion(version_id))
version->SetDevToolsAttached(attached);
}
void UpdateLoaderFactoriesOnCoreThread(
scoped_refptr<ServiceWorkerContextWrapper> context,
int64_t version_id,
std::unique_ptr<blink::PendingURLLoaderFactoryBundle> script_bundle,
std::unique_ptr<blink::PendingURLLoaderFactoryBundle> subresource_bundle) {
auto* version = context->GetLiveVersion(version_id);
if (!version)
return;
version->embedded_worker()->UpdateLoaderFactories(
std::move(script_bundle), std::move(subresource_bundle));
}
} // namespace
ServiceWorkerDevToolsAgentHost::ServiceWorkerDevToolsAgentHost(
int worker_process_id,
int worker_route_id,
scoped_refptr<ServiceWorkerContextWrapper> context_wrapper,
int64_t version_id,
const GURL& url,
const GURL& scope,
bool is_installed_version,
base::Optional<network::CrossOriginEmbedderPolicy>
cross_origin_embedder_policy,
mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
coep_reporter,
const base::UnguessableToken& devtools_worker_token)
: DevToolsAgentHostImpl(devtools_worker_token.ToString()),
state_(WORKER_NOT_READY),
devtools_worker_token_(devtools_worker_token),
worker_process_id_(worker_process_id),
worker_route_id_(worker_route_id),
context_wrapper_(context_wrapper),
version_id_(version_id),
url_(url),
scope_(scope),
version_installed_time_(is_installed_version ? base::Time::Now()
: base::Time()),
cross_origin_embedder_policy_(std::move(cross_origin_embedder_policy)),
coep_reporter_(std::move(coep_reporter)) {
NotifyCreated();
}
BrowserContext* ServiceWorkerDevToolsAgentHost::GetBrowserContext() {
return context_wrapper_->browser_context();
}
std::string ServiceWorkerDevToolsAgentHost::GetType() {
return kTypeServiceWorker;
}
std::string ServiceWorkerDevToolsAgentHost::GetTitle() {
return "Service Worker " + url_.spec();
}
GURL ServiceWorkerDevToolsAgentHost::GetURL() {
return url_;
}
bool ServiceWorkerDevToolsAgentHost::Activate() {
return false;
}
void ServiceWorkerDevToolsAgentHost::Reload() {
}
bool ServiceWorkerDevToolsAgentHost::Close() {
RunOrPostTaskOnThread(FROM_HERE, ServiceWorkerContext::GetCoreThreadId(),
base::BindOnce(&TerminateServiceWorkerOnCoreThread,
context_wrapper_, version_id_));
return true;
}
void ServiceWorkerDevToolsAgentHost::WorkerVersionInstalled() {
version_installed_time_ = base::Time::Now();
}
void ServiceWorkerDevToolsAgentHost::WorkerVersionDoomed() {
version_doomed_time_ = base::Time::Now();
}
ServiceWorkerDevToolsAgentHost::~ServiceWorkerDevToolsAgentHost() {
ServiceWorkerDevToolsManager::GetInstance()->AgentHostDestroyed(this);
}
bool ServiceWorkerDevToolsAgentHost::AttachSession(DevToolsSession* session) {
session->AddHandler(base::WrapUnique(new protocol::InspectorHandler()));
session->AddHandler(base::WrapUnique(new protocol::NetworkHandler(
GetId(), devtools_worker_token_, GetIOContext(), base::DoNothing())));
session->AddHandler(base::WrapUnique(new protocol::FetchHandler(
GetIOContext(),
base::BindRepeating(
&ServiceWorkerDevToolsAgentHost::UpdateLoaderFactories,
base::Unretained(this)))));
session->AddHandler(base::WrapUnique(new protocol::SchemaHandler()));
session->AddHandler(std::make_unique<protocol::TargetHandler>(
protocol::TargetHandler::AccessMode::kAutoAttachOnly, GetId(),
GetRendererChannel(), session->GetRootSession()));
if (state_ == WORKER_READY && sessions().empty())
UpdateIsAttached(true);
return true;
}
void ServiceWorkerDevToolsAgentHost::DetachSession(DevToolsSession* session) {
// Destroying session automatically detaches in renderer.
if (state_ == WORKER_READY && sessions().empty())
UpdateIsAttached(false);
}
void ServiceWorkerDevToolsAgentHost::WorkerReadyForInspection(
mojo::PendingRemote<blink::mojom::DevToolsAgent> agent_remote,
mojo::PendingReceiver<blink::mojom::DevToolsAgentHost> host_receiver) {
DCHECK_EQ(WORKER_NOT_READY, state_);
state_ = WORKER_READY;
GetRendererChannel()->SetRenderer(
std::move(agent_remote), std::move(host_receiver), worker_process_id_);
for (auto* inspector : protocol::InspectorHandler::ForAgentHost(this))
inspector->TargetReloadedAfterCrash();
if (!sessions().empty())
UpdateIsAttached(true);
}
void ServiceWorkerDevToolsAgentHost::UpdateCrossOriginEmbedderPolicy(
network::CrossOriginEmbedderPolicy cross_origin_embedder_policy,
mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
coep_reporter) {
cross_origin_embedder_policy_ = std::move(cross_origin_embedder_policy);
coep_reporter_.Bind(std::move(coep_reporter));
}
void ServiceWorkerDevToolsAgentHost::WorkerRestarted(int worker_process_id,
int worker_route_id) {
DCHECK_EQ(WORKER_TERMINATED, state_);
state_ = WORKER_NOT_READY;
worker_process_id_ = worker_process_id;
worker_route_id_ = worker_route_id;
}
void ServiceWorkerDevToolsAgentHost::WorkerStopped() {
DCHECK_NE(WORKER_TERMINATED, state_);
state_ = WORKER_TERMINATED;
for (auto* inspector : protocol::InspectorHandler::ForAgentHost(this))
inspector->TargetCrashed();
GetRendererChannel()->SetRenderer(mojo::NullRemote(), mojo::NullReceiver(),
ChildProcessHost::kInvalidUniqueID);
if (!sessions().empty())
UpdateIsAttached(false);
}
void ServiceWorkerDevToolsAgentHost::UpdateIsAttached(bool attached) {
RunOrPostTaskOnThread(
FROM_HERE, ServiceWorkerContext::GetCoreThreadId(),
base::BindOnce(&SetDevToolsAttachedOnCoreThread, context_wrapper_,
version_id_, attached));
}
void ServiceWorkerDevToolsAgentHost::UpdateLoaderFactories(
base::OnceClosure callback) {
RenderProcessHost* rph = RenderProcessHost::FromID(worker_process_id_);
if (!rph) {
std::move(callback).Run();
return;
}
const url::Origin origin = url::Origin::Create(url_);
mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
coep_reporter_for_script_loader;
mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
coep_reporter_for_subresource_loader;
if (coep_reporter_) {
coep_reporter_->Clone(
coep_reporter_for_script_loader.InitWithNewPipeAndPassReceiver());
coep_reporter_->Clone(
coep_reporter_for_subresource_loader.InitWithNewPipeAndPassReceiver());
}
// Use the default CrossOriginEmbedderPolicy if
// |cross_origin_embedder_policy_| is nullopt. It's acceptable because the
// factory bundles are updated with correct COEP value before any subresource
// requests in that case.
auto script_bundle = EmbeddedWorkerInstance::CreateFactoryBundleOnUI(
rph, worker_route_id_, origin,
cross_origin_embedder_policy_ ? cross_origin_embedder_policy_.value()
: network::CrossOriginEmbedderPolicy(),
std::move(coep_reporter_for_script_loader),
ContentBrowserClient::URLLoaderFactoryType::kServiceWorkerScript);
auto subresource_bundle = EmbeddedWorkerInstance::CreateFactoryBundleOnUI(
rph, worker_route_id_, origin,
cross_origin_embedder_policy_ ? cross_origin_embedder_policy_.value()
: network::CrossOriginEmbedderPolicy(),
std::move(coep_reporter_for_subresource_loader),
ContentBrowserClient::URLLoaderFactoryType::kServiceWorkerSubResource);
if (ServiceWorkerContext::IsServiceWorkerOnUIEnabled()) {
UpdateLoaderFactoriesOnCoreThread(context_wrapper_, version_id_,
std::move(script_bundle),
std::move(subresource_bundle));
std::move(callback).Run();
} else {
GetIOThreadTaskRunner({})->PostTaskAndReply(
FROM_HERE,
base::BindOnce(&UpdateLoaderFactoriesOnCoreThread, context_wrapper_,
version_id_, std::move(script_bundle),
std::move(subresource_bundle)),
std::move(callback));
}
}
} // namespace content