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