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