inspect_ui.cc revision 8bcbed890bc3ce4d7a057a8f32cab53fa534672e
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/devtools/port_forwarding_controller.h"
20#include "chrome/browser/extensions/extension_service.h"
21#include "chrome/browser/profiles/profile.h"
22#include "chrome/browser/ui/browser_navigator.h"
23#include "chrome/browser/ui/singleton_tabs.h"
24#include "chrome/browser/ui/tab_contents/tab_contents_iterator.h"
25#include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
26#include "chrome/browser/ui/webui/theme_source.h"
27#include "chrome/common/extensions/extension_constants.h"
28#include "chrome/common/pref_names.h"
29#include "chrome/common/url_constants.h"
30#include "content/public/browser/browser_child_process_observer.h"
31#include "content/public/browser/browser_thread.h"
32#include "content/public/browser/child_process_data.h"
33#include "content/public/browser/devtools_agent_host.h"
34#include "content/public/browser/devtools_client_host.h"
35#include "content/public/browser/devtools_manager.h"
36#include "content/public/browser/favicon_status.h"
37#include "content/public/browser/navigation_entry.h"
38#include "content/public/browser/notification_service.h"
39#include "content/public/browser/notification_source.h"
40#include "content/public/browser/notification_types.h"
41#include "content/public/browser/render_process_host.h"
42#include "content/public/browser/render_view_host.h"
43#include "content/public/browser/user_metrics.h"
44#include "content/public/browser/web_contents.h"
45#include "content/public/browser/web_ui.h"
46#include "content/public/browser/web_ui_data_source.h"
47#include "content/public/browser/web_ui_message_handler.h"
48#include "content/public/browser/worker_service.h"
49#include "content/public/browser/worker_service_observer.h"
50#include "content/public/common/process_type.h"
51#include "grit/browser_resources.h"
52#include "grit/generated_resources.h"
53#include "net/base/escape.h"
54#include "net/base/net_errors.h"
55#include "ui/base/resource/resource_bundle.h"
56
57using content::BrowserThread;
58using content::ChildProcessData;
59using content::DevToolsAgentHost;
60using content::DevToolsClientHost;
61using content::DevToolsManager;
62using content::RenderProcessHost;
63using content::RenderViewHost;
64using content::RenderViewHostDelegate;
65using content::RenderWidgetHost;
66using content::WebContents;
67using content::WebUIMessageHandler;
68using content::WorkerService;
69using content::WorkerServiceObserver;
70
71namespace {
72
73const char kAppTargetType[] = "app";
74const char kExtensionTargetType[]  = "extension";
75const char kPageTargetType[]  = "page";
76const char kWorkerTargetType[]  = "worker";
77const char kAdbTargetType[]  = "adb_page";
78
79const char kInitUICommand[]  = "init-ui";
80const char kInspectCommand[]  = "inspect";
81const char kActivateCommand[]  = "activate";
82const char kTerminateCommand[]  = "terminate";
83const char kReloadCommand[]  = "reload";
84const char kOpenCommand[]  = "open";
85
86const char kDiscoverUsbDevicesEnabledCommand[] =
87    "set-discover-usb-devices-enabled";
88const char kPortForwardingEnabledCommand[] =
89    "set-port-forwarding-enabled";
90const char kPortForwardingConfigCommand[] = "set-port-forwarding-config";
91
92const char kPortForwardingDefaultPort[] = "8080";
93const char kPortForwardingDefaultLocation[] = "localhost:8080";
94
95const char kTargetTypeField[]  = "type";
96const char kAttachedField[]  = "attached";
97const char kProcessIdField[]  = "processId";
98const char kRouteIdField[]  = "routeId";
99const char kUrlField[]  = "url";
100const char kNameField[]  = "name";
101const char kFaviconUrlField[] = "faviconUrl";
102const char kDescription[] = "description";
103const char kPidField[]  = "pid";
104const char kAdbConnectedField[] = "adbConnected";
105const char kAdbModelField[] = "adbModel";
106const char kAdbSerialField[] = "adbSerial";
107const char kAdbBrowserProductField[] = "adbBrowserProduct";
108const char kAdbBrowserPackageField[] = "adbBrowserPackage";
109const char kAdbBrowserVersionField[] = "adbBrowserVersion";
110const char kAdbGlobalIdField[] = "adbGlobalId";
111const char kAdbBrowsersField[] = "browsers";
112const char kAdbPagesField[] = "pages";
113const char kAdbPortStatus[] = "adbPortStatus";
114const char kAdbScreenWidthField[] = "adbScreenWidth";
115const char kAdbScreenHeightField[] = "adbScreenHeight";
116const char kAdbAttachedForeignField[]  = "adbAttachedForeign";
117const char kGuestList[] = "guests";
118
119DictionaryValue* BuildTargetDescriptor(
120    const std::string& target_type,
121    bool attached,
122    const GURL& url,
123    const std::string& name,
124    const GURL& favicon_url,
125    const std::string& description,
126    int process_id,
127    int route_id,
128    base::ProcessHandle handle = base::kNullProcessHandle) {
129  DictionaryValue* target_data = new DictionaryValue();
130  target_data->SetString(kTargetTypeField, target_type);
131  target_data->SetBoolean(kAttachedField, attached);
132  target_data->SetInteger(kProcessIdField, process_id);
133  target_data->SetInteger(kRouteIdField, route_id);
134  target_data->SetString(kUrlField, url.spec());
135  target_data->SetString(kNameField, net::EscapeForHTML(name));
136  target_data->SetInteger(kPidField, base::GetProcId(handle));
137  target_data->SetString(kFaviconUrlField, favicon_url.spec());
138  target_data->SetString(kDescription, description);
139
140  return target_data;
141}
142
143bool HasClientHost(RenderViewHost* rvh) {
144  if (!DevToolsAgentHost::HasFor(rvh))
145    return false;
146
147  scoped_refptr<DevToolsAgentHost> agent(
148      DevToolsAgentHost::GetOrCreateFor(rvh));
149  return agent->IsAttached();
150}
151
152bool HasClientHost(int process_id, int route_id) {
153  if (!DevToolsAgentHost::HasForWorker(process_id, route_id))
154    return false;
155
156  scoped_refptr<DevToolsAgentHost> agent(
157      DevToolsAgentHost::GetForWorker(process_id, route_id));
158  return agent->IsAttached();
159}
160
161DictionaryValue* BuildTargetDescriptor(RenderViewHost* rvh, bool is_tab) {
162  WebContents* web_contents = WebContents::FromRenderViewHost(rvh);
163  std::string title;
164  std::string target_type = is_tab ? kPageTargetType : "";
165  GURL url;
166  GURL favicon_url;
167  if (web_contents) {
168    url = web_contents->GetURL();
169    title = UTF16ToUTF8(web_contents->GetTitle());
170    content::NavigationController& controller = web_contents->GetController();
171    content::NavigationEntry* entry = controller.GetActiveEntry();
172    if (entry != NULL && entry->GetURL().is_valid())
173      favicon_url = entry->GetFavicon().url;
174
175    Profile* profile = Profile::FromBrowserContext(
176        web_contents->GetBrowserContext());
177    if (profile) {
178      ExtensionService* extension_service = profile->GetExtensionService();
179      const extensions::Extension* extension = extension_service->
180          extensions()->GetByID(url.host());
181      if (extension) {
182        if (extension->is_hosted_app()
183            || extension->is_legacy_packaged_app()
184            || extension->is_platform_app())
185          target_type = kAppTargetType;
186        else
187          target_type = kExtensionTargetType;
188        title = extension->name();
189        favicon_url = extensions::ExtensionIconSource::GetIconURL(
190            extension, extension_misc::EXTENSION_ICON_SMALLISH,
191            ExtensionIconSet::MATCH_BIGGER, false, NULL);
192      }
193    }
194  }
195
196  return BuildTargetDescriptor(target_type,
197                               HasClientHost(rvh),
198                               url,
199                               title,
200                               favicon_url,
201                               "",
202                               rvh->GetProcess()->GetID(),
203                               rvh->GetRoutingID());
204}
205
206class InspectMessageHandler : public WebUIMessageHandler {
207 public:
208  explicit InspectMessageHandler(InspectUI* inspect_ui)
209      : inspect_ui_(inspect_ui) {}
210  virtual ~InspectMessageHandler() {}
211
212 private:
213  // WebUIMessageHandler implementation.
214  virtual void RegisterMessages() OVERRIDE;
215
216  void HandleInitUICommand(const ListValue* args);
217  void HandleInspectCommand(const ListValue* args);
218  void HandleActivateCommand(const ListValue* args);
219  void HandleTerminateCommand(const ListValue* args);
220  void HandleReloadCommand(const ListValue* args);
221  void HandleOpenCommand(const ListValue* args);
222  void HandleBooleanPrefChanged(const char* pref_name,
223                                const ListValue* args);
224  void HandlePortForwardingConfigCommand(const ListValue* args);
225
226  static bool GetProcessAndRouteId(const ListValue* args,
227                                   int* process_id,
228                                   int* route_id);
229
230  static bool GetRemotePageId(const ListValue* args, std::string* page_id);
231
232  InspectUI* inspect_ui_;
233
234  DISALLOW_COPY_AND_ASSIGN(InspectMessageHandler);
235};
236
237void InspectMessageHandler::RegisterMessages() {
238  web_ui()->RegisterMessageCallback(kInitUICommand,
239      base::Bind(&InspectMessageHandler::HandleInitUICommand,
240                 base::Unretained(this)));
241  web_ui()->RegisterMessageCallback(kInspectCommand,
242      base::Bind(&InspectMessageHandler::HandleInspectCommand,
243                 base::Unretained(this)));
244  web_ui()->RegisterMessageCallback(kActivateCommand,
245      base::Bind(&InspectMessageHandler::HandleActivateCommand,
246                 base::Unretained(this)));
247  web_ui()->RegisterMessageCallback(kTerminateCommand,
248      base::Bind(&InspectMessageHandler::HandleTerminateCommand,
249                 base::Unretained(this)));
250  web_ui()->RegisterMessageCallback(kDiscoverUsbDevicesEnabledCommand,
251      base::Bind(&InspectMessageHandler::HandleBooleanPrefChanged,
252                  base::Unretained(this),
253                  &prefs::kDevToolsDiscoverUsbDevicesEnabled[0]));
254  web_ui()->RegisterMessageCallback(kPortForwardingEnabledCommand,
255      base::Bind(&InspectMessageHandler::HandleBooleanPrefChanged,
256                 base::Unretained(this),
257                 &prefs::kDevToolsPortForwardingEnabled[0]));
258  web_ui()->RegisterMessageCallback(kPortForwardingConfigCommand,
259      base::Bind(&InspectMessageHandler::HandlePortForwardingConfigCommand,
260                 base::Unretained(this)));
261  web_ui()->RegisterMessageCallback(kReloadCommand,
262      base::Bind(&InspectMessageHandler::HandleReloadCommand,
263                 base::Unretained(this)));
264  web_ui()->RegisterMessageCallback(kOpenCommand,
265      base::Bind(&InspectMessageHandler::HandleOpenCommand,
266                 base::Unretained(this)));
267}
268
269void InspectMessageHandler::HandleInitUICommand(const ListValue*) {
270  inspect_ui_->InitUI();
271}
272
273void InspectMessageHandler::HandleInspectCommand(const ListValue* args) {
274  Profile* profile = Profile::FromWebUI(web_ui());
275  if (!profile)
276    return;
277
278  std::string page_id;
279  if (GetRemotePageId(args, &page_id)) {
280    inspect_ui_->InspectRemotePage(page_id);
281    return;
282  }
283
284  int process_id;
285  int route_id;
286  if (!GetProcessAndRouteId(args, &process_id, &route_id) || process_id == 0
287      || route_id == 0) {
288    return;
289  }
290
291  RenderViewHost* rvh = RenderViewHost::FromID(process_id, route_id);
292  if (rvh) {
293    DevToolsWindow::OpenDevToolsWindow(rvh);
294    return;
295  }
296
297  scoped_refptr<DevToolsAgentHost> agent_host(
298      DevToolsAgentHost::GetForWorker(process_id, route_id));
299  if (!agent_host.get())
300    return;
301
302  DevToolsWindow::OpenDevToolsWindowForWorker(profile, agent_host.get());
303}
304
305void InspectMessageHandler::HandleActivateCommand(const ListValue* args) {
306  std::string page_id;
307  if (GetRemotePageId(args, &page_id))
308    inspect_ui_->ActivateRemotePage(page_id);
309}
310
311static void TerminateWorker(int process_id, int route_id) {
312  WorkerService::GetInstance()->TerminateWorker(process_id, route_id);
313}
314
315void InspectMessageHandler::HandleTerminateCommand(const ListValue* args) {
316  std::string page_id;
317  if (GetRemotePageId(args, &page_id)) {
318    inspect_ui_->CloseRemotePage(page_id);
319    return;
320  }
321
322  int process_id;
323  int route_id;
324  if (!GetProcessAndRouteId(args, &process_id, &route_id))
325    return;
326
327  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
328      base::Bind(&TerminateWorker, process_id, route_id));
329}
330
331void InspectMessageHandler::HandleReloadCommand(const ListValue* args) {
332  std::string page_id;
333  if (GetRemotePageId(args, &page_id))
334    inspect_ui_->ReloadRemotePage(page_id);
335}
336
337void InspectMessageHandler::HandleOpenCommand(const ListValue* args) {
338  std::string browser_id;
339  std::string url;
340  if (args->GetSize() == 2 &&
341      args->GetString(0, &browser_id) &&
342      args->GetString(1, &url)) {
343    inspect_ui_->OpenRemotePage(browser_id, url);
344  }
345}
346
347bool InspectMessageHandler::GetProcessAndRouteId(const ListValue* args,
348                                                 int* process_id,
349                                                 int* route_id) {
350  const DictionaryValue* data;
351  if (args->GetSize() == 1 && args->GetDictionary(0, &data) &&
352      data->GetInteger(kProcessIdField, process_id) &&
353      data->GetInteger(kRouteIdField, route_id)) {
354    return true;
355  }
356  return false;
357}
358
359void InspectMessageHandler::HandleBooleanPrefChanged(
360    const char* pref_name,
361    const ListValue* args) {
362  Profile* profile = Profile::FromWebUI(web_ui());
363  if (!profile)
364    return;
365
366  bool enabled;
367  if (args->GetSize() == 1 && args->GetBoolean(0, &enabled))
368    profile->GetPrefs()->SetBoolean(pref_name, enabled);
369}
370
371void InspectMessageHandler::HandlePortForwardingConfigCommand(
372    const ListValue* args) {
373  Profile* profile = Profile::FromWebUI(web_ui());
374  if (!profile)
375    return;
376
377  const DictionaryValue* dict_src;
378  if (args->GetSize() == 1 && args->GetDictionary(0, &dict_src))
379    profile->GetPrefs()->Set(prefs::kDevToolsPortForwardingConfig, *dict_src);
380}
381
382bool InspectMessageHandler::GetRemotePageId(const ListValue* args,
383                                            std::string* page_id) {
384  const DictionaryValue* data;
385  if (args->GetSize() == 1 && args->GetDictionary(0, &data) &&
386      data->GetString(kAdbGlobalIdField, page_id)) {
387    return true;
388  }
389  return false;
390}
391
392}  // namespace
393
394class InspectUI::WorkerCreationDestructionListener
395    : public WorkerServiceObserver,
396      public content::BrowserChildProcessObserver,
397      public base::RefCountedThreadSafe<WorkerCreationDestructionListener> {
398 public:
399  WorkerCreationDestructionListener()
400      : discovery_ui_(NULL) {}
401
402  void Init(InspectUI* workers_ui) {
403    DCHECK(workers_ui);
404    DCHECK(!discovery_ui_);
405    discovery_ui_ = workers_ui;
406    BrowserChildProcessObserver::Add(this);
407    BrowserThread::PostTask(
408        BrowserThread::IO, FROM_HERE,
409        base::Bind(&WorkerCreationDestructionListener::RegisterObserver,
410                   this));
411  }
412
413  void InspectUIDestroyed() {
414    DCHECK(discovery_ui_);
415    discovery_ui_ = NULL;
416    BrowserChildProcessObserver::Remove(this);
417    BrowserThread::PostTask(
418        BrowserThread::IO, FROM_HERE,
419        base::Bind(&WorkerCreationDestructionListener::UnregisterObserver,
420                   this));
421  }
422
423  void UpdateUI() {
424    BrowserThread::PostTask(
425        BrowserThread::IO, FROM_HERE,
426        base::Bind(&WorkerCreationDestructionListener::CollectWorkersData,
427                   this));
428  }
429
430 private:
431  friend class base::RefCountedThreadSafe<WorkerCreationDestructionListener>;
432  virtual ~WorkerCreationDestructionListener() {}
433
434  virtual void WorkerCreated(
435      const GURL& url,
436      const string16& name,
437      int process_id,
438      int route_id) OVERRIDE {
439    CollectWorkersData();
440  }
441
442  virtual void WorkerDestroyed(int process_id, int route_id) OVERRIDE {
443    CollectWorkersData();
444  }
445
446  virtual void BrowserChildProcessHostConnected(
447      const content::ChildProcessData& data) OVERRIDE {
448    if (data.process_type == content::PROCESS_TYPE_WORKER)
449      UpdateUI();
450  }
451
452  virtual void BrowserChildProcessHostDisconnected(
453      const content::ChildProcessData& data) OVERRIDE {
454    if (data.process_type == content::PROCESS_TYPE_WORKER)
455      UpdateUI();
456  }
457
458  void CollectWorkersData() {
459    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
460    BrowserThread::PostTask(
461        BrowserThread::UI, FROM_HERE,
462        base::Bind(&WorkerCreationDestructionListener::PopulateWorkersList,
463                   this, WorkerService::GetInstance()->GetWorkers()));
464  }
465
466  void RegisterObserver() {
467    WorkerService::GetInstance()->AddObserver(this);
468  }
469
470  void UnregisterObserver() {
471    WorkerService::GetInstance()->RemoveObserver(this);
472  }
473
474  void PopulateWorkersList(
475      const std::vector<WorkerService::WorkerInfo>& worker_info) {
476    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
477    if (!discovery_ui_)
478      return;
479
480    ListValue target_list;
481    for (size_t i = 0; i < worker_info.size(); ++i) {
482      if (!worker_info[i].handle)
483        continue;  // Process is still being created.
484      target_list.Append(BuildTargetDescriptor(
485          kWorkerTargetType,
486          HasClientHost(worker_info[i].process_id, worker_info[i].route_id),
487          worker_info[i].url,
488          UTF16ToUTF8(worker_info[i].name),
489          GURL(),
490          "",
491          worker_info[i].process_id,
492          worker_info[i].route_id,
493          worker_info[i].handle));
494    }
495    discovery_ui_->web_ui()->CallJavascriptFunction(
496        "populateWorkersList", target_list);
497  }
498
499  InspectUI* discovery_ui_;
500};
501
502InspectUI::InspectUI(content::WebUI* web_ui)
503    : WebUIController(web_ui) {
504  web_ui->AddMessageHandler(new InspectMessageHandler(this));
505  Profile* profile = Profile::FromWebUI(web_ui);
506  content::WebUIDataSource::Add(profile, CreateInspectUIHTMLSource());
507
508  // Set up the chrome://theme/ source.
509  ThemeSource* theme = new ThemeSource(profile);
510  content::URLDataSource::Add(profile, theme);
511}
512
513InspectUI::~InspectUI() {
514  StopListeningNotifications();
515}
516
517void InspectUI::InitUI() {
518  SetPortForwardingDefaults();
519  StartListeningNotifications();
520  PopulateLists();
521  UpdateDiscoverUsbDevicesEnabled();
522  UpdatePortForwardingEnabled();
523  UpdatePortForwardingConfig();
524  observer_->UpdateUI();
525}
526
527void InspectUI::InspectRemotePage(const std::string& id) {
528  RemotePages::iterator it = remote_pages_.find(id);
529  if (it != remote_pages_.end()) {
530    Profile* profile = Profile::FromWebUI(web_ui());
531    it->second->Inspect(profile);
532  }
533}
534
535void InspectUI::ActivateRemotePage(const std::string& id) {
536  RemotePages::iterator it = remote_pages_.find(id);
537  if (it != remote_pages_.end())
538    it->second->Activate();
539}
540
541void InspectUI::ReloadRemotePage(const std::string& id) {
542  RemotePages::iterator it = remote_pages_.find(id);
543  if (it != remote_pages_.end())
544    it->second->Reload();
545}
546
547void InspectUI::CloseRemotePage(const std::string& id) {
548  RemotePages::iterator it = remote_pages_.find(id);
549  if (it != remote_pages_.end())
550    it->second->Close();
551}
552
553void InspectUI::OpenRemotePage(const std::string& browser_id,
554                               const std::string& url) {
555  GURL gurl(url);
556  if (!gurl.is_valid()) {
557    gurl = GURL("http://" + url);
558    if (!gurl.is_valid())
559      return;
560  }
561  RemoteBrowsers::iterator it = remote_browsers_.find(browser_id);
562  if (it != remote_browsers_.end())
563    it->second->Open(gurl.spec());
564}
565
566void InspectUI::InspectDevices(Browser* browser) {
567  content::RecordAction(content::UserMetricsAction("InspectDevices"));
568  chrome::NavigateParams params(chrome::GetSingletonTabNavigateParams(
569      browser, GURL(chrome::kChromeUIInspectURL)));
570  params.path_behavior = chrome::NavigateParams::IGNORE_AND_NAVIGATE;
571  ShowSingletonTabOverwritingNTP(browser, params);
572}
573
574void InspectUI::PopulateLists() {
575  std::set<RenderViewHost*> tab_rvhs;
576  for (TabContentsIterator it; !it.done(); it.Next())
577    tab_rvhs.insert(it->GetRenderViewHost());
578
579  scoped_ptr<ListValue> target_list(new ListValue());
580
581  std::vector<RenderViewHost*> rvh_vector =
582      DevToolsAgentHost::GetValidRenderViewHosts();
583
584  std::map<WebContents*, DictionaryValue*> description_map;
585  std::vector<WebContents*> guest_contents;
586
587  for (std::vector<RenderViewHost*>::iterator it(rvh_vector.begin());
588       it != rvh_vector.end(); it++) {
589    bool is_tab = tab_rvhs.find(*it) != tab_rvhs.end();
590    RenderViewHost* rvh = (*it);
591    WebContents* web_contents = WebContents::FromRenderViewHost(rvh);
592    if (rvh->GetProcess()->IsGuest()) {
593      if (web_contents)
594        guest_contents.push_back(web_contents);
595    } else {
596      DictionaryValue* dictionary = BuildTargetDescriptor(rvh, is_tab);
597      if (web_contents)
598        description_map[web_contents] = dictionary;
599      target_list->Append(dictionary);
600    }
601  }
602
603  // Add the list of guest-views to each of its embedders.
604  for (std::vector<WebContents*>::iterator it(guest_contents.begin());
605       it != guest_contents.end(); ++it) {
606    WebContents* guest = (*it);
607    WebContents* embedder = guest->GetEmbedderWebContents();
608    if (embedder && description_map.count(embedder) > 0) {
609      DictionaryValue* description = description_map[embedder];
610      ListValue* guests = NULL;
611      if (!description->GetList(kGuestList, &guests)) {
612        guests = new ListValue();
613        description->Set(kGuestList, guests);
614      }
615      RenderViewHost* rvh = guest->GetRenderViewHost();
616      if (rvh)
617        guests->Append(BuildTargetDescriptor(rvh, false));
618    }
619  }
620
621  web_ui()->CallJavascriptFunction("populateLists", *target_list.get());
622}
623
624void InspectUI::Observe(int type,
625    const content::NotificationSource& source,
626    const content::NotificationDetails& details) {
627  if (source != content::Source<WebContents>(web_ui()->GetWebContents()))
628    PopulateLists();
629  else if (type == content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED)
630    StopListeningNotifications();
631}
632
633void InspectUI::StartListeningNotifications() {
634  if (observer_)
635    return;
636
637  observer_ = new WorkerCreationDestructionListener();
638  observer_->Init(this);
639
640  Profile* profile = Profile::FromWebUI(web_ui());
641  DevToolsAdbBridge* adb_bridge =
642      DevToolsAdbBridge::Factory::GetForProfile(profile);
643  if (adb_bridge)
644    adb_bridge->AddListener(this);
645
646  notification_registrar_.Add(this,
647                              content::NOTIFICATION_WEB_CONTENTS_CONNECTED,
648                              content::NotificationService::AllSources());
649  notification_registrar_.Add(this,
650                              content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
651                              content::NotificationService::AllSources());
652  notification_registrar_.Add(this,
653                              content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
654                              content::NotificationService::AllSources());
655
656  pref_change_registrar_.Init(profile->GetPrefs());
657  pref_change_registrar_.Add(prefs::kDevToolsDiscoverUsbDevicesEnabled,
658      base::Bind(&InspectUI::UpdateDiscoverUsbDevicesEnabled,
659                 base::Unretained(this)));
660  pref_change_registrar_.Add(prefs::kDevToolsPortForwardingEnabled,
661      base::Bind(&InspectUI::UpdatePortForwardingEnabled,
662                 base::Unretained(this)));
663  pref_change_registrar_.Add(prefs::kDevToolsPortForwardingConfig,
664      base::Bind(&InspectUI::UpdatePortForwardingConfig,
665                 base::Unretained(this)));
666}
667
668void InspectUI::StopListeningNotifications()
669{
670  if (!observer_.get())
671    return;
672  Profile* profile = Profile::FromWebUI(web_ui());
673  DevToolsAdbBridge* adb_bridge =
674      DevToolsAdbBridge::Factory::GetForProfile(profile);
675  if (adb_bridge)
676    adb_bridge->RemoveListener(this);
677  observer_->InspectUIDestroyed();
678  observer_ = NULL;
679  notification_registrar_.RemoveAll();
680  pref_change_registrar_.RemoveAll();
681}
682
683content::WebUIDataSource* InspectUI::CreateInspectUIHTMLSource() {
684  content::WebUIDataSource* source =
685      content::WebUIDataSource::Create(chrome::kChromeUIInspectHost);
686  source->AddResourcePath("inspect.css", IDR_INSPECT_CSS);
687  source->AddResourcePath("inspect.js", IDR_INSPECT_JS);
688  source->SetDefaultResource(IDR_INSPECT_HTML);
689  return source;
690}
691
692void InspectUI::RemoteDevicesChanged(
693    DevToolsAdbBridge::RemoteDevices* devices) {
694  Profile* profile = Profile::FromWebUI(web_ui());
695  PortForwardingController* port_forwarding_controller =
696      PortForwardingController::Factory::GetForProfile(profile);
697  PortForwardingController::DevicesStatus port_forwarding_status;
698  if (port_forwarding_controller)
699    port_forwarding_status =
700        port_forwarding_controller->UpdateDeviceList(*devices);
701
702  remote_browsers_.clear();
703  remote_pages_.clear();
704  ListValue device_list;
705  for (DevToolsAdbBridge::RemoteDevices::iterator dit = devices->begin();
706       dit != devices->end(); ++dit) {
707    DevToolsAdbBridge::RemoteDevice* device = dit->get();
708    DictionaryValue* device_data = new DictionaryValue();
709    device_data->SetString(kAdbModelField, device->GetModel());
710    device_data->SetString(kAdbSerialField, device->GetSerial());
711    device_data->SetBoolean(kAdbConnectedField, device->IsConnected());
712    std::string device_id = base::StringPrintf(
713        "device:%s",
714        device->GetSerial().c_str());
715    device_data->SetString(kAdbGlobalIdField, device_id);
716    ListValue* browser_list = new ListValue();
717    device_data->Set(kAdbBrowsersField, browser_list);
718
719    DevToolsAdbBridge::RemoteBrowsers& browsers = device->browsers();
720    for (DevToolsAdbBridge::RemoteBrowsers::iterator bit =
721        browsers.begin(); bit != browsers.end(); ++bit) {
722      DevToolsAdbBridge::RemoteBrowser* browser = bit->get();
723      DictionaryValue* browser_data = new DictionaryValue();
724      browser_data->SetString(kAdbBrowserProductField, browser->product());
725      browser_data->SetString(kAdbBrowserPackageField, browser->package());
726      browser_data->SetString(kAdbBrowserVersionField, browser->version());
727      std::string browser_id = base::StringPrintf(
728          "browser:%s:%s:%s",
729          device->GetSerial().c_str(),
730          browser->product().c_str(),  // Force sorting by product name.
731          browser->socket().c_str());
732      browser_data->SetString(kAdbGlobalIdField, browser_id);
733      remote_browsers_[browser_id] = browser;
734      ListValue* page_list = new ListValue();
735      browser_data->Set(kAdbPagesField, page_list);
736
737      DevToolsAdbBridge::RemotePages& pages = browser->pages();
738      for (DevToolsAdbBridge::RemotePages::iterator it =
739          pages.begin(); it != pages.end(); ++it) {
740        DevToolsAdbBridge::RemotePage* page =  it->get();
741        DictionaryValue* page_data = BuildTargetDescriptor(
742            kAdbTargetType, page->attached(),
743            GURL(page->url()), page->title(), GURL(page->favicon_url()),
744            page->description(), 0, 0);
745        std::string page_id = base::StringPrintf("page:%s:%s:%s",
746            device->GetSerial().c_str(),
747            browser->socket().c_str(),
748            page->id().c_str());
749        page_data->SetString(kAdbGlobalIdField, page_id);
750        page_data->SetBoolean(kAdbAttachedForeignField,
751            page->attached() && !page->HasDevToolsWindow());
752        // Pass the screen size in the page object to make sure that
753        // the caching logic does not prevent the page item from updating
754        // when the screen size changes.
755        gfx::Size screen_size = device->screen_size();
756        page_data->SetInteger(kAdbScreenWidthField, screen_size.width());
757        page_data->SetInteger(kAdbScreenHeightField, screen_size.height());
758        remote_pages_[page_id] = page;
759        page_list->Append(page_data);
760      }
761      browser_list->Append(browser_data);
762    }
763
764    if (port_forwarding_controller) {
765      PortForwardingController::DevicesStatus::iterator sit =
766          port_forwarding_status.find(device->GetSerial());
767      if (sit != port_forwarding_status.end()) {
768        DictionaryValue* port_status_dict = new DictionaryValue();
769        typedef PortForwardingController::PortStatusMap StatusMap;
770        const StatusMap& port_status = sit->second;
771        for (StatusMap::const_iterator it = port_status.begin();
772             it != port_status.end(); ++it) {
773          port_status_dict->SetInteger(
774              base::StringPrintf("%d", it->first), it->second);
775        }
776        device_data->Set(kAdbPortStatus, port_status_dict);
777      }
778    }
779
780    device_list.Append(device_data);
781  }
782  web_ui()->CallJavascriptFunction("populateDeviceLists", device_list);
783}
784
785void InspectUI::UpdateDiscoverUsbDevicesEnabled() {
786  const Value* value = GetPrefValue(prefs::kDevToolsDiscoverUsbDevicesEnabled);
787  web_ui()->CallJavascriptFunction("updateDiscoverUsbDevicesEnabled", *value);
788
789  // Configure adb bridge.
790  Profile* profile = Profile::FromWebUI(web_ui());
791  DevToolsAdbBridge* adb_bridge =
792      DevToolsAdbBridge::Factory::GetForProfile(profile);
793  if (adb_bridge) {
794    bool enabled = false;
795    value->GetAsBoolean(&enabled);
796    adb_bridge->set_discover_usb_devices(enabled);
797  }
798}
799
800void InspectUI::UpdatePortForwardingEnabled() {
801  web_ui()->CallJavascriptFunction("updatePortForwardingEnabled",
802      *GetPrefValue(prefs::kDevToolsPortForwardingEnabled));
803
804}
805
806void InspectUI::UpdatePortForwardingConfig() {
807  web_ui()->CallJavascriptFunction("updatePortForwardingConfig",
808      *GetPrefValue(prefs::kDevToolsPortForwardingConfig));
809}
810
811void InspectUI::SetPortForwardingDefaults() {
812  Profile* profile = Profile::FromWebUI(web_ui());
813  PrefService* prefs = profile->GetPrefs();
814
815  bool default_set;
816  if (!GetPrefValue(prefs::kDevToolsPortForwardingDefaultSet)->
817      GetAsBoolean(&default_set) || default_set) {
818    return;
819  }
820
821  // This is the first chrome://inspect invocation on a fresh profile or after
822  // upgrade from a version that did not have kDevToolsPortForwardingDefaultSet.
823  prefs->SetBoolean(prefs::kDevToolsPortForwardingDefaultSet, true);
824
825  bool enabled;
826  const base::DictionaryValue* config;
827  if (!GetPrefValue(prefs::kDevToolsPortForwardingEnabled)->
828        GetAsBoolean(&enabled) ||
829      !GetPrefValue(prefs::kDevToolsPortForwardingConfig)->
830        GetAsDictionary(&config)) {
831    return;
832  }
833
834  // Do nothing if user already took explicit action.
835  if (enabled || config->size() != 0)
836    return;
837
838  base::DictionaryValue default_config;
839  default_config.SetString(
840      kPortForwardingDefaultPort, kPortForwardingDefaultLocation);
841  prefs->Set(prefs::kDevToolsPortForwardingConfig, default_config);
842}
843
844const base::Value* InspectUI::GetPrefValue(const char* name) {
845  Profile* profile = Profile::FromWebUI(web_ui());
846  return profile->GetPrefs()->FindPreference(name)->GetValue();
847}
848