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"
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
;
82 // Helpers --------------------------------------------------------------------
84 void CopyDebuggee(Debuggee
* dst
, const Debuggee
& src
) {
86 dst
->tab_id
.reset(new int(*src
.tab_id
));
88 dst
->extension_id
.reset(new std::string(*src
.extension_id
));
90 dst
->target_id
.reset(new std::string(*src
.target_id
));
94 // ExtensionDevToolsInfoBarDelegate -------------------------------------------
96 class ExtensionDevToolsInfoBarDelegate
: public ConfirmInfoBarDelegate
{
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
);
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
);
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 {
148 bool ExtensionDevToolsInfoBarDelegate::ShouldExpire(
149 const NavigationDetails
& details
) const {
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() {
170 // InfoBarDismissed() will have closed us already.
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
{
184 GlobalConfirmInfoBar(const base::Closure
& dismissed_callback
,
185 const std::string
& client_name
);
186 ~GlobalConfirmInfoBar() override
;
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
,
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
,
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.
258 infobars_
[infobar_service
] = infobar
;
259 infobar_service
->AddObserver(this);
263 void GlobalConfirmInfoBar::OnInfoBarRemoved(infobars::InfoBar
* infobar
,
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());
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
{
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(); }
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
;
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
;
339 scoped_refptr
<DevToolsAgentHost
> agent_host_
;
340 std::string extension_id_
;
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(
359 DevToolsAgentHost
* agent_host
,
360 const std::string
& extension_id
,
361 const std::string
& extension_name
,
362 const Debuggee
& debuggee
)
364 agent_host_(agent_host
),
365 extension_id_(extension_id
),
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
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)),
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
;
410 void ExtensionDevToolsClientHost::Close() {
411 agent_host_
->DetachClient();
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
;
440 void ExtensionDevToolsClientHost::SendDetachedEvent() {
441 if (!EventRouter::Get(profile_
))
444 scoped_ptr
<base::ListValue
> args(OnDetach::Create(debuggee_
,
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_
)
461 void ExtensionDevToolsClientHost::Observe(
463 const content::NotificationSource
& source
,
464 const content::NotificationDetails
& details
) {
465 DCHECK_EQ(chrome::NOTIFICATION_APP_TERMINATING
, type
);
469 void ExtensionDevToolsClientHost::DispatchProtocolMessage(
470 DevToolsAgentHost
* agent_host
, const std::string
& message
) {
471 DCHECK(agent_host
== agent_host_
.get());
472 if (!EventRouter::Get(profile_
))
475 scoped_ptr
<base::Value
> result
= base::JSONReader::Read(message
);
476 if (!result
->IsType(base::Value::TYPE_DICTIONARY
))
478 base::DictionaryValue
* dictionary
=
479 static_cast<base::DictionaryValue
*>(result
.get());
482 if (!dictionary
->GetInteger("id", &id
)) {
483 std::string method_name
;
484 if (!dictionary
->GetString("method", &method_name
))
487 OnEvent::Params params
;
488 base::DictionaryValue
* params_value
;
489 if (dictionary
->GetDictionary("params", ¶ms_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());
500 DebuggerSendCommandFunction
* function
= pending_requests_
[id
].get();
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
);
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
,
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_
))
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(),
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(),
567 agent_host_
= nullptr;
572 error_
= keys::kInvalidTargetError
;
576 if (!agent_host_
.get()) {
577 FormatErrorMessage(keys::kNoTargetError
);
583 bool DebuggerFunction::InitClientHost() {
584 if (!InitAgentHost())
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
);
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())
623 if (!DevToolsAgentHost::IsSupportedProtocolVersion(
624 params
->required_version
)) {
625 error_
= ErrorUtils::FormatErrorMessage(
626 keys::kProtocolVersionNotSupportedError
,
627 params
->required_version
);
631 if (agent_host_
->IsAttached()) {
632 FormatErrorMessage(keys::kAlreadyAttachedError
);
636 new ExtensionDevToolsClientHost(GetProfile(), agent_host_
.get(),
637 extension()->id(), extension()->name(),
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())
660 client_host_
->Close();
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())
682 client_host_
->SendMessageToBackend(this, params
->method
,
683 params
->command_params
.get());
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_
);
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
);
706 // DebuggerGetTargetsFunction -------------------------------------------------
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());
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
,
762 base::Bind(&DebuggerGetTargetsFunction::SendTargetList
, this, list
));
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());
776 } // namespace extensions