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