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 "base/version.h"
12 #include "chrome/browser/devtools/device/devtools_android_bridge.h"
13 #include "chrome/browser/devtools/devtools_target_impl.h"
14 #include "chrome/common/chrome_version_info.h"
15 #include "content/public/browser/browser_child_process_observer.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "content/public/browser/child_process_data.h"
18 #include "content/public/browser/notification_observer.h"
19 #include "content/public/browser/notification_registrar.h"
20 #include "content/public/browser/notification_service.h"
21 #include "content/public/browser/notification_source.h"
22 #include "content/public/browser/notification_types.h"
23 #include "content/public/browser/worker_service.h"
24 #include "content/public/browser/worker_service_observer.h"
25 #include "content/public/common/process_type.h"
26 #include "net/base/escape.h"
28 using content::BrowserThread
;
32 const char kTargetSourceField
[] = "source";
33 const char kTargetSourceLocal
[] = "local";
34 const char kTargetSourceRemote
[] = "remote";
36 const char kTargetIdField
[] = "id";
37 const char kTargetTypeField
[] = "type";
38 const char kAttachedField
[] = "attached";
39 const char kUrlField
[] = "url";
40 const char kNameField
[] = "name";
41 const char kFaviconUrlField
[] = "faviconUrl";
42 const char kDescriptionField
[] = "description";
44 const char kGuestList
[] = "guests";
46 const char kAdbModelField
[] = "adbModel";
47 const char kAdbConnectedField
[] = "adbConnected";
48 const char kAdbSerialField
[] = "adbSerial";
49 const char kAdbBrowsersList
[] = "browsers";
50 const char kAdbDeviceIdFormat
[] = "device:%s";
52 const char kAdbBrowserNameField
[] = "adbBrowserName";
53 const char kAdbBrowserUserField
[] = "adbBrowserUser";
54 const char kAdbBrowserVersionField
[] = "adbBrowserVersion";
55 const char kAdbBrowserChromeVersionField
[] = "adbBrowserChromeVersion";
56 const char kAdbPagesList
[] = "pages";
58 const char kAdbScreenWidthField
[] = "adbScreenWidth";
59 const char kAdbScreenHeightField
[] = "adbScreenHeight";
60 const char kAdbAttachedForeignField
[] = "adbAttachedForeign";
62 const char kPortForwardingPorts
[] = "ports";
63 const char kPortForwardingBrowserId
[] = "browserId";
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 // WorkerObserver -------------------------------------------------------------
88 : public content::WorkerServiceObserver
,
89 public base::RefCountedThreadSafe
<WorkerObserver
> {
93 void Start(base::Closure callback
) {
94 DCHECK(callback_
.is_null());
95 DCHECK(!callback
.is_null());
97 BrowserThread::PostTask(
98 BrowserThread::IO
, FROM_HERE
,
99 base::Bind(&WorkerObserver::StartOnIOThread
, this));
103 DCHECK(!callback_
.is_null());
104 callback_
= base::Closure();
105 BrowserThread::PostTask(
106 BrowserThread::IO
, FROM_HERE
,
107 base::Bind(&WorkerObserver::StopOnIOThread
, this));
111 friend class base::RefCountedThreadSafe
<WorkerObserver
>;
112 ~WorkerObserver() override
{}
114 // content::WorkerServiceObserver overrides:
115 void WorkerCreated(const GURL
& url
,
116 const base::string16
& name
,
118 int route_id
) override
{
122 void WorkerDestroyed(int process_id
, int route_id
) override
{
126 void StartOnIOThread() {
127 content::WorkerService::GetInstance()->AddObserver(this);
130 void StopOnIOThread() {
131 content::WorkerService::GetInstance()->RemoveObserver(this);
134 void NotifyOnIOThread() {
135 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
136 BrowserThread::PostTask(
137 BrowserThread::UI
, FROM_HERE
,
138 base::Bind(&WorkerObserver::NotifyOnUIThread
, this));
141 void NotifyOnUIThread() {
142 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
143 if (callback_
.is_null())
148 // Accessed on UI thread.
149 base::Closure callback_
;
152 // LocalTargetsUIHandler ---------------------------------------------
154 class LocalTargetsUIHandler
155 : public DevToolsTargetsUIHandler
,
156 public content::NotificationObserver
{
158 explicit LocalTargetsUIHandler(const Callback
& callback
);
159 ~LocalTargetsUIHandler() override
;
161 // DevToolsTargetsUIHandler overrides.
162 void ForceUpdate() override
;
165 // content::NotificationObserver overrides.
166 void Observe(int type
,
167 const content::NotificationSource
& source
,
168 const content::NotificationDetails
& details
) override
;
170 void ScheduleUpdate();
171 void UpdateTargets();
172 void SendTargets(const DevToolsTargetImpl::List
& targets
);
174 content::NotificationRegistrar notification_registrar_
;
175 scoped_ptr
<CancelableTimer
> timer_
;
176 scoped_refptr
<WorkerObserver
> observer_
;
177 base::WeakPtrFactory
<LocalTargetsUIHandler
> weak_factory_
;
180 LocalTargetsUIHandler::LocalTargetsUIHandler(
181 const Callback
& callback
)
182 : DevToolsTargetsUIHandler(kTargetSourceLocal
, callback
),
183 observer_(new WorkerObserver()),
184 weak_factory_(this) {
185 notification_registrar_
.Add(this,
186 content::NOTIFICATION_WEB_CONTENTS_CONNECTED
,
187 content::NotificationService::AllSources());
188 notification_registrar_
.Add(this,
189 content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED
,
190 content::NotificationService::AllSources());
191 notification_registrar_
.Add(this,
192 content::NOTIFICATION_WEB_CONTENTS_DESTROYED
,
193 content::NotificationService::AllSources());
194 observer_
->Start(base::Bind(&LocalTargetsUIHandler::ScheduleUpdate
,
195 base::Unretained(this)));
199 LocalTargetsUIHandler::~LocalTargetsUIHandler() {
200 notification_registrar_
.RemoveAll();
204 void LocalTargetsUIHandler::Observe(
206 const content::NotificationSource
& source
,
207 const content::NotificationDetails
& details
) {
211 void LocalTargetsUIHandler::ForceUpdate() {
215 void LocalTargetsUIHandler::ScheduleUpdate() {
216 const int kUpdateDelay
= 100;
219 base::Bind(&LocalTargetsUIHandler::UpdateTargets
,
220 base::Unretained(this)),
221 base::TimeDelta::FromMilliseconds(kUpdateDelay
)));
224 void LocalTargetsUIHandler::UpdateTargets() {
225 DevToolsTargetImpl::EnumerateAllTargets(base::Bind(
226 &LocalTargetsUIHandler::SendTargets
,
227 weak_factory_
.GetWeakPtr()));
230 void LocalTargetsUIHandler::SendTargets(
231 const DevToolsTargetImpl::List
& targets
) {
232 base::ListValue list_value
;
233 std::map
<std::string
, base::DictionaryValue
*> id_to_descriptor
;
235 STLDeleteValues(&targets_
);
236 for (DevToolsTargetImpl::List::const_iterator it
= targets
.begin();
237 it
!= targets
.end(); ++it
) {
238 DevToolsTargetImpl
* target
= *it
;
239 targets_
[target
->GetId()] = target
;
240 id_to_descriptor
[target
->GetId()] = Serialize(*target
);
243 for (TargetMap::iterator
it(targets_
.begin()); it
!= targets_
.end(); ++it
) {
244 DevToolsTargetImpl
* target
= it
->second
;
245 base::DictionaryValue
* descriptor
= id_to_descriptor
[target
->GetId()];
246 std::string parent_id
= target
->GetParentId();
247 if (parent_id
.empty() || id_to_descriptor
.count(parent_id
) == 0) {
248 list_value
.Append(descriptor
);
250 base::DictionaryValue
* parent
= id_to_descriptor
[parent_id
];
251 base::ListValue
* guests
= NULL
;
252 if (!parent
->GetList(kGuestList
, &guests
)) {
253 guests
= new base::ListValue();
254 parent
->Set(kGuestList
, guests
);
256 guests
->Append(descriptor
);
260 SendSerializedTargets(list_value
);
263 // AdbTargetsUIHandler --------------------------------------------------------
265 class AdbTargetsUIHandler
266 : public DevToolsTargetsUIHandler
,
267 public DevToolsAndroidBridge::DeviceListListener
{
269 AdbTargetsUIHandler(const Callback
& callback
, Profile
* profile
);
270 ~AdbTargetsUIHandler() override
;
272 void Open(const std::string
& browser_id
, const std::string
& url
) override
;
274 scoped_refptr
<content::DevToolsAgentHost
> GetBrowserAgentHost(
275 const std::string
& browser_id
) override
;
278 // DevToolsAndroidBridge::Listener overrides.
279 void DeviceListChanged(
280 const DevToolsAndroidBridge::RemoteDevices
& devices
) override
;
282 DevToolsAndroidBridge
* GetAndroidBridge();
284 Profile
* const profile_
;
285 DevToolsAndroidBridge
* const android_bridge_
;
287 typedef std::map
<std::string
,
288 scoped_refptr
<DevToolsAndroidBridge::RemoteBrowser
> > RemoteBrowsers
;
289 RemoteBrowsers remote_browsers_
;
292 AdbTargetsUIHandler::AdbTargetsUIHandler(const Callback
& callback
,
294 : DevToolsTargetsUIHandler(kTargetSourceRemote
, callback
),
297 DevToolsAndroidBridge::Factory::GetForProfile(profile_
)) {
298 DCHECK(android_bridge_
);
299 android_bridge_
->AddDeviceListListener(this);
302 AdbTargetsUIHandler::~AdbTargetsUIHandler() {
303 android_bridge_
->RemoveDeviceListListener(this);
306 void AdbTargetsUIHandler::Open(const std::string
& browser_id
,
307 const std::string
& url
) {
308 RemoteBrowsers::iterator it
= remote_browsers_
.find(browser_id
);
309 if (it
!= remote_browsers_
.end())
310 android_bridge_
->OpenRemotePage(it
->second
, url
);
313 scoped_refptr
<content::DevToolsAgentHost
>
314 AdbTargetsUIHandler::GetBrowserAgentHost(
315 const std::string
& browser_id
) {
316 RemoteBrowsers::iterator it
= remote_browsers_
.find(browser_id
);
317 if (it
== remote_browsers_
.end())
320 return android_bridge_
->GetBrowserAgentHost(it
->second
);
323 void AdbTargetsUIHandler::DeviceListChanged(
324 const DevToolsAndroidBridge::RemoteDevices
& devices
) {
325 remote_browsers_
.clear();
326 STLDeleteValues(&targets_
);
328 base::ListValue device_list
;
329 for (DevToolsAndroidBridge::RemoteDevices::const_iterator dit
=
330 devices
.begin(); dit
!= devices
.end(); ++dit
) {
331 DevToolsAndroidBridge::RemoteDevice
* device
= dit
->get();
332 base::DictionaryValue
* device_data
= new base::DictionaryValue();
333 device_data
->SetString(kAdbModelField
, device
->model());
334 device_data
->SetString(kAdbSerialField
, device
->serial());
335 device_data
->SetBoolean(kAdbConnectedField
, device
->is_connected());
336 std::string device_id
= base::StringPrintf(
338 device
->serial().c_str());
339 device_data
->SetString(kTargetIdField
, device_id
);
340 base::ListValue
* browser_list
= new base::ListValue();
341 device_data
->Set(kAdbBrowsersList
, browser_list
);
343 DevToolsAndroidBridge::RemoteBrowsers
& browsers
= device
->browsers();
344 for (DevToolsAndroidBridge::RemoteBrowsers::iterator bit
=
345 browsers
.begin(); bit
!= browsers
.end(); ++bit
) {
346 DevToolsAndroidBridge::RemoteBrowser
* browser
= bit
->get();
347 base::DictionaryValue
* browser_data
= new base::DictionaryValue();
348 browser_data
->SetString(kAdbBrowserNameField
, browser
->display_name());
349 browser_data
->SetString(kAdbBrowserUserField
, browser
->user());
350 browser_data
->SetString(kAdbBrowserVersionField
, browser
->version());
351 DevToolsAndroidBridge::RemoteBrowser::ParsedVersion parsed
=
352 browser
->GetParsedVersion();
353 browser_data
->SetInteger(
354 kAdbBrowserChromeVersionField
,
355 browser
->IsChrome() && !parsed
.empty() ? parsed
[0] : 0);
356 std::string browser_id
= browser
->GetId();
357 browser_data
->SetString(kTargetIdField
, browser_id
);
358 browser_data
->SetString(kTargetSourceField
, source_id());
360 base::ListValue
* page_list
= new base::ListValue();
361 remote_browsers_
[browser_id
] = browser
;
362 browser_data
->Set(kAdbPagesList
, page_list
);
363 for (const auto& page
: browser
->pages()) {
364 DevToolsTargetImpl
* target
= android_bridge_
->CreatePageTarget(page
);
365 base::DictionaryValue
* target_data
= Serialize(*target
);
366 target_data
->SetBoolean(
367 kAdbAttachedForeignField
,
368 target
->IsAttached() &&
369 !android_bridge_
->HasDevToolsWindow(target
->GetId()));
370 // Pass the screen size in the target object to make sure that
371 // the caching logic does not prevent the target item from updating
372 // when the screen size changes.
373 gfx::Size screen_size
= device
->screen_size();
374 target_data
->SetInteger(kAdbScreenWidthField
, screen_size
.width());
375 target_data
->SetInteger(kAdbScreenHeightField
, screen_size
.height());
376 targets_
[target
->GetId()] = target
;
377 page_list
->Append(target_data
);
379 browser_list
->Append(browser_data
);
382 device_list
.Append(device_data
);
384 SendSerializedTargets(device_list
);
389 // DevToolsTargetsUIHandler ---------------------------------------------------
391 DevToolsTargetsUIHandler::DevToolsTargetsUIHandler(
392 const std::string
& source_id
,
393 const Callback
& callback
)
394 : source_id_(source_id
),
395 callback_(callback
) {
398 DevToolsTargetsUIHandler::~DevToolsTargetsUIHandler() {
399 STLDeleteValues(&targets_
);
403 scoped_ptr
<DevToolsTargetsUIHandler
>
404 DevToolsTargetsUIHandler::CreateForLocal(
405 const DevToolsTargetsUIHandler::Callback
& callback
) {
406 return scoped_ptr
<DevToolsTargetsUIHandler
>(
407 new LocalTargetsUIHandler(callback
));
411 scoped_ptr
<DevToolsTargetsUIHandler
>
412 DevToolsTargetsUIHandler::CreateForAdb(
413 const DevToolsTargetsUIHandler::Callback
& callback
, Profile
* profile
) {
414 return scoped_ptr
<DevToolsTargetsUIHandler
>(
415 new AdbTargetsUIHandler(callback
, profile
));
418 DevToolsTargetImpl
* DevToolsTargetsUIHandler::GetTarget(
419 const std::string
& target_id
) {
420 TargetMap::iterator it
= targets_
.find(target_id
);
421 if (it
!= targets_
.end())
426 void DevToolsTargetsUIHandler::Open(const std::string
& browser_id
,
427 const std::string
& url
) {
430 scoped_refptr
<content::DevToolsAgentHost
>
431 DevToolsTargetsUIHandler::GetBrowserAgentHost(const std::string
& browser_id
) {
435 base::DictionaryValue
* DevToolsTargetsUIHandler::Serialize(
436 const DevToolsTargetImpl
& target
) {
437 base::DictionaryValue
* target_data
= new base::DictionaryValue();
438 target_data
->SetString(kTargetSourceField
, source_id_
);
439 target_data
->SetString(kTargetIdField
, target
.GetId());
440 target_data
->SetString(kTargetTypeField
, target
.GetType());
441 target_data
->SetBoolean(kAttachedField
, target
.IsAttached());
442 target_data
->SetString(kUrlField
, target
.GetURL().spec());
443 target_data
->SetString(kNameField
, net::EscapeForHTML(target
.GetTitle()));
444 target_data
->SetString(kFaviconUrlField
, target
.GetFaviconURL().spec());
445 target_data
->SetString(kDescriptionField
, target
.GetDescription());
449 void DevToolsTargetsUIHandler::SendSerializedTargets(
450 const base::ListValue
& list
) {
451 callback_
.Run(source_id_
, list
);
454 void DevToolsTargetsUIHandler::ForceUpdate() {
457 // PortForwardingStatusSerializer ---------------------------------------------
459 PortForwardingStatusSerializer::PortForwardingStatusSerializer(
460 const Callback
& callback
, Profile
* profile
)
461 : callback_(callback
),
463 DevToolsAndroidBridge
* android_bridge
=
464 DevToolsAndroidBridge::Factory::GetForProfile(profile_
);
466 android_bridge
->AddPortForwardingListener(this);
469 PortForwardingStatusSerializer::~PortForwardingStatusSerializer() {
470 DevToolsAndroidBridge
* android_bridge
=
471 DevToolsAndroidBridge::Factory::GetForProfile(profile_
);
473 android_bridge
->RemovePortForwardingListener(this);
476 void PortForwardingStatusSerializer::PortStatusChanged(
477 const ForwardingStatus
& status
) {
478 base::DictionaryValue result
;
479 for (ForwardingStatus::const_iterator sit
= status
.begin();
480 sit
!= status
.end(); ++sit
) {
481 base::DictionaryValue
* port_status_dict
= new base::DictionaryValue();
482 const PortStatusMap
& port_status_map
= sit
->second
;
483 for (PortStatusMap::const_iterator it
= port_status_map
.begin();
484 it
!= port_status_map
.end(); ++it
) {
485 port_status_dict
->SetInteger(
486 base::StringPrintf("%d", it
->first
), it
->second
);
489 base::DictionaryValue
* device_status_dict
= new base::DictionaryValue();
490 device_status_dict
->Set(kPortForwardingPorts
, port_status_dict
);
491 device_status_dict
->SetString(kPortForwardingBrowserId
,
492 sit
->first
->GetId());
494 std::string device_id
= base::StringPrintf(
496 sit
->first
->serial().c_str());
497 result
.Set(device_id
, device_status_dict
);
499 callback_
.Run(result
);