blob: e4bea03b6b7755ed62dc2fa973dd23c2c71a4fc8 [file] [log] [blame]
// Copyright 2014 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/browser_devtools_agent_host.h"
#include "base/clang_profiling_buildflags.h"
#include "base/functional/bind.h"
#include "base/json/json_reader.h"
#include "base/memory/ptr_util.h"
#include "base/no_destructor.h"
#include "base/task/single_thread_task_runner.h"
#include "base/uuid.h"
#include "build/config/compiler/compiler_buildflags.h"
#include "components/viz/common/buildflags.h"
#include "content/browser/devtools/devtools_session.h"
#include "content/browser/devtools/protocol/browser_handler.h"
#include "content/browser/devtools/protocol/fetch_handler.h"
#include "content/browser/devtools/protocol/io_handler.h"
#include "content/browser/devtools/protocol/memory_handler.h"
#include "content/browser/devtools/protocol/protocol.h"
#include "content/browser/devtools/protocol/security_handler.h"
#include "content/browser/devtools/protocol/storage_handler.h"
#include "content/browser/devtools/protocol/system_info_handler.h"
#include "content/browser/devtools/protocol/target_handler.h"
#include "content/browser/devtools/protocol/tethering_handler.h"
#include "content/browser/devtools/protocol/tracing_handler.h"
#include "content/browser/devtools/render_frame_devtools_agent_host.h"
#include "content/browser/devtools/service_worker_devtools_agent_host.h"
#include "content/browser/renderer_host/frame_tree_node.h"
#include "content/browser/web_contents/web_contents_impl.h"
#if BUILDFLAG(USE_VIZ_DEBUGGER)
#include "content/browser/devtools/protocol/visual_debugger_handler.h"
#endif
#if BUILDFLAG(CLANG_PROFILING_INSIDE_SANDBOX) && BUILDFLAG(CLANG_PGO)
#include "content/browser/devtools/protocol/native_profiling_handler.h"
#endif
namespace content {
scoped_refptr<DevToolsAgentHost> DevToolsAgentHost::CreateForBrowser(
scoped_refptr<base::SingleThreadTaskRunner> tethering_task_runner,
const CreateServerSocketCallback& socket_callback) {
return new BrowserDevToolsAgentHost(
tethering_task_runner, socket_callback, false);
}
scoped_refptr<DevToolsAgentHost> DevToolsAgentHost::CreateForDiscovery() {
CreateServerSocketCallback null_callback;
return new BrowserDevToolsAgentHost(nullptr, std::move(null_callback), true);
}
namespace {
std::set<BrowserDevToolsAgentHost*>& BrowserDevToolsAgentHostInstances() {
static base::NoDestructor<std::set<BrowserDevToolsAgentHost*>> instances;
return *instances;
}
} // namespace
class BrowserDevToolsAgentHost::BrowserAutoAttacher final
: public protocol::TargetAutoAttacher,
public ServiceWorkerDevToolsManager::Observer,
public DevToolsAgentHostObserver {
public:
BrowserAutoAttacher() = default;
~BrowserAutoAttacher() override = default;
protected:
// ServiceWorkerDevToolsManager::Observer implementation.
void WorkerCreated(ServiceWorkerDevToolsAgentHost* host,
bool* should_pause_on_start) override {
*should_pause_on_start = wait_for_debugger_on_start();
DispatchAutoAttach(host, *should_pause_on_start);
}
void WorkerDestroyed(ServiceWorkerDevToolsAgentHost* host) override {
DispatchAutoDetach(host);
}
void ReattachServiceWorkers() {
DCHECK(auto_attach());
ServiceWorkerDevToolsAgentHost::List agent_hosts;
ServiceWorkerDevToolsManager::GetInstance()->AddAllAgentHosts(&agent_hosts);
Hosts new_hosts(agent_hosts.begin(), agent_hosts.end());
DispatchSetAttachedTargetsOfType(new_hosts,
DevToolsAgentHost::kTypeServiceWorker);
}
void UpdateAutoAttach(base::OnceClosure callback) override {
if (auto_attach()) {
base::AutoReset<bool> auto_reset(&processing_existent_targets_, true);
if (!have_observers_) {
ServiceWorkerDevToolsManager::GetInstance()->AddObserver(this);
// DevToolsAgentHost's observer immediately notifies about all existing
// ones.
DevToolsAgentHost::AddObserver(this);
} else {
// Manually collect existing hosts to update the list.
DevToolsAgentHost::List hosts;
RenderFrameDevToolsAgentHost::AddAllAgentHosts(&hosts);
for (auto& host : hosts)
DevToolsAgentHostCreated(host.get());
}
ReattachServiceWorkers();
} else {
if (have_observers_) {
DevToolsAgentHost::RemoveObserver(this);
ServiceWorkerDevToolsManager::GetInstance()->RemoveObserver(this);
}
}
have_observers_ = auto_attach();
std::move(callback).Run();
}
// DevToolsAgentHostObserver overrides.
void DevToolsAgentHostCreated(DevToolsAgentHost* host) override {
DCHECK(auto_attach());
// In the top level target handler, auto-attach to pages as soon as they
// are created, otherwise if they don't incur any network activity we'll
// never get a chance to throttle them (and auto-attach there).
if (ShouldAttachToTarget(host)) {
DispatchAutoAttach(
host, wait_for_debugger_on_start() && !processing_existent_targets_);
}
}
bool ShouldForceDevToolsAgentHostCreation() override { return true; }
static bool ShouldAttachToTarget(DevToolsAgentHost* host) {
if (host->GetType() == DevToolsAgentHost::kTypeSharedWorker) {
return true;
}
if (host->GetType() == DevToolsAgentHost::kTypeTab) {
return true;
}
return IsMainFrameHost(host);
}
static bool IsMainFrameHost(DevToolsAgentHost* host) {
WebContentsImpl* web_contents =
static_cast<WebContentsImpl*>(host->GetWebContents());
if (!web_contents)
return false;
FrameTreeNode* frame_tree_node = web_contents->GetPrimaryFrameTree().root();
if (!frame_tree_node)
return false;
return host == RenderFrameDevToolsAgentHost::GetFor(frame_tree_node);
}
bool processing_existent_targets_ = false;
bool have_observers_ = false;
};
// static
const std::set<BrowserDevToolsAgentHost*>&
BrowserDevToolsAgentHost::Instances() {
return BrowserDevToolsAgentHostInstances();
}
BrowserDevToolsAgentHost::BrowserDevToolsAgentHost(
scoped_refptr<base::SingleThreadTaskRunner> tethering_task_runner,
const CreateServerSocketCallback& socket_callback,
bool only_discovery)
: DevToolsAgentHostImpl(base::Uuid::GenerateRandomV4().AsLowercaseString()),
auto_attacher_(std::make_unique<BrowserAutoAttacher>()),
tethering_task_runner_(tethering_task_runner),
socket_callback_(socket_callback),
only_discovery_(only_discovery) {
NotifyCreated();
BrowserDevToolsAgentHostInstances().insert(this);
}
BrowserDevToolsAgentHost::~BrowserDevToolsAgentHost() {
BrowserDevToolsAgentHostInstances().erase(this);
}
bool BrowserDevToolsAgentHost::AttachSession(DevToolsSession* session,
bool acquire_wake_lock) {
if (!session->GetClient()->IsTrusted())
return false;
session->SetBrowserOnly(true);
session->CreateAndAddHandler<protocol::TargetHandler>(
protocol::TargetHandler::AccessMode::kBrowser, GetId(),
auto_attacher_.get(), session);
if (only_discovery_)
return true;
session->CreateAndAddHandler<protocol::BrowserHandler>(
session->GetClient()->MayWriteLocalFiles());
#if BUILDFLAG(USE_VIZ_DEBUGGER)
session->CreateAndAddHandler<protocol::VisualDebuggerHandler>();
#endif
session->CreateAndAddHandler<protocol::IOHandler>(GetIOContext());
session->CreateAndAddHandler<protocol::FetchHandler>(
GetIOContext(),
base::BindRepeating([](base::OnceClosure cb) { std::move(cb).Run(); }));
session->CreateAndAddHandler<protocol::MemoryHandler>();
session->CreateAndAddHandler<protocol::SecurityHandler>();
session->CreateAndAddHandler<protocol::StorageHandler>(
session->GetClient()->IsTrusted());
session->CreateAndAddHandler<protocol::SystemInfoHandler>(
/* is_browser_sessoin= */ true);
if (tethering_task_runner_) {
session->CreateAndAddHandler<protocol::TetheringHandler>(
socket_callback_, tethering_task_runner_);
}
session->CreateAndAddHandler<protocol::TracingHandler>(
this, GetIOContext(), /* root_session */ nullptr);
#if BUILDFLAG(CLANG_PROFILING_INSIDE_SANDBOX) && BUILDFLAG(CLANG_PGO)
session->CreateAndAddHandler<protocol::NativeProfilingHandler>();
#endif
return true;
}
void BrowserDevToolsAgentHost::DetachSession(DevToolsSession* session) {
}
protocol::TargetAutoAttacher* BrowserDevToolsAgentHost::auto_attacher() {
return auto_attacher_.get();
}
std::string BrowserDevToolsAgentHost::GetType() {
return kTypeBrowser;
}
std::string BrowserDevToolsAgentHost::GetTitle() {
return "";
}
GURL BrowserDevToolsAgentHost::GetURL() {
return GURL();
}
bool BrowserDevToolsAgentHost::Activate() {
return false;
}
bool BrowserDevToolsAgentHost::Close() {
return false;
}
void BrowserDevToolsAgentHost::Reload() {
}
} // content