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 // Implements the Chrome Extensions Debugger API. 6 7 #include "chrome/browser/extensions/api/debugger/debugger_api.h" 8 9 #include <map> 10 #include <set> 11 12 #include "base/command_line.h" 13 #include "base/json/json_reader.h" 14 #include "base/json/json_writer.h" 15 #include "base/memory/scoped_ptr.h" 16 #include "base/memory/singleton.h" 17 #include "base/strings/string_number_conversions.h" 18 #include "base/strings/utf_string_conversions.h" 19 #include "base/values.h" 20 #include "chrome/browser/chrome_notification_types.h" 21 #include "chrome/browser/extensions/api/debugger/debugger_api_constants.h" 22 #include "chrome/browser/extensions/event_router.h" 23 #include "chrome/browser/extensions/extension_host.h" 24 #include "chrome/browser/extensions/extension_service.h" 25 #include "chrome/browser/extensions/extension_system.h" 26 #include "chrome/browser/extensions/extension_tab_util.h" 27 #include "chrome/browser/infobars/confirm_infobar_delegate.h" 28 #include "chrome/browser/infobars/infobar.h" 29 #include "chrome/browser/infobars/infobar_service.h" 30 #include "chrome/browser/profiles/profile.h" 31 #include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h" 32 #include "chrome/common/chrome_switches.h" 33 #include "chrome/common/extensions/extension.h" 34 #include "content/public/browser/devtools_agent_host.h" 35 #include "content/public/browser/devtools_client_host.h" 36 #include "content/public/browser/devtools_http_handler.h" 37 #include "content/public/browser/devtools_manager.h" 38 #include "content/public/browser/favicon_status.h" 39 #include "content/public/browser/navigation_entry.h" 40 #include "content/public/browser/notification_service.h" 41 #include "content/public/browser/notification_source.h" 42 #include "content/public/browser/render_process_host.h" 43 #include "content/public/browser/render_view_host.h" 44 #include "content/public/browser/render_widget_host.h" 45 #include "content/public/browser/web_contents.h" 46 #include "content/public/browser/worker_service.h" 47 #include "content/public/common/content_client.h" 48 #include "content/public/common/url_utils.h" 49 #include "extensions/common/error_utils.h" 50 #include "grit/generated_resources.h" 51 #include "ui/base/l10n/l10n_util.h" 52 53 using content::DevToolsAgentHost; 54 using content::DevToolsClientHost; 55 using content::DevToolsHttpHandler; 56 using content::DevToolsManager; 57 using content::RenderProcessHost; 58 using content::RenderViewHost; 59 using content::RenderWidgetHost; 60 using content::WebContents; 61 using content::WorkerService; 62 using extensions::ErrorUtils; 63 64 namespace keys = debugger_api_constants; 65 namespace Attach = extensions::api::debugger::Attach; 66 namespace Detach = extensions::api::debugger::Detach; 67 namespace OnDetach = extensions::api::debugger::OnDetach; 68 namespace OnEvent = extensions::api::debugger::OnEvent; 69 namespace SendCommand = extensions::api::debugger::SendCommand; 70 71 namespace { 72 class ExtensionDevToolsInfoBarDelegate; 73 } // namespace 74 75 76 // ExtensionDevToolsClientHost ------------------------------------------------ 77 78 class ExtensionDevToolsClientHost : public DevToolsClientHost, 79 public content::NotificationObserver { 80 public: 81 ExtensionDevToolsClientHost( 82 Profile* profile, 83 DevToolsAgentHost* agent_host, 84 const std::string& extension_id, 85 const std::string& extension_name, 86 const Debuggee& debuggee, 87 ExtensionDevToolsInfoBarDelegate* infobar); 88 89 virtual ~ExtensionDevToolsClientHost(); 90 91 const std::string& extension_id() { return extension_id_; } 92 void Close(); 93 void SendMessageToBackend(DebuggerSendCommandFunction* function, 94 const std::string& method, 95 SendCommand::Params::CommandParams* command_params); 96 97 // Marks connection as to-be-terminated by the user. 98 void MarkAsDismissed(); 99 100 // DevToolsClientHost interface 101 virtual void InspectedContentsClosing() OVERRIDE; 102 virtual void DispatchOnInspectorFrontend(const std::string& message) OVERRIDE; 103 virtual void ReplacedWithAnotherClient() OVERRIDE; 104 105 private: 106 void SendDetachedEvent(); 107 108 // content::NotificationObserver implementation. 109 virtual void Observe(int type, 110 const content::NotificationSource& source, 111 const content::NotificationDetails& details) OVERRIDE; 112 113 Profile* profile_; 114 scoped_refptr<DevToolsAgentHost> agent_host_; 115 std::string extension_id_; 116 Debuggee debuggee_; 117 content::NotificationRegistrar registrar_; 118 int last_request_id_; 119 typedef std::map<int, scoped_refptr<DebuggerSendCommandFunction> > 120 PendingRequests; 121 PendingRequests pending_requests_; 122 ExtensionDevToolsInfoBarDelegate* infobar_; 123 OnDetach::Reason detach_reason_; 124 125 DISALLOW_COPY_AND_ASSIGN(ExtensionDevToolsClientHost); 126 }; 127 128 // The member function declarations come after the other class declarations, so 129 // they can call members on them. 130 131 132 namespace { 133 134 // Helpers -------------------------------------------------------------------- 135 136 void CopyDebuggee(Debuggee* dst, const Debuggee& src) { 137 if (src.tab_id) 138 dst->tab_id.reset(new int(*src.tab_id)); 139 if (src.extension_id) 140 dst->extension_id.reset(new std::string(*src.extension_id)); 141 if (src.target_id) 142 dst->target_id.reset(new std::string(*src.target_id)); 143 } 144 145 146 // ExtensionDevToolsInfoBarDelegate ------------------------------------------- 147 148 class ExtensionDevToolsInfoBarDelegate : public ConfirmInfoBarDelegate { 149 public: 150 // Creates an extension dev tools infobar delegate and adds it to the 151 // InfoBarService associated with |rvh|. Returns the delegate if it was 152 // successfully added. 153 static ExtensionDevToolsInfoBarDelegate* Create( 154 RenderViewHost* rvh, 155 const std::string& client_name); 156 157 void set_client_host(ExtensionDevToolsClientHost* client_host) { 158 client_host_ = client_host; 159 } 160 161 private: 162 ExtensionDevToolsInfoBarDelegate(InfoBarService* infobar_service, 163 const std::string& client_name); 164 virtual ~ExtensionDevToolsInfoBarDelegate(); 165 166 // ConfirmInfoBarDelegate: 167 virtual void InfoBarDismissed() OVERRIDE; 168 virtual Type GetInfoBarType() const OVERRIDE; 169 virtual bool ShouldExpireInternal( 170 const content::LoadCommittedDetails& details) const OVERRIDE; 171 virtual string16 GetMessageText() const OVERRIDE; 172 virtual int GetButtons() const OVERRIDE; 173 virtual bool Cancel() OVERRIDE; 174 175 std::string client_name_; 176 ExtensionDevToolsClientHost* client_host_; 177 178 DISALLOW_COPY_AND_ASSIGN(ExtensionDevToolsInfoBarDelegate); 179 }; 180 181 // static 182 ExtensionDevToolsInfoBarDelegate* ExtensionDevToolsInfoBarDelegate::Create( 183 RenderViewHost* rvh, 184 const std::string& client_name) { 185 if (!rvh) 186 return NULL; 187 188 WebContents* web_contents = WebContents::FromRenderViewHost(rvh); 189 if (!web_contents) 190 return NULL; 191 192 InfoBarService* infobar_service = 193 InfoBarService::FromWebContents(web_contents); 194 if (!infobar_service) 195 return NULL; 196 197 return static_cast<ExtensionDevToolsInfoBarDelegate*>( 198 infobar_service->AddInfoBar(scoped_ptr<InfoBarDelegate>( 199 new ExtensionDevToolsInfoBarDelegate(infobar_service, client_name)))); 200 } 201 202 ExtensionDevToolsInfoBarDelegate::ExtensionDevToolsInfoBarDelegate( 203 InfoBarService* infobar_service, 204 const std::string& client_name) 205 : ConfirmInfoBarDelegate(infobar_service), 206 client_name_(client_name), 207 client_host_(NULL) { 208 } 209 210 ExtensionDevToolsInfoBarDelegate::~ExtensionDevToolsInfoBarDelegate() { 211 } 212 213 void ExtensionDevToolsInfoBarDelegate::InfoBarDismissed() { 214 if (client_host_) 215 client_host_->MarkAsDismissed(); 216 } 217 218 InfoBarDelegate::Type ExtensionDevToolsInfoBarDelegate::GetInfoBarType() const { 219 return WARNING_TYPE; 220 } 221 222 bool ExtensionDevToolsInfoBarDelegate::ShouldExpireInternal( 223 const content::LoadCommittedDetails& details) const { 224 return false; 225 } 226 227 string16 ExtensionDevToolsInfoBarDelegate::GetMessageText() const { 228 return l10n_util::GetStringFUTF16(IDS_DEV_TOOLS_INFOBAR_LABEL, 229 UTF8ToUTF16(client_name_)); 230 } 231 232 int ExtensionDevToolsInfoBarDelegate::GetButtons() const { 233 return BUTTON_CANCEL; 234 } 235 236 bool ExtensionDevToolsInfoBarDelegate::Cancel() { 237 InfoBarDismissed(); 238 return true; 239 } 240 241 242 // AttachedClientHosts -------------------------------------------------------- 243 244 class AttachedClientHosts { 245 public: 246 AttachedClientHosts(); 247 ~AttachedClientHosts(); 248 249 // Returns the singleton instance of this class. 250 static AttachedClientHosts* GetInstance(); 251 252 void Add(ExtensionDevToolsClientHost* client_host); 253 void Remove(ExtensionDevToolsClientHost* client_host); 254 ExtensionDevToolsClientHost* Lookup(DevToolsAgentHost* agent_host, 255 const std::string& extension_id); 256 257 private: 258 typedef std::set<ExtensionDevToolsClientHost*> ClientHosts; 259 ClientHosts client_hosts_; 260 261 DISALLOW_COPY_AND_ASSIGN(AttachedClientHosts); 262 }; 263 264 AttachedClientHosts::AttachedClientHosts() { 265 } 266 267 AttachedClientHosts::~AttachedClientHosts() { 268 } 269 270 // static 271 AttachedClientHosts* AttachedClientHosts::GetInstance() { 272 return Singleton<AttachedClientHosts>::get(); 273 } 274 275 void AttachedClientHosts::Add(ExtensionDevToolsClientHost* client_host) { 276 client_hosts_.insert(client_host); 277 } 278 279 void AttachedClientHosts::Remove(ExtensionDevToolsClientHost* client_host) { 280 client_hosts_.erase(client_host); 281 } 282 283 ExtensionDevToolsClientHost* AttachedClientHosts::Lookup( 284 DevToolsAgentHost* agent_host, 285 const std::string& extension_id) { 286 DevToolsManager* manager = DevToolsManager::GetInstance(); 287 for (ClientHosts::iterator it = client_hosts_.begin(); 288 it != client_hosts_.end(); ++it) { 289 ExtensionDevToolsClientHost* client_host = *it; 290 if (manager->GetDevToolsAgentHostFor(client_host) == agent_host && 291 client_host->extension_id() == extension_id) 292 return client_host; 293 } 294 return NULL; 295 } 296 297 } // namespace 298 299 300 // ExtensionDevToolsClientHost ------------------------------------------------ 301 302 ExtensionDevToolsClientHost::ExtensionDevToolsClientHost( 303 Profile* profile, 304 DevToolsAgentHost* agent_host, 305 const std::string& extension_id, 306 const std::string& extension_name, 307 const Debuggee& debuggee, 308 ExtensionDevToolsInfoBarDelegate* infobar) 309 : profile_(profile), 310 agent_host_(agent_host), 311 extension_id_(extension_id), 312 last_request_id_(0), 313 infobar_(infobar), 314 detach_reason_(OnDetach::REASON_TARGET_CLOSED) { 315 CopyDebuggee(&debuggee_, debuggee); 316 317 AttachedClientHosts::GetInstance()->Add(this); 318 319 // Detach from debugger when extension unloads. 320 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, 321 content::Source<Profile>(profile_)); 322 323 // RVH-based agents disconnect from their clients when the app is terminating 324 // but shared worker-based agents do not. 325 // Disconnect explicitly to make sure that |this| observer is not leaked. 326 registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING, 327 content::NotificationService::AllSources()); 328 329 // Attach to debugger and tell it we are ready. 330 DevToolsManager::GetInstance()->RegisterDevToolsClientHostFor( 331 agent_host_.get(), this); 332 333 if (infobar_) { 334 infobar_->set_client_host(this); 335 registrar_.Add( 336 this, chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED, 337 content::Source<InfoBarService>(InfoBarService::FromWebContents( 338 WebContents::FromRenderViewHost( 339 agent_host_->GetRenderViewHost())))); 340 } 341 } 342 343 ExtensionDevToolsClientHost::~ExtensionDevToolsClientHost() { 344 // Ensure calling RemoveInfoBar() below won't result in Observe() trying to 345 // Close() us. 346 registrar_.RemoveAll(); 347 348 if (infobar_) { 349 infobar_->set_client_host(NULL); 350 InfoBarService::FromWebContents(WebContents::FromRenderViewHost( 351 agent_host_->GetRenderViewHost()))->RemoveInfoBar(infobar_); 352 } 353 AttachedClientHosts::GetInstance()->Remove(this); 354 } 355 356 // DevToolsClientHost interface 357 void ExtensionDevToolsClientHost::InspectedContentsClosing() { 358 SendDetachedEvent(); 359 delete this; 360 } 361 362 void ExtensionDevToolsClientHost::ReplacedWithAnotherClient() { 363 detach_reason_ = OnDetach::REASON_REPLACED_WITH_DEVTOOLS; 364 } 365 366 void ExtensionDevToolsClientHost::Close() { 367 DevToolsManager::GetInstance()->ClientHostClosing(this); 368 delete this; 369 } 370 371 void ExtensionDevToolsClientHost::SendMessageToBackend( 372 DebuggerSendCommandFunction* function, 373 const std::string& method, 374 SendCommand::Params::CommandParams* command_params) { 375 base::DictionaryValue protocol_request; 376 int request_id = ++last_request_id_; 377 pending_requests_[request_id] = function; 378 protocol_request.SetInteger("id", request_id); 379 protocol_request.SetString("method", method); 380 if (command_params) { 381 protocol_request.Set("params", 382 command_params->additional_properties.DeepCopy()); 383 } 384 385 std::string json_args; 386 base::JSONWriter::Write(&protocol_request, &json_args); 387 DevToolsManager::GetInstance()->DispatchOnInspectorBackend(this, json_args); 388 } 389 390 void ExtensionDevToolsClientHost::MarkAsDismissed() { 391 detach_reason_ = OnDetach::REASON_CANCELED_BY_USER; 392 } 393 394 void ExtensionDevToolsClientHost::SendDetachedEvent() { 395 if (!extensions::ExtensionSystem::Get(profile_)->event_router()) 396 return; 397 398 scoped_ptr<base::ListValue> args(OnDetach::Create(debuggee_, 399 detach_reason_)); 400 scoped_ptr<extensions::Event> event(new extensions::Event( 401 keys::kOnDetach, args.Pass())); 402 event->restrict_to_profile = profile_; 403 extensions::ExtensionSystem::Get(profile_)->event_router()-> 404 DispatchEventToExtension(extension_id_, event.Pass()); 405 } 406 407 void ExtensionDevToolsClientHost::Observe( 408 int type, 409 const content::NotificationSource& source, 410 const content::NotificationDetails& details) { 411 if (type == chrome::NOTIFICATION_EXTENSION_UNLOADED) { 412 if (content::Details<extensions::UnloadedExtensionInfo>(details)-> 413 extension->id() == extension_id_) 414 Close(); 415 } else if (type == chrome::NOTIFICATION_APP_TERMINATING) { 416 Close(); 417 } else { 418 DCHECK_EQ(chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED, type); 419 if (content::Details<InfoBarRemovedDetails>(details)->first == infobar_) { 420 infobar_ = NULL; 421 SendDetachedEvent(); 422 Close(); 423 } 424 } 425 } 426 427 void ExtensionDevToolsClientHost::DispatchOnInspectorFrontend( 428 const std::string& message) { 429 if (!extensions::ExtensionSystem::Get(profile_)->event_router()) 430 return; 431 432 scoped_ptr<Value> result(base::JSONReader::Read(message)); 433 if (!result->IsType(Value::TYPE_DICTIONARY)) 434 return; 435 base::DictionaryValue* dictionary = 436 static_cast<base::DictionaryValue*>(result.get()); 437 438 int id; 439 if (!dictionary->GetInteger("id", &id)) { 440 std::string method_name; 441 if (!dictionary->GetString("method", &method_name)) 442 return; 443 444 OnEvent::Params params; 445 base::DictionaryValue* params_value; 446 if (dictionary->GetDictionary("params", ¶ms_value)) 447 params.additional_properties.Swap(params_value); 448 449 scoped_ptr<ListValue> args(OnEvent::Create(debuggee_, method_name, params)); 450 scoped_ptr<extensions::Event> event(new extensions::Event( 451 keys::kOnEvent, args.Pass())); 452 event->restrict_to_profile = profile_; 453 extensions::ExtensionSystem::Get(profile_)->event_router()-> 454 DispatchEventToExtension(extension_id_, event.Pass()); 455 } else { 456 DebuggerSendCommandFunction* function = pending_requests_[id].get(); 457 if (!function) 458 return; 459 460 function->SendResponseBody(dictionary); 461 pending_requests_.erase(id); 462 } 463 } 464 465 466 // DebuggerFunction ----------------------------------------------------------- 467 468 DebuggerFunction::DebuggerFunction() 469 : client_host_(NULL) { 470 } 471 472 DebuggerFunction::~DebuggerFunction() { 473 } 474 475 void DebuggerFunction::FormatErrorMessage(const std::string& format) { 476 if (debuggee_.tab_id) 477 error_ = ErrorUtils::FormatErrorMessage( 478 format, keys::kTabTargetType, base::IntToString(*debuggee_.tab_id)); 479 else if (debuggee_.extension_id) 480 error_ = ErrorUtils::FormatErrorMessage( 481 format, keys::kBackgroundPageTargetType, *debuggee_.extension_id); 482 else 483 error_ = ErrorUtils::FormatErrorMessage( 484 format, keys::kOpaqueTargetType, *debuggee_.target_id); 485 } 486 487 bool DebuggerFunction::InitAgentHost() { 488 if (debuggee_.tab_id) { 489 WebContents* web_contents = NULL; 490 bool result = ExtensionTabUtil::GetTabById( 491 *debuggee_.tab_id, profile(), include_incognito(), NULL, NULL, 492 &web_contents, NULL); 493 if (result && web_contents) { 494 if (content::HasWebUIScheme(web_contents->GetURL())) { 495 error_ = ErrorUtils::FormatErrorMessage( 496 keys::kAttachToWebUIError, 497 web_contents->GetURL().scheme()); 498 return false; 499 } 500 agent_host_ = DevToolsAgentHost::GetOrCreateFor( 501 web_contents->GetRenderViewHost()); 502 } 503 } else if (debuggee_.extension_id) { 504 extensions::ExtensionHost* extension_host = 505 extensions::ExtensionSystem::Get(profile())->process_manager()-> 506 GetBackgroundHostForExtension(*debuggee_.extension_id); 507 if (extension_host) { 508 agent_host_ = DevToolsAgentHost::GetOrCreateFor( 509 extension_host->render_view_host()); 510 } 511 } else if (debuggee_.target_id) { 512 agent_host_ = DevToolsAgentHost::GetForId(*debuggee_.target_id); 513 } else { 514 error_ = keys::kInvalidTargetError; 515 return false; 516 } 517 518 if (!agent_host_.get()) { 519 FormatErrorMessage(keys::kNoTargetError); 520 return false; 521 } 522 return true; 523 } 524 525 bool DebuggerFunction::InitClientHost() { 526 if (!InitAgentHost()) 527 return false; 528 529 client_host_ = AttachedClientHosts::GetInstance()->Lookup( 530 agent_host_.get(), GetExtension()->id()); 531 532 if (!client_host_) { 533 FormatErrorMessage(keys::kNotAttachedError); 534 return false; 535 } 536 return true; 537 } 538 539 540 // DebuggerAttachFunction ----------------------------------------------------- 541 542 DebuggerAttachFunction::DebuggerAttachFunction() { 543 } 544 545 DebuggerAttachFunction::~DebuggerAttachFunction() { 546 } 547 548 bool DebuggerAttachFunction::RunImpl() { 549 scoped_ptr<Attach::Params> params(Attach::Params::Create(*args_)); 550 EXTENSION_FUNCTION_VALIDATE(params.get()); 551 552 CopyDebuggee(&debuggee_, params->target); 553 if (!InitAgentHost()) 554 return false; 555 556 if (!DevToolsHttpHandler::IsSupportedProtocolVersion( 557 params->required_version)) { 558 error_ = ErrorUtils::FormatErrorMessage( 559 keys::kProtocolVersionNotSupportedError, 560 params->required_version); 561 return false; 562 } 563 564 if (agent_host_->IsAttached()) { 565 FormatErrorMessage(keys::kAlreadyAttachedError); 566 return false; 567 } 568 569 ExtensionDevToolsInfoBarDelegate* infobar = NULL; 570 if (!CommandLine::ForCurrentProcess()-> 571 HasSwitch(switches::kSilentDebuggerExtensionAPI)) { 572 // Do not attach to the target if for any reason the infobar cannot be shown 573 // for this WebContents instance. 574 infobar = ExtensionDevToolsInfoBarDelegate::Create( 575 agent_host_->GetRenderViewHost(), GetExtension()->name()); 576 if (!infobar) { 577 error_ = ErrorUtils::FormatErrorMessage( 578 keys::kSilentDebuggingRequired, 579 switches::kSilentDebuggerExtensionAPI); 580 return false; 581 } 582 } 583 584 new ExtensionDevToolsClientHost(profile(), agent_host_.get(), 585 GetExtension()->id(), GetExtension()->name(), 586 debuggee_, infobar); 587 SendResponse(true); 588 return true; 589 } 590 591 592 // DebuggerDetachFunction ----------------------------------------------------- 593 594 DebuggerDetachFunction::DebuggerDetachFunction() { 595 } 596 597 DebuggerDetachFunction::~DebuggerDetachFunction() { 598 } 599 600 bool DebuggerDetachFunction::RunImpl() { 601 scoped_ptr<Detach::Params> params(Detach::Params::Create(*args_)); 602 EXTENSION_FUNCTION_VALIDATE(params.get()); 603 604 CopyDebuggee(&debuggee_, params->target); 605 if (!InitClientHost()) 606 return false; 607 608 client_host_->Close(); 609 SendResponse(true); 610 return true; 611 } 612 613 614 // DebuggerSendCommandFunction ------------------------------------------------ 615 616 DebuggerSendCommandFunction::DebuggerSendCommandFunction() { 617 } 618 619 DebuggerSendCommandFunction::~DebuggerSendCommandFunction() { 620 } 621 622 bool DebuggerSendCommandFunction::RunImpl() { 623 scoped_ptr<SendCommand::Params> params(SendCommand::Params::Create(*args_)); 624 EXTENSION_FUNCTION_VALIDATE(params.get()); 625 626 CopyDebuggee(&debuggee_, params->target); 627 if (!InitClientHost()) 628 return false; 629 630 client_host_->SendMessageToBackend(this, params->method, 631 params->command_params.get()); 632 return true; 633 } 634 635 void DebuggerSendCommandFunction::SendResponseBody( 636 base::DictionaryValue* response) { 637 Value* error_body; 638 if (response->Get("error", &error_body)) { 639 base::JSONWriter::Write(error_body, &error_); 640 SendResponse(false); 641 return; 642 } 643 644 base::DictionaryValue* result_body; 645 SendCommand::Results::Result result; 646 if (response->GetDictionary("result", &result_body)) 647 result.additional_properties.Swap(result_body); 648 649 results_ = SendCommand::Results::Create(result); 650 SendResponse(true); 651 } 652 653 654 // DebuggerGetTargetsFunction ------------------------------------------------- 655 656 namespace { 657 658 const char kTargetIdField[] = "id"; 659 const char kTargetTypeField[] = "type"; 660 const char kTargetTitleField[] = "title"; 661 const char kTargetAttachedField[] = "attached"; 662 const char kTargetUrlField[] = "url"; 663 const char kTargetFaviconUrlField[] = "faviconUrl"; 664 const char kTargetTypePage[] = "page"; 665 const char kTargetTypeBackgroundPage[] = "background_page"; 666 const char kTargetTypeWorker[] = "worker"; 667 const char kTargetTypeOther[] = "other"; 668 const char kTargetTabIdField[] = "tabId"; 669 const char kTargetExtensionIdField[] = "extensionId"; 670 671 extensions::ExtensionHost* 672 GetExtensionBackgroundHost(WebContents* web_contents) { 673 Profile* profile = 674 Profile::FromBrowserContext(web_contents->GetBrowserContext()); 675 if (!profile) 676 return NULL; 677 678 extensions::ExtensionHost* extension_host = 679 extensions::ExtensionSystem::Get(profile)->process_manager()-> 680 GetBackgroundHostForExtension(web_contents->GetURL().host()); 681 682 return (extension_host && extension_host->host_contents() == web_contents) ? 683 extension_host : NULL; 684 } 685 686 base::Value* SerializePageInfo(RenderViewHost* rvh) { 687 WebContents* web_contents = WebContents::FromRenderViewHost(rvh); 688 if (!web_contents) 689 return NULL; 690 691 DevToolsAgentHost* agent_host = DevToolsAgentHost::GetOrCreateFor(rvh); 692 693 base::DictionaryValue* dictionary = new base::DictionaryValue(); 694 695 dictionary->SetString(kTargetIdField, agent_host->GetId()); 696 dictionary->SetBoolean(kTargetAttachedField, agent_host->IsAttached()); 697 dictionary->SetString(kTargetUrlField, web_contents->GetURL().spec()); 698 699 extensions::ExtensionHost* extension_host = 700 GetExtensionBackgroundHost(web_contents); 701 if (extension_host) { 702 // This RenderViewHost belongs to a background page. 703 dictionary->SetString(kTargetTypeField, kTargetTypeBackgroundPage); 704 dictionary->SetString(kTargetExtensionIdField, 705 extension_host->extension()->id()); 706 dictionary->SetString(kTargetTitleField, 707 extension_host->extension()->name()); 708 } else { 709 int tab_id = ExtensionTabUtil::GetTabId(web_contents); 710 if (tab_id != -1) { 711 // This RenderViewHost belongs to a regular page. 712 dictionary->SetString(kTargetTypeField, kTargetTypePage); 713 dictionary->SetInteger(kTargetTabIdField, tab_id); 714 } else { 715 dictionary->SetString(kTargetTypeField, kTargetTypeOther); 716 } 717 dictionary->SetString(kTargetTitleField, web_contents->GetTitle()); 718 719 content::NavigationController& controller = web_contents->GetController(); 720 content::NavigationEntry* entry = controller.GetActiveEntry(); 721 if (entry != NULL && entry->GetURL().is_valid()) { 722 dictionary->SetString(kTargetFaviconUrlField, 723 entry->GetFavicon().url.spec()); 724 } 725 } 726 727 return dictionary; 728 } 729 730 base::Value* SerializeWorkerInfo(const WorkerService::WorkerInfo& worker) { 731 base::DictionaryValue* dictionary = new base::DictionaryValue; 732 733 scoped_refptr<DevToolsAgentHost> agent(DevToolsAgentHost::GetForWorker( 734 worker.process_id, worker.route_id)); 735 dictionary->SetString(kTargetIdField, agent->GetId()); 736 dictionary->SetString(kTargetTypeField, kTargetTypeWorker); 737 dictionary->SetString(kTargetTitleField, worker.name); 738 dictionary->SetString(kTargetUrlField, worker.url.spec()); 739 dictionary->SetBoolean(kTargetAttachedField, agent->IsAttached()); 740 741 return dictionary; 742 } 743 744 } // namespace 745 746 DebuggerGetTargetsFunction::DebuggerGetTargetsFunction() { 747 } 748 749 DebuggerGetTargetsFunction::~DebuggerGetTargetsFunction() { 750 } 751 752 bool DebuggerGetTargetsFunction::RunImpl() { 753 base::ListValue* results_list = new base::ListValue(); 754 755 std::vector<RenderViewHost*> rvh_list = 756 DevToolsAgentHost::GetValidRenderViewHosts(); 757 for (std::vector<RenderViewHost*>::iterator it = rvh_list.begin(); 758 it != rvh_list.end(); ++it) { 759 base::Value* value = SerializePageInfo(*it); 760 if (value) 761 results_list->Append(value); 762 } 763 764 content::BrowserThread::PostTaskAndReply( 765 content::BrowserThread::IO, 766 FROM_HERE, 767 base::Bind(&DebuggerGetTargetsFunction::CollectWorkerInfo, this, 768 results_list), 769 base::Bind(&DebuggerGetTargetsFunction::SendTargetList, this, 770 results_list)); 771 return true; 772 } 773 774 void DebuggerGetTargetsFunction::CollectWorkerInfo(base::ListValue* list) { 775 std::vector<WorkerService::WorkerInfo> worker_info = 776 WorkerService::GetInstance()->GetWorkers(); 777 for (size_t i = 0; i < worker_info.size(); ++i) 778 list->Append(SerializeWorkerInfo(worker_info[i])); 779 } 780 781 void DebuggerGetTargetsFunction::SendTargetList(base::ListValue* list) { 782 SetResult(list); 783 SendResponse(true); 784 } 785