Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / debugger / debugger_api.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Implements the Chrome Extensions Debugger API.
6
7 #include "chrome/browser/extensions/api/debugger/debugger_api.h"
8
9 #include <map>
10 #include <set>
11
12 #include "base/command_line.h"
13 #include "base/json/json_reader.h"
14 #include "base/json/json_writer.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/memory/singleton.h"
17 #include "base/scoped_observer.h"
18 #include "base/stl_util.h"
19 #include "base/strings/string_number_conversions.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "base/values.h"
22 #include "chrome/browser/chrome_notification_types.h"
23 #include "chrome/browser/devtools/devtools_target_impl.h"
24 #include "chrome/browser/extensions/api/debugger/debugger_api_constants.h"
25 #include "chrome/browser/extensions/extension_service.h"
26 #include "chrome/browser/extensions/extension_tab_util.h"
27 #include "chrome/browser/infobars/infobar_service.h"
28 #include "chrome/browser/profiles/profile.h"
29 #include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h"
30 #include "chrome/common/chrome_switches.h"
31 #include "chrome/grit/generated_resources.h"
32 #include "components/infobars/core/confirm_infobar_delegate.h"
33 #include "components/infobars/core/infobar.h"
34 #include "content/public/browser/devtools_agent_host.h"
35 #include "content/public/browser/devtools_http_handler.h"
36 #include "content/public/browser/notification_service.h"
37 #include "content/public/browser/notification_source.h"
38 #include "content/public/browser/render_process_host.h"
39 #include "content/public/browser/render_view_host.h"
40 #include "content/public/browser/render_widget_host.h"
41 #include "content/public/browser/web_contents.h"
42 #include "content/public/common/content_client.h"
43 #include "content/public/common/url_utils.h"
44 #include "extensions/browser/event_router.h"
45 #include "extensions/browser/extension_host.h"
46 #include "extensions/browser/extension_registry.h"
47 #include "extensions/browser/extension_registry_observer.h"
48 #include "extensions/browser/extension_system.h"
49 #include "extensions/common/constants.h"
50 #include "extensions/common/error_utils.h"
51 #include "extensions/common/extension.h"
52 #include "extensions/common/manifest_constants.h"
53 #include "extensions/common/permissions/permissions_data.h"
54 #include "extensions/common/switches.h"
55 #include "ui/base/l10n/l10n_util.h"
56
57 using content::DevToolsAgentHost;
58 using content::DevToolsHttpHandler;
59 using content::RenderProcessHost;
60 using content::RenderViewHost;
61 using content::RenderWidgetHost;
62 using content::WebContents;
63
64 namespace keys = debugger_api_constants;
65 namespace Attach = extensions::api::debugger::Attach;
66 namespace Detach = extensions::api::debugger::Detach;
67 namespace OnDetach = extensions::api::debugger::OnDetach;
68 namespace OnEvent = extensions::api::debugger::OnEvent;
69 namespace SendCommand = extensions::api::debugger::SendCommand;
70
71 namespace extensions {
72 class ExtensionRegistry;
73
74 // ExtensionDevToolsClientHost ------------------------------------------------
75
76 class ExtensionDevToolsClientHost : public content::DevToolsAgentHostClient,
77                                     public content::NotificationObserver,
78                                     public ExtensionRegistryObserver {
79  public:
80   ExtensionDevToolsClientHost(Profile* profile,
81                               DevToolsAgentHost* agent_host,
82                               const std::string& extension_id,
83                               const std::string& extension_name,
84                               const Debuggee& debuggee,
85                               infobars::InfoBar* infobar);
86
87   virtual ~ExtensionDevToolsClientHost();
88
89   const std::string& extension_id() { return extension_id_; }
90   DevToolsAgentHost* agent_host() { return agent_host_.get(); }
91   void Close();
92   void SendMessageToBackend(DebuggerSendCommandFunction* function,
93                             const std::string& method,
94                             SendCommand::Params::CommandParams* command_params);
95
96   // Marks connection as to-be-terminated by the user.
97   void MarkAsDismissed();
98
99   // DevToolsAgentHostClient interface.
100   virtual void AgentHostClosed(
101       DevToolsAgentHost* agent_host,
102       bool replaced_with_another_client) OVERRIDE;
103   virtual void DispatchProtocolMessage(
104       DevToolsAgentHost* agent_host,
105       const std::string& message) OVERRIDE;
106
107  private:
108   void SendDetachedEvent();
109
110   // content::NotificationObserver implementation.
111   virtual void Observe(int type,
112                        const content::NotificationSource& source,
113                        const content::NotificationDetails& details) OVERRIDE;
114
115   // ExtensionRegistryObserver implementation.
116   virtual void OnExtensionUnloaded(
117       content::BrowserContext* browser_context,
118       const Extension* extension,
119       UnloadedExtensionInfo::Reason reason) OVERRIDE;
120
121   Profile* profile_;
122   scoped_refptr<DevToolsAgentHost> agent_host_;
123   std::string extension_id_;
124   Debuggee debuggee_;
125   content::NotificationRegistrar registrar_;
126   int last_request_id_;
127   typedef std::map<int, scoped_refptr<DebuggerSendCommandFunction> >
128       PendingRequests;
129   PendingRequests pending_requests_;
130   infobars::InfoBar* infobar_;
131   OnDetach::Reason detach_reason_;
132
133   // Listen to extension unloaded notification.
134   ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver>
135       extension_registry_observer_;
136
137   DISALLOW_COPY_AND_ASSIGN(ExtensionDevToolsClientHost);
138 };
139
140 // The member function declarations come after the other class declarations, so
141 // they can call members on them.
142
143
144 namespace {
145
146 // Helpers --------------------------------------------------------------------
147
148 void CopyDebuggee(Debuggee* dst, const Debuggee& src) {
149   if (src.tab_id)
150     dst->tab_id.reset(new int(*src.tab_id));
151   if (src.extension_id)
152     dst->extension_id.reset(new std::string(*src.extension_id));
153   if (src.target_id)
154     dst->target_id.reset(new std::string(*src.target_id));
155 }
156
157
158 // ExtensionDevToolsInfoBarDelegate -------------------------------------------
159
160 class ExtensionDevToolsInfoBarDelegate : public ConfirmInfoBarDelegate {
161  public:
162   // Creates an extension dev tools infobar and delegate and adds the infobar to
163   // the InfoBarService associated with |rvh|.  Returns the infobar if it was
164   // successfully added.
165   static infobars::InfoBar* Create(WebContents* web_contents,
166                                    const std::string& client_name);
167
168   void set_client_host(ExtensionDevToolsClientHost* client_host) {
169     client_host_ = client_host;
170   }
171
172  private:
173   explicit ExtensionDevToolsInfoBarDelegate(const std::string& client_name);
174   virtual ~ExtensionDevToolsInfoBarDelegate();
175
176   // ConfirmInfoBarDelegate:
177   virtual void InfoBarDismissed() OVERRIDE;
178   virtual Type GetInfoBarType() const OVERRIDE;
179   virtual bool ShouldExpireInternal(
180       const NavigationDetails& details) const OVERRIDE;
181   virtual base::string16 GetMessageText() const OVERRIDE;
182   virtual int GetButtons() const OVERRIDE;
183   virtual bool Cancel() OVERRIDE;
184
185   std::string client_name_;
186   ExtensionDevToolsClientHost* client_host_;
187
188   DISALLOW_COPY_AND_ASSIGN(ExtensionDevToolsInfoBarDelegate);
189 };
190
191 // static
192 infobars::InfoBar* ExtensionDevToolsInfoBarDelegate::Create(
193     WebContents* web_contents,
194     const std::string& client_name) {
195   if (!web_contents)
196     return NULL;
197
198   InfoBarService* infobar_service =
199       InfoBarService::FromWebContents(web_contents);
200   if (!infobar_service)
201     return NULL;
202
203   return infobar_service->AddInfoBar(ConfirmInfoBarDelegate::CreateInfoBar(
204       scoped_ptr<ConfirmInfoBarDelegate>(
205           new ExtensionDevToolsInfoBarDelegate(client_name))));
206 }
207
208 ExtensionDevToolsInfoBarDelegate::ExtensionDevToolsInfoBarDelegate(
209     const std::string& client_name)
210     : ConfirmInfoBarDelegate(),
211       client_name_(client_name),
212       client_host_(NULL) {
213 }
214
215 ExtensionDevToolsInfoBarDelegate::~ExtensionDevToolsInfoBarDelegate() {
216 }
217
218 void ExtensionDevToolsInfoBarDelegate::InfoBarDismissed() {
219   if (client_host_)
220     client_host_->MarkAsDismissed();
221 }
222
223 infobars::InfoBarDelegate::Type
224 ExtensionDevToolsInfoBarDelegate::GetInfoBarType() const {
225   return WARNING_TYPE;
226 }
227
228 bool ExtensionDevToolsInfoBarDelegate::ShouldExpireInternal(
229     const NavigationDetails& details) const {
230   return false;
231 }
232
233 base::string16 ExtensionDevToolsInfoBarDelegate::GetMessageText() const {
234   return l10n_util::GetStringFUTF16(IDS_DEV_TOOLS_INFOBAR_LABEL,
235                                     base::UTF8ToUTF16(client_name_));
236 }
237
238 int ExtensionDevToolsInfoBarDelegate::GetButtons() const {
239   return BUTTON_CANCEL;
240 }
241
242 bool ExtensionDevToolsInfoBarDelegate::Cancel() {
243   InfoBarDismissed();
244   return true;
245 }
246
247
248 // AttachedClientHosts --------------------------------------------------------
249
250 class AttachedClientHosts {
251  public:
252   AttachedClientHosts();
253   ~AttachedClientHosts();
254
255   // Returns the singleton instance of this class.
256   static AttachedClientHosts* GetInstance();
257
258   void Add(ExtensionDevToolsClientHost* client_host);
259   void Remove(ExtensionDevToolsClientHost* client_host);
260   ExtensionDevToolsClientHost* Lookup(DevToolsAgentHost* agent_host,
261                                       const std::string& extension_id);
262
263  private:
264   typedef std::set<ExtensionDevToolsClientHost*> ClientHosts;
265   ClientHosts client_hosts_;
266
267   DISALLOW_COPY_AND_ASSIGN(AttachedClientHosts);
268 };
269
270 AttachedClientHosts::AttachedClientHosts() {
271 }
272
273 AttachedClientHosts::~AttachedClientHosts() {
274 }
275
276 // static
277 AttachedClientHosts* AttachedClientHosts::GetInstance() {
278   return Singleton<AttachedClientHosts>::get();
279 }
280
281 void AttachedClientHosts::Add(ExtensionDevToolsClientHost* client_host) {
282   client_hosts_.insert(client_host);
283 }
284
285 void AttachedClientHosts::Remove(ExtensionDevToolsClientHost* client_host) {
286   client_hosts_.erase(client_host);
287 }
288
289 ExtensionDevToolsClientHost* AttachedClientHosts::Lookup(
290     DevToolsAgentHost* agent_host,
291     const std::string& extension_id) {
292   for (ClientHosts::iterator it = client_hosts_.begin();
293        it != client_hosts_.end(); ++it) {
294     ExtensionDevToolsClientHost* client_host = *it;
295     if (client_host->agent_host() == agent_host &&
296         client_host->extension_id() == extension_id)
297       return client_host;
298   }
299   return NULL;
300 }
301
302 }  // namespace
303
304
305 // ExtensionDevToolsClientHost ------------------------------------------------
306
307 ExtensionDevToolsClientHost::ExtensionDevToolsClientHost(
308     Profile* profile,
309     DevToolsAgentHost* agent_host,
310     const std::string& extension_id,
311     const std::string& extension_name,
312     const Debuggee& debuggee,
313     infobars::InfoBar* infobar)
314     : profile_(profile),
315       agent_host_(agent_host),
316       extension_id_(extension_id),
317       last_request_id_(0),
318       infobar_(infobar),
319       detach_reason_(OnDetach::REASON_TARGET_CLOSED),
320       extension_registry_observer_(this) {
321   CopyDebuggee(&debuggee_, debuggee);
322
323   AttachedClientHosts::GetInstance()->Add(this);
324
325   // ExtensionRegistryObserver listen extension unloaded and detach debugger
326   // from there.
327   extension_registry_observer_.Add(ExtensionRegistry::Get(profile_));
328
329   // RVH-based agents disconnect from their clients when the app is terminating
330   // but shared worker-based agents do not.
331   // Disconnect explicitly to make sure that |this| observer is not leaked.
332   registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
333                  content::NotificationService::AllSources());
334
335   // Attach to debugger and tell it we are ready.
336   agent_host_->AttachClient(this);
337
338   if (infobar_) {
339     static_cast<ExtensionDevToolsInfoBarDelegate*>(
340         infobar_->delegate())->set_client_host(this);
341     registrar_.Add(
342         this,
343         chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED,
344         content::Source<InfoBarService>(
345             InfoBarService::FromWebContents(agent_host_->GetWebContents())));
346   }
347 }
348
349 ExtensionDevToolsClientHost::~ExtensionDevToolsClientHost() {
350   // Ensure calling RemoveInfoBar() below won't result in Observe() trying to
351   // Close() us.
352   registrar_.RemoveAll();
353
354   if (infobar_) {
355     static_cast<ExtensionDevToolsInfoBarDelegate*>(
356         infobar_->delegate())->set_client_host(NULL);
357     InfoBarService* infobar_service =
358         InfoBarService::FromWebContents(agent_host_->GetWebContents());
359     infobar_service->RemoveInfoBar(infobar_);
360   }
361   AttachedClientHosts::GetInstance()->Remove(this);
362 }
363
364 // DevToolsAgentHostClient implementation.
365 void ExtensionDevToolsClientHost::AgentHostClosed(
366     DevToolsAgentHost* agent_host, bool replaced_with_another_client) {
367   DCHECK(agent_host == agent_host_.get());
368   if (replaced_with_another_client)
369     detach_reason_ = OnDetach::REASON_REPLACED_WITH_DEVTOOLS;
370   SendDetachedEvent();
371   delete this;
372 }
373
374 void ExtensionDevToolsClientHost::Close() {
375   agent_host_->DetachClient();
376   delete this;
377 }
378
379 void ExtensionDevToolsClientHost::SendMessageToBackend(
380     DebuggerSendCommandFunction* function,
381     const std::string& method,
382     SendCommand::Params::CommandParams* command_params) {
383   base::DictionaryValue protocol_request;
384   int request_id = ++last_request_id_;
385   pending_requests_[request_id] = function;
386   protocol_request.SetInteger("id", request_id);
387   protocol_request.SetString("method", method);
388   if (command_params) {
389     protocol_request.Set("params",
390                          command_params->additional_properties.DeepCopy());
391   }
392
393   std::string json_args;
394   base::JSONWriter::Write(&protocol_request, &json_args);
395   agent_host_->DispatchProtocolMessage(json_args);
396 }
397
398 void ExtensionDevToolsClientHost::MarkAsDismissed() {
399   detach_reason_ = OnDetach::REASON_CANCELED_BY_USER;
400 }
401
402 void ExtensionDevToolsClientHost::SendDetachedEvent() {
403   if (!EventRouter::Get(profile_))
404     return;
405
406   scoped_ptr<base::ListValue> args(OnDetach::Create(debuggee_,
407                                                     detach_reason_));
408   scoped_ptr<Event> event(new Event(OnDetach::kEventName, args.Pass()));
409   event->restrict_to_browser_context = profile_;
410   EventRouter::Get(profile_)
411       ->DispatchEventToExtension(extension_id_, event.Pass());
412 }
413
414 void ExtensionDevToolsClientHost::OnExtensionUnloaded(
415     content::BrowserContext* browser_context,
416     const Extension* extension,
417     UnloadedExtensionInfo::Reason reason) {
418   if (extension->id() == extension_id_)
419     Close();
420 }
421
422 void ExtensionDevToolsClientHost::Observe(
423     int type,
424     const content::NotificationSource& source,
425     const content::NotificationDetails& details) {
426   switch (type) {
427     case chrome::NOTIFICATION_APP_TERMINATING:
428       Close();
429       break;
430     case chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED:
431       if (content::Details<infobars::InfoBar::RemovedDetails>(details)->first ==
432           infobar_) {
433         infobar_ = NULL;
434         SendDetachedEvent();
435         Close();
436       }
437       break;
438     default:
439       NOTREACHED();
440   }
441 }
442
443 void ExtensionDevToolsClientHost::DispatchProtocolMessage(
444     DevToolsAgentHost* agent_host, const std::string& message) {
445   DCHECK(agent_host == agent_host_.get());
446   if (!EventRouter::Get(profile_))
447     return;
448
449   scoped_ptr<base::Value> result(base::JSONReader::Read(message));
450   if (!result->IsType(base::Value::TYPE_DICTIONARY))
451     return;
452   base::DictionaryValue* dictionary =
453       static_cast<base::DictionaryValue*>(result.get());
454
455   int id;
456   if (!dictionary->GetInteger("id", &id)) {
457     std::string method_name;
458     if (!dictionary->GetString("method", &method_name))
459       return;
460
461     OnEvent::Params params;
462     base::DictionaryValue* params_value;
463     if (dictionary->GetDictionary("params", &params_value))
464       params.additional_properties.Swap(params_value);
465
466     scoped_ptr<base::ListValue> args(
467         OnEvent::Create(debuggee_, method_name, params));
468     scoped_ptr<Event> event(new Event(OnEvent::kEventName, args.Pass()));
469     event->restrict_to_browser_context = profile_;
470     EventRouter::Get(profile_)
471         ->DispatchEventToExtension(extension_id_, event.Pass());
472   } else {
473     DebuggerSendCommandFunction* function = pending_requests_[id].get();
474     if (!function)
475       return;
476
477     function->SendResponseBody(dictionary);
478     pending_requests_.erase(id);
479   }
480 }
481
482
483 // DebuggerFunction -----------------------------------------------------------
484
485 DebuggerFunction::DebuggerFunction()
486     : client_host_(NULL) {
487 }
488
489 DebuggerFunction::~DebuggerFunction() {
490 }
491
492 void DebuggerFunction::FormatErrorMessage(const std::string& format) {
493   if (debuggee_.tab_id)
494     error_ = ErrorUtils::FormatErrorMessage(
495       format, keys::kTabTargetType, base::IntToString(*debuggee_.tab_id));
496   else if (debuggee_.extension_id)
497     error_ = ErrorUtils::FormatErrorMessage(
498       format, keys::kBackgroundPageTargetType, *debuggee_.extension_id);
499   else
500     error_ = ErrorUtils::FormatErrorMessage(
501       format, keys::kOpaqueTargetType, *debuggee_.target_id);
502 }
503
504 bool DebuggerFunction::InitAgentHost() {
505   if (debuggee_.tab_id) {
506     WebContents* web_contents = NULL;
507     bool result = ExtensionTabUtil::GetTabById(*debuggee_.tab_id,
508                                                GetProfile(),
509                                                include_incognito(),
510                                                NULL,
511                                                NULL,
512                                                &web_contents,
513                                                NULL);
514     if (result && web_contents) {
515       // TODO(rdevlin.cronin) This should definitely be GetLastCommittedURL().
516       GURL url = web_contents->GetVisibleURL();
517       if (PermissionsData::IsRestrictedUrl(url, url, extension(), &error_))
518         return false;
519       agent_host_ = DevToolsAgentHost::GetOrCreateFor(web_contents);
520     }
521   } else if (debuggee_.extension_id) {
522     ExtensionHost* extension_host =
523         ExtensionSystem::Get(GetProfile())
524             ->process_manager()
525             ->GetBackgroundHostForExtension(*debuggee_.extension_id);
526     if (extension_host) {
527       if (PermissionsData::IsRestrictedUrl(extension_host->GetURL(),
528                                            extension_host->GetURL(),
529                                            extension(),
530                                            &error_)) {
531         return false;
532       }
533       agent_host_ =
534           DevToolsAgentHost::GetOrCreateFor(extension_host->host_contents());
535     }
536   } else if (debuggee_.target_id) {
537     agent_host_ = DevToolsAgentHost::GetForId(*debuggee_.target_id);
538   } else {
539     error_ = keys::kInvalidTargetError;
540     return false;
541   }
542
543   if (!agent_host_.get()) {
544     FormatErrorMessage(keys::kNoTargetError);
545     return false;
546   }
547   return true;
548 }
549
550 bool DebuggerFunction::InitClientHost() {
551   if (!InitAgentHost())
552     return false;
553
554   client_host_ = AttachedClientHosts::GetInstance()->Lookup(agent_host_.get(),
555                                                             extension()->id());
556
557   if (!client_host_) {
558     FormatErrorMessage(keys::kNotAttachedError);
559     return false;
560   }
561   return true;
562 }
563
564
565 // DebuggerAttachFunction -----------------------------------------------------
566
567 DebuggerAttachFunction::DebuggerAttachFunction() {
568 }
569
570 DebuggerAttachFunction::~DebuggerAttachFunction() {
571 }
572
573 bool DebuggerAttachFunction::RunAsync() {
574   scoped_ptr<Attach::Params> params(Attach::Params::Create(*args_));
575   EXTENSION_FUNCTION_VALIDATE(params.get());
576
577   CopyDebuggee(&debuggee_, params->target);
578   if (!InitAgentHost())
579     return false;
580
581   if (!DevToolsHttpHandler::IsSupportedProtocolVersion(
582           params->required_version)) {
583     error_ = ErrorUtils::FormatErrorMessage(
584         keys::kProtocolVersionNotSupportedError,
585         params->required_version);
586     return false;
587   }
588
589   if (agent_host_->IsAttached()) {
590     FormatErrorMessage(keys::kAlreadyAttachedError);
591     return false;
592   }
593
594   infobars::InfoBar* infobar = NULL;
595   if (!CommandLine::ForCurrentProcess()->
596        HasSwitch(::switches::kSilentDebuggerExtensionAPI)) {
597     // Do not attach to the target if for any reason the infobar cannot be shown
598     // for this WebContents instance.
599     infobar = ExtensionDevToolsInfoBarDelegate::Create(
600         agent_host_->GetWebContents(), extension()->name());
601     if (!infobar) {
602       error_ = ErrorUtils::FormatErrorMessage(
603           keys::kSilentDebuggingRequired,
604           ::switches::kSilentDebuggerExtensionAPI);
605       return false;
606     }
607   }
608
609   new ExtensionDevToolsClientHost(GetProfile(),
610                                   agent_host_.get(),
611                                   extension()->id(),
612                                   extension()->name(),
613                                   debuggee_,
614                                   infobar);
615   SendResponse(true);
616   return true;
617 }
618
619
620 // DebuggerDetachFunction -----------------------------------------------------
621
622 DebuggerDetachFunction::DebuggerDetachFunction() {
623 }
624
625 DebuggerDetachFunction::~DebuggerDetachFunction() {
626 }
627
628 bool DebuggerDetachFunction::RunAsync() {
629   scoped_ptr<Detach::Params> params(Detach::Params::Create(*args_));
630   EXTENSION_FUNCTION_VALIDATE(params.get());
631
632   CopyDebuggee(&debuggee_, params->target);
633   if (!InitClientHost())
634     return false;
635
636   client_host_->Close();
637   SendResponse(true);
638   return true;
639 }
640
641
642 // DebuggerSendCommandFunction ------------------------------------------------
643
644 DebuggerSendCommandFunction::DebuggerSendCommandFunction() {
645 }
646
647 DebuggerSendCommandFunction::~DebuggerSendCommandFunction() {
648 }
649
650 bool DebuggerSendCommandFunction::RunAsync() {
651   scoped_ptr<SendCommand::Params> params(SendCommand::Params::Create(*args_));
652   EXTENSION_FUNCTION_VALIDATE(params.get());
653
654   CopyDebuggee(&debuggee_, params->target);
655   if (!InitClientHost())
656     return false;
657
658   client_host_->SendMessageToBackend(this, params->method,
659       params->command_params.get());
660   return true;
661 }
662
663 void DebuggerSendCommandFunction::SendResponseBody(
664     base::DictionaryValue* response) {
665   base::Value* error_body;
666   if (response->Get("error", &error_body)) {
667     base::JSONWriter::Write(error_body, &error_);
668     SendResponse(false);
669     return;
670   }
671
672   base::DictionaryValue* result_body;
673   SendCommand::Results::Result result;
674   if (response->GetDictionary("result", &result_body))
675     result.additional_properties.Swap(result_body);
676
677   results_ = SendCommand::Results::Create(result);
678   SendResponse(true);
679 }
680
681
682 // DebuggerGetTargetsFunction -------------------------------------------------
683
684 namespace {
685
686 const char kTargetIdField[] = "id";
687 const char kTargetTypeField[] = "type";
688 const char kTargetTitleField[] = "title";
689 const char kTargetAttachedField[] = "attached";
690 const char kTargetUrlField[] = "url";
691 const char kTargetFaviconUrlField[] = "faviconUrl";
692 const char kTargetTypePage[] = "page";
693 const char kTargetTypeBackgroundPage[] = "background_page";
694 const char kTargetTypeWorker[] = "worker";
695 const char kTargetTypeOther[] = "other";
696 const char kTargetTabIdField[] = "tabId";
697 const char kTargetExtensionIdField[] = "extensionId";
698
699 base::Value* SerializeTarget(const DevToolsTargetImpl& target) {
700   base::DictionaryValue* dictionary = new base::DictionaryValue();
701
702   dictionary->SetString(kTargetIdField, target.GetId());
703   dictionary->SetString(kTargetTitleField, target.GetTitle());
704   dictionary->SetBoolean(kTargetAttachedField, target.IsAttached());
705   dictionary->SetString(kTargetUrlField, target.GetURL().spec());
706
707   std::string type = target.GetType();
708   if (type == kTargetTypePage) {
709     dictionary->SetInteger(kTargetTabIdField, target.GetTabId());
710   } else if (type == kTargetTypeBackgroundPage) {
711     dictionary->SetString(kTargetExtensionIdField, target.GetExtensionId());
712   } else if (type != kTargetTypeWorker) {
713     // DevToolsTargetImpl may support more types than the debugger API.
714     type = kTargetTypeOther;
715   }
716   dictionary->SetString(kTargetTypeField, type);
717
718   GURL favicon_url = target.GetFaviconURL();
719   if (favicon_url.is_valid())
720     dictionary->SetString(kTargetFaviconUrlField, favicon_url.spec());
721
722   return dictionary;
723 }
724
725 }  // namespace
726
727 DebuggerGetTargetsFunction::DebuggerGetTargetsFunction() {
728 }
729
730 DebuggerGetTargetsFunction::~DebuggerGetTargetsFunction() {
731 }
732
733 bool DebuggerGetTargetsFunction::RunAsync() {
734   DevToolsTargetImpl::EnumerateAllTargets(
735       base::Bind(&DebuggerGetTargetsFunction::SendTargetList, this));
736   return true;
737 }
738
739 void DebuggerGetTargetsFunction::SendTargetList(
740     const std::vector<DevToolsTargetImpl*>& target_list) {
741   scoped_ptr<base::ListValue> result(new base::ListValue());
742   for (size_t i = 0; i < target_list.size(); ++i)
743     result->Append(SerializeTarget(*target_list[i]));
744   STLDeleteContainerPointers(target_list.begin(), target_list.end());
745   SetResult(result.release());
746   SendResponse(true);
747 }
748
749 }  // namespace extensions