1// Copyright 2013 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#include "content/shell/browser/shell_devtools_delegate.h"
6
7#include <vector>
8
9#include "base/bind.h"
10#include "base/command_line.h"
11#include "base/files/file_path.h"
12#include "base/strings/string_number_conversions.h"
13#include "base/strings/stringprintf.h"
14#include "base/strings/utf_string_conversions.h"
15#include "content/public/browser/devtools_agent_host.h"
16#include "content/public/browser/devtools_http_handler.h"
17#include "content/public/browser/devtools_target.h"
18#include "content/public/browser/favicon_status.h"
19#include "content/public/browser/navigation_entry.h"
20#include "content/public/browser/render_view_host.h"
21#include "content/public/browser/web_contents.h"
22#include "content/public/common/content_switches.h"
23#include "content/public/common/url_constants.h"
24#include "content/public/common/user_agent.h"
25#include "content/shell/browser/shell.h"
26#include "grit/shell_resources.h"
27#include "net/socket/tcp_server_socket.h"
28#include "ui/base/resource/resource_bundle.h"
29
30#if defined(OS_ANDROID)
31#include "content/public/browser/android/devtools_auth.h"
32#include "net/socket/unix_domain_server_socket_posix.h"
33#endif
34
35using content::DevToolsAgentHost;
36using content::RenderViewHost;
37using content::WebContents;
38
39namespace {
40
41#if defined(OS_ANDROID)
42const char kFrontEndURL[] =
43    "http://chrome-devtools-frontend.appspot.com/serve_rev/%s/devtools.html";
44#endif
45const char kTargetTypePage[] = "page";
46const char kTargetTypeServiceWorker[] = "service_worker";
47const char kTargetTypeOther[] = "other";
48
49#if defined(OS_ANDROID)
50class UnixDomainServerSocketFactory
51    : public content::DevToolsHttpHandler::ServerSocketFactory {
52 public:
53  explicit UnixDomainServerSocketFactory(const std::string& socket_name)
54      : content::DevToolsHttpHandler::ServerSocketFactory(socket_name, 0, 1) {}
55
56 private:
57  // content::DevToolsHttpHandler::ServerSocketFactory.
58  virtual scoped_ptr<net::ServerSocket> Create() const OVERRIDE {
59    return scoped_ptr<net::ServerSocket>(
60        new net::UnixDomainServerSocket(
61            base::Bind(&content::CanUserConnectToDevTools),
62            true /* use_abstract_namespace */));
63  }
64
65  DISALLOW_COPY_AND_ASSIGN(UnixDomainServerSocketFactory);
66};
67#else
68class TCPServerSocketFactory
69    : public content::DevToolsHttpHandler::ServerSocketFactory {
70 public:
71  TCPServerSocketFactory(const std::string& address, int port, int backlog)
72      : content::DevToolsHttpHandler::ServerSocketFactory(
73            address, port, backlog) {}
74
75 private:
76  // content::DevToolsHttpHandler::ServerSocketFactory.
77  virtual scoped_ptr<net::ServerSocket> Create() const OVERRIDE {
78    return scoped_ptr<net::ServerSocket>(
79        new net::TCPServerSocket(NULL, net::NetLog::Source()));
80  }
81
82  DISALLOW_COPY_AND_ASSIGN(TCPServerSocketFactory);
83};
84#endif
85
86scoped_ptr<content::DevToolsHttpHandler::ServerSocketFactory>
87CreateSocketFactory() {
88  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
89#if defined(OS_ANDROID)
90  std::string socket_name = "content_shell_devtools_remote";
91  if (command_line.HasSwitch(switches::kRemoteDebuggingSocketName)) {
92    socket_name = command_line.GetSwitchValueASCII(
93        switches::kRemoteDebuggingSocketName);
94  }
95  return scoped_ptr<content::DevToolsHttpHandler::ServerSocketFactory>(
96      new UnixDomainServerSocketFactory(socket_name));
97#else
98  // See if the user specified a port on the command line (useful for
99  // automation). If not, use an ephemeral port by specifying 0.
100  int port = 0;
101  if (command_line.HasSwitch(switches::kRemoteDebuggingPort)) {
102    int temp_port;
103    std::string port_str =
104        command_line.GetSwitchValueASCII(switches::kRemoteDebuggingPort);
105    if (base::StringToInt(port_str, &temp_port) &&
106        temp_port > 0 && temp_port < 65535) {
107      port = temp_port;
108    } else {
109      DLOG(WARNING) << "Invalid http debugger port number " << temp_port;
110    }
111  }
112  return scoped_ptr<content::DevToolsHttpHandler::ServerSocketFactory>(
113      new TCPServerSocketFactory("127.0.0.1", port, 1));
114#endif
115}
116
117class Target : public content::DevToolsTarget {
118 public:
119  explicit Target(scoped_refptr<DevToolsAgentHost> agent_host);
120
121  virtual std::string GetId() const OVERRIDE { return agent_host_->GetId(); }
122  virtual std::string GetParentId() const OVERRIDE { return std::string(); }
123  virtual std::string GetType() const OVERRIDE {
124    switch (agent_host_->GetType()) {
125      case DevToolsAgentHost::TYPE_WEB_CONTENTS:
126        return kTargetTypePage;
127      case DevToolsAgentHost::TYPE_SERVICE_WORKER:
128        return kTargetTypeServiceWorker;
129      default:
130        break;
131    }
132    return kTargetTypeOther;
133  }
134  virtual std::string GetTitle() const OVERRIDE {
135    return agent_host_->GetTitle();
136  }
137  virtual std::string GetDescription() const OVERRIDE { return std::string(); }
138  virtual GURL GetURL() const OVERRIDE { return agent_host_->GetURL(); }
139  virtual GURL GetFaviconURL() const OVERRIDE { return favicon_url_; }
140  virtual base::TimeTicks GetLastActivityTime() const OVERRIDE {
141    return last_activity_time_;
142  }
143  virtual bool IsAttached() const OVERRIDE {
144    return agent_host_->IsAttached();
145  }
146  virtual scoped_refptr<DevToolsAgentHost> GetAgentHost() const OVERRIDE {
147    return agent_host_;
148  }
149  virtual bool Activate() const OVERRIDE;
150  virtual bool Close() const OVERRIDE;
151
152 private:
153  scoped_refptr<DevToolsAgentHost> agent_host_;
154  GURL favicon_url_;
155  base::TimeTicks last_activity_time_;
156};
157
158Target::Target(scoped_refptr<DevToolsAgentHost> agent_host)
159    : agent_host_(agent_host) {
160  if (WebContents* web_contents = agent_host_->GetWebContents()) {
161    content::NavigationController& controller = web_contents->GetController();
162    content::NavigationEntry* entry = controller.GetActiveEntry();
163    if (entry != NULL && entry->GetURL().is_valid())
164      favicon_url_ = entry->GetFavicon().url;
165    last_activity_time_ = web_contents->GetLastActiveTime();
166  }
167}
168
169bool Target::Activate() const {
170  return agent_host_->Activate();
171}
172
173bool Target::Close() const {
174  return agent_host_->Close();
175}
176
177}  // namespace
178
179namespace content {
180
181// ShellDevToolsDelegate ----------------------------------------------------
182
183ShellDevToolsDelegate::ShellDevToolsDelegate(BrowserContext* browser_context)
184    : browser_context_(browser_context) {
185  std::string frontend_url;
186#if defined(OS_ANDROID)
187  frontend_url = base::StringPrintf(kFrontEndURL, GetWebKitRevision().c_str());
188#endif
189  devtools_http_handler_ =
190      DevToolsHttpHandler::Start(CreateSocketFactory(), frontend_url, this,
191                                 base::FilePath());
192}
193
194ShellDevToolsDelegate::~ShellDevToolsDelegate() {
195}
196
197void ShellDevToolsDelegate::Stop() {
198  // The call below destroys this.
199  devtools_http_handler_->Stop();
200}
201
202std::string ShellDevToolsDelegate::GetDiscoveryPageHTML() {
203#if defined(OS_ANDROID)
204  return std::string();
205#else
206  return ResourceBundle::GetSharedInstance().GetRawDataResource(
207      IDR_CONTENT_SHELL_DEVTOOLS_DISCOVERY_PAGE).as_string();
208#endif
209}
210
211bool ShellDevToolsDelegate::BundlesFrontendResources() {
212#if defined(OS_ANDROID)
213  return false;
214#else
215  return true;
216#endif
217}
218
219base::FilePath ShellDevToolsDelegate::GetDebugFrontendDir() {
220  return base::FilePath();
221}
222
223scoped_ptr<net::StreamListenSocket>
224ShellDevToolsDelegate::CreateSocketForTethering(
225    net::StreamListenSocket::Delegate* delegate,
226    std::string* name) {
227  return scoped_ptr<net::StreamListenSocket>();
228}
229
230// ShellDevToolsManagerDelegate ----------------------------------------------
231
232ShellDevToolsManagerDelegate::ShellDevToolsManagerDelegate(
233    BrowserContext* browser_context)
234    : browser_context_(browser_context) {
235}
236
237ShellDevToolsManagerDelegate::~ShellDevToolsManagerDelegate() {
238}
239
240base::DictionaryValue* ShellDevToolsManagerDelegate::HandleCommand(
241    DevToolsAgentHost* agent_host,
242    base::DictionaryValue* command) {
243  return NULL;
244}
245
246std::string ShellDevToolsManagerDelegate::GetPageThumbnailData(
247    const GURL& url) {
248  return std::string();
249}
250
251scoped_ptr<DevToolsTarget>
252ShellDevToolsManagerDelegate::CreateNewTarget(const GURL& url) {
253  Shell* shell = Shell::CreateNewWindow(browser_context_,
254                                        url,
255                                        NULL,
256                                        MSG_ROUTING_NONE,
257                                        gfx::Size());
258  return scoped_ptr<DevToolsTarget>(
259      new Target(DevToolsAgentHost::GetOrCreateFor(shell->web_contents())));
260}
261
262void ShellDevToolsManagerDelegate::EnumerateTargets(TargetCallback callback) {
263  TargetList targets;
264  content::DevToolsAgentHost::List agents =
265      content::DevToolsAgentHost::GetOrCreateAll();
266  for (content::DevToolsAgentHost::List::iterator it = agents.begin();
267       it != agents.end(); ++it) {
268    targets.push_back(new Target(*it));
269  }
270  callback.Run(targets);
271}
272
273}  // namespace content
274