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