• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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