inspect_ui.cc revision 03b57e008b61dfcb1fbad3aea950ae0e001748b0
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 32using content::WebContents; 33using content::WebUIMessageHandler; 34 35namespace { 36 37const char kInitUICommand[] = "init-ui"; 38const char kInspectCommand[] = "inspect"; 39const char kActivateCommand[] = "activate"; 40const char kCloseCommand[] = "close"; 41const char kReloadCommand[] = "reload"; 42const char kOpenCommand[] = "open"; 43const char kInspectBrowser[] = "inspect-browser"; 44const char kLocalHost[] = "localhost"; 45 46const char kDiscoverUsbDevicesEnabledCommand[] = 47 "set-discover-usb-devices-enabled"; 48const char kPortForwardingEnabledCommand[] = 49 "set-port-forwarding-enabled"; 50const char kPortForwardingConfigCommand[] = "set-port-forwarding-config"; 51 52const char kPortForwardingDefaultPort[] = "8080"; 53const char kPortForwardingDefaultLocation[] = "localhost:8080"; 54 55// InspectMessageHandler -------------------------------------------- 56 57class InspectMessageHandler : public WebUIMessageHandler { 58 public: 59 explicit InspectMessageHandler(InspectUI* inspect_ui) 60 : inspect_ui_(inspect_ui) {} 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 83void 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 118void InspectMessageHandler::HandleInitUICommand(const base::ListValue*) { 119 inspect_ui_->InitUI(); 120} 121 122static 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 132void 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 139void 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 146void 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 153void 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 160void 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 168void 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 179void 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 191void 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 204class DevToolsUIBindingsEnabler 205 : public content::WebContentsObserver { 206 public: 207 DevToolsUIBindingsEnabler(WebContents* web_contents, 208 const GURL& url); 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 224DevToolsUIBindingsEnabler::DevToolsUIBindingsEnabler( 225 WebContents* web_contents, 226 const GURL& url) 227 : WebContentsObserver(web_contents), 228 bindings_(web_contents), 229 url_(url) { 230} 231 232DevToolsUIBindings* DevToolsUIBindingsEnabler::GetBindings() { 233 return &bindings_; 234} 235 236void DevToolsUIBindingsEnabler::WebContentsDestroyed() { 237 delete this; 238} 239 240void 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 252InspectUI::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 263InspectUI::~InspectUI() { 264 StopListeningNotifications(); 265} 266 267void InspectUI::InitUI() { 268 SetPortForwardingDefaults(); 269 StartListeningNotifications(); 270 UpdateDiscoverUsbDevicesEnabled(); 271 UpdatePortForwardingEnabled(); 272 UpdatePortForwardingConfig(); 273} 274 275void 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 target->Inspect(Profile::FromWebUI(web_ui())); 280} 281 282void InspectUI::Activate(const std::string& source_id, 283 const std::string& target_id) { 284 DevToolsTargetImpl* target = FindTarget(source_id, target_id); 285 if (target) 286 target->Activate(); 287} 288 289void InspectUI::Close(const std::string& source_id, 290 const std::string& target_id) { 291 DevToolsTargetImpl* target = FindTarget(source_id, target_id); 292 if (target) 293 target->Close(); 294} 295 296void InspectUI::Reload(const std::string& source_id, 297 const std::string& target_id) { 298 DevToolsTargetImpl* target = FindTarget(source_id, target_id); 299 if (target) 300 target->Reload(); 301} 302 303static void NoOp(DevToolsTargetImpl*) {} 304 305void InspectUI::Open(const std::string& source_id, 306 const std::string& browser_id, 307 const std::string& url) { 308 DevToolsTargetsUIHandler* handler = FindTargetHandler(source_id); 309 if (handler) 310 handler->Open(browser_id, url, base::Bind(&NoOp)); 311} 312 313void InspectUI::InspectBrowserWithCustomFrontend( 314 const std::string& source_id, 315 const std::string& browser_id, 316 const GURL& frontend_url) { 317 if (!frontend_url.SchemeIs(content::kChromeUIScheme) && 318 !frontend_url.SchemeIs(content::kChromeDevToolsScheme) && 319 frontend_url.host() != kLocalHost) { 320 return; 321 } 322 323 DevToolsTargetsUIHandler* handler = FindTargetHandler(source_id); 324 if (!handler) 325 return; 326 327 // Fetch agent host from remote browser. 328 scoped_refptr<content::DevToolsAgentHost> agent_host = 329 handler->GetBrowserAgentHost(browser_id); 330 if (agent_host->IsAttached()) 331 return; 332 333 // Create web contents for the front-end. 334 WebContents* inspect_ui = web_ui()->GetWebContents(); 335 WebContents* front_end = inspect_ui->GetDelegate()->OpenURLFromTab( 336 inspect_ui, 337 content::OpenURLParams(frontend_url, 338 content::Referrer(), 339 NEW_FOREGROUND_TAB, 340 content::PAGE_TRANSITION_AUTO_TOPLEVEL, 341 false)); 342 343 // Install devtools bindings. 344 DevToolsUIBindingsEnabler* bindings_enabler = 345 new DevToolsUIBindingsEnabler(front_end, frontend_url); 346 bindings_enabler->GetBindings()->AttachTo(agent_host.get()); 347} 348 349void InspectUI::InspectDevices(Browser* browser) { 350 content::RecordAction(base::UserMetricsAction("InspectDevices")); 351 chrome::NavigateParams params(chrome::GetSingletonTabNavigateParams( 352 browser, GURL(chrome::kChromeUIInspectURL))); 353 params.path_behavior = chrome::NavigateParams::IGNORE_AND_NAVIGATE; 354 ShowSingletonTabOverwritingNTP(browser, params); 355} 356 357void InspectUI::Observe(int type, 358 const content::NotificationSource& source, 359 const content::NotificationDetails& details) { 360 if (source == content::Source<WebContents>(web_ui()->GetWebContents())) 361 StopListeningNotifications(); 362} 363 364void InspectUI::StartListeningNotifications() { 365 if (!target_handlers_.empty()) // Possible when reloading the page. 366 StopListeningNotifications(); 367 368 Profile* profile = Profile::FromWebUI(web_ui()); 369 370 DevToolsTargetsUIHandler::Callback callback = 371 base::Bind(&InspectUI::PopulateTargets, base::Unretained(this)); 372 373 AddTargetUIHandler( 374 DevToolsTargetsUIHandler::CreateForRenderers(callback)); 375 AddTargetUIHandler( 376 DevToolsTargetsUIHandler::CreateForWorkers(callback)); 377 if (profile->IsOffTheRecord()) { 378 ShowIncognitoWarning(); 379 } else { 380 AddTargetUIHandler( 381 DevToolsTargetsUIHandler::CreateForAdb(callback, profile)); 382 } 383 384 port_status_serializer_.reset( 385 new PortForwardingStatusSerializer( 386 base::Bind(&InspectUI::PopulatePortStatus, base::Unretained(this)), 387 profile)); 388 389 notification_registrar_.Add(this, 390 content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED, 391 content::NotificationService::AllSources()); 392 393 pref_change_registrar_.Init(profile->GetPrefs()); 394 pref_change_registrar_.Add(prefs::kDevToolsDiscoverUsbDevicesEnabled, 395 base::Bind(&InspectUI::UpdateDiscoverUsbDevicesEnabled, 396 base::Unretained(this))); 397 pref_change_registrar_.Add(prefs::kDevToolsPortForwardingEnabled, 398 base::Bind(&InspectUI::UpdatePortForwardingEnabled, 399 base::Unretained(this))); 400 pref_change_registrar_.Add(prefs::kDevToolsPortForwardingConfig, 401 base::Bind(&InspectUI::UpdatePortForwardingConfig, 402 base::Unretained(this))); 403} 404 405void InspectUI::StopListeningNotifications() { 406 if (target_handlers_.empty()) 407 return; 408 409 STLDeleteValues(&target_handlers_); 410 411 port_status_serializer_.reset(); 412 413 notification_registrar_.RemoveAll(); 414 pref_change_registrar_.RemoveAll(); 415} 416 417content::WebUIDataSource* InspectUI::CreateInspectUIHTMLSource() { 418 content::WebUIDataSource* source = 419 content::WebUIDataSource::Create(chrome::kChromeUIInspectHost); 420 source->AddResourcePath("inspect.css", IDR_INSPECT_CSS); 421 source->AddResourcePath("inspect.js", IDR_INSPECT_JS); 422 source->SetDefaultResource(IDR_INSPECT_HTML); 423 source->OverrideContentSecurityPolicyFrameSrc( 424 "frame-src chrome://serviceworker-internals;"); 425 serviceworker_webui_.reset(web_ui()->GetWebContents()->CreateWebUI( 426 GURL(content::kChromeUIServiceWorkerInternalsURL))); 427 serviceworker_webui_->OverrideJavaScriptFrame( 428 content::kChromeUIServiceWorkerInternalsHost); 429 return source; 430} 431 432void InspectUI::RenderViewCreated(content::RenderViewHost* render_view_host) { 433 serviceworker_webui_->GetController()->RenderViewCreated(render_view_host); 434} 435 436void InspectUI::RenderViewReused(content::RenderViewHost* render_view_host) { 437 serviceworker_webui_->GetController()->RenderViewReused(render_view_host); 438} 439 440bool InspectUI::OverrideHandleWebUIMessage(const GURL& source_url, 441 const std::string& message, 442 const base::ListValue& args) { 443 if (source_url.SchemeIs(content::kChromeUIScheme) && 444 source_url.host() == content::kChromeUIServiceWorkerInternalsHost) { 445 serviceworker_webui_->ProcessWebUIMessage(source_url, message, args); 446 return true; 447 } 448 return false; 449} 450 451void InspectUI::UpdateDiscoverUsbDevicesEnabled() { 452 web_ui()->CallJavascriptFunction( 453 "updateDiscoverUsbDevicesEnabled", 454 *GetPrefValue(prefs::kDevToolsDiscoverUsbDevicesEnabled)); 455} 456 457void InspectUI::UpdatePortForwardingEnabled() { 458 web_ui()->CallJavascriptFunction( 459 "updatePortForwardingEnabled", 460 *GetPrefValue(prefs::kDevToolsPortForwardingEnabled)); 461} 462 463void InspectUI::UpdatePortForwardingConfig() { 464 web_ui()->CallJavascriptFunction( 465 "updatePortForwardingConfig", 466 *GetPrefValue(prefs::kDevToolsPortForwardingConfig)); 467} 468 469void InspectUI::SetPortForwardingDefaults() { 470 Profile* profile = Profile::FromWebUI(web_ui()); 471 PrefService* prefs = profile->GetPrefs(); 472 473 bool default_set; 474 if (!GetPrefValue(prefs::kDevToolsPortForwardingDefaultSet)-> 475 GetAsBoolean(&default_set) || default_set) { 476 return; 477 } 478 479 // This is the first chrome://inspect invocation on a fresh profile or after 480 // upgrade from a version that did not have kDevToolsPortForwardingDefaultSet. 481 prefs->SetBoolean(prefs::kDevToolsPortForwardingDefaultSet, true); 482 483 bool enabled; 484 const base::DictionaryValue* config; 485 if (!GetPrefValue(prefs::kDevToolsPortForwardingEnabled)-> 486 GetAsBoolean(&enabled) || 487 !GetPrefValue(prefs::kDevToolsPortForwardingConfig)-> 488 GetAsDictionary(&config)) { 489 return; 490 } 491 492 // Do nothing if user already took explicit action. 493 if (enabled || config->size() != 0) 494 return; 495 496 base::DictionaryValue default_config; 497 default_config.SetString( 498 kPortForwardingDefaultPort, kPortForwardingDefaultLocation); 499 prefs->Set(prefs::kDevToolsPortForwardingConfig, default_config); 500} 501 502const base::Value* InspectUI::GetPrefValue(const char* name) { 503 Profile* profile = Profile::FromWebUI(web_ui()); 504 return profile->GetPrefs()->FindPreference(name)->GetValue(); 505} 506 507void InspectUI::AddTargetUIHandler( 508 scoped_ptr<DevToolsTargetsUIHandler> handler) { 509 DevToolsTargetsUIHandler* handler_ptr = handler.release(); 510 target_handlers_[handler_ptr->source_id()] = handler_ptr; 511} 512 513DevToolsTargetsUIHandler* InspectUI::FindTargetHandler( 514 const std::string& source_id) { 515 TargetHandlerMap::iterator it = target_handlers_.find(source_id); 516 return it != target_handlers_.end() ? it->second : NULL; 517} 518 519DevToolsTargetImpl* InspectUI::FindTarget( 520 const std::string& source_id, const std::string& target_id) { 521 TargetHandlerMap::iterator it = target_handlers_.find(source_id); 522 return it != target_handlers_.end() ? 523 it->second->GetTarget(target_id) : NULL; 524} 525 526void InspectUI::PopulateTargets(const std::string& source, 527 const base::ListValue& targets) { 528 web_ui()->CallJavascriptFunction("populateTargets", 529 base::StringValue(source), 530 targets); 531} 532 533void InspectUI::PopulatePortStatus(const base::Value& status) { 534 web_ui()->CallJavascriptFunction("populatePortStatus", status); 535} 536 537void InspectUI::ShowIncognitoWarning() { 538 web_ui()->CallJavascriptFunction("showIncognitoWarning"); 539} 540