• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/devtools/devtools_ui_bindings.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/ui/browser_navigator.h"
14 #include "chrome/browser/ui/singleton_tabs.h"
15 #include "chrome/browser/ui/webui/theme_source.h"
16 #include "chrome/common/pref_names.h"
17 #include "chrome/common/url_constants.h"
18 #include "content/public/browser/devtools_agent_host.h"
19 #include "content/public/browser/navigation_entry.h"
20 #include "content/public/browser/notification_service.h"
21 #include "content/public/browser/notification_source.h"
22 #include "content/public/browser/notification_types.h"
23 #include "content/public/browser/user_metrics.h"
24 #include "content/public/browser/web_contents.h"
25 #include "content/public/browser/web_contents_delegate.h"
26 #include "content/public/browser/web_contents_observer.h"
27 #include "content/public/browser/web_ui.h"
28 #include "content/public/browser/web_ui_data_source.h"
29 #include "content/public/browser/web_ui_message_handler.h"
30 #include "grit/browser_resources.h"
31 
32 using content::WebContents;
33 using content::WebUIMessageHandler;
34 
35 namespace {
36 
37 const char kInitUICommand[]  = "init-ui";
38 const char kInspectCommand[]  = "inspect";
39 const char kActivateCommand[]  = "activate";
40 const char kCloseCommand[]  = "close";
41 const char kReloadCommand[]  = "reload";
42 const char kOpenCommand[]  = "open";
43 const char kInspectBrowser[] = "inspect-browser";
44 const char kLocalHost[] = "localhost";
45 
46 const char kDiscoverUsbDevicesEnabledCommand[] =
47     "set-discover-usb-devices-enabled";
48 const char kPortForwardingEnabledCommand[] =
49     "set-port-forwarding-enabled";
50 const char kPortForwardingConfigCommand[] = "set-port-forwarding-config";
51 
52 const char kPortForwardingDefaultPort[] = "8080";
53 const char kPortForwardingDefaultLocation[] = "localhost:8080";
54 
55 // InspectMessageHandler --------------------------------------------
56 
57 class InspectMessageHandler : public WebUIMessageHandler {
58  public:
InspectMessageHandler(InspectUI * inspect_ui)59   explicit InspectMessageHandler(InspectUI* inspect_ui)
60       : inspect_ui_(inspect_ui) {}
~InspectMessageHandler()61   virtual ~InspectMessageHandler() {}
62 
63  private:
64   // WebUIMessageHandler implementation.
65   virtual void RegisterMessages() OVERRIDE;
66 
67   void HandleInitUICommand(const base::ListValue* args);
68   void HandleInspectCommand(const base::ListValue* args);
69   void HandleActivateCommand(const base::ListValue* args);
70   void HandleCloseCommand(const base::ListValue* args);
71   void HandleReloadCommand(const base::ListValue* args);
72   void HandleOpenCommand(const base::ListValue* args);
73   void HandleInspectBrowserCommand(const base::ListValue* args);
74   void HandleBooleanPrefChanged(const char* pref_name,
75                                 const base::ListValue* args);
76   void HandlePortForwardingConfigCommand(const base::ListValue* args);
77 
78   InspectUI* inspect_ui_;
79 
80   DISALLOW_COPY_AND_ASSIGN(InspectMessageHandler);
81 };
82 
RegisterMessages()83 void InspectMessageHandler::RegisterMessages() {
84   web_ui()->RegisterMessageCallback(kInitUICommand,
85       base::Bind(&InspectMessageHandler::HandleInitUICommand,
86                  base::Unretained(this)));
87   web_ui()->RegisterMessageCallback(kInspectCommand,
88       base::Bind(&InspectMessageHandler::HandleInspectCommand,
89                  base::Unretained(this)));
90   web_ui()->RegisterMessageCallback(kActivateCommand,
91       base::Bind(&InspectMessageHandler::HandleActivateCommand,
92                  base::Unretained(this)));
93   web_ui()->RegisterMessageCallback(kCloseCommand,
94       base::Bind(&InspectMessageHandler::HandleCloseCommand,
95                  base::Unretained(this)));
96   web_ui()->RegisterMessageCallback(kDiscoverUsbDevicesEnabledCommand,
97       base::Bind(&InspectMessageHandler::HandleBooleanPrefChanged,
98                   base::Unretained(this),
99                   &prefs::kDevToolsDiscoverUsbDevicesEnabled[0]));
100   web_ui()->RegisterMessageCallback(kPortForwardingEnabledCommand,
101       base::Bind(&InspectMessageHandler::HandleBooleanPrefChanged,
102                  base::Unretained(this),
103                  &prefs::kDevToolsPortForwardingEnabled[0]));
104   web_ui()->RegisterMessageCallback(kPortForwardingConfigCommand,
105       base::Bind(&InspectMessageHandler::HandlePortForwardingConfigCommand,
106                  base::Unretained(this)));
107   web_ui()->RegisterMessageCallback(kReloadCommand,
108       base::Bind(&InspectMessageHandler::HandleReloadCommand,
109                  base::Unretained(this)));
110   web_ui()->RegisterMessageCallback(kOpenCommand,
111       base::Bind(&InspectMessageHandler::HandleOpenCommand,
112                  base::Unretained(this)));
113   web_ui()->RegisterMessageCallback(kInspectBrowser,
114       base::Bind(&InspectMessageHandler::HandleInspectBrowserCommand,
115                  base::Unretained(this)));
116 }
117 
HandleInitUICommand(const base::ListValue *)118 void InspectMessageHandler::HandleInitUICommand(const base::ListValue*) {
119   inspect_ui_->InitUI();
120 }
121 
ParseStringArgs(const base::ListValue * args,std::string * arg0,std::string * arg1,std::string * arg2=0)122 static bool ParseStringArgs(const base::ListValue* args,
123                             std::string* arg0,
124                             std::string* arg1,
125                             std::string* arg2 = 0) {
126   int arg_size = args->GetSize();
127   return (!arg0 || (arg_size > 0 && args->GetString(0, arg0))) &&
128          (!arg1 || (arg_size > 1 && args->GetString(1, arg1))) &&
129          (!arg2 || (arg_size > 2 && args->GetString(2, arg2)));
130 }
131 
HandleInspectCommand(const base::ListValue * args)132 void InspectMessageHandler::HandleInspectCommand(const base::ListValue* args) {
133   std::string source;
134   std::string id;
135   if (ParseStringArgs(args, &source, &id))
136     inspect_ui_->Inspect(source, id);
137 }
138 
HandleActivateCommand(const base::ListValue * args)139 void InspectMessageHandler::HandleActivateCommand(const base::ListValue* args) {
140   std::string source;
141   std::string id;
142   if (ParseStringArgs(args, &source, &id))
143     inspect_ui_->Activate(source, id);
144 }
145 
HandleCloseCommand(const base::ListValue * args)146 void InspectMessageHandler::HandleCloseCommand(const base::ListValue* args) {
147   std::string source;
148   std::string id;
149   if (ParseStringArgs(args, &source, &id))
150     inspect_ui_->Close(source, id);
151 }
152 
HandleReloadCommand(const base::ListValue * args)153 void InspectMessageHandler::HandleReloadCommand(const base::ListValue* args) {
154   std::string source;
155   std::string id;
156   if (ParseStringArgs(args, &source, &id))
157     inspect_ui_->Reload(source, id);
158 }
159 
HandleOpenCommand(const base::ListValue * args)160 void InspectMessageHandler::HandleOpenCommand(const base::ListValue* args) {
161   std::string source_id;
162   std::string browser_id;
163   std::string url;
164   if (ParseStringArgs(args, &source_id, &browser_id, &url))
165     inspect_ui_->Open(source_id, browser_id, url);
166 }
167 
HandleInspectBrowserCommand(const base::ListValue * args)168 void InspectMessageHandler::HandleInspectBrowserCommand(
169     const base::ListValue* args) {
170   std::string source_id;
171   std::string browser_id;
172   std::string front_end;
173   if (ParseStringArgs(args, &source_id, &browser_id, &front_end)) {
174     inspect_ui_->InspectBrowserWithCustomFrontend(
175         source_id, browser_id, GURL(front_end));
176   }
177 }
178 
HandleBooleanPrefChanged(const char * pref_name,const base::ListValue * args)179 void InspectMessageHandler::HandleBooleanPrefChanged(
180     const char* pref_name,
181     const base::ListValue* args) {
182   Profile* profile = Profile::FromWebUI(web_ui());
183   if (!profile)
184     return;
185 
186   bool enabled;
187   if (args->GetSize() == 1 && args->GetBoolean(0, &enabled))
188     profile->GetPrefs()->SetBoolean(pref_name, enabled);
189 }
190 
HandlePortForwardingConfigCommand(const base::ListValue * args)191 void InspectMessageHandler::HandlePortForwardingConfigCommand(
192     const base::ListValue* args) {
193   Profile* profile = Profile::FromWebUI(web_ui());
194   if (!profile)
195     return;
196 
197   const base::DictionaryValue* dict_src;
198   if (args->GetSize() == 1 && args->GetDictionary(0, &dict_src))
199     profile->GetPrefs()->Set(prefs::kDevToolsPortForwardingConfig, *dict_src);
200 }
201 
202 // DevToolsUIBindingsEnabler ----------------------------------------
203 
204 class DevToolsUIBindingsEnabler
205     : public content::WebContentsObserver {
206  public:
207   DevToolsUIBindingsEnabler(WebContents* web_contents,
208                             const GURL& url);
~DevToolsUIBindingsEnabler()209   virtual ~DevToolsUIBindingsEnabler() {}
210 
211   DevToolsUIBindings* GetBindings();
212 
213  private:
214   // contents::WebContentsObserver overrides.
215   virtual void WebContentsDestroyed() OVERRIDE;
216   virtual void AboutToNavigateRenderView(
217       content::RenderViewHost* render_view_host) OVERRIDE;
218 
219   DevToolsUIBindings bindings_;
220   GURL url_;
221   DISALLOW_COPY_AND_ASSIGN(DevToolsUIBindingsEnabler);
222 };
223 
DevToolsUIBindingsEnabler(WebContents * web_contents,const GURL & url)224 DevToolsUIBindingsEnabler::DevToolsUIBindingsEnabler(
225     WebContents* web_contents,
226     const GURL& url)
227     : WebContentsObserver(web_contents),
228       bindings_(web_contents),
229       url_(url) {
230 }
231 
GetBindings()232 DevToolsUIBindings* DevToolsUIBindingsEnabler::GetBindings() {
233   return &bindings_;
234 }
235 
WebContentsDestroyed()236 void DevToolsUIBindingsEnabler::WebContentsDestroyed() {
237   delete this;
238 }
239 
AboutToNavigateRenderView(content::RenderViewHost * render_view_host)240 void DevToolsUIBindingsEnabler::AboutToNavigateRenderView(
241     content::RenderViewHost* render_view_host) {
242    content::NavigationEntry* entry =
243        web_contents()->GetController().GetActiveEntry();
244    if (url_ != entry->GetURL())
245      delete this;
246 }
247 
248 }  // namespace
249 
250 // InspectUI --------------------------------------------------------
251 
InspectUI(content::WebUI * web_ui)252 InspectUI::InspectUI(content::WebUI* web_ui)
253     : WebUIController(web_ui) {
254   web_ui->AddMessageHandler(new InspectMessageHandler(this));
255   Profile* profile = Profile::FromWebUI(web_ui);
256   content::WebUIDataSource::Add(profile, CreateInspectUIHTMLSource());
257 
258   // Set up the chrome://theme/ source.
259   ThemeSource* theme = new ThemeSource(profile);
260   content::URLDataSource::Add(profile, theme);
261 }
262 
~InspectUI()263 InspectUI::~InspectUI() {
264   StopListeningNotifications();
265 }
266 
InitUI()267 void InspectUI::InitUI() {
268   SetPortForwardingDefaults();
269   StartListeningNotifications();
270   UpdateDiscoverUsbDevicesEnabled();
271   UpdatePortForwardingEnabled();
272   UpdatePortForwardingConfig();
273 }
274 
Inspect(const std::string & source_id,const std::string & target_id)275 void InspectUI::Inspect(const std::string& source_id,
276                         const std::string& target_id) {
277   DevToolsTargetImpl* target = FindTarget(source_id, target_id);
278   if (target) {
279     const std::string target_type = target->GetType();
280     target->Inspect(Profile::FromWebUI(web_ui()));
281     ForceUpdateIfNeeded(source_id, target_type);
282   }
283 }
284 
Activate(const std::string & source_id,const std::string & target_id)285 void InspectUI::Activate(const std::string& source_id,
286                          const std::string& target_id) {
287   DevToolsTargetImpl* target = FindTarget(source_id, target_id);
288   if (target) {
289     const std::string target_type = target->GetType();
290     target->Activate();
291     ForceUpdateIfNeeded(source_id, target_type);
292   }
293 }
294 
Close(const std::string & source_id,const std::string & target_id)295 void InspectUI::Close(const std::string& source_id,
296                       const std::string& target_id) {
297   DevToolsTargetImpl* target = FindTarget(source_id, target_id);
298   if (target) {
299     const std::string target_type = target->GetType();
300     target->Close();
301     ForceUpdateIfNeeded(source_id, target_type);
302   }
303 }
304 
Reload(const std::string & source_id,const std::string & target_id)305 void InspectUI::Reload(const std::string& source_id,
306                        const std::string& target_id) {
307   DevToolsTargetImpl* target = FindTarget(source_id, target_id);
308   if (target) {
309     const std::string target_type = target->GetType();
310     target->Reload();
311     ForceUpdateIfNeeded(source_id, target_type);
312   }
313 }
314 
NoOp(DevToolsTargetImpl *)315 static void NoOp(DevToolsTargetImpl*) {}
316 
Open(const std::string & source_id,const std::string & browser_id,const std::string & url)317 void InspectUI::Open(const std::string& source_id,
318                      const std::string& browser_id,
319                      const std::string& url) {
320   DevToolsTargetsUIHandler* handler = FindTargetHandler(source_id);
321   if (handler)
322     handler->Open(browser_id, url, base::Bind(&NoOp));
323 }
324 
InspectBrowserWithCustomFrontend(const std::string & source_id,const std::string & browser_id,const GURL & frontend_url)325 void InspectUI::InspectBrowserWithCustomFrontend(
326     const std::string& source_id,
327     const std::string& browser_id,
328     const GURL& frontend_url) {
329   if (!frontend_url.SchemeIs(content::kChromeUIScheme) &&
330       !frontend_url.SchemeIs(content::kChromeDevToolsScheme) &&
331       frontend_url.host() != kLocalHost) {
332     return;
333   }
334 
335   DevToolsTargetsUIHandler* handler = FindTargetHandler(source_id);
336   if (!handler)
337     return;
338 
339   // Fetch agent host from remote browser.
340   scoped_refptr<content::DevToolsAgentHost> agent_host =
341       handler->GetBrowserAgentHost(browser_id);
342   if (agent_host->IsAttached())
343     return;
344 
345   // Create web contents for the front-end.
346   WebContents* inspect_ui = web_ui()->GetWebContents();
347   WebContents* front_end = inspect_ui->GetDelegate()->OpenURLFromTab(
348       inspect_ui,
349       content::OpenURLParams(frontend_url,
350                              content::Referrer(),
351                              NEW_FOREGROUND_TAB,
352                              ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
353                              false));
354 
355   // Install devtools bindings.
356   DevToolsUIBindingsEnabler* bindings_enabler =
357       new DevToolsUIBindingsEnabler(front_end, frontend_url);
358   bindings_enabler->GetBindings()->AttachTo(agent_host);
359 }
360 
InspectDevices(Browser * browser)361 void InspectUI::InspectDevices(Browser* browser) {
362   content::RecordAction(base::UserMetricsAction("InspectDevices"));
363   chrome::NavigateParams params(chrome::GetSingletonTabNavigateParams(
364       browser, GURL(chrome::kChromeUIInspectURL)));
365   params.path_behavior = chrome::NavigateParams::IGNORE_AND_NAVIGATE;
366   ShowSingletonTabOverwritingNTP(browser, params);
367 }
368 
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)369 void InspectUI::Observe(int type,
370     const content::NotificationSource& source,
371     const content::NotificationDetails& details) {
372   if (source == content::Source<WebContents>(web_ui()->GetWebContents()))
373     StopListeningNotifications();
374 }
375 
StartListeningNotifications()376 void InspectUI::StartListeningNotifications() {
377   if (!target_handlers_.empty())  // Possible when reloading the page.
378     StopListeningNotifications();
379 
380   Profile* profile = Profile::FromWebUI(web_ui());
381 
382   DevToolsTargetsUIHandler::Callback callback =
383       base::Bind(&InspectUI::PopulateTargets, base::Unretained(this));
384 
385   AddTargetUIHandler(
386       DevToolsTargetsUIHandler::CreateForLocal(callback));
387   if (profile->IsOffTheRecord()) {
388     ShowIncognitoWarning();
389   } else {
390     AddTargetUIHandler(
391         DevToolsTargetsUIHandler::CreateForAdb(callback, profile));
392   }
393 
394   port_status_serializer_.reset(
395       new PortForwardingStatusSerializer(
396           base::Bind(&InspectUI::PopulatePortStatus, base::Unretained(this)),
397           profile));
398 
399   notification_registrar_.Add(this,
400                               content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
401                               content::NotificationService::AllSources());
402 
403   pref_change_registrar_.Init(profile->GetPrefs());
404   pref_change_registrar_.Add(prefs::kDevToolsDiscoverUsbDevicesEnabled,
405       base::Bind(&InspectUI::UpdateDiscoverUsbDevicesEnabled,
406                  base::Unretained(this)));
407   pref_change_registrar_.Add(prefs::kDevToolsPortForwardingEnabled,
408       base::Bind(&InspectUI::UpdatePortForwardingEnabled,
409                  base::Unretained(this)));
410   pref_change_registrar_.Add(prefs::kDevToolsPortForwardingConfig,
411       base::Bind(&InspectUI::UpdatePortForwardingConfig,
412                  base::Unretained(this)));
413 }
414 
StopListeningNotifications()415 void InspectUI::StopListeningNotifications() {
416   if (target_handlers_.empty())
417     return;
418 
419   STLDeleteValues(&target_handlers_);
420 
421   port_status_serializer_.reset();
422 
423   notification_registrar_.RemoveAll();
424   pref_change_registrar_.RemoveAll();
425 }
426 
CreateInspectUIHTMLSource()427 content::WebUIDataSource* InspectUI::CreateInspectUIHTMLSource() {
428   content::WebUIDataSource* source =
429       content::WebUIDataSource::Create(chrome::kChromeUIInspectHost);
430   source->AddResourcePath("inspect.css", IDR_INSPECT_CSS);
431   source->AddResourcePath("inspect.js", IDR_INSPECT_JS);
432   source->SetDefaultResource(IDR_INSPECT_HTML);
433   return source;
434 }
435 
UpdateDiscoverUsbDevicesEnabled()436 void InspectUI::UpdateDiscoverUsbDevicesEnabled() {
437   web_ui()->CallJavascriptFunction(
438       "updateDiscoverUsbDevicesEnabled",
439       *GetPrefValue(prefs::kDevToolsDiscoverUsbDevicesEnabled));
440 }
441 
UpdatePortForwardingEnabled()442 void InspectUI::UpdatePortForwardingEnabled() {
443   web_ui()->CallJavascriptFunction(
444       "updatePortForwardingEnabled",
445       *GetPrefValue(prefs::kDevToolsPortForwardingEnabled));
446 }
447 
UpdatePortForwardingConfig()448 void InspectUI::UpdatePortForwardingConfig() {
449   web_ui()->CallJavascriptFunction(
450       "updatePortForwardingConfig",
451       *GetPrefValue(prefs::kDevToolsPortForwardingConfig));
452 }
453 
SetPortForwardingDefaults()454 void InspectUI::SetPortForwardingDefaults() {
455   Profile* profile = Profile::FromWebUI(web_ui());
456   PrefService* prefs = profile->GetPrefs();
457 
458   bool default_set;
459   if (!GetPrefValue(prefs::kDevToolsPortForwardingDefaultSet)->
460       GetAsBoolean(&default_set) || default_set) {
461     return;
462   }
463 
464   // This is the first chrome://inspect invocation on a fresh profile or after
465   // upgrade from a version that did not have kDevToolsPortForwardingDefaultSet.
466   prefs->SetBoolean(prefs::kDevToolsPortForwardingDefaultSet, true);
467 
468   bool enabled;
469   const base::DictionaryValue* config;
470   if (!GetPrefValue(prefs::kDevToolsPortForwardingEnabled)->
471         GetAsBoolean(&enabled) ||
472       !GetPrefValue(prefs::kDevToolsPortForwardingConfig)->
473         GetAsDictionary(&config)) {
474     return;
475   }
476 
477   // Do nothing if user already took explicit action.
478   if (enabled || config->size() != 0)
479     return;
480 
481   base::DictionaryValue default_config;
482   default_config.SetString(
483       kPortForwardingDefaultPort, kPortForwardingDefaultLocation);
484   prefs->Set(prefs::kDevToolsPortForwardingConfig, default_config);
485 }
486 
GetPrefValue(const char * name)487 const base::Value* InspectUI::GetPrefValue(const char* name) {
488   Profile* profile = Profile::FromWebUI(web_ui());
489   return profile->GetPrefs()->FindPreference(name)->GetValue();
490 }
491 
AddTargetUIHandler(scoped_ptr<DevToolsTargetsUIHandler> handler)492 void InspectUI::AddTargetUIHandler(
493     scoped_ptr<DevToolsTargetsUIHandler> handler) {
494   DevToolsTargetsUIHandler* handler_ptr = handler.release();
495   target_handlers_[handler_ptr->source_id()] = handler_ptr;
496 }
497 
FindTargetHandler(const std::string & source_id)498 DevToolsTargetsUIHandler* InspectUI::FindTargetHandler(
499     const std::string& source_id) {
500   TargetHandlerMap::iterator it = target_handlers_.find(source_id);
501      return it != target_handlers_.end() ? it->second : NULL;
502 }
503 
FindTarget(const std::string & source_id,const std::string & target_id)504 DevToolsTargetImpl* InspectUI::FindTarget(
505     const std::string& source_id, const std::string& target_id) {
506   TargetHandlerMap::iterator it = target_handlers_.find(source_id);
507   return it != target_handlers_.end() ?
508          it->second->GetTarget(target_id) : NULL;
509 }
510 
PopulateTargets(const std::string & source,const base::ListValue & targets)511 void InspectUI::PopulateTargets(const std::string& source,
512                                 const base::ListValue& targets) {
513   web_ui()->CallJavascriptFunction("populateTargets",
514                                    base::StringValue(source),
515                                    targets);
516 }
517 
ForceUpdateIfNeeded(const std::string & source_id,const std::string & target_type)518 void InspectUI::ForceUpdateIfNeeded(const std::string& source_id,
519                                     const std::string& target_type) {
520   // TODO(dgozman): remove this after moving discovery to protocol.
521   // See crbug.com/398049.
522   if (target_type != DevToolsTargetImpl::kTargetTypeServiceWorker)
523     return;
524   DevToolsTargetsUIHandler* handler = FindTargetHandler(source_id);
525   if (handler)
526     handler->ForceUpdate();
527 }
528 
PopulatePortStatus(const base::Value & status)529 void InspectUI::PopulatePortStatus(const base::Value& status) {
530   web_ui()->CallJavascriptFunction("populatePortStatus", status);
531 }
532 
ShowIncognitoWarning()533 void InspectUI::ShowIncognitoWarning() {
534   web_ui()->CallJavascriptFunction("showIncognitoWarning");
535 }
536