17d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
27d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
37d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)// found in the LICENSE file.
47d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
57d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)// Implements the Chrome Extensions Debugger API.
67d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
77d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "chrome/browser/extensions/api/debugger/debugger_api.h"
87d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
97d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include <map>
107d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include <set>
117d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
127d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "base/command_line.h"
137d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "base/json/json_reader.h"
147d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "base/json/json_writer.h"
157d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "base/memory/scoped_ptr.h"
167d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "base/memory/singleton.h"
177d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "base/strings/string_number_conversions.h"
187d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
197d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "base/values.h"
207d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "chrome/browser/chrome_notification_types.h"
217d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "chrome/browser/extensions/api/debugger/debugger_api_constants.h"
227d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "chrome/browser/extensions/event_router.h"
237d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "chrome/browser/extensions/extension_host.h"
247d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "chrome/browser/extensions/extension_service.h"
257d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "chrome/browser/extensions/extension_system.h"
267d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "chrome/browser/extensions/extension_tab_util.h"
277d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "chrome/browser/infobars/confirm_infobar_delegate.h"
287d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "chrome/browser/infobars/infobar.h"
29#include "chrome/browser/infobars/infobar_service.h"
30#include "chrome/browser/profiles/profile.h"
31#include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.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
71namespace {
72class ExtensionDevToolsInfoBarDelegate;
73}  // namespace
74
75
76// ExtensionDevToolsClientHost ------------------------------------------------
77
78class ExtensionDevToolsClientHost : public DevToolsClientHost,
79                                    public content::NotificationObserver {
80 public:
81  ExtensionDevToolsClientHost(
82      Profile* profile,
83      DevToolsAgentHost* agent_host,
84      const std::string& extension_id,
85      const std::string& extension_name,
86      const Debuggee& debuggee,
87      ExtensionDevToolsInfoBarDelegate* infobar);
88
89  virtual ~ExtensionDevToolsClientHost();
90
91  const std::string& extension_id() { return extension_id_; }
92  void Close();
93  void SendMessageToBackend(DebuggerSendCommandFunction* function,
94                            const std::string& method,
95                            SendCommand::Params::CommandParams* command_params);
96
97  // Marks connection as to-be-terminated by the user.
98  void MarkAsDismissed();
99
100  // DevToolsClientHost interface
101  virtual void InspectedContentsClosing() OVERRIDE;
102  virtual void DispatchOnInspectorFrontend(const std::string& message) OVERRIDE;
103  virtual void ReplacedWithAnotherClient() OVERRIDE;
104
105 private:
106  void SendDetachedEvent();
107
108  // content::NotificationObserver implementation.
109  virtual void Observe(int type,
110                       const content::NotificationSource& source,
111                       const content::NotificationDetails& details) OVERRIDE;
112
113  Profile* profile_;
114  scoped_refptr<DevToolsAgentHost> agent_host_;
115  std::string extension_id_;
116  Debuggee debuggee_;
117  content::NotificationRegistrar registrar_;
118  int last_request_id_;
119  typedef std::map<int, scoped_refptr<DebuggerSendCommandFunction> >
120      PendingRequests;
121  PendingRequests pending_requests_;
122  ExtensionDevToolsInfoBarDelegate* infobar_;
123  OnDetach::Reason detach_reason_;
124
125  DISALLOW_COPY_AND_ASSIGN(ExtensionDevToolsClientHost);
126};
127
128// The member function declarations come after the other class declarations, so
129// they can call members on them.
130
131
132namespace {
133
134// Helpers --------------------------------------------------------------------
135
136void CopyDebuggee(Debuggee* dst, const Debuggee& src) {
137  if (src.tab_id)
138    dst->tab_id.reset(new int(*src.tab_id));
139  if (src.extension_id)
140    dst->extension_id.reset(new std::string(*src.extension_id));
141  if (src.target_id)
142    dst->target_id.reset(new std::string(*src.target_id));
143}
144
145
146// ExtensionDevToolsInfoBarDelegate -------------------------------------------
147
148class ExtensionDevToolsInfoBarDelegate : public ConfirmInfoBarDelegate {
149 public:
150  // Creates an extension dev tools infobar delegate and adds it to the
151  // InfoBarService associated with |rvh|.  Returns the delegate if it was
152  // successfully added.
153  static ExtensionDevToolsInfoBarDelegate* Create(
154      RenderViewHost* rvh,
155      const std::string& client_name);
156
157  void set_client_host(ExtensionDevToolsClientHost* client_host) {
158    client_host_ = client_host;
159  }
160
161 private:
162  ExtensionDevToolsInfoBarDelegate(InfoBarService* infobar_service,
163                                   const std::string& client_name);
164  virtual ~ExtensionDevToolsInfoBarDelegate();
165
166  // ConfirmInfoBarDelegate:
167  virtual void InfoBarDismissed() OVERRIDE;
168  virtual Type GetInfoBarType() const OVERRIDE;
169  virtual bool ShouldExpireInternal(
170      const content::LoadCommittedDetails& details) const OVERRIDE;
171  virtual string16 GetMessageText() const OVERRIDE;
172  virtual int GetButtons() const OVERRIDE;
173  virtual bool Cancel() OVERRIDE;
174
175  std::string client_name_;
176  ExtensionDevToolsClientHost* client_host_;
177
178  DISALLOW_COPY_AND_ASSIGN(ExtensionDevToolsInfoBarDelegate);
179};
180
181// static
182ExtensionDevToolsInfoBarDelegate* ExtensionDevToolsInfoBarDelegate::Create(
183    RenderViewHost* rvh,
184    const std::string& client_name) {
185  if (!rvh)
186    return NULL;
187
188  WebContents* web_contents = WebContents::FromRenderViewHost(rvh);
189  if (!web_contents)
190    return NULL;
191
192  InfoBarService* infobar_service =
193      InfoBarService::FromWebContents(web_contents);
194  if (!infobar_service)
195    return NULL;
196
197  return static_cast<ExtensionDevToolsInfoBarDelegate*>(
198      infobar_service->AddInfoBar(scoped_ptr<InfoBarDelegate>(
199          new ExtensionDevToolsInfoBarDelegate(infobar_service, client_name))));
200}
201
202ExtensionDevToolsInfoBarDelegate::ExtensionDevToolsInfoBarDelegate(
203    InfoBarService* infobar_service,
204    const std::string& client_name)
205    : ConfirmInfoBarDelegate(infobar_service),
206      client_name_(client_name),
207      client_host_(NULL) {
208}
209
210ExtensionDevToolsInfoBarDelegate::~ExtensionDevToolsInfoBarDelegate() {
211}
212
213void ExtensionDevToolsInfoBarDelegate::InfoBarDismissed() {
214  if (client_host_)
215    client_host_->MarkAsDismissed();
216}
217
218InfoBarDelegate::Type ExtensionDevToolsInfoBarDelegate::GetInfoBarType() const {
219  return WARNING_TYPE;
220}
221
222bool ExtensionDevToolsInfoBarDelegate::ShouldExpireInternal(
223    const content::LoadCommittedDetails& details) const {
224  return false;
225}
226
227string16 ExtensionDevToolsInfoBarDelegate::GetMessageText() const {
228  return l10n_util::GetStringFUTF16(IDS_DEV_TOOLS_INFOBAR_LABEL,
229                                    UTF8ToUTF16(client_name_));
230}
231
232int ExtensionDevToolsInfoBarDelegate::GetButtons() const {
233  return BUTTON_CANCEL;
234}
235
236bool ExtensionDevToolsInfoBarDelegate::Cancel() {
237  InfoBarDismissed();
238  return true;
239}
240
241
242// AttachedClientHosts --------------------------------------------------------
243
244class AttachedClientHosts {
245 public:
246  AttachedClientHosts();
247  ~AttachedClientHosts();
248
249  // Returns the singleton instance of this class.
250  static AttachedClientHosts* GetInstance();
251
252  void Add(ExtensionDevToolsClientHost* client_host);
253  void Remove(ExtensionDevToolsClientHost* client_host);
254  ExtensionDevToolsClientHost* Lookup(DevToolsAgentHost* agent_host,
255                                      const std::string& extension_id);
256
257 private:
258  typedef std::set<ExtensionDevToolsClientHost*> ClientHosts;
259  ClientHosts client_hosts_;
260
261  DISALLOW_COPY_AND_ASSIGN(AttachedClientHosts);
262};
263
264AttachedClientHosts::AttachedClientHosts() {
265}
266
267AttachedClientHosts::~AttachedClientHosts() {
268}
269
270// static
271AttachedClientHosts* AttachedClientHosts::GetInstance() {
272  return Singleton<AttachedClientHosts>::get();
273}
274
275void AttachedClientHosts::Add(ExtensionDevToolsClientHost* client_host) {
276  client_hosts_.insert(client_host);
277}
278
279void AttachedClientHosts::Remove(ExtensionDevToolsClientHost* client_host) {
280  client_hosts_.erase(client_host);
281}
282
283ExtensionDevToolsClientHost* AttachedClientHosts::Lookup(
284    DevToolsAgentHost* agent_host,
285    const std::string& extension_id) {
286  DevToolsManager* manager = DevToolsManager::GetInstance();
287  for (ClientHosts::iterator it = client_hosts_.begin();
288       it != client_hosts_.end(); ++it) {
289    ExtensionDevToolsClientHost* client_host = *it;
290    if (manager->GetDevToolsAgentHostFor(client_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
302ExtensionDevToolsClientHost::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    ExtensionDevToolsInfoBarDelegate* 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  CopyDebuggee(&debuggee_, debuggee);
316
317  AttachedClientHosts::GetInstance()->Add(this);
318
319  // Detach from debugger when extension unloads.
320  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
321                 content::Source<Profile>(profile_));
322
323  // RVH-based agents disconnect from their clients when the app is terminating
324  // but shared worker-based agents do not.
325  // Disconnect explicitly to make sure that |this| observer is not leaked.
326  registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
327                 content::NotificationService::AllSources());
328
329  // Attach to debugger and tell it we are ready.
330  DevToolsManager::GetInstance()->RegisterDevToolsClientHostFor(
331      agent_host_.get(), this);
332
333  if (infobar_) {
334    infobar_->set_client_host(this);
335    registrar_.Add(
336        this, chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED,
337        content::Source<InfoBarService>(InfoBarService::FromWebContents(
338            WebContents::FromRenderViewHost(
339                agent_host_->GetRenderViewHost()))));
340  }
341}
342
343ExtensionDevToolsClientHost::~ExtensionDevToolsClientHost() {
344  // Ensure calling RemoveInfoBar() below won't result in Observe() trying to
345  // Close() us.
346  registrar_.RemoveAll();
347
348  if (infobar_) {
349    infobar_->set_client_host(NULL);
350    InfoBarService::FromWebContents(WebContents::FromRenderViewHost(
351        agent_host_->GetRenderViewHost()))->RemoveInfoBar(infobar_);
352  }
353  AttachedClientHosts::GetInstance()->Remove(this);
354}
355
356// DevToolsClientHost interface
357void ExtensionDevToolsClientHost::InspectedContentsClosing() {
358  SendDetachedEvent();
359  delete this;
360}
361
362void ExtensionDevToolsClientHost::ReplacedWithAnotherClient() {
363  detach_reason_ = OnDetach::REASON_REPLACED_WITH_DEVTOOLS;
364}
365
366void ExtensionDevToolsClientHost::Close() {
367  DevToolsManager::GetInstance()->ClientHostClosing(this);
368  delete this;
369}
370
371void ExtensionDevToolsClientHost::SendMessageToBackend(
372    DebuggerSendCommandFunction* function,
373    const std::string& method,
374    SendCommand::Params::CommandParams* command_params) {
375  base::DictionaryValue protocol_request;
376  int request_id = ++last_request_id_;
377  pending_requests_[request_id] = function;
378  protocol_request.SetInteger("id", request_id);
379  protocol_request.SetString("method", method);
380  if (command_params) {
381    protocol_request.Set("params",
382                         command_params->additional_properties.DeepCopy());
383  }
384
385  std::string json_args;
386  base::JSONWriter::Write(&protocol_request, &json_args);
387  DevToolsManager::GetInstance()->DispatchOnInspectorBackend(this, json_args);
388}
389
390void ExtensionDevToolsClientHost::MarkAsDismissed() {
391  detach_reason_ = OnDetach::REASON_CANCELED_BY_USER;
392}
393
394void ExtensionDevToolsClientHost::SendDetachedEvent() {
395  if (!extensions::ExtensionSystem::Get(profile_)->event_router())
396    return;
397
398  scoped_ptr<base::ListValue> args(OnDetach::Create(debuggee_,
399                                                    detach_reason_));
400  scoped_ptr<extensions::Event> event(new extensions::Event(
401      keys::kOnDetach, args.Pass()));
402  event->restrict_to_profile = profile_;
403  extensions::ExtensionSystem::Get(profile_)->event_router()->
404      DispatchEventToExtension(extension_id_, event.Pass());
405}
406
407void ExtensionDevToolsClientHost::Observe(
408    int type,
409    const content::NotificationSource& source,
410    const content::NotificationDetails& details) {
411  if (type == chrome::NOTIFICATION_EXTENSION_UNLOADED) {
412    if (content::Details<extensions::UnloadedExtensionInfo>(details)->
413        extension->id() == extension_id_)
414      Close();
415  } else if (type == chrome::NOTIFICATION_APP_TERMINATING) {
416    Close();
417  } else {
418    DCHECK_EQ(chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED, type);
419    if (content::Details<InfoBarRemovedDetails>(details)->first == infobar_) {
420      infobar_ = NULL;
421      SendDetachedEvent();
422      Close();
423    }
424  }
425}
426
427void ExtensionDevToolsClientHost::DispatchOnInspectorFrontend(
428    const std::string& message) {
429  if (!extensions::ExtensionSystem::Get(profile_)->event_router())
430    return;
431
432  scoped_ptr<Value> result(base::JSONReader::Read(message));
433  if (!result->IsType(Value::TYPE_DICTIONARY))
434    return;
435  base::DictionaryValue* dictionary =
436      static_cast<base::DictionaryValue*>(result.get());
437
438  int id;
439  if (!dictionary->GetInteger("id", &id)) {
440    std::string method_name;
441    if (!dictionary->GetString("method", &method_name))
442      return;
443
444    OnEvent::Params params;
445    base::DictionaryValue* params_value;
446    if (dictionary->GetDictionary("params", &params_value))
447      params.additional_properties.Swap(params_value);
448
449    scoped_ptr<ListValue> args(OnEvent::Create(debuggee_, method_name, params));
450    scoped_ptr<extensions::Event> event(new extensions::Event(
451        keys::kOnEvent, args.Pass()));
452    event->restrict_to_profile = profile_;
453    extensions::ExtensionSystem::Get(profile_)->event_router()->
454        DispatchEventToExtension(extension_id_, event.Pass());
455  } else {
456    DebuggerSendCommandFunction* function = pending_requests_[id].get();
457    if (!function)
458      return;
459
460    function->SendResponseBody(dictionary);
461    pending_requests_.erase(id);
462  }
463}
464
465
466// DebuggerFunction -----------------------------------------------------------
467
468DebuggerFunction::DebuggerFunction()
469    : client_host_(NULL) {
470}
471
472DebuggerFunction::~DebuggerFunction() {
473}
474
475void DebuggerFunction::FormatErrorMessage(const std::string& format) {
476  if (debuggee_.tab_id)
477    error_ = ErrorUtils::FormatErrorMessage(
478      format, keys::kTabTargetType, base::IntToString(*debuggee_.tab_id));
479  else if (debuggee_.extension_id)
480    error_ = ErrorUtils::FormatErrorMessage(
481      format, keys::kBackgroundPageTargetType, *debuggee_.extension_id);
482  else
483    error_ = ErrorUtils::FormatErrorMessage(
484      format, keys::kOpaqueTargetType, *debuggee_.target_id);
485}
486
487bool DebuggerFunction::InitAgentHost() {
488  if (debuggee_.tab_id) {
489    WebContents* web_contents = NULL;
490    bool result = ExtensionTabUtil::GetTabById(
491        *debuggee_.tab_id, profile(), include_incognito(), NULL, NULL,
492        &web_contents, NULL);
493    if (result && web_contents) {
494      if (content::HasWebUIScheme(web_contents->GetURL())) {
495        error_ = ErrorUtils::FormatErrorMessage(
496            keys::kAttachToWebUIError,
497            web_contents->GetURL().scheme());
498        return false;
499      }
500      agent_host_ = DevToolsAgentHost::GetOrCreateFor(
501          web_contents->GetRenderViewHost());
502    }
503  } else if (debuggee_.extension_id) {
504    extensions::ExtensionHost* extension_host =
505        extensions::ExtensionSystem::Get(profile())->process_manager()->
506            GetBackgroundHostForExtension(*debuggee_.extension_id);
507    if (extension_host) {
508      agent_host_ = DevToolsAgentHost::GetOrCreateFor(
509          extension_host->render_view_host());
510    }
511  } else if (debuggee_.target_id) {
512    agent_host_ = DevToolsAgentHost::GetForId(*debuggee_.target_id);
513  } else {
514    error_ = keys::kInvalidTargetError;
515    return false;
516  }
517
518  if (!agent_host_.get()) {
519    FormatErrorMessage(keys::kNoTargetError);
520    return false;
521  }
522  return true;
523}
524
525bool DebuggerFunction::InitClientHost() {
526  if (!InitAgentHost())
527    return false;
528
529  client_host_ = AttachedClientHosts::GetInstance()->Lookup(
530      agent_host_.get(), GetExtension()->id());
531
532  if (!client_host_) {
533    FormatErrorMessage(keys::kNotAttachedError);
534    return false;
535  }
536  return true;
537}
538
539
540// DebuggerAttachFunction -----------------------------------------------------
541
542DebuggerAttachFunction::DebuggerAttachFunction() {
543}
544
545DebuggerAttachFunction::~DebuggerAttachFunction() {
546}
547
548bool DebuggerAttachFunction::RunImpl() {
549  scoped_ptr<Attach::Params> params(Attach::Params::Create(*args_));
550  EXTENSION_FUNCTION_VALIDATE(params.get());
551
552  CopyDebuggee(&debuggee_, params->target);
553  if (!InitAgentHost())
554    return false;
555
556  if (!DevToolsHttpHandler::IsSupportedProtocolVersion(
557          params->required_version)) {
558    error_ = ErrorUtils::FormatErrorMessage(
559        keys::kProtocolVersionNotSupportedError,
560        params->required_version);
561    return false;
562  }
563
564  if (agent_host_->IsAttached()) {
565    FormatErrorMessage(keys::kAlreadyAttachedError);
566    return false;
567  }
568
569  ExtensionDevToolsInfoBarDelegate* infobar = NULL;
570  if (!CommandLine::ForCurrentProcess()->
571       HasSwitch(switches::kSilentDebuggerExtensionAPI)) {
572    // Do not attach to the target if for any reason the infobar cannot be shown
573    // for this WebContents instance.
574    infobar = ExtensionDevToolsInfoBarDelegate::Create(
575          agent_host_->GetRenderViewHost(), GetExtension()->name());
576    if (!infobar) {
577      error_ = ErrorUtils::FormatErrorMessage(
578          keys::kSilentDebuggingRequired,
579          switches::kSilentDebuggerExtensionAPI);
580      return false;
581    }
582  }
583
584  new ExtensionDevToolsClientHost(profile(), agent_host_.get(),
585                                  GetExtension()->id(), GetExtension()->name(),
586                                  debuggee_, infobar);
587  SendResponse(true);
588  return true;
589}
590
591
592// DebuggerDetachFunction -----------------------------------------------------
593
594DebuggerDetachFunction::DebuggerDetachFunction() {
595}
596
597DebuggerDetachFunction::~DebuggerDetachFunction() {
598}
599
600bool 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
616DebuggerSendCommandFunction::DebuggerSendCommandFunction() {
617}
618
619DebuggerSendCommandFunction::~DebuggerSendCommandFunction() {
620}
621
622bool 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
635void 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
656namespace {
657
658const char kTargetIdField[] = "id";
659const char kTargetTypeField[] = "type";
660const char kTargetTitleField[] = "title";
661const char kTargetAttachedField[] = "attached";
662const char kTargetUrlField[] = "url";
663const char kTargetFaviconUrlField[] = "faviconUrl";
664const char kTargetTypePage[] = "page";
665const char kTargetTypeBackgroundPage[] = "background_page";
666const char kTargetTypeWorker[] = "worker";
667const char kTargetTypeOther[] = "other";
668const char kTargetTabIdField[] = "tabId";
669const char kTargetExtensionIdField[] = "extensionId";
670
671extensions::ExtensionHost*
672    GetExtensionBackgroundHost(WebContents* web_contents) {
673  Profile* profile =
674      Profile::FromBrowserContext(web_contents->GetBrowserContext());
675  if (!profile)
676    return NULL;
677
678  extensions::ExtensionHost* extension_host =
679      extensions::ExtensionSystem::Get(profile)->process_manager()->
680          GetBackgroundHostForExtension(web_contents->GetURL().host());
681
682  return (extension_host && extension_host->host_contents() == web_contents) ?
683      extension_host : NULL;
684}
685
686base::Value* SerializePageInfo(RenderViewHost* rvh) {
687  WebContents* web_contents = WebContents::FromRenderViewHost(rvh);
688  if (!web_contents)
689    return NULL;
690
691  DevToolsAgentHost* agent_host = DevToolsAgentHost::GetOrCreateFor(rvh);
692
693  base::DictionaryValue* dictionary = new base::DictionaryValue();
694
695  dictionary->SetString(kTargetIdField, agent_host->GetId());
696  dictionary->SetBoolean(kTargetAttachedField, agent_host->IsAttached());
697  dictionary->SetString(kTargetUrlField, web_contents->GetURL().spec());
698
699  extensions::ExtensionHost* extension_host =
700      GetExtensionBackgroundHost(web_contents);
701  if (extension_host) {
702    // This RenderViewHost belongs to a background page.
703    dictionary->SetString(kTargetTypeField, kTargetTypeBackgroundPage);
704    dictionary->SetString(kTargetExtensionIdField,
705                          extension_host->extension()->id());
706    dictionary->SetString(kTargetTitleField,
707                          extension_host->extension()->name());
708  } else {
709    int tab_id = ExtensionTabUtil::GetTabId(web_contents);
710    if (tab_id != -1) {
711      // This RenderViewHost belongs to a regular page.
712      dictionary->SetString(kTargetTypeField, kTargetTypePage);
713      dictionary->SetInteger(kTargetTabIdField, tab_id);
714    } else {
715      dictionary->SetString(kTargetTypeField, kTargetTypeOther);
716    }
717    dictionary->SetString(kTargetTitleField, web_contents->GetTitle());
718
719    content::NavigationController& controller = web_contents->GetController();
720    content::NavigationEntry* entry = controller.GetActiveEntry();
721    if (entry != NULL && entry->GetURL().is_valid()) {
722      dictionary->SetString(kTargetFaviconUrlField,
723                            entry->GetFavicon().url.spec());
724    }
725  }
726
727  return dictionary;
728}
729
730base::Value* SerializeWorkerInfo(const WorkerService::WorkerInfo& worker) {
731  base::DictionaryValue* dictionary = new base::DictionaryValue;
732
733  scoped_refptr<DevToolsAgentHost> agent(DevToolsAgentHost::GetForWorker(
734      worker.process_id, worker.route_id));
735  dictionary->SetString(kTargetIdField, agent->GetId());
736  dictionary->SetString(kTargetTypeField, kTargetTypeWorker);
737  dictionary->SetString(kTargetTitleField, worker.name);
738  dictionary->SetString(kTargetUrlField, worker.url.spec());
739  dictionary->SetBoolean(kTargetAttachedField, agent->IsAttached());
740
741  return dictionary;
742}
743
744} // namespace
745
746DebuggerGetTargetsFunction::DebuggerGetTargetsFunction() {
747}
748
749DebuggerGetTargetsFunction::~DebuggerGetTargetsFunction() {
750}
751
752bool DebuggerGetTargetsFunction::RunImpl() {
753  base::ListValue* results_list = new base::ListValue();
754
755  std::vector<RenderViewHost*> rvh_list =
756      DevToolsAgentHost::GetValidRenderViewHosts();
757  for (std::vector<RenderViewHost*>::iterator it = rvh_list.begin();
758       it != rvh_list.end(); ++it) {
759    base::Value* value = SerializePageInfo(*it);
760    if (value)
761      results_list->Append(value);
762  }
763
764  content::BrowserThread::PostTaskAndReply(
765      content::BrowserThread::IO,
766      FROM_HERE,
767      base::Bind(&DebuggerGetTargetsFunction::CollectWorkerInfo, this,
768                 results_list),
769      base::Bind(&DebuggerGetTargetsFunction::SendTargetList, this,
770                 results_list));
771  return true;
772}
773
774void DebuggerGetTargetsFunction::CollectWorkerInfo(base::ListValue* list) {
775  std::vector<WorkerService::WorkerInfo> worker_info =
776      WorkerService::GetInstance()->GetWorkers();
777  for (size_t i = 0; i < worker_info.size(); ++i)
778    list->Append(SerializeWorkerInfo(worker_info[i]));
779}
780
781void DebuggerGetTargetsFunction::SendTargetList(base::ListValue* list) {
782  SetResult(list);
783  SendResponse(true);
784}
785