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/location.h"
8 #include "base/memory/weak_ptr.h"
9 #include "base/single_thread_task_runner.h"
10 #include "base/stl_util.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/thread_task_runner_handle.h"
14 #include "base/values.h"
15 #include "base/version.h"
16 #include "chrome/browser/devtools/device/devtools_android_bridge.h"
17 #include "chrome/browser/devtools/devtools_target_impl.h"
18 #include "content/public/browser/browser_child_process_observer.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "content/public/browser/child_process_data.h"
21 #include "content/public/browser/notification_observer.h"
22 #include "content/public/browser/notification_registrar.h"
23 #include "content/public/browser/notification_service.h"
24 #include "content/public/browser/notification_source.h"
25 #include "content/public/browser/notification_types.h"
26 #include "content/public/browser/worker_service.h"
27 #include "content/public/browser/worker_service_observer.h"
28 #include "content/public/common/process_type.h"
29 #include "net/base/escape.h"
31 using content::BrowserThread
;
35 const char kTargetSourceField
[] = "source";
36 const char kTargetSourceLocal
[] = "local";
37 const char kTargetSourceRemote
[] = "remote";
39 const char kTargetIdField
[] = "id";
40 const char kTargetTypeField
[] = "type";
41 const char kAttachedField
[] = "attached";
42 const char kUrlField
[] = "url";
43 const char kNameField
[] = "name";
44 const char kFaviconUrlField
[] = "faviconUrl";
45 const char kDescriptionField
[] = "description";
47 const char kGuestList
[] = "guests";
49 const char kAdbModelField
[] = "adbModel";
50 const char kAdbConnectedField
[] = "adbConnected";
51 const char kAdbSerialField
[] = "adbSerial";
52 const char kAdbBrowsersList
[] = "browsers";
53 const char kAdbDeviceIdFormat
[] = "device:%s";
55 const char kAdbBrowserNameField
[] = "adbBrowserName";
56 const char kAdbBrowserUserField
[] = "adbBrowserUser";
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 const char kPortForwardingPorts
[] = "ports";
66 const char kPortForwardingBrowserId
[] = "browserId";
68 // CancelableTimer ------------------------------------------------------------
70 class CancelableTimer
{
72 CancelableTimer(base::Closure callback
, base::TimeDelta delay
)
73 : callback_(callback
),
75 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
77 base::Bind(&CancelableTimer::Fire
, weak_factory_
.GetWeakPtr()), delay
);
81 void Fire() { callback_
.Run(); }
83 base::Closure callback_
;
84 base::WeakPtrFactory
<CancelableTimer
> weak_factory_
;
87 // WorkerObserver -------------------------------------------------------------
90 : public content::WorkerServiceObserver
,
91 public base::RefCountedThreadSafe
<WorkerObserver
> {
95 void Start(base::Closure callback
) {
96 DCHECK(callback_
.is_null());
97 DCHECK(!callback
.is_null());
99 BrowserThread::PostTask(
100 BrowserThread::IO
, FROM_HERE
,
101 base::Bind(&WorkerObserver::StartOnIOThread
, this));
105 DCHECK(!callback_
.is_null());
106 callback_
= base::Closure();
107 BrowserThread::PostTask(
108 BrowserThread::IO
, FROM_HERE
,
109 base::Bind(&WorkerObserver::StopOnIOThread
, this));
113 friend class base::RefCountedThreadSafe
<WorkerObserver
>;
114 ~WorkerObserver() override
{}
116 // content::WorkerServiceObserver overrides:
117 void WorkerCreated(const GURL
& url
,
118 const base::string16
& name
,
120 int route_id
) override
{
124 void WorkerDestroyed(int process_id
, int route_id
) override
{
128 void StartOnIOThread() {
129 content::WorkerService::GetInstance()->AddObserver(this);
132 void StopOnIOThread() {
133 content::WorkerService::GetInstance()->RemoveObserver(this);
136 void NotifyOnIOThread() {
137 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
138 BrowserThread::PostTask(
139 BrowserThread::UI
, FROM_HERE
,
140 base::Bind(&WorkerObserver::NotifyOnUIThread
, this));
143 void NotifyOnUIThread() {
144 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
145 if (callback_
.is_null())
150 // Accessed on UI thread.
151 base::Closure callback_
;
154 // LocalTargetsUIHandler ---------------------------------------------
156 class LocalTargetsUIHandler
157 : public DevToolsTargetsUIHandler
,
158 public content::NotificationObserver
{
160 explicit LocalTargetsUIHandler(const Callback
& callback
);
161 ~LocalTargetsUIHandler() override
;
163 // DevToolsTargetsUIHandler overrides.
164 void ForceUpdate() override
;
167 // content::NotificationObserver overrides.
168 void Observe(int type
,
169 const content::NotificationSource
& source
,
170 const content::NotificationDetails
& details
) override
;
172 void ScheduleUpdate();
173 void UpdateTargets();
174 void SendTargets(const std::vector
<DevToolsTargetImpl
*>& targets
);
176 content::NotificationRegistrar notification_registrar_
;
177 scoped_ptr
<CancelableTimer
> timer_
;
178 scoped_refptr
<WorkerObserver
> observer_
;
179 base::WeakPtrFactory
<LocalTargetsUIHandler
> weak_factory_
;
182 LocalTargetsUIHandler::LocalTargetsUIHandler(
183 const Callback
& callback
)
184 : DevToolsTargetsUIHandler(kTargetSourceLocal
, callback
),
185 observer_(new WorkerObserver()),
186 weak_factory_(this) {
187 notification_registrar_
.Add(this,
188 content::NOTIFICATION_WEB_CONTENTS_CONNECTED
,
189 content::NotificationService::AllSources());
190 notification_registrar_
.Add(this,
191 content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED
,
192 content::NotificationService::AllSources());
193 notification_registrar_
.Add(this,
194 content::NOTIFICATION_WEB_CONTENTS_DESTROYED
,
195 content::NotificationService::AllSources());
196 observer_
->Start(base::Bind(&LocalTargetsUIHandler::ScheduleUpdate
,
197 base::Unretained(this)));
201 LocalTargetsUIHandler::~LocalTargetsUIHandler() {
202 notification_registrar_
.RemoveAll();
206 void LocalTargetsUIHandler::Observe(
208 const content::NotificationSource
& source
,
209 const content::NotificationDetails
& details
) {
213 void LocalTargetsUIHandler::ForceUpdate() {
217 void LocalTargetsUIHandler::ScheduleUpdate() {
218 const int kUpdateDelay
= 100;
221 base::Bind(&LocalTargetsUIHandler::UpdateTargets
,
222 base::Unretained(this)),
223 base::TimeDelta::FromMilliseconds(kUpdateDelay
)));
226 void LocalTargetsUIHandler::UpdateTargets() {
227 SendTargets(DevToolsTargetImpl::EnumerateAll());
230 void LocalTargetsUIHandler::SendTargets(
231 const std::vector
<DevToolsTargetImpl
*>& targets
) {
232 base::ListValue list_value
;
233 std::map
<std::string
, base::DictionaryValue
*> id_to_descriptor
;
235 STLDeleteValues(&targets_
);
236 for (DevToolsTargetImpl
* target
: targets
) {
237 targets_
[target
->GetId()] = target
;
238 id_to_descriptor
[target
->GetId()] = Serialize(*target
);
241 for (TargetMap::iterator
it(targets_
.begin()); it
!= targets_
.end(); ++it
) {
242 DevToolsTargetImpl
* target
= it
->second
;
243 base::DictionaryValue
* descriptor
= id_to_descriptor
[target
->GetId()];
244 std::string parent_id
= target
->GetParentId();
245 if (parent_id
.empty() || id_to_descriptor
.count(parent_id
) == 0) {
246 list_value
.Append(descriptor
);
248 base::DictionaryValue
* parent
= id_to_descriptor
[parent_id
];
249 base::ListValue
* guests
= NULL
;
250 if (!parent
->GetList(kGuestList
, &guests
)) {
251 guests
= new base::ListValue();
252 parent
->Set(kGuestList
, guests
);
254 guests
->Append(descriptor
);
258 SendSerializedTargets(list_value
);
261 // AdbTargetsUIHandler --------------------------------------------------------
263 class AdbTargetsUIHandler
264 : public DevToolsTargetsUIHandler
,
265 public DevToolsAndroidBridge::DeviceListListener
{
267 AdbTargetsUIHandler(const Callback
& callback
, Profile
* profile
);
268 ~AdbTargetsUIHandler() override
;
270 void Open(const std::string
& browser_id
, const std::string
& url
) override
;
272 scoped_refptr
<content::DevToolsAgentHost
> GetBrowserAgentHost(
273 const std::string
& browser_id
) override
;
276 // DevToolsAndroidBridge::Listener overrides.
277 void DeviceListChanged(
278 const DevToolsAndroidBridge::RemoteDevices
& devices
) override
;
280 DevToolsAndroidBridge
* GetAndroidBridge();
282 Profile
* const profile_
;
283 DevToolsAndroidBridge
* const android_bridge_
;
285 typedef std::map
<std::string
,
286 scoped_refptr
<DevToolsAndroidBridge::RemoteBrowser
> > RemoteBrowsers
;
287 RemoteBrowsers remote_browsers_
;
290 AdbTargetsUIHandler::AdbTargetsUIHandler(const Callback
& callback
,
292 : DevToolsTargetsUIHandler(kTargetSourceRemote
, callback
),
295 DevToolsAndroidBridge::Factory::GetForProfile(profile_
)) {
296 DCHECK(android_bridge_
);
297 android_bridge_
->AddDeviceListListener(this);
300 AdbTargetsUIHandler::~AdbTargetsUIHandler() {
301 android_bridge_
->RemoveDeviceListListener(this);
304 void AdbTargetsUIHandler::Open(const std::string
& browser_id
,
305 const std::string
& url
) {
306 RemoteBrowsers::iterator it
= remote_browsers_
.find(browser_id
);
307 if (it
!= remote_browsers_
.end())
308 android_bridge_
->OpenRemotePage(it
->second
, url
);
311 scoped_refptr
<content::DevToolsAgentHost
>
312 AdbTargetsUIHandler::GetBrowserAgentHost(
313 const std::string
& browser_id
) {
314 RemoteBrowsers::iterator it
= remote_browsers_
.find(browser_id
);
315 if (it
== remote_browsers_
.end())
318 return android_bridge_
->GetBrowserAgentHost(it
->second
);
321 void AdbTargetsUIHandler::DeviceListChanged(
322 const DevToolsAndroidBridge::RemoteDevices
& devices
) {
323 remote_browsers_
.clear();
324 STLDeleteValues(&targets_
);
326 base::ListValue device_list
;
327 for (DevToolsAndroidBridge::RemoteDevices::const_iterator dit
=
328 devices
.begin(); dit
!= devices
.end(); ++dit
) {
329 DevToolsAndroidBridge::RemoteDevice
* device
= dit
->get();
330 base::DictionaryValue
* device_data
= new base::DictionaryValue();
331 device_data
->SetString(kAdbModelField
, device
->model());
332 device_data
->SetString(kAdbSerialField
, device
->serial());
333 device_data
->SetBoolean(kAdbConnectedField
, device
->is_connected());
334 std::string device_id
= base::StringPrintf(
336 device
->serial().c_str());
337 device_data
->SetString(kTargetIdField
, device_id
);
338 base::ListValue
* browser_list
= new base::ListValue();
339 device_data
->Set(kAdbBrowsersList
, browser_list
);
341 DevToolsAndroidBridge::RemoteBrowsers
& browsers
= device
->browsers();
342 for (DevToolsAndroidBridge::RemoteBrowsers::iterator bit
=
343 browsers
.begin(); bit
!= browsers
.end(); ++bit
) {
344 DevToolsAndroidBridge::RemoteBrowser
* browser
= bit
->get();
345 base::DictionaryValue
* browser_data
= new base::DictionaryValue();
346 browser_data
->SetString(kAdbBrowserNameField
, browser
->display_name());
347 browser_data
->SetString(kAdbBrowserUserField
, browser
->user());
348 browser_data
->SetString(kAdbBrowserVersionField
, browser
->version());
349 DevToolsAndroidBridge::RemoteBrowser::ParsedVersion parsed
=
350 browser
->GetParsedVersion();
351 browser_data
->SetInteger(
352 kAdbBrowserChromeVersionField
,
353 browser
->IsChrome() && !parsed
.empty() ? parsed
[0] : 0);
354 std::string browser_id
= browser
->GetId();
355 browser_data
->SetString(kTargetIdField
, browser_id
);
356 browser_data
->SetString(kTargetSourceField
, source_id());
358 base::ListValue
* page_list
= new base::ListValue();
359 remote_browsers_
[browser_id
] = browser
;
360 browser_data
->Set(kAdbPagesList
, page_list
);
361 for (const auto& page
: browser
->pages()) {
362 DevToolsTargetImpl
* target
= android_bridge_
->CreatePageTarget(page
);
363 base::DictionaryValue
* target_data
= Serialize(*target
);
364 target_data
->SetBoolean(
365 kAdbAttachedForeignField
,
366 target
->IsAttached() &&
367 !android_bridge_
->HasDevToolsWindow(target
->GetId()));
368 // Pass the screen size in the target object to make sure that
369 // the caching logic does not prevent the target item from updating
370 // when the screen size changes.
371 gfx::Size screen_size
= device
->screen_size();
372 target_data
->SetInteger(kAdbScreenWidthField
, screen_size
.width());
373 target_data
->SetInteger(kAdbScreenHeightField
, screen_size
.height());
374 targets_
[target
->GetId()] = target
;
375 page_list
->Append(target_data
);
377 browser_list
->Append(browser_data
);
380 device_list
.Append(device_data
);
382 SendSerializedTargets(device_list
);
387 // DevToolsTargetsUIHandler ---------------------------------------------------
389 DevToolsTargetsUIHandler::DevToolsTargetsUIHandler(
390 const std::string
& source_id
,
391 const Callback
& callback
)
392 : source_id_(source_id
),
393 callback_(callback
) {
396 DevToolsTargetsUIHandler::~DevToolsTargetsUIHandler() {
397 STLDeleteValues(&targets_
);
401 scoped_ptr
<DevToolsTargetsUIHandler
>
402 DevToolsTargetsUIHandler::CreateForLocal(
403 const DevToolsTargetsUIHandler::Callback
& callback
) {
404 return scoped_ptr
<DevToolsTargetsUIHandler
>(
405 new LocalTargetsUIHandler(callback
));
409 scoped_ptr
<DevToolsTargetsUIHandler
>
410 DevToolsTargetsUIHandler::CreateForAdb(
411 const DevToolsTargetsUIHandler::Callback
& callback
, Profile
* profile
) {
412 return scoped_ptr
<DevToolsTargetsUIHandler
>(
413 new AdbTargetsUIHandler(callback
, profile
));
416 DevToolsTargetImpl
* DevToolsTargetsUIHandler::GetTarget(
417 const std::string
& target_id
) {
418 TargetMap::iterator it
= targets_
.find(target_id
);
419 if (it
!= targets_
.end())
424 void DevToolsTargetsUIHandler::Open(const std::string
& browser_id
,
425 const std::string
& url
) {
428 scoped_refptr
<content::DevToolsAgentHost
>
429 DevToolsTargetsUIHandler::GetBrowserAgentHost(const std::string
& browser_id
) {
433 base::DictionaryValue
* DevToolsTargetsUIHandler::Serialize(
434 const DevToolsTargetImpl
& target
) {
435 base::DictionaryValue
* target_data
= new base::DictionaryValue();
436 target_data
->SetString(kTargetSourceField
, source_id_
);
437 target_data
->SetString(kTargetIdField
, target
.GetId());
438 target_data
->SetString(kTargetTypeField
, target
.GetType());
439 target_data
->SetBoolean(kAttachedField
, target
.IsAttached());
440 target_data
->SetString(kUrlField
, target
.GetURL().spec());
441 target_data
->SetString(kNameField
, net::EscapeForHTML(target
.GetTitle()));
442 target_data
->SetString(kFaviconUrlField
, target
.GetFaviconURL().spec());
443 target_data
->SetString(kDescriptionField
, target
.GetDescription());
447 void DevToolsTargetsUIHandler::SendSerializedTargets(
448 const base::ListValue
& list
) {
449 callback_
.Run(source_id_
, list
);
452 void DevToolsTargetsUIHandler::ForceUpdate() {
455 // PortForwardingStatusSerializer ---------------------------------------------
457 PortForwardingStatusSerializer::PortForwardingStatusSerializer(
458 const Callback
& callback
, Profile
* profile
)
459 : callback_(callback
),
461 DevToolsAndroidBridge
* android_bridge
=
462 DevToolsAndroidBridge::Factory::GetForProfile(profile_
);
464 android_bridge
->AddPortForwardingListener(this);
467 PortForwardingStatusSerializer::~PortForwardingStatusSerializer() {
468 DevToolsAndroidBridge
* android_bridge
=
469 DevToolsAndroidBridge::Factory::GetForProfile(profile_
);
471 android_bridge
->RemovePortForwardingListener(this);
474 void PortForwardingStatusSerializer::PortStatusChanged(
475 const ForwardingStatus
& status
) {
476 base::DictionaryValue result
;
477 for (ForwardingStatus::const_iterator sit
= status
.begin();
478 sit
!= status
.end(); ++sit
) {
479 base::DictionaryValue
* port_status_dict
= new base::DictionaryValue();
480 const PortStatusMap
& port_status_map
= sit
->second
;
481 for (PortStatusMap::const_iterator it
= port_status_map
.begin();
482 it
!= port_status_map
.end(); ++it
) {
483 port_status_dict
->SetInteger(base::IntToString(it
->first
), it
->second
);
486 base::DictionaryValue
* device_status_dict
= new base::DictionaryValue();
487 device_status_dict
->Set(kPortForwardingPorts
, port_status_dict
);
488 device_status_dict
->SetString(kPortForwardingBrowserId
,
489 sit
->first
->GetId());
491 std::string device_id
= base::StringPrintf(
493 sit
->first
->serial().c_str());
494 result
.Set(device_id
, device_status_dict
);
496 callback_
.Run(result
);