1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/browser/devtools/devtools_agent_host_impl.h"
6
7 #include <map>
8 #include <vector>
9
10 #include "base/bind.h"
11 #include "base/lazy_instance.h"
12 #include "base/memory/ref_counted_memory.h"
13 #include "base/observer_list.h"
14 #include "base/stl_util.h"
15 #include "content/browser/devtools/devtools_manager.h"
16 #include "content/browser/devtools/devtools_stream_file.h"
17 #include "content/browser/devtools/forwarding_agent_host.h"
18 #include "content/browser/devtools/protocol/page.h"
19 #include "content/browser/devtools/protocol/security_handler.h"
20 #include "content/browser/devtools/render_frame_devtools_agent_host.h"
21 #include "content/browser/devtools/service_worker_devtools_agent_host.h"
22 #include "content/browser/devtools/service_worker_devtools_manager.h"
23 #include "content/browser/devtools/shared_worker_devtools_agent_host.h"
24 #include "content/browser/devtools/shared_worker_devtools_manager.h"
25 #include "content/browser/renderer_host/frame_tree_node.h"
26 #include "content/browser/renderer_host/render_view_host_impl.h"
27 #include "content/public/browser/browser_thread.h"
28 #include "content/public/browser/content_browser_client.h"
29 #include "content/public/browser/devtools_external_agent_proxy_delegate.h"
30
31 namespace content {
32
33 namespace {
34 typedef std::map<std::string, DevToolsAgentHostImpl*> DevToolsMap;
35 base::LazyInstance<DevToolsMap>::Leaky g_devtools_instances =
36 LAZY_INSTANCE_INITIALIZER;
37
38 base::LazyInstance<base::ObserverList<DevToolsAgentHostObserver>::Unchecked>::
39 Leaky g_devtools_observers = LAZY_INSTANCE_INITIALIZER;
40 } // namespace
41
42 const char DevToolsAgentHost::kTypePage[] = "page";
43 const char DevToolsAgentHost::kTypeFrame[] = "iframe";
44 const char DevToolsAgentHost::kTypeDedicatedWorker[] = "worker";
45 const char DevToolsAgentHost::kTypeSharedWorker[] = "shared_worker";
46 const char DevToolsAgentHost::kTypeServiceWorker[] = "service_worker";
47 const char DevToolsAgentHost::kTypeBrowser[] = "browser";
48 const char DevToolsAgentHost::kTypeGuest[] = "webview";
49 const char DevToolsAgentHost::kTypeOther[] = "other";
50 int DevToolsAgentHostImpl::s_force_creation_count_ = 0;
51
52 // static
GetProtocolVersion()53 std::string DevToolsAgentHost::GetProtocolVersion() {
54 // TODO(dgozman): generate this.
55 return "1.3";
56 }
57
58 // static
IsSupportedProtocolVersion(const std::string & version)59 bool DevToolsAgentHost::IsSupportedProtocolVersion(const std::string& version) {
60 // TODO(dgozman): generate this.
61 return version == "1.0" || version == "1.1" || version == "1.2" ||
62 version == "1.3";
63 }
64
65 // static
GetOrCreateAll()66 DevToolsAgentHost::List DevToolsAgentHost::GetOrCreateAll() {
67 List result;
68 SharedWorkerDevToolsAgentHost::List shared_list;
69 SharedWorkerDevToolsManager::GetInstance()->AddAllAgentHosts(&shared_list);
70 for (const auto& host : shared_list)
71 result.push_back(host);
72
73 ServiceWorkerDevToolsAgentHost::List service_list;
74 ServiceWorkerDevToolsManager::GetInstance()->AddAllAgentHosts(&service_list);
75 for (const auto& host : service_list)
76 result.push_back(host);
77
78 // TODO(dgozman): we should add dedicated workers here, but clients are not
79 // ready.
80 RenderFrameDevToolsAgentHost::AddAllAgentHosts(&result);
81
82 #if DCHECK_IS_ON()
83 for (auto it : result) {
84 DevToolsAgentHostImpl* host = static_cast<DevToolsAgentHostImpl*>(it.get());
85 DCHECK(g_devtools_instances.Get().find(host->id_) !=
86 g_devtools_instances.Get().end());
87 }
88 #endif
89
90 return result;
91 }
92
DevToolsAgentHostImpl(const std::string & id)93 DevToolsAgentHostImpl::DevToolsAgentHostImpl(const std::string& id)
94 : id_(id), renderer_channel_(this) {
95 DCHECK_CURRENTLY_ON(BrowserThread::UI);
96 }
97
~DevToolsAgentHostImpl()98 DevToolsAgentHostImpl::~DevToolsAgentHostImpl() {
99 DCHECK_CURRENTLY_ON(BrowserThread::UI);
100 NotifyDestroyed();
101 }
102
103 // static
GetForId(const std::string & id)104 scoped_refptr<DevToolsAgentHostImpl> DevToolsAgentHostImpl::GetForId(
105 const std::string& id) {
106 if (!g_devtools_instances.IsCreated())
107 return nullptr;
108 auto it = g_devtools_instances.Get().find(id);
109 if (it == g_devtools_instances.Get().end())
110 return nullptr;
111 return it->second;
112 }
113
114 // static
GetForId(const std::string & id)115 scoped_refptr<DevToolsAgentHost> DevToolsAgentHost::GetForId(
116 const std::string& id) {
117 return DevToolsAgentHostImpl::GetForId(id);
118 }
119
120 // static
Forward(const std::string & id,std::unique_ptr<DevToolsExternalAgentProxyDelegate> delegate)121 scoped_refptr<DevToolsAgentHost> DevToolsAgentHost::Forward(
122 const std::string& id,
123 std::unique_ptr<DevToolsExternalAgentProxyDelegate> delegate) {
124 scoped_refptr<DevToolsAgentHost> result = DevToolsAgentHost::GetForId(id);
125 if (result)
126 return result;
127 return new ForwardingAgentHost(id, std::move(delegate));
128 }
129
SessionByClient(DevToolsAgentHostClient * client)130 DevToolsSession* DevToolsAgentHostImpl::SessionByClient(
131 DevToolsAgentHostClient* client) {
132 auto it = session_by_client_.find(client);
133 return it == session_by_client_.end() ? nullptr : it->second.get();
134 }
135
AttachInternal(std::unique_ptr<DevToolsSession> session_owned)136 bool DevToolsAgentHostImpl::AttachInternal(
137 std::unique_ptr<DevToolsSession> session_owned) {
138 return AttachInternal(std::move(session_owned), true);
139 }
140
AttachInternal(std::unique_ptr<DevToolsSession> session_owned,bool acquire_wake_lock)141 bool DevToolsAgentHostImpl::AttachInternal(
142 std::unique_ptr<DevToolsSession> session_owned,
143 bool acquire_wake_lock) {
144 scoped_refptr<DevToolsAgentHostImpl> protect(this);
145 DevToolsSession* session = session_owned.get();
146 session->SetAgentHost(this);
147 if (!AttachSession(session, acquire_wake_lock))
148 return false;
149 renderer_channel_.AttachSession(session);
150 sessions_.push_back(session);
151 DCHECK(session_by_client_.find(session->GetClient()) ==
152 session_by_client_.end());
153 session_by_client_.emplace(session->GetClient(), std::move(session_owned));
154 if (sessions_.size() == 1)
155 NotifyAttached();
156 DevToolsManager* manager = DevToolsManager::GetInstance();
157 if (manager->delegate())
158 manager->delegate()->ClientAttached(session);
159 return true;
160 }
161
AttachClient(DevToolsAgentHostClient * client)162 bool DevToolsAgentHostImpl::AttachClient(DevToolsAgentHostClient* client) {
163 if (SessionByClient(client))
164 return false;
165 return AttachInternal(
166 std::make_unique<DevToolsSession>(client, /*session_id=*/""),
167 /*acquire_wake_lock=*/true);
168 }
169
AttachClientWithoutWakeLock(content::DevToolsAgentHostClient * client)170 bool DevToolsAgentHostImpl::AttachClientWithoutWakeLock(
171 content::DevToolsAgentHostClient* client) {
172 if (SessionByClient(client))
173 return false;
174 return AttachInternal(
175 std::make_unique<DevToolsSession>(client, /*session_id=*/""),
176 /*acquire_wake_lock=*/false);
177 }
178
DetachClient(DevToolsAgentHostClient * client)179 bool DevToolsAgentHostImpl::DetachClient(DevToolsAgentHostClient* client) {
180 DevToolsSession* session = SessionByClient(client);
181 if (!session)
182 return false;
183 scoped_refptr<DevToolsAgentHostImpl> protect(this);
184 DetachInternal(session);
185 return true;
186 }
187
DispatchProtocolMessage(DevToolsAgentHostClient * client,base::span<const uint8_t> message)188 void DevToolsAgentHostImpl::DispatchProtocolMessage(
189 DevToolsAgentHostClient* client,
190 base::span<const uint8_t> message) {
191 DevToolsSession* session = SessionByClient(client);
192 if (session)
193 session->DispatchProtocolMessage(message);
194 }
195
DetachInternal(DevToolsSession * session)196 void DevToolsAgentHostImpl::DetachInternal(DevToolsSession* session) {
197 std::unique_ptr<DevToolsSession> session_owned =
198 std::move(session_by_client_[session->GetClient()]);
199 DCHECK_EQ(session, session_owned.get());
200 // Make sure we dispose session prior to reporting it to the host.
201 session->Dispose();
202 base::Erase(sessions_, session);
203 session_by_client_.erase(session->GetClient());
204 DetachSession(session);
205 DevToolsManager* manager = DevToolsManager::GetInstance();
206 if (manager->delegate())
207 manager->delegate()->ClientDetached(session);
208 if (sessions_.empty()) {
209 io_context_.DiscardAllStreams();
210 NotifyDetached();
211 }
212 }
213
IsAttached()214 bool DevToolsAgentHostImpl::IsAttached() {
215 return !sessions_.empty();
216 }
217
InspectElement(RenderFrameHost * frame_host,int x,int y)218 void DevToolsAgentHostImpl::InspectElement(RenderFrameHost* frame_host,
219 int x,
220 int y) {}
221
GetId()222 std::string DevToolsAgentHostImpl::GetId() {
223 return id_;
224 }
225
CreateIOStreamFromData(scoped_refptr<base::RefCountedMemory> data)226 std::string DevToolsAgentHostImpl::CreateIOStreamFromData(
227 scoped_refptr<base::RefCountedMemory> data) {
228 scoped_refptr<DevToolsStreamFile> stream =
229 DevToolsStreamFile::Create(GetIOContext(), true /* binary */);
230 std::string text(reinterpret_cast<const char*>(data->front()), data->size());
231 stream->Append(std::make_unique<std::string>(text));
232 return stream->handle();
233 }
234
GetParentId()235 std::string DevToolsAgentHostImpl::GetParentId() {
236 return std::string();
237 }
238
GetOpenerId()239 std::string DevToolsAgentHostImpl::GetOpenerId() {
240 return std::string();
241 }
242
GetOpenerFrameId()243 std::string DevToolsAgentHostImpl::GetOpenerFrameId() {
244 return std::string();
245 }
246
CanAccessOpener()247 bool DevToolsAgentHostImpl::CanAccessOpener() {
248 return false;
249 }
250
GetDescription()251 std::string DevToolsAgentHostImpl::GetDescription() {
252 return std::string();
253 }
254
GetFaviconURL()255 GURL DevToolsAgentHostImpl::GetFaviconURL() {
256 return GURL();
257 }
258
GetFrontendURL()259 std::string DevToolsAgentHostImpl::GetFrontendURL() {
260 return std::string();
261 }
262
GetLastActivityTime()263 base::TimeTicks DevToolsAgentHostImpl::GetLastActivityTime() {
264 return base::TimeTicks();
265 }
266
GetBrowserContext()267 BrowserContext* DevToolsAgentHostImpl::GetBrowserContext() {
268 return nullptr;
269 }
270
GetWebContents()271 WebContents* DevToolsAgentHostImpl::GetWebContents() {
272 return nullptr;
273 }
274
DisconnectWebContents()275 void DevToolsAgentHostImpl::DisconnectWebContents() {
276 }
277
ConnectWebContents(WebContents * wc)278 void DevToolsAgentHostImpl::ConnectWebContents(WebContents* wc) {
279 }
280
Inspect()281 bool DevToolsAgentHostImpl::Inspect() {
282 DevToolsManager* manager = DevToolsManager::GetInstance();
283 if (manager->delegate()) {
284 manager->delegate()->Inspect(this);
285 return true;
286 }
287 return false;
288 }
289
ForceDetachAllSessions()290 void DevToolsAgentHostImpl::ForceDetachAllSessions() {
291 scoped_refptr<DevToolsAgentHostImpl> protect(this);
292 while (!sessions_.empty()) {
293 DevToolsAgentHostClient* client = (*sessions_.begin())->GetClient();
294 DetachClient(client);
295 client->AgentHostClosed(this);
296 }
297 }
298
ForceDetachRestrictedSessions(const std::vector<DevToolsSession * > & restricted_sessions)299 void DevToolsAgentHostImpl::ForceDetachRestrictedSessions(
300 const std::vector<DevToolsSession*>& restricted_sessions) {
301 scoped_refptr<DevToolsAgentHostImpl> protect(this);
302
303 for (DevToolsSession* session : restricted_sessions) {
304 DevToolsAgentHostClient* client = session->GetClient();
305 DetachClient(client);
306 client->AgentHostClosed(this);
307 }
308 }
309
AttachSession(DevToolsSession * session,bool acquire_wake_lock)310 bool DevToolsAgentHostImpl::AttachSession(DevToolsSession* session,
311 bool acquire_wake_lock) {
312 return false;
313 }
314
DetachSession(DevToolsSession * session)315 void DevToolsAgentHostImpl::DetachSession(DevToolsSession* session) {}
316
UpdateRendererChannel(bool force)317 void DevToolsAgentHostImpl::UpdateRendererChannel(bool force) {}
318
319 // static
DetachAllClients()320 void DevToolsAgentHost::DetachAllClients() {
321 if (!g_devtools_instances.IsCreated())
322 return;
323
324 // Make a copy, since detaching may lead to agent destruction, which
325 // removes it from the instances.
326 std::vector<scoped_refptr<DevToolsAgentHostImpl>> copy;
327 for (auto it(g_devtools_instances.Get().begin());
328 it != g_devtools_instances.Get().end(); ++it)
329 copy.push_back(it->second);
330 for (auto it(copy.begin()); it != copy.end(); ++it)
331 it->get()->ForceDetachAllSessions();
332 }
333
334 // static
AddObserver(DevToolsAgentHostObserver * observer)335 void DevToolsAgentHost::AddObserver(DevToolsAgentHostObserver* observer) {
336 if (observer->ShouldForceDevToolsAgentHostCreation()) {
337 if (!DevToolsAgentHostImpl::s_force_creation_count_) {
338 // Force all agent hosts when first observer is added.
339 DevToolsAgentHost::GetOrCreateAll();
340 }
341 DevToolsAgentHostImpl::s_force_creation_count_++;
342 }
343
344 g_devtools_observers.Get().AddObserver(observer);
345 for (const auto& id_host : g_devtools_instances.Get())
346 observer->DevToolsAgentHostCreated(id_host.second);
347 }
348
349 // static
RemoveObserver(DevToolsAgentHostObserver * observer)350 void DevToolsAgentHost::RemoveObserver(DevToolsAgentHostObserver* observer) {
351 if (observer->ShouldForceDevToolsAgentHostCreation())
352 DevToolsAgentHostImpl::s_force_creation_count_--;
353 g_devtools_observers.Get().RemoveObserver(observer);
354 }
355
356 // static
ShouldForceCreation()357 bool DevToolsAgentHostImpl::ShouldForceCreation() {
358 return !!s_force_creation_count_;
359 }
360
NotifyCreated()361 void DevToolsAgentHostImpl::NotifyCreated() {
362 DCHECK(g_devtools_instances.Get().find(id_) ==
363 g_devtools_instances.Get().end());
364 g_devtools_instances.Get()[id_] = this;
365 for (auto& observer : g_devtools_observers.Get())
366 observer.DevToolsAgentHostCreated(this);
367 }
368
NotifyNavigated()369 void DevToolsAgentHostImpl::NotifyNavigated() {
370 for (auto& observer : g_devtools_observers.Get())
371 observer.DevToolsAgentHostNavigated(this);
372 }
373
NotifyAttached()374 void DevToolsAgentHostImpl::NotifyAttached() {
375 for (auto& observer : g_devtools_observers.Get())
376 observer.DevToolsAgentHostAttached(this);
377 }
378
NotifyDetached()379 void DevToolsAgentHostImpl::NotifyDetached() {
380 for (auto& observer : g_devtools_observers.Get())
381 observer.DevToolsAgentHostDetached(this);
382 }
383
NotifyCrashed(base::TerminationStatus status)384 void DevToolsAgentHostImpl::NotifyCrashed(base::TerminationStatus status) {
385 for (auto& observer : g_devtools_observers.Get())
386 observer.DevToolsAgentHostCrashed(this, status);
387 }
388
NotifyDestroyed()389 void DevToolsAgentHostImpl::NotifyDestroyed() {
390 DCHECK(g_devtools_instances.Get().find(id_) !=
391 g_devtools_instances.Get().end());
392 for (auto& observer : g_devtools_observers.Get())
393 observer.DevToolsAgentHostDestroyed(this);
394 g_devtools_instances.Get().erase(id_);
395 }
396
397 DevToolsAgentHostImpl::NetworkLoaderFactoryParamsAndInfo::
398 NetworkLoaderFactoryParamsAndInfo() = default;
399 DevToolsAgentHostImpl::NetworkLoaderFactoryParamsAndInfo::
NetworkLoaderFactoryParamsAndInfo(url::Origin origin,net::SiteForCookies site_for_cookies,network::mojom::URLLoaderFactoryParamsPtr factory_params)400 NetworkLoaderFactoryParamsAndInfo(
401 url::Origin origin,
402 net::SiteForCookies site_for_cookies,
403 network::mojom::URLLoaderFactoryParamsPtr factory_params)
404 : origin(std::move(origin)),
405 site_for_cookies(std::move(site_for_cookies)),
406 factory_params(std::move(factory_params)) {}
407 DevToolsAgentHostImpl::NetworkLoaderFactoryParamsAndInfo::
408 NetworkLoaderFactoryParamsAndInfo(
409 DevToolsAgentHostImpl::NetworkLoaderFactoryParamsAndInfo&&) = default;
410 DevToolsAgentHostImpl::NetworkLoaderFactoryParamsAndInfo::
411 ~NetworkLoaderFactoryParamsAndInfo() = default;
412
413 DevToolsAgentHostImpl::NetworkLoaderFactoryParamsAndInfo
CreateNetworkFactoryParamsForDevTools()414 DevToolsAgentHostImpl::CreateNetworkFactoryParamsForDevTools() {
415 return {};
416 }
417
GetProcessHost()418 RenderProcessHost* DevToolsAgentHostImpl::GetProcessHost() {
419 return nullptr;
420 }
421
422 base::Optional<network::CrossOriginEmbedderPolicy>
cross_origin_embedder_policy(const std::string & id)423 DevToolsAgentHostImpl::cross_origin_embedder_policy(const std::string& id) {
424 return base::nullopt;
425 }
426
427 base::Optional<network::CrossOriginOpenerPolicy>
cross_origin_opener_policy(const std::string & id)428 DevToolsAgentHostImpl::cross_origin_opener_policy(const std::string& id) {
429 return base::nullopt;
430 }
431
432 } // namespace content
433