• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "chrome/browser/devtools/devtools_targets_ui.h"
6 
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"
27 
28 using content::BrowserThread;
29 
30 namespace {
31 
32 const char kTargetSourceField[]  = "source";
33 const char kTargetSourceRenderer[]  = "renderers";
34 const char kTargetSourceWorker[]  = "workers";
35 const char kTargetSourceAdb[]  = "adb";
36 
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";
44 
45 const char kGuestList[] = "guests";
46 
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";
52 
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";
58 
59 const char kAdbScreenWidthField[] = "adbScreenWidth";
60 const char kAdbScreenHeightField[] = "adbScreenHeight";
61 const char kAdbAttachedForeignField[]  = "adbAttachedForeign";
62 
63 // CancelableTimer ------------------------------------------------------------
64 
65 class CancelableTimer {
66  public:
CancelableTimer(base::Closure callback,base::TimeDelta delay)67   CancelableTimer(base::Closure callback, base::TimeDelta delay)
68       : callback_(callback),
69         weak_factory_(this) {
70     base::MessageLoop::current()->PostDelayedTask(
71         FROM_HERE,
72         base::Bind(&CancelableTimer::Fire, weak_factory_.GetWeakPtr()),
73         delay);
74   }
75 
76  private:
Fire()77   void Fire() { callback_.Run(); }
78 
79   base::Closure callback_;
80   base::WeakPtrFactory<CancelableTimer> weak_factory_;
81 };
82 
83 // RenderViewHostTargetsUIHandler ---------------------------------------------
84 
85 class RenderViewHostTargetsUIHandler
86     : public DevToolsTargetsUIHandler,
87       public content::NotificationObserver {
88  public:
89   explicit RenderViewHostTargetsUIHandler(Callback callback);
90   virtual ~RenderViewHostTargetsUIHandler();
91  private:
92   // content::NotificationObserver overrides.
93   virtual void Observe(int type,
94                        const content::NotificationSource& source,
95                        const content::NotificationDetails& details) OVERRIDE;
96 
97   void UpdateTargets();
98 
99   content::NotificationRegistrar notification_registrar_;
100   scoped_ptr<CancelableTimer> timer_;
101 };
102 
RenderViewHostTargetsUIHandler(Callback callback)103 RenderViewHostTargetsUIHandler::RenderViewHostTargetsUIHandler(
104     Callback callback)
105     : DevToolsTargetsUIHandler(kTargetSourceRenderer, callback) {
106   notification_registrar_.Add(this,
107                               content::NOTIFICATION_WEB_CONTENTS_CONNECTED,
108                               content::NotificationService::AllSources());
109   notification_registrar_.Add(this,
110                               content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
111                               content::NotificationService::AllSources());
112   notification_registrar_.Add(this,
113                               content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
114                               content::NotificationService::AllSources());
115   UpdateTargets();
116 }
117 
~RenderViewHostTargetsUIHandler()118 RenderViewHostTargetsUIHandler::~RenderViewHostTargetsUIHandler() {
119   notification_registrar_.RemoveAll();
120 }
121 
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)122 void RenderViewHostTargetsUIHandler::Observe(
123     int type,
124     const content::NotificationSource& source,
125     const content::NotificationDetails& details) {
126   const int kUpdateDelay = 100;
127   timer_.reset(
128       new CancelableTimer(
129           base::Bind(&RenderViewHostTargetsUIHandler::UpdateTargets,
130                      base::Unretained(this)),
131           base::TimeDelta::FromMilliseconds(kUpdateDelay)));
132 }
133 
UpdateTargets()134 void RenderViewHostTargetsUIHandler::UpdateTargets() {
135   scoped_ptr<base::ListValue> list_value(new base::ListValue());
136 
137   std::map<std::string, base::DictionaryValue*> id_to_descriptor;
138 
139   DevToolsTargetImpl::List targets =
140       DevToolsTargetImpl::EnumerateRenderViewHostTargets();
141 
142   STLDeleteValues(&targets_);
143   for (DevToolsTargetImpl::List::iterator it = targets.begin();
144       it != targets.end(); ++it) {
145     DevToolsTargetImpl* target = *it;
146     targets_[target->GetId()] = target;
147     id_to_descriptor[target->GetId()] = Serialize(*target);
148   }
149 
150   for (TargetMap::iterator it(targets_.begin()); it != targets_.end(); ++it) {
151     DevToolsTargetImpl* target = it->second;
152     base::DictionaryValue* descriptor = id_to_descriptor[target->GetId()];
153 
154     std::string parent_id = target->GetParentId();
155     if (parent_id.empty() || id_to_descriptor.count(parent_id) == 0) {
156       list_value->Append(descriptor);
157     } else {
158       base::DictionaryValue* parent = id_to_descriptor[parent_id];
159       base::ListValue* guests = NULL;
160       if (!parent->GetList(kGuestList, &guests)) {
161         guests = new base::ListValue();
162         parent->Set(kGuestList, guests);
163       }
164       guests->Append(descriptor);
165     }
166   }
167 
168   SendSerializedTargets(list_value.Pass());
169 }
170 
171 // WorkerObserver -------------------------------------------------------------
172 
173 class WorkerObserver
174     : public content::WorkerServiceObserver,
175       public base::RefCountedThreadSafe<WorkerObserver> {
176  public:
WorkerObserver()177   WorkerObserver() {}
178 
Start(DevToolsTargetImpl::Callback callback)179   void Start(DevToolsTargetImpl::Callback callback) {
180     DCHECK(callback_.is_null());
181     DCHECK(!callback.is_null());
182     callback_ = callback;
183     BrowserThread::PostTask(
184         BrowserThread::IO, FROM_HERE,
185         base::Bind(&WorkerObserver::StartOnIOThread, this));
186   }
187 
Stop()188   void Stop() {
189     DCHECK(!callback_.is_null());
190     callback_ = DevToolsTargetImpl::Callback();
191     BrowserThread::PostTask(
192         BrowserThread::IO, FROM_HERE,
193         base::Bind(&WorkerObserver::StopOnIOThread, this));
194   }
195 
Enumerate()196   void Enumerate() {
197     BrowserThread::PostTask(
198         BrowserThread::IO, FROM_HERE,
199         base::Bind(&WorkerObserver::EnumerateOnIOThread,
200                    this));
201   }
202 
203  private:
204   friend class base::RefCountedThreadSafe<WorkerObserver>;
~WorkerObserver()205   virtual ~WorkerObserver() {}
206 
207   // content::WorkerServiceObserver overrides:
WorkerCreated(const GURL & url,const base::string16 & name,int process_id,int route_id)208   virtual void WorkerCreated(
209       const GURL& url,
210       const base::string16& name,
211       int process_id,
212       int route_id) OVERRIDE {
213     EnumerateOnIOThread();
214   }
215 
WorkerDestroyed(int process_id,int route_id)216   virtual void WorkerDestroyed(int process_id, int route_id) OVERRIDE {
217     EnumerateOnIOThread();
218   }
219 
StartOnIOThread()220   void StartOnIOThread() {
221     content::WorkerService::GetInstance()->AddObserver(this);
222     EnumerateOnIOThread();
223   }
224 
StopOnIOThread()225   void StopOnIOThread() {
226     content::WorkerService::GetInstance()->RemoveObserver(this);
227   }
228 
EnumerateOnIOThread()229   void EnumerateOnIOThread() {
230     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
231     DevToolsTargetImpl::EnumerateWorkerTargets(
232         base::Bind(&WorkerObserver::RespondOnUIThread, this));
233   }
234 
RespondOnUIThread(const DevToolsTargetImpl::List & targets)235   void RespondOnUIThread(const DevToolsTargetImpl::List& targets) {
236     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
237     if (callback_.is_null())
238       return;
239     callback_.Run(targets);
240   }
241 
242   DevToolsTargetImpl::Callback callback_;
243 };
244 
245 // WorkerTargetsUIHandler -----------------------------------------------------
246 
247 class WorkerTargetsUIHandler
248     : public DevToolsTargetsUIHandler,
249       public content::BrowserChildProcessObserver {
250  public:
251   explicit WorkerTargetsUIHandler(Callback callback);
252   virtual ~WorkerTargetsUIHandler();
253 
254  private:
255   // content::BrowserChildProcessObserver overrides.
256   virtual void BrowserChildProcessHostConnected(
257       const content::ChildProcessData& data) OVERRIDE;
258   virtual void BrowserChildProcessHostDisconnected(
259       const content::ChildProcessData& data) OVERRIDE;
260 
261   void UpdateTargets(const DevToolsTargetImpl::List& targets);
262 
263   scoped_refptr<WorkerObserver> observer_;
264 };
265 
WorkerTargetsUIHandler(Callback callback)266 WorkerTargetsUIHandler::WorkerTargetsUIHandler(Callback callback)
267     : DevToolsTargetsUIHandler(kTargetSourceWorker, callback),
268       observer_(new WorkerObserver()) {
269   observer_->Start(base::Bind(&WorkerTargetsUIHandler::UpdateTargets,
270                               base::Unretained(this)));
271   BrowserChildProcessObserver::Add(this);
272 }
273 
~WorkerTargetsUIHandler()274 WorkerTargetsUIHandler::~WorkerTargetsUIHandler() {
275   BrowserChildProcessObserver::Remove(this);
276   observer_->Stop();
277 }
278 
BrowserChildProcessHostConnected(const content::ChildProcessData & data)279 void WorkerTargetsUIHandler::BrowserChildProcessHostConnected(
280     const content::ChildProcessData& data) {
281   if (data.process_type == content::PROCESS_TYPE_WORKER)
282     observer_->Enumerate();
283 }
284 
BrowserChildProcessHostDisconnected(const content::ChildProcessData & data)285 void WorkerTargetsUIHandler::BrowserChildProcessHostDisconnected(
286     const content::ChildProcessData& data) {
287   if (data.process_type == content::PROCESS_TYPE_WORKER)
288     observer_->Enumerate();
289 }
290 
UpdateTargets(const DevToolsTargetImpl::List & targets)291 void WorkerTargetsUIHandler::UpdateTargets(
292     const DevToolsTargetImpl::List& targets) {
293   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
294   scoped_ptr<base::ListValue> list_value(new base::ListValue());
295   STLDeleteValues(&targets_);
296   for (DevToolsTargetImpl::List::const_iterator it = targets.begin();
297       it != targets.end(); ++it) {
298     DevToolsTargetImpl* target = *it;
299     list_value->Append(Serialize(*target));
300     targets_[target->GetId()] = target;
301   }
302   SendSerializedTargets(list_value.Pass());
303 }
304 
305 // AdbTargetsUIHandler --------------------------------------------------------
306 
307 class AdbTargetsUIHandler
308     : public DevToolsTargetsUIHandler,
309       public DevToolsAndroidBridge::DeviceListListener {
310  public:
311   AdbTargetsUIHandler(Callback callback, Profile* profile);
312   virtual ~AdbTargetsUIHandler();
313 
314   virtual void Open(const std::string& browser_id,
315                     const std::string& url,
316                     const DevToolsTargetsUIHandler::TargetCallback&) OVERRIDE;
317 
318   virtual scoped_refptr<content::DevToolsAgentHost> GetBrowserAgentHost(
319       const std::string& browser_id) OVERRIDE;
320 
321  private:
322   // DevToolsAndroidBridge::Listener overrides.
323   virtual void DeviceListChanged(
324       const DevToolsAndroidBridge::RemoteDevices& devices) OVERRIDE;
325 
326   Profile* profile_;
327 
328   typedef std::map<std::string,
329       scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> > RemoteBrowsers;
330   RemoteBrowsers remote_browsers_;
331 };
332 
AdbTargetsUIHandler(Callback callback,Profile * profile)333 AdbTargetsUIHandler::AdbTargetsUIHandler(Callback callback, Profile* profile)
334     : DevToolsTargetsUIHandler(kTargetSourceAdb, callback),
335       profile_(profile) {
336   DevToolsAndroidBridge* android_bridge =
337       DevToolsAndroidBridge::Factory::GetForProfile(profile_);
338   if (android_bridge)
339     android_bridge->AddDeviceListListener(this);
340 }
341 
~AdbTargetsUIHandler()342 AdbTargetsUIHandler::~AdbTargetsUIHandler() {
343   DevToolsAndroidBridge* android_bridge =
344       DevToolsAndroidBridge::Factory::GetForProfile(profile_);
345   if (android_bridge)
346     android_bridge->RemoveDeviceListListener(this);
347 }
348 
CallOnTarget(const DevToolsTargetsUIHandler::TargetCallback & callback,DevToolsAndroidBridge::RemotePage * page)349 static void CallOnTarget(
350     const DevToolsTargetsUIHandler::TargetCallback& callback,
351     DevToolsAndroidBridge::RemotePage* page) {
352   scoped_ptr<DevToolsAndroidBridge::RemotePage> my_page(page);
353   callback.Run(my_page ? my_page->GetTarget() : NULL);
354 }
355 
Open(const std::string & browser_id,const std::string & url,const DevToolsTargetsUIHandler::TargetCallback & callback)356 void AdbTargetsUIHandler::Open(
357     const std::string& browser_id,
358     const std::string& url,
359     const DevToolsTargetsUIHandler::TargetCallback& callback) {
360   RemoteBrowsers::iterator it = remote_browsers_.find(browser_id);
361   if (it !=  remote_browsers_.end())
362     it->second->Open(url, base::Bind(&CallOnTarget, callback));
363 }
364 
365 scoped_refptr<content::DevToolsAgentHost>
GetBrowserAgentHost(const std::string & browser_id)366 AdbTargetsUIHandler::GetBrowserAgentHost(
367     const std::string& browser_id) {
368   RemoteBrowsers::iterator it = remote_browsers_.find(browser_id);
369   return it != remote_browsers_.end() ? it->second->GetAgentHost() : NULL;
370 }
371 
DeviceListChanged(const DevToolsAndroidBridge::RemoteDevices & devices)372 void AdbTargetsUIHandler::DeviceListChanged(
373     const DevToolsAndroidBridge::RemoteDevices& devices) {
374   remote_browsers_.clear();
375   STLDeleteValues(&targets_);
376 
377   scoped_ptr<base::ListValue> device_list(new base::ListValue());
378   for (DevToolsAndroidBridge::RemoteDevices::const_iterator dit =
379       devices.begin(); dit != devices.end(); ++dit) {
380     DevToolsAndroidBridge::RemoteDevice* device = dit->get();
381     base::DictionaryValue* device_data = new base::DictionaryValue();
382     device_data->SetString(kAdbModelField, device->model());
383     device_data->SetString(kAdbSerialField, device->serial());
384     device_data->SetBoolean(kAdbConnectedField, device->is_connected());
385     std::string device_id = base::StringPrintf(
386         kAdbDeviceIdFormat,
387         device->serial().c_str());
388     device_data->SetString(kTargetIdField, device_id);
389     base::ListValue* browser_list = new base::ListValue();
390     device_data->Set(kAdbBrowsersList, browser_list);
391 
392     DevToolsAndroidBridge::RemoteBrowsers& browsers = device->browsers();
393     for (DevToolsAndroidBridge::RemoteBrowsers::iterator bit =
394         browsers.begin(); bit != browsers.end(); ++bit) {
395       DevToolsAndroidBridge::RemoteBrowser* browser = bit->get();
396       base::DictionaryValue* browser_data = new base::DictionaryValue();
397       browser_data->SetString(kAdbBrowserNameField, browser->display_name());
398       browser_data->SetString(kAdbBrowserVersionField, browser->version());
399       DevToolsAndroidBridge::RemoteBrowser::ParsedVersion parsed =
400           browser->GetParsedVersion();
401       browser_data->SetInteger(
402           kAdbBrowserChromeVersionField,
403           browser->IsChrome() && !parsed.empty() ? parsed[0] : 0);
404       std::string browser_id = base::StringPrintf(
405           "browser:%s:%s:%s:%s",
406           device->serial().c_str(), // Ensure uniqueness across devices.
407           browser->display_name().c_str(),  // Sort by display name.
408           browser->version().c_str(),  // Then by version.
409           browser->socket().c_str());  // Ensure uniqueness on the device.
410       browser_data->SetString(kTargetIdField, browser_id);
411       browser_data->SetString(kTargetSourceField, source_id());
412 
413       base::Version remote_version;
414       if (browser->IsChrome()) {
415         remote_version = base::Version(browser->version());
416       } else {
417         // Try parse WebView version.
418         std::string version = browser->version();
419         size_t pos = version.find("Chrome/");
420         if (pos != std::string::npos) {
421           remote_version = base::Version(browser->version().substr(pos + 7));
422         }
423       }
424       chrome::VersionInfo version_info;
425       base::Version local_version(version_info.Version());
426 
427       browser_data->SetBoolean(kCompatibleVersion,
428           (!remote_version.IsValid()) || (!local_version.IsValid()) ||
429           remote_version.components()[0] <= local_version.components()[0]);
430 
431       base::ListValue* page_list = new base::ListValue();
432       remote_browsers_[browser_id] = browser;
433             browser_data->Set(kAdbPagesList, page_list);
434       std::vector<DevToolsAndroidBridge::RemotePage*> pages =
435           browser->CreatePages();
436       for (std::vector<DevToolsAndroidBridge::RemotePage*>::iterator it =
437           pages.begin(); it != pages.end(); ++it) {
438         DevToolsAndroidBridge::RemotePage* page =  *it;
439         DevToolsTargetImpl* target = page->GetTarget();
440         base::DictionaryValue* target_data = Serialize(*target);
441         target_data->SetBoolean(
442             kAdbAttachedForeignField,
443             target->IsAttached() &&
444                 !DevToolsAndroidBridge::HasDevToolsWindow(target->GetId()));
445         // Pass the screen size in the target object to make sure that
446         // the caching logic does not prevent the target item from updating
447         // when the screen size changes.
448         gfx::Size screen_size = device->screen_size();
449         target_data->SetInteger(kAdbScreenWidthField, screen_size.width());
450         target_data->SetInteger(kAdbScreenHeightField, screen_size.height());
451         targets_[target->GetId()] = target;
452         page_list->Append(target_data);
453       }
454       browser_list->Append(browser_data);
455     }
456 
457     device_list->Append(device_data);
458   }
459   SendSerializedTargets(device_list.Pass());
460 }
461 
462 } // namespace
463 
464 // DevToolsTargetsUIHandler ---------------------------------------------------
465 
DevToolsTargetsUIHandler(const std::string & source_id,Callback callback)466 DevToolsTargetsUIHandler::DevToolsTargetsUIHandler(
467     const std::string& source_id,
468     Callback callback)
469     : source_id_(source_id),
470       callback_(callback) {
471 }
472 
~DevToolsTargetsUIHandler()473 DevToolsTargetsUIHandler::~DevToolsTargetsUIHandler() {
474   STLDeleteValues(&targets_);
475 }
476 
477 // static
478 scoped_ptr<DevToolsTargetsUIHandler>
CreateForRenderers(DevToolsTargetsUIHandler::Callback callback)479 DevToolsTargetsUIHandler::CreateForRenderers(
480     DevToolsTargetsUIHandler::Callback callback) {
481   return scoped_ptr<DevToolsTargetsUIHandler>(
482       new RenderViewHostTargetsUIHandler(callback));
483 }
484 
485 // static
486 scoped_ptr<DevToolsTargetsUIHandler>
CreateForWorkers(DevToolsTargetsUIHandler::Callback callback)487 DevToolsTargetsUIHandler::CreateForWorkers(
488     DevToolsTargetsUIHandler::Callback callback) {
489   return scoped_ptr<DevToolsTargetsUIHandler>(
490       new WorkerTargetsUIHandler(callback));
491 }
492 
493 // static
494 scoped_ptr<DevToolsTargetsUIHandler>
CreateForAdb(DevToolsTargetsUIHandler::Callback callback,Profile * profile)495 DevToolsTargetsUIHandler::CreateForAdb(
496     DevToolsTargetsUIHandler::Callback callback, Profile* profile) {
497   return scoped_ptr<DevToolsTargetsUIHandler>(
498       new AdbTargetsUIHandler(callback, profile));
499 }
500 
GetTarget(const std::string & target_id)501 DevToolsTargetImpl* DevToolsTargetsUIHandler::GetTarget(
502     const std::string& target_id) {
503   TargetMap::iterator it = targets_.find(target_id);
504   if (it != targets_.end())
505     return it->second;
506   return NULL;
507 }
508 
Open(const std::string & browser_id,const std::string & url,const TargetCallback & callback)509 void DevToolsTargetsUIHandler::Open(const std::string& browser_id,
510                                     const std::string& url,
511                                     const TargetCallback& callback) {
512   callback.Run(NULL);
513 }
514 
515 scoped_refptr<content::DevToolsAgentHost>
GetBrowserAgentHost(const std::string & browser_id)516 DevToolsTargetsUIHandler::GetBrowserAgentHost(const std::string& browser_id) {
517   return NULL;
518 }
519 
Serialize(const DevToolsTargetImpl & target)520 base::DictionaryValue* DevToolsTargetsUIHandler::Serialize(
521     const DevToolsTargetImpl& target) {
522   base::DictionaryValue* target_data = new base::DictionaryValue();
523   target_data->SetString(kTargetSourceField, source_id_);
524   target_data->SetString(kTargetIdField, target.GetId());
525   target_data->SetString(kTargetTypeField, target.GetType());
526   target_data->SetBoolean(kAttachedField, target.IsAttached());
527   target_data->SetString(kUrlField, target.GetURL().spec());
528   target_data->SetString(kNameField, net::EscapeForHTML(target.GetTitle()));
529   target_data->SetString(kFaviconUrlField, target.GetFaviconURL().spec());
530   target_data->SetString(kDescriptionField, target.GetDescription());
531   return target_data;
532 }
533 
SendSerializedTargets(scoped_ptr<base::ListValue> list)534 void DevToolsTargetsUIHandler::SendSerializedTargets(
535     scoped_ptr<base::ListValue> list) {
536   callback_.Run(source_id_, list.Pass());
537 }
538 
539 // PortForwardingStatusSerializer ---------------------------------------------
540 
PortForwardingStatusSerializer(const Callback & callback,Profile * profile)541 PortForwardingStatusSerializer::PortForwardingStatusSerializer(
542     const Callback& callback, Profile* profile)
543       : callback_(callback),
544         profile_(profile) {
545   PortForwardingController* port_forwarding_controller =
546       PortForwardingController::Factory::GetForProfile(profile_);
547   if (port_forwarding_controller)
548     port_forwarding_controller->AddListener(this);
549 }
550 
~PortForwardingStatusSerializer()551 PortForwardingStatusSerializer::~PortForwardingStatusSerializer() {
552   PortForwardingController* port_forwarding_controller =
553       PortForwardingController::Factory::GetForProfile(profile_);
554   if (port_forwarding_controller)
555     port_forwarding_controller->RemoveListener(this);
556 }
557 
PortStatusChanged(const DevicesStatus & status)558 void PortForwardingStatusSerializer::PortStatusChanged(
559     const DevicesStatus& status) {
560   base::DictionaryValue result;
561   for (DevicesStatus::const_iterator sit = status.begin();
562       sit != status.end(); ++sit) {
563     base::DictionaryValue* device_status_dict = new base::DictionaryValue();
564     const PortStatusMap& device_status_map = sit->second;
565     for (PortStatusMap::const_iterator it = device_status_map.begin();
566          it != device_status_map.end(); ++it) {
567       device_status_dict->SetInteger(
568           base::StringPrintf("%d", it->first), it->second);
569     }
570 
571     std::string device_id = base::StringPrintf(
572         kAdbDeviceIdFormat,
573         sit->first.c_str());
574     result.Set(device_id, device_status_dict);
575   }
576   callback_.Run(result);
577 }
578