blob: f7163f244635c037d57ddb897a47b834ec83c649 [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/worker_devtools_agent_host.h"
#include "base/guid.h"
#include "base/memory/ptr_util.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/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"
namespace content {
BrowserContext* WorkerDevToolsAgentHost::GetBrowserContext() {
RenderProcessHost* rph = RenderProcessHost::FromID(worker_id_.first);
return rph ? rph->GetBrowserContext() : nullptr;
}
void WorkerDevToolsAgentHost::AttachSession(DevToolsSession* session) {
if (state_ != WORKER_INSPECTED) {
state_ = WORKER_INSPECTED;
AttachToWorker();
}
if (RenderProcessHost* host = RenderProcessHost::FromID(worker_id_.first)) {
session->SetRenderer(host, nullptr);
host->Send(
new DevToolsAgentMsg_Attach(worker_id_.second, 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()));
OnAttachedStateChanged(true);
}
void WorkerDevToolsAgentHost::DetachSession(int session_id) {
if (RenderProcessHost* host = RenderProcessHost::FromID(worker_id_.first))
host->Send(new DevToolsAgentMsg_Detach(worker_id_.second, session_id));
OnAttachedStateChanged(false);
if (state_ == WORKER_INSPECTED) {
state_ = WORKER_UNINSPECTED;
DetachFromWorker();
} else if (state_ == WORKER_PAUSED_FOR_REATTACH) {
state_ = WORKER_UNINSPECTED;
}
}
bool WorkerDevToolsAgentHost::DispatchProtocolMessage(
DevToolsSession* session,
const std::string& message) {
if (state_ != WORKER_INSPECTED)
return true;
int call_id = 0;
std::string method;
if (session->Dispatch(message, &call_id, &method) !=
protocol::Response::kFallThrough) {
return true;
}
if (RenderProcessHost* host = RenderProcessHost::FromID(worker_id_.first)) {
host->Send(new DevToolsAgentMsg_DispatchOnInspectorBackend(
worker_id_.second, session->session_id(), call_id, method, message));
session->waiting_messages()[call_id] = {method, message};
}
return true;
}
bool WorkerDevToolsAgentHost::OnMessageReceived(
const IPC::Message& msg) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(WorkerDevToolsAgentHost, msg)
IPC_MESSAGE_HANDLER(DevToolsClientMsg_DispatchOnInspectorFrontend,
OnDispatchOnInspectorFrontend)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void WorkerDevToolsAgentHost::PauseForDebugOnStart() {
DCHECK(state_ == WORKER_UNINSPECTED);
state_ = WORKER_PAUSED_FOR_DEBUG_ON_START;
}
bool WorkerDevToolsAgentHost::IsPausedForDebugOnStart() {
return state_ == WORKER_PAUSED_FOR_DEBUG_ON_START ||
state_ == WORKER_READY_FOR_DEBUG_ON_START;
}
bool WorkerDevToolsAgentHost::IsReadyForInspection() {
return state_ == WORKER_INSPECTED || state_ == WORKER_UNINSPECTED ||
state_ == WORKER_READY_FOR_DEBUG_ON_START;
}
void WorkerDevToolsAgentHost::WorkerReadyForInspection() {
if (state_ == WORKER_PAUSED_FOR_REATTACH) {
DCHECK(IsAttached());
state_ = WORKER_INSPECTED;
AttachToWorker();
if (RenderProcessHost* host = RenderProcessHost::FromID(worker_id_.first)) {
for (DevToolsSession* session : sessions()) {
session->SetRenderer(host, nullptr);
host->Send(new DevToolsAgentMsg_Reattach(
worker_id_.second, 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_id_.second, session->session_id(), call_id, message.method,
message.message));
}
}
}
OnAttachedStateChanged(true);
} else if (state_ == WORKER_PAUSED_FOR_DEBUG_ON_START) {
state_ = WORKER_READY_FOR_DEBUG_ON_START;
}
}
void WorkerDevToolsAgentHost::WorkerRestarted(WorkerId worker_id) {
DCHECK_EQ(WORKER_TERMINATED, state_);
state_ = IsAttached() ? WORKER_PAUSED_FOR_REATTACH : WORKER_UNINSPECTED;
worker_id_ = worker_id;
RenderProcessHost* host = RenderProcessHost::FromID(worker_id_.first);
for (DevToolsSession* session : sessions())
session->SetRenderer(host, nullptr);
WorkerCreated();
}
void WorkerDevToolsAgentHost::WorkerDestroyed() {
DCHECK_NE(WORKER_TERMINATED, state_);
if (state_ == WORKER_INSPECTED) {
DCHECK(IsAttached());
for (auto* inspector : protocol::InspectorHandler::ForAgentHost(this))
inspector->TargetCrashed();
DetachFromWorker();
for (DevToolsSession* session : sessions())
session->SetRenderer(nullptr, nullptr);
}
state_ = WORKER_TERMINATED;
Release(); // Balanced in WorkerCreated().
}
bool WorkerDevToolsAgentHost::IsTerminated() {
return state_ == WORKER_TERMINATED;
}
WorkerDevToolsAgentHost::WorkerDevToolsAgentHost(
const base::UnguessableToken& devtools_worker_token,
WorkerId worker_id)
: DevToolsAgentHostImpl(devtools_worker_token.ToString()),
state_(WORKER_UNINSPECTED),
worker_id_(worker_id) {
WorkerCreated();
}
WorkerDevToolsAgentHost::~WorkerDevToolsAgentHost() {
DCHECK_EQ(WORKER_TERMINATED, state_);
}
void WorkerDevToolsAgentHost::OnAttachedStateChanged(bool attached) {
}
void WorkerDevToolsAgentHost::AttachToWorker() {
if (RenderProcessHost* host = RenderProcessHost::FromID(worker_id_.first))
host->AddRoute(worker_id_.second, this);
}
void WorkerDevToolsAgentHost::DetachFromWorker() {
if (RenderProcessHost* host = RenderProcessHost::FromID(worker_id_.first))
host->RemoveRoute(worker_id_.second);
}
void WorkerDevToolsAgentHost::WorkerCreated() {
AddRef(); // Balanced in WorkerDestroyed()
}
void WorkerDevToolsAgentHost::OnDispatchOnInspectorFrontend(
const DevToolsMessageChunk& message) {
if (!IsAttached())
return;
DevToolsSession* session = SessionById(message.session_id);
if (session)
session->ReceiveMessageChunk(message);
}
} // namespace content