inspect_ui.cc revision 424c4d7b64af9d0d8fd9624f381f469654d5e3d2
1// Copyright (c) 2012 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/ui/webui/inspect_ui.h"
6
7#include <set>
8
9#include "base/bind.h"
10#include "base/bind_helpers.h"
11#include "base/json/json_writer.h"
12#include "base/memory/ref_counted_memory.h"
13#include "base/strings/string_number_conversions.h"
14#include "base/strings/string_util.h"
15#include "base/strings/stringprintf.h"
16#include "base/strings/utf_string_conversions.h"
17#include "base/values.h"
18#include "chrome/browser/devtools/devtools_window.h"
19#include "chrome/browser/extensions/extension_service.h"
20#include "chrome/browser/profiles/profile.h"
21#include "chrome/browser/ui/tab_contents/tab_contents_iterator.h"
22#include "chrome/browser/ui/webui/theme_source.h"
23#include "chrome/common/pref_names.h"
24#include "chrome/common/url_constants.h"
25#include "content/public/browser/browser_child_process_observer.h"
26#include "content/public/browser/browser_thread.h"
27#include "content/public/browser/child_process_data.h"
28#include "content/public/browser/devtools_agent_host.h"
29#include "content/public/browser/devtools_client_host.h"
30#include "content/public/browser/devtools_manager.h"
31#include "content/public/browser/favicon_status.h"
32#include "content/public/browser/navigation_entry.h"
33#include "content/public/browser/notification_service.h"
34#include "content/public/browser/notification_source.h"
35#include "content/public/browser/notification_types.h"
36#include "content/public/browser/render_process_host.h"
37#include "content/public/browser/render_view_host.h"
38#include "content/public/browser/web_contents.h"
39#include "content/public/browser/web_ui.h"
40#include "content/public/browser/web_ui_data_source.h"
41#include "content/public/browser/web_ui_message_handler.h"
42#include "content/public/browser/worker_service.h"
43#include "content/public/browser/worker_service_observer.h"
44#include "content/public/common/process_type.h"
45#include "grit/browser_resources.h"
46#include "grit/generated_resources.h"
47#include "net/base/escape.h"
48#include "net/base/net_errors.h"
49#include "ui/base/resource/resource_bundle.h"
50
51using content::BrowserThread;
52using content::ChildProcessData;
53using content::DevToolsAgentHost;
54using content::DevToolsClientHost;
55using content::DevToolsManager;
56using content::RenderProcessHost;
57using content::RenderViewHost;
58using content::RenderViewHostDelegate;
59using content::RenderWidgetHost;
60using content::WebContents;
61using content::WebUIMessageHandler;
62using content::WorkerService;
63using content::WorkerServiceObserver;
64
65namespace {
66
67static const char kDataFile[] = "targets-data.json";
68static const char kAdbPages[] = "adb-pages";
69
70static const char kAppTargetType[] = "app";
71static const char kExtensionTargetType[]  = "extension";
72static const char kPageTargetType[]  = "page";
73static const char kWorkerTargetType[]  = "worker";
74static const char kAdbTargetType[]  = "adb_page";
75
76static const char kInitUICommand[]  = "init-ui";
77static const char kInspectCommand[]  = "inspect";
78static const char kActivateCommand[]  = "activate";
79static const char kTerminateCommand[]  = "terminate";
80static const char kReloadCommand[]  = "reload";
81static const char kOpenCommand[]  = "open";
82
83static const char kPortForwardingEnabledCommand[] =
84    "set-port-forwarding-enabled";
85static const char kPortForwardingConfigCommand[] = "set-port-forwarding-config";
86
87static const char kTargetTypeField[]  = "type";
88static const char kAttachedField[]  = "attached";
89static const char kProcessIdField[]  = "processId";
90static const char kRouteIdField[]  = "routeId";
91static const char kUrlField[]  = "url";
92static const char kNameField[]  = "name";
93static const char kFaviconUrlField[] = "faviconUrl";
94static const char kDescription[] = "description";
95static const char kPidField[]  = "pid";
96static const char kAdbSerialField[] = "adbSerial";
97static const char kAdbModelField[] = "adbModel";
98static const char kAdbBrowserProductField[] = "adbBrowserProduct";
99static const char kAdbBrowserPackageField[] = "adbBrowserPackage";
100static const char kAdbBrowserVersionField[] = "adbBrowserVersion";
101static const char kAdbGlobalIdField[] = "adbGlobalId";
102static const char kAdbBrowsersField[] = "browsers";
103static const char kAdbPagesField[] = "pages";
104static const char kAdbPortStatus[] = "adbPortStatus";
105
106DictionaryValue* BuildTargetDescriptor(
107    const std::string& target_type,
108    bool attached,
109    const GURL& url,
110    const std::string& name,
111    const GURL& favicon_url,
112    const std::string& description,
113    int process_id,
114    int route_id,
115    base::ProcessHandle handle = base::kNullProcessHandle) {
116  DictionaryValue* target_data = new DictionaryValue();
117  target_data->SetString(kTargetTypeField, target_type);
118  target_data->SetBoolean(kAttachedField, attached);
119  target_data->SetInteger(kProcessIdField, process_id);
120  target_data->SetInteger(kRouteIdField, route_id);
121  target_data->SetString(kUrlField, url.spec());
122  target_data->SetString(kNameField, net::EscapeForHTML(name));
123  target_data->SetInteger(kPidField, base::GetProcId(handle));
124  target_data->SetString(kFaviconUrlField, favicon_url.spec());
125  target_data->SetString(kDescription, description);
126
127  return target_data;
128}
129
130bool HasClientHost(RenderViewHost* rvh) {
131  if (!DevToolsAgentHost::HasFor(rvh))
132    return false;
133
134  scoped_refptr<DevToolsAgentHost> agent(
135      DevToolsAgentHost::GetOrCreateFor(rvh));
136  return agent->IsAttached();
137}
138
139bool HasClientHost(int process_id, int route_id) {
140  if (!DevToolsAgentHost::HasForWorker(process_id, route_id))
141    return false;
142
143  scoped_refptr<DevToolsAgentHost> agent(
144      DevToolsAgentHost::GetForWorker(process_id, route_id));
145  return agent->IsAttached();
146}
147
148DictionaryValue* BuildTargetDescriptor(RenderViewHost* rvh, bool is_tab) {
149  WebContents* web_contents = WebContents::FromRenderViewHost(rvh);
150  std::string title;
151  std::string target_type = is_tab ? kPageTargetType : "";
152  GURL url;
153  GURL favicon_url;
154  if (web_contents) {
155    url = web_contents->GetURL();
156    title = UTF16ToUTF8(web_contents->GetTitle());
157    content::NavigationController& controller = web_contents->GetController();
158    content::NavigationEntry* entry = controller.GetActiveEntry();
159    if (entry != NULL && entry->GetURL().is_valid())
160      favicon_url = entry->GetFavicon().url;
161
162    Profile* profile = Profile::FromBrowserContext(
163        web_contents->GetBrowserContext());
164    if (profile) {
165      ExtensionService* extension_service = profile->GetExtensionService();
166      const extensions::Extension* extension = extension_service->
167          extensions()->GetByID(url.host());
168      if (extension) {
169        if (extension->is_hosted_app()
170            || extension->is_legacy_packaged_app()
171            || extension->is_platform_app())
172          target_type = kAppTargetType;
173        else
174          target_type = kExtensionTargetType;
175        title = extension->name();
176      }
177    }
178  }
179
180  return BuildTargetDescriptor(target_type,
181                               HasClientHost(rvh),
182                               url,
183                               title,
184                               favicon_url,
185                               "",
186                               rvh->GetProcess()->GetID(),
187                               rvh->GetRoutingID());
188}
189
190class InspectMessageHandler : public WebUIMessageHandler {
191 public:
192  explicit InspectMessageHandler(InspectUI* inspect_ui)
193      : inspect_ui_(inspect_ui) {}
194  virtual ~InspectMessageHandler() {}
195
196 private:
197  // WebUIMessageHandler implementation.
198  virtual void RegisterMessages() OVERRIDE;
199
200  void HandleInitUICommand(const ListValue* args);
201  void HandleInspectCommand(const ListValue* args);
202  void HandleActivateCommand(const ListValue* args);
203  void HandleTerminateCommand(const ListValue* args);
204  void HandleReloadCommand(const ListValue* args);
205  void HandleOpenCommand(const ListValue* args);
206  void HandlePortForwardingEnabledCommand(const ListValue* args);
207  void HandlePortForwardingConfigCommand(const ListValue* args);
208
209  static bool GetProcessAndRouteId(const ListValue* args,
210                                   int* process_id,
211                                   int* route_id);
212
213  static bool GetRemotePageId(const ListValue* args, std::string* page_id);
214
215  InspectUI* inspect_ui_;
216
217  DISALLOW_COPY_AND_ASSIGN(InspectMessageHandler);
218};
219
220void InspectMessageHandler::RegisterMessages() {
221  web_ui()->RegisterMessageCallback(kInitUICommand,
222      base::Bind(&InspectMessageHandler::HandleInitUICommand,
223                 base::Unretained(this)));
224  web_ui()->RegisterMessageCallback(kInspectCommand,
225      base::Bind(&InspectMessageHandler::HandleInspectCommand,
226                 base::Unretained(this)));
227  web_ui()->RegisterMessageCallback(kActivateCommand,
228      base::Bind(&InspectMessageHandler::HandleActivateCommand,
229                 base::Unretained(this)));
230  web_ui()->RegisterMessageCallback(kTerminateCommand,
231      base::Bind(&InspectMessageHandler::HandleTerminateCommand,
232                 base::Unretained(this)));
233  web_ui()->RegisterMessageCallback(kPortForwardingEnabledCommand,
234      base::Bind(&InspectMessageHandler::HandlePortForwardingEnabledCommand,
235                 base::Unretained(this)));
236  web_ui()->RegisterMessageCallback(kPortForwardingConfigCommand,
237      base::Bind(&InspectMessageHandler::HandlePortForwardingConfigCommand,
238                 base::Unretained(this)));
239  web_ui()->RegisterMessageCallback(kReloadCommand,
240      base::Bind(&InspectMessageHandler::HandleReloadCommand,
241                 base::Unretained(this)));
242  web_ui()->RegisterMessageCallback(kOpenCommand,
243      base::Bind(&InspectMessageHandler::HandleOpenCommand,
244                 base::Unretained(this)));
245}
246
247void InspectMessageHandler::HandleInitUICommand(const ListValue*) {
248  inspect_ui_->InitUI();
249}
250
251void InspectMessageHandler::HandleInspectCommand(const ListValue* args) {
252  Profile* profile = Profile::FromWebUI(web_ui());
253  if (!profile)
254    return;
255
256  std::string page_id;
257  if (GetRemotePageId(args, &page_id)) {
258    inspect_ui_->InspectRemotePage(page_id);
259    return;
260  }
261
262  int process_id;
263  int route_id;
264  if (!GetProcessAndRouteId(args, &process_id, &route_id) || process_id == 0
265      || route_id == 0) {
266    return;
267  }
268
269  RenderViewHost* rvh = RenderViewHost::FromID(process_id, route_id);
270  if (rvh) {
271    DevToolsWindow::OpenDevToolsWindow(rvh);
272    return;
273  }
274
275  scoped_refptr<DevToolsAgentHost> agent_host(
276      DevToolsAgentHost::GetForWorker(process_id, route_id));
277  if (!agent_host.get())
278    return;
279
280  DevToolsWindow::OpenDevToolsWindowForWorker(profile, agent_host.get());
281}
282
283void InspectMessageHandler::HandleActivateCommand(const ListValue* args) {
284  std::string page_id;
285  if (GetRemotePageId(args, &page_id))
286    inspect_ui_->ActivateRemotePage(page_id);
287}
288
289static void TerminateWorker(int process_id, int route_id) {
290  WorkerService::GetInstance()->TerminateWorker(process_id, route_id);
291}
292
293void InspectMessageHandler::HandleTerminateCommand(const ListValue* args) {
294  std::string page_id;
295  if (GetRemotePageId(args, &page_id)) {
296    inspect_ui_->CloseRemotePage(page_id);
297    return;
298  }
299
300  int process_id;
301  int route_id;
302  if (!GetProcessAndRouteId(args, &process_id, &route_id))
303    return;
304
305  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
306      base::Bind(&TerminateWorker, process_id, route_id));
307}
308
309void InspectMessageHandler::HandleReloadCommand(const ListValue* args) {
310  std::string page_id;
311  if (GetRemotePageId(args, &page_id))
312    inspect_ui_->ReloadRemotePage(page_id);
313}
314
315void InspectMessageHandler::HandleOpenCommand(const ListValue* args) {
316  std::string browser_id;
317  std::string url;
318  if (args->GetSize() == 2 &&
319      args->GetString(0, &browser_id) &&
320      args->GetString(1, &url)) {
321    inspect_ui_->OpenRemotePage(browser_id, url);
322  }
323}
324
325bool InspectMessageHandler::GetProcessAndRouteId(const ListValue* args,
326                                                 int* process_id,
327                                                 int* route_id) {
328  const DictionaryValue* data;
329  if (args->GetSize() == 1 && args->GetDictionary(0, &data) &&
330      data->GetInteger(kProcessIdField, process_id) &&
331      data->GetInteger(kRouteIdField, route_id)) {
332    return true;
333  }
334  return false;
335}
336
337void InspectMessageHandler::HandlePortForwardingEnabledCommand(
338    const ListValue* args) {
339  Profile* profile = Profile::FromWebUI(web_ui());
340  if (!profile)
341    return;
342
343  bool enabled;
344  if (args->GetSize() == 1 && args->GetBoolean(0, &enabled)) {
345    profile->GetPrefs()->SetBoolean(
346        prefs::kDevToolsPortForwardingEnabled, enabled);
347  }
348}
349
350void InspectMessageHandler::HandlePortForwardingConfigCommand(
351    const ListValue* args) {
352  Profile* profile = Profile::FromWebUI(web_ui());
353  if (!profile)
354    return;
355
356  const DictionaryValue* dict_src;
357  if (args->GetSize() == 1 && args->GetDictionary(0, &dict_src))
358    profile->GetPrefs()->Set(prefs::kDevToolsPortForwardingConfig, *dict_src);
359}
360
361bool InspectMessageHandler::GetRemotePageId(const ListValue* args,
362                                            std::string* page_id) {
363  const DictionaryValue* data;
364  if (args->GetSize() == 1 && args->GetDictionary(0, &data) &&
365      data->GetString(kAdbGlobalIdField, page_id)) {
366    return true;
367  }
368  return false;
369}
370
371}  // namespace
372
373class InspectUI::WorkerCreationDestructionListener
374    : public WorkerServiceObserver,
375      public content::BrowserChildProcessObserver,
376      public base::RefCountedThreadSafe<WorkerCreationDestructionListener> {
377 public:
378  WorkerCreationDestructionListener()
379      : discovery_ui_(NULL) {}
380
381  void Init(InspectUI* workers_ui) {
382    DCHECK(workers_ui);
383    DCHECK(!discovery_ui_);
384    discovery_ui_ = workers_ui;
385    BrowserChildProcessObserver::Add(this);
386    BrowserThread::PostTask(
387        BrowserThread::IO, FROM_HERE,
388        base::Bind(&WorkerCreationDestructionListener::RegisterObserver,
389                   this));
390  }
391
392  void InspectUIDestroyed() {
393    DCHECK(discovery_ui_);
394    discovery_ui_ = NULL;
395    BrowserChildProcessObserver::Remove(this);
396    BrowserThread::PostTask(
397        BrowserThread::IO, FROM_HERE,
398        base::Bind(&WorkerCreationDestructionListener::UnregisterObserver,
399                   this));
400  }
401
402  void UpdateUI() {
403    BrowserThread::PostTask(
404        BrowserThread::IO, FROM_HERE,
405        base::Bind(&WorkerCreationDestructionListener::CollectWorkersData,
406                   this));
407  }
408
409 private:
410  friend class base::RefCountedThreadSafe<WorkerCreationDestructionListener>;
411  virtual ~WorkerCreationDestructionListener() {}
412
413  virtual void WorkerCreated(
414      const GURL& url,
415      const string16& name,
416      int process_id,
417      int route_id) OVERRIDE {
418    CollectWorkersData();
419  }
420
421  virtual void WorkerDestroyed(int process_id, int route_id) OVERRIDE {
422    CollectWorkersData();
423  }
424
425  virtual void BrowserChildProcessHostConnected(
426      const content::ChildProcessData& data) OVERRIDE {
427    if (data.process_type == content::PROCESS_TYPE_WORKER)
428      UpdateUI();
429  }
430
431  virtual void BrowserChildProcessHostDisconnected(
432      const content::ChildProcessData& data) OVERRIDE {
433    if (data.process_type == content::PROCESS_TYPE_WORKER)
434      UpdateUI();
435  }
436
437  void CollectWorkersData() {
438    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
439    BrowserThread::PostTask(
440        BrowserThread::UI, FROM_HERE,
441        base::Bind(&WorkerCreationDestructionListener::PopulateWorkersList,
442                   this, WorkerService::GetInstance()->GetWorkers()));
443  }
444
445  void RegisterObserver() {
446    WorkerService::GetInstance()->AddObserver(this);
447  }
448
449  void UnregisterObserver() {
450    WorkerService::GetInstance()->RemoveObserver(this);
451  }
452
453  void PopulateWorkersList(
454      const std::vector<WorkerService::WorkerInfo>& worker_info) {
455    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
456    if (!discovery_ui_)
457      return;
458
459    ListValue target_list;
460    for (size_t i = 0; i < worker_info.size(); ++i) {
461      if (!worker_info[i].handle)
462        continue;  // Process is still being created.
463      target_list.Append(BuildTargetDescriptor(
464          kWorkerTargetType,
465          HasClientHost(worker_info[i].process_id, worker_info[i].route_id),
466          worker_info[i].url,
467          UTF16ToUTF8(worker_info[i].name),
468          GURL(),
469          "",
470          worker_info[i].process_id,
471          worker_info[i].route_id,
472          worker_info[i].handle));
473    }
474    discovery_ui_->web_ui()->CallJavascriptFunction(
475        "populateWorkersList", target_list);
476  }
477
478  InspectUI* discovery_ui_;
479};
480
481InspectUI::InspectUI(content::WebUI* web_ui)
482    : WebUIController(web_ui) {
483  web_ui->AddMessageHandler(new InspectMessageHandler(this));
484  Profile* profile = Profile::FromWebUI(web_ui);
485  content::WebUIDataSource::Add(profile, CreateInspectUIHTMLSource());
486
487  // Set up the chrome://theme/ source.
488  ThemeSource* theme = new ThemeSource(profile);
489  content::URLDataSource::Add(profile, theme);
490}
491
492InspectUI::~InspectUI() {
493  StopListeningNotifications();
494}
495
496void InspectUI::InitUI() {
497  StartListeningNotifications();
498  PopulateLists();
499  UpdatePortForwardingEnabled();
500  UpdatePortForwardingConfig();
501  observer_->UpdateUI();
502}
503
504void InspectUI::InspectRemotePage(const std::string& id) {
505  RemotePages::iterator it = remote_pages_.find(id);
506  if (it != remote_pages_.end()) {
507    Profile* profile = Profile::FromWebUI(web_ui());
508    it->second->Inspect(profile);
509  }
510}
511
512void InspectUI::ActivateRemotePage(const std::string& id) {
513  RemotePages::iterator it = remote_pages_.find(id);
514  if (it != remote_pages_.end())
515    it->second->Activate();
516}
517
518void InspectUI::ReloadRemotePage(const std::string& id) {
519  RemotePages::iterator it = remote_pages_.find(id);
520  if (it != remote_pages_.end())
521    it->second->Reload();
522}
523
524void InspectUI::CloseRemotePage(const std::string& id) {
525  RemotePages::iterator it = remote_pages_.find(id);
526  if (it != remote_pages_.end())
527    it->second->Close();
528}
529
530void InspectUI::OpenRemotePage(const std::string& browser_id,
531                               const std::string& url) {
532  GURL gurl(url);
533  if (!gurl.is_valid()) {
534    gurl = GURL("http://" + url);
535    if (!gurl.is_valid())
536      return;
537  }
538  RemoteBrowsers::iterator it = remote_browsers_.find(browser_id);
539  if (it != remote_browsers_.end())
540    it->second->Open(gurl.spec());
541}
542
543void InspectUI::PopulateLists() {
544  std::set<RenderViewHost*> tab_rvhs;
545  for (TabContentsIterator it; !it.done(); it.Next())
546    tab_rvhs.insert(it->GetRenderViewHost());
547
548  scoped_ptr<ListValue> target_list(new ListValue());
549
550  std::vector<RenderViewHost*> rvh_vector =
551      DevToolsAgentHost::GetValidRenderViewHosts();
552
553  for (std::vector<RenderViewHost*>::iterator it(rvh_vector.begin());
554       it != rvh_vector.end(); it++) {
555    bool is_tab = tab_rvhs.find(*it) != tab_rvhs.end();
556    target_list->Append(BuildTargetDescriptor(*it, is_tab));
557  }
558  web_ui()->CallJavascriptFunction("populateLists", *target_list.get());
559}
560
561void InspectUI::Observe(int type,
562    const content::NotificationSource& source,
563    const content::NotificationDetails& details) {
564  if (source != content::Source<WebContents>(web_ui()->GetWebContents()))
565    PopulateLists();
566  else if (type == content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED)
567    StopListeningNotifications();
568}
569
570void InspectUI::StartListeningNotifications() {
571  if (observer_)
572    return;
573
574  observer_ = new WorkerCreationDestructionListener();
575  observer_->Init(this);
576
577  Profile* profile = Profile::FromWebUI(web_ui());
578  DevToolsAdbBridge* adb_bridge =
579      DevToolsAdbBridge::Factory::GetForProfile(profile);
580  if (adb_bridge)
581    adb_bridge->AddListener(this);
582
583  notification_registrar_.Add(this,
584                              content::NOTIFICATION_WEB_CONTENTS_CONNECTED,
585                              content::NotificationService::AllSources());
586  notification_registrar_.Add(this,
587                              content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
588                              content::NotificationService::AllSources());
589  notification_registrar_.Add(this,
590                              content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
591                              content::NotificationService::AllSources());
592
593  pref_change_registrar_.Init(profile->GetPrefs());
594  pref_change_registrar_.Add(prefs::kDevToolsPortForwardingEnabled,
595      base::Bind(&InspectUI::UpdatePortForwardingEnabled,
596                 base::Unretained(this)));
597  pref_change_registrar_.Add(prefs::kDevToolsPortForwardingConfig,
598      base::Bind(&InspectUI::UpdatePortForwardingConfig,
599                 base::Unretained(this)));
600}
601
602void InspectUI::StopListeningNotifications()
603{
604  if (!observer_.get())
605    return;
606  Profile* profile = Profile::FromWebUI(web_ui());
607  DevToolsAdbBridge* adb_bridge =
608      DevToolsAdbBridge::Factory::GetForProfile(profile);
609  if (adb_bridge)
610    adb_bridge->RemoveListener(this);
611  observer_->InspectUIDestroyed();
612  observer_ = NULL;
613  notification_registrar_.RemoveAll();
614  pref_change_registrar_.RemoveAll();
615}
616
617content::WebUIDataSource* InspectUI::CreateInspectUIHTMLSource() {
618  content::WebUIDataSource* source =
619      content::WebUIDataSource::Create(chrome::kChromeUIInspectHost);
620  source->AddResourcePath("inspect.css", IDR_INSPECT_CSS);
621  source->AddResourcePath("inspect.js", IDR_INSPECT_JS);
622  source->SetDefaultResource(IDR_INSPECT_HTML);
623  return source;
624}
625
626void InspectUI::RemoteDevicesChanged(
627    DevToolsAdbBridge::RemoteDevices* devices) {
628  remote_browsers_.clear();
629  remote_pages_.clear();
630  ListValue device_list;
631  for (DevToolsAdbBridge::RemoteDevices::iterator dit = devices->begin();
632       dit != devices->end(); ++dit) {
633    DevToolsAdbBridge::RemoteDevice* device = dit->get();
634    DictionaryValue* device_data = new DictionaryValue();
635    device_data->SetString(kAdbModelField, device->model());
636    device_data->SetString(kAdbSerialField, device->serial());
637    std::string device_id = base::StringPrintf(
638        "device:%s",
639        device->serial().c_str());
640    device_data->SetString(kAdbGlobalIdField, device_id);
641    ListValue* browser_list = new ListValue();
642    device_data->Set(kAdbBrowsersField, browser_list);
643
644    DevToolsAdbBridge::RemoteBrowsers& browsers = device->browsers();
645    for (DevToolsAdbBridge::RemoteBrowsers::iterator bit =
646        browsers.begin(); bit != browsers.end(); ++bit) {
647      DevToolsAdbBridge::RemoteBrowser* browser = bit->get();
648      DictionaryValue* browser_data = new DictionaryValue();
649      browser_data->SetString(kAdbBrowserProductField, browser->product());
650      browser_data->SetString(kAdbBrowserPackageField, browser->package());
651      browser_data->SetString(kAdbBrowserVersionField, browser->version());
652      std::string browser_id = base::StringPrintf(
653          "browser:%s:%s:%s",
654          device->serial().c_str(),
655          browser->product().c_str(),  // Force sorting by product name.
656          browser->socket().c_str());
657      browser_data->SetString(kAdbGlobalIdField, browser_id);
658      remote_browsers_[browser_id] = browser;
659      ListValue* page_list = new ListValue();
660      browser_data->Set(kAdbPagesField, page_list);
661
662      DevToolsAdbBridge::RemotePages& pages = browser->pages();
663      for (DevToolsAdbBridge::RemotePages::iterator it =
664          pages.begin(); it != pages.end(); ++it) {
665        DevToolsAdbBridge::RemotePage* page =  it->get();
666        DictionaryValue* page_data = BuildTargetDescriptor(
667            kAdbTargetType, page->attached(),
668            GURL(page->url()), page->title(), GURL(page->favicon_url()),
669            page->description(), 0, 0);
670        std::string page_id = base::StringPrintf("page:%s:%s:%s",
671            device->serial().c_str(),
672            browser->socket().c_str(),
673            page->id().c_str());
674        page_data->SetString(kAdbGlobalIdField, page_id);
675        remote_pages_[page_id] = page;
676        page_list->Append(page_data);
677      }
678      browser_list->Append(browser_data);
679    }
680
681    DictionaryValue* port_status_dict = new DictionaryValue();
682    typedef DevToolsAdbBridge::RemoteDevice::PortStatusMap StatusMap;
683    const StatusMap& port_status = device->port_status();
684    for (StatusMap::const_iterator it = port_status.begin();
685         it != port_status.end(); ++it) {
686      port_status_dict->SetInteger(
687          base::StringPrintf("%d", it->first), it->second);
688    }
689    device_data->Set(kAdbPortStatus, port_status_dict);
690
691    device_list.Append(device_data);
692  }
693  web_ui()->CallJavascriptFunction("populateDeviceLists", device_list);
694}
695
696void InspectUI::UpdatePortForwardingEnabled() {
697  Profile* profile = Profile::FromWebUI(web_ui());
698  const base::Value* value = profile->GetPrefs()->FindPreference(
699      prefs::kDevToolsPortForwardingEnabled)->GetValue();
700  web_ui()->CallJavascriptFunction("updatePortForwardingEnabled", *value);
701
702}
703
704void InspectUI::UpdatePortForwardingConfig() {
705  Profile* profile = Profile::FromWebUI(web_ui());
706  const base::Value* value = profile->GetPrefs()->FindPreference(
707      prefs::kDevToolsPortForwardingConfig)->GetValue();
708  web_ui()->CallJavascriptFunction("updatePortForwardingConfig", *value);
709}
710