1 // Copyright 2013 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.
5 #include "chrome/browser/devtools/devtools_targets_ui.h"
7 #include "base/memory/weak_ptr.h"
8 #include "base/stl_util.h"
9 #include "base/strings/stringprintf.h"
10 #include "base/values.h"
11 #include "chrome/browser/devtools/devtools_adb_bridge.h"
12 #include "chrome/browser/devtools/devtools_target_impl.h"
13 #include "chrome/browser/devtools/port_forwarding_controller.h"
14 #include "content/public/browser/browser_child_process_observer.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "content/public/browser/child_process_data.h"
17 #include "content/public/browser/notification_observer.h"
18 #include "content/public/browser/notification_registrar.h"
19 #include "content/public/browser/notification_service.h"
20 #include "content/public/browser/notification_source.h"
21 #include "content/public/browser/notification_types.h"
22 #include "content/public/browser/render_process_host.h"
23 #include "content/public/browser/render_view_host.h"
24 #include "content/public/browser/web_contents.h"
25 #include "content/public/browser/worker_service.h"
26 #include "content/public/browser/worker_service_observer.h"
27 #include "content/public/common/process_type.h"
28 #include "net/base/escape.h"
30 using content::BrowserThread
;
31 using content::WebContents
;
35 const char kTargetSourceField
[] = "source";
36 const char kTargetSourceRenderer
[] = "renderers";
37 const char kTargetSourceWorker
[] = "workers";
38 const char kTargetSourceAdb
[] = "adb";
40 const char kTargetIdField
[] = "id";
41 const char kTargetTypeField
[] = "type";
42 const char kAttachedField
[] = "attached";
43 const char kUrlField
[] = "url";
44 const char kNameField
[] = "name";
45 const char kFaviconUrlField
[] = "faviconUrl";
46 const char kDescriptionField
[] = "description";
48 const char kGuestList
[] = "guests";
50 const char kAdbModelField
[] = "adbModel";
51 const char kAdbConnectedField
[] = "adbConnected";
52 const char kAdbSerialField
[] = "adbSerial";
53 const char kAdbPortStatus
[] = "adbPortStatus";
54 const char kAdbBrowsersList
[] = "browsers";
56 const char kAdbBrowserNameField
[] = "adbBrowserName";
57 const char kAdbBrowserVersionField
[] = "adbBrowserVersion";
58 const char kAdbBrowserChromeVersionField
[] = "adbBrowserChromeVersion";
59 const char kAdbPagesList
[] = "pages";
61 const char kAdbScreenWidthField
[] = "adbScreenWidth";
62 const char kAdbScreenHeightField
[] = "adbScreenHeight";
63 const char kAdbAttachedForeignField
[] = "adbAttachedForeign";
65 // CancelableTimer ------------------------------------------------------------
67 class CancelableTimer
{
69 CancelableTimer(base::Closure callback
, base::TimeDelta delay
)
70 : callback_(callback
),
72 base::MessageLoop::current()->PostDelayedTask(
74 base::Bind(&CancelableTimer::Fire
, weak_factory_
.GetWeakPtr()),
79 void Fire() { callback_
.Run(); }
81 base::Closure callback_
;
82 base::WeakPtrFactory
<CancelableTimer
> weak_factory_
;
85 // RenderViewHostTargetsUIHandler ---------------------------------------------
87 class RenderViewHostTargetsUIHandler
88 : public DevToolsTargetsUIHandler
,
89 public content::NotificationObserver
{
91 explicit RenderViewHostTargetsUIHandler(Callback callback
);
92 virtual ~RenderViewHostTargetsUIHandler();
94 // content::NotificationObserver overrides.
95 virtual void Observe(int type
,
96 const content::NotificationSource
& source
,
97 const content::NotificationDetails
& details
) OVERRIDE
;
101 content::NotificationRegistrar notification_registrar_
;
102 scoped_ptr
<CancelableTimer
> timer_
;
105 RenderViewHostTargetsUIHandler::RenderViewHostTargetsUIHandler(
107 : DevToolsTargetsUIHandler(kTargetSourceRenderer
, callback
) {
108 notification_registrar_
.Add(this,
109 content::NOTIFICATION_WEB_CONTENTS_CONNECTED
,
110 content::NotificationService::AllSources());
111 notification_registrar_
.Add(this,
112 content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED
,
113 content::NotificationService::AllSources());
114 notification_registrar_
.Add(this,
115 content::NOTIFICATION_WEB_CONTENTS_DESTROYED
,
116 content::NotificationService::AllSources());
120 RenderViewHostTargetsUIHandler::~RenderViewHostTargetsUIHandler() {
121 notification_registrar_
.RemoveAll();
124 void RenderViewHostTargetsUIHandler::Observe(
126 const content::NotificationSource
& source
,
127 const content::NotificationDetails
& details
) {
128 const int kUpdateDelay
= 100;
131 base::Bind(&RenderViewHostTargetsUIHandler::UpdateTargets
,
132 base::Unretained(this)),
133 base::TimeDelta::FromMilliseconds(kUpdateDelay
)));
136 void RenderViewHostTargetsUIHandler::UpdateTargets() {
137 scoped_ptr
<base::ListValue
> list_value(new base::ListValue());
139 std::map
<WebContents
*, base::DictionaryValue
*> web_contents_to_descriptor_
;
140 std::vector
<DevToolsTargetImpl
*> guest_targets
;
142 DevToolsTargetImpl::List targets
=
143 DevToolsTargetImpl::EnumerateRenderViewHostTargets();
145 STLDeleteValues(&targets_
);
146 for (DevToolsTargetImpl::List::iterator it
= targets
.begin();
147 it
!= targets
.end(); ++it
) {
148 scoped_ptr
<DevToolsTargetImpl
> target(*it
);
149 content::RenderViewHost
* rvh
= target
->GetRenderViewHost();
152 WebContents
* web_contents
= WebContents::FromRenderViewHost(rvh
);
156 DevToolsTargetImpl
* target_ptr
= target
.get();
157 targets_
[target_ptr
->GetId()] = target
.release();
158 if (rvh
->GetProcess()->IsGuest()) {
159 guest_targets
.push_back(target_ptr
);
161 base::DictionaryValue
* descriptor
= Serialize(*target_ptr
);
162 list_value
->Append(descriptor
);
163 web_contents_to_descriptor_
[web_contents
] = descriptor
;
167 // Add the list of guest-views to each of its embedders.
168 for (std::vector
<DevToolsTargetImpl
*>::iterator
it(guest_targets
.begin());
169 it
!= guest_targets
.end(); ++it
) {
170 DevToolsTargetImpl
* guest
= (*it
);
171 WebContents
* guest_web_contents
=
172 WebContents::FromRenderViewHost(guest
->GetRenderViewHost());
173 WebContents
* embedder
= guest_web_contents
->GetEmbedderWebContents();
174 if (embedder
&& web_contents_to_descriptor_
.count(embedder
) > 0) {
175 base::DictionaryValue
* parent
= web_contents_to_descriptor_
[embedder
];
176 base::ListValue
* guests
= NULL
;
177 if (!parent
->GetList(kGuestList
, &guests
)) {
178 guests
= new base::ListValue();
179 parent
->Set(kGuestList
, guests
);
181 guests
->Append(Serialize(*guest
));
185 SendSerializedTargets(list_value
.Pass());
188 // WorkerObserver -------------------------------------------------------------
191 : public content::WorkerServiceObserver
,
192 public base::RefCountedThreadSafe
<WorkerObserver
> {
196 void Start(DevToolsTargetImpl::Callback callback
) {
197 DCHECK(callback_
.is_null());
198 DCHECK(!callback
.is_null());
199 callback_
= callback
;
200 BrowserThread::PostTask(
201 BrowserThread::IO
, FROM_HERE
,
202 base::Bind(&WorkerObserver::StartOnIOThread
, this));
206 DCHECK(!callback_
.is_null());
207 callback_
= DevToolsTargetImpl::Callback();
208 BrowserThread::PostTask(
209 BrowserThread::IO
, FROM_HERE
,
210 base::Bind(&WorkerObserver::StopOnIOThread
, this));
214 BrowserThread::PostTask(
215 BrowserThread::IO
, FROM_HERE
,
216 base::Bind(&WorkerObserver::EnumerateOnIOThread
,
221 friend class base::RefCountedThreadSafe
<WorkerObserver
>;
222 virtual ~WorkerObserver() {}
224 // content::WorkerServiceObserver overrides:
225 virtual void WorkerCreated(
227 const base::string16
& name
,
229 int route_id
) OVERRIDE
{
230 EnumerateOnIOThread();
233 virtual void WorkerDestroyed(int process_id
, int route_id
) OVERRIDE
{
234 EnumerateOnIOThread();
237 void StartOnIOThread() {
238 content::WorkerService::GetInstance()->AddObserver(this);
239 EnumerateOnIOThread();
242 void StopOnIOThread() {
243 content::WorkerService::GetInstance()->RemoveObserver(this);
246 void EnumerateOnIOThread() {
247 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
248 DevToolsTargetImpl::EnumerateWorkerTargets(
249 base::Bind(&WorkerObserver::RespondOnUIThread
, this));
252 void RespondOnUIThread(const DevToolsTargetImpl::List
& targets
) {
253 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
254 if (callback_
.is_null())
256 callback_
.Run(targets
);
259 DevToolsTargetImpl::Callback callback_
;
262 // WorkerTargetsUIHandler -----------------------------------------------------
264 class WorkerTargetsUIHandler
265 : public DevToolsTargetsUIHandler
,
266 public content::BrowserChildProcessObserver
{
268 explicit WorkerTargetsUIHandler(Callback callback
);
269 virtual ~WorkerTargetsUIHandler();
272 // content::BrowserChildProcessObserver overrides.
273 virtual void BrowserChildProcessHostConnected(
274 const content::ChildProcessData
& data
) OVERRIDE
;
275 virtual void BrowserChildProcessHostDisconnected(
276 const content::ChildProcessData
& data
) OVERRIDE
;
278 void UpdateTargets(const DevToolsTargetImpl::List
& targets
);
280 scoped_refptr
<WorkerObserver
> observer_
;
283 WorkerTargetsUIHandler::WorkerTargetsUIHandler(Callback callback
)
284 : DevToolsTargetsUIHandler(kTargetSourceWorker
, callback
),
285 observer_(new WorkerObserver()) {
286 observer_
->Start(base::Bind(&WorkerTargetsUIHandler::UpdateTargets
,
287 base::Unretained(this)));
288 BrowserChildProcessObserver::Add(this);
291 WorkerTargetsUIHandler::~WorkerTargetsUIHandler() {
292 BrowserChildProcessObserver::Remove(this);
296 void WorkerTargetsUIHandler::BrowserChildProcessHostConnected(
297 const content::ChildProcessData
& data
) {
298 if (data
.process_type
== content::PROCESS_TYPE_WORKER
)
299 observer_
->Enumerate();
302 void WorkerTargetsUIHandler::BrowserChildProcessHostDisconnected(
303 const content::ChildProcessData
& data
) {
304 if (data
.process_type
== content::PROCESS_TYPE_WORKER
)
305 observer_
->Enumerate();
308 void WorkerTargetsUIHandler::UpdateTargets(
309 const DevToolsTargetImpl::List
& targets
) {
310 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
311 scoped_ptr
<base::ListValue
> list_value(new base::ListValue());
312 STLDeleteValues(&targets_
);
313 for (DevToolsTargetImpl::List::const_iterator it
= targets
.begin();
314 it
!= targets
.end(); ++it
) {
315 DevToolsTargetImpl
* target
= *it
;
316 list_value
->Append(Serialize(*target
));
317 targets_
[target
->GetId()] = target
;
319 SendSerializedTargets(list_value
.Pass());
322 // AdbTargetsUIHandler --------------------------------------------------------
324 class AdbTargetsUIHandler
325 : public DevToolsRemoteTargetsUIHandler
,
326 public DevToolsAdbBridge::Listener
{
328 AdbTargetsUIHandler(Callback callback
, Profile
* profile
);
329 virtual ~AdbTargetsUIHandler();
331 virtual void Open(const std::string
& browser_id
,
332 const std::string
& url
) OVERRIDE
;
335 // DevToolsAdbBridge::Listener overrides.
336 virtual void RemoteDevicesChanged(
337 DevToolsAdbBridge::RemoteDevices
* devices
) OVERRIDE
;
341 typedef std::map
<std::string
,
342 scoped_refptr
<DevToolsAdbBridge::RemoteBrowser
> > RemoteBrowsers
;
343 RemoteBrowsers remote_browsers_
;
346 AdbTargetsUIHandler::AdbTargetsUIHandler(Callback callback
, Profile
* profile
)
347 : DevToolsRemoteTargetsUIHandler(kTargetSourceAdb
, callback
),
349 DevToolsAdbBridge
* adb_bridge
=
350 DevToolsAdbBridge::Factory::GetForProfile(profile_
);
352 adb_bridge
->AddListener(this);
355 AdbTargetsUIHandler::~AdbTargetsUIHandler() {
356 DevToolsAdbBridge
* adb_bridge
=
357 DevToolsAdbBridge::Factory::GetForProfile(profile_
);
359 adb_bridge
->RemoveListener(this);
362 void AdbTargetsUIHandler::Open(const std::string
& browser_id
,
363 const std::string
& url
) {
364 RemoteBrowsers::iterator it
= remote_browsers_
.find(browser_id
);
365 if (it
!= remote_browsers_
.end())
366 it
->second
->Open(url
);
369 void AdbTargetsUIHandler::RemoteDevicesChanged(
370 DevToolsAdbBridge::RemoteDevices
* devices
) {
371 PortForwardingController
* port_forwarding_controller
=
372 PortForwardingController::Factory::GetForProfile(profile_
);
373 PortForwardingController::DevicesStatus port_forwarding_status
;
374 if (port_forwarding_controller
)
375 port_forwarding_status
=
376 port_forwarding_controller
->UpdateDeviceList(*devices
);
378 remote_browsers_
.clear();
379 STLDeleteValues(&targets_
);
381 scoped_ptr
<base::ListValue
> device_list(new base::ListValue());
382 for (DevToolsAdbBridge::RemoteDevices::iterator dit
= devices
->begin();
383 dit
!= devices
->end(); ++dit
) {
384 DevToolsAdbBridge::RemoteDevice
* device
= dit
->get();
385 base::DictionaryValue
* device_data
= new base::DictionaryValue();
386 device_data
->SetString(kAdbModelField
, device
->GetModel());
387 device_data
->SetString(kAdbSerialField
, device
->GetSerial());
388 device_data
->SetBoolean(kAdbConnectedField
, device
->IsConnected());
389 std::string device_id
= base::StringPrintf(
391 device
->GetSerial().c_str());
392 device_data
->SetString(kTargetIdField
, device_id
);
393 base::ListValue
* browser_list
= new base::ListValue();
394 device_data
->Set(kAdbBrowsersList
, browser_list
);
396 DevToolsAdbBridge::RemoteBrowsers
& browsers
= device
->browsers();
397 for (DevToolsAdbBridge::RemoteBrowsers::iterator bit
=
398 browsers
.begin(); bit
!= browsers
.end(); ++bit
) {
399 DevToolsAdbBridge::RemoteBrowser
* browser
= bit
->get();
400 base::DictionaryValue
* browser_data
= new base::DictionaryValue();
401 browser_data
->SetString(kAdbBrowserNameField
, browser
->display_name());
402 browser_data
->SetString(kAdbBrowserVersionField
, browser
->version());
403 DevToolsAdbBridge::RemoteBrowser::ParsedVersion parsed
=
404 browser
->GetParsedVersion();
405 browser_data
->SetInteger(
406 kAdbBrowserChromeVersionField
,
407 browser
->IsChrome() && !parsed
.empty() ? parsed
[0] : 0);
408 std::string browser_id
= base::StringPrintf(
409 "browser:%s:%s:%s:%s",
410 device
->GetSerial().c_str(), // Ensure uniqueness across devices.
411 browser
->display_name().c_str(), // Sort by display name.
412 browser
->version().c_str(), // Then by version.
413 browser
->socket().c_str()); // Ensure uniqueness on the device.
414 browser_data
->SetString(kTargetIdField
, browser_id
);
415 browser_data
->SetString(kTargetSourceField
, source_id());
416 remote_browsers_
[browser_id
] = browser
;
417 base::ListValue
* page_list
= new base::ListValue();
418 browser_data
->Set(kAdbPagesList
, page_list
);
420 DevToolsTargetImpl::List pages
= browser
->CreatePageTargets();
421 for (DevToolsTargetImpl::List::iterator it
=
422 pages
.begin(); it
!= pages
.end(); ++it
) {
423 DevToolsTargetImpl
* target
= *it
;
424 base::DictionaryValue
* target_data
= Serialize(*target
);
425 target_data
->SetBoolean(
426 kAdbAttachedForeignField
,
427 target
->IsAttached() &&
428 !DevToolsAdbBridge::HasDevToolsWindow(target
->GetId()));
429 // Pass the screen size in the target object to make sure that
430 // the caching logic does not prevent the target item from updating
431 // when the screen size changes.
432 gfx::Size screen_size
= device
->screen_size();
433 target_data
->SetInteger(kAdbScreenWidthField
, screen_size
.width());
434 target_data
->SetInteger(kAdbScreenHeightField
, screen_size
.height());
435 targets_
[target
->GetId()] = target
;
436 page_list
->Append(target_data
);
438 browser_list
->Append(browser_data
);
441 if (port_forwarding_controller
) {
442 PortForwardingController::DevicesStatus::iterator sit
=
443 port_forwarding_status
.find(device
->GetSerial());
444 if (sit
!= port_forwarding_status
.end()) {
445 base::DictionaryValue
* port_status_dict
= new base::DictionaryValue();
446 typedef PortForwardingController::PortStatusMap StatusMap
;
447 const StatusMap
& port_status
= sit
->second
;
448 for (StatusMap::const_iterator it
= port_status
.begin();
449 it
!= port_status
.end(); ++it
) {
450 port_status_dict
->SetInteger(
451 base::StringPrintf("%d", it
->first
), it
->second
);
453 device_data
->Set(kAdbPortStatus
, port_status_dict
);
457 device_list
->Append(device_data
);
459 SendSerializedTargets(device_list
.Pass());
464 // DevToolsTargetsUIHandler ---------------------------------------------------
466 DevToolsTargetsUIHandler::DevToolsTargetsUIHandler(
467 const std::string
& source_id
,
469 : source_id_(source_id
),
470 callback_(callback
) {
473 DevToolsTargetsUIHandler::~DevToolsTargetsUIHandler() {
474 STLDeleteValues(&targets_
);
478 scoped_ptr
<DevToolsTargetsUIHandler
>
479 DevToolsTargetsUIHandler::CreateForRenderers(
480 DevToolsTargetsUIHandler::Callback callback
) {
481 return scoped_ptr
<DevToolsTargetsUIHandler
>(
482 new RenderViewHostTargetsUIHandler(callback
));
486 scoped_ptr
<DevToolsTargetsUIHandler
>
487 DevToolsTargetsUIHandler::CreateForWorkers(
488 DevToolsTargetsUIHandler::Callback callback
) {
489 return scoped_ptr
<DevToolsTargetsUIHandler
>(
490 new WorkerTargetsUIHandler(callback
));
493 void DevToolsTargetsUIHandler::Inspect(const std::string
& target_id
,
495 TargetMap::iterator it
= targets_
.find(target_id
);
496 if (it
!= targets_
.end())
497 it
->second
->Inspect(profile
);
500 void DevToolsTargetsUIHandler::Activate(const std::string
& target_id
) {
501 TargetMap::iterator it
= targets_
.find(target_id
);
502 if (it
!= targets_
.end())
503 it
->second
->Activate();
506 void DevToolsTargetsUIHandler::Close(const std::string
& target_id
) {
507 TargetMap::iterator it
= targets_
.find(target_id
);
508 if (it
!= targets_
.end())
512 void DevToolsTargetsUIHandler::Reload(const std::string
& target_id
) {
513 TargetMap::iterator it
= targets_
.find(target_id
);
514 if (it
!= targets_
.end())
515 it
->second
->Reload();
518 base::DictionaryValue
*
519 DevToolsTargetsUIHandler::Serialize(
520 const DevToolsTargetImpl
& target
) {
521 base::DictionaryValue
* target_data
= new base::DictionaryValue();
522 target_data
->SetString(kTargetSourceField
, source_id_
);
523 target_data
->SetString(kTargetIdField
, target
.GetId());
524 target_data
->SetString(kTargetTypeField
, target
.GetType());
525 target_data
->SetBoolean(kAttachedField
, target
.IsAttached());
526 target_data
->SetString(kUrlField
, target
.GetUrl().spec());
527 target_data
->SetString(kNameField
, net::EscapeForHTML(target
.GetTitle()));
528 target_data
->SetString(kFaviconUrlField
, target
.GetFaviconUrl().spec());
529 target_data
->SetString(kDescriptionField
, target
.GetDescription());
533 void DevToolsTargetsUIHandler::SendSerializedTargets(
534 scoped_ptr
<base::ListValue
> list
) {
535 callback_
.Run(source_id_
, list
.Pass());
538 // DevToolsRemoteTargetsUIHandler ---------------------------------------------
540 DevToolsRemoteTargetsUIHandler::DevToolsRemoteTargetsUIHandler(
541 const std::string
& source_id
,
543 : DevToolsTargetsUIHandler(source_id
, callback
) {
547 scoped_ptr
<DevToolsRemoteTargetsUIHandler
>
548 DevToolsRemoteTargetsUIHandler::CreateForAdb(
549 DevToolsTargetsUIHandler::Callback callback
, Profile
* profile
) {
550 return scoped_ptr
<DevToolsRemoteTargetsUIHandler
>(
551 new AdbTargetsUIHandler(callback
, profile
));