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