Upstream version 7.36.149.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/confirm_infobar_delegate.h"
28 #include "chrome/browser/infobars/infobar_service.h"
29 #include "chrome/browser/profiles/profile.h"
30 #include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h"
31 #include "chrome/common/chrome_switches.h"
32 #include "components/infobars/core/infobar.h"
33 #include "content/public/browser/devtools_agent_host.h"
34 #include "content/public/browser/devtools_client_host.h"
35 #include "content/public/browser/devtools_http_handler.h"
36 #include "content/public/browser/devtools_manager.h"
37 #include "content/public/browser/notification_service.h"
38 #include "content/public/browser/notification_source.h"
39 #include "content/public/browser/render_process_host.h"
40 #include "content/public/browser/render_view_host.h"
41 #include "content/public/browser/render_widget_host.h"
42 #include "content/public/browser/web_contents.h"
43 #include "content/public/common/content_client.h"
44 #include "content/public/common/url_utils.h"
45 #include "extensions/browser/event_router.h"
46 #include "extensions/browser/extension_host.h"
47 #include "extensions/browser/extension_registry.h"
48 #include "extensions/browser/extension_registry_observer.h"
49 #include "extensions/browser/extension_system.h"
50 #include "extensions/common/error_utils.h"
51 #include "extensions/common/extension.h"
52 #include "grit/generated_resources.h"
53 #include "ui/base/l10n/l10n_util.h"
54
55 using content::DevToolsAgentHost;
56 using content::DevToolsClientHost;
57 using content::DevToolsHttpHandler;
58 using content::DevToolsManager;
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 DevToolsClientHost,
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   void Close();
91   void SendMessageToBackend(DebuggerSendCommandFunction* function,
92                             const std::string& method,
93                             SendCommand::Params::CommandParams* command_params);
94
95   // Marks connection as to-be-terminated by the user.
96   void MarkAsDismissed();
97
98   // DevToolsClientHost interface
99   virtual void InspectedContentsClosing() OVERRIDE;
100   virtual void DispatchOnInspectorFrontend(const std::string& message) OVERRIDE;
101   virtual void ReplacedWithAnotherClient() OVERRIDE;
102
103  private:
104   void SendDetachedEvent();
105
106   // content::NotificationObserver implementation.
107   virtual void Observe(int type,
108                        const content::NotificationSource& source,
109                        const content::NotificationDetails& details) OVERRIDE;
110
111   // ExtensionRegistryObserver implementation.
112   virtual void OnExtensionUnloaded(
113       content::BrowserContext* browser_context,
114       const Extension* extension,
115       UnloadedExtensionInfo::Reason reason) OVERRIDE;
116
117   Profile* profile_;
118   scoped_refptr<DevToolsAgentHost> agent_host_;
119   std::string extension_id_;
120   Debuggee debuggee_;
121   content::NotificationRegistrar registrar_;
122   int last_request_id_;
123   typedef std::map<int, scoped_refptr<DebuggerSendCommandFunction> >
124       PendingRequests;
125   PendingRequests pending_requests_;
126   infobars::InfoBar* infobar_;
127   OnDetach::Reason detach_reason_;
128
129   // Listen to extension unloaded notification.
130   ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver>
131       extension_registry_observer_;
132
133   DISALLOW_COPY_AND_ASSIGN(ExtensionDevToolsClientHost);
134 };
135
136 // The member function declarations come after the other class declarations, so
137 // they can call members on them.
138
139
140 namespace {
141
142 // Helpers --------------------------------------------------------------------
143
144 void CopyDebuggee(Debuggee* dst, const Debuggee& src) {
145   if (src.tab_id)
146     dst->tab_id.reset(new int(*src.tab_id));
147   if (src.extension_id)
148     dst->extension_id.reset(new std::string(*src.extension_id));
149   if (src.target_id)
150     dst->target_id.reset(new std::string(*src.target_id));
151 }
152
153
154 // ExtensionDevToolsInfoBarDelegate -------------------------------------------
155
156 class ExtensionDevToolsInfoBarDelegate : public ConfirmInfoBarDelegate {
157  public:
158   // Creates an extension dev tools infobar and delegate and adds the infobar to
159   // the InfoBarService associated with |rvh|.  Returns the infobar if it was
160   // successfully added.
161   static infobars::InfoBar* Create(RenderViewHost* rvh,
162                                    const std::string& client_name);
163
164   void set_client_host(ExtensionDevToolsClientHost* client_host) {
165     client_host_ = client_host;
166   }
167
168  private:
169   explicit ExtensionDevToolsInfoBarDelegate(const std::string& client_name);
170   virtual ~ExtensionDevToolsInfoBarDelegate();
171
172   // ConfirmInfoBarDelegate:
173   virtual void InfoBarDismissed() OVERRIDE;
174   virtual Type GetInfoBarType() const OVERRIDE;
175   virtual bool ShouldExpireInternal(
176       const NavigationDetails& details) const OVERRIDE;
177   virtual base::string16 GetMessageText() const OVERRIDE;
178   virtual int GetButtons() const OVERRIDE;
179   virtual bool Cancel() OVERRIDE;
180
181   std::string client_name_;
182   ExtensionDevToolsClientHost* client_host_;
183
184   DISALLOW_COPY_AND_ASSIGN(ExtensionDevToolsInfoBarDelegate);
185 };
186
187 // static
188 infobars::InfoBar* ExtensionDevToolsInfoBarDelegate::Create(
189     RenderViewHost* rvh,
190     const std::string& client_name) {
191   if (!rvh)
192     return NULL;
193
194   WebContents* web_contents = WebContents::FromRenderViewHost(rvh);
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   DevToolsManager* manager = DevToolsManager::GetInstance();
293   for (ClientHosts::iterator it = client_hosts_.begin();
294        it != client_hosts_.end(); ++it) {
295     ExtensionDevToolsClientHost* client_host = *it;
296     if (manager->GetDevToolsAgentHostFor(client_host) == agent_host &&
297         client_host->extension_id() == extension_id)
298       return client_host;
299   }
300   return NULL;
301 }
302
303 }  // namespace
304
305
306 // ExtensionDevToolsClientHost ------------------------------------------------
307
308 ExtensionDevToolsClientHost::ExtensionDevToolsClientHost(
309     Profile* profile,
310     DevToolsAgentHost* agent_host,
311     const std::string& extension_id,
312     const std::string& extension_name,
313     const Debuggee& debuggee,
314     infobars::InfoBar* infobar)
315     : profile_(profile),
316       agent_host_(agent_host),
317       extension_id_(extension_id),
318       last_request_id_(0),
319       infobar_(infobar),
320       detach_reason_(OnDetach::REASON_TARGET_CLOSED),
321       extension_registry_observer_(this) {
322   CopyDebuggee(&debuggee_, debuggee);
323
324   AttachedClientHosts::GetInstance()->Add(this);
325
326   // ExtensionRegistryObserver listen extension unloaded and detach debugger
327   // from there.
328   extension_registry_observer_.Add(ExtensionRegistry::Get(profile_));
329
330   // RVH-based agents disconnect from their clients when the app is terminating
331   // but shared worker-based agents do not.
332   // Disconnect explicitly to make sure that |this| observer is not leaked.
333   registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
334                  content::NotificationService::AllSources());
335
336   // Attach to debugger and tell it we are ready.
337   DevToolsManager::GetInstance()->RegisterDevToolsClientHostFor(
338       agent_host_.get(), this);
339
340   if (infobar_) {
341     static_cast<ExtensionDevToolsInfoBarDelegate*>(
342         infobar_->delegate())->set_client_host(this);
343     registrar_.Add(
344         this, chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED,
345         content::Source<InfoBarService>(InfoBarService::FromWebContents(
346             WebContents::FromRenderViewHost(
347                 agent_host_->GetRenderViewHost()))));
348   }
349 }
350
351 ExtensionDevToolsClientHost::~ExtensionDevToolsClientHost() {
352   // Ensure calling RemoveInfoBar() below won't result in Observe() trying to
353   // Close() us.
354   registrar_.RemoveAll();
355
356   if (infobar_) {
357     static_cast<ExtensionDevToolsInfoBarDelegate*>(
358         infobar_->delegate())->set_client_host(NULL);
359     InfoBarService* infobar_service = InfoBarService::FromWebContents(
360         WebContents::FromRenderViewHost(agent_host_->GetRenderViewHost()));
361     infobar_service->RemoveInfoBar(infobar_);
362   }
363   AttachedClientHosts::GetInstance()->Remove(this);
364 }
365
366 // DevToolsClientHost interface
367 void ExtensionDevToolsClientHost::InspectedContentsClosing() {
368   SendDetachedEvent();
369   delete this;
370 }
371
372 void ExtensionDevToolsClientHost::ReplacedWithAnotherClient() {
373   detach_reason_ = OnDetach::REASON_REPLACED_WITH_DEVTOOLS;
374 }
375
376 void ExtensionDevToolsClientHost::Close() {
377   DevToolsManager::GetInstance()->ClientHostClosing(this);
378   delete this;
379 }
380
381 void ExtensionDevToolsClientHost::SendMessageToBackend(
382     DebuggerSendCommandFunction* function,
383     const std::string& method,
384     SendCommand::Params::CommandParams* command_params) {
385   base::DictionaryValue protocol_request;
386   int request_id = ++last_request_id_;
387   pending_requests_[request_id] = function;
388   protocol_request.SetInteger("id", request_id);
389   protocol_request.SetString("method", method);
390   if (command_params) {
391     protocol_request.Set("params",
392                          command_params->additional_properties.DeepCopy());
393   }
394
395   std::string json_args;
396   base::JSONWriter::Write(&protocol_request, &json_args);
397   DevToolsManager::GetInstance()->DispatchOnInspectorBackend(this, json_args);
398 }
399
400 void ExtensionDevToolsClientHost::MarkAsDismissed() {
401   detach_reason_ = OnDetach::REASON_CANCELED_BY_USER;
402 }
403
404 void ExtensionDevToolsClientHost::SendDetachedEvent() {
405   if (!EventRouter::Get(profile_))
406     return;
407
408   scoped_ptr<base::ListValue> args(OnDetach::Create(debuggee_,
409                                                     detach_reason_));
410   scoped_ptr<Event> event(new Event(OnDetach::kEventName, args.Pass()));
411   event->restrict_to_browser_context = profile_;
412   EventRouter::Get(profile_)
413       ->DispatchEventToExtension(extension_id_, event.Pass());
414 }
415
416 void ExtensionDevToolsClientHost::OnExtensionUnloaded(
417     content::BrowserContext* browser_context,
418     const Extension* extension,
419     UnloadedExtensionInfo::Reason reason) {
420   if (extension->id() == extension_id_)
421     Close();
422 }
423
424 void ExtensionDevToolsClientHost::Observe(
425     int type,
426     const content::NotificationSource& source,
427     const content::NotificationDetails& details) {
428   switch (type) {
429     case chrome::NOTIFICATION_APP_TERMINATING:
430       Close();
431       break;
432     case chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED:
433       if (content::Details<infobars::InfoBar::RemovedDetails>(details)->first ==
434           infobar_) {
435         infobar_ = NULL;
436         SendDetachedEvent();
437         Close();
438       }
439       break;
440     default:
441       NOTREACHED();
442   }
443 }
444
445 void ExtensionDevToolsClientHost::DispatchOnInspectorFrontend(
446     const std::string& message) {
447   if (!EventRouter::Get(profile_))
448     return;
449
450   scoped_ptr<base::Value> result(base::JSONReader::Read(message));
451   if (!result->IsType(base::Value::TYPE_DICTIONARY))
452     return;
453   base::DictionaryValue* dictionary =
454       static_cast<base::DictionaryValue*>(result.get());
455
456   int id;
457   if (!dictionary->GetInteger("id", &id)) {
458     std::string method_name;
459     if (!dictionary->GetString("method", &method_name))
460       return;
461
462     OnEvent::Params params;
463     base::DictionaryValue* params_value;
464     if (dictionary->GetDictionary("params", &params_value))
465       params.additional_properties.Swap(params_value);
466
467     scoped_ptr<base::ListValue> args(
468         OnEvent::Create(debuggee_, method_name, params));
469     scoped_ptr<Event> event(new Event(OnEvent::kEventName, args.Pass()));
470     event->restrict_to_browser_context = profile_;
471     EventRouter::Get(profile_)
472         ->DispatchEventToExtension(extension_id_, event.Pass());
473   } else {
474     DebuggerSendCommandFunction* function = pending_requests_[id].get();
475     if (!function)
476       return;
477
478     function->SendResponseBody(dictionary);
479     pending_requests_.erase(id);
480   }
481 }
482
483
484 // DebuggerFunction -----------------------------------------------------------
485
486 DebuggerFunction::DebuggerFunction()
487     : client_host_(NULL) {
488 }
489
490 DebuggerFunction::~DebuggerFunction() {
491 }
492
493 void DebuggerFunction::FormatErrorMessage(const std::string& format) {
494   if (debuggee_.tab_id)
495     error_ = ErrorUtils::FormatErrorMessage(
496       format, keys::kTabTargetType, base::IntToString(*debuggee_.tab_id));
497   else if (debuggee_.extension_id)
498     error_ = ErrorUtils::FormatErrorMessage(
499       format, keys::kBackgroundPageTargetType, *debuggee_.extension_id);
500   else
501     error_ = ErrorUtils::FormatErrorMessage(
502       format, keys::kOpaqueTargetType, *debuggee_.target_id);
503 }
504
505 bool DebuggerFunction::InitAgentHost() {
506   if (debuggee_.tab_id) {
507     WebContents* web_contents = NULL;
508     bool result = ExtensionTabUtil::GetTabById(*debuggee_.tab_id,
509                                                GetProfile(),
510                                                include_incognito(),
511                                                NULL,
512                                                NULL,
513                                                &web_contents,
514                                                NULL);
515     if (result && web_contents) {
516       if (content::HasWebUIScheme(web_contents->GetURL())) {
517         error_ = ErrorUtils::FormatErrorMessage(
518             keys::kAttachToWebUIError,
519             web_contents->GetURL().scheme());
520         return false;
521       }
522       agent_host_ = DevToolsAgentHost::GetOrCreateFor(web_contents);
523     }
524   } else if (debuggee_.extension_id) {
525     ExtensionHost* extension_host =
526         ExtensionSystem::Get(GetProfile())
527             ->process_manager()
528             ->GetBackgroundHostForExtension(*debuggee_.extension_id);
529     if (extension_host) {
530       agent_host_ = DevToolsAgentHost::GetOrCreateFor(
531           extension_host->render_view_host());
532     }
533   } else if (debuggee_.target_id) {
534     agent_host_ = DevToolsAgentHost::GetForId(*debuggee_.target_id);
535   } else {
536     error_ = keys::kInvalidTargetError;
537     return false;
538   }
539
540   if (!agent_host_.get()) {
541     FormatErrorMessage(keys::kNoTargetError);
542     return false;
543   }
544   return true;
545 }
546
547 bool DebuggerFunction::InitClientHost() {
548   if (!InitAgentHost())
549     return false;
550
551   client_host_ = AttachedClientHosts::GetInstance()->Lookup(
552       agent_host_.get(), GetExtension()->id());
553
554   if (!client_host_) {
555     FormatErrorMessage(keys::kNotAttachedError);
556     return false;
557   }
558   return true;
559 }
560
561
562 // DebuggerAttachFunction -----------------------------------------------------
563
564 DebuggerAttachFunction::DebuggerAttachFunction() {
565 }
566
567 DebuggerAttachFunction::~DebuggerAttachFunction() {
568 }
569
570 bool DebuggerAttachFunction::RunAsync() {
571   scoped_ptr<Attach::Params> params(Attach::Params::Create(*args_));
572   EXTENSION_FUNCTION_VALIDATE(params.get());
573
574   CopyDebuggee(&debuggee_, params->target);
575   if (!InitAgentHost())
576     return false;
577
578   if (!DevToolsHttpHandler::IsSupportedProtocolVersion(
579           params->required_version)) {
580     error_ = ErrorUtils::FormatErrorMessage(
581         keys::kProtocolVersionNotSupportedError,
582         params->required_version);
583     return false;
584   }
585
586   if (agent_host_->IsAttached()) {
587     FormatErrorMessage(keys::kAlreadyAttachedError);
588     return false;
589   }
590
591   infobars::InfoBar* infobar = NULL;
592   if (!CommandLine::ForCurrentProcess()->
593        HasSwitch(switches::kSilentDebuggerExtensionAPI)) {
594     // Do not attach to the target if for any reason the infobar cannot be shown
595     // for this WebContents instance.
596     infobar = ExtensionDevToolsInfoBarDelegate::Create(
597         agent_host_->GetRenderViewHost(), GetExtension()->name());
598     if (!infobar) {
599       error_ = ErrorUtils::FormatErrorMessage(
600           keys::kSilentDebuggingRequired,
601           switches::kSilentDebuggerExtensionAPI);
602       return false;
603     }
604   }
605
606   new ExtensionDevToolsClientHost(GetProfile(),
607                                   agent_host_.get(),
608                                   GetExtension()->id(),
609                                   GetExtension()->name(),
610                                   debuggee_,
611                                   infobar);
612   SendResponse(true);
613   return true;
614 }
615
616
617 // DebuggerDetachFunction -----------------------------------------------------
618
619 DebuggerDetachFunction::DebuggerDetachFunction() {
620 }
621
622 DebuggerDetachFunction::~DebuggerDetachFunction() {
623 }
624
625 bool DebuggerDetachFunction::RunAsync() {
626   scoped_ptr<Detach::Params> params(Detach::Params::Create(*args_));
627   EXTENSION_FUNCTION_VALIDATE(params.get());
628
629   CopyDebuggee(&debuggee_, params->target);
630   if (!InitClientHost())
631     return false;
632
633   client_host_->Close();
634   SendResponse(true);
635   return true;
636 }
637
638
639 // DebuggerSendCommandFunction ------------------------------------------------
640
641 DebuggerSendCommandFunction::DebuggerSendCommandFunction() {
642 }
643
644 DebuggerSendCommandFunction::~DebuggerSendCommandFunction() {
645 }
646
647 bool DebuggerSendCommandFunction::RunAsync() {
648   scoped_ptr<SendCommand::Params> params(SendCommand::Params::Create(*args_));
649   EXTENSION_FUNCTION_VALIDATE(params.get());
650
651   CopyDebuggee(&debuggee_, params->target);
652   if (!InitClientHost())
653     return false;
654
655   client_host_->SendMessageToBackend(this, params->method,
656       params->command_params.get());
657   return true;
658 }
659
660 void DebuggerSendCommandFunction::SendResponseBody(
661     base::DictionaryValue* response) {
662   base::Value* error_body;
663   if (response->Get("error", &error_body)) {
664     base::JSONWriter::Write(error_body, &error_);
665     SendResponse(false);
666     return;
667   }
668
669   base::DictionaryValue* result_body;
670   SendCommand::Results::Result result;
671   if (response->GetDictionary("result", &result_body))
672     result.additional_properties.Swap(result_body);
673
674   results_ = SendCommand::Results::Create(result);
675   SendResponse(true);
676 }
677
678
679 // DebuggerGetTargetsFunction -------------------------------------------------
680
681 namespace {
682
683 const char kTargetIdField[] = "id";
684 const char kTargetTypeField[] = "type";
685 const char kTargetTitleField[] = "title";
686 const char kTargetAttachedField[] = "attached";
687 const char kTargetUrlField[] = "url";
688 const char kTargetFaviconUrlField[] = "faviconUrl";
689 const char kTargetTypePage[] = "page";
690 const char kTargetTypeBackgroundPage[] = "background_page";
691 const char kTargetTypeWorker[] = "worker";
692 const char kTargetTypeOther[] = "other";
693 const char kTargetTabIdField[] = "tabId";
694 const char kTargetExtensionIdField[] = "extensionId";
695
696 base::Value* SerializeTarget(const DevToolsTargetImpl& target) {
697   base::DictionaryValue* dictionary = new base::DictionaryValue();
698
699   dictionary->SetString(kTargetIdField, target.GetId());
700   dictionary->SetString(kTargetTitleField, target.GetTitle());
701   dictionary->SetBoolean(kTargetAttachedField, target.IsAttached());
702   dictionary->SetString(kTargetUrlField, target.GetURL().spec());
703
704   std::string type = target.GetType();
705   if (type == kTargetTypePage) {
706     dictionary->SetInteger(kTargetTabIdField, target.GetTabId());
707   } else if (type == kTargetTypeBackgroundPage) {
708     dictionary->SetString(kTargetExtensionIdField, target.GetExtensionId());
709   } else if (type != kTargetTypeWorker) {
710     // DevToolsTargetImpl may support more types than the debugger API.
711     type = kTargetTypeOther;
712   }
713   dictionary->SetString(kTargetTypeField, type);
714
715   GURL favicon_url = target.GetFaviconURL();
716   if (favicon_url.is_valid())
717     dictionary->SetString(kTargetFaviconUrlField, favicon_url.spec());
718
719   return dictionary;
720 }
721
722 }  // namespace
723
724 DebuggerGetTargetsFunction::DebuggerGetTargetsFunction() {
725 }
726
727 DebuggerGetTargetsFunction::~DebuggerGetTargetsFunction() {
728 }
729
730 bool DebuggerGetTargetsFunction::RunAsync() {
731   DevToolsTargetImpl::EnumerateAllTargets(
732       base::Bind(&DebuggerGetTargetsFunction::SendTargetList, this));
733   return true;
734 }
735
736 void DebuggerGetTargetsFunction::SendTargetList(
737     const std::vector<DevToolsTargetImpl*>& target_list) {
738   scoped_ptr<base::ListValue> result(new base::ListValue());
739   for (size_t i = 0; i < target_list.size(); ++i)
740     result->Append(SerializeTarget(*target_list[i]));
741   STLDeleteContainerPointers(target_list.begin(), target_list.end());
742   SetResult(result.release());
743   SendResponse(true);
744 }
745
746 }  // namespace extensions