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