blob: 466d7fc51dd382040e13dd70ff4d593c9e24dbc4 [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/devtools/devtools_renderer_channel.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/safety_checks.h"
#include "content/browser/devtools/dedicated_worker_devtools_agent_host.h"
#include "content/browser/devtools/devtools_agent_host_impl.h"
#include "content/browser/devtools/devtools_manager.h"
#include "content/browser/devtools/devtools_session.h"
#include "content/browser/devtools/protocol/devtools_domain_handler.h"
#include "content/browser/devtools/worker_devtools_manager.h"
#include "content/browser/devtools/worklet_devtools_agent_host.h"
#include "content/public/browser/child_process_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_delegate.h"
#include "third_party/blink/public/common/features.h"
#include "ui/gfx/geometry/point.h"
namespace content {
DevToolsRendererChannel::DevToolsRendererChannel(DevToolsAgentHostImpl* owner)
: owner_(owner),
process_id_(ChildProcessHost::kInvalidUniqueID) {}
DevToolsRendererChannel::~DevToolsRendererChannel() = default;
void DevToolsRendererChannel::SetRenderer(
mojo::PendingRemote<blink::mojom::DevToolsAgent> agent_remote,
mojo::PendingReceiver<blink::mojom::DevToolsAgentHost> host_receiver,
int process_id,
base::OnceClosure connection_error) {
CleanupConnection();
blink::mojom::DevToolsAgent* agent = nullptr;
if (agent_remote.is_valid()) {
agent_remote_.Bind(std::move(agent_remote));
agent = agent_remote_.get();
}
if (connection_error)
agent_remote_.set_disconnect_handler(std::move(connection_error));
if (host_receiver)
receiver_.Bind(std::move(host_receiver));
const bool force_using_io = true;
SetRendererInternal(agent, process_id, nullptr, force_using_io);
}
void DevToolsRendererChannel::SetRendererAssociated(
mojo::PendingAssociatedRemote<blink::mojom::DevToolsAgent> agent_remote,
mojo::PendingAssociatedReceiver<blink::mojom::DevToolsAgentHost>
host_receiver,
int process_id,
RenderFrameHostImpl* frame_host) {
CleanupConnection();
blink::mojom::DevToolsAgent* agent = nullptr;
if (agent_remote.is_valid()) {
associated_agent_remote_.Bind(std::move(agent_remote));
agent = associated_agent_remote_.get();
}
if (host_receiver)
associated_receiver_.Bind(std::move(host_receiver));
const bool force_using_io = false;
SetRendererInternal(agent, process_id, frame_host, force_using_io);
}
void DevToolsRendererChannel::CleanupConnection() {
receiver_.reset();
associated_receiver_.reset();
associated_agent_remote_.reset();
agent_remote_.reset();
}
void DevToolsRendererChannel::ForceDetachWorkerSessions() {
for (WorkerOrWorkletDevToolsAgentHost* host : child_targets_) {
host->ForceDetachAllSessions();
}
}
void DevToolsRendererChannel::SetRendererInternal(
blink::mojom::DevToolsAgent* agent,
int process_id,
RenderFrameHostImpl* frame_host,
bool force_using_io) {
ReportChildTargetsCallback();
process_id_ = process_id;
frame_host_ = frame_host;
if (agent && child_target_created_callback_) {
agent->ReportChildTargets(true /* report */, wait_for_debugger_,
base::DoNothing());
}
for (DevToolsSession* session : owner_->sessions()) {
for (auto& pair : session->handlers())
pair.second->SetRenderer(process_id_, frame_host_);
session->AttachToAgent(agent, force_using_io);
}
}
void DevToolsRendererChannel::AttachSession(DevToolsSession* session) {
if (!agent_remote_ && !associated_agent_remote_)
owner_->UpdateRendererChannel(true /* force */);
for (auto& pair : session->handlers())
pair.second->SetRenderer(process_id_, frame_host_);
if (agent_remote_)
session->AttachToAgent(agent_remote_.get(), true);
else if (associated_agent_remote_)
session->AttachToAgent(associated_agent_remote_.get(), false);
}
void DevToolsRendererChannel::InspectElement(const gfx::Point& point) {
if (!agent_remote_ && !associated_agent_remote_)
owner_->UpdateRendererChannel(true /* force */);
// Previous call might update |agent_remote_| or |associated_agent_remote_|
// via SetRenderer(), so we should check them again.
if (agent_remote_)
agent_remote_->InspectElement(point);
else if (associated_agent_remote_)
associated_agent_remote_->InspectElement(point);
}
void DevToolsRendererChannel::SetReportChildTargets(
ChildTargetCreatedCallback report_callback,
bool wait_for_debugger,
base::OnceClosure completion_callback) {
DCHECK(report_callback || !wait_for_debugger);
ReportChildTargetsCallback();
set_report_completion_callback_ = std::move(completion_callback);
if (child_target_created_callback_ == report_callback &&
wait_for_debugger_ == wait_for_debugger) {
ReportChildTargetsCallback();
return;
}
if (report_callback) {
for (DevToolsAgentHostImpl* host : child_targets_)
report_callback.Run(host, false /* waiting_for_debugger */);
}
child_target_created_callback_ = std::move(report_callback);
wait_for_debugger_ = wait_for_debugger;
if (agent_remote_) {
agent_remote_->ReportChildTargets(
!!child_target_created_callback_, wait_for_debugger_,
base::BindOnce(&DevToolsRendererChannel::ReportChildTargetsCallback,
base::Unretained(this)));
} else if (associated_agent_remote_) {
associated_agent_remote_->ReportChildTargets(
!!child_target_created_callback_, wait_for_debugger_,
base::BindOnce(&DevToolsRendererChannel::ReportChildTargetsCallback,
base::Unretained(this)));
} else {
ReportChildTargetsCallback();
}
}
void DevToolsRendererChannel::ReportChildTargetsCallback() {
if (set_report_completion_callback_)
std::move(set_report_completion_callback_).Run();
}
void DevToolsRendererChannel::ChildTargetCreated(
mojo::PendingRemote<blink::mojom::DevToolsAgent> worker_devtools_agent,
mojo::PendingReceiver<blink::mojom::DevToolsAgentHost> host_receiver,
const GURL& url,
const std::string& name,
const base::UnguessableToken& devtools_worker_token,
bool waiting_for_debugger,
blink::mojom::DevToolsExecutionContextType context_type) {
// This function is known to be heap allocation heavy and performance
// critical. Extra memory safety checks can introduce regression
// (https://crbug.com/414710225) and these are disabled here.
base::ScopedSafetyChecksExclusion scoped_unsafe;
RenderProcessHost* process = RenderProcessHost::FromID(process_id_);
if (!process) {
return;
}
GURL filtered_url = url;
process->FilterURL(/*empty_allowed=*/true, &filtered_url);
scoped_refptr<WorkerOrWorkletDevToolsAgentHost> agent_host;
switch (context_type) {
case blink::mojom::DevToolsExecutionContextType::kDedicatedWorker: {
// WorkerDevToolsAgentHost for dedicated workers is already created in the
// browser process.
DCHECK(content::DevToolsAgentHost::GetForId(
devtools_worker_token.ToString()));
DedicatedWorkerDevToolsAgentHost* dedicated_worker_agent_host =
WorkerDevToolsManager::GetInstance().GetDevToolsHostFromToken(
devtools_worker_token);
if (!dedicated_worker_agent_host) {
// If `dedicated_worker_agent_host` is nullptr, we can assume that
// `DedicatedWorkerHost` has been destructed while handling
// `DedicatedWorker::ContinueStart`. We do not need to continue in that
// case.
return;
}
dedicated_worker_agent_host->ChildWorkerCreated(
url, name,
base::BindOnce(&DevToolsRendererChannel::ChildTargetDestroyed,
weak_factory_.GetWeakPtr()));
agent_host = dedicated_worker_agent_host;
break;
}
case blink::mojom::DevToolsExecutionContextType::kWorklet:
if (content::DevToolsAgentHost::GetForId(
devtools_worker_token.ToString())) {
mojo::ReportBadMessage("Workers should have unique tokens.");
return;
}
agent_host = base::MakeRefCounted<WorkletDevToolsAgentHost>(
process_id_, filtered_url, std::move(name), devtools_worker_token,
owner_->GetId(),
base::BindOnce(&DevToolsRendererChannel::ChildTargetDestroyed,
weak_factory_.GetWeakPtr()));
break;
}
agent_host->SetRenderer(process_id_, std::move(worker_devtools_agent),
std::move(host_receiver));
child_targets_.insert(agent_host.get());
if (child_target_created_callback_) {
child_target_created_callback_.Run(agent_host.get(), waiting_for_debugger);
}
}
void DevToolsRendererChannel::ChildTargetDestroyed(
DevToolsAgentHostImpl* host) {
child_targets_.erase(host);
}
void DevToolsRendererChannel::MainThreadDebuggerPaused() {
owner_->MainThreadDebuggerPaused();
}
void DevToolsRendererChannel::MainThreadDebuggerResumed() {
owner_->MainThreadDebuggerResumed();
}
void DevToolsRendererChannel::BringToForeground() {
DevToolsManager* manager = DevToolsManager::GetInstance();
if (manager->delegate()) {
manager->delegate()->Activate(owner_);
}
}
} // namespace content