Add new certificateProvider extension API.
[chromium-blink-merge.git] / chrome / browser / extensions / api / debugger / debugger_api.cc
blobec0d6b96476024ad0aa70691dc63fd7a85aaaefc
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.
5 // Implements the Chrome Extensions Debugger API.
7 #include "chrome/browser/extensions/api/debugger/debugger_api.h"
9 #include <map>
10 #include <set>
12 #include "base/callback_helpers.h"
13 #include "base/command_line.h"
14 #include "base/json/json_reader.h"
15 #include "base/json/json_writer.h"
16 #include "base/lazy_instance.h"
17 #include "base/memory/scoped_ptr.h"
18 #include "base/memory/singleton.h"
19 #include "base/scoped_observer.h"
20 #include "base/stl_util.h"
21 #include "base/strings/string_number_conversions.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/values.h"
24 #include "chrome/browser/chrome_notification_types.h"
25 #include "chrome/browser/devtools/devtools_target_impl.h"
26 #include "chrome/browser/extensions/api/debugger/debugger_api_constants.h"
27 #include "chrome/browser/extensions/extension_service.h"
28 #include "chrome/browser/extensions/extension_tab_util.h"
29 #include "chrome/browser/infobars/infobar_service.h"
30 #include "chrome/browser/profiles/profile.h"
31 #include "chrome/browser/ui/browser.h"
32 #include "chrome/browser/ui/browser_list.h"
33 #include "chrome/browser/ui/browser_list_observer.h"
34 #include "chrome/browser/ui/tabs/tab_strip_model.h"
35 #include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
36 #include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h"
37 #include "chrome/common/chrome_switches.h"
38 #include "chrome/grit/generated_resources.h"
39 #include "components/infobars/core/confirm_infobar_delegate.h"
40 #include "components/infobars/core/infobar.h"
41 #include "content/public/browser/browser_thread.h"
42 #include "content/public/browser/devtools_agent_host.h"
43 #include "content/public/browser/notification_service.h"
44 #include "content/public/browser/notification_source.h"
45 #include "content/public/browser/render_process_host.h"
46 #include "content/public/browser/render_view_host.h"
47 #include "content/public/browser/render_widget_host.h"
48 #include "content/public/browser/web_contents.h"
49 #include "content/public/common/content_client.h"
50 #include "content/public/common/url_utils.h"
51 #include "extensions/browser/event_router.h"
52 #include "extensions/browser/extension_host.h"
53 #include "extensions/browser/extension_registry.h"
54 #include "extensions/browser/extension_registry_observer.h"
55 #include "extensions/common/constants.h"
56 #include "extensions/common/error_utils.h"
57 #include "extensions/common/extension.h"
58 #include "extensions/common/manifest_constants.h"
59 #include "extensions/common/permissions/permissions_data.h"
60 #include "extensions/common/switches.h"
61 #include "ui/base/l10n/l10n_util.h"
63 using content::DevToolsAgentHost;
64 using content::RenderProcessHost;
65 using content::RenderViewHost;
66 using content::RenderWidgetHost;
67 using content::WebContents;
69 namespace keys = debugger_api_constants;
70 namespace Attach = extensions::api::debugger::Attach;
71 namespace Detach = extensions::api::debugger::Detach;
72 namespace OnDetach = extensions::api::debugger::OnDetach;
73 namespace OnEvent = extensions::api::debugger::OnEvent;
74 namespace SendCommand = extensions::api::debugger::SendCommand;
76 namespace extensions {
77 class ExtensionRegistry;
78 class ExtensionDevToolsClientHost;
80 namespace {
82 // Helpers --------------------------------------------------------------------
84 void CopyDebuggee(Debuggee* dst, const Debuggee& src) {
85 if (src.tab_id)
86 dst->tab_id.reset(new int(*src.tab_id));
87 if (src.extension_id)
88 dst->extension_id.reset(new std::string(*src.extension_id));
89 if (src.target_id)
90 dst->target_id.reset(new std::string(*src.target_id));
94 // ExtensionDevToolsInfoBarDelegate -------------------------------------------
96 class ExtensionDevToolsInfoBarDelegate : public ConfirmInfoBarDelegate {
97 public:
98 // Creates an extension dev tools infobar and delegate and adds the infobar to
99 // the |infobar_service|. Returns the infobar if it was successfully added.
100 static infobars::InfoBar* Create(InfoBarService* infobar_service,
101 const base::Closure& dismissed_callback,
102 const std::string& client_name);
104 private:
105 ExtensionDevToolsInfoBarDelegate(const base::Closure& dismissed_callback,
106 const std::string& client_name);
107 ~ExtensionDevToolsInfoBarDelegate() override;
109 // ConfirmInfoBarDelegate:
110 Type GetInfoBarType() const override;
111 bool ShouldExpire(const NavigationDetails& details) const override;
112 void InfoBarDismissed() override;
113 base::string16 GetMessageText() const override;
114 int GetButtons() const override;
115 bool Cancel() override;
117 std::string client_name_;
118 base::Closure dismissed_callback_;
120 DISALLOW_COPY_AND_ASSIGN(ExtensionDevToolsInfoBarDelegate);
123 // static
124 infobars::InfoBar* ExtensionDevToolsInfoBarDelegate::Create(
125 InfoBarService* infobar_service,
126 const base::Closure& dismissed_callback,
127 const std::string& client_name) {
128 return infobar_service->AddInfoBar(infobar_service->CreateConfirmInfoBar(
129 scoped_ptr<ConfirmInfoBarDelegate>(new ExtensionDevToolsInfoBarDelegate(
130 dismissed_callback, client_name))));
133 ExtensionDevToolsInfoBarDelegate::ExtensionDevToolsInfoBarDelegate(
134 const base::Closure& dismissed_callback,
135 const std::string& client_name)
136 : ConfirmInfoBarDelegate(),
137 client_name_(client_name),
138 dismissed_callback_(dismissed_callback) {}
140 ExtensionDevToolsInfoBarDelegate::~ExtensionDevToolsInfoBarDelegate() {
143 infobars::InfoBarDelegate::Type
144 ExtensionDevToolsInfoBarDelegate::GetInfoBarType() const {
145 return WARNING_TYPE;
148 bool ExtensionDevToolsInfoBarDelegate::ShouldExpire(
149 const NavigationDetails& details) const {
150 return false;
153 void ExtensionDevToolsInfoBarDelegate::InfoBarDismissed() {
154 DCHECK(!dismissed_callback_.is_null());
155 // Use ResetAndReturn() since running the callback may delete |this|.
156 base::ResetAndReturn(&dismissed_callback_).Run();
159 base::string16 ExtensionDevToolsInfoBarDelegate::GetMessageText() const {
160 return l10n_util::GetStringFUTF16(IDS_DEV_TOOLS_INFOBAR_LABEL,
161 base::UTF8ToUTF16(client_name_));
164 int ExtensionDevToolsInfoBarDelegate::GetButtons() const {
165 return BUTTON_CANCEL;
168 bool ExtensionDevToolsInfoBarDelegate::Cancel() {
169 InfoBarDismissed();
170 // InfoBarDismissed() will have closed us already.
171 return false;
174 // GlobalConfirmInfoBar -------------------------------------------------------
176 // GlobalConfirmInfobar is shown for every tab in every browser until it
177 // is dismissed or the object itself is destroyed.
178 // It listens to all tabs in all browsers and adds/removes confirm infobar
179 // to each of the tabs.
180 class GlobalConfirmInfoBar : public chrome::BrowserListObserver,
181 public TabStripModelObserver,
182 public infobars::InfoBarManager::Observer {
183 public:
184 GlobalConfirmInfoBar(const base::Closure& dismissed_callback,
185 const std::string& client_name);
186 ~GlobalConfirmInfoBar() override;
188 private:
189 using InfoBarMap = std::map<InfoBarService*, infobars::InfoBar*>;
191 // chrome::BrowserListObserver:
192 void OnBrowserAdded(Browser* browser) override;
193 void OnBrowserRemoved(Browser* browser) override;
195 // TabStripModelObserver:
196 void TabInsertedAt(content::WebContents* web_contents,
197 int index,
198 bool foreground) override;
200 // infobars::InfoBarManager::Observer:
201 void OnInfoBarRemoved(infobars::InfoBar* infobar, bool animate) override;
202 void OnManagerShuttingDown(infobars::InfoBarManager* manager) override;
203 // We don't override OnInfoBarReplaces, as extension debugger api infobar
204 // should not be involved in replacements.
206 base::Closure dismissed_callback_;
207 std::string client_name_;
208 InfoBarMap infobars_;
210 DISALLOW_COPY_AND_ASSIGN(GlobalConfirmInfoBar);
213 GlobalConfirmInfoBar::GlobalConfirmInfoBar(
214 const base::Closure& dismissed_callback,
215 const std::string& client_name)
216 : dismissed_callback_(dismissed_callback), client_name_(client_name) {
217 BrowserList::AddObserver(this);
218 for (Browser* browser : *BrowserList::GetInstance(chrome::GetActiveDesktop()))
219 OnBrowserAdded(browser);
222 GlobalConfirmInfoBar::~GlobalConfirmInfoBar() {
223 while (!infobars_.empty()) {
224 InfoBarMap::iterator it = infobars_.begin();
225 it->first->RemoveInfoBar(it->second);
228 for (Browser* browser : *BrowserList::GetInstance(chrome::GetActiveDesktop()))
229 OnBrowserRemoved(browser);
230 BrowserList::RemoveObserver(this);
233 void GlobalConfirmInfoBar::OnBrowserAdded(Browser* browser) {
234 TabStripModel* tab_strip_model = browser->tab_strip_model();
235 tab_strip_model->AddObserver(this);
237 for (int index = 0; index < tab_strip_model->count(); ++index)
238 TabInsertedAt(tab_strip_model->GetWebContentsAt(index), index, false);
241 void GlobalConfirmInfoBar::OnBrowserRemoved(Browser* browser) {
242 browser->tab_strip_model()->RemoveObserver(this);
245 void GlobalConfirmInfoBar::TabInsertedAt(content::WebContents* web_contents,
246 int index,
247 bool foreground) {
248 InfoBarService* infobar_service =
249 InfoBarService::FromWebContents(web_contents);
250 // WebContents from the tab strip must have the infobar service.
251 DCHECK(infobar_service);
253 infobars::InfoBar* infobar = ExtensionDevToolsInfoBarDelegate::Create(
254 infobar_service, dismissed_callback_, client_name_);
255 // Infobar with the same delegate won't be added again, so it's safe
256 // to not listen to TabReplacedAt.
257 if (infobar) {
258 infobars_[infobar_service] = infobar;
259 infobar_service->AddObserver(this);
263 void GlobalConfirmInfoBar::OnInfoBarRemoved(infobars::InfoBar* infobar,
264 bool animate) {
265 // Generally, our infobars should not be removed externally and we wouldn't
266 // need OnInfoBarRemoved. But during browser shutdown all infobars are removed
267 // before this class gets a chance to remove infobars itself.
268 InfoBarMap::iterator it =
269 std::find_if(infobars_.begin(), infobars_.end(),
270 [&infobar](const InfoBarMap::value_type& value) {
271 return value.second == infobar;
273 if (it != infobars_.end())
274 OnManagerShuttingDown(it->first);
277 void GlobalConfirmInfoBar::OnManagerShuttingDown(
278 infobars::InfoBarManager* manager) {
279 InfoBarService* infobar_service = static_cast<InfoBarService*>(manager);
280 infobar_service->RemoveObserver(this);
281 InfoBarMap::iterator it = infobars_.find(infobar_service);
282 DCHECK(it != infobars_.end());
283 infobars_.erase(it);
286 } // namespace
288 // ExtensionDevToolsClientHost ------------------------------------------------
290 using AttachedClientHosts = std::set<ExtensionDevToolsClientHost*>;
291 base::LazyInstance<AttachedClientHosts>::Leaky g_attached_client_hosts =
292 LAZY_INSTANCE_INITIALIZER;
294 class ExtensionDevToolsClientHost : public content::DevToolsAgentHostClient,
295 public content::NotificationObserver,
296 public ExtensionRegistryObserver {
297 public:
298 ExtensionDevToolsClientHost(Profile* profile,
299 DevToolsAgentHost* agent_host,
300 const std::string& extension_id,
301 const std::string& extension_name,
302 const Debuggee& debuggee);
304 ~ExtensionDevToolsClientHost() override;
306 const std::string& extension_id() { return extension_id_; }
307 DevToolsAgentHost* agent_host() { return agent_host_.get(); }
308 void Close();
309 void SendMessageToBackend(DebuggerSendCommandFunction* function,
310 const std::string& method,
311 SendCommand::Params::CommandParams* command_params);
313 // Closes connection as terminated by the user.
314 void InfoBarDismissed();
316 // DevToolsAgentHostClient interface.
317 void AgentHostClosed(DevToolsAgentHost* agent_host,
318 bool replaced_with_another_client) override;
319 void DispatchProtocolMessage(DevToolsAgentHost* agent_host,
320 const std::string& message) override;
322 private:
323 using PendingRequests =
324 std::map<int, scoped_refptr<DebuggerSendCommandFunction>>;
326 void SendDetachedEvent();
328 // content::NotificationObserver implementation.
329 void Observe(int type,
330 const content::NotificationSource& source,
331 const content::NotificationDetails& details) override;
333 // ExtensionRegistryObserver implementation.
334 void OnExtensionUnloaded(content::BrowserContext* browser_context,
335 const Extension* extension,
336 UnloadedExtensionInfo::Reason reason) override;
338 Profile* profile_;
339 scoped_refptr<DevToolsAgentHost> agent_host_;
340 std::string extension_id_;
341 Debuggee debuggee_;
342 content::NotificationRegistrar registrar_;
343 int last_request_id_;
344 PendingRequests pending_requests_;
345 scoped_ptr<GlobalConfirmInfoBar> infobar_;
346 api::debugger::DetachReason detach_reason_;
348 // Listen to extension unloaded notification.
349 ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver>
350 extension_registry_observer_;
352 DISALLOW_COPY_AND_ASSIGN(ExtensionDevToolsClientHost);
355 // ExtensionDevToolsClientHost ------------------------------------------------
357 ExtensionDevToolsClientHost::ExtensionDevToolsClientHost(
358 Profile* profile,
359 DevToolsAgentHost* agent_host,
360 const std::string& extension_id,
361 const std::string& extension_name,
362 const Debuggee& debuggee)
363 : profile_(profile),
364 agent_host_(agent_host),
365 extension_id_(extension_id),
366 last_request_id_(0),
367 detach_reason_(api::debugger::DETACH_REASON_TARGET_CLOSED),
368 extension_registry_observer_(this) {
369 CopyDebuggee(&debuggee_, debuggee);
371 g_attached_client_hosts.Get().insert(this);
373 // ExtensionRegistryObserver listen extension unloaded and detach debugger
374 // from there.
375 extension_registry_observer_.Add(ExtensionRegistry::Get(profile_));
377 // RVH-based agents disconnect from their clients when the app is terminating
378 // but shared worker-based agents do not.
379 // Disconnect explicitly to make sure that |this| observer is not leaked.
380 registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
381 content::NotificationService::AllSources());
383 // Attach to debugger and tell it we are ready.
384 agent_host_->AttachClient(this);
386 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
387 ::switches::kSilentDebuggerExtensionAPI)) {
388 // This class owns the |infobar_|, so it's safe to pass Unretained(this).
389 infobar_.reset(new GlobalConfirmInfoBar(
390 base::Bind(&ExtensionDevToolsClientHost::InfoBarDismissed,
391 base::Unretained(this)),
392 extension_name));
396 ExtensionDevToolsClientHost::~ExtensionDevToolsClientHost() {
397 g_attached_client_hosts.Get().erase(this);
400 // DevToolsAgentHostClient implementation.
401 void ExtensionDevToolsClientHost::AgentHostClosed(
402 DevToolsAgentHost* agent_host, bool replaced_with_another_client) {
403 DCHECK(agent_host == agent_host_.get());
404 if (replaced_with_another_client)
405 detach_reason_ = api::debugger::DETACH_REASON_REPLACED_WITH_DEVTOOLS;
406 SendDetachedEvent();
407 delete this;
410 void ExtensionDevToolsClientHost::Close() {
411 agent_host_->DetachClient();
412 delete this;
415 void ExtensionDevToolsClientHost::SendMessageToBackend(
416 DebuggerSendCommandFunction* function,
417 const std::string& method,
418 SendCommand::Params::CommandParams* command_params) {
419 base::DictionaryValue protocol_request;
420 int request_id = ++last_request_id_;
421 pending_requests_[request_id] = function;
422 protocol_request.SetInteger("id", request_id);
423 protocol_request.SetString("method", method);
424 if (command_params) {
425 protocol_request.Set("params",
426 command_params->additional_properties.DeepCopy());
429 std::string json_args;
430 base::JSONWriter::Write(protocol_request, &json_args);
431 agent_host_->DispatchProtocolMessage(json_args);
434 void ExtensionDevToolsClientHost::InfoBarDismissed() {
435 detach_reason_ = api::debugger::DETACH_REASON_CANCELED_BY_USER;
436 SendDetachedEvent();
437 Close();
440 void ExtensionDevToolsClientHost::SendDetachedEvent() {
441 if (!EventRouter::Get(profile_))
442 return;
444 scoped_ptr<base::ListValue> args(OnDetach::Create(debuggee_,
445 detach_reason_));
446 scoped_ptr<Event> event(
447 new Event(events::DEBUGGER_ON_DETACH, OnDetach::kEventName, args.Pass()));
448 event->restrict_to_browser_context = profile_;
449 EventRouter::Get(profile_)
450 ->DispatchEventToExtension(extension_id_, event.Pass());
453 void ExtensionDevToolsClientHost::OnExtensionUnloaded(
454 content::BrowserContext* browser_context,
455 const Extension* extension,
456 UnloadedExtensionInfo::Reason reason) {
457 if (extension->id() == extension_id_)
458 Close();
461 void ExtensionDevToolsClientHost::Observe(
462 int type,
463 const content::NotificationSource& source,
464 const content::NotificationDetails& details) {
465 DCHECK_EQ(chrome::NOTIFICATION_APP_TERMINATING, type);
466 Close();
469 void ExtensionDevToolsClientHost::DispatchProtocolMessage(
470 DevToolsAgentHost* agent_host, const std::string& message) {
471 DCHECK(agent_host == agent_host_.get());
472 if (!EventRouter::Get(profile_))
473 return;
475 scoped_ptr<base::Value> result = base::JSONReader::Read(message);
476 if (!result->IsType(base::Value::TYPE_DICTIONARY))
477 return;
478 base::DictionaryValue* dictionary =
479 static_cast<base::DictionaryValue*>(result.get());
481 int id;
482 if (!dictionary->GetInteger("id", &id)) {
483 std::string method_name;
484 if (!dictionary->GetString("method", &method_name))
485 return;
487 OnEvent::Params params;
488 base::DictionaryValue* params_value;
489 if (dictionary->GetDictionary("params", &params_value))
490 params.additional_properties.Swap(params_value);
492 scoped_ptr<base::ListValue> args(
493 OnEvent::Create(debuggee_, method_name, params));
494 scoped_ptr<Event> event(
495 new Event(events::DEBUGGER_ON_EVENT, OnEvent::kEventName, args.Pass()));
496 event->restrict_to_browser_context = profile_;
497 EventRouter::Get(profile_)
498 ->DispatchEventToExtension(extension_id_, event.Pass());
499 } else {
500 DebuggerSendCommandFunction* function = pending_requests_[id].get();
501 if (!function)
502 return;
504 function->SendResponseBody(dictionary);
505 pending_requests_.erase(id);
510 // DebuggerFunction -----------------------------------------------------------
512 DebuggerFunction::DebuggerFunction()
513 : client_host_(NULL) {
516 DebuggerFunction::~DebuggerFunction() {
519 void DebuggerFunction::FormatErrorMessage(const std::string& format) {
520 if (debuggee_.tab_id)
521 error_ = ErrorUtils::FormatErrorMessage(
522 format, keys::kTabTargetType, base::IntToString(*debuggee_.tab_id));
523 else if (debuggee_.extension_id)
524 error_ = ErrorUtils::FormatErrorMessage(
525 format, keys::kBackgroundPageTargetType, *debuggee_.extension_id);
526 else
527 error_ = ErrorUtils::FormatErrorMessage(
528 format, keys::kOpaqueTargetType, *debuggee_.target_id);
531 bool DebuggerFunction::InitAgentHost() {
532 if (debuggee_.tab_id) {
533 WebContents* web_contents = NULL;
534 bool result = ExtensionTabUtil::GetTabById(*debuggee_.tab_id,
535 GetProfile(),
536 include_incognito(),
537 NULL,
538 NULL,
539 &web_contents,
540 NULL);
541 if (result && web_contents) {
542 // TODO(rdevlin.cronin) This should definitely be GetLastCommittedURL().
543 GURL url = web_contents->GetVisibleURL();
544 if (PermissionsData::IsRestrictedUrl(url, extension(), &error_))
545 return false;
546 agent_host_ = DevToolsAgentHost::GetOrCreateFor(web_contents);
548 } else if (debuggee_.extension_id) {
549 ExtensionHost* extension_host =
550 ProcessManager::Get(GetProfile())
551 ->GetBackgroundHostForExtension(*debuggee_.extension_id);
552 if (extension_host) {
553 if (PermissionsData::IsRestrictedUrl(extension_host->GetURL(),
554 extension(),
555 &error_)) {
556 return false;
558 agent_host_ =
559 DevToolsAgentHost::GetOrCreateFor(extension_host->host_contents());
561 } else if (debuggee_.target_id) {
562 agent_host_ = DevToolsAgentHost::GetForId(*debuggee_.target_id);
563 if (agent_host_.get()) {
564 if (PermissionsData::IsRestrictedUrl(agent_host_->GetURL(),
565 extension(),
566 &error_)) {
567 agent_host_ = nullptr;
568 return false;
571 } else {
572 error_ = keys::kInvalidTargetError;
573 return false;
576 if (!agent_host_.get()) {
577 FormatErrorMessage(keys::kNoTargetError);
578 return false;
580 return true;
583 bool DebuggerFunction::InitClientHost() {
584 if (!InitAgentHost())
585 return false;
587 const std::string& extension_id = extension()->id();
588 DevToolsAgentHost* agent_host = agent_host_.get();
589 AttachedClientHosts& hosts = g_attached_client_hosts.Get();
590 AttachedClientHosts::iterator it = std::find_if(
591 hosts.begin(), hosts.end(),
592 [&agent_host, &extension_id](ExtensionDevToolsClientHost* client_host) {
593 return client_host->agent_host() == agent_host &&
594 client_host->extension_id() == extension_id;
597 if (it == hosts.end()) {
598 FormatErrorMessage(keys::kNotAttachedError);
599 return false;
602 client_host_ = *it;
603 return true;
607 // DebuggerAttachFunction -----------------------------------------------------
609 DebuggerAttachFunction::DebuggerAttachFunction() {
612 DebuggerAttachFunction::~DebuggerAttachFunction() {
615 bool DebuggerAttachFunction::RunAsync() {
616 scoped_ptr<Attach::Params> params(Attach::Params::Create(*args_));
617 EXTENSION_FUNCTION_VALIDATE(params.get());
619 CopyDebuggee(&debuggee_, params->target);
620 if (!InitAgentHost())
621 return false;
623 if (!DevToolsAgentHost::IsSupportedProtocolVersion(
624 params->required_version)) {
625 error_ = ErrorUtils::FormatErrorMessage(
626 keys::kProtocolVersionNotSupportedError,
627 params->required_version);
628 return false;
631 if (agent_host_->IsAttached()) {
632 FormatErrorMessage(keys::kAlreadyAttachedError);
633 return false;
636 new ExtensionDevToolsClientHost(GetProfile(), agent_host_.get(),
637 extension()->id(), extension()->name(),
638 debuggee_);
639 SendResponse(true);
640 return true;
644 // DebuggerDetachFunction -----------------------------------------------------
646 DebuggerDetachFunction::DebuggerDetachFunction() {
649 DebuggerDetachFunction::~DebuggerDetachFunction() {
652 bool DebuggerDetachFunction::RunAsync() {
653 scoped_ptr<Detach::Params> params(Detach::Params::Create(*args_));
654 EXTENSION_FUNCTION_VALIDATE(params.get());
656 CopyDebuggee(&debuggee_, params->target);
657 if (!InitClientHost())
658 return false;
660 client_host_->Close();
661 SendResponse(true);
662 return true;
666 // DebuggerSendCommandFunction ------------------------------------------------
668 DebuggerSendCommandFunction::DebuggerSendCommandFunction() {
671 DebuggerSendCommandFunction::~DebuggerSendCommandFunction() {
674 bool DebuggerSendCommandFunction::RunAsync() {
675 scoped_ptr<SendCommand::Params> params(SendCommand::Params::Create(*args_));
676 EXTENSION_FUNCTION_VALIDATE(params.get());
678 CopyDebuggee(&debuggee_, params->target);
679 if (!InitClientHost())
680 return false;
682 client_host_->SendMessageToBackend(this, params->method,
683 params->command_params.get());
684 return true;
687 void DebuggerSendCommandFunction::SendResponseBody(
688 base::DictionaryValue* response) {
689 base::Value* error_body;
690 if (response->Get("error", &error_body)) {
691 base::JSONWriter::Write(*error_body, &error_);
692 SendResponse(false);
693 return;
696 base::DictionaryValue* result_body;
697 SendCommand::Results::Result result;
698 if (response->GetDictionary("result", &result_body))
699 result.additional_properties.Swap(result_body);
701 results_ = SendCommand::Results::Create(result);
702 SendResponse(true);
706 // DebuggerGetTargetsFunction -------------------------------------------------
708 namespace {
710 const char kTargetIdField[] = "id";
711 const char kTargetTypeField[] = "type";
712 const char kTargetTitleField[] = "title";
713 const char kTargetAttachedField[] = "attached";
714 const char kTargetUrlField[] = "url";
715 const char kTargetFaviconUrlField[] = "faviconUrl";
716 const char kTargetTypePage[] = "page";
717 const char kTargetTypeBackgroundPage[] = "background_page";
718 const char kTargetTypeWorker[] = "worker";
719 const char kTargetTypeOther[] = "other";
720 const char kTargetTabIdField[] = "tabId";
721 const char kTargetExtensionIdField[] = "extensionId";
723 base::Value* SerializeTarget(const DevToolsTargetImpl& target) {
724 base::DictionaryValue* dictionary = new base::DictionaryValue();
726 dictionary->SetString(kTargetIdField, target.GetId());
727 dictionary->SetString(kTargetTitleField, target.GetTitle());
728 dictionary->SetBoolean(kTargetAttachedField, target.IsAttached());
729 dictionary->SetString(kTargetUrlField, target.GetURL().spec());
731 std::string type = target.GetType();
732 if (type == kTargetTypePage) {
733 dictionary->SetInteger(kTargetTabIdField, target.GetTabId());
734 } else if (type == kTargetTypeBackgroundPage) {
735 dictionary->SetString(kTargetExtensionIdField, target.GetExtensionId());
736 } else if (type != kTargetTypeWorker) {
737 // DevToolsTargetImpl may support more types than the debugger API.
738 type = kTargetTypeOther;
740 dictionary->SetString(kTargetTypeField, type);
742 GURL favicon_url = target.GetFaviconURL();
743 if (favicon_url.is_valid())
744 dictionary->SetString(kTargetFaviconUrlField, favicon_url.spec());
746 return dictionary;
749 } // namespace
751 DebuggerGetTargetsFunction::DebuggerGetTargetsFunction() {
754 DebuggerGetTargetsFunction::~DebuggerGetTargetsFunction() {
757 bool DebuggerGetTargetsFunction::RunAsync() {
758 std::vector<DevToolsTargetImpl*> list = DevToolsTargetImpl::EnumerateAll();
759 content::BrowserThread::PostTask(
760 content::BrowserThread::UI,
761 FROM_HERE,
762 base::Bind(&DebuggerGetTargetsFunction::SendTargetList, this, list));
763 return true;
766 void DebuggerGetTargetsFunction::SendTargetList(
767 const std::vector<DevToolsTargetImpl*>& target_list) {
768 scoped_ptr<base::ListValue> result(new base::ListValue());
769 for (size_t i = 0; i < target_list.size(); ++i)
770 result->Append(SerializeTarget(*target_list[i]));
771 STLDeleteContainerPointers(target_list.begin(), target_list.end());
772 SetResult(result.release());
773 SendResponse(true);
776 } // namespace extensions