From ec5a24bd9aff8645121d06c72ca93576e4a30128 Mon Sep 17 00:00:00 2001 From: dgozman Date: Tue, 30 Jun 2015 06:37:10 -0700 Subject: [PATCH] [DevTools] Capabilities on browser side. BUG=504412 Review URL: https://codereview.chromium.org/1208993002 Cr-Commit-Position: refs/heads/master@{#336772} --- .../devtools/devtools_protocol_handler.cc | 13 ++-- .../devtools/devtools_protocol_handler.h | 7 +- .../devtools_protocol_handler_generator.py | 51 +++++++++++++ .../devtools/protocol/emulation_handler.cc | 16 ++-- .../devtools/protocol/emulation_handler.h | 1 + .../browser/devtools/protocol/page_handler.cc | 13 +++- .../browser/devtools/protocol/page_handler.h | 1 + .../render_frame_devtools_agent_host.cc | 73 +++++++++++++++++-- .../render_frame_devtools_agent_host.h | 5 ++ .../devtools/worker_devtools_agent_host.cc | 3 +- 10 files changed, 158 insertions(+), 25 deletions(-) diff --git a/content/browser/devtools/devtools_protocol_handler.cc b/content/browser/devtools/devtools_protocol_handler.cc index 86ccd8e5d2d058..811bc4a61e683a 100644 --- a/content/browser/devtools/devtools_protocol_handler.cc +++ b/content/browser/devtools/devtools_protocol_handler.cc @@ -55,13 +55,13 @@ void DevToolsProtocolHandler::HandleMessage(const std::string& message) { } bool DevToolsProtocolHandler::HandleOptionalMessage( - const std::string& message, int* call_id) { + const std::string& message, int* call_id, std::string* method) { scoped_ptr command = ParseCommand(message); if (!command) return true; if (PassCommandToDelegate(command.get())) return true; - return HandleOptionalCommand(command.Pass(), call_id); + return HandleOptionalCommand(command.Pass(), call_id, method); } bool DevToolsProtocolHandler::PassCommandToDelegate( @@ -134,13 +134,14 @@ void DevToolsProtocolHandler::HandleCommand( } bool DevToolsProtocolHandler::HandleOptionalCommand( - scoped_ptr command, int* call_id) { + scoped_ptr command, + int* call_id, + std::string* method) { *call_id = DevToolsProtocolClient::kNoId; - std::string method; command->GetInteger(kIdParam, call_id); - command->GetString(kMethodParam, &method); + command->GetString(kMethodParam, method); DevToolsProtocolDispatcher::CommandHandler command_handler( - dispatcher_.FindCommandHandler(method)); + dispatcher_.FindCommandHandler(*method)); if (!command_handler.is_null()) { return command_handler.Run( *call_id, TakeDictionary(command.get(), kParamsParam)); diff --git a/content/browser/devtools/devtools_protocol_handler.h b/content/browser/devtools/devtools_protocol_handler.h index 299b3423088457..7c4ac12ab7790b 100644 --- a/content/browser/devtools/devtools_protocol_handler.h +++ b/content/browser/devtools/devtools_protocol_handler.h @@ -21,7 +21,9 @@ class DevToolsProtocolHandler { virtual ~DevToolsProtocolHandler(); void HandleMessage(const std::string& message); - bool HandleOptionalMessage(const std::string& message, int* call_id); + bool HandleOptionalMessage(const std::string& message, + int* call_id, + std::string* method); DevToolsProtocolDispatcher* dispatcher() { return &dispatcher_; } @@ -30,7 +32,8 @@ class DevToolsProtocolHandler { bool PassCommandToDelegate(base::DictionaryValue* command); void HandleCommand(scoped_ptr command); bool HandleOptionalCommand(scoped_ptr command, - int* call_id); + int* call_id, + std::string* method); DevToolsAgentHost* agent_host_; DevToolsProtocolClient client_; diff --git a/content/browser/devtools/protocol/devtools_protocol_handler_generator.py b/content/browser/devtools/protocol/devtools_protocol_handler_generator.py index 0ba741503f998f..21dfc8bc8b5438 100755 --- a/content/browser/devtools/protocol/devtools_protocol_handler_generator.py +++ b/content/browser/devtools/protocol/devtools_protocol_handler_generator.py @@ -68,6 +68,8 @@ class DevToolsProtocolDispatcher; template<> base::Value* CreateValue(const std::string& param); +${capabilities}\ + ${types}\ } // namespace devtools @@ -102,6 +104,30 @@ class DevToolsProtocolDispatcher { #endif // CONTENT_BROWSER_DEVTOOLS_PROTOCOL_DEVTOOLS_PROTOCOL_DISPATCHER_H_ """) +tmpl_domain_capability = string.Template("""\ + static const char ${capability}[]; +""") + +tmpl_domain_capabilities = string.Template("""\ +namespace ${domain} { +struct Capabilities { + static const char ${Domain}[]; +${capabilities}\ +}; +} // namespace ${domain} +""") + +tmpl_domain_capability_impl = string.Template("""\ + const char Capabilities::${capability}[] = "${Domain}.${capability_lower}"; +""") + +tmpl_domain_capabilities_impl = string.Template("""\ +namespace ${domain} { + const char Capabilities::${Domain}[] = "${Domain}"; +${capabilities}\ +} // namespace ${domain} +""") + tmpl_typedef = string.Template("""\ namespace ${domain} { typedef ${param_type} ${declared_name}; @@ -284,6 +310,8 @@ class Client : public DevToolsProtocolClient { return new base::StringValue(param); } +${capabilities}\ + ${types}\ } // namespace devtools @@ -629,6 +657,8 @@ def ResolveType(json, mapping): raise Exception("Unknown type at %s.%s %s" % (mapping["Domain"], mapping["command"], mapping["proto_param"])) +capabilities = [] +capabilities_impl = [] setters = [] fields = [] @@ -646,6 +676,25 @@ def ResolveType(json, mapping): domain_empty = True domain_needs_client = False + domain_capabilities = [] + domain_capabilities_impl = [] + if "capabilities" in json_domain: + for json_capability in json_domain["capabilities"]: + domain_capabilities.append(tmpl_domain_capability.substitute( + domain_map, + capability=Capitalize(json_capability["name"]))) + domain_capabilities_impl.append(tmpl_domain_capability_impl.substitute( + domain_map, + capability=Capitalize(json_capability["name"]), + capability_lower=json_capability["name"])) + + capabilities.append(tmpl_domain_capabilities.substitute( + domain_map, + capabilities="\n".join(domain_capabilities))) + capabilities_impl.append(tmpl_domain_capabilities_impl.substitute( + domain_map, + capabilities="\n".join(domain_capabilities_impl))) + if "commands" in json_domain: for json_command in json_domain["commands"]: if (not ("handlers" in json_command) or @@ -784,6 +833,7 @@ def ResolveType(json, mapping): output_cc_file = open(output_cc_path, "w") output_h_file.write(template_h.substitute({}, + capabilities = "\n".join(capabilities), types = "\n".join(type_decls), setters = "".join(setters), methods = "".join(handler_methods), @@ -796,5 +846,6 @@ def ResolveType(json, mapping): includes = "".join(sorted(includes)), fields_init = ",\n ".join(fields_init), methods = "\n".join(handler_method_impls), + capabilities = "\n".join(capabilities_impl), types = "\n".join(type_impls))) output_cc_file.close() diff --git a/content/browser/devtools/protocol/emulation_handler.cc b/content/browser/devtools/protocol/emulation_handler.cc index 2a217fc46dfb89..831354a22001a4 100644 --- a/content/browser/devtools/protocol/emulation_handler.cc +++ b/content/browser/devtools/protocol/emulation_handler.cc @@ -112,16 +112,22 @@ Response EmulationHandler::SetTouchEmulationEnabled( } Response EmulationHandler::CanEmulate(bool* result) { + *result = IsDeviceEmulationAvailable(); + return Response::OK(); +} + +bool EmulationHandler::IsDeviceEmulationAvailable() { + bool result; #if defined(OS_ANDROID) - *result = false; + result = false; #else - *result = true; + result = true; if (WebContentsImpl* web_contents = GetWebContents()) - *result &= !web_contents->GetVisibleURL().SchemeIs(kChromeDevToolsScheme); + result &= !web_contents->GetVisibleURL().SchemeIs(kChromeDevToolsScheme); if (host_ && host_->GetRenderWidgetHost()) - *result &= !host_->GetRenderWidgetHost()->auto_resize_enabled(); + result &= !host_->GetRenderWidgetHost()->auto_resize_enabled(); #endif // defined(OS_ANDROID) - return Response::OK(); + return result; } Response EmulationHandler::SetDeviceMetricsOverride( diff --git a/content/browser/devtools/protocol/emulation_handler.h b/content/browser/devtools/protocol/emulation_handler.h index c2a4bd38fbd80e..51dad8c138f66a 100644 --- a/content/browser/devtools/protocol/emulation_handler.h +++ b/content/browser/devtools/protocol/emulation_handler.h @@ -32,6 +32,7 @@ class EmulationHandler : public page::PageHandler::ScreencastListener { void SetRenderFrameHost(RenderFrameHostImpl* host); void Detached(); + bool IsDeviceEmulationAvailable(); Response SetGeolocationOverride(double* latitude, double* longitude, diff --git a/content/browser/devtools/protocol/page_handler.cc b/content/browser/devtools/protocol/page_handler.cc index 63550c016f0bd2..b4004a14318d40 100644 --- a/content/browser/devtools/protocol/page_handler.cc +++ b/content/browser/devtools/protocol/page_handler.cc @@ -271,12 +271,19 @@ Response PageHandler::CaptureScreenshot(DevToolsCommandId command_id) { } Response PageHandler::CanScreencast(bool* result) { + *result = IsScreencastAvailable(); + return Response::OK(); +} + +bool PageHandler::IsScreencastAvailable() +{ + bool result; #if defined(OS_ANDROID) - *result = true; + result = true; #else - *result = false; + result = false; #endif // defined(OS_ANDROID) - return Response::OK(); + return result; } Response PageHandler::StartScreencast(const std::string* format, diff --git a/content/browser/devtools/protocol/page_handler.h b/content/browser/devtools/protocol/page_handler.h index e26f7b21883dc6..770e8cc613c565 100644 --- a/content/browser/devtools/protocol/page_handler.h +++ b/content/browser/devtools/protocol/page_handler.h @@ -48,6 +48,7 @@ class PageHandler : public NotificationObserver { void DidDetachInterstitialPage(); void SetScreencastListener(ScreencastListener* listener); bool screencast_enabled() const { return enabled_ && screencast_enabled_; } + bool IsScreencastAvailable(); Response Enable(); Response Disable(); diff --git a/content/browser/devtools/render_frame_devtools_agent_host.cc b/content/browser/devtools/render_frame_devtools_agent_host.cc index 988114a82bb906..50089aaefa1201 100644 --- a/content/browser/devtools/render_frame_devtools_agent_host.cc +++ b/content/browser/devtools/render_frame_devtools_agent_host.cc @@ -5,6 +5,8 @@ #include "content/browser/devtools/render_frame_devtools_agent_host.h" #include "base/basictypes.h" +#include "base/json/json_reader.h" +#include "base/json/json_writer.h" #include "base/lazy_instance.h" #include "base/strings/utf_string_conversions.h" #include "content/browser/child_process_security_policy_impl.h" @@ -41,6 +43,10 @@ namespace content { typedef std::vector Instances; namespace { + +const char kGetCapabilitiesMethod[] = "Inspector.getCapabilities"; +const char kCapabilitiesResultPath[] = "result.capabilities"; + base::LazyInstance::Leaky g_instances = LAZY_INSTANCE_INITIALIZER; static RenderFrameDevToolsAgentHost* FindAgentHost(RenderFrameHost* host) { @@ -102,7 +108,7 @@ class RenderFrameDevToolsAgentHost::FrameHostHolder { bool attached_; bool suspended_; DevToolsMessageChunkProcessor chunk_processor_; - std::vector pending_messages_; + std::vector> pending_messages_; std::map sent_messages_; }; @@ -199,11 +205,12 @@ RenderFrameDevToolsAgentHost::FrameHostHolder::ProcessChunkedMessageFromAgent( void RenderFrameDevToolsAgentHost::FrameHostHolder::SendMessageToClient( const std::string& message) { - sent_messages_.erase(chunk_processor_.last_call_id()); + int call_id = chunk_processor_.last_call_id(); + sent_messages_.erase(call_id); if (suspended_) - pending_messages_.push_back(message); + pending_messages_.push_back(std::make_pair(message, call_id)); else - agent_->SendMessageToClient(message); + agent_->OnMessageFromBackend(message, call_id); } void RenderFrameDevToolsAgentHost::FrameHostHolder::Suspend() { @@ -212,9 +219,9 @@ void RenderFrameDevToolsAgentHost::FrameHostHolder::Suspend() { void RenderFrameDevToolsAgentHost::FrameHostHolder::Resume() { suspended_ = false; - for (const std::string& message : pending_messages_) - agent_->SendMessageToClient(message); - std::vector empty; + for (const auto& pair : pending_messages_) + agent_->OnMessageFromBackend(pair.first, pair.second); + std::vector> empty; pending_messages_.swap(empty); } @@ -310,12 +317,20 @@ RenderFrameDevToolsAgentHost::RenderFrameDevToolsAgentHost( current_frame_crashed_(false) { DevToolsProtocolDispatcher* dispatcher = protocol_handler_->dispatcher(); dispatcher->SetDOMHandler(dom_handler_.get()); + capabilities_.push_back(devtools::dom::Capabilities::DOM); dispatcher->SetInputHandler(input_handler_.get()); + capabilities_.push_back(devtools::input::Capabilities::Input); dispatcher->SetInspectorHandler(inspector_handler_.get()); + capabilities_.push_back(devtools::inspector::Capabilities::Inspector); dispatcher->SetNetworkHandler(network_handler_.get()); + capabilities_.push_back(devtools::network::Capabilities::Network); dispatcher->SetPowerHandler(power_handler_.get()); + capabilities_.push_back(devtools::power::Capabilities::Power); dispatcher->SetServiceWorkerHandler(service_worker_handler_.get()); + capabilities_.push_back( + devtools::service_worker::Capabilities::ServiceWorker); dispatcher->SetTracingHandler(tracing_handler_.get()); + capabilities_.push_back(devtools::tracing::Capabilities::Tracing); if (!host->GetParent()) { security_handler_.reset(new devtools::security::SecurityHandler()); @@ -323,14 +338,22 @@ RenderFrameDevToolsAgentHost::RenderFrameDevToolsAgentHost( emulation_handler_.reset( new devtools::emulation::EmulationHandler(page_handler_.get())); dispatcher->SetSecurityHandler(security_handler_.get()); + capabilities_.push_back(devtools::security::Capabilities::Security); dispatcher->SetPageHandler(page_handler_.get()); + capabilities_.push_back(devtools::page::Capabilities::Page); dispatcher->SetEmulationHandler(emulation_handler_.get()); + capabilities_.push_back(devtools::emulation::Capabilities::Emulation); } SetPending(host); CommitPending(); WebContentsObserver::Observe(WebContents::FromRenderFrameHost(host)); + if (emulation_handler_ && emulation_handler_->IsDeviceEmulationAvailable()) + capabilities_.push_back(devtools::emulation::Capabilities::DeviceMetrics); + if (page_handler_ && page_handler_->IsScreencastAvailable()) + capabilities_.push_back(devtools::page::Capabilities::Screencast); + g_instances.Get().push_back(this); AddRef(); // Balanced in RenderFrameHostDestroyed. } @@ -401,8 +424,11 @@ void RenderFrameDevToolsAgentHost::Detach() { bool RenderFrameDevToolsAgentHost::DispatchProtocolMessage( const std::string& message) { int call_id = 0; - if (protocol_handler_->HandleOptionalMessage(message, &call_id)) + std::string method; + if (protocol_handler_->HandleOptionalMessage(message, &call_id, &method)) return true; + if (method == kGetCapabilitiesMethod) + get_capabilities_call_ids_.insert(call_id); if (current_) current_->DispatchProtocolMessage(call_id, message); @@ -411,6 +437,37 @@ bool RenderFrameDevToolsAgentHost::DispatchProtocolMessage( return true; } +void RenderFrameDevToolsAgentHost::OnMessageFromBackend( + const std::string& message, int call_id) { + if (get_capabilities_call_ids_.find(call_id) != + get_capabilities_call_ids_.end()) { + get_capabilities_call_ids_.erase(call_id); + SendMessageToClient(AppendCapabilities(message)); + } else { + SendMessageToClient(message); + } +} + +std::string RenderFrameDevToolsAgentHost::AppendCapabilities( + const std::string& message) { + scoped_ptr value = base::JSONReader::Read(message); + if (!value || !value->IsType(base::Value::TYPE_DICTIONARY)) + return message; + scoped_ptr dict = + make_scoped_ptr(static_cast(value.release())); + base::ListValue* list; + if (!dict->GetList(kCapabilitiesResultPath, &list)) + return message; + + for (const std::string& capabilitiy : capabilities_) { + scoped_ptr value(new base::StringValue(capabilitiy)); + if (list->Find(*value) == list->end()) + list->Append(value.Pass()); + } + std::string result; + return base::JSONWriter::Write(*dict, &result) ? result : message; +} + void RenderFrameDevToolsAgentHost::InspectElement(int x, int y) { if (current_) current_->InspectElement(x, y); diff --git a/content/browser/devtools/render_frame_devtools_agent_host.h b/content/browser/devtools/render_frame_devtools_agent_host.h index 0d9c3ed903d9d8..ef1ed115bba8a6 100644 --- a/content/browser/devtools/render_frame_devtools_agent_host.h +++ b/content/browser/devtools/render_frame_devtools_agent_host.h @@ -122,6 +122,9 @@ class CONTENT_EXPORT RenderFrameDevToolsAgentHost void OnSwapCompositorFrame(const IPC::Message& message); void DestroyOnRenderFrameGone(); + void OnMessageFromBackend(const std::string& message, int call_id); + std::string AppendCapabilities(const std::string& message); + class FrameHostHolder; scoped_ptr current_; @@ -144,6 +147,8 @@ class CONTENT_EXPORT RenderFrameDevToolsAgentHost #endif scoped_ptr protocol_handler_; bool current_frame_crashed_; + std::set get_capabilities_call_ids_; + std::vector capabilities_; DISALLOW_COPY_AND_ASSIGN(RenderFrameDevToolsAgentHost); }; diff --git a/content/browser/devtools/worker_devtools_agent_host.cc b/content/browser/devtools/worker_devtools_agent_host.cc index 32cd668e3d68e3..63ffb2b9aee5dd 100644 --- a/content/browser/devtools/worker_devtools_agent_host.cc +++ b/content/browser/devtools/worker_devtools_agent_host.cc @@ -45,7 +45,8 @@ bool WorkerDevToolsAgentHost::DispatchProtocolMessage( return true; int call_id; - if (protocol_handler_->HandleOptionalMessage(message, &call_id)) + std::string method; + if (protocol_handler_->HandleOptionalMessage(message, &call_id, &method)) return true; if (RenderProcessHost* host = RenderProcessHost::FromID(worker_id_.first)) {