blob: a00aa7eadb52d151651ac094bda6d061075d307c [file] [log] [blame]
// Copyright 2016 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 "headless/lib/browser/headless_devtools_client_impl.h"
#include <memory>
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/memory/ptr_util.h"
#include "base/values.h"
#include "content/public/browser/devtools_agent_host.h"
namespace headless {
// static
std::unique_ptr<HeadlessDevToolsClient> HeadlessDevToolsClient::Create() {
return base::WrapUnique(new HeadlessDevToolsClientImpl());
}
// static
HeadlessDevToolsClientImpl* HeadlessDevToolsClientImpl::From(
HeadlessDevToolsClient* client) {
// This downcast is safe because there is only one implementation of
// HeadlessDevToolsClient.
return static_cast<HeadlessDevToolsClientImpl*>(client);
}
HeadlessDevToolsClientImpl::HeadlessDevToolsClientImpl()
: agent_host_(nullptr),
next_message_id_(0),
accessibility_domain_(this),
animation_domain_(this),
application_cache_domain_(this),
browser_domain_(this),
cache_storage_domain_(this),
console_domain_(this),
css_domain_(this),
database_domain_(this),
debugger_domain_(this),
device_orientation_domain_(this),
dom_debugger_domain_(this),
dom_domain_(this),
dom_storage_domain_(this),
emulation_domain_(this),
heap_profiler_domain_(this),
indexeddb_domain_(this),
input_domain_(this),
inspector_domain_(this),
io_domain_(this),
layer_tree_domain_(this),
memory_domain_(this),
network_domain_(this),
page_domain_(this),
profiler_domain_(this),
rendering_domain_(this),
runtime_domain_(this),
security_domain_(this),
service_worker_domain_(this),
tracing_domain_(this),
worker_domain_(this) {}
HeadlessDevToolsClientImpl::~HeadlessDevToolsClientImpl() {}
void HeadlessDevToolsClientImpl::AttachToHost(
content::DevToolsAgentHost* agent_host) {
DCHECK(!agent_host_);
agent_host_ = agent_host;
agent_host_->AttachClient(this);
}
void HeadlessDevToolsClientImpl::DetachFromHost(
content::DevToolsAgentHost* agent_host) {
DCHECK_EQ(agent_host_, agent_host);
agent_host_->DetachClient(this);
agent_host_ = nullptr;
pending_messages_.clear();
}
void HeadlessDevToolsClientImpl::DispatchProtocolMessage(
content::DevToolsAgentHost* agent_host,
const std::string& json_message) {
DCHECK_EQ(agent_host_, agent_host);
std::unique_ptr<base::Value> message =
base::JSONReader::Read(json_message, base::JSON_PARSE_RFC);
const base::DictionaryValue* message_dict;
if (!message || !message->GetAsDictionary(&message_dict)) {
NOTREACHED() << "Badly formed reply";
return;
}
if (!DispatchMessageReply(*message_dict) && !DispatchEvent(*message_dict))
DLOG(ERROR) << "Unhandled protocol message: " << json_message;
}
bool HeadlessDevToolsClientImpl::DispatchMessageReply(
const base::DictionaryValue& message_dict) {
int id = 0;
if (!message_dict.GetInteger("id", &id))
return false;
auto it = pending_messages_.find(id);
if (it == pending_messages_.end()) {
NOTREACHED() << "Unexpected reply";
return false;
}
Callback callback = std::move(it->second);
pending_messages_.erase(it);
if (!callback.callback_with_result.is_null()) {
const base::DictionaryValue* result_dict;
if (!message_dict.GetDictionary("result", &result_dict)) {
NOTREACHED() << "Badly formed reply result";
return false;
}
callback.callback_with_result.Run(*result_dict);
} else if (!callback.callback.is_null()) {
callback.callback.Run();
}
return true;
}
bool HeadlessDevToolsClientImpl::DispatchEvent(
const base::DictionaryValue& message_dict) {
std::string method;
if (!message_dict.GetString("method", &method))
return false;
auto it = event_handlers_.find(method);
if (it == event_handlers_.end()) {
NOTREACHED() << "Unknown event: " << method;
return false;
}
if (!it->second.is_null()) {
const base::DictionaryValue* result_dict;
if (!message_dict.GetDictionary("params", &result_dict)) {
NOTREACHED() << "Badly formed event parameters";
return false;
}
it->second.Run(*result_dict);
}
return true;
}
void HeadlessDevToolsClientImpl::AgentHostClosed(
content::DevToolsAgentHost* agent_host,
bool replaced_with_another_client) {
DCHECK_EQ(agent_host_, agent_host);
agent_host = nullptr;
pending_messages_.clear();
}
accessibility::Domain* HeadlessDevToolsClientImpl::GetAccessibility() {
return &accessibility_domain_;
}
animation::Domain* HeadlessDevToolsClientImpl::GetAnimation() {
return &animation_domain_;
}
application_cache::Domain* HeadlessDevToolsClientImpl::GetApplicationCache() {
return &application_cache_domain_;
}
browser::Domain* HeadlessDevToolsClientImpl::GetBrowser() {
return &browser_domain_;
}
cache_storage::Domain* HeadlessDevToolsClientImpl::GetCacheStorage() {
return &cache_storage_domain_;
}
console::Domain* HeadlessDevToolsClientImpl::GetConsole() {
return &console_domain_;
}
css::Domain* HeadlessDevToolsClientImpl::GetCSS() {
return &css_domain_;
}
database::Domain* HeadlessDevToolsClientImpl::GetDatabase() {
return &database_domain_;
}
debugger::Domain* HeadlessDevToolsClientImpl::GetDebugger() {
return &debugger_domain_;
}
device_orientation::Domain* HeadlessDevToolsClientImpl::GetDeviceOrientation() {
return &device_orientation_domain_;
}
dom_debugger::Domain* HeadlessDevToolsClientImpl::GetDOMDebugger() {
return &dom_debugger_domain_;
}
dom::Domain* HeadlessDevToolsClientImpl::GetDOM() {
return &dom_domain_;
}
dom_storage::Domain* HeadlessDevToolsClientImpl::GetDOMStorage() {
return &dom_storage_domain_;
}
emulation::Domain* HeadlessDevToolsClientImpl::GetEmulation() {
return &emulation_domain_;
}
heap_profiler::Domain* HeadlessDevToolsClientImpl::GetHeapProfiler() {
return &heap_profiler_domain_;
}
indexeddb::Domain* HeadlessDevToolsClientImpl::GetIndexedDB() {
return &indexeddb_domain_;
}
input::Domain* HeadlessDevToolsClientImpl::GetInput() {
return &input_domain_;
}
inspector::Domain* HeadlessDevToolsClientImpl::GetInspector() {
return &inspector_domain_;
}
io::Domain* HeadlessDevToolsClientImpl::GetIO() {
return &io_domain_;
}
layer_tree::Domain* HeadlessDevToolsClientImpl::GetLayerTree() {
return &layer_tree_domain_;
}
memory::Domain* HeadlessDevToolsClientImpl::GetMemory() {
return &memory_domain_;
}
network::Domain* HeadlessDevToolsClientImpl::GetNetwork() {
return &network_domain_;
}
page::Domain* HeadlessDevToolsClientImpl::GetPage() {
return &page_domain_;
}
profiler::Domain* HeadlessDevToolsClientImpl::GetProfiler() {
return &profiler_domain_;
}
rendering::Domain* HeadlessDevToolsClientImpl::GetRendering() {
return &rendering_domain_;
}
runtime::Domain* HeadlessDevToolsClientImpl::GetRuntime() {
return &runtime_domain_;
}
security::Domain* HeadlessDevToolsClientImpl::GetSecurity() {
return &security_domain_;
}
service_worker::Domain* HeadlessDevToolsClientImpl::GetServiceWorker() {
return &service_worker_domain_;
}
tracing::Domain* HeadlessDevToolsClientImpl::GetTracing() {
return &tracing_domain_;
}
worker::Domain* HeadlessDevToolsClientImpl::GetWorker() {
return &worker_domain_;
}
template <typename CallbackType>
void HeadlessDevToolsClientImpl::FinalizeAndSendMessage(
base::DictionaryValue* message,
CallbackType callback) {
DCHECK(agent_host_);
int id = next_message_id_++;
message->SetInteger("id", id);
std::string json_message;
base::JSONWriter::Write(*message, &json_message);
pending_messages_[id] = Callback(callback);
agent_host_->DispatchProtocolMessage(this, json_message);
}
template <typename CallbackType>
void HeadlessDevToolsClientImpl::SendMessageWithParams(
const char* method,
std::unique_ptr<base::Value> params,
CallbackType callback) {
base::DictionaryValue message;
message.SetString("method", method);
message.Set("params", std::move(params));
FinalizeAndSendMessage(&message, std::move(callback));
}
template <typename CallbackType>
void HeadlessDevToolsClientImpl::SendMessageWithoutParams(
const char* method,
CallbackType callback) {
base::DictionaryValue message;
message.SetString("method", method);
FinalizeAndSendMessage(&message, std::move(callback));
}
void HeadlessDevToolsClientImpl::SendMessage(
const char* method,
std::unique_ptr<base::Value> params,
base::Callback<void(const base::Value&)> callback) {
SendMessageWithParams(method, std::move(params), std::move(callback));
}
void HeadlessDevToolsClientImpl::SendMessage(
const char* method,
std::unique_ptr<base::Value> params,
base::Callback<void()> callback) {
SendMessageWithParams(method, std::move(params), std::move(callback));
}
void HeadlessDevToolsClientImpl::SendMessage(
const char* method,
base::Callback<void(const base::Value&)> callback) {
SendMessageWithoutParams(method, std::move(callback));
}
void HeadlessDevToolsClientImpl::SendMessage(const char* method,
base::Callback<void()> callback) {
SendMessageWithoutParams(method, std::move(callback));
}
void HeadlessDevToolsClientImpl::RegisterEventHandler(
const char* method,
base::Callback<void(const base::Value&)> callback) {
DCHECK(event_handlers_.find(method) == event_handlers_.end());
event_handlers_[method] = callback;
}
HeadlessDevToolsClientImpl::Callback::Callback() {}
HeadlessDevToolsClientImpl::Callback::Callback(Callback&& other) = default;
HeadlessDevToolsClientImpl::Callback::Callback(base::Callback<void()> callback)
: callback(callback) {}
HeadlessDevToolsClientImpl::Callback::Callback(
base::Callback<void(const base::Value&)> callback)
: callback_with_result(callback) {}
HeadlessDevToolsClientImpl::Callback::~Callback() {}
HeadlessDevToolsClientImpl::Callback& HeadlessDevToolsClientImpl::Callback::
operator=(Callback&& other) = default;
} // namespace headless