1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Copyright 2013 The Chromium Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
6 #include "xwalk/runtime/browser/android/xwalk_dev_tools_server.h"
12 #include "base/android/jni_string.h"
13 #include "base/basictypes.h"
14 #include "base/bind.h"
15 #include "base/callback.h"
16 #include "base/compiler_specific.h"
17 #include "base/logging.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "content/public/browser/android/devtools_auth.h"
21 #include "content/public/browser/devtools_agent_host.h"
22 #include "content/public/browser/devtools_http_handler.h"
23 #include "content/public/browser/devtools_http_handler_delegate.h"
24 #include "content/public/browser/devtools_manager_delegate.h"
25 #include "content/public/browser/devtools_target.h"
26 #include "content/public/browser/favicon_status.h"
27 #include "content/public/browser/navigation_entry.h"
28 #include "content/public/browser/render_view_host.h"
29 #include "content/public/browser/web_contents.h"
30 #include "content/public/browser/web_contents_delegate.h"
31 #include "content/public/common/user_agent.h"
32 #include "grit/xwalk_resources.h"
33 #include "jni/XWalkDevToolsServer_jni.h"
34 #include "net/socket/unix_domain_listen_socket_posix.h"
35 #include "ui/base/resource/resource_bundle.h"
37 using content::DevToolsAgentHost;
38 using content::RenderViewHost;
39 using content::WebContents;
43 // FIXME(girish): The frontend URL needs to be served from the domain below
44 // for remote debugging to work in chrome (see chrome's devtools_ui.cc).
45 // Currently, the chrome version is hardcoded because of this dependancy.
46 const char kFrontEndURL[] =
47 "http://chrome-devtools-frontend.appspot.com/serve_rev/%s/devtools.html";
48 const char kTargetTypePage[] = "page";
49 const char kTargetTypeServiceWorker[] = "service_worker";
50 const char kTargetTypeOther[] = "other";
52 bool AuthorizeSocketAccessWithDebugPermission(
53 const net::UnixDomainServerSocket::Credentials& credentials) {
54 JNIEnv* env = base::android::AttachCurrentThread();
55 return xwalk::Java_XWalkDevToolsServer_checkDebugPermission(
56 env, base::android::GetApplicationContext(),
57 credentials.process_id, credentials.user_id) ||
58 content::CanUserConnectToDevTools(credentials);
61 class Target : public content::DevToolsTarget {
63 explicit Target(scoped_refptr<content::DevToolsAgentHost> agent_host);
65 virtual std::string GetId() const OVERRIDE { return agent_host_->GetId(); }
66 virtual std::string GetType() const OVERRIDE {
67 switch (agent_host_->GetType()) {
68 case content::DevToolsAgentHost::TYPE_WEB_CONTENTS:
69 return kTargetTypePage;
70 case content::DevToolsAgentHost::TYPE_SERVICE_WORKER:
71 return kTargetTypeServiceWorker;
75 return kTargetTypeOther;
77 virtual std::string GetTitle() const OVERRIDE {
78 return agent_host_->GetTitle();
81 // TODO(hmin): Get the description about web contents view.
82 virtual std::string GetDescription() const OVERRIDE { return std::string(); }
83 virtual GURL GetURL() const OVERRIDE { return url_; }
84 virtual GURL GetFaviconURL() const OVERRIDE { return GURL(); }
85 virtual base::TimeTicks GetLastActivityTime() const OVERRIDE {
86 return last_activity_time_;
88 virtual std::string GetParentId() const OVERRIDE { return std::string(); }
89 virtual bool IsAttached() const OVERRIDE {
90 return agent_host_->IsAttached();
92 virtual scoped_refptr<DevToolsAgentHost> GetAgentHost() const OVERRIDE {
96 virtual bool Activate() const OVERRIDE {
97 WebContents* web_contents = agent_host_->GetWebContents();
100 web_contents->GetDelegate()->ActivateContents(web_contents);
104 virtual bool Close() const OVERRIDE { return false; }
107 scoped_refptr<DevToolsAgentHost> agent_host_;
112 base::TimeTicks last_activity_time_;
115 Target::Target(scoped_refptr<content::DevToolsAgentHost> agent_host)
116 : agent_host_(agent_host) {
117 if (content::WebContents* web_contents = agent_host_->GetWebContents()) {
118 content::NavigationController& controller = web_contents->GetController();
119 content::NavigationEntry* entry = controller.GetActiveEntry();
120 if (entry != NULL && entry->GetURL().is_valid())
121 favicon_url_ = entry->GetFavicon().url;
122 last_activity_time_ = web_contents->GetLastActiveTime();
126 // Delegate implementation for the devtools http handler on android. A new
127 // instance of this gets created each time devtools is enabled.
128 class XWalkDevToolsHttpHandlerDelegate
129 : public content::DevToolsHttpHandlerDelegate {
131 explicit XWalkDevToolsHttpHandlerDelegate(
132 const net::UnixDomainServerSocket::AuthCallback& auth_callback)
133 : auth_callback_(auth_callback) {
136 virtual std::string GetDiscoveryPageHTML() OVERRIDE {
137 return ResourceBundle::GetSharedInstance().GetRawDataResource(
138 IDR_DEVTOOLS_FRONTEND_PAGE_HTML).as_string();
141 virtual bool BundlesFrontendResources() OVERRIDE {
145 virtual base::FilePath GetDebugFrontendDir() OVERRIDE {
146 return base::FilePath();
149 virtual scoped_ptr<net::StreamListenSocket> CreateSocketForTethering(
150 net::StreamListenSocket::Delegate* delegate,
151 std::string* name) OVERRIDE {
152 return scoped_ptr<net::StreamListenSocket>();
155 const net::UnixDomainServerSocket::AuthCallback auth_callback_;
156 DISALLOW_COPY_AND_ASSIGN(XWalkDevToolsHttpHandlerDelegate);
159 class XWalkDevToolsDelegate
160 : public content::DevToolsManagerDelegate {
162 virtual std::string GetPageThumbnailData(const GURL& url) OVERRIDE {
163 return std::string();
166 virtual scoped_ptr<content::DevToolsTarget> CreateNewTarget(
167 const GURL&) OVERRIDE {
168 return scoped_ptr<content::DevToolsTarget>();
170 virtual void EnumerateTargets(TargetCallback callback) OVERRIDE {
172 content::DevToolsAgentHost::List agents =
173 content::DevToolsAgentHost::GetOrCreateAll();
174 for (content::DevToolsAgentHost::List::iterator it = agents.begin();
175 it != agents.end(); ++it) {
176 targets.push_back(new Target(*it));
178 callback.Run(targets);
182 // Factory for UnixDomainServerSocket.
183 class UnixDomainServerSocketFactory
184 : public content::DevToolsHttpHandler::ServerSocketFactory {
186 explicit UnixDomainServerSocketFactory(const std::string& socket_name)
187 : content::DevToolsHttpHandler::ServerSocketFactory(socket_name, 0, 1) {}
190 // content::DevToolsHttpHandler::ServerSocketFactory.
191 virtual scoped_ptr<net::ServerSocket> Create() const OVERRIDE {
192 return scoped_ptr<net::ServerSocket>(
193 new net::UnixDomainServerSocket(
194 base::Bind(&content::CanUserConnectToDevTools),
195 true /* use_abstract_namespace */));
198 DISALLOW_COPY_AND_ASSIGN(UnixDomainServerSocketFactory);
205 XWalkDevToolsServer::XWalkDevToolsServer(const std::string& socket_name)
206 : socket_name_(socket_name),
207 protocol_handler_(NULL),
211 XWalkDevToolsServer::~XWalkDevToolsServer() {
215 // Allow connection from uid specified using AllowConnectionFromUid to devtools
216 // server. This supports the XDK usage: debug bridge wrapper runs in a separate
217 // process and connects to the devtools server.
218 bool XWalkDevToolsServer::CanUserConnectToDevTools(
219 const net::UnixDomainServerSocket::Credentials& credentials) {
220 if (credentials.user_id == allowed_uid_)
222 return content::CanUserConnectToDevTools(credentials);
225 void XWalkDevToolsServer::Start(bool allow_debug_permission) {
226 if (protocol_handler_)
229 net::UnixDomainServerSocket::AuthCallback auth_callback =
230 allow_debug_permission ?
231 base::Bind(&AuthorizeSocketAccessWithDebugPermission) :
232 base::Bind(&content::CanUserConnectToDevTools);
234 scoped_ptr<content::DevToolsHttpHandler::ServerSocketFactory> factory(
235 new UnixDomainServerSocketFactory(socket_name_));
236 protocol_handler_ = content::DevToolsHttpHandler::Start(
238 base::StringPrintf(kFrontEndURL, content::GetWebKitRevision().c_str()),
239 new XWalkDevToolsHttpHandlerDelegate(auth_callback), base::FilePath());
242 void XWalkDevToolsServer::Stop() {
243 if (!protocol_handler_)
245 // Note that the call to Stop() below takes care of |protocol_handler_|
247 protocol_handler_->Stop();
248 protocol_handler_ = NULL;
251 bool XWalkDevToolsServer::IsStarted() const {
252 return protocol_handler_;
255 void XWalkDevToolsServer::AllowConnectionFromUid(uid_t uid) {
259 bool RegisterXWalkDevToolsServer(JNIEnv* env) {
260 return RegisterNativesImpl(env);
263 static jlong InitRemoteDebugging(JNIEnv* env,
265 jstring socketName) {
266 XWalkDevToolsServer* server = new XWalkDevToolsServer(
267 base::android::ConvertJavaStringToUTF8(env, socketName));
268 return reinterpret_cast<intptr_t>(server);
271 static void DestroyRemoteDebugging(JNIEnv* env, jobject obj, jlong server) {
272 delete reinterpret_cast<XWalkDevToolsServer*>(server);
275 static jboolean IsRemoteDebuggingEnabled(JNIEnv* env,
278 return reinterpret_cast<XWalkDevToolsServer*>(server)->IsStarted();
281 static void SetRemoteDebuggingEnabled(JNIEnv* env,
285 jboolean allow_debug_permission) {
286 XWalkDevToolsServer* devtools_server =
287 reinterpret_cast<XWalkDevToolsServer*>(server);
289 devtools_server->Start(allow_debug_permission);
291 devtools_server->Stop();
295 static void AllowConnectionFromUid(JNIEnv* env,
299 XWalkDevToolsServer* devtools_server =
300 reinterpret_cast<XWalkDevToolsServer*>(server);
301 devtools_server->AllowConnectionFromUid((uid_t) uid);