| // 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 <stddef.h> |
| |
| #include <map> |
| #include <memory> |
| #include <set> |
| #include <utility> |
| |
| #include "base/callback_helpers.h" |
| #include "base/command_line.h" |
| #include "base/json/json_reader.h" |
| #include "base/json/json_writer.h" |
| #include "base/lazy_instance.h" |
| #include "base/macros.h" |
| #include "base/memory/singleton.h" |
| #include "base/scoped_observer.h" |
| #include "base/stl_util.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/devtools/chrome_devtools_manager_delegate.h" |
| #include "chrome/browser/devtools/global_confirm_info_bar.h" |
| #include "chrome/browser/extensions/api/debugger/debugger_api_constants.h" |
| #include "chrome/browser/extensions/extension_service.h" |
| #include "chrome/browser/extensions/extension_tab_util.h" |
| #include "chrome/browser/infobars/infobar_service.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/grit/generated_resources.h" |
| #include "components/infobars/core/confirm_infobar_delegate.h" |
| #include "components/infobars/core/infobar.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/devtools_agent_host.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_widget_host.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/common/content_client.h" |
| #include "content/public/common/url_utils.h" |
| #include "extensions/browser/event_router.h" |
| #include "extensions/browser/extension_host.h" |
| #include "extensions/browser/extension_registry.h" |
| #include "extensions/browser/extension_registry_observer.h" |
| #include "extensions/common/constants.h" |
| #include "extensions/common/error_utils.h" |
| #include "extensions/common/extension.h" |
| #include "extensions/common/manifest_constants.h" |
| #include "extensions/common/permissions/permissions_data.h" |
| #include "extensions/common/switches.h" |
| #include "ui/base/l10n/l10n_util.h" |
| |
| using content::DevToolsAgentHost; |
| using content::RenderProcessHost; |
| using content::RenderWidgetHost; |
| using content::WebContents; |
| |
| 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 extensions { |
| class ExtensionRegistry; |
| class ExtensionDevToolsClientHost; |
| |
| 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: |
| ExtensionDevToolsInfoBarDelegate(const base::Closure& dismissed_callback, |
| const std::string& client_name); |
| ~ExtensionDevToolsInfoBarDelegate() override; |
| |
| // ConfirmInfoBarDelegate: |
| Type GetInfoBarType() const override; |
| infobars::InfoBarDelegate::InfoBarIdentifier GetIdentifier() const override; |
| bool ShouldExpire(const NavigationDetails& details) const override; |
| void InfoBarDismissed() override; |
| base::string16 GetMessageText() const override; |
| int GetButtons() const override; |
| bool Cancel() override; |
| |
| private: |
| const base::string16 client_name_; |
| base::Closure dismissed_callback_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ExtensionDevToolsInfoBarDelegate); |
| }; |
| |
| ExtensionDevToolsInfoBarDelegate::ExtensionDevToolsInfoBarDelegate( |
| const base::Closure& dismissed_callback, |
| const std::string& client_name) |
| : ConfirmInfoBarDelegate(), |
| client_name_(base::UTF8ToUTF16(client_name)), |
| dismissed_callback_(dismissed_callback) {} |
| |
| ExtensionDevToolsInfoBarDelegate::~ExtensionDevToolsInfoBarDelegate() { |
| } |
| |
| infobars::InfoBarDelegate::Type |
| ExtensionDevToolsInfoBarDelegate::GetInfoBarType() const { |
| return WARNING_TYPE; |
| } |
| |
| infobars::InfoBarDelegate::InfoBarIdentifier |
| ExtensionDevToolsInfoBarDelegate::GetIdentifier() const { |
| return EXTENSION_DEV_TOOLS_INFOBAR_DELEGATE; |
| } |
| |
| bool ExtensionDevToolsInfoBarDelegate::ShouldExpire( |
| const NavigationDetails& details) const { |
| return false; |
| } |
| |
| void ExtensionDevToolsInfoBarDelegate::InfoBarDismissed() { |
| DCHECK(!dismissed_callback_.is_null()); |
| // Use ResetAndReturn() since running the callback may delete |this|. |
| base::ResetAndReturn(&dismissed_callback_).Run(); |
| } |
| |
| base::string16 ExtensionDevToolsInfoBarDelegate::GetMessageText() const { |
| return l10n_util::GetStringFUTF16(IDS_DEV_TOOLS_INFOBAR_LABEL, client_name_); |
| } |
| |
| int ExtensionDevToolsInfoBarDelegate::GetButtons() const { |
| return BUTTON_CANCEL; |
| } |
| |
| bool ExtensionDevToolsInfoBarDelegate::Cancel() { |
| InfoBarDismissed(); |
| // InfoBarDismissed() will have closed us already. |
| return false; |
| } |
| |
| // ExtensionDevToolsInfoBar --------------------------------------------------- |
| |
| class ExtensionDevToolsInfoBar; |
| using ExtensionInfoBars = |
| std::map<std::string, ExtensionDevToolsInfoBar*>; |
| base::LazyInstance<ExtensionInfoBars>::Leaky g_extension_info_bars = |
| LAZY_INSTANCE_INITIALIZER; |
| |
| class ExtensionDevToolsInfoBar { |
| public: |
| static ExtensionDevToolsInfoBar* Create( |
| const std::string& extension_id, |
| const std::string& extension_name, |
| ExtensionDevToolsClientHost* client_host, |
| const base::Closure& dismissed_callback); |
| void Remove(ExtensionDevToolsClientHost* client_host); |
| |
| private: |
| ExtensionDevToolsInfoBar(const std::string& extension_id, |
| const std::string& extension_name); |
| ~ExtensionDevToolsInfoBar(); |
| void InfoBarDismissed(); |
| |
| std::string extension_id_; |
| std::map<ExtensionDevToolsClientHost*, base::Closure> callbacks_; |
| base::WeakPtr<GlobalConfirmInfoBar> infobar_; |
| }; |
| |
| // static |
| ExtensionDevToolsInfoBar* ExtensionDevToolsInfoBar::Create( |
| const std::string& extension_id, |
| const std::string& extension_name, |
| ExtensionDevToolsClientHost* client_host, |
| const base::Closure& dismissed_callback) { |
| ExtensionInfoBars::iterator it = |
| g_extension_info_bars.Get().find(extension_id); |
| ExtensionDevToolsInfoBar* infobar = nullptr; |
| if (it != g_extension_info_bars.Get().end()) |
| infobar = it->second; |
| else |
| infobar = new ExtensionDevToolsInfoBar(extension_id, extension_name); |
| infobar->callbacks_[client_host] = dismissed_callback; |
| return infobar; |
| } |
| |
| ExtensionDevToolsInfoBar::ExtensionDevToolsInfoBar( |
| const std::string& extension_id, |
| const std::string& extension_name) |
| : extension_id_(extension_id) { |
| g_extension_info_bars.Get()[extension_id] = this; |
| |
| // This class closes the |infobar_|, so it's safe to pass Unretained(this). |
| std::unique_ptr<ExtensionDevToolsInfoBarDelegate> delegate( |
| new ExtensionDevToolsInfoBarDelegate( |
| base::Bind(&ExtensionDevToolsInfoBar::InfoBarDismissed, |
| base::Unretained(this)), |
| extension_name)); |
| infobar_ = GlobalConfirmInfoBar::Show(std::move(delegate)); |
| } |
| |
| ExtensionDevToolsInfoBar::~ExtensionDevToolsInfoBar() { |
| g_extension_info_bars.Get().erase(extension_id_); |
| if (infobar_) |
| infobar_->Close(); |
| } |
| |
| void ExtensionDevToolsInfoBar::Remove( |
| ExtensionDevToolsClientHost* client_host) { |
| callbacks_.erase(client_host); |
| if (callbacks_.empty()) |
| delete this; |
| } |
| |
| void ExtensionDevToolsInfoBar::InfoBarDismissed() { |
| std::map<ExtensionDevToolsClientHost*, base::Closure> copy = callbacks_; |
| for (const auto& pair : copy) |
| pair.second.Run(); |
| } |
| |
| } // namespace |
| |
| // ExtensionDevToolsClientHost ------------------------------------------------ |
| |
| using AttachedClientHosts = std::set<ExtensionDevToolsClientHost*>; |
| base::LazyInstance<AttachedClientHosts>::Leaky g_attached_client_hosts = |
| LAZY_INSTANCE_INITIALIZER; |
| |
| class ExtensionDevToolsClientHost : public content::DevToolsAgentHostClient, |
| public content::NotificationObserver, |
| public ExtensionRegistryObserver { |
| public: |
| ExtensionDevToolsClientHost(Profile* profile, |
| DevToolsAgentHost* agent_host, |
| const std::string& extension_id, |
| const std::string& extension_name, |
| const Debuggee& debuggee); |
| |
| ~ExtensionDevToolsClientHost() override; |
| |
| const std::string& extension_id() { return extension_id_; } |
| DevToolsAgentHost* agent_host() { return agent_host_.get(); } |
| void Close(); |
| void SendMessageToBackend(DebuggerSendCommandFunction* function, |
| const std::string& method, |
| SendCommand::Params::CommandParams* command_params); |
| |
| // Closes connection as terminated by the user. |
| void InfoBarDismissed(); |
| |
| // DevToolsAgentHostClient interface. |
| void AgentHostClosed(DevToolsAgentHost* agent_host) override; |
| void DispatchProtocolMessage(DevToolsAgentHost* agent_host, |
| const std::string& message) override; |
| |
| private: |
| using PendingRequests = |
| std::map<int, scoped_refptr<DebuggerSendCommandFunction>>; |
| |
| void SendDetachedEvent(); |
| |
| // content::NotificationObserver implementation. |
| void Observe(int type, |
| const content::NotificationSource& source, |
| const content::NotificationDetails& details) override; |
| |
| // ExtensionRegistryObserver implementation. |
| void OnExtensionUnloaded(content::BrowserContext* browser_context, |
| const Extension* extension, |
| UnloadedExtensionReason reason) override; |
| |
| Profile* profile_; |
| scoped_refptr<DevToolsAgentHost> agent_host_; |
| std::string extension_id_; |
| Debuggee debuggee_; |
| content::NotificationRegistrar registrar_; |
| int last_request_id_; |
| PendingRequests pending_requests_; |
| ExtensionDevToolsInfoBar* infobar_; |
| api::debugger::DetachReason detach_reason_; |
| |
| // Listen to extension unloaded notification. |
| ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver> |
| extension_registry_observer_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ExtensionDevToolsClientHost); |
| }; |
| |
| // ExtensionDevToolsClientHost ------------------------------------------------ |
| |
| ExtensionDevToolsClientHost::ExtensionDevToolsClientHost( |
| Profile* profile, |
| DevToolsAgentHost* agent_host, |
| const std::string& extension_id, |
| const std::string& extension_name, |
| const Debuggee& debuggee) |
| : profile_(profile), |
| agent_host_(agent_host), |
| extension_id_(extension_id), |
| last_request_id_(0), |
| infobar_(nullptr), |
| detach_reason_(api::debugger::DETACH_REASON_TARGET_CLOSED), |
| extension_registry_observer_(this) { |
| CopyDebuggee(&debuggee_, debuggee); |
| |
| g_attached_client_hosts.Get().insert(this); |
| |
| // ExtensionRegistryObserver listen extension unloaded and detach debugger |
| // from there. |
| extension_registry_observer_.Add(ExtensionRegistry::Get(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. |
| agent_host_->AttachClient(this); |
| |
| if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
| ::switches::kSilentDebuggerExtensionAPI)) { |
| return; |
| } |
| |
| // We allow policy-installed extensions to circumvent the normal |
| // infobar warning. See crbug.com/693621. |
| const Extension* extension = |
| ExtensionRegistry::Get(profile)->enabled_extensions().GetByID( |
| extension_id); |
| if (extension && Manifest::IsPolicyLocation(extension->location())) |
| return; |
| |
| infobar_ = ExtensionDevToolsInfoBar::Create( |
| extension_id, extension_name, this, |
| base::Bind(&ExtensionDevToolsClientHost::InfoBarDismissed, |
| base::Unretained(this))); |
| } |
| |
| ExtensionDevToolsClientHost::~ExtensionDevToolsClientHost() { |
| if (infobar_) |
| infobar_->Remove(this); |
| g_attached_client_hosts.Get().erase(this); |
| } |
| |
| // DevToolsAgentHostClient implementation. |
| void ExtensionDevToolsClientHost::AgentHostClosed( |
| DevToolsAgentHost* agent_host) { |
| DCHECK(agent_host == agent_host_.get()); |
| SendDetachedEvent(); |
| delete this; |
| } |
| |
| void ExtensionDevToolsClientHost::Close() { |
| agent_host_->DetachClient(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.CreateDeepCopy()); |
| } |
| |
| std::string json_args; |
| base::JSONWriter::Write(protocol_request, &json_args); |
| agent_host_->DispatchProtocolMessage(this, json_args); |
| } |
| |
| void ExtensionDevToolsClientHost::InfoBarDismissed() { |
| detach_reason_ = api::debugger::DETACH_REASON_CANCELED_BY_USER; |
| SendDetachedEvent(); |
| Close(); |
| } |
| |
| void ExtensionDevToolsClientHost::SendDetachedEvent() { |
| if (!EventRouter::Get(profile_)) |
| return; |
| |
| std::unique_ptr<base::ListValue> args( |
| OnDetach::Create(debuggee_, detach_reason_)); |
| auto event = |
| base::MakeUnique<Event>(events::DEBUGGER_ON_DETACH, OnDetach::kEventName, |
| std::move(args), profile_); |
| EventRouter::Get(profile_) |
| ->DispatchEventToExtension(extension_id_, std::move(event)); |
| } |
| |
| void ExtensionDevToolsClientHost::OnExtensionUnloaded( |
| content::BrowserContext* browser_context, |
| const Extension* extension, |
| UnloadedExtensionReason reason) { |
| if (extension->id() == extension_id_) |
| Close(); |
| } |
| |
| void ExtensionDevToolsClientHost::Observe( |
| int type, |
| const content::NotificationSource& source, |
| const content::NotificationDetails& details) { |
| DCHECK_EQ(chrome::NOTIFICATION_APP_TERMINATING, type); |
| Close(); |
| } |
| |
| void ExtensionDevToolsClientHost::DispatchProtocolMessage( |
| DevToolsAgentHost* agent_host, const std::string& message) { |
| DCHECK(agent_host == agent_host_.get()); |
| if (!EventRouter::Get(profile_)) |
| return; |
| |
| std::unique_ptr<base::Value> result = base::JSONReader::Read(message); |
| if (!result || !result->is_dict()) |
| 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); |
| |
| std::unique_ptr<base::ListValue> args( |
| OnEvent::Create(debuggee_, method_name, params)); |
| auto event = |
| base::MakeUnique<Event>(events::DEBUGGER_ON_EVENT, OnEvent::kEventName, |
| std::move(args), profile_); |
| EventRouter::Get(profile_) |
| ->DispatchEventToExtension(extension_id_, std::move(event)); |
| } 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, |
| GetProfile(), |
| include_incognito(), |
| NULL, |
| NULL, |
| &web_contents, |
| NULL); |
| if (result && web_contents) { |
| // TODO(rdevlin.cronin) This should definitely be GetLastCommittedURL(). |
| GURL url = web_contents->GetVisibleURL(); |
| if (PermissionsData::IsRestrictedUrl(url, extension(), &error_)) |
| return false; |
| agent_host_ = DevToolsAgentHost::GetOrCreateFor(web_contents); |
| } |
| } else if (debuggee_.extension_id) { |
| ExtensionHost* extension_host = |
| ProcessManager::Get(GetProfile()) |
| ->GetBackgroundHostForExtension(*debuggee_.extension_id); |
| if (extension_host) { |
| if (PermissionsData::IsRestrictedUrl(extension_host->GetURL(), |
| extension(), |
| &error_)) { |
| return false; |
| } |
| agent_host_ = |
| DevToolsAgentHost::GetOrCreateFor(extension_host->host_contents()); |
| } |
| } else if (debuggee_.target_id) { |
| agent_host_ = DevToolsAgentHost::GetForId(*debuggee_.target_id); |
| if (agent_host_.get()) { |
| if (PermissionsData::IsRestrictedUrl(agent_host_->GetURL(), |
| extension(), |
| &error_)) { |
| agent_host_ = nullptr; |
| return false; |
| } |
| } |
| } 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_ = FindClientHost(); |
| if (!client_host_) { |
| FormatErrorMessage(keys::kNotAttachedError); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| ExtensionDevToolsClientHost* DebuggerFunction::FindClientHost() { |
| if (!agent_host_.get()) |
| return nullptr; |
| |
| const std::string& extension_id = extension()->id(); |
| DevToolsAgentHost* agent_host = agent_host_.get(); |
| AttachedClientHosts& hosts = g_attached_client_hosts.Get(); |
| AttachedClientHosts::iterator it = std::find_if( |
| hosts.begin(), hosts.end(), |
| [&agent_host, &extension_id](ExtensionDevToolsClientHost* client_host) { |
| return client_host->agent_host() == agent_host && |
| client_host->extension_id() == extension_id; |
| }); |
| |
| return it == hosts.end() ? nullptr : *it; |
| } |
| |
| // DebuggerAttachFunction ----------------------------------------------------- |
| |
| DebuggerAttachFunction::DebuggerAttachFunction() { |
| } |
| |
| DebuggerAttachFunction::~DebuggerAttachFunction() { |
| } |
| |
| bool DebuggerAttachFunction::RunAsync() { |
| std::unique_ptr<Attach::Params> params(Attach::Params::Create(*args_)); |
| EXTENSION_FUNCTION_VALIDATE(params.get()); |
| |
| CopyDebuggee(&debuggee_, params->target); |
| if (!InitAgentHost()) |
| return false; |
| |
| if (!DevToolsAgentHost::IsSupportedProtocolVersion( |
| params->required_version)) { |
| error_ = ErrorUtils::FormatErrorMessage( |
| keys::kProtocolVersionNotSupportedError, |
| params->required_version); |
| return false; |
| } |
| |
| if (FindClientHost()) { |
| FormatErrorMessage(keys::kAlreadyAttachedError); |
| return false; |
| } |
| |
| new ExtensionDevToolsClientHost(GetProfile(), agent_host_.get(), |
| extension()->id(), extension()->name(), |
| debuggee_); |
| SendResponse(true); |
| return true; |
| } |
| |
| |
| // DebuggerDetachFunction ----------------------------------------------------- |
| |
| DebuggerDetachFunction::DebuggerDetachFunction() { |
| } |
| |
| DebuggerDetachFunction::~DebuggerDetachFunction() { |
| } |
| |
| bool DebuggerDetachFunction::RunAsync() { |
| std::unique_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::RunAsync() { |
| std::unique_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) { |
| base::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 kTargetTabIdField[] = "tabId"; |
| const char kTargetExtensionIdField[] = "extensionId"; |
| const char kTargetTypeWorker[] = "worker"; |
| |
| std::unique_ptr<base::DictionaryValue> SerializeTarget( |
| scoped_refptr<DevToolsAgentHost> host) { |
| std::unique_ptr<base::DictionaryValue> dictionary( |
| new base::DictionaryValue()); |
| dictionary->SetString(kTargetIdField, host->GetId()); |
| dictionary->SetString(kTargetTitleField, host->GetTitle()); |
| dictionary->SetBoolean(kTargetAttachedField, host->IsAttached()); |
| dictionary->SetString(kTargetUrlField, host->GetURL().spec()); |
| |
| std::string type = host->GetType(); |
| if (type == DevToolsAgentHost::kTypePage) { |
| int tab_id = |
| extensions::ExtensionTabUtil::GetTabId(host->GetWebContents()); |
| dictionary->SetInteger(kTargetTabIdField, tab_id); |
| } else if (type == ChromeDevToolsManagerDelegate::kTypeBackgroundPage) { |
| dictionary->SetString(kTargetExtensionIdField, host->GetURL().host()); |
| } |
| |
| if (type == DevToolsAgentHost::kTypeServiceWorker || |
| type == DevToolsAgentHost::kTypeSharedWorker) { |
| type = kTargetTypeWorker; |
| } |
| |
| dictionary->SetString(kTargetTypeField, type); |
| |
| GURL favicon_url = host->GetFaviconURL(); |
| if (favicon_url.is_valid()) |
| dictionary->SetString(kTargetFaviconUrlField, favicon_url.spec()); |
| |
| return dictionary; |
| } |
| |
| } // namespace |
| |
| DebuggerGetTargetsFunction::DebuggerGetTargetsFunction() { |
| } |
| |
| DebuggerGetTargetsFunction::~DebuggerGetTargetsFunction() { |
| } |
| |
| bool DebuggerGetTargetsFunction::RunAsync() { |
| content::DevToolsAgentHost::List list = DevToolsAgentHost::GetOrCreateAll(); |
| content::BrowserThread::PostTask( |
| content::BrowserThread::UI, FROM_HERE, |
| base::BindOnce(&DebuggerGetTargetsFunction::SendTargetList, this, list)); |
| return true; |
| } |
| |
| void DebuggerGetTargetsFunction::SendTargetList( |
| const content::DevToolsAgentHost::List& target_list) { |
| std::unique_ptr<base::ListValue> result(new base::ListValue()); |
| for (size_t i = 0; i < target_list.size(); ++i) |
| result->Append(SerializeTarget(target_list[i])); |
| SetResult(std::move(result)); |
| SendResponse(true); |
| } |
| |
| } // namespace extensions |