inspect_ui.cc revision a02191e04bc25c4935f804f2c080ae28663d096d
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 "base/prefs/pref_service.h"
8#include "base/stl_util.h"
9#include "chrome/browser/devtools/devtools_adb_bridge.h"
10#include "chrome/browser/devtools/devtools_targets_ui.h"
11#include "chrome/browser/profiles/profile.h"
12#include "chrome/browser/ui/browser_navigator.h"
13#include "chrome/browser/ui/singleton_tabs.h"
14#include "chrome/browser/ui/webui/theme_source.h"
15#include "chrome/common/pref_names.h"
16#include "chrome/common/url_constants.h"
17#include "content/public/browser/notification_service.h"
18#include "content/public/browser/notification_source.h"
19#include "content/public/browser/notification_types.h"
20#include "content/public/browser/user_metrics.h"
21#include "content/public/browser/web_contents.h"
22#include "content/public/browser/web_ui.h"
23#include "content/public/browser/web_ui_data_source.h"
24#include "content/public/browser/web_ui_message_handler.h"
25#include "grit/browser_resources.h"
26
27using content::WebContents;
28using content::WebUIMessageHandler;
29
30namespace {
31
32const char kInitUICommand[]  = "init-ui";
33const char kInspectCommand[]  = "inspect";
34const char kActivateCommand[]  = "activate";
35const char kCloseCommand[]  = "close";
36const char kReloadCommand[]  = "reload";
37const char kOpenCommand[]  = "open";
38
39const char kDiscoverUsbDevicesEnabledCommand[] =
40    "set-discover-usb-devices-enabled";
41const char kPortForwardingEnabledCommand[] =
42    "set-port-forwarding-enabled";
43const char kPortForwardingConfigCommand[] = "set-port-forwarding-config";
44
45const char kPortForwardingDefaultPort[] = "8080";
46const char kPortForwardingDefaultLocation[] = "localhost:8080";
47
48class InspectMessageHandler : public WebUIMessageHandler {
49 public:
50  explicit InspectMessageHandler(InspectUI* inspect_ui)
51      : inspect_ui_(inspect_ui) {}
52  virtual ~InspectMessageHandler() {}
53
54 private:
55  // WebUIMessageHandler implementation.
56  virtual void RegisterMessages() OVERRIDE;
57
58  void HandleInitUICommand(const base::ListValue* args);
59  void HandleInspectCommand(const base::ListValue* args);
60  void HandleActivateCommand(const base::ListValue* args);
61  void HandleCloseCommand(const base::ListValue* args);
62  void HandleReloadCommand(const base::ListValue* args);
63  void HandleOpenCommand(const base::ListValue* args);
64  void HandleBooleanPrefChanged(const char* pref_name,
65                                const base::ListValue* args);
66  void HandlePortForwardingConfigCommand(const base::ListValue* args);
67
68  InspectUI* inspect_ui_;
69
70  DISALLOW_COPY_AND_ASSIGN(InspectMessageHandler);
71};
72
73void InspectMessageHandler::RegisterMessages() {
74  web_ui()->RegisterMessageCallback(kInitUICommand,
75      base::Bind(&InspectMessageHandler::HandleInitUICommand,
76                 base::Unretained(this)));
77  web_ui()->RegisterMessageCallback(kInspectCommand,
78      base::Bind(&InspectMessageHandler::HandleInspectCommand,
79                 base::Unretained(this)));
80  web_ui()->RegisterMessageCallback(kActivateCommand,
81      base::Bind(&InspectMessageHandler::HandleActivateCommand,
82                 base::Unretained(this)));
83  web_ui()->RegisterMessageCallback(kCloseCommand,
84      base::Bind(&InspectMessageHandler::HandleCloseCommand,
85                 base::Unretained(this)));
86  web_ui()->RegisterMessageCallback(kDiscoverUsbDevicesEnabledCommand,
87      base::Bind(&InspectMessageHandler::HandleBooleanPrefChanged,
88                  base::Unretained(this),
89                  &prefs::kDevToolsDiscoverUsbDevicesEnabled[0]));
90  web_ui()->RegisterMessageCallback(kPortForwardingEnabledCommand,
91      base::Bind(&InspectMessageHandler::HandleBooleanPrefChanged,
92                 base::Unretained(this),
93                 &prefs::kDevToolsPortForwardingEnabled[0]));
94  web_ui()->RegisterMessageCallback(kPortForwardingConfigCommand,
95      base::Bind(&InspectMessageHandler::HandlePortForwardingConfigCommand,
96                 base::Unretained(this)));
97  web_ui()->RegisterMessageCallback(kReloadCommand,
98      base::Bind(&InspectMessageHandler::HandleReloadCommand,
99                 base::Unretained(this)));
100  web_ui()->RegisterMessageCallback(kOpenCommand,
101      base::Bind(&InspectMessageHandler::HandleOpenCommand,
102                 base::Unretained(this)));
103}
104
105void InspectMessageHandler::HandleInitUICommand(const base::ListValue*) {
106  inspect_ui_->InitUI();
107}
108
109static bool ParseStringArgs(const base::ListValue* args,
110                            std::string* arg0,
111                            std::string* arg1,
112                            std::string* arg2 = 0) {
113  int arg_size = args->GetSize();
114  return (!arg0 || (arg_size > 0 && args->GetString(0, arg0))) &&
115         (!arg1 || (arg_size > 1 && args->GetString(1, arg1))) &&
116         (!arg2 || (arg_size > 2 && args->GetString(2, arg2)));
117}
118
119void InspectMessageHandler::HandleInspectCommand(const base::ListValue* args) {
120  std::string source;
121  std::string id;
122  if (ParseStringArgs(args, &source, &id))
123    inspect_ui_->Inspect(source, id);
124}
125
126void InspectMessageHandler::HandleActivateCommand(const base::ListValue* args) {
127  std::string source;
128  std::string id;
129  if (ParseStringArgs(args, &source, &id))
130    inspect_ui_->Activate(source, id);
131}
132
133void InspectMessageHandler::HandleCloseCommand(const base::ListValue* args) {
134  std::string source;
135  std::string id;
136  if (ParseStringArgs(args, &source, &id))
137    inspect_ui_->Close(source, id);
138}
139
140void InspectMessageHandler::HandleReloadCommand(const base::ListValue* args) {
141  std::string source;
142  std::string id;
143  if (ParseStringArgs(args, &source, &id))
144    inspect_ui_->Reload(source, id);
145}
146
147void InspectMessageHandler::HandleOpenCommand(const base::ListValue* args) {
148  std::string source_id;
149  std::string browser_id;
150  std::string url;
151  if (ParseStringArgs(args, &source_id, &browser_id, &url))
152    inspect_ui_->Open(source_id, browser_id, url);
153}
154
155void InspectMessageHandler::HandleBooleanPrefChanged(
156    const char* pref_name,
157    const base::ListValue* args) {
158  Profile* profile = Profile::FromWebUI(web_ui());
159  if (!profile)
160    return;
161
162  bool enabled;
163  if (args->GetSize() == 1 && args->GetBoolean(0, &enabled))
164    profile->GetPrefs()->SetBoolean(pref_name, enabled);
165}
166
167void InspectMessageHandler::HandlePortForwardingConfigCommand(
168    const base::ListValue* args) {
169  Profile* profile = Profile::FromWebUI(web_ui());
170  if (!profile)
171    return;
172
173  const base::DictionaryValue* dict_src;
174  if (args->GetSize() == 1 && args->GetDictionary(0, &dict_src))
175    profile->GetPrefs()->Set(prefs::kDevToolsPortForwardingConfig, *dict_src);
176}
177
178}  // namespace
179
180InspectUI::InspectUI(content::WebUI* web_ui)
181    : WebUIController(web_ui) {
182  web_ui->AddMessageHandler(new InspectMessageHandler(this));
183  Profile* profile = Profile::FromWebUI(web_ui);
184  content::WebUIDataSource::Add(profile, CreateInspectUIHTMLSource());
185
186  // Set up the chrome://theme/ source.
187  ThemeSource* theme = new ThemeSource(profile);
188  content::URLDataSource::Add(profile, theme);
189}
190
191InspectUI::~InspectUI() {
192  StopListeningNotifications();
193}
194
195void InspectUI::InitUI() {
196  SetPortForwardingDefaults();
197  StartListeningNotifications();
198  UpdateDiscoverUsbDevicesEnabled();
199  UpdatePortForwardingEnabled();
200  UpdatePortForwardingConfig();
201}
202
203void InspectUI::Inspect(const std::string& source_id,
204                        const std::string& target_id) {
205  DevToolsTargetsUIHandler* handler = FindTargetHandler(source_id);
206  if (handler)
207    handler->Inspect(target_id, Profile::FromWebUI(web_ui()));
208}
209
210void InspectUI::Activate(const std::string& source_id,
211                         const std::string& target_id) {
212  DevToolsTargetsUIHandler* handler = FindTargetHandler(source_id);
213  if (handler)
214    handler->Activate(target_id);
215}
216
217void InspectUI::Close(const std::string& source_id,
218                      const std::string& target_id) {
219  DevToolsTargetsUIHandler* handler = FindTargetHandler(source_id);
220  if (handler)
221    handler->Close(target_id);
222}
223
224void InspectUI::Reload(const std::string& source_id,
225                       const std::string& target_id) {
226  DevToolsTargetsUIHandler* handler = FindTargetHandler(source_id);
227  if (handler)
228    handler->Reload(target_id);
229}
230
231void InspectUI::Open(const std::string& source_id,
232                     const std::string& browser_id,
233                     const std::string& url) {
234  DevToolsRemoteTargetsUIHandler* handler = FindRemoteTargetHandler(source_id);
235  if (handler)
236    handler->Open(browser_id, url);
237}
238
239void InspectUI::InspectDevices(Browser* browser) {
240  content::RecordAction(base::UserMetricsAction("InspectDevices"));
241  chrome::NavigateParams params(chrome::GetSingletonTabNavigateParams(
242      browser, GURL(chrome::kChromeUIInspectURL)));
243  params.path_behavior = chrome::NavigateParams::IGNORE_AND_NAVIGATE;
244  ShowSingletonTabOverwritingNTP(browser, params);
245}
246
247void InspectUI::Observe(int type,
248    const content::NotificationSource& source,
249    const content::NotificationDetails& details) {
250  if (source == content::Source<WebContents>(web_ui()->GetWebContents()))
251    StopListeningNotifications();
252}
253
254void InspectUI::StartListeningNotifications() {
255  if (!target_handlers_.empty())  // Possible when reloading the page.
256    StopListeningNotifications();
257
258  Profile* profile = Profile::FromWebUI(web_ui());
259
260  DevToolsTargetsUIHandler::Callback callback =
261      base::Bind(&InspectUI::PopulateTargets, base::Unretained(this));
262
263  AddTargetUIHandler(
264      DevToolsTargetsUIHandler::CreateForRenderers(callback));
265  AddTargetUIHandler(
266      DevToolsTargetsUIHandler::CreateForWorkers(callback));
267  AddRemoteTargetUIHandler(
268      DevToolsRemoteTargetsUIHandler::CreateForAdb(callback, profile));
269
270  port_status_serializer_.reset(
271      new PortForwardingStatusSerializer(
272          base::Bind(&InspectUI::PopulatePortStatus, base::Unretained(this)),
273          profile));
274
275  notification_registrar_.Add(this,
276                              content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
277                              content::NotificationService::AllSources());
278
279  pref_change_registrar_.Init(profile->GetPrefs());
280  pref_change_registrar_.Add(prefs::kDevToolsDiscoverUsbDevicesEnabled,
281      base::Bind(&InspectUI::UpdateDiscoverUsbDevicesEnabled,
282                 base::Unretained(this)));
283  pref_change_registrar_.Add(prefs::kDevToolsPortForwardingEnabled,
284      base::Bind(&InspectUI::UpdatePortForwardingEnabled,
285                 base::Unretained(this)));
286  pref_change_registrar_.Add(prefs::kDevToolsPortForwardingConfig,
287      base::Bind(&InspectUI::UpdatePortForwardingConfig,
288                 base::Unretained(this)));
289}
290
291void InspectUI::StopListeningNotifications() {
292  if (target_handlers_.empty())
293    return;
294
295  STLDeleteValues(&target_handlers_);
296  STLDeleteValues(&remote_target_handlers_);
297
298  port_status_serializer_.reset();
299
300  notification_registrar_.RemoveAll();
301  pref_change_registrar_.RemoveAll();
302}
303
304content::WebUIDataSource* InspectUI::CreateInspectUIHTMLSource() {
305  content::WebUIDataSource* source =
306      content::WebUIDataSource::Create(chrome::kChromeUIInspectHost);
307  source->AddResourcePath("inspect.css", IDR_INSPECT_CSS);
308  source->AddResourcePath("inspect.js", IDR_INSPECT_JS);
309  source->SetDefaultResource(IDR_INSPECT_HTML);
310  return source;
311}
312
313void InspectUI::UpdateDiscoverUsbDevicesEnabled() {
314  web_ui()->CallJavascriptFunction(
315      "updateDiscoverUsbDevicesEnabled",
316      *GetPrefValue(prefs::kDevToolsDiscoverUsbDevicesEnabled));
317}
318
319void InspectUI::UpdatePortForwardingEnabled() {
320  web_ui()->CallJavascriptFunction(
321      "updatePortForwardingEnabled",
322      *GetPrefValue(prefs::kDevToolsPortForwardingEnabled));
323}
324
325void InspectUI::UpdatePortForwardingConfig() {
326  web_ui()->CallJavascriptFunction(
327      "updatePortForwardingConfig",
328      *GetPrefValue(prefs::kDevToolsPortForwardingConfig));
329}
330
331void InspectUI::SetPortForwardingDefaults() {
332  Profile* profile = Profile::FromWebUI(web_ui());
333  PrefService* prefs = profile->GetPrefs();
334
335  bool default_set;
336  if (!GetPrefValue(prefs::kDevToolsPortForwardingDefaultSet)->
337      GetAsBoolean(&default_set) || default_set) {
338    return;
339  }
340
341  // This is the first chrome://inspect invocation on a fresh profile or after
342  // upgrade from a version that did not have kDevToolsPortForwardingDefaultSet.
343  prefs->SetBoolean(prefs::kDevToolsPortForwardingDefaultSet, true);
344
345  bool enabled;
346  const base::DictionaryValue* config;
347  if (!GetPrefValue(prefs::kDevToolsPortForwardingEnabled)->
348        GetAsBoolean(&enabled) ||
349      !GetPrefValue(prefs::kDevToolsPortForwardingConfig)->
350        GetAsDictionary(&config)) {
351    return;
352  }
353
354  // Do nothing if user already took explicit action.
355  if (enabled || config->size() != 0)
356    return;
357
358  base::DictionaryValue default_config;
359  default_config.SetString(
360      kPortForwardingDefaultPort, kPortForwardingDefaultLocation);
361  prefs->Set(prefs::kDevToolsPortForwardingConfig, default_config);
362}
363
364const base::Value* InspectUI::GetPrefValue(const char* name) {
365  Profile* profile = Profile::FromWebUI(web_ui());
366  return profile->GetPrefs()->FindPreference(name)->GetValue();
367}
368
369void InspectUI::AddTargetUIHandler(
370    scoped_ptr<DevToolsTargetsUIHandler> handler) {
371  DevToolsTargetsUIHandler* handler_ptr = handler.release();
372  target_handlers_[handler_ptr->source_id()] = handler_ptr;
373}
374
375void InspectUI::AddRemoteTargetUIHandler(
376    scoped_ptr<DevToolsRemoteTargetsUIHandler> handler) {
377  DevToolsRemoteTargetsUIHandler* handler_ptr = handler.release();
378  remote_target_handlers_[handler_ptr->source_id()] = handler_ptr;
379}
380
381DevToolsTargetsUIHandler* InspectUI::FindTargetHandler(
382    const std::string& source_id) {
383  TargetHandlerMap::iterator it = target_handlers_.find(source_id);
384  return it != target_handlers_.end() ?
385         it->second :
386         FindRemoteTargetHandler(source_id);
387}
388
389DevToolsRemoteTargetsUIHandler* InspectUI::FindRemoteTargetHandler(
390    const std::string& source_id) {
391  RemoteTargetHandlerMap::iterator it = remote_target_handlers_.find(source_id);
392  return it != remote_target_handlers_.end() ? it->second : NULL;
393}
394
395void InspectUI::PopulateTargets(const std::string& source,
396                                scoped_ptr<base::ListValue> targets) {
397  scoped_ptr<base::Value> source_value(base::Value::CreateStringValue(source));
398  web_ui()->CallJavascriptFunction(
399      "populateTargets",
400      *source_value.get(),
401      *targets.get());
402}
403
404void InspectUI::PopulatePortStatus(const base::Value& status) {
405  web_ui()->CallJavascriptFunction("populatePortStatus", status);
406}
407