blob: 400eb0e633ca4d606ee8dbb3b2629ffea249ea68 [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/strings/stringprintf.h"
#include "content/browser/devtools/devtools_session.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/service_worker_devtools_manager.h"
#include "content/browser/service_worker/service_worker_context_core.h"
#include "content/browser/service_worker/service_worker_version.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"
namespace content {
namespace {
void TerminateServiceWorkerOnIO(
base::WeakPtr<ServiceWorkerContextCore> context_weak,
int64_t version_id) {
if (ServiceWorkerContextCore* context = context_weak.get()) {
if (ServiceWorkerVersion* version = context->GetLiveVersion(version_id))
version->StopWorker(base::BindOnce(&base::DoNothing));
}
}
void SetDevToolsAttachedOnIO(
base::WeakPtr<ServiceWorkerContextCore> context_weak,
int64_t version_id,
bool attached) {
if (ServiceWorkerContextCore* context = context_weak.get()) {
if (ServiceWorkerVersion* version = context->GetLiveVersion(version_id))
version->SetDevToolsAttached(attached);
}
}
} // namespace
ServiceWorkerDevToolsAgentHost::ServiceWorkerDevToolsAgentHost(
int worker_process_id,
int worker_route_id,
const ServiceWorkerContextCore* context,
base::WeakPtr<ServiceWorkerContextCore> context_weak,
int64_t version_id,
const GURL& url,
const GURL& scope,
bool is_installed_version,
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_(context),
context_weak_(context_weak),
version_id_(version_id),
url_(url),
scope_(scope),
version_installed_time_(is_installed_version ? base::Time::Now()
: base::Time()) {
NotifyCreated();
}
BrowserContext* ServiceWorkerDevToolsAgentHost::GetBrowserContext() {
RenderProcessHost* rph = RenderProcessHost::FromID(worker_process_id_);
return rph ? rph->GetBrowserContext() : nullptr;
}
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() {
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::BindOnce(&TerminateServiceWorkerOnIO, context_weak_, version_id_));
return true;
}
void ServiceWorkerDevToolsAgentHost::WorkerVersionInstalled() {
version_installed_time_ = base::Time::Now();
}
void ServiceWorkerDevToolsAgentHost::WorkerVersionDoomed() {
version_doomed_time_ = base::Time::Now();
}
bool ServiceWorkerDevToolsAgentHost::Matches(
const ServiceWorkerContextCore* context,
int64_t version_id) {
return context_ == context && version_id_ == version_id;
}
ServiceWorkerDevToolsAgentHost::~ServiceWorkerDevToolsAgentHost() {
ServiceWorkerDevToolsManager::GetInstance()->AgentHostDestroyed(this);
}
void ServiceWorkerDevToolsAgentHost::AttachSession(DevToolsSession* session) {
if (state_ == WORKER_READY) {
if (sessions().size() == 1) {
AttachToWorker();
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::BindOnce(&SetDevToolsAttachedOnIO,
context_weak_, version_id_, true));
}
if (RenderProcessHost* host =
RenderProcessHost::FromID(worker_process_id_)) {
session->SetRenderer(host, nullptr);
host->Send(
new DevToolsAgentMsg_Attach(worker_route_id_, session->session_id()));
}
}
session->SetFallThroughForNotFound(true);
session->AddHandler(base::WrapUnique(new protocol::InspectorHandler()));
session->AddHandler(base::WrapUnique(new protocol::NetworkHandler(GetId())));
session->AddHandler(base::WrapUnique(new protocol::SchemaHandler()));
}
void ServiceWorkerDevToolsAgentHost::DetachSession(int session_id) {
if (state_ == WORKER_READY) {
if (RenderProcessHost* host = RenderProcessHost::FromID(worker_process_id_))
host->Send(new DevToolsAgentMsg_Detach(worker_route_id_, session_id));
if (sessions().empty()) {
DetachFromWorker();
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::BindOnce(&SetDevToolsAttachedOnIO, context_weak_, version_id_,
false));
}
}
}
bool ServiceWorkerDevToolsAgentHost::DispatchProtocolMessage(
DevToolsSession* session,
const std::string& message) {
int call_id = 0;
std::string method;
if (session->Dispatch(message, &call_id, &method) !=
protocol::Response::kFallThrough) {
return true;
}
if (state_ == WORKER_READY) {
if (RenderProcessHost* host =
RenderProcessHost::FromID(worker_process_id_)) {
host->Send(new DevToolsAgentMsg_DispatchOnInspectorBackend(
worker_route_id_, session->session_id(), call_id, method, message));
}
}
session->waiting_messages()[call_id] = {method, message};
return true;
}
bool ServiceWorkerDevToolsAgentHost::OnMessageReceived(
const IPC::Message& msg) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(ServiceWorkerDevToolsAgentHost, msg)
IPC_MESSAGE_HANDLER(DevToolsClientMsg_DispatchOnInspectorFrontend,
OnDispatchOnInspectorFrontend)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void ServiceWorkerDevToolsAgentHost::WorkerReadyForInspection() {
DCHECK_EQ(WORKER_NOT_READY, state_);
state_ = WORKER_READY;
if (!sessions().empty()) {
AttachToWorker();
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::BindOnce(&SetDevToolsAttachedOnIO,
context_weak_, version_id_, true));
}
if (RenderProcessHost* host = RenderProcessHost::FromID(worker_process_id_)) {
for (DevToolsSession* session : sessions()) {
session->SetRenderer(host, nullptr);
host->Send(new DevToolsAgentMsg_Reattach(
worker_route_id_, session->session_id(), session->state_cookie()));
for (const auto& pair : session->waiting_messages()) {
int call_id = pair.first;
const DevToolsSession::Message& message = pair.second;
host->Send(new DevToolsAgentMsg_DispatchOnInspectorBackend(
worker_route_id_, session->session_id(), call_id, message.method,
message.message));
}
}
}
}
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;
RenderProcessHost* host = RenderProcessHost::FromID(worker_process_id_);
for (DevToolsSession* session : sessions())
session->SetRenderer(host, nullptr);
}
void ServiceWorkerDevToolsAgentHost::WorkerDestroyed() {
DCHECK_NE(WORKER_TERMINATED, state_);
state_ = WORKER_TERMINATED;
for (auto* inspector : protocol::InspectorHandler::ForAgentHost(this))
inspector->TargetCrashed();
for (DevToolsSession* session : sessions())
session->SetRenderer(nullptr, nullptr);
if (!sessions().empty())
DetachFromWorker();
}
void ServiceWorkerDevToolsAgentHost::AttachToWorker() {
if (RenderProcessHost* host = RenderProcessHost::FromID(worker_process_id_))
host->AddRoute(worker_route_id_, this);
}
void ServiceWorkerDevToolsAgentHost::DetachFromWorker() {
if (RenderProcessHost* host = RenderProcessHost::FromID(worker_process_id_))
host->RemoveRoute(worker_route_id_);
}
void ServiceWorkerDevToolsAgentHost::OnDispatchOnInspectorFrontend(
const DevToolsMessageChunk& message) {
DevToolsSession* session = SessionById(message.session_id);
if (session)
session->ReceiveMessageChunk(message);
}
} // namespace content