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