inspect_ui.cc revision 5c02ac1a9c1b504631c0a3d2b6e737b5d738bae1
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_target_impl.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  DevToolsTargetImpl* target = FindTarget(source_id, target_id);
206  if (target)
207    target->Inspect(Profile::FromWebUI(web_ui()));
208}
209
210void InspectUI::Activate(const std::string& source_id,
211                         const std::string& target_id) {
212  DevToolsTargetImpl* target = FindTarget(source_id, target_id);
213  if (target)
214    target->Activate();
215}
216
217void InspectUI::Close(const std::string& source_id,
218                      const std::string& target_id) {
219  DevToolsTargetImpl* target = FindTarget(source_id, target_id);
220  if (target)
221    target->Close();
222}
223
224void InspectUI::Reload(const std::string& source_id,
225                       const std::string& target_id) {
226  DevToolsTargetImpl* target = FindTarget(source_id, target_id);
227  if (target)
228    target->Reload();
229}
230
231static void NoOp(DevToolsTargetImpl*) {}
232
233void InspectUI::Open(const std::string& source_id,
234                     const std::string& browser_id,
235                     const std::string& url) {
236  DevToolsTargetsUIHandler* handler = FindTargetHandler(source_id);
237  if (handler)
238    handler->Open(browser_id, url, base::Bind(&NoOp));
239}
240
241void InspectUI::InspectDevices(Browser* browser) {
242  content::RecordAction(base::UserMetricsAction("InspectDevices"));
243  chrome::NavigateParams params(chrome::GetSingletonTabNavigateParams(
244      browser, GURL(chrome::kChromeUIInspectURL)));
245  params.path_behavior = chrome::NavigateParams::IGNORE_AND_NAVIGATE;
246  ShowSingletonTabOverwritingNTP(browser, params);
247}
248
249void InspectUI::Observe(int type,
250    const content::NotificationSource& source,
251    const content::NotificationDetails& details) {
252  if (source == content::Source<WebContents>(web_ui()->GetWebContents()))
253    StopListeningNotifications();
254}
255
256void InspectUI::StartListeningNotifications() {
257  if (!target_handlers_.empty())  // Possible when reloading the page.
258    StopListeningNotifications();
259
260  Profile* profile = Profile::FromWebUI(web_ui());
261
262  DevToolsTargetsUIHandler::Callback callback =
263      base::Bind(&InspectUI::PopulateTargets, base::Unretained(this));
264
265  AddTargetUIHandler(
266      DevToolsTargetsUIHandler::CreateForRenderers(callback));
267  AddTargetUIHandler(
268      DevToolsTargetsUIHandler::CreateForWorkers(callback));
269  AddTargetUIHandler(
270      DevToolsTargetsUIHandler::CreateForAdb(callback, profile));
271
272  port_status_serializer_.reset(
273      new PortForwardingStatusSerializer(
274          base::Bind(&InspectUI::PopulatePortStatus, base::Unretained(this)),
275          profile));
276
277  notification_registrar_.Add(this,
278                              content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
279                              content::NotificationService::AllSources());
280
281  pref_change_registrar_.Init(profile->GetPrefs());
282  pref_change_registrar_.Add(prefs::kDevToolsDiscoverUsbDevicesEnabled,
283      base::Bind(&InspectUI::UpdateDiscoverUsbDevicesEnabled,
284                 base::Unretained(this)));
285  pref_change_registrar_.Add(prefs::kDevToolsPortForwardingEnabled,
286      base::Bind(&InspectUI::UpdatePortForwardingEnabled,
287                 base::Unretained(this)));
288  pref_change_registrar_.Add(prefs::kDevToolsPortForwardingConfig,
289      base::Bind(&InspectUI::UpdatePortForwardingConfig,
290                 base::Unretained(this)));
291}
292
293void InspectUI::StopListeningNotifications() {
294  if (target_handlers_.empty())
295    return;
296
297  STLDeleteValues(&target_handlers_);
298
299  port_status_serializer_.reset();
300
301  notification_registrar_.RemoveAll();
302  pref_change_registrar_.RemoveAll();
303}
304
305content::WebUIDataSource* InspectUI::CreateInspectUIHTMLSource() {
306  content::WebUIDataSource* source =
307      content::WebUIDataSource::Create(chrome::kChromeUIInspectHost);
308  source->AddResourcePath("inspect.css", IDR_INSPECT_CSS);
309  source->AddResourcePath("inspect.js", IDR_INSPECT_JS);
310  source->SetDefaultResource(IDR_INSPECT_HTML);
311  return source;
312}
313
314void InspectUI::UpdateDiscoverUsbDevicesEnabled() {
315  web_ui()->CallJavascriptFunction(
316      "updateDiscoverUsbDevicesEnabled",
317      *GetPrefValue(prefs::kDevToolsDiscoverUsbDevicesEnabled));
318}
319
320void InspectUI::UpdatePortForwardingEnabled() {
321  web_ui()->CallJavascriptFunction(
322      "updatePortForwardingEnabled",
323      *GetPrefValue(prefs::kDevToolsPortForwardingEnabled));
324}
325
326void InspectUI::UpdatePortForwardingConfig() {
327  web_ui()->CallJavascriptFunction(
328      "updatePortForwardingConfig",
329      *GetPrefValue(prefs::kDevToolsPortForwardingConfig));
330}
331
332void InspectUI::SetPortForwardingDefaults() {
333  Profile* profile = Profile::FromWebUI(web_ui());
334  PrefService* prefs = profile->GetPrefs();
335
336  bool default_set;
337  if (!GetPrefValue(prefs::kDevToolsPortForwardingDefaultSet)->
338      GetAsBoolean(&default_set) || default_set) {
339    return;
340  }
341
342  // This is the first chrome://inspect invocation on a fresh profile or after
343  // upgrade from a version that did not have kDevToolsPortForwardingDefaultSet.
344  prefs->SetBoolean(prefs::kDevToolsPortForwardingDefaultSet, true);
345
346  bool enabled;
347  const base::DictionaryValue* config;
348  if (!GetPrefValue(prefs::kDevToolsPortForwardingEnabled)->
349        GetAsBoolean(&enabled) ||
350      !GetPrefValue(prefs::kDevToolsPortForwardingConfig)->
351        GetAsDictionary(&config)) {
352    return;
353  }
354
355  // Do nothing if user already took explicit action.
356  if (enabled || config->size() != 0)
357    return;
358
359  base::DictionaryValue default_config;
360  default_config.SetString(
361      kPortForwardingDefaultPort, kPortForwardingDefaultLocation);
362  prefs->Set(prefs::kDevToolsPortForwardingConfig, default_config);
363}
364
365const base::Value* InspectUI::GetPrefValue(const char* name) {
366  Profile* profile = Profile::FromWebUI(web_ui());
367  return profile->GetPrefs()->FindPreference(name)->GetValue();
368}
369
370void InspectUI::AddTargetUIHandler(
371    scoped_ptr<DevToolsTargetsUIHandler> handler) {
372  DevToolsTargetsUIHandler* handler_ptr = handler.release();
373  target_handlers_[handler_ptr->source_id()] = handler_ptr;
374}
375
376DevToolsTargetsUIHandler* InspectUI::FindTargetHandler(
377    const std::string& source_id) {
378  TargetHandlerMap::iterator it = target_handlers_.find(source_id);
379     return it != target_handlers_.end() ? it->second : NULL;
380}
381
382DevToolsTargetImpl* InspectUI::FindTarget(
383    const std::string& source_id, const std::string& target_id) {
384  TargetHandlerMap::iterator it = target_handlers_.find(source_id);
385  return it != target_handlers_.end() ?
386         it->second->GetTarget(target_id) : NULL;
387}
388
389void InspectUI::PopulateTargets(const std::string& source,
390                                scoped_ptr<base::ListValue> targets) {
391  scoped_ptr<base::Value> source_value(base::Value::CreateStringValue(source));
392  web_ui()->CallJavascriptFunction(
393      "populateTargets",
394      *source_value.get(),
395      *targets.get());
396}
397
398void InspectUI::PopulatePortStatus(const base::Value& status) {
399  web_ui()->CallJavascriptFunction("populatePortStatus", status);
400}
401