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", ¶ms_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