• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_host.h"
25 #include "chrome/browser/extensions/extension_service.h"
26 #include "chrome/browser/extensions/extension_system.h"
27 #include "chrome/browser/extensions/extension_tab_util.h"
28 #include "chrome/browser/infobars/confirm_infobar_delegate.h"
29 #include "chrome/browser/infobars/infobar.h"
30 #include "chrome/browser/infobars/infobar_service.h"
31 #include "chrome/browser/profiles/profile.h"
32 #include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h"
33 #include "chrome/common/chrome_switches.h"
34 #include "content/public/browser/devtools_agent_host.h"
35 #include "content/public/browser/devtools_client_host.h"
36 #include "content/public/browser/devtools_http_handler.h"
37 #include "content/public/browser/devtools_manager.h"
38 #include "content/public/browser/notification_service.h"
39 #include "content/public/browser/notification_source.h"
40 #include "content/public/browser/render_process_host.h"
41 #include "content/public/browser/render_view_host.h"
42 #include "content/public/browser/render_widget_host.h"
43 #include "content/public/browser/web_contents.h"
44 #include "content/public/common/content_client.h"
45 #include "content/public/common/url_utils.h"
46 #include "extensions/browser/event_router.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 
extension_id()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 
CopyDebuggee(Debuggee * dst,const Debuggee & src)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 
set_client_host(ExtensionDevToolsClientHost * client_host)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
Create(RenderViewHost * rvh,const std::string & client_name)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 
ExtensionDevToolsInfoBarDelegate(const std::string & client_name)193 ExtensionDevToolsInfoBarDelegate::ExtensionDevToolsInfoBarDelegate(
194     const std::string& client_name)
195     : ConfirmInfoBarDelegate(),
196       client_name_(client_name),
197       client_host_(NULL) {
198 }
199 
~ExtensionDevToolsInfoBarDelegate()200 ExtensionDevToolsInfoBarDelegate::~ExtensionDevToolsInfoBarDelegate() {
201 }
202 
InfoBarDismissed()203 void ExtensionDevToolsInfoBarDelegate::InfoBarDismissed() {
204   if (client_host_)
205     client_host_->MarkAsDismissed();
206 }
207 
GetInfoBarType() const208 InfoBarDelegate::Type ExtensionDevToolsInfoBarDelegate::GetInfoBarType() const {
209   return WARNING_TYPE;
210 }
211 
ShouldExpireInternal(const content::LoadCommittedDetails & details) const212 bool ExtensionDevToolsInfoBarDelegate::ShouldExpireInternal(
213     const content::LoadCommittedDetails& details) const {
214   return false;
215 }
216 
GetMessageText() const217 base::string16 ExtensionDevToolsInfoBarDelegate::GetMessageText() const {
218   return l10n_util::GetStringFUTF16(IDS_DEV_TOOLS_INFOBAR_LABEL,
219                                     UTF8ToUTF16(client_name_));
220 }
221 
GetButtons() const222 int ExtensionDevToolsInfoBarDelegate::GetButtons() const {
223   return BUTTON_CANCEL;
224 }
225 
Cancel()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 
AttachedClientHosts()254 AttachedClientHosts::AttachedClientHosts() {
255 }
256 
~AttachedClientHosts()257 AttachedClientHosts::~AttachedClientHosts() {
258 }
259 
260 // static
GetInstance()261 AttachedClientHosts* AttachedClientHosts::GetInstance() {
262   return Singleton<AttachedClientHosts>::get();
263 }
264 
Add(ExtensionDevToolsClientHost * client_host)265 void AttachedClientHosts::Add(ExtensionDevToolsClientHost* client_host) {
266   client_hosts_.insert(client_host);
267 }
268 
Remove(ExtensionDevToolsClientHost * client_host)269 void AttachedClientHosts::Remove(ExtensionDevToolsClientHost* client_host) {
270   client_hosts_.erase(client_host);
271 }
272 
Lookup(DevToolsAgentHost * agent_host,const std::string & extension_id)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 
ExtensionDevToolsClientHost(Profile * profile,DevToolsAgentHost * agent_host,const std::string & extension_id,const std::string & extension_name,const Debuggee & debuggee,InfoBar * infobar)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,
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 
~ExtensionDevToolsClientHost()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
InspectedContentsClosing()349 void ExtensionDevToolsClientHost::InspectedContentsClosing() {
350   SendDetachedEvent();
351   delete this;
352 }
353 
ReplacedWithAnotherClient()354 void ExtensionDevToolsClientHost::ReplacedWithAnotherClient() {
355   detach_reason_ = OnDetach::REASON_REPLACED_WITH_DEVTOOLS;
356 }
357 
Close()358 void ExtensionDevToolsClientHost::Close() {
359   DevToolsManager::GetInstance()->ClientHostClosing(this);
360   delete this;
361 }
362 
SendMessageToBackend(DebuggerSendCommandFunction * function,const std::string & method,SendCommand::Params::CommandParams * command_params)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 
MarkAsDismissed()382 void ExtensionDevToolsClientHost::MarkAsDismissed() {
383   detach_reason_ = OnDetach::REASON_CANCELED_BY_USER;
384 }
385 
SendDetachedEvent()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 
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)399 void ExtensionDevToolsClientHost::Observe(
400     int type,
401     const content::NotificationSource& source,
402     const content::NotificationDetails& details) {
403   if (type == chrome::NOTIFICATION_EXTENSION_UNLOADED) {
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 
DispatchOnInspectorFrontend(const std::string & message)419 void ExtensionDevToolsClientHost::DispatchOnInspectorFrontend(
420     const std::string& message) {
421   if (!extensions::ExtensionSystem::Get(profile_)->event_router())
422     return;
423 
424   scoped_ptr<Value> result(base::JSONReader::Read(message));
425   if (!result->IsType(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<ListValue> args(OnEvent::Create(debuggee_, method_name, params));
442     scoped_ptr<extensions::Event> event(new extensions::Event(
443         OnEvent::kEventName, args.Pass()));
444     event->restrict_to_browser_context = profile_;
445     extensions::ExtensionSystem::Get(profile_)->event_router()->
446         DispatchEventToExtension(extension_id_, event.Pass());
447   } else {
448     DebuggerSendCommandFunction* function = pending_requests_[id].get();
449     if (!function)
450       return;
451 
452     function->SendResponseBody(dictionary);
453     pending_requests_.erase(id);
454   }
455 }
456 
457 
458 // DebuggerFunction -----------------------------------------------------------
459 
DebuggerFunction()460 DebuggerFunction::DebuggerFunction()
461     : client_host_(NULL) {
462 }
463 
~DebuggerFunction()464 DebuggerFunction::~DebuggerFunction() {
465 }
466 
FormatErrorMessage(const std::string & format)467 void DebuggerFunction::FormatErrorMessage(const std::string& format) {
468   if (debuggee_.tab_id)
469     error_ = ErrorUtils::FormatErrorMessage(
470       format, keys::kTabTargetType, base::IntToString(*debuggee_.tab_id));
471   else if (debuggee_.extension_id)
472     error_ = ErrorUtils::FormatErrorMessage(
473       format, keys::kBackgroundPageTargetType, *debuggee_.extension_id);
474   else
475     error_ = ErrorUtils::FormatErrorMessage(
476       format, keys::kOpaqueTargetType, *debuggee_.target_id);
477 }
478 
InitAgentHost()479 bool DebuggerFunction::InitAgentHost() {
480   if (debuggee_.tab_id) {
481     WebContents* web_contents = NULL;
482     bool result = extensions::ExtensionTabUtil::GetTabById(*debuggee_.tab_id,
483                                                            GetProfile(),
484                                                            include_incognito(),
485                                                            NULL,
486                                                            NULL,
487                                                            &web_contents,
488                                                            NULL);
489     if (result && web_contents) {
490       if (content::HasWebUIScheme(web_contents->GetURL())) {
491         error_ = ErrorUtils::FormatErrorMessage(
492             keys::kAttachToWebUIError,
493             web_contents->GetURL().scheme());
494         return false;
495       }
496       agent_host_ = DevToolsAgentHost::GetOrCreateFor(
497           web_contents->GetRenderViewHost());
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 
InitClientHost()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 
DebuggerAttachFunction()539 DebuggerAttachFunction::DebuggerAttachFunction() {
540 }
541 
~DebuggerAttachFunction()542 DebuggerAttachFunction::~DebuggerAttachFunction() {
543 }
544 
RunImpl()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 
DebuggerDetachFunction()594 DebuggerDetachFunction::DebuggerDetachFunction() {
595 }
596 
~DebuggerDetachFunction()597 DebuggerDetachFunction::~DebuggerDetachFunction() {
598 }
599 
RunImpl()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 
DebuggerSendCommandFunction()616 DebuggerSendCommandFunction::DebuggerSendCommandFunction() {
617 }
618 
~DebuggerSendCommandFunction()619 DebuggerSendCommandFunction::~DebuggerSendCommandFunction() {
620 }
621 
RunImpl()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 
SendResponseBody(base::DictionaryValue * response)635 void DebuggerSendCommandFunction::SendResponseBody(
636     base::DictionaryValue* response) {
637   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 
SerializeTarget(const DevToolsTargetImpl & target)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 
DebuggerGetTargetsFunction()699 DebuggerGetTargetsFunction::DebuggerGetTargetsFunction() {
700 }
701 
~DebuggerGetTargetsFunction()702 DebuggerGetTargetsFunction::~DebuggerGetTargetsFunction() {
703 }
704 
RunImpl()705 bool DebuggerGetTargetsFunction::RunImpl() {
706   DevToolsTargetImpl::EnumerateAllTargets(
707       base::Bind(&DebuggerGetTargetsFunction::SendTargetList, this));
708   return true;
709 }
710 
SendTargetList(const std::vector<DevToolsTargetImpl * > & target_list)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 }
720