inspect_ui.cc revision 8bcbed890bc3ce4d7a057a8f32cab53fa534672e
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 <set> 8 9#include "base/bind.h" 10#include "base/bind_helpers.h" 11#include "base/json/json_writer.h" 12#include "base/memory/ref_counted_memory.h" 13#include "base/strings/string_number_conversions.h" 14#include "base/strings/string_util.h" 15#include "base/strings/stringprintf.h" 16#include "base/strings/utf_string_conversions.h" 17#include "base/values.h" 18#include "chrome/browser/devtools/devtools_window.h" 19#include "chrome/browser/devtools/port_forwarding_controller.h" 20#include "chrome/browser/extensions/extension_service.h" 21#include "chrome/browser/profiles/profile.h" 22#include "chrome/browser/ui/browser_navigator.h" 23#include "chrome/browser/ui/singleton_tabs.h" 24#include "chrome/browser/ui/tab_contents/tab_contents_iterator.h" 25#include "chrome/browser/ui/webui/extensions/extension_icon_source.h" 26#include "chrome/browser/ui/webui/theme_source.h" 27#include "chrome/common/extensions/extension_constants.h" 28#include "chrome/common/pref_names.h" 29#include "chrome/common/url_constants.h" 30#include "content/public/browser/browser_child_process_observer.h" 31#include "content/public/browser/browser_thread.h" 32#include "content/public/browser/child_process_data.h" 33#include "content/public/browser/devtools_agent_host.h" 34#include "content/public/browser/devtools_client_host.h" 35#include "content/public/browser/devtools_manager.h" 36#include "content/public/browser/favicon_status.h" 37#include "content/public/browser/navigation_entry.h" 38#include "content/public/browser/notification_service.h" 39#include "content/public/browser/notification_source.h" 40#include "content/public/browser/notification_types.h" 41#include "content/public/browser/render_process_host.h" 42#include "content/public/browser/render_view_host.h" 43#include "content/public/browser/user_metrics.h" 44#include "content/public/browser/web_contents.h" 45#include "content/public/browser/web_ui.h" 46#include "content/public/browser/web_ui_data_source.h" 47#include "content/public/browser/web_ui_message_handler.h" 48#include "content/public/browser/worker_service.h" 49#include "content/public/browser/worker_service_observer.h" 50#include "content/public/common/process_type.h" 51#include "grit/browser_resources.h" 52#include "grit/generated_resources.h" 53#include "net/base/escape.h" 54#include "net/base/net_errors.h" 55#include "ui/base/resource/resource_bundle.h" 56 57using content::BrowserThread; 58using content::ChildProcessData; 59using content::DevToolsAgentHost; 60using content::DevToolsClientHost; 61using content::DevToolsManager; 62using content::RenderProcessHost; 63using content::RenderViewHost; 64using content::RenderViewHostDelegate; 65using content::RenderWidgetHost; 66using content::WebContents; 67using content::WebUIMessageHandler; 68using content::WorkerService; 69using content::WorkerServiceObserver; 70 71namespace { 72 73const char kAppTargetType[] = "app"; 74const char kExtensionTargetType[] = "extension"; 75const char kPageTargetType[] = "page"; 76const char kWorkerTargetType[] = "worker"; 77const char kAdbTargetType[] = "adb_page"; 78 79const char kInitUICommand[] = "init-ui"; 80const char kInspectCommand[] = "inspect"; 81const char kActivateCommand[] = "activate"; 82const char kTerminateCommand[] = "terminate"; 83const char kReloadCommand[] = "reload"; 84const char kOpenCommand[] = "open"; 85 86const char kDiscoverUsbDevicesEnabledCommand[] = 87 "set-discover-usb-devices-enabled"; 88const char kPortForwardingEnabledCommand[] = 89 "set-port-forwarding-enabled"; 90const char kPortForwardingConfigCommand[] = "set-port-forwarding-config"; 91 92const char kPortForwardingDefaultPort[] = "8080"; 93const char kPortForwardingDefaultLocation[] = "localhost:8080"; 94 95const char kTargetTypeField[] = "type"; 96const char kAttachedField[] = "attached"; 97const char kProcessIdField[] = "processId"; 98const char kRouteIdField[] = "routeId"; 99const char kUrlField[] = "url"; 100const char kNameField[] = "name"; 101const char kFaviconUrlField[] = "faviconUrl"; 102const char kDescription[] = "description"; 103const char kPidField[] = "pid"; 104const char kAdbConnectedField[] = "adbConnected"; 105const char kAdbModelField[] = "adbModel"; 106const char kAdbSerialField[] = "adbSerial"; 107const char kAdbBrowserProductField[] = "adbBrowserProduct"; 108const char kAdbBrowserPackageField[] = "adbBrowserPackage"; 109const char kAdbBrowserVersionField[] = "adbBrowserVersion"; 110const char kAdbGlobalIdField[] = "adbGlobalId"; 111const char kAdbBrowsersField[] = "browsers"; 112const char kAdbPagesField[] = "pages"; 113const char kAdbPortStatus[] = "adbPortStatus"; 114const char kAdbScreenWidthField[] = "adbScreenWidth"; 115const char kAdbScreenHeightField[] = "adbScreenHeight"; 116const char kAdbAttachedForeignField[] = "adbAttachedForeign"; 117const char kGuestList[] = "guests"; 118 119DictionaryValue* BuildTargetDescriptor( 120 const std::string& target_type, 121 bool attached, 122 const GURL& url, 123 const std::string& name, 124 const GURL& favicon_url, 125 const std::string& description, 126 int process_id, 127 int route_id, 128 base::ProcessHandle handle = base::kNullProcessHandle) { 129 DictionaryValue* target_data = new DictionaryValue(); 130 target_data->SetString(kTargetTypeField, target_type); 131 target_data->SetBoolean(kAttachedField, attached); 132 target_data->SetInteger(kProcessIdField, process_id); 133 target_data->SetInteger(kRouteIdField, route_id); 134 target_data->SetString(kUrlField, url.spec()); 135 target_data->SetString(kNameField, net::EscapeForHTML(name)); 136 target_data->SetInteger(kPidField, base::GetProcId(handle)); 137 target_data->SetString(kFaviconUrlField, favicon_url.spec()); 138 target_data->SetString(kDescription, description); 139 140 return target_data; 141} 142 143bool HasClientHost(RenderViewHost* rvh) { 144 if (!DevToolsAgentHost::HasFor(rvh)) 145 return false; 146 147 scoped_refptr<DevToolsAgentHost> agent( 148 DevToolsAgentHost::GetOrCreateFor(rvh)); 149 return agent->IsAttached(); 150} 151 152bool HasClientHost(int process_id, int route_id) { 153 if (!DevToolsAgentHost::HasForWorker(process_id, route_id)) 154 return false; 155 156 scoped_refptr<DevToolsAgentHost> agent( 157 DevToolsAgentHost::GetForWorker(process_id, route_id)); 158 return agent->IsAttached(); 159} 160 161DictionaryValue* BuildTargetDescriptor(RenderViewHost* rvh, bool is_tab) { 162 WebContents* web_contents = WebContents::FromRenderViewHost(rvh); 163 std::string title; 164 std::string target_type = is_tab ? kPageTargetType : ""; 165 GURL url; 166 GURL favicon_url; 167 if (web_contents) { 168 url = web_contents->GetURL(); 169 title = UTF16ToUTF8(web_contents->GetTitle()); 170 content::NavigationController& controller = web_contents->GetController(); 171 content::NavigationEntry* entry = controller.GetActiveEntry(); 172 if (entry != NULL && entry->GetURL().is_valid()) 173 favicon_url = entry->GetFavicon().url; 174 175 Profile* profile = Profile::FromBrowserContext( 176 web_contents->GetBrowserContext()); 177 if (profile) { 178 ExtensionService* extension_service = profile->GetExtensionService(); 179 const extensions::Extension* extension = extension_service-> 180 extensions()->GetByID(url.host()); 181 if (extension) { 182 if (extension->is_hosted_app() 183 || extension->is_legacy_packaged_app() 184 || extension->is_platform_app()) 185 target_type = kAppTargetType; 186 else 187 target_type = kExtensionTargetType; 188 title = extension->name(); 189 favicon_url = extensions::ExtensionIconSource::GetIconURL( 190 extension, extension_misc::EXTENSION_ICON_SMALLISH, 191 ExtensionIconSet::MATCH_BIGGER, false, NULL); 192 } 193 } 194 } 195 196 return BuildTargetDescriptor(target_type, 197 HasClientHost(rvh), 198 url, 199 title, 200 favicon_url, 201 "", 202 rvh->GetProcess()->GetID(), 203 rvh->GetRoutingID()); 204} 205 206class InspectMessageHandler : public WebUIMessageHandler { 207 public: 208 explicit InspectMessageHandler(InspectUI* inspect_ui) 209 : inspect_ui_(inspect_ui) {} 210 virtual ~InspectMessageHandler() {} 211 212 private: 213 // WebUIMessageHandler implementation. 214 virtual void RegisterMessages() OVERRIDE; 215 216 void HandleInitUICommand(const ListValue* args); 217 void HandleInspectCommand(const ListValue* args); 218 void HandleActivateCommand(const ListValue* args); 219 void HandleTerminateCommand(const ListValue* args); 220 void HandleReloadCommand(const ListValue* args); 221 void HandleOpenCommand(const ListValue* args); 222 void HandleBooleanPrefChanged(const char* pref_name, 223 const ListValue* args); 224 void HandlePortForwardingConfigCommand(const ListValue* args); 225 226 static bool GetProcessAndRouteId(const ListValue* args, 227 int* process_id, 228 int* route_id); 229 230 static bool GetRemotePageId(const ListValue* args, std::string* page_id); 231 232 InspectUI* inspect_ui_; 233 234 DISALLOW_COPY_AND_ASSIGN(InspectMessageHandler); 235}; 236 237void InspectMessageHandler::RegisterMessages() { 238 web_ui()->RegisterMessageCallback(kInitUICommand, 239 base::Bind(&InspectMessageHandler::HandleInitUICommand, 240 base::Unretained(this))); 241 web_ui()->RegisterMessageCallback(kInspectCommand, 242 base::Bind(&InspectMessageHandler::HandleInspectCommand, 243 base::Unretained(this))); 244 web_ui()->RegisterMessageCallback(kActivateCommand, 245 base::Bind(&InspectMessageHandler::HandleActivateCommand, 246 base::Unretained(this))); 247 web_ui()->RegisterMessageCallback(kTerminateCommand, 248 base::Bind(&InspectMessageHandler::HandleTerminateCommand, 249 base::Unretained(this))); 250 web_ui()->RegisterMessageCallback(kDiscoverUsbDevicesEnabledCommand, 251 base::Bind(&InspectMessageHandler::HandleBooleanPrefChanged, 252 base::Unretained(this), 253 &prefs::kDevToolsDiscoverUsbDevicesEnabled[0])); 254 web_ui()->RegisterMessageCallback(kPortForwardingEnabledCommand, 255 base::Bind(&InspectMessageHandler::HandleBooleanPrefChanged, 256 base::Unretained(this), 257 &prefs::kDevToolsPortForwardingEnabled[0])); 258 web_ui()->RegisterMessageCallback(kPortForwardingConfigCommand, 259 base::Bind(&InspectMessageHandler::HandlePortForwardingConfigCommand, 260 base::Unretained(this))); 261 web_ui()->RegisterMessageCallback(kReloadCommand, 262 base::Bind(&InspectMessageHandler::HandleReloadCommand, 263 base::Unretained(this))); 264 web_ui()->RegisterMessageCallback(kOpenCommand, 265 base::Bind(&InspectMessageHandler::HandleOpenCommand, 266 base::Unretained(this))); 267} 268 269void InspectMessageHandler::HandleInitUICommand(const ListValue*) { 270 inspect_ui_->InitUI(); 271} 272 273void InspectMessageHandler::HandleInspectCommand(const ListValue* args) { 274 Profile* profile = Profile::FromWebUI(web_ui()); 275 if (!profile) 276 return; 277 278 std::string page_id; 279 if (GetRemotePageId(args, &page_id)) { 280 inspect_ui_->InspectRemotePage(page_id); 281 return; 282 } 283 284 int process_id; 285 int route_id; 286 if (!GetProcessAndRouteId(args, &process_id, &route_id) || process_id == 0 287 || route_id == 0) { 288 return; 289 } 290 291 RenderViewHost* rvh = RenderViewHost::FromID(process_id, route_id); 292 if (rvh) { 293 DevToolsWindow::OpenDevToolsWindow(rvh); 294 return; 295 } 296 297 scoped_refptr<DevToolsAgentHost> agent_host( 298 DevToolsAgentHost::GetForWorker(process_id, route_id)); 299 if (!agent_host.get()) 300 return; 301 302 DevToolsWindow::OpenDevToolsWindowForWorker(profile, agent_host.get()); 303} 304 305void InspectMessageHandler::HandleActivateCommand(const ListValue* args) { 306 std::string page_id; 307 if (GetRemotePageId(args, &page_id)) 308 inspect_ui_->ActivateRemotePage(page_id); 309} 310 311static void TerminateWorker(int process_id, int route_id) { 312 WorkerService::GetInstance()->TerminateWorker(process_id, route_id); 313} 314 315void InspectMessageHandler::HandleTerminateCommand(const ListValue* args) { 316 std::string page_id; 317 if (GetRemotePageId(args, &page_id)) { 318 inspect_ui_->CloseRemotePage(page_id); 319 return; 320 } 321 322 int process_id; 323 int route_id; 324 if (!GetProcessAndRouteId(args, &process_id, &route_id)) 325 return; 326 327 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, 328 base::Bind(&TerminateWorker, process_id, route_id)); 329} 330 331void InspectMessageHandler::HandleReloadCommand(const ListValue* args) { 332 std::string page_id; 333 if (GetRemotePageId(args, &page_id)) 334 inspect_ui_->ReloadRemotePage(page_id); 335} 336 337void InspectMessageHandler::HandleOpenCommand(const ListValue* args) { 338 std::string browser_id; 339 std::string url; 340 if (args->GetSize() == 2 && 341 args->GetString(0, &browser_id) && 342 args->GetString(1, &url)) { 343 inspect_ui_->OpenRemotePage(browser_id, url); 344 } 345} 346 347bool InspectMessageHandler::GetProcessAndRouteId(const ListValue* args, 348 int* process_id, 349 int* route_id) { 350 const DictionaryValue* data; 351 if (args->GetSize() == 1 && args->GetDictionary(0, &data) && 352 data->GetInteger(kProcessIdField, process_id) && 353 data->GetInteger(kRouteIdField, route_id)) { 354 return true; 355 } 356 return false; 357} 358 359void InspectMessageHandler::HandleBooleanPrefChanged( 360 const char* pref_name, 361 const ListValue* args) { 362 Profile* profile = Profile::FromWebUI(web_ui()); 363 if (!profile) 364 return; 365 366 bool enabled; 367 if (args->GetSize() == 1 && args->GetBoolean(0, &enabled)) 368 profile->GetPrefs()->SetBoolean(pref_name, enabled); 369} 370 371void InspectMessageHandler::HandlePortForwardingConfigCommand( 372 const ListValue* args) { 373 Profile* profile = Profile::FromWebUI(web_ui()); 374 if (!profile) 375 return; 376 377 const DictionaryValue* dict_src; 378 if (args->GetSize() == 1 && args->GetDictionary(0, &dict_src)) 379 profile->GetPrefs()->Set(prefs::kDevToolsPortForwardingConfig, *dict_src); 380} 381 382bool InspectMessageHandler::GetRemotePageId(const ListValue* args, 383 std::string* page_id) { 384 const DictionaryValue* data; 385 if (args->GetSize() == 1 && args->GetDictionary(0, &data) && 386 data->GetString(kAdbGlobalIdField, page_id)) { 387 return true; 388 } 389 return false; 390} 391 392} // namespace 393 394class InspectUI::WorkerCreationDestructionListener 395 : public WorkerServiceObserver, 396 public content::BrowserChildProcessObserver, 397 public base::RefCountedThreadSafe<WorkerCreationDestructionListener> { 398 public: 399 WorkerCreationDestructionListener() 400 : discovery_ui_(NULL) {} 401 402 void Init(InspectUI* workers_ui) { 403 DCHECK(workers_ui); 404 DCHECK(!discovery_ui_); 405 discovery_ui_ = workers_ui; 406 BrowserChildProcessObserver::Add(this); 407 BrowserThread::PostTask( 408 BrowserThread::IO, FROM_HERE, 409 base::Bind(&WorkerCreationDestructionListener::RegisterObserver, 410 this)); 411 } 412 413 void InspectUIDestroyed() { 414 DCHECK(discovery_ui_); 415 discovery_ui_ = NULL; 416 BrowserChildProcessObserver::Remove(this); 417 BrowserThread::PostTask( 418 BrowserThread::IO, FROM_HERE, 419 base::Bind(&WorkerCreationDestructionListener::UnregisterObserver, 420 this)); 421 } 422 423 void UpdateUI() { 424 BrowserThread::PostTask( 425 BrowserThread::IO, FROM_HERE, 426 base::Bind(&WorkerCreationDestructionListener::CollectWorkersData, 427 this)); 428 } 429 430 private: 431 friend class base::RefCountedThreadSafe<WorkerCreationDestructionListener>; 432 virtual ~WorkerCreationDestructionListener() {} 433 434 virtual void WorkerCreated( 435 const GURL& url, 436 const string16& name, 437 int process_id, 438 int route_id) OVERRIDE { 439 CollectWorkersData(); 440 } 441 442 virtual void WorkerDestroyed(int process_id, int route_id) OVERRIDE { 443 CollectWorkersData(); 444 } 445 446 virtual void BrowserChildProcessHostConnected( 447 const content::ChildProcessData& data) OVERRIDE { 448 if (data.process_type == content::PROCESS_TYPE_WORKER) 449 UpdateUI(); 450 } 451 452 virtual void BrowserChildProcessHostDisconnected( 453 const content::ChildProcessData& data) OVERRIDE { 454 if (data.process_type == content::PROCESS_TYPE_WORKER) 455 UpdateUI(); 456 } 457 458 void CollectWorkersData() { 459 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 460 BrowserThread::PostTask( 461 BrowserThread::UI, FROM_HERE, 462 base::Bind(&WorkerCreationDestructionListener::PopulateWorkersList, 463 this, WorkerService::GetInstance()->GetWorkers())); 464 } 465 466 void RegisterObserver() { 467 WorkerService::GetInstance()->AddObserver(this); 468 } 469 470 void UnregisterObserver() { 471 WorkerService::GetInstance()->RemoveObserver(this); 472 } 473 474 void PopulateWorkersList( 475 const std::vector<WorkerService::WorkerInfo>& worker_info) { 476 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 477 if (!discovery_ui_) 478 return; 479 480 ListValue target_list; 481 for (size_t i = 0; i < worker_info.size(); ++i) { 482 if (!worker_info[i].handle) 483 continue; // Process is still being created. 484 target_list.Append(BuildTargetDescriptor( 485 kWorkerTargetType, 486 HasClientHost(worker_info[i].process_id, worker_info[i].route_id), 487 worker_info[i].url, 488 UTF16ToUTF8(worker_info[i].name), 489 GURL(), 490 "", 491 worker_info[i].process_id, 492 worker_info[i].route_id, 493 worker_info[i].handle)); 494 } 495 discovery_ui_->web_ui()->CallJavascriptFunction( 496 "populateWorkersList", target_list); 497 } 498 499 InspectUI* discovery_ui_; 500}; 501 502InspectUI::InspectUI(content::WebUI* web_ui) 503 : WebUIController(web_ui) { 504 web_ui->AddMessageHandler(new InspectMessageHandler(this)); 505 Profile* profile = Profile::FromWebUI(web_ui); 506 content::WebUIDataSource::Add(profile, CreateInspectUIHTMLSource()); 507 508 // Set up the chrome://theme/ source. 509 ThemeSource* theme = new ThemeSource(profile); 510 content::URLDataSource::Add(profile, theme); 511} 512 513InspectUI::~InspectUI() { 514 StopListeningNotifications(); 515} 516 517void InspectUI::InitUI() { 518 SetPortForwardingDefaults(); 519 StartListeningNotifications(); 520 PopulateLists(); 521 UpdateDiscoverUsbDevicesEnabled(); 522 UpdatePortForwardingEnabled(); 523 UpdatePortForwardingConfig(); 524 observer_->UpdateUI(); 525} 526 527void InspectUI::InspectRemotePage(const std::string& id) { 528 RemotePages::iterator it = remote_pages_.find(id); 529 if (it != remote_pages_.end()) { 530 Profile* profile = Profile::FromWebUI(web_ui()); 531 it->second->Inspect(profile); 532 } 533} 534 535void InspectUI::ActivateRemotePage(const std::string& id) { 536 RemotePages::iterator it = remote_pages_.find(id); 537 if (it != remote_pages_.end()) 538 it->second->Activate(); 539} 540 541void InspectUI::ReloadRemotePage(const std::string& id) { 542 RemotePages::iterator it = remote_pages_.find(id); 543 if (it != remote_pages_.end()) 544 it->second->Reload(); 545} 546 547void InspectUI::CloseRemotePage(const std::string& id) { 548 RemotePages::iterator it = remote_pages_.find(id); 549 if (it != remote_pages_.end()) 550 it->second->Close(); 551} 552 553void InspectUI::OpenRemotePage(const std::string& browser_id, 554 const std::string& url) { 555 GURL gurl(url); 556 if (!gurl.is_valid()) { 557 gurl = GURL("http://" + url); 558 if (!gurl.is_valid()) 559 return; 560 } 561 RemoteBrowsers::iterator it = remote_browsers_.find(browser_id); 562 if (it != remote_browsers_.end()) 563 it->second->Open(gurl.spec()); 564} 565 566void InspectUI::InspectDevices(Browser* browser) { 567 content::RecordAction(content::UserMetricsAction("InspectDevices")); 568 chrome::NavigateParams params(chrome::GetSingletonTabNavigateParams( 569 browser, GURL(chrome::kChromeUIInspectURL))); 570 params.path_behavior = chrome::NavigateParams::IGNORE_AND_NAVIGATE; 571 ShowSingletonTabOverwritingNTP(browser, params); 572} 573 574void InspectUI::PopulateLists() { 575 std::set<RenderViewHost*> tab_rvhs; 576 for (TabContentsIterator it; !it.done(); it.Next()) 577 tab_rvhs.insert(it->GetRenderViewHost()); 578 579 scoped_ptr<ListValue> target_list(new ListValue()); 580 581 std::vector<RenderViewHost*> rvh_vector = 582 DevToolsAgentHost::GetValidRenderViewHosts(); 583 584 std::map<WebContents*, DictionaryValue*> description_map; 585 std::vector<WebContents*> guest_contents; 586 587 for (std::vector<RenderViewHost*>::iterator it(rvh_vector.begin()); 588 it != rvh_vector.end(); it++) { 589 bool is_tab = tab_rvhs.find(*it) != tab_rvhs.end(); 590 RenderViewHost* rvh = (*it); 591 WebContents* web_contents = WebContents::FromRenderViewHost(rvh); 592 if (rvh->GetProcess()->IsGuest()) { 593 if (web_contents) 594 guest_contents.push_back(web_contents); 595 } else { 596 DictionaryValue* dictionary = BuildTargetDescriptor(rvh, is_tab); 597 if (web_contents) 598 description_map[web_contents] = dictionary; 599 target_list->Append(dictionary); 600 } 601 } 602 603 // Add the list of guest-views to each of its embedders. 604 for (std::vector<WebContents*>::iterator it(guest_contents.begin()); 605 it != guest_contents.end(); ++it) { 606 WebContents* guest = (*it); 607 WebContents* embedder = guest->GetEmbedderWebContents(); 608 if (embedder && description_map.count(embedder) > 0) { 609 DictionaryValue* description = description_map[embedder]; 610 ListValue* guests = NULL; 611 if (!description->GetList(kGuestList, &guests)) { 612 guests = new ListValue(); 613 description->Set(kGuestList, guests); 614 } 615 RenderViewHost* rvh = guest->GetRenderViewHost(); 616 if (rvh) 617 guests->Append(BuildTargetDescriptor(rvh, false)); 618 } 619 } 620 621 web_ui()->CallJavascriptFunction("populateLists", *target_list.get()); 622} 623 624void InspectUI::Observe(int type, 625 const content::NotificationSource& source, 626 const content::NotificationDetails& details) { 627 if (source != content::Source<WebContents>(web_ui()->GetWebContents())) 628 PopulateLists(); 629 else if (type == content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED) 630 StopListeningNotifications(); 631} 632 633void InspectUI::StartListeningNotifications() { 634 if (observer_) 635 return; 636 637 observer_ = new WorkerCreationDestructionListener(); 638 observer_->Init(this); 639 640 Profile* profile = Profile::FromWebUI(web_ui()); 641 DevToolsAdbBridge* adb_bridge = 642 DevToolsAdbBridge::Factory::GetForProfile(profile); 643 if (adb_bridge) 644 adb_bridge->AddListener(this); 645 646 notification_registrar_.Add(this, 647 content::NOTIFICATION_WEB_CONTENTS_CONNECTED, 648 content::NotificationService::AllSources()); 649 notification_registrar_.Add(this, 650 content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED, 651 content::NotificationService::AllSources()); 652 notification_registrar_.Add(this, 653 content::NOTIFICATION_WEB_CONTENTS_DESTROYED, 654 content::NotificationService::AllSources()); 655 656 pref_change_registrar_.Init(profile->GetPrefs()); 657 pref_change_registrar_.Add(prefs::kDevToolsDiscoverUsbDevicesEnabled, 658 base::Bind(&InspectUI::UpdateDiscoverUsbDevicesEnabled, 659 base::Unretained(this))); 660 pref_change_registrar_.Add(prefs::kDevToolsPortForwardingEnabled, 661 base::Bind(&InspectUI::UpdatePortForwardingEnabled, 662 base::Unretained(this))); 663 pref_change_registrar_.Add(prefs::kDevToolsPortForwardingConfig, 664 base::Bind(&InspectUI::UpdatePortForwardingConfig, 665 base::Unretained(this))); 666} 667 668void InspectUI::StopListeningNotifications() 669{ 670 if (!observer_.get()) 671 return; 672 Profile* profile = Profile::FromWebUI(web_ui()); 673 DevToolsAdbBridge* adb_bridge = 674 DevToolsAdbBridge::Factory::GetForProfile(profile); 675 if (adb_bridge) 676 adb_bridge->RemoveListener(this); 677 observer_->InspectUIDestroyed(); 678 observer_ = NULL; 679 notification_registrar_.RemoveAll(); 680 pref_change_registrar_.RemoveAll(); 681} 682 683content::WebUIDataSource* InspectUI::CreateInspectUIHTMLSource() { 684 content::WebUIDataSource* source = 685 content::WebUIDataSource::Create(chrome::kChromeUIInspectHost); 686 source->AddResourcePath("inspect.css", IDR_INSPECT_CSS); 687 source->AddResourcePath("inspect.js", IDR_INSPECT_JS); 688 source->SetDefaultResource(IDR_INSPECT_HTML); 689 return source; 690} 691 692void InspectUI::RemoteDevicesChanged( 693 DevToolsAdbBridge::RemoteDevices* devices) { 694 Profile* profile = Profile::FromWebUI(web_ui()); 695 PortForwardingController* port_forwarding_controller = 696 PortForwardingController::Factory::GetForProfile(profile); 697 PortForwardingController::DevicesStatus port_forwarding_status; 698 if (port_forwarding_controller) 699 port_forwarding_status = 700 port_forwarding_controller->UpdateDeviceList(*devices); 701 702 remote_browsers_.clear(); 703 remote_pages_.clear(); 704 ListValue device_list; 705 for (DevToolsAdbBridge::RemoteDevices::iterator dit = devices->begin(); 706 dit != devices->end(); ++dit) { 707 DevToolsAdbBridge::RemoteDevice* device = dit->get(); 708 DictionaryValue* device_data = new DictionaryValue(); 709 device_data->SetString(kAdbModelField, device->GetModel()); 710 device_data->SetString(kAdbSerialField, device->GetSerial()); 711 device_data->SetBoolean(kAdbConnectedField, device->IsConnected()); 712 std::string device_id = base::StringPrintf( 713 "device:%s", 714 device->GetSerial().c_str()); 715 device_data->SetString(kAdbGlobalIdField, device_id); 716 ListValue* browser_list = new ListValue(); 717 device_data->Set(kAdbBrowsersField, browser_list); 718 719 DevToolsAdbBridge::RemoteBrowsers& browsers = device->browsers(); 720 for (DevToolsAdbBridge::RemoteBrowsers::iterator bit = 721 browsers.begin(); bit != browsers.end(); ++bit) { 722 DevToolsAdbBridge::RemoteBrowser* browser = bit->get(); 723 DictionaryValue* browser_data = new DictionaryValue(); 724 browser_data->SetString(kAdbBrowserProductField, browser->product()); 725 browser_data->SetString(kAdbBrowserPackageField, browser->package()); 726 browser_data->SetString(kAdbBrowserVersionField, browser->version()); 727 std::string browser_id = base::StringPrintf( 728 "browser:%s:%s:%s", 729 device->GetSerial().c_str(), 730 browser->product().c_str(), // Force sorting by product name. 731 browser->socket().c_str()); 732 browser_data->SetString(kAdbGlobalIdField, browser_id); 733 remote_browsers_[browser_id] = browser; 734 ListValue* page_list = new ListValue(); 735 browser_data->Set(kAdbPagesField, page_list); 736 737 DevToolsAdbBridge::RemotePages& pages = browser->pages(); 738 for (DevToolsAdbBridge::RemotePages::iterator it = 739 pages.begin(); it != pages.end(); ++it) { 740 DevToolsAdbBridge::RemotePage* page = it->get(); 741 DictionaryValue* page_data = BuildTargetDescriptor( 742 kAdbTargetType, page->attached(), 743 GURL(page->url()), page->title(), GURL(page->favicon_url()), 744 page->description(), 0, 0); 745 std::string page_id = base::StringPrintf("page:%s:%s:%s", 746 device->GetSerial().c_str(), 747 browser->socket().c_str(), 748 page->id().c_str()); 749 page_data->SetString(kAdbGlobalIdField, page_id); 750 page_data->SetBoolean(kAdbAttachedForeignField, 751 page->attached() && !page->HasDevToolsWindow()); 752 // Pass the screen size in the page object to make sure that 753 // the caching logic does not prevent the page item from updating 754 // when the screen size changes. 755 gfx::Size screen_size = device->screen_size(); 756 page_data->SetInteger(kAdbScreenWidthField, screen_size.width()); 757 page_data->SetInteger(kAdbScreenHeightField, screen_size.height()); 758 remote_pages_[page_id] = page; 759 page_list->Append(page_data); 760 } 761 browser_list->Append(browser_data); 762 } 763 764 if (port_forwarding_controller) { 765 PortForwardingController::DevicesStatus::iterator sit = 766 port_forwarding_status.find(device->GetSerial()); 767 if (sit != port_forwarding_status.end()) { 768 DictionaryValue* port_status_dict = new DictionaryValue(); 769 typedef PortForwardingController::PortStatusMap StatusMap; 770 const StatusMap& port_status = sit->second; 771 for (StatusMap::const_iterator it = port_status.begin(); 772 it != port_status.end(); ++it) { 773 port_status_dict->SetInteger( 774 base::StringPrintf("%d", it->first), it->second); 775 } 776 device_data->Set(kAdbPortStatus, port_status_dict); 777 } 778 } 779 780 device_list.Append(device_data); 781 } 782 web_ui()->CallJavascriptFunction("populateDeviceLists", device_list); 783} 784 785void InspectUI::UpdateDiscoverUsbDevicesEnabled() { 786 const Value* value = GetPrefValue(prefs::kDevToolsDiscoverUsbDevicesEnabled); 787 web_ui()->CallJavascriptFunction("updateDiscoverUsbDevicesEnabled", *value); 788 789 // Configure adb bridge. 790 Profile* profile = Profile::FromWebUI(web_ui()); 791 DevToolsAdbBridge* adb_bridge = 792 DevToolsAdbBridge::Factory::GetForProfile(profile); 793 if (adb_bridge) { 794 bool enabled = false; 795 value->GetAsBoolean(&enabled); 796 adb_bridge->set_discover_usb_devices(enabled); 797 } 798} 799 800void InspectUI::UpdatePortForwardingEnabled() { 801 web_ui()->CallJavascriptFunction("updatePortForwardingEnabled", 802 *GetPrefValue(prefs::kDevToolsPortForwardingEnabled)); 803 804} 805 806void InspectUI::UpdatePortForwardingConfig() { 807 web_ui()->CallJavascriptFunction("updatePortForwardingConfig", 808 *GetPrefValue(prefs::kDevToolsPortForwardingConfig)); 809} 810 811void InspectUI::SetPortForwardingDefaults() { 812 Profile* profile = Profile::FromWebUI(web_ui()); 813 PrefService* prefs = profile->GetPrefs(); 814 815 bool default_set; 816 if (!GetPrefValue(prefs::kDevToolsPortForwardingDefaultSet)-> 817 GetAsBoolean(&default_set) || default_set) { 818 return; 819 } 820 821 // This is the first chrome://inspect invocation on a fresh profile or after 822 // upgrade from a version that did not have kDevToolsPortForwardingDefaultSet. 823 prefs->SetBoolean(prefs::kDevToolsPortForwardingDefaultSet, true); 824 825 bool enabled; 826 const base::DictionaryValue* config; 827 if (!GetPrefValue(prefs::kDevToolsPortForwardingEnabled)-> 828 GetAsBoolean(&enabled) || 829 !GetPrefValue(prefs::kDevToolsPortForwardingConfig)-> 830 GetAsDictionary(&config)) { 831 return; 832 } 833 834 // Do nothing if user already took explicit action. 835 if (enabled || config->size() != 0) 836 return; 837 838 base::DictionaryValue default_config; 839 default_config.SetString( 840 kPortForwardingDefaultPort, kPortForwardingDefaultLocation); 841 prefs->Set(prefs::kDevToolsPortForwardingConfig, default_config); 842} 843 844const base::Value* InspectUI::GetPrefValue(const char* name) { 845 Profile* profile = Profile::FromWebUI(web_ui()); 846 return profile->GetPrefs()->FindPreference(name)->GetValue(); 847} 848