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 kTargetSourceRenderer[] = "renderers";
34 const char kTargetSourceWorker[] = "workers";
35 const char kTargetSourceAdb[] = "adb";
37 const char kTargetIdField[] = "id";
38 const char kTargetTypeField[] = "type";
39 const char kAttachedField[] = "attached";
40 const char kUrlField[] = "url";
41 const char kNameField[] = "name";
42 const char kFaviconUrlField[] = "faviconUrl";
43 const char kDescriptionField[] = "description";
45 const char kGuestList[] = "guests";
47 const char kAdbModelField[] = "adbModel";
48 const char kAdbConnectedField[] = "adbConnected";
49 const char kAdbSerialField[] = "adbSerial";
50 const char kAdbBrowsersList[] = "browsers";
51 const char kAdbDeviceIdFormat[] = "device:%s";
53 const char kAdbBrowserNameField[] = "adbBrowserName";
54 const char kAdbBrowserVersionField[] = "adbBrowserVersion";
55 const char kAdbBrowserChromeVersionField[] = "adbBrowserChromeVersion";
56 const char kCompatibleVersion[] = "compatibleVersion";
57 const char kAdbPagesList[] = "pages";
59 const char kAdbScreenWidthField[] = "adbScreenWidth";
60 const char kAdbScreenHeightField[] = "adbScreenHeight";
61 const char kAdbAttachedForeignField[] = "adbAttachedForeign";
63 // CancelableTimer ------------------------------------------------------------
65 class CancelableTimer {
67 CancelableTimer(base::Closure callback, base::TimeDelta delay)
68 : callback_(callback),
70 base::MessageLoop::current()->PostDelayedTask(
72 base::Bind(&CancelableTimer::Fire, weak_factory_.GetWeakPtr()),
77 void Fire() { callback_.Run(); }
79 base::Closure callback_;
80 base::WeakPtrFactory<CancelableTimer> weak_factory_;
83 // RenderViewHostTargetsUIHandler ---------------------------------------------
85 class RenderViewHostTargetsUIHandler
86 : public DevToolsTargetsUIHandler,
87 public content::NotificationObserver {
89 explicit RenderViewHostTargetsUIHandler(const Callback& callback);
90 virtual ~RenderViewHostTargetsUIHandler();
93 // content::NotificationObserver overrides.
94 virtual void Observe(int type,
95 const content::NotificationSource& source,
96 const content::NotificationDetails& details) OVERRIDE;
100 content::NotificationRegistrar notification_registrar_;
101 scoped_ptr<CancelableTimer> timer_;
104 RenderViewHostTargetsUIHandler::RenderViewHostTargetsUIHandler(
105 const Callback& callback)
106 : DevToolsTargetsUIHandler(kTargetSourceRenderer, callback) {
107 notification_registrar_.Add(this,
108 content::NOTIFICATION_WEB_CONTENTS_CONNECTED,
109 content::NotificationService::AllSources());
110 notification_registrar_.Add(this,
111 content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
112 content::NotificationService::AllSources());
113 notification_registrar_.Add(this,
114 content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
115 content::NotificationService::AllSources());
119 RenderViewHostTargetsUIHandler::~RenderViewHostTargetsUIHandler() {
120 notification_registrar_.RemoveAll();
123 void RenderViewHostTargetsUIHandler::Observe(
125 const content::NotificationSource& source,
126 const content::NotificationDetails& details) {
127 const int kUpdateDelay = 100;
130 base::Bind(&RenderViewHostTargetsUIHandler::UpdateTargets,
131 base::Unretained(this)),
132 base::TimeDelta::FromMilliseconds(kUpdateDelay)));
135 void RenderViewHostTargetsUIHandler::UpdateTargets() {
136 base::ListValue list_value;
138 std::map<std::string, base::DictionaryValue*> id_to_descriptor;
140 DevToolsTargetImpl::List targets =
141 DevToolsTargetImpl::EnumerateWebContentsTargets();
143 STLDeleteValues(&targets_);
144 for (DevToolsTargetImpl::List::iterator it = targets.begin();
145 it != targets.end(); ++it) {
146 DevToolsTargetImpl* target = *it;
147 targets_[target->GetId()] = target;
148 id_to_descriptor[target->GetId()] = Serialize(*target);
151 for (TargetMap::iterator it(targets_.begin()); it != targets_.end(); ++it) {
152 DevToolsTargetImpl* target = it->second;
153 base::DictionaryValue* descriptor = id_to_descriptor[target->GetId()];
155 std::string parent_id = target->GetParentId();
156 if (parent_id.empty() || id_to_descriptor.count(parent_id) == 0) {
157 list_value.Append(descriptor);
159 base::DictionaryValue* parent = id_to_descriptor[parent_id];
160 base::ListValue* guests = NULL;
161 if (!parent->GetList(kGuestList, &guests)) {
162 guests = new base::ListValue();
163 parent->Set(kGuestList, guests);
165 guests->Append(descriptor);
169 SendSerializedTargets(list_value);
172 // WorkerObserver -------------------------------------------------------------
175 : public content::WorkerServiceObserver,
176 public base::RefCountedThreadSafe<WorkerObserver> {
180 void Start(DevToolsTargetImpl::Callback callback) {
181 DCHECK(callback_.is_null());
182 DCHECK(!callback.is_null());
183 callback_ = callback;
184 BrowserThread::PostTask(
185 BrowserThread::IO, FROM_HERE,
186 base::Bind(&WorkerObserver::StartOnIOThread, this));
190 DCHECK(!callback_.is_null());
191 callback_ = DevToolsTargetImpl::Callback();
192 BrowserThread::PostTask(
193 BrowserThread::IO, FROM_HERE,
194 base::Bind(&WorkerObserver::StopOnIOThread, this));
198 BrowserThread::PostTask(
199 BrowserThread::IO, FROM_HERE,
200 base::Bind(&WorkerObserver::EnumerateOnIOThread,
205 friend class base::RefCountedThreadSafe<WorkerObserver>;
206 virtual ~WorkerObserver() {}
208 // content::WorkerServiceObserver overrides:
209 virtual void WorkerCreated(
211 const base::string16& name,
213 int route_id) OVERRIDE {
214 EnumerateOnIOThread();
217 virtual void WorkerDestroyed(int process_id, int route_id) OVERRIDE {
218 EnumerateOnIOThread();
221 void StartOnIOThread() {
222 content::WorkerService::GetInstance()->AddObserver(this);
223 EnumerateOnIOThread();
226 void StopOnIOThread() {
227 content::WorkerService::GetInstance()->RemoveObserver(this);
230 void EnumerateOnIOThread() {
231 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
232 DevToolsTargetImpl::EnumerateWorkerTargets(
233 base::Bind(&WorkerObserver::RespondOnUIThread, this));
236 void RespondOnUIThread(const DevToolsTargetImpl::List& targets) {
237 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
238 if (callback_.is_null())
240 callback_.Run(targets);
243 DevToolsTargetImpl::Callback callback_;
246 // WorkerTargetsUIHandler -----------------------------------------------------
248 class WorkerTargetsUIHandler
249 : public DevToolsTargetsUIHandler {
251 explicit WorkerTargetsUIHandler(const Callback& callback);
252 virtual ~WorkerTargetsUIHandler();
255 void UpdateTargets(const DevToolsTargetImpl::List& targets);
257 scoped_refptr<WorkerObserver> observer_;
260 WorkerTargetsUIHandler::WorkerTargetsUIHandler(const Callback& callback)
261 : DevToolsTargetsUIHandler(kTargetSourceWorker, callback),
262 observer_(new WorkerObserver()) {
263 observer_->Start(base::Bind(&WorkerTargetsUIHandler::UpdateTargets,
264 base::Unretained(this)));
267 WorkerTargetsUIHandler::~WorkerTargetsUIHandler() {
271 void WorkerTargetsUIHandler::UpdateTargets(
272 const DevToolsTargetImpl::List& targets) {
273 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
274 base::ListValue list_value;
275 STLDeleteValues(&targets_);
276 for (DevToolsTargetImpl::List::const_iterator it = targets.begin();
277 it != targets.end(); ++it) {
278 DevToolsTargetImpl* target = *it;
279 list_value.Append(Serialize(*target));
280 targets_[target->GetId()] = target;
282 SendSerializedTargets(list_value);
285 // AdbTargetsUIHandler --------------------------------------------------------
287 class AdbTargetsUIHandler
288 : public DevToolsTargetsUIHandler,
289 public DevToolsAndroidBridge::DeviceListListener {
291 AdbTargetsUIHandler(const Callback& callback, Profile* profile);
292 virtual ~AdbTargetsUIHandler();
294 virtual void Open(const std::string& browser_id,
295 const std::string& url,
296 const DevToolsTargetsUIHandler::TargetCallback&) OVERRIDE;
298 virtual scoped_refptr<content::DevToolsAgentHost> GetBrowserAgentHost(
299 const std::string& browser_id) OVERRIDE;
302 // DevToolsAndroidBridge::Listener overrides.
303 virtual void DeviceListChanged(
304 const DevToolsAndroidBridge::RemoteDevices& devices) OVERRIDE;
308 typedef std::map<std::string,
309 scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> > RemoteBrowsers;
310 RemoteBrowsers remote_browsers_;
313 AdbTargetsUIHandler::AdbTargetsUIHandler(const Callback& callback,
315 : DevToolsTargetsUIHandler(kTargetSourceAdb, callback),
317 DevToolsAndroidBridge* android_bridge =
318 DevToolsAndroidBridge::Factory::GetForProfile(profile_);
320 android_bridge->AddDeviceListListener(this);
323 AdbTargetsUIHandler::~AdbTargetsUIHandler() {
324 DevToolsAndroidBridge* android_bridge =
325 DevToolsAndroidBridge::Factory::GetForProfile(profile_);
327 android_bridge->RemoveDeviceListListener(this);
330 static void CallOnTarget(
331 const DevToolsTargetsUIHandler::TargetCallback& callback,
332 DevToolsAndroidBridge::RemotePage* page) {
333 scoped_ptr<DevToolsAndroidBridge::RemotePage> my_page(page);
334 callback.Run(my_page ? my_page->GetTarget() : NULL);
337 void AdbTargetsUIHandler::Open(
338 const std::string& browser_id,
339 const std::string& url,
340 const DevToolsTargetsUIHandler::TargetCallback& callback) {
341 RemoteBrowsers::iterator it = remote_browsers_.find(browser_id);
342 if (it != remote_browsers_.end())
343 it->second->Open(url, base::Bind(&CallOnTarget, callback));
346 scoped_refptr<content::DevToolsAgentHost>
347 AdbTargetsUIHandler::GetBrowserAgentHost(
348 const std::string& browser_id) {
349 RemoteBrowsers::iterator it = remote_browsers_.find(browser_id);
350 return it != remote_browsers_.end() ? it->second->GetAgentHost() : NULL;
353 void AdbTargetsUIHandler::DeviceListChanged(
354 const DevToolsAndroidBridge::RemoteDevices& devices) {
355 remote_browsers_.clear();
356 STLDeleteValues(&targets_);
358 base::ListValue device_list;
359 for (DevToolsAndroidBridge::RemoteDevices::const_iterator dit =
360 devices.begin(); dit != devices.end(); ++dit) {
361 DevToolsAndroidBridge::RemoteDevice* device = dit->get();
362 base::DictionaryValue* device_data = new base::DictionaryValue();
363 device_data->SetString(kAdbModelField, device->model());
364 device_data->SetString(kAdbSerialField, device->serial());
365 device_data->SetBoolean(kAdbConnectedField, device->is_connected());
366 std::string device_id = base::StringPrintf(
368 device->serial().c_str());
369 device_data->SetString(kTargetIdField, device_id);
370 base::ListValue* browser_list = new base::ListValue();
371 device_data->Set(kAdbBrowsersList, browser_list);
373 DevToolsAndroidBridge::RemoteBrowsers& browsers = device->browsers();
374 for (DevToolsAndroidBridge::RemoteBrowsers::iterator bit =
375 browsers.begin(); bit != browsers.end(); ++bit) {
376 DevToolsAndroidBridge::RemoteBrowser* browser = bit->get();
377 base::DictionaryValue* browser_data = new base::DictionaryValue();
378 browser_data->SetString(kAdbBrowserNameField, browser->display_name());
379 browser_data->SetString(kAdbBrowserVersionField, browser->version());
380 DevToolsAndroidBridge::RemoteBrowser::ParsedVersion parsed =
381 browser->GetParsedVersion();
382 browser_data->SetInteger(
383 kAdbBrowserChromeVersionField,
384 browser->IsChrome() && !parsed.empty() ? parsed[0] : 0);
385 std::string browser_id = base::StringPrintf(
386 "browser:%s:%s:%s:%s",
387 device->serial().c_str(), // Ensure uniqueness across devices.
388 browser->display_name().c_str(), // Sort by display name.
389 browser->version().c_str(), // Then by version.
390 browser->socket().c_str()); // Ensure uniqueness on the device.
391 browser_data->SetString(kTargetIdField, browser_id);
392 browser_data->SetString(kTargetSourceField, source_id());
394 base::Version remote_version;
395 remote_version = base::Version(browser->version());
397 chrome::VersionInfo version_info;
398 base::Version local_version(version_info.Version());
400 browser_data->SetBoolean(kCompatibleVersion,
401 (!remote_version.IsValid()) || (!local_version.IsValid()) ||
402 remote_version.components()[0] <= local_version.components()[0]);
404 base::ListValue* page_list = new base::ListValue();
405 remote_browsers_[browser_id] = browser;
406 browser_data->Set(kAdbPagesList, page_list);
407 std::vector<DevToolsAndroidBridge::RemotePage*> pages =
408 browser->CreatePages();
409 for (std::vector<DevToolsAndroidBridge::RemotePage*>::iterator it =
410 pages.begin(); it != pages.end(); ++it) {
411 DevToolsAndroidBridge::RemotePage* page = *it;
412 DevToolsTargetImpl* target = page->GetTarget();
413 base::DictionaryValue* target_data = Serialize(*target);
414 target_data->SetBoolean(
415 kAdbAttachedForeignField,
416 target->IsAttached() &&
417 !DevToolsAndroidBridge::HasDevToolsWindow(target->GetId()));
418 // Pass the screen size in the target object to make sure that
419 // the caching logic does not prevent the target item from updating
420 // when the screen size changes.
421 gfx::Size screen_size = device->screen_size();
422 target_data->SetInteger(kAdbScreenWidthField, screen_size.width());
423 target_data->SetInteger(kAdbScreenHeightField, screen_size.height());
424 targets_[target->GetId()] = target;
425 page_list->Append(target_data);
427 browser_list->Append(browser_data);
430 device_list.Append(device_data);
432 SendSerializedTargets(device_list);
437 // DevToolsTargetsUIHandler ---------------------------------------------------
439 DevToolsTargetsUIHandler::DevToolsTargetsUIHandler(
440 const std::string& source_id,
441 const Callback& callback)
442 : source_id_(source_id),
443 callback_(callback) {
446 DevToolsTargetsUIHandler::~DevToolsTargetsUIHandler() {
447 STLDeleteValues(&targets_);
451 scoped_ptr<DevToolsTargetsUIHandler>
452 DevToolsTargetsUIHandler::CreateForRenderers(
453 const DevToolsTargetsUIHandler::Callback& callback) {
454 return scoped_ptr<DevToolsTargetsUIHandler>(
455 new RenderViewHostTargetsUIHandler(callback));
459 scoped_ptr<DevToolsTargetsUIHandler>
460 DevToolsTargetsUIHandler::CreateForWorkers(
461 const DevToolsTargetsUIHandler::Callback& callback) {
462 return scoped_ptr<DevToolsTargetsUIHandler>(
463 new WorkerTargetsUIHandler(callback));
467 scoped_ptr<DevToolsTargetsUIHandler>
468 DevToolsTargetsUIHandler::CreateForAdb(
469 const DevToolsTargetsUIHandler::Callback& callback, Profile* profile) {
470 return scoped_ptr<DevToolsTargetsUIHandler>(
471 new AdbTargetsUIHandler(callback, profile));
474 DevToolsTargetImpl* DevToolsTargetsUIHandler::GetTarget(
475 const std::string& target_id) {
476 TargetMap::iterator it = targets_.find(target_id);
477 if (it != targets_.end())
482 void DevToolsTargetsUIHandler::Open(const std::string& browser_id,
483 const std::string& url,
484 const TargetCallback& callback) {
488 scoped_refptr<content::DevToolsAgentHost>
489 DevToolsTargetsUIHandler::GetBrowserAgentHost(const std::string& browser_id) {
493 base::DictionaryValue* DevToolsTargetsUIHandler::Serialize(
494 const DevToolsTargetImpl& target) {
495 base::DictionaryValue* target_data = new base::DictionaryValue();
496 target_data->SetString(kTargetSourceField, source_id_);
497 target_data->SetString(kTargetIdField, target.GetId());
498 target_data->SetString(kTargetTypeField, target.GetType());
499 target_data->SetBoolean(kAttachedField, target.IsAttached());
500 target_data->SetString(kUrlField, target.GetURL().spec());
501 target_data->SetString(kNameField, net::EscapeForHTML(target.GetTitle()));
502 target_data->SetString(kFaviconUrlField, target.GetFaviconURL().spec());
503 target_data->SetString(kDescriptionField, target.GetDescription());
507 void DevToolsTargetsUIHandler::SendSerializedTargets(
508 const base::ListValue& list) {
509 callback_.Run(source_id_, list);
512 // PortForwardingStatusSerializer ---------------------------------------------
514 PortForwardingStatusSerializer::PortForwardingStatusSerializer(
515 const Callback& callback, Profile* profile)
516 : callback_(callback),
518 PortForwardingController* port_forwarding_controller =
519 PortForwardingController::Factory::GetForProfile(profile_);
520 if (port_forwarding_controller)
521 port_forwarding_controller->AddListener(this);
524 PortForwardingStatusSerializer::~PortForwardingStatusSerializer() {
525 PortForwardingController* port_forwarding_controller =
526 PortForwardingController::Factory::GetForProfile(profile_);
527 if (port_forwarding_controller)
528 port_forwarding_controller->RemoveListener(this);
531 void PortForwardingStatusSerializer::PortStatusChanged(
532 const DevicesStatus& status) {
533 base::DictionaryValue result;
534 for (DevicesStatus::const_iterator sit = status.begin();
535 sit != status.end(); ++sit) {
536 base::DictionaryValue* device_status_dict = new base::DictionaryValue();
537 const PortStatusMap& device_status_map = sit->second;
538 for (PortStatusMap::const_iterator it = device_status_map.begin();
539 it != device_status_map.end(); ++it) {
540 device_status_dict->SetInteger(
541 base::StringPrintf("%d", it->first), it->second);
544 std::string device_id = base::StringPrintf(
547 result.Set(device_id, device_status_dict);
549 callback_.Run(result);