1 // Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
2 // reserved. Use of this source code is governed by a BSD-style license that
3 // can be found in the LICENSE file.
4
5 #include "libcef/browser/devtools/devtools_controller.h"
6
7 #include "libcef/browser/devtools/devtools_util.h"
8 #include "libcef/browser/thread_util.h"
9
10 #include "base/json/json_reader.h"
11 #include "base/json/json_writer.h"
12 #include "content/public/browser/devtools_agent_host.h"
13
CefDevToolsController(content::WebContents * inspected_contents)14 CefDevToolsController::CefDevToolsController(
15 content::WebContents* inspected_contents)
16 : inspected_contents_(inspected_contents), weak_ptr_factory_(this) {
17 DCHECK(inspected_contents_);
18 }
19
~CefDevToolsController()20 CefDevToolsController::~CefDevToolsController() {
21 if (agent_host_) {
22 agent_host_->DetachClient(this);
23 AgentHostClosed(agent_host_.get());
24 }
25
26 for (auto& observer : observers_) {
27 observer.OnDevToolsControllerDestroyed();
28 }
29 }
30
SendDevToolsMessage(const base::StringPiece & message)31 bool CefDevToolsController::SendDevToolsMessage(
32 const base::StringPiece& message) {
33 CEF_REQUIRE_UIT();
34 if (!EnsureAgentHost())
35 return false;
36
37 agent_host_->DispatchProtocolMessage(
38 this, base::as_bytes(base::make_span(message)));
39 return true;
40 }
41
ExecuteDevToolsMethod(int suggested_message_id,const std::string & method,const base::DictionaryValue * params)42 int CefDevToolsController::ExecuteDevToolsMethod(
43 int suggested_message_id,
44 const std::string& method,
45 const base::DictionaryValue* params) {
46 CEF_REQUIRE_UIT();
47 if (!EnsureAgentHost())
48 return 0;
49
50 // Message IDs must always be increasing and unique.
51 int message_id = suggested_message_id;
52 if (message_id < next_message_id_)
53 message_id = next_message_id_++;
54 else
55 next_message_id_ = message_id + 1;
56
57 base::DictionaryValue message;
58 message.SetIntKey("id", message_id);
59 message.SetStringKey("method", method);
60 if (params)
61 message.SetKey("params", params->Clone());
62
63 std::string protocol_message;
64 if (!base::JSONWriter::Write(message, &protocol_message))
65 return 0;
66
67 agent_host_->DispatchProtocolMessage(
68 this, base::as_bytes(base::make_span(protocol_message)));
69 return message_id;
70 }
71
AgentHostClosed(content::DevToolsAgentHost * agent_host)72 void CefDevToolsController::AgentHostClosed(
73 content::DevToolsAgentHost* agent_host) {
74 DCHECK(agent_host == agent_host_.get());
75 agent_host_ = nullptr;
76 for (auto& observer : observers_) {
77 observer.OnDevToolsAgentDetached();
78 }
79 }
80
AddObserver(Observer * observer)81 void CefDevToolsController::AddObserver(Observer* observer) {
82 CEF_REQUIRE_UIT();
83 observers_.AddObserver(observer);
84 }
85
RemoveObserver(Observer * observer)86 void CefDevToolsController::RemoveObserver(Observer* observer) {
87 CEF_REQUIRE_UIT();
88 observers_.RemoveObserver(observer);
89 }
90
DispatchProtocolMessage(content::DevToolsAgentHost * agent_host,base::span<const uint8_t> message)91 void CefDevToolsController::DispatchProtocolMessage(
92 content::DevToolsAgentHost* agent_host,
93 base::span<const uint8_t> message) {
94 if (observers_.empty())
95 return;
96
97 base::StringPiece str_message(reinterpret_cast<const char*>(message.data()),
98 message.size());
99 if (!devtools_util::ProtocolParser::IsValidMessage(str_message)) {
100 LOG(WARNING) << "Invalid message: " << str_message.substr(0, 100);
101 return;
102 }
103
104 devtools_util::ProtocolParser parser;
105
106 for (auto& observer : observers_) {
107 if (observer.OnDevToolsMessage(str_message)) {
108 continue;
109 }
110
111 // Only perform parsing a single time.
112 if (parser.Initialize(str_message) && parser.IsFailure()) {
113 LOG(WARNING) << "Failed to parse message: " << str_message.substr(0, 100);
114 }
115
116 if (parser.IsEvent()) {
117 observer.OnDevToolsEvent(parser.method_, parser.params_);
118 } else if (parser.IsResult()) {
119 observer.OnDevToolsMethodResult(parser.message_id_, parser.success_,
120 parser.params_);
121 }
122 }
123 }
124
EnsureAgentHost()125 bool CefDevToolsController::EnsureAgentHost() {
126 if (!agent_host_) {
127 agent_host_ =
128 content::DevToolsAgentHost::GetOrCreateFor(inspected_contents_);
129 if (agent_host_) {
130 agent_host_->AttachClient(this);
131 for (auto& observer : observers_) {
132 observer.OnDevToolsAgentAttached();
133 }
134 }
135 }
136 return !!agent_host_;
137 }
138