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