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