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