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