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