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