| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // Implements the Chrome Extensions Debugger API. |
| |
| #include "chrome/browser/extensions/api/debugger/debugger_api.h" |
| |
| #include <map> |
| #include <set> |
| |
| #include "base/command_line.h" |
| #include "base/json/json_reader.h" |
| #include "base/json/json_writer.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "base/memory/singleton.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/values.h" |
| #include "chrome/browser/chrome_notification_types.h" |
| #include "chrome/browser/extensions/api/debugger/debugger_api_constants.h" |
| #include "chrome/browser/extensions/event_router.h" |
| #include "chrome/browser/extensions/extension_host.h" |
| #include "chrome/browser/extensions/extension_service.h" |
| #include "chrome/browser/extensions/extension_system.h" |
| #include "chrome/browser/extensions/extension_tab_util.h" |
| #include "chrome/browser/infobars/confirm_infobar_delegate.h" |
| #include "chrome/browser/infobars/infobar.h" |
| #include "chrome/browser/infobars/infobar_service.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/common/extensions/extension.h" |
| #include "content/public/browser/devtools_agent_host.h" |
| #include "content/public/browser/devtools_client_host.h" |
| #include "content/public/browser/devtools_http_handler.h" |
| #include "content/public/browser/devtools_manager.h" |
| #include "content/public/browser/favicon_status.h" |
| #include "content/public/browser/navigation_entry.h" |
| #include "content/public/browser/notification_service.h" |
| #include "content/public/browser/notification_source.h" |
| #include "content/public/browser/render_process_host.h" |
| #include "content/public/browser/render_view_host.h" |
| #include "content/public/browser/render_widget_host.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/browser/worker_service.h" |
| #include "content/public/common/content_client.h" |
| #include "content/public/common/url_utils.h" |
| #include "extensions/common/error_utils.h" |
| #include "grit/generated_resources.h" |
| #include "ui/base/l10n/l10n_util.h" |
| |
| using content::DevToolsAgentHost; |
| using content::DevToolsClientHost; |
| using content::DevToolsHttpHandler; |
| using content::DevToolsManager; |
| using content::RenderProcessHost; |
| using content::RenderViewHost; |
| using content::RenderWidgetHost; |
| using content::WebContents; |
| using content::WorkerService; |
| using extensions::ErrorUtils; |
| |
| namespace keys = debugger_api_constants; |
| namespace Attach = extensions::api::debugger::Attach; |
| namespace Detach = extensions::api::debugger::Detach; |
| namespace OnDetach = extensions::api::debugger::OnDetach; |
| namespace OnEvent = extensions::api::debugger::OnEvent; |
| namespace SendCommand = extensions::api::debugger::SendCommand; |
| |
| namespace { |
| class ExtensionDevToolsInfoBarDelegate; |
| } // namespace |
| |
| |
| // ExtensionDevToolsClientHost ------------------------------------------------ |
| |
| class ExtensionDevToolsClientHost : public DevToolsClientHost, |
| public content::NotificationObserver { |
| public: |
| ExtensionDevToolsClientHost( |
| Profile* profile, |
| DevToolsAgentHost* agent_host, |
| const std::string& extension_id, |
| const std::string& extension_name, |
| const Debuggee& debuggee, |
| ExtensionDevToolsInfoBarDelegate* infobar); |
| |
| virtual ~ExtensionDevToolsClientHost(); |
| |
| const std::string& extension_id() { return extension_id_; } |
| void Close(); |
| void SendMessageToBackend(DebuggerSendCommandFunction* function, |
| const std::string& method, |
| SendCommand::Params::CommandParams* command_params); |
| |
| // Marks connection as to-be-terminated by the user. |
| void MarkAsDismissed(); |
| |
| // DevToolsClientHost interface |
| virtual void InspectedContentsClosing() OVERRIDE; |
| virtual void DispatchOnInspectorFrontend(const std::string& message) OVERRIDE; |
| virtual void ReplacedWithAnotherClient() OVERRIDE; |
| |
| private: |
| void SendDetachedEvent(); |
| |
| // content::NotificationObserver implementation. |
| virtual void Observe(int type, |
| const content::NotificationSource& source, |
| const content::NotificationDetails& details) OVERRIDE; |
| |
| Profile* profile_; |
| scoped_refptr<DevToolsAgentHost> agent_host_; |
| std::string extension_id_; |
| Debuggee debuggee_; |
| content::NotificationRegistrar registrar_; |
| int last_request_id_; |
| typedef std::map<int, scoped_refptr<DebuggerSendCommandFunction> > |
| PendingRequests; |
| PendingRequests pending_requests_; |
| ExtensionDevToolsInfoBarDelegate* infobar_; |
| OnDetach::Reason detach_reason_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ExtensionDevToolsClientHost); |
| }; |
| |
| // The member function declarations come after the other class declarations, so |
| // they can call members on them. |
| |
| |
| namespace { |
| |
| // Helpers -------------------------------------------------------------------- |
| |
| void CopyDebuggee(Debuggee* dst, const Debuggee& src) { |
| if (src.tab_id) |
| dst->tab_id.reset(new int(*src.tab_id)); |
| if (src.extension_id) |
| dst->extension_id.reset(new std::string(*src.extension_id)); |
| if (src.target_id) |
| dst->target_id.reset(new std::string(*src.target_id)); |
| } |
| |
| |
| // ExtensionDevToolsInfoBarDelegate ------------------------------------------- |
| |
| class ExtensionDevToolsInfoBarDelegate : public ConfirmInfoBarDelegate { |
| public: |
| // Creates an extension dev tools infobar delegate and adds it to the |
| // InfoBarService associated with |rvh|. Returns the delegate if it was |
| // successfully added. |
| static ExtensionDevToolsInfoBarDelegate* Create( |
| RenderViewHost* rvh, |
| const std::string& client_name); |
| |
| void set_client_host(ExtensionDevToolsClientHost* client_host) { |
| client_host_ = client_host; |
| } |
| |
| private: |
| ExtensionDevToolsInfoBarDelegate(InfoBarService* infobar_service, |
| const std::string& client_name); |
| virtual ~ExtensionDevToolsInfoBarDelegate(); |
| |
| // ConfirmInfoBarDelegate: |
| virtual void InfoBarDismissed() OVERRIDE; |
| virtual Type GetInfoBarType() const OVERRIDE; |
| virtual bool ShouldExpireInternal( |
| const content::LoadCommittedDetails& details) const OVERRIDE; |
| virtual string16 GetMessageText() const OVERRIDE; |
| virtual int GetButtons() const OVERRIDE; |
| virtual bool Cancel() OVERRIDE; |
| |
| std::string client_name_; |
| ExtensionDevToolsClientHost* client_host_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ExtensionDevToolsInfoBarDelegate); |
| }; |
| |
| // static |
| ExtensionDevToolsInfoBarDelegate* ExtensionDevToolsInfoBarDelegate::Create( |
| RenderViewHost* rvh, |
| const std::string& client_name) { |
| if (!rvh) |
| return NULL; |
| |
| WebContents* web_contents = WebContents::FromRenderViewHost(rvh); |
| if (!web_contents) |
| return NULL; |
| |
| InfoBarService* infobar_service = |
| InfoBarService::FromWebContents(web_contents); |
| if (!infobar_service) |
| return NULL; |
| |
| return static_cast<ExtensionDevToolsInfoBarDelegate*>( |
| infobar_service->AddInfoBar(scoped_ptr<InfoBarDelegate>( |
| new ExtensionDevToolsInfoBarDelegate(infobar_service, client_name)))); |
| } |
| |
| ExtensionDevToolsInfoBarDelegate::ExtensionDevToolsInfoBarDelegate( |
| InfoBarService* infobar_service, |
| const std::string& client_name) |
| : ConfirmInfoBarDelegate(infobar_service), |
| client_name_(client_name), |
| client_host_(NULL) { |
| } |
| |
| ExtensionDevToolsInfoBarDelegate::~ExtensionDevToolsInfoBarDelegate() { |
| } |
| |
| void ExtensionDevToolsInfoBarDelegate::InfoBarDismissed() { |
| if (client_host_) |
| client_host_->MarkAsDismissed(); |
| } |
| |
| InfoBarDelegate::Type ExtensionDevToolsInfoBarDelegate::GetInfoBarType() const { |
| return WARNING_TYPE; |
| } |
| |
| bool ExtensionDevToolsInfoBarDelegate::ShouldExpireInternal( |
| const content::LoadCommittedDetails& details) const { |
| return false; |
| } |
| |
| string16 ExtensionDevToolsInfoBarDelegate::GetMessageText() const { |
| return l10n_util::GetStringFUTF16(IDS_DEV_TOOLS_INFOBAR_LABEL, |
| UTF8ToUTF16(client_name_)); |
| } |
| |
| int ExtensionDevToolsInfoBarDelegate::GetButtons() const { |
| return BUTTON_CANCEL; |
| } |
| |
| bool ExtensionDevToolsInfoBarDelegate::Cancel() { |
| InfoBarDismissed(); |
| return true; |
| } |
| |
| |
| // AttachedClientHosts -------------------------------------------------------- |
| |
| class AttachedClientHosts { |
| public: |
| AttachedClientHosts(); |
| ~AttachedClientHosts(); |
| |
| // Returns the singleton instance of this class. |
| static AttachedClientHosts* GetInstance(); |
| |
| void Add(ExtensionDevToolsClientHost* client_host); |
| void Remove(ExtensionDevToolsClientHost* client_host); |
| ExtensionDevToolsClientHost* Lookup(DevToolsAgentHost* agent_host, |
| const std::string& extension_id); |
| |
| private: |
| typedef std::set<ExtensionDevToolsClientHost*> ClientHosts; |
| ClientHosts client_hosts_; |
| |
| DISALLOW_COPY_AND_ASSIGN(AttachedClientHosts); |
| }; |
| |
| AttachedClientHosts::AttachedClientHosts() { |
| } |
| |
| AttachedClientHosts::~AttachedClientHosts() { |
| } |
| |
| // static |
| AttachedClientHosts* AttachedClientHosts::GetInstance() { |
| return Singleton<AttachedClientHosts>::get(); |
| } |
| |
| void AttachedClientHosts::Add(ExtensionDevToolsClientHost* client_host) { |
| client_hosts_.insert(client_host); |
| } |
| |
| void AttachedClientHosts::Remove(ExtensionDevToolsClientHost* client_host) { |
| client_hosts_.erase(client_host); |
| } |
| |
| ExtensionDevToolsClientHost* AttachedClientHosts::Lookup( |
| DevToolsAgentHost* agent_host, |
| const std::string& extension_id) { |
| DevToolsManager* manager = DevToolsManager::GetInstance(); |
| for (ClientHosts::iterator it = client_hosts_.begin(); |
| it != client_hosts_.end(); ++it) { |
| ExtensionDevToolsClientHost* client_host = *it; |
| if (manager->GetDevToolsAgentHostFor(client_host) == agent_host && |
| client_host->extension_id() == extension_id) |
| return client_host; |
| } |
| return NULL; |
| } |
| |
| } // namespace |
| |
| |
| // ExtensionDevToolsClientHost ------------------------------------------------ |
| |
| ExtensionDevToolsClientHost::ExtensionDevToolsClientHost( |
| Profile* profile, |
| DevToolsAgentHost* agent_host, |
| const std::string& extension_id, |
| const std::string& extension_name, |
| const Debuggee& debuggee, |
| ExtensionDevToolsInfoBarDelegate* infobar) |
| : profile_(profile), |
| agent_host_(agent_host), |
| extension_id_(extension_id), |
| last_request_id_(0), |
| infobar_(infobar), |
| detach_reason_(OnDetach::REASON_TARGET_CLOSED) { |
| CopyDebuggee(&debuggee_, debuggee); |
| |
| AttachedClientHosts::GetInstance()->Add(this); |
| |
| // Detach from debugger when extension unloads. |
| registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, |
| content::Source<Profile>(profile_)); |
| |
| // RVH-based agents disconnect from their clients when the app is terminating |
| // but shared worker-based agents do not. |
| // Disconnect explicitly to make sure that |this| observer is not leaked. |
| registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING, |
| content::NotificationService::AllSources()); |
| |
| // Attach to debugger and tell it we are ready. |
| DevToolsManager::GetInstance()->RegisterDevToolsClientHostFor( |
| agent_host_.get(), this); |
| |
| if (infobar_) { |
| infobar_->set_client_host(this); |
| registrar_.Add( |
| this, chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED, |
| content::Source<InfoBarService>(InfoBarService::FromWebContents( |
| WebContents::FromRenderViewHost( |
| agent_host_->GetRenderViewHost())))); |
| } |
| } |
| |
| ExtensionDevToolsClientHost::~ExtensionDevToolsClientHost() { |
| // Ensure calling RemoveInfoBar() below won't result in Observe() trying to |
| // Close() us. |
| registrar_.RemoveAll(); |
| |
| if (infobar_) { |
| infobar_->set_client_host(NULL); |
| InfoBarService::FromWebContents(WebContents::FromRenderViewHost( |
| agent_host_->GetRenderViewHost()))->RemoveInfoBar(infobar_); |
| } |
| AttachedClientHosts::GetInstance()->Remove(this); |
| } |
| |
| // DevToolsClientHost interface |
| void ExtensionDevToolsClientHost::InspectedContentsClosing() { |
| SendDetachedEvent(); |
| delete this; |
| } |
| |
| void ExtensionDevToolsClientHost::ReplacedWithAnotherClient() { |
| detach_reason_ = OnDetach::REASON_REPLACED_WITH_DEVTOOLS; |
| } |
| |
| void ExtensionDevToolsClientHost::Close() { |
| DevToolsManager::GetInstance()->ClientHostClosing(this); |
| delete this; |
| } |
| |
| void ExtensionDevToolsClientHost::SendMessageToBackend( |
| DebuggerSendCommandFunction* function, |
| const std::string& method, |
| SendCommand::Params::CommandParams* command_params) { |
| base::DictionaryValue protocol_request; |
| int request_id = ++last_request_id_; |
| pending_requests_[request_id] = function; |
| protocol_request.SetInteger("id", request_id); |
| protocol_request.SetString("method", method); |
| if (command_params) { |
| protocol_request.Set("params", |
| command_params->additional_properties.DeepCopy()); |
| } |
| |
| std::string json_args; |
| base::JSONWriter::Write(&protocol_request, &json_args); |
| DevToolsManager::GetInstance()->DispatchOnInspectorBackend(this, json_args); |
| } |
| |
| void ExtensionDevToolsClientHost::MarkAsDismissed() { |
| detach_reason_ = OnDetach::REASON_CANCELED_BY_USER; |
| } |
| |
| void ExtensionDevToolsClientHost::SendDetachedEvent() { |
| if (!extensions::ExtensionSystem::Get(profile_)->event_router()) |
| return; |
| |
| scoped_ptr<base::ListValue> args(OnDetach::Create(debuggee_, |
| detach_reason_)); |
| scoped_ptr<extensions::Event> event(new extensions::Event( |
| OnDetach::kEventName, args.Pass())); |
| event->restrict_to_profile = profile_; |
| extensions::ExtensionSystem::Get(profile_)->event_router()-> |
| DispatchEventToExtension(extension_id_, event.Pass()); |
| } |
| |
| void ExtensionDevToolsClientHost::Observe( |
| int type, |
| const content::NotificationSource& source, |
| const content::NotificationDetails& details) { |
| if (type == chrome::NOTIFICATION_EXTENSION_UNLOADED) { |
| if (content::Details<extensions::UnloadedExtensionInfo>(details)-> |
| extension->id() == extension_id_) |
| Close(); |
| } else if (type == chrome::NOTIFICATION_APP_TERMINATING) { |
| Close(); |
| } else { |
| DCHECK_EQ(chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED, type); |
| if (content::Details<InfoBarRemovedDetails>(details)->first == infobar_) { |
| infobar_ = NULL; |
| SendDetachedEvent(); |
| Close(); |
| } |
| } |
| } |
| |
| void ExtensionDevToolsClientHost::DispatchOnInspectorFrontend( |
| const std::string& message) { |
| if (!extensions::ExtensionSystem::Get(profile_)->event_router()) |
| return; |
| |
| scoped_ptr<Value> result(base::JSONReader::Read(message)); |
| if (!result->IsType(Value::TYPE_DICTIONARY)) |
| return; |
| base::DictionaryValue* dictionary = |
| static_cast<base::DictionaryValue*>(result.get()); |
| |
| int id; |
| if (!dictionary->GetInteger("id", &id)) { |
| std::string method_name; |
| if (!dictionary->GetString("method", &method_name)) |
| return; |
| |
| OnEvent::Params params; |
| base::DictionaryValue* params_value; |
| if (dictionary->GetDictionary("params", ¶ms_value)) |
| params.additional_properties.Swap(params_value); |
| |
| scoped_ptr<ListValue> args(OnEvent::Create(debuggee_, method_name, params)); |
| scoped_ptr<extensions::Event> event(new extensions::Event( |
| OnEvent::kEventName, args.Pass())); |
| event->restrict_to_profile = profile_; |
| extensions::ExtensionSystem::Get(profile_)->event_router()-> |
| DispatchEventToExtension(extension_id_, event.Pass()); |
| } else { |
| DebuggerSendCommandFunction* function = pending_requests_[id].get(); |
| if (!function) |
| return; |
| |
| function->SendResponseBody(dictionary); |
| pending_requests_.erase(id); |
| } |
| } |
| |
| |
| // DebuggerFunction ----------------------------------------------------------- |
| |
| DebuggerFunction::DebuggerFunction() |
| : client_host_(NULL) { |
| } |
| |
| DebuggerFunction::~DebuggerFunction() { |
| } |
| |
| void DebuggerFunction::FormatErrorMessage(const std::string& format) { |
| if (debuggee_.tab_id) |
| error_ = ErrorUtils::FormatErrorMessage( |
| format, keys::kTabTargetType, base::IntToString(*debuggee_.tab_id)); |
| else if (debuggee_.extension_id) |
| error_ = ErrorUtils::FormatErrorMessage( |
| format, keys::kBackgroundPageTargetType, *debuggee_.extension_id); |
| else |
| error_ = ErrorUtils::FormatErrorMessage( |
| format, keys::kOpaqueTargetType, *debuggee_.target_id); |
| } |
| |
| bool DebuggerFunction::InitAgentHost() { |
| if (debuggee_.tab_id) { |
| WebContents* web_contents = NULL; |
| bool result = ExtensionTabUtil::GetTabById( |
| *debuggee_.tab_id, profile(), include_incognito(), NULL, NULL, |
| &web_contents, NULL); |
| if (result && web_contents) { |
| if (content::HasWebUIScheme(web_contents->GetURL())) { |
| error_ = ErrorUtils::FormatErrorMessage( |
| keys::kAttachToWebUIError, |
| web_contents->GetURL().scheme()); |
| return false; |
| } |
| agent_host_ = DevToolsAgentHost::GetOrCreateFor( |
| web_contents->GetRenderViewHost()); |
| } |
| } else if (debuggee_.extension_id) { |
| extensions::ExtensionHost* extension_host = |
| extensions::ExtensionSystem::Get(profile())->process_manager()-> |
| GetBackgroundHostForExtension(*debuggee_.extension_id); |
| if (extension_host) { |
| agent_host_ = DevToolsAgentHost::GetOrCreateFor( |
| extension_host->render_view_host()); |
| } |
| } else if (debuggee_.target_id) { |
| agent_host_ = DevToolsAgentHost::GetForId(*debuggee_.target_id); |
| } else { |
| error_ = keys::kInvalidTargetError; |
| return false; |
| } |
| |
| if (!agent_host_.get()) { |
| FormatErrorMessage(keys::kNoTargetError); |
| return false; |
| } |
| return true; |
| } |
| |
| bool DebuggerFunction::InitClientHost() { |
| if (!InitAgentHost()) |
| return false; |
| |
| client_host_ = AttachedClientHosts::GetInstance()->Lookup( |
| agent_host_.get(), GetExtension()->id()); |
| |
| if (!client_host_) { |
| FormatErrorMessage(keys::kNotAttachedError); |
| return false; |
| } |
| return true; |
| } |
| |
| |
| // DebuggerAttachFunction ----------------------------------------------------- |
| |
| DebuggerAttachFunction::DebuggerAttachFunction() { |
| } |
| |
| DebuggerAttachFunction::~DebuggerAttachFunction() { |
| } |
| |
| bool DebuggerAttachFunction::RunImpl() { |
| scoped_ptr<Attach::Params> params(Attach::Params::Create(*args_)); |
| EXTENSION_FUNCTION_VALIDATE(params.get()); |
| |
| CopyDebuggee(&debuggee_, params->target); |
| if (!InitAgentHost()) |
| return false; |
| |
| if (!DevToolsHttpHandler::IsSupportedProtocolVersion( |
| params->required_version)) { |
| error_ = ErrorUtils::FormatErrorMessage( |
| keys::kProtocolVersionNotSupportedError, |
| params->required_version); |
| return false; |
| } |
| |
| if (agent_host_->IsAttached()) { |
| FormatErrorMessage(keys::kAlreadyAttachedError); |
| return false; |
| } |
| |
| ExtensionDevToolsInfoBarDelegate* infobar = NULL; |
| if (!CommandLine::ForCurrentProcess()-> |
| HasSwitch(switches::kSilentDebuggerExtensionAPI)) { |
| // Do not attach to the target if for any reason the infobar cannot be shown |
| // for this WebContents instance. |
| infobar = ExtensionDevToolsInfoBarDelegate::Create( |
| agent_host_->GetRenderViewHost(), GetExtension()->name()); |
| if (!infobar) { |
| error_ = ErrorUtils::FormatErrorMessage( |
| keys::kSilentDebuggingRequired, |
| switches::kSilentDebuggerExtensionAPI); |
| return false; |
| } |
| } |
| |
| new ExtensionDevToolsClientHost(profile(), agent_host_.get(), |
| GetExtension()->id(), GetExtension()->name(), |
| debuggee_, infobar); |
| SendResponse(true); |
| return true; |
| } |
| |
| |
| // DebuggerDetachFunction ----------------------------------------------------- |
| |
| DebuggerDetachFunction::DebuggerDetachFunction() { |
| } |
| |
| DebuggerDetachFunction::~DebuggerDetachFunction() { |
| } |
| |
| bool DebuggerDetachFunction::RunImpl() { |
| scoped_ptr<Detach::Params> params(Detach::Params::Create(*args_)); |
| EXTENSION_FUNCTION_VALIDATE(params.get()); |
| |
| CopyDebuggee(&debuggee_, params->target); |
| if (!InitClientHost()) |
| return false; |
| |
| client_host_->Close(); |
| SendResponse(true); |
| return true; |
| } |
| |
| |
| // DebuggerSendCommandFunction ------------------------------------------------ |
| |
| DebuggerSendCommandFunction::DebuggerSendCommandFunction() { |
| } |
| |
| DebuggerSendCommandFunction::~DebuggerSendCommandFunction() { |
| } |
| |
| bool DebuggerSendCommandFunction::RunImpl() { |
| scoped_ptr<SendCommand::Params> params(SendCommand::Params::Create(*args_)); |
| EXTENSION_FUNCTION_VALIDATE(params.get()); |
| |
| CopyDebuggee(&debuggee_, params->target); |
| if (!InitClientHost()) |
| return false; |
| |
| client_host_->SendMessageToBackend(this, params->method, |
| params->command_params.get()); |
| return true; |
| } |
| |
| void DebuggerSendCommandFunction::SendResponseBody( |
| base::DictionaryValue* response) { |
| Value* error_body; |
| if (response->Get("error", &error_body)) { |
| base::JSONWriter::Write(error_body, &error_); |
| SendResponse(false); |
| return; |
| } |
| |
| base::DictionaryValue* result_body; |
| SendCommand::Results::Result result; |
| if (response->GetDictionary("result", &result_body)) |
| result.additional_properties.Swap(result_body); |
| |
| results_ = SendCommand::Results::Create(result); |
| SendResponse(true); |
| } |
| |
| |
| // DebuggerGetTargetsFunction ------------------------------------------------- |
| |
| namespace { |
| |
| const char kTargetIdField[] = "id"; |
| const char kTargetTypeField[] = "type"; |
| const char kTargetTitleField[] = "title"; |
| const char kTargetAttachedField[] = "attached"; |
| const char kTargetUrlField[] = "url"; |
| const char kTargetFaviconUrlField[] = "faviconUrl"; |
| const char kTargetTypePage[] = "page"; |
| const char kTargetTypeBackgroundPage[] = "background_page"; |
| const char kTargetTypeWorker[] = "worker"; |
| const char kTargetTypeOther[] = "other"; |
| const char kTargetTabIdField[] = "tabId"; |
| const char kTargetExtensionIdField[] = "extensionId"; |
| |
| extensions::ExtensionHost* |
| GetExtensionBackgroundHost(WebContents* web_contents) { |
| Profile* profile = |
| Profile::FromBrowserContext(web_contents->GetBrowserContext()); |
| if (!profile) |
| return NULL; |
| |
| extensions::ExtensionHost* extension_host = |
| extensions::ExtensionSystem::Get(profile)->process_manager()-> |
| GetBackgroundHostForExtension(web_contents->GetURL().host()); |
| |
| return (extension_host && extension_host->host_contents() == web_contents) ? |
| extension_host : NULL; |
| } |
| |
| base::Value* SerializePageInfo(RenderViewHost* rvh) { |
| WebContents* web_contents = WebContents::FromRenderViewHost(rvh); |
| if (!web_contents) |
| return NULL; |
| |
| DevToolsAgentHost* agent_host = DevToolsAgentHost::GetOrCreateFor(rvh); |
| |
| base::DictionaryValue* dictionary = new base::DictionaryValue(); |
| |
| dictionary->SetString(kTargetIdField, agent_host->GetId()); |
| dictionary->SetBoolean(kTargetAttachedField, agent_host->IsAttached()); |
| dictionary->SetString(kTargetUrlField, web_contents->GetURL().spec()); |
| |
| extensions::ExtensionHost* extension_host = |
| GetExtensionBackgroundHost(web_contents); |
| if (extension_host) { |
| // This RenderViewHost belongs to a background page. |
| dictionary->SetString(kTargetTypeField, kTargetTypeBackgroundPage); |
| dictionary->SetString(kTargetExtensionIdField, |
| extension_host->extension()->id()); |
| dictionary->SetString(kTargetTitleField, |
| extension_host->extension()->name()); |
| } else { |
| int tab_id = ExtensionTabUtil::GetTabId(web_contents); |
| if (tab_id != -1) { |
| // This RenderViewHost belongs to a regular page. |
| dictionary->SetString(kTargetTypeField, kTargetTypePage); |
| dictionary->SetInteger(kTargetTabIdField, tab_id); |
| } else { |
| dictionary->SetString(kTargetTypeField, kTargetTypeOther); |
| } |
| dictionary->SetString(kTargetTitleField, web_contents->GetTitle()); |
| |
| content::NavigationController& controller = web_contents->GetController(); |
| content::NavigationEntry* entry = controller.GetVisibleEntry(); |
| if (entry != NULL && entry->GetURL().is_valid()) { |
| dictionary->SetString(kTargetFaviconUrlField, |
| entry->GetFavicon().url.spec()); |
| } |
| } |
| |
| return dictionary; |
| } |
| |
| base::Value* SerializeWorkerInfo(const WorkerService::WorkerInfo& worker) { |
| base::DictionaryValue* dictionary = new base::DictionaryValue; |
| |
| scoped_refptr<DevToolsAgentHost> agent(DevToolsAgentHost::GetForWorker( |
| worker.process_id, worker.route_id)); |
| dictionary->SetString(kTargetIdField, agent->GetId()); |
| dictionary->SetString(kTargetTypeField, kTargetTypeWorker); |
| dictionary->SetString(kTargetTitleField, worker.name); |
| dictionary->SetString(kTargetUrlField, worker.url.spec()); |
| dictionary->SetBoolean(kTargetAttachedField, agent->IsAttached()); |
| |
| return dictionary; |
| } |
| |
| } // namespace |
| |
| DebuggerGetTargetsFunction::DebuggerGetTargetsFunction() { |
| } |
| |
| DebuggerGetTargetsFunction::~DebuggerGetTargetsFunction() { |
| } |
| |
| bool DebuggerGetTargetsFunction::RunImpl() { |
| base::ListValue* results_list = new base::ListValue(); |
| |
| std::vector<RenderViewHost*> rvh_list = |
| DevToolsAgentHost::GetValidRenderViewHosts(); |
| for (std::vector<RenderViewHost*>::iterator it = rvh_list.begin(); |
| it != rvh_list.end(); ++it) { |
| base::Value* value = SerializePageInfo(*it); |
| if (value) |
| results_list->Append(value); |
| } |
| |
| content::BrowserThread::PostTaskAndReplyWithResult( |
| content::BrowserThread::IO, |
| FROM_HERE, |
| base::Bind(&DebuggerGetTargetsFunction::CollectWorkerInfo, this), |
| base::Bind(&DebuggerGetTargetsFunction::SendTargetList, this, |
| results_list)); |
| return true; |
| } |
| |
| DebuggerGetTargetsFunction::WorkerInfoList |
| DebuggerGetTargetsFunction::CollectWorkerInfo() { |
| return WorkerService::GetInstance()->GetWorkers(); |
| } |
| |
| void DebuggerGetTargetsFunction::SendTargetList( |
| base::ListValue* list, |
| const WorkerInfoList& worker_info) { |
| for (size_t i = 0; i < worker_info.size(); ++i) |
| list->Append(SerializeWorkerInfo(worker_info[i])); |
| SetResult(list); |
| SendResponse(true); |
| } |