inspect_ui.cc revision 0f1bc08d4cfcc34181b0b5cbf065c40f687bf740
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/prefs/pref_service.h"
14#include "base/stl_util.h"
15#include "base/strings/string_number_conversions.h"
16#include "base/strings/string_util.h"
17#include "base/strings/stringprintf.h"
18#include "base/strings/utf_string_conversions.h"
19#include "base/values.h"
20#include "chrome/browser/devtools/devtools_target_impl.h"
21#include "chrome/browser/devtools/port_forwarding_controller.h"
22#include "chrome/browser/profiles/profile.h"
23#include "chrome/browser/ui/browser_navigator.h"
24#include "chrome/browser/ui/singleton_tabs.h"
25#include "chrome/browser/ui/tab_contents/tab_contents_iterator.h"
26#include "chrome/browser/ui/webui/theme_source.h"
27#include "chrome/common/pref_names.h"
28#include "chrome/common/url_constants.h"
29#include "content/public/browser/browser_child_process_observer.h"
30#include "content/public/browser/browser_thread.h"
31#include "content/public/browser/child_process_data.h"
32#include "content/public/browser/devtools_agent_host.h"
33#include "content/public/browser/devtools_client_host.h"
34#include "content/public/browser/devtools_manager.h"
35#include "content/public/browser/notification_service.h"
36#include "content/public/browser/notification_source.h"
37#include "content/public/browser/notification_types.h"
38#include "content/public/browser/render_process_host.h"
39#include "content/public/browser/render_view_host.h"
40#include "content/public/browser/user_metrics.h"
41#include "content/public/browser/web_contents.h"
42#include "content/public/browser/web_ui.h"
43#include "content/public/browser/web_ui_data_source.h"
44#include "content/public/browser/web_ui_message_handler.h"
45#include "content/public/browser/worker_service.h"
46#include "content/public/browser/worker_service_observer.h"
47#include "content/public/common/process_type.h"
48#include "grit/browser_resources.h"
49#include "grit/generated_resources.h"
50#include "net/base/escape.h"
51#include "net/base/net_errors.h"
52#include "ui/base/resource/resource_bundle.h"
53
54using content::BrowserThread;
55using content::ChildProcessData;
56using content::DevToolsAgentHost;
57using content::DevToolsClientHost;
58using content::DevToolsManager;
59using content::RenderProcessHost;
60using content::RenderViewHost;
61using content::RenderViewHostDelegate;
62using content::RenderWidgetHost;
63using content::WebContents;
64using content::WebUIMessageHandler;
65using content::WorkerService;
66using content::WorkerServiceObserver;
67
68namespace {
69
70const char kWorkerTargetType[]  = "worker";
71const char kAdbTargetType[]  = "adb_page";
72
73const char kInitUICommand[]  = "init-ui";
74const char kInspectCommand[]  = "inspect";
75const char kActivateCommand[]  = "activate";
76const char kCloseCommand[]  = "close";
77const char kReloadCommand[]  = "reload";
78const char kOpenCommand[]  = "open";
79
80const char kDiscoverUsbDevicesEnabledCommand[] =
81    "set-discover-usb-devices-enabled";
82const char kPortForwardingEnabledCommand[] =
83    "set-port-forwarding-enabled";
84const char kPortForwardingConfigCommand[] = "set-port-forwarding-config";
85
86const char kPortForwardingDefaultPort[] = "8080";
87const char kPortForwardingDefaultLocation[] = "localhost:8080";
88
89const char kTargetIdField[]  = "id";
90const char kTargetTypeField[]  = "type";
91const char kAttachedField[]  = "attached";
92const char kUrlField[]  = "url";
93const char kNameField[]  = "name";
94const char kFaviconUrlField[] = "faviconUrl";
95const char kDescription[] = "description";
96const char kAdbConnectedField[] = "adbConnected";
97const char kAdbModelField[] = "adbModel";
98const char kAdbSerialField[] = "adbSerial";
99const char kAdbBrowserProductField[] = "adbBrowserProduct";
100const char kAdbBrowserPackageField[] = "adbBrowserPackage";
101const char kAdbBrowserVersionField[] = "adbBrowserVersion";
102const char kAdbGlobalIdField[] = "adbGlobalId";
103const char kAdbBrowsersField[] = "browsers";
104const char kAdbPagesField[] = "pages";
105const char kAdbPortStatus[] = "adbPortStatus";
106const char kAdbScreenWidthField[] = "adbScreenWidth";
107const char kAdbScreenHeightField[] = "adbScreenHeight";
108const char kAdbAttachedForeignField[]  = "adbAttachedForeign";
109const char kGuestList[] = "guests";
110
111DictionaryValue* BuildTargetDescriptor(
112    const DevToolsTargetImpl& target) {
113  DictionaryValue* target_data = new DictionaryValue();
114  target_data->SetString(kTargetIdField, target.GetId());
115  target_data->SetString(kTargetTypeField, target.GetType());
116  target_data->SetBoolean(kAttachedField, target.IsAttached());
117  target_data->SetString(kUrlField, target.GetUrl().spec());
118  target_data->SetString(kNameField, net::EscapeForHTML(target.GetTitle()));
119  target_data->SetString(kFaviconUrlField, target.GetFaviconUrl().spec());
120  target_data->SetString(kDescription, target.GetDescription());
121
122  return target_data;
123}
124
125class InspectMessageHandler : public WebUIMessageHandler {
126 public:
127  explicit InspectMessageHandler(InspectUI* inspect_ui)
128      : inspect_ui_(inspect_ui) {}
129  virtual ~InspectMessageHandler() {}
130
131 private:
132  // WebUIMessageHandler implementation.
133  virtual void RegisterMessages() OVERRIDE;
134
135  void HandleInitUICommand(const ListValue* args);
136  void HandleInspectCommand(const ListValue* args);
137  void HandleActivateCommand(const ListValue* args);
138  void HandleCloseCommand(const ListValue* args);
139  void HandleReloadCommand(const ListValue* args);
140  void HandleOpenCommand(const ListValue* args);
141  void HandleBooleanPrefChanged(const char* pref_name,
142                                const ListValue* args);
143  void HandlePortForwardingConfigCommand(const ListValue* args);
144
145  DevToolsTargetImpl* FindTarget(const ListValue* args);
146
147  InspectUI* inspect_ui_;
148
149  DISALLOW_COPY_AND_ASSIGN(InspectMessageHandler);
150};
151
152void InspectMessageHandler::RegisterMessages() {
153  web_ui()->RegisterMessageCallback(kInitUICommand,
154      base::Bind(&InspectMessageHandler::HandleInitUICommand,
155                 base::Unretained(this)));
156  web_ui()->RegisterMessageCallback(kInspectCommand,
157      base::Bind(&InspectMessageHandler::HandleInspectCommand,
158                 base::Unretained(this)));
159  web_ui()->RegisterMessageCallback(kActivateCommand,
160      base::Bind(&InspectMessageHandler::HandleActivateCommand,
161                 base::Unretained(this)));
162  web_ui()->RegisterMessageCallback(kCloseCommand,
163      base::Bind(&InspectMessageHandler::HandleCloseCommand,
164                 base::Unretained(this)));
165  web_ui()->RegisterMessageCallback(kDiscoverUsbDevicesEnabledCommand,
166      base::Bind(&InspectMessageHandler::HandleBooleanPrefChanged,
167                  base::Unretained(this),
168                  &prefs::kDevToolsDiscoverUsbDevicesEnabled[0]));
169  web_ui()->RegisterMessageCallback(kPortForwardingEnabledCommand,
170      base::Bind(&InspectMessageHandler::HandleBooleanPrefChanged,
171                 base::Unretained(this),
172                 &prefs::kDevToolsPortForwardingEnabled[0]));
173  web_ui()->RegisterMessageCallback(kPortForwardingConfigCommand,
174      base::Bind(&InspectMessageHandler::HandlePortForwardingConfigCommand,
175                 base::Unretained(this)));
176  web_ui()->RegisterMessageCallback(kReloadCommand,
177      base::Bind(&InspectMessageHandler::HandleReloadCommand,
178                 base::Unretained(this)));
179  web_ui()->RegisterMessageCallback(kOpenCommand,
180      base::Bind(&InspectMessageHandler::HandleOpenCommand,
181                 base::Unretained(this)));
182}
183
184void InspectMessageHandler::HandleInitUICommand(const ListValue*) {
185  inspect_ui_->InitUI();
186}
187
188void InspectMessageHandler::HandleInspectCommand(const ListValue* args) {
189  Profile* profile = Profile::FromWebUI(web_ui());
190  if (!profile)
191    return;
192  DevToolsTargetImpl* target = FindTarget(args);
193  if (target)
194    target->Inspect(profile);
195}
196
197void InspectMessageHandler::HandleActivateCommand(const ListValue* args) {
198  DevToolsTargetImpl* target = FindTarget(args);
199  if (target)
200    target->Activate();
201}
202
203void InspectMessageHandler::HandleCloseCommand(const ListValue* args) {
204  DevToolsTargetImpl* target = FindTarget(args);
205  if (target)
206    target->Close();
207}
208
209void InspectMessageHandler::HandleReloadCommand(const ListValue* args) {
210  DevToolsTargetImpl* target = FindTarget(args);
211  if (target)
212    target->Reload();
213}
214
215void InspectMessageHandler::HandleOpenCommand(const ListValue* args) {
216  if (args->GetSize() != 2)
217    return;
218  std::string browser_id;
219  if (!args->GetString(0, &browser_id))
220    return;
221  scoped_refptr<DevToolsAdbBridge::RemoteBrowser> remote_browser =
222      inspect_ui_->FindRemoteBrowser(browser_id);
223  if (!remote_browser)
224    return;
225  std::string url;
226  if (!args->GetString(1, &url))
227    return;
228  GURL gurl(url);
229  if (!gurl.is_valid()) {
230    gurl = GURL("http://" + url);
231    if (!gurl.is_valid())
232     return;
233  }
234  remote_browser->Open(gurl.spec());
235}
236
237DevToolsTargetImpl* InspectMessageHandler::FindTarget(const ListValue* args) {
238  const DictionaryValue* data;
239  std::string type;
240  std::string id;
241  if (args->GetSize() == 1 && args->GetDictionary(0, &data) &&
242      data->GetString(kTargetTypeField, &type) &&
243      data->GetString(kTargetIdField, &id)) {
244    return inspect_ui_->FindTarget(type, id);
245  }
246  return NULL;
247}
248
249void InspectMessageHandler::HandleBooleanPrefChanged(
250    const char* pref_name,
251    const ListValue* args) {
252  Profile* profile = Profile::FromWebUI(web_ui());
253  if (!profile)
254    return;
255
256  bool enabled;
257  if (args->GetSize() == 1 && args->GetBoolean(0, &enabled))
258    profile->GetPrefs()->SetBoolean(pref_name, enabled);
259}
260
261void InspectMessageHandler::HandlePortForwardingConfigCommand(
262    const ListValue* args) {
263  Profile* profile = Profile::FromWebUI(web_ui());
264  if (!profile)
265    return;
266
267  const DictionaryValue* dict_src;
268  if (args->GetSize() == 1 && args->GetDictionary(0, &dict_src))
269    profile->GetPrefs()->Set(prefs::kDevToolsPortForwardingConfig, *dict_src);
270}
271
272}  // namespace
273
274class InspectUI::WorkerCreationDestructionListener
275    : public WorkerServiceObserver,
276      public content::BrowserChildProcessObserver,
277      public base::RefCountedThreadSafe<WorkerCreationDestructionListener> {
278 public:
279  WorkerCreationDestructionListener()
280      : discovery_ui_(NULL) {}
281
282  void Init(InspectUI* workers_ui) {
283    DCHECK(workers_ui);
284    DCHECK(!discovery_ui_);
285    discovery_ui_ = workers_ui;
286    BrowserChildProcessObserver::Add(this);
287    BrowserThread::PostTask(
288        BrowserThread::IO, FROM_HERE,
289        base::Bind(&WorkerCreationDestructionListener::RegisterObserver,
290                   this));
291  }
292
293  void InspectUIDestroyed() {
294    DCHECK(discovery_ui_);
295    discovery_ui_ = NULL;
296    BrowserChildProcessObserver::Remove(this);
297    BrowserThread::PostTask(
298        BrowserThread::IO, FROM_HERE,
299        base::Bind(&WorkerCreationDestructionListener::UnregisterObserver,
300                   this));
301  }
302
303  void UpdateUI() {
304    BrowserThread::PostTask(
305        BrowserThread::IO, FROM_HERE,
306        base::Bind(&WorkerCreationDestructionListener::CollectWorkersData,
307                   this));
308  }
309
310 private:
311  friend class base::RefCountedThreadSafe<WorkerCreationDestructionListener>;
312  virtual ~WorkerCreationDestructionListener() {}
313
314  virtual void WorkerCreated(
315      const GURL& url,
316      const string16& name,
317      int process_id,
318      int route_id) OVERRIDE {
319    CollectWorkersData();
320  }
321
322  virtual void WorkerDestroyed(int process_id, int route_id) OVERRIDE {
323    CollectWorkersData();
324  }
325
326  virtual void BrowserChildProcessHostConnected(
327      const content::ChildProcessData& data) OVERRIDE {
328    if (data.process_type == content::PROCESS_TYPE_WORKER)
329      UpdateUI();
330  }
331
332  virtual void BrowserChildProcessHostDisconnected(
333      const content::ChildProcessData& data) OVERRIDE {
334    if (data.process_type == content::PROCESS_TYPE_WORKER)
335      UpdateUI();
336  }
337
338  void CollectWorkersData() {
339    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
340    DevToolsTargetImpl::EnumerateWorkerTargets(
341        base::Bind(
342            &WorkerCreationDestructionListener::PopulateWorkersList, this));
343  }
344
345  void RegisterObserver() {
346    WorkerService::GetInstance()->AddObserver(this);
347  }
348
349  void UnregisterObserver() {
350    WorkerService::GetInstance()->RemoveObserver(this);
351  }
352
353  void PopulateWorkersList(const DevToolsTargetImpl::List& targets) {
354    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
355    if (discovery_ui_)
356       discovery_ui_->PopulateWorkerTargets(targets);
357  }
358
359  InspectUI* discovery_ui_;
360};
361
362InspectUI::InspectUI(content::WebUI* web_ui)
363    : WebUIController(web_ui) {
364  web_ui->AddMessageHandler(new InspectMessageHandler(this));
365  Profile* profile = Profile::FromWebUI(web_ui);
366  content::WebUIDataSource::Add(profile, CreateInspectUIHTMLSource());
367
368  // Set up the chrome://theme/ source.
369  ThemeSource* theme = new ThemeSource(profile);
370  content::URLDataSource::Add(profile, theme);
371}
372
373InspectUI::~InspectUI() {
374  StopListeningNotifications();
375  STLDeleteValues(&render_view_host_targets_);
376}
377
378void InspectUI::InitUI() {
379  SetPortForwardingDefaults();
380  StartListeningNotifications();
381  PopulateWebContentsTargets();
382  UpdateDiscoverUsbDevicesEnabled();
383  UpdatePortForwardingEnabled();
384  UpdatePortForwardingConfig();
385  observer_->UpdateUI();
386}
387
388DevToolsTargetImpl* InspectUI::FindTarget(const std::string& type,
389                                          const std::string& id) {
390  if (type == kWorkerTargetType) {
391    TargetMap::iterator it = worker_targets_.find(id);
392    return it == worker_targets_.end() ? NULL : it->second;
393  } else if (type == kAdbTargetType) {
394    TargetMap::iterator it = remote_targets_.find(id);
395    return it == remote_targets_.end() ? NULL : it->second;
396  } else {
397    TargetMap::iterator it = render_view_host_targets_.find(id);
398    return it == render_view_host_targets_.end() ? NULL : it->second;
399  }
400}
401
402scoped_refptr<DevToolsAdbBridge::RemoteBrowser>
403InspectUI::FindRemoteBrowser(const std::string& id) {
404  RemoteBrowsers::iterator it = remote_browsers_.find(id);
405  return it == remote_browsers_.end() ? NULL : it->second;
406}
407
408void InspectUI::InspectDevices(Browser* browser) {
409  content::RecordAction(content::UserMetricsAction("InspectDevices"));
410  chrome::NavigateParams params(chrome::GetSingletonTabNavigateParams(
411      browser, GURL(chrome::kChromeUIInspectURL)));
412  params.path_behavior = chrome::NavigateParams::IGNORE_AND_NAVIGATE;
413  ShowSingletonTabOverwritingNTP(browser, params);
414}
415
416void InspectUI::PopulateWebContentsTargets() {
417  ListValue list_value;
418
419  std::map<WebContents*, DictionaryValue*> web_contents_to_descriptor_;
420  std::vector<DevToolsTargetImpl*> guest_targets;
421
422  DevToolsTargetImpl::List targets =
423      DevToolsTargetImpl::EnumerateRenderViewHostTargets();
424
425  STLDeleteValues(&render_view_host_targets_);
426  for (DevToolsTargetImpl::List::iterator it = targets.begin();
427      it != targets.end(); ++it) {
428    scoped_ptr<DevToolsTargetImpl> target(*it);
429    RenderViewHost* rvh = target->GetRenderViewHost();
430    if (!rvh)
431      continue;
432    WebContents* web_contents = WebContents::FromRenderViewHost(rvh);
433    if (!web_contents)
434      continue;
435
436    DevToolsTargetImpl* target_ptr = target.get();
437    render_view_host_targets_[target_ptr->GetId()] = target.release();
438    if (rvh->GetProcess()->IsGuest()) {
439      guest_targets.push_back(target_ptr);
440    } else {
441      DictionaryValue* descriptor = BuildTargetDescriptor(*target_ptr);
442      list_value.Append(descriptor);
443      web_contents_to_descriptor_[web_contents] = descriptor;
444    }
445  }
446
447  // Add the list of guest-views to each of its embedders.
448  for (std::vector<DevToolsTargetImpl*>::iterator it(guest_targets.begin());
449       it != guest_targets.end(); ++it) {
450    DevToolsTargetImpl* guest = (*it);
451    WebContents* guest_web_contents =
452        WebContents::FromRenderViewHost(guest->GetRenderViewHost());
453    WebContents* embedder = guest_web_contents->GetEmbedderWebContents();
454    if (embedder && web_contents_to_descriptor_.count(embedder) > 0) {
455      DictionaryValue* parent = web_contents_to_descriptor_[embedder];
456      ListValue* guests = NULL;
457      if (!parent->GetList(kGuestList, &guests)) {
458        guests = new ListValue();
459        parent->Set(kGuestList, guests);
460      }
461      guests->Append(BuildTargetDescriptor(*guest));
462    }
463  }
464
465  web_ui()->CallJavascriptFunction("populateWebContentsTargets", list_value);
466}
467
468void InspectUI::PopulateWorkerTargets(const DevToolsTargetImpl::List& targets) {
469  ListValue list_value;
470
471  STLDeleteValues(&worker_targets_);
472  for (DevToolsTargetImpl::List::const_iterator it = targets.begin();
473      it != targets.end(); ++it) {
474    DevToolsTargetImpl* target = *it;
475    list_value.Append(BuildTargetDescriptor(*target));
476    worker_targets_[target->GetId()] = target;
477  }
478
479  web_ui()->CallJavascriptFunction("populateWorkerTargets", list_value);
480}
481
482void InspectUI::Observe(int type,
483    const content::NotificationSource& source,
484    const content::NotificationDetails& details) {
485  if (source != content::Source<WebContents>(web_ui()->GetWebContents()))
486    PopulateWebContentsTargets();
487  else if (type == content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED)
488    StopListeningNotifications();
489}
490
491void InspectUI::StartListeningNotifications() {
492  if (observer_)
493    return;
494
495  observer_ = new WorkerCreationDestructionListener();
496  observer_->Init(this);
497
498  Profile* profile = Profile::FromWebUI(web_ui());
499  DevToolsAdbBridge* adb_bridge =
500      DevToolsAdbBridge::Factory::GetForProfile(profile);
501  if (adb_bridge)
502    adb_bridge->AddListener(this);
503
504  notification_registrar_.Add(this,
505                              content::NOTIFICATION_WEB_CONTENTS_CONNECTED,
506                              content::NotificationService::AllSources());
507  notification_registrar_.Add(this,
508                              content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
509                              content::NotificationService::AllSources());
510  notification_registrar_.Add(this,
511                              content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
512                              content::NotificationService::AllSources());
513
514  pref_change_registrar_.Init(profile->GetPrefs());
515  pref_change_registrar_.Add(prefs::kDevToolsDiscoverUsbDevicesEnabled,
516      base::Bind(&InspectUI::UpdateDiscoverUsbDevicesEnabled,
517                 base::Unretained(this)));
518  pref_change_registrar_.Add(prefs::kDevToolsPortForwardingEnabled,
519      base::Bind(&InspectUI::UpdatePortForwardingEnabled,
520                 base::Unretained(this)));
521  pref_change_registrar_.Add(prefs::kDevToolsPortForwardingConfig,
522      base::Bind(&InspectUI::UpdatePortForwardingConfig,
523                 base::Unretained(this)));
524}
525
526void InspectUI::StopListeningNotifications()
527{
528  if (!observer_.get())
529    return;
530  Profile* profile = Profile::FromWebUI(web_ui());
531  DevToolsAdbBridge* adb_bridge =
532      DevToolsAdbBridge::Factory::GetForProfile(profile);
533  if (adb_bridge)
534    adb_bridge->RemoveListener(this);
535  observer_->InspectUIDestroyed();
536  observer_ = NULL;
537  notification_registrar_.RemoveAll();
538  pref_change_registrar_.RemoveAll();
539}
540
541content::WebUIDataSource* InspectUI::CreateInspectUIHTMLSource() {
542  content::WebUIDataSource* source =
543      content::WebUIDataSource::Create(chrome::kChromeUIInspectHost);
544  source->AddResourcePath("inspect.css", IDR_INSPECT_CSS);
545  source->AddResourcePath("inspect.js", IDR_INSPECT_JS);
546  source->SetDefaultResource(IDR_INSPECT_HTML);
547  return source;
548}
549
550void InspectUI::RemoteDevicesChanged(
551    DevToolsAdbBridge::RemoteDevices* devices) {
552  Profile* profile = Profile::FromWebUI(web_ui());
553  PortForwardingController* port_forwarding_controller =
554      PortForwardingController::Factory::GetForProfile(profile);
555  PortForwardingController::DevicesStatus port_forwarding_status;
556  if (port_forwarding_controller)
557    port_forwarding_status =
558        port_forwarding_controller->UpdateDeviceList(*devices);
559
560  remote_browsers_.clear();
561  STLDeleteValues(&remote_targets_);
562  ListValue device_list;
563  for (DevToolsAdbBridge::RemoteDevices::iterator dit = devices->begin();
564       dit != devices->end(); ++dit) {
565    DevToolsAdbBridge::RemoteDevice* device = dit->get();
566    DictionaryValue* device_data = new DictionaryValue();
567    device_data->SetString(kAdbModelField, device->GetModel());
568    device_data->SetString(kAdbSerialField, device->GetSerial());
569    device_data->SetBoolean(kAdbConnectedField, device->IsConnected());
570    std::string device_id = base::StringPrintf(
571        "device:%s",
572        device->GetSerial().c_str());
573    device_data->SetString(kAdbGlobalIdField, device_id);
574    ListValue* browser_list = new ListValue();
575    device_data->Set(kAdbBrowsersField, browser_list);
576
577    DevToolsAdbBridge::RemoteBrowsers& browsers = device->browsers();
578    for (DevToolsAdbBridge::RemoteBrowsers::iterator bit =
579        browsers.begin(); bit != browsers.end(); ++bit) {
580      DevToolsAdbBridge::RemoteBrowser* browser = bit->get();
581      DictionaryValue* browser_data = new DictionaryValue();
582      browser_data->SetString(kAdbBrowserProductField, browser->product());
583      browser_data->SetString(kAdbBrowserPackageField, browser->package());
584      browser_data->SetString(kAdbBrowserVersionField, browser->version());
585      std::string browser_id = base::StringPrintf(
586          "browser:%s:%s:%s",
587          device->GetSerial().c_str(),
588          browser->product().c_str(),  // Force sorting by product name.
589          browser->socket().c_str());
590      browser_data->SetString(kAdbGlobalIdField, browser_id);
591      remote_browsers_[browser_id] = browser;
592      ListValue* page_list = new ListValue();
593      browser_data->Set(kAdbPagesField, page_list);
594
595      DevToolsTargetImpl::List pages = browser->CreatePageTargets();
596      for (DevToolsTargetImpl::List::iterator it =
597          pages.begin(); it != pages.end(); ++it) {
598        DevToolsTargetImpl* page =  *it;
599        DictionaryValue* page_data = BuildTargetDescriptor(*page);
600        page_data->SetBoolean(
601            kAdbAttachedForeignField,
602            page->IsAttached() &&
603                !DevToolsAdbBridge::HasDevToolsWindow(page->GetId()));
604        // Pass the screen size in the page object to make sure that
605        // the caching logic does not prevent the page item from updating
606        // when the screen size changes.
607        gfx::Size screen_size = device->screen_size();
608        page_data->SetInteger(kAdbScreenWidthField, screen_size.width());
609        page_data->SetInteger(kAdbScreenHeightField, screen_size.height());
610        remote_targets_[page->GetId()] = page;
611        page_list->Append(page_data);
612      }
613      browser_list->Append(browser_data);
614    }
615
616    if (port_forwarding_controller) {
617      PortForwardingController::DevicesStatus::iterator sit =
618          port_forwarding_status.find(device->GetSerial());
619      if (sit != port_forwarding_status.end()) {
620        DictionaryValue* port_status_dict = new DictionaryValue();
621        typedef PortForwardingController::PortStatusMap StatusMap;
622        const StatusMap& port_status = sit->second;
623        for (StatusMap::const_iterator it = port_status.begin();
624             it != port_status.end(); ++it) {
625          port_status_dict->SetInteger(
626              base::StringPrintf("%d", it->first), it->second);
627        }
628        device_data->Set(kAdbPortStatus, port_status_dict);
629      }
630    }
631
632    device_list.Append(device_data);
633  }
634  web_ui()->CallJavascriptFunction("populateRemoteTargets", device_list);
635}
636
637void InspectUI::UpdateDiscoverUsbDevicesEnabled() {
638  const Value* value = GetPrefValue(prefs::kDevToolsDiscoverUsbDevicesEnabled);
639  web_ui()->CallJavascriptFunction("updateDiscoverUsbDevicesEnabled", *value);
640
641  // Configure adb bridge.
642  Profile* profile = Profile::FromWebUI(web_ui());
643  DevToolsAdbBridge* adb_bridge =
644      DevToolsAdbBridge::Factory::GetForProfile(profile);
645  if (adb_bridge) {
646    bool enabled = false;
647    value->GetAsBoolean(&enabled);
648
649    DevToolsAdbBridge::DeviceProviders device_providers;
650    device_providers.push_back(AndroidDeviceProvider::GetAdbDeviceProvider());
651
652    if (enabled) {
653      device_providers.push_back(
654          AndroidDeviceProvider::GetUsbDeviceProvider(profile));
655    }
656
657    adb_bridge->set_device_providers(device_providers);
658  }
659}
660
661void InspectUI::UpdatePortForwardingEnabled() {
662  web_ui()->CallJavascriptFunction("updatePortForwardingEnabled",
663      *GetPrefValue(prefs::kDevToolsPortForwardingEnabled));
664
665}
666
667void InspectUI::UpdatePortForwardingConfig() {
668  web_ui()->CallJavascriptFunction("updatePortForwardingConfig",
669      *GetPrefValue(prefs::kDevToolsPortForwardingConfig));
670}
671
672void InspectUI::SetPortForwardingDefaults() {
673  Profile* profile = Profile::FromWebUI(web_ui());
674  PrefService* prefs = profile->GetPrefs();
675
676  bool default_set;
677  if (!GetPrefValue(prefs::kDevToolsPortForwardingDefaultSet)->
678      GetAsBoolean(&default_set) || default_set) {
679    return;
680  }
681
682  // This is the first chrome://inspect invocation on a fresh profile or after
683  // upgrade from a version that did not have kDevToolsPortForwardingDefaultSet.
684  prefs->SetBoolean(prefs::kDevToolsPortForwardingDefaultSet, true);
685
686  bool enabled;
687  const base::DictionaryValue* config;
688  if (!GetPrefValue(prefs::kDevToolsPortForwardingEnabled)->
689        GetAsBoolean(&enabled) ||
690      !GetPrefValue(prefs::kDevToolsPortForwardingConfig)->
691        GetAsDictionary(&config)) {
692    return;
693  }
694
695  // Do nothing if user already took explicit action.
696  if (enabled || config->size() != 0)
697    return;
698
699  base::DictionaryValue default_config;
700  default_config.SetString(
701      kPortForwardingDefaultPort, kPortForwardingDefaultLocation);
702  prefs->Set(prefs::kDevToolsPortForwardingConfig, default_config);
703}
704
705const base::Value* InspectUI::GetPrefValue(const char* name) {
706  Profile* profile = Profile::FromWebUI(web_ui());
707  return profile->GetPrefs()->FindPreference(name)->GetValue();
708}
709