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/stl_util.h" 18 #include "base/strings/string_number_conversions.h" 19 #include "base/strings/utf_string_conversions.h" 20 #include "base/values.h" 21 #include "chrome/browser/chrome_notification_types.h" 22 #include "chrome/browser/devtools/devtools_target_impl.h" 23 #include "chrome/browser/extensions/api/debugger/debugger_api_constants.h" 24 #include "chrome/browser/extensions/extension_host.h" 25 #include "chrome/browser/extensions/extension_service.h" 26 #include "chrome/browser/extensions/extension_system.h" 27 #include "chrome/browser/extensions/extension_tab_util.h" 28 #include "chrome/browser/infobars/confirm_infobar_delegate.h" 29 #include "chrome/browser/infobars/infobar.h" 30 #include "chrome/browser/infobars/infobar_service.h" 31 #include "chrome/browser/profiles/profile.h" 32 #include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h" 33 #include "chrome/common/chrome_switches.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/notification_service.h" 39 #include "content/public/browser/notification_source.h" 40 #include "content/public/browser/render_process_host.h" 41 #include "content/public/browser/render_view_host.h" 42 #include "content/public/browser/render_widget_host.h" 43 #include "content/public/browser/web_contents.h" 44 #include "content/public/common/content_client.h" 45 #include "content/public/common/url_utils.h" 46 #include "extensions/browser/event_router.h" 47 #include "extensions/common/error_utils.h" 48 #include "extensions/common/extension.h" 49 #include "grit/generated_resources.h" 50 #include "ui/base/l10n/l10n_util.h" 51 52 using content::DevToolsAgentHost; 53 using content::DevToolsClientHost; 54 using content::DevToolsHttpHandler; 55 using content::DevToolsManager; 56 using content::RenderProcessHost; 57 using content::RenderViewHost; 58 using content::RenderWidgetHost; 59 using content::WebContents; 60 using extensions::ErrorUtils; 61 62 namespace keys = debugger_api_constants; 63 namespace Attach = extensions::api::debugger::Attach; 64 namespace Detach = extensions::api::debugger::Detach; 65 namespace OnDetach = extensions::api::debugger::OnDetach; 66 namespace OnEvent = extensions::api::debugger::OnEvent; 67 namespace SendCommand = extensions::api::debugger::SendCommand; 68 69 70 // ExtensionDevToolsClientHost ------------------------------------------------ 71 72 class ExtensionDevToolsClientHost : public DevToolsClientHost, 73 public content::NotificationObserver { 74 public: 75 ExtensionDevToolsClientHost( 76 Profile* profile, 77 DevToolsAgentHost* agent_host, 78 const std::string& extension_id, 79 const std::string& extension_name, 80 const Debuggee& debuggee, 81 InfoBar* infobar); 82 83 virtual ~ExtensionDevToolsClientHost(); 84 85 const std::string& extension_id() { return extension_id_; } 86 void Close(); 87 void SendMessageToBackend(DebuggerSendCommandFunction* function, 88 const std::string& method, 89 SendCommand::Params::CommandParams* command_params); 90 91 // Marks connection as to-be-terminated by the user. 92 void MarkAsDismissed(); 93 94 // DevToolsClientHost interface 95 virtual void InspectedContentsClosing() OVERRIDE; 96 virtual void DispatchOnInspectorFrontend(const std::string& message) OVERRIDE; 97 virtual void ReplacedWithAnotherClient() OVERRIDE; 98 99 private: 100 void SendDetachedEvent(); 101 102 // content::NotificationObserver implementation. 103 virtual void Observe(int type, 104 const content::NotificationSource& source, 105 const content::NotificationDetails& details) OVERRIDE; 106 107 Profile* profile_; 108 scoped_refptr<DevToolsAgentHost> agent_host_; 109 std::string extension_id_; 110 Debuggee debuggee_; 111 content::NotificationRegistrar registrar_; 112 int last_request_id_; 113 typedef std::map<int, scoped_refptr<DebuggerSendCommandFunction> > 114 PendingRequests; 115 PendingRequests pending_requests_; 116 InfoBar* infobar_; 117 OnDetach::Reason detach_reason_; 118 119 DISALLOW_COPY_AND_ASSIGN(ExtensionDevToolsClientHost); 120 }; 121 122 // The member function declarations come after the other class declarations, so 123 // they can call members on them. 124 125 126 namespace { 127 128 // Helpers -------------------------------------------------------------------- 129 130 void CopyDebuggee(Debuggee* dst, const Debuggee& src) { 131 if (src.tab_id) 132 dst->tab_id.reset(new int(*src.tab_id)); 133 if (src.extension_id) 134 dst->extension_id.reset(new std::string(*src.extension_id)); 135 if (src.target_id) 136 dst->target_id.reset(new std::string(*src.target_id)); 137 } 138 139 140 // ExtensionDevToolsInfoBarDelegate ------------------------------------------- 141 142 class ExtensionDevToolsInfoBarDelegate : public ConfirmInfoBarDelegate { 143 public: 144 // Creates an extension dev tools infobar and delegate and adds the infobar to 145 // the InfoBarService associated with |rvh|. Returns the infobar if it was 146 // successfully added. 147 static InfoBar* Create(RenderViewHost* rvh, const std::string& client_name); 148 149 void set_client_host(ExtensionDevToolsClientHost* client_host) { 150 client_host_ = client_host; 151 } 152 153 private: 154 explicit ExtensionDevToolsInfoBarDelegate(const std::string& client_name); 155 virtual ~ExtensionDevToolsInfoBarDelegate(); 156 157 // ConfirmInfoBarDelegate: 158 virtual void InfoBarDismissed() OVERRIDE; 159 virtual Type GetInfoBarType() const OVERRIDE; 160 virtual bool ShouldExpireInternal( 161 const content::LoadCommittedDetails& details) const OVERRIDE; 162 virtual base::string16 GetMessageText() const OVERRIDE; 163 virtual int GetButtons() const OVERRIDE; 164 virtual bool Cancel() OVERRIDE; 165 166 std::string client_name_; 167 ExtensionDevToolsClientHost* client_host_; 168 169 DISALLOW_COPY_AND_ASSIGN(ExtensionDevToolsInfoBarDelegate); 170 }; 171 172 // static 173 InfoBar* ExtensionDevToolsInfoBarDelegate::Create( 174 RenderViewHost* rvh, 175 const std::string& client_name) { 176 if (!rvh) 177 return NULL; 178 179 WebContents* web_contents = WebContents::FromRenderViewHost(rvh); 180 if (!web_contents) 181 return NULL; 182 183 InfoBarService* infobar_service = 184 InfoBarService::FromWebContents(web_contents); 185 if (!infobar_service) 186 return NULL; 187 188 return infobar_service->AddInfoBar(ConfirmInfoBarDelegate::CreateInfoBar( 189 scoped_ptr<ConfirmInfoBarDelegate>( 190 new ExtensionDevToolsInfoBarDelegate(client_name)))); 191 } 192 193 ExtensionDevToolsInfoBarDelegate::ExtensionDevToolsInfoBarDelegate( 194 const std::string& client_name) 195 : ConfirmInfoBarDelegate(), 196 client_name_(client_name), 197 client_host_(NULL) { 198 } 199 200 ExtensionDevToolsInfoBarDelegate::~ExtensionDevToolsInfoBarDelegate() { 201 } 202 203 void ExtensionDevToolsInfoBarDelegate::InfoBarDismissed() { 204 if (client_host_) 205 client_host_->MarkAsDismissed(); 206 } 207 208 InfoBarDelegate::Type ExtensionDevToolsInfoBarDelegate::GetInfoBarType() const { 209 return WARNING_TYPE; 210 } 211 212 bool ExtensionDevToolsInfoBarDelegate::ShouldExpireInternal( 213 const content::LoadCommittedDetails& details) const { 214 return false; 215 } 216 217 base::string16 ExtensionDevToolsInfoBarDelegate::GetMessageText() const { 218 return l10n_util::GetStringFUTF16(IDS_DEV_TOOLS_INFOBAR_LABEL, 219 UTF8ToUTF16(client_name_)); 220 } 221 222 int ExtensionDevToolsInfoBarDelegate::GetButtons() const { 223 return BUTTON_CANCEL; 224 } 225 226 bool ExtensionDevToolsInfoBarDelegate::Cancel() { 227 InfoBarDismissed(); 228 return true; 229 } 230 231 232 // AttachedClientHosts -------------------------------------------------------- 233 234 class AttachedClientHosts { 235 public: 236 AttachedClientHosts(); 237 ~AttachedClientHosts(); 238 239 // Returns the singleton instance of this class. 240 static AttachedClientHosts* GetInstance(); 241 242 void Add(ExtensionDevToolsClientHost* client_host); 243 void Remove(ExtensionDevToolsClientHost* client_host); 244 ExtensionDevToolsClientHost* Lookup(DevToolsAgentHost* agent_host, 245 const std::string& extension_id); 246 247 private: 248 typedef std::set<ExtensionDevToolsClientHost*> ClientHosts; 249 ClientHosts client_hosts_; 250 251 DISALLOW_COPY_AND_ASSIGN(AttachedClientHosts); 252 }; 253 254 AttachedClientHosts::AttachedClientHosts() { 255 } 256 257 AttachedClientHosts::~AttachedClientHosts() { 258 } 259 260 // static 261 AttachedClientHosts* AttachedClientHosts::GetInstance() { 262 return Singleton<AttachedClientHosts>::get(); 263 } 264 265 void AttachedClientHosts::Add(ExtensionDevToolsClientHost* client_host) { 266 client_hosts_.insert(client_host); 267 } 268 269 void AttachedClientHosts::Remove(ExtensionDevToolsClientHost* client_host) { 270 client_hosts_.erase(client_host); 271 } 272 273 ExtensionDevToolsClientHost* AttachedClientHosts::Lookup( 274 DevToolsAgentHost* agent_host, 275 const std::string& extension_id) { 276 DevToolsManager* manager = DevToolsManager::GetInstance(); 277 for (ClientHosts::iterator it = client_hosts_.begin(); 278 it != client_hosts_.end(); ++it) { 279 ExtensionDevToolsClientHost* client_host = *it; 280 if (manager->GetDevToolsAgentHostFor(client_host) == agent_host && 281 client_host->extension_id() == extension_id) 282 return client_host; 283 } 284 return NULL; 285 } 286 287 } // namespace 288 289 290 // ExtensionDevToolsClientHost ------------------------------------------------ 291 292 ExtensionDevToolsClientHost::ExtensionDevToolsClientHost( 293 Profile* profile, 294 DevToolsAgentHost* agent_host, 295 const std::string& extension_id, 296 const std::string& extension_name, 297 const Debuggee& debuggee, 298 InfoBar* infobar) 299 : profile_(profile), 300 agent_host_(agent_host), 301 extension_id_(extension_id), 302 last_request_id_(0), 303 infobar_(infobar), 304 detach_reason_(OnDetach::REASON_TARGET_CLOSED) { 305 CopyDebuggee(&debuggee_, debuggee); 306 307 AttachedClientHosts::GetInstance()->Add(this); 308 309 // Detach from debugger when extension unloads. 310 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, 311 content::Source<Profile>(profile_)); 312 313 // RVH-based agents disconnect from their clients when the app is terminating 314 // but shared worker-based agents do not. 315 // Disconnect explicitly to make sure that |this| observer is not leaked. 316 registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING, 317 content::NotificationService::AllSources()); 318 319 // Attach to debugger and tell it we are ready. 320 DevToolsManager::GetInstance()->RegisterDevToolsClientHostFor( 321 agent_host_.get(), this); 322 323 if (infobar_) { 324 static_cast<ExtensionDevToolsInfoBarDelegate*>( 325 infobar_->delegate())->set_client_host(this); 326 registrar_.Add( 327 this, chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED, 328 content::Source<InfoBarService>(InfoBarService::FromWebContents( 329 WebContents::FromRenderViewHost( 330 agent_host_->GetRenderViewHost())))); 331 } 332 } 333 334 ExtensionDevToolsClientHost::~ExtensionDevToolsClientHost() { 335 // Ensure calling RemoveInfoBar() below won't result in Observe() trying to 336 // Close() us. 337 registrar_.RemoveAll(); 338 339 if (infobar_) { 340 static_cast<ExtensionDevToolsInfoBarDelegate*>( 341 infobar_->delegate())->set_client_host(NULL); 342 InfoBarService::FromWebContents(WebContents::FromRenderViewHost( 343 agent_host_->GetRenderViewHost()))->RemoveInfoBar(infobar_); 344 } 345 AttachedClientHosts::GetInstance()->Remove(this); 346 } 347 348 // DevToolsClientHost interface 349 void ExtensionDevToolsClientHost::InspectedContentsClosing() { 350 SendDetachedEvent(); 351 delete this; 352 } 353 354 void ExtensionDevToolsClientHost::ReplacedWithAnotherClient() { 355 detach_reason_ = OnDetach::REASON_REPLACED_WITH_DEVTOOLS; 356 } 357 358 void ExtensionDevToolsClientHost::Close() { 359 DevToolsManager::GetInstance()->ClientHostClosing(this); 360 delete this; 361 } 362 363 void ExtensionDevToolsClientHost::SendMessageToBackend( 364 DebuggerSendCommandFunction* function, 365 const std::string& method, 366 SendCommand::Params::CommandParams* command_params) { 367 base::DictionaryValue protocol_request; 368 int request_id = ++last_request_id_; 369 pending_requests_[request_id] = function; 370 protocol_request.SetInteger("id", request_id); 371 protocol_request.SetString("method", method); 372 if (command_params) { 373 protocol_request.Set("params", 374 command_params->additional_properties.DeepCopy()); 375 } 376 377 std::string json_args; 378 base::JSONWriter::Write(&protocol_request, &json_args); 379 DevToolsManager::GetInstance()->DispatchOnInspectorBackend(this, json_args); 380 } 381 382 void ExtensionDevToolsClientHost::MarkAsDismissed() { 383 detach_reason_ = OnDetach::REASON_CANCELED_BY_USER; 384 } 385 386 void ExtensionDevToolsClientHost::SendDetachedEvent() { 387 if (!extensions::ExtensionSystem::Get(profile_)->event_router()) 388 return; 389 390 scoped_ptr<base::ListValue> args(OnDetach::Create(debuggee_, 391 detach_reason_)); 392 scoped_ptr<extensions::Event> event(new extensions::Event( 393 OnDetach::kEventName, args.Pass())); 394 event->restrict_to_browser_context = profile_; 395 extensions::ExtensionSystem::Get(profile_)->event_router()-> 396 DispatchEventToExtension(extension_id_, event.Pass()); 397 } 398 399 void ExtensionDevToolsClientHost::Observe( 400 int type, 401 const content::NotificationSource& source, 402 const content::NotificationDetails& details) { 403 if (type == chrome::NOTIFICATION_EXTENSION_UNLOADED) { 404 if (content::Details<extensions::UnloadedExtensionInfo>(details)-> 405 extension->id() == extension_id_) 406 Close(); 407 } else if (type == chrome::NOTIFICATION_APP_TERMINATING) { 408 Close(); 409 } else { 410 DCHECK_EQ(chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED, type); 411 if (content::Details<InfoBar::RemovedDetails>(details)->first == infobar_) { 412 infobar_ = NULL; 413 SendDetachedEvent(); 414 Close(); 415 } 416 } 417 } 418 419 void ExtensionDevToolsClientHost::DispatchOnInspectorFrontend( 420 const std::string& message) { 421 if (!extensions::ExtensionSystem::Get(profile_)->event_router()) 422 return; 423 424 scoped_ptr<Value> result(base::JSONReader::Read(message)); 425 if (!result->IsType(Value::TYPE_DICTIONARY)) 426 return; 427 base::DictionaryValue* dictionary = 428 static_cast<base::DictionaryValue*>(result.get()); 429 430 int id; 431 if (!dictionary->GetInteger("id", &id)) { 432 std::string method_name; 433 if (!dictionary->GetString("method", &method_name)) 434 return; 435 436 OnEvent::Params params; 437 base::DictionaryValue* params_value; 438 if (dictionary->GetDictionary("params", ¶ms_value)) 439 params.additional_properties.Swap(params_value); 440 441 scoped_ptr<ListValue> args(OnEvent::Create(debuggee_, method_name, params)); 442 scoped_ptr<extensions::Event> event(new extensions::Event( 443 OnEvent::kEventName, args.Pass())); 444 event->restrict_to_browser_context = profile_; 445 extensions::ExtensionSystem::Get(profile_)->event_router()-> 446 DispatchEventToExtension(extension_id_, event.Pass()); 447 } else { 448 DebuggerSendCommandFunction* function = pending_requests_[id].get(); 449 if (!function) 450 return; 451 452 function->SendResponseBody(dictionary); 453 pending_requests_.erase(id); 454 } 455 } 456 457 458 // DebuggerFunction ----------------------------------------------------------- 459 460 DebuggerFunction::DebuggerFunction() 461 : client_host_(NULL) { 462 } 463 464 DebuggerFunction::~DebuggerFunction() { 465 } 466 467 void DebuggerFunction::FormatErrorMessage(const std::string& format) { 468 if (debuggee_.tab_id) 469 error_ = ErrorUtils::FormatErrorMessage( 470 format, keys::kTabTargetType, base::IntToString(*debuggee_.tab_id)); 471 else if (debuggee_.extension_id) 472 error_ = ErrorUtils::FormatErrorMessage( 473 format, keys::kBackgroundPageTargetType, *debuggee_.extension_id); 474 else 475 error_ = ErrorUtils::FormatErrorMessage( 476 format, keys::kOpaqueTargetType, *debuggee_.target_id); 477 } 478 479 bool DebuggerFunction::InitAgentHost() { 480 if (debuggee_.tab_id) { 481 WebContents* web_contents = NULL; 482 bool result = extensions::ExtensionTabUtil::GetTabById(*debuggee_.tab_id, 483 GetProfile(), 484 include_incognito(), 485 NULL, 486 NULL, 487 &web_contents, 488 NULL); 489 if (result && web_contents) { 490 if (content::HasWebUIScheme(web_contents->GetURL())) { 491 error_ = ErrorUtils::FormatErrorMessage( 492 keys::kAttachToWebUIError, 493 web_contents->GetURL().scheme()); 494 return false; 495 } 496 agent_host_ = DevToolsAgentHost::GetOrCreateFor( 497 web_contents->GetRenderViewHost()); 498 } 499 } else if (debuggee_.extension_id) { 500 extensions::ExtensionHost* extension_host = 501 extensions::ExtensionSystem::Get(GetProfile()) 502 ->process_manager() 503 ->GetBackgroundHostForExtension(*debuggee_.extension_id); 504 if (extension_host) { 505 agent_host_ = DevToolsAgentHost::GetOrCreateFor( 506 extension_host->render_view_host()); 507 } 508 } else if (debuggee_.target_id) { 509 agent_host_ = DevToolsAgentHost::GetForId(*debuggee_.target_id); 510 } else { 511 error_ = keys::kInvalidTargetError; 512 return false; 513 } 514 515 if (!agent_host_.get()) { 516 FormatErrorMessage(keys::kNoTargetError); 517 return false; 518 } 519 return true; 520 } 521 522 bool DebuggerFunction::InitClientHost() { 523 if (!InitAgentHost()) 524 return false; 525 526 client_host_ = AttachedClientHosts::GetInstance()->Lookup( 527 agent_host_.get(), GetExtension()->id()); 528 529 if (!client_host_) { 530 FormatErrorMessage(keys::kNotAttachedError); 531 return false; 532 } 533 return true; 534 } 535 536 537 // DebuggerAttachFunction ----------------------------------------------------- 538 539 DebuggerAttachFunction::DebuggerAttachFunction() { 540 } 541 542 DebuggerAttachFunction::~DebuggerAttachFunction() { 543 } 544 545 bool DebuggerAttachFunction::RunImpl() { 546 scoped_ptr<Attach::Params> params(Attach::Params::Create(*args_)); 547 EXTENSION_FUNCTION_VALIDATE(params.get()); 548 549 CopyDebuggee(&debuggee_, params->target); 550 if (!InitAgentHost()) 551 return false; 552 553 if (!DevToolsHttpHandler::IsSupportedProtocolVersion( 554 params->required_version)) { 555 error_ = ErrorUtils::FormatErrorMessage( 556 keys::kProtocolVersionNotSupportedError, 557 params->required_version); 558 return false; 559 } 560 561 if (agent_host_->IsAttached()) { 562 FormatErrorMessage(keys::kAlreadyAttachedError); 563 return false; 564 } 565 566 InfoBar* infobar = NULL; 567 if (!CommandLine::ForCurrentProcess()-> 568 HasSwitch(switches::kSilentDebuggerExtensionAPI)) { 569 // Do not attach to the target if for any reason the infobar cannot be shown 570 // for this WebContents instance. 571 infobar = ExtensionDevToolsInfoBarDelegate::Create( 572 agent_host_->GetRenderViewHost(), GetExtension()->name()); 573 if (!infobar) { 574 error_ = ErrorUtils::FormatErrorMessage( 575 keys::kSilentDebuggingRequired, 576 switches::kSilentDebuggerExtensionAPI); 577 return false; 578 } 579 } 580 581 new ExtensionDevToolsClientHost(GetProfile(), 582 agent_host_.get(), 583 GetExtension()->id(), 584 GetExtension()->name(), 585 debuggee_, 586 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 base::Value* SerializeTarget(const DevToolsTargetImpl& target) { 672 base::DictionaryValue* dictionary = new base::DictionaryValue(); 673 674 dictionary->SetString(kTargetIdField, target.GetId()); 675 dictionary->SetString(kTargetTitleField, target.GetTitle()); 676 dictionary->SetBoolean(kTargetAttachedField, target.IsAttached()); 677 dictionary->SetString(kTargetUrlField, target.GetUrl().spec()); 678 679 std::string type = target.GetType(); 680 if (type == kTargetTypePage) { 681 dictionary->SetInteger(kTargetTabIdField, target.GetTabId()); 682 } else if (type == kTargetTypeBackgroundPage) { 683 dictionary->SetString(kTargetExtensionIdField, target.GetExtensionId()); 684 } else if (type != kTargetTypeWorker) { 685 // DevToolsTargetImpl may support more types than the debugger API. 686 type = kTargetTypeOther; 687 } 688 dictionary->SetString(kTargetTypeField, type); 689 690 GURL favicon_url = target.GetFaviconUrl(); 691 if (favicon_url.is_valid()) 692 dictionary->SetString(kTargetFaviconUrlField, favicon_url.spec()); 693 694 return dictionary; 695 } 696 697 } // namespace 698 699 DebuggerGetTargetsFunction::DebuggerGetTargetsFunction() { 700 } 701 702 DebuggerGetTargetsFunction::~DebuggerGetTargetsFunction() { 703 } 704 705 bool DebuggerGetTargetsFunction::RunImpl() { 706 DevToolsTargetImpl::EnumerateAllTargets( 707 base::Bind(&DebuggerGetTargetsFunction::SendTargetList, this)); 708 return true; 709 } 710 711 void DebuggerGetTargetsFunction::SendTargetList( 712 const std::vector<DevToolsTargetImpl*>& target_list) { 713 scoped_ptr<base::ListValue> result(new base::ListValue()); 714 for (size_t i = 0; i < target_list.size(); ++i) 715 result->Append(SerializeTarget(*target_list[i])); 716 STLDeleteContainerPointers(target_list.begin(), target_list.end()); 717 SetResult(result.release()); 718 SendResponse(true); 719 } 720