inspect_ui.cc revision f2477e01787aa58f445919b809d89e252beef54f
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 ListValue* args);
59  void HandleInspectCommand(const ListValue* args);
60  void HandleActivateCommand(const ListValue* args);
61  void HandleCloseCommand(const ListValue* args);
62  void HandleReloadCommand(const ListValue* args);
63  void HandleOpenCommand(const ListValue* args);
64  void HandleBooleanPrefChanged(const char* pref_name,
65                                const ListValue* args);
66  void HandlePortForwardingConfigCommand(const 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 ListValue*) {
106  inspect_ui_->InitUI();
107}
108
109static bool ParseStringArgs(const 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 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 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 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 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 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 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 ListValue* args) {
169  Profile* profile = Profile::FromWebUI(web_ui());
170  if (!profile)
171    return;
172
173  const 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(content::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())
256    return;
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  notification_registrar_.Add(this,
271                              content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
272                              content::NotificationService::AllSources());
273
274  pref_change_registrar_.Init(profile->GetPrefs());
275  pref_change_registrar_.Add(prefs::kDevToolsDiscoverUsbDevicesEnabled,
276      base::Bind(&InspectUI::UpdateDiscoverUsbDevicesEnabled,
277                 base::Unretained(this)));
278  pref_change_registrar_.Add(prefs::kDevToolsPortForwardingEnabled,
279      base::Bind(&InspectUI::UpdatePortForwardingEnabled,
280                 base::Unretained(this)));
281  pref_change_registrar_.Add(prefs::kDevToolsPortForwardingConfig,
282      base::Bind(&InspectUI::UpdatePortForwardingConfig,
283                 base::Unretained(this)));
284}
285
286void InspectUI::StopListeningNotifications() {
287  if (target_handlers_.empty())
288    return;
289
290  STLDeleteValues(&target_handlers_);
291  STLDeleteValues(&remote_target_handlers_);
292
293  notification_registrar_.RemoveAll();
294  pref_change_registrar_.RemoveAll();
295}
296
297content::WebUIDataSource* InspectUI::CreateInspectUIHTMLSource() {
298  content::WebUIDataSource* source =
299      content::WebUIDataSource::Create(chrome::kChromeUIInspectHost);
300  source->AddResourcePath("inspect.css", IDR_INSPECT_CSS);
301  source->AddResourcePath("inspect.js", IDR_INSPECT_JS);
302  source->SetDefaultResource(IDR_INSPECT_HTML);
303  return source;
304}
305
306void InspectUI::UpdateDiscoverUsbDevicesEnabled() {
307  const Value* value = GetPrefValue(prefs::kDevToolsDiscoverUsbDevicesEnabled);
308  web_ui()->CallJavascriptFunction("updateDiscoverUsbDevicesEnabled", *value);
309
310  // Configure adb bridge.
311  Profile* profile = Profile::FromWebUI(web_ui());
312  DevToolsAdbBridge* adb_bridge =
313      DevToolsAdbBridge::Factory::GetForProfile(profile);
314  if (adb_bridge) {
315    bool enabled = false;
316    value->GetAsBoolean(&enabled);
317
318    DevToolsAdbBridge::DeviceProviders device_providers;
319    device_providers.push_back(AndroidDeviceProvider::GetAdbDeviceProvider());
320
321    if (enabled) {
322      device_providers.push_back(
323          AndroidDeviceProvider::GetUsbDeviceProvider(profile));
324    }
325
326    adb_bridge->set_device_providers(device_providers);
327  }
328}
329
330void InspectUI::UpdatePortForwardingEnabled() {
331  web_ui()->CallJavascriptFunction("updatePortForwardingEnabled",
332      *GetPrefValue(prefs::kDevToolsPortForwardingEnabled));
333
334}
335
336void InspectUI::UpdatePortForwardingConfig() {
337  web_ui()->CallJavascriptFunction("updatePortForwardingConfig",
338      *GetPrefValue(prefs::kDevToolsPortForwardingConfig));
339}
340
341void InspectUI::SetPortForwardingDefaults() {
342  Profile* profile = Profile::FromWebUI(web_ui());
343  PrefService* prefs = profile->GetPrefs();
344
345  bool default_set;
346  if (!GetPrefValue(prefs::kDevToolsPortForwardingDefaultSet)->
347      GetAsBoolean(&default_set) || default_set) {
348    return;
349  }
350
351  // This is the first chrome://inspect invocation on a fresh profile or after
352  // upgrade from a version that did not have kDevToolsPortForwardingDefaultSet.
353  prefs->SetBoolean(prefs::kDevToolsPortForwardingDefaultSet, true);
354
355  bool enabled;
356  const base::DictionaryValue* config;
357  if (!GetPrefValue(prefs::kDevToolsPortForwardingEnabled)->
358        GetAsBoolean(&enabled) ||
359      !GetPrefValue(prefs::kDevToolsPortForwardingConfig)->
360        GetAsDictionary(&config)) {
361    return;
362  }
363
364  // Do nothing if user already took explicit action.
365  if (enabled || config->size() != 0)
366    return;
367
368  base::DictionaryValue default_config;
369  default_config.SetString(
370      kPortForwardingDefaultPort, kPortForwardingDefaultLocation);
371  prefs->Set(prefs::kDevToolsPortForwardingConfig, default_config);
372}
373
374const base::Value* InspectUI::GetPrefValue(const char* name) {
375  Profile* profile = Profile::FromWebUI(web_ui());
376  return profile->GetPrefs()->FindPreference(name)->GetValue();
377}
378
379void InspectUI::AddTargetUIHandler(
380    scoped_ptr<DevToolsTargetsUIHandler> handler) {
381  DevToolsTargetsUIHandler* handler_ptr = handler.release();
382  target_handlers_[handler_ptr->source_id()] = handler_ptr;
383}
384
385void InspectUI::AddRemoteTargetUIHandler(
386    scoped_ptr<DevToolsRemoteTargetsUIHandler> handler) {
387  DevToolsRemoteTargetsUIHandler* handler_ptr = handler.release();
388  remote_target_handlers_[handler_ptr->source_id()] = handler_ptr;
389}
390
391DevToolsTargetsUIHandler* InspectUI::FindTargetHandler(
392    const std::string& source_id) {
393  TargetHandlerMap::iterator it = target_handlers_.find(source_id);
394  return it != target_handlers_.end() ?
395         it->second :
396         FindRemoteTargetHandler(source_id);
397}
398
399DevToolsRemoteTargetsUIHandler* InspectUI::FindRemoteTargetHandler(
400    const std::string& source_id) {
401  RemoteTargetHandlerMap::iterator it = remote_target_handlers_.find(source_id);
402  return it != remote_target_handlers_.end() ? it->second : NULL;
403}
404
405void InspectUI::PopulateTargets(const std::string& source,
406                                scoped_ptr<base::ListValue> targets) {
407  scoped_ptr<base::Value> source_value(base::Value::CreateStringValue(source));
408  web_ui()->CallJavascriptFunction(
409      "populateTargets",
410      *source_value.get(),
411      *targets.get());
412}
413