Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / android / dev_tools_server.cc
blob3318a70b1715867d5c70117dee6e9ee7e765d206
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.
5 #include "chrome/browser/android/dev_tools_server.h"
7 #include <pwd.h>
8 #include <cstring>
10 #include "base/android/jni_string.h"
11 #include "base/basictypes.h"
12 #include "base/bind.h"
13 #include "base/callback.h"
14 #include "base/command_line.h"
15 #include "base/compiler_specific.h"
16 #include "base/logging.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "chrome/browser/android/tab_android.h"
21 #include "chrome/browser/browser_process.h"
22 #include "chrome/browser/devtools/devtools_adb_bridge.h"
23 #include "chrome/browser/history/top_sites.h"
24 #include "chrome/browser/profiles/profile_manager.h"
25 #include "chrome/browser/ui/android/tab_model/tab_model.h"
26 #include "chrome/browser/ui/android/tab_model/tab_model_list.h"
27 #include "content/public/browser/android/devtools_auth.h"
28 #include "content/public/browser/browser_thread.h"
29 #include "content/public/browser/devtools_agent_host.h"
30 #include "content/public/browser/devtools_http_handler.h"
31 #include "content/public/browser/devtools_http_handler_delegate.h"
32 #include "content/public/browser/devtools_target.h"
33 #include "content/public/browser/favicon_status.h"
34 #include "content/public/browser/navigation_entry.h"
35 #include "content/public/browser/render_view_host.h"
36 #include "content/public/browser/web_contents.h"
37 #include "content/public/browser/web_contents_delegate.h"
38 #include "content/public/common/content_switches.h"
39 #include "content/public/common/url_constants.h"
40 #include "grit/devtools_discovery_page_resources.h"
41 #include "jni/DevToolsServer_jni.h"
42 #include "net/socket/unix_domain_socket_posix.h"
43 #include "net/url_request/url_request_context_getter.h"
44 #include "ui/base/resource/resource_bundle.h"
45 #include "webkit/common/user_agent/user_agent_util.h"
47 using content::DevToolsAgentHost;
48 using content::RenderViewHost;
49 using content::WebContents;
51 namespace {
53 const char kFrontEndURL[] =
54 "http://chrome-devtools-frontend.appspot.com/serve_rev/%s/devtools.html";
55 const char kDefaultSocketNamePrefix[] = "chrome";
56 const char kTetheringSocketName[] = "chrome_devtools_tethering_%d_%d";
58 const char kTargetTypePage[] = "page";
59 const char kTargetTypeOther[] = "other";
61 static GURL GetFaviconURL(WebContents* web_contents) {
62 content::NavigationController& controller = web_contents->GetController();
63 content::NavigationEntry* entry = controller.GetActiveEntry();
64 if (entry != NULL && entry->GetURL().is_valid())
65 return entry->GetFavicon().url;
66 return GURL();
69 class TargetBase : public content::DevToolsTarget {
70 public:
71 // content::DevToolsTarget implementation:
72 virtual std::string GetTitle() const OVERRIDE { return title_; }
74 virtual std::string GetDescription() const OVERRIDE { return std::string(); }
76 virtual GURL GetUrl() const OVERRIDE { return url_; }
78 virtual GURL GetFaviconUrl() const OVERRIDE { return favicon_url_; }
80 virtual base::TimeTicks GetLastActivityTime() const OVERRIDE {
81 return last_activity_time_;
84 protected:
85 explicit TargetBase(WebContents* web_contents)
86 : title_(base::UTF16ToUTF8(web_contents->GetTitle())),
87 url_(web_contents->GetURL()),
88 favicon_url_(GetFaviconURL(web_contents)),
89 last_activity_time_(web_contents->GetLastSelectedTime()) {
92 TargetBase(const base::string16& title, const GURL& url)
93 : title_(base::UTF16ToUTF8(title)),
94 url_(url)
97 private:
98 const std::string title_;
99 const GURL url_;
100 const GURL favicon_url_;
101 const base::TimeTicks last_activity_time_;
104 class TabTarget : public TargetBase {
105 public:
106 static TabTarget* CreateForWebContents(int tab_id,
107 WebContents* web_contents) {
108 return new TabTarget(tab_id, web_contents);
111 static TabTarget* CreateForUnloadedTab(int tab_id,
112 const base::string16& title,
113 const GURL& url) {
114 return new TabTarget(tab_id, title, url);
117 // content::DevToolsTarget implementation:
118 virtual std::string GetId() const OVERRIDE {
119 return base::IntToString(tab_id_);
122 virtual std::string GetType() const OVERRIDE { return kTargetTypePage; }
124 virtual bool IsAttached() const OVERRIDE {
125 TabModel* model;
126 int index;
127 if (!FindTab(&model, &index))
128 return false;
129 WebContents* web_contents = model->GetWebContentsAt(index);
130 if (!web_contents)
131 return false;
132 return DevToolsAgentHost::IsDebuggerAttached(web_contents);
135 virtual scoped_refptr<DevToolsAgentHost> GetAgentHost() const OVERRIDE {
136 TabModel* model;
137 int index;
138 if (!FindTab(&model, &index))
139 return NULL;
140 WebContents* web_contents = model->GetWebContentsAt(index);
141 if (!web_contents) {
142 // The tab has been pushed out of memory, pull it back.
143 TabAndroid* tab = model->GetTabAt(index);
144 tab->RestoreIfNeeded();
145 web_contents = model->GetWebContentsAt(index);
146 if (!web_contents)
147 return NULL;
149 RenderViewHost* rvh = web_contents->GetRenderViewHost();
150 return rvh ? DevToolsAgentHost::GetOrCreateFor(rvh) : NULL;
153 virtual bool Activate() const OVERRIDE {
154 TabModel* model;
155 int index;
156 if (!FindTab(&model, &index))
157 return false;
158 model->SetActiveIndex(index);
159 return true;
162 virtual bool Close() const OVERRIDE {
163 TabModel* model;
164 int index;
165 if (!FindTab(&model, &index))
166 return false;
167 model->CloseTabAt(index);
168 return true;
171 private:
172 TabTarget(int tab_id, WebContents* web_contents)
173 : TargetBase(web_contents),
174 tab_id_(tab_id) {
177 TabTarget(int tab_id, const base::string16& title, const GURL& url)
178 : TargetBase(title, url),
179 tab_id_(tab_id) {
182 bool FindTab(TabModel** model_result, int* index_result) const {
183 for (TabModelList::const_iterator iter = TabModelList::begin();
184 iter != TabModelList::end(); ++iter) {
185 TabModel* model = *iter;
186 for (int i = 0; i < model->GetTabCount(); ++i) {
187 TabAndroid* tab = model->GetTabAt(i);
188 if (tab->GetAndroidId() == tab_id_) {
189 *model_result = model;
190 *index_result = i;
191 return true;
195 return false;
198 const int tab_id_;
201 class NonTabTarget : public TargetBase {
202 public:
203 explicit NonTabTarget(WebContents* web_contents)
204 : TargetBase(web_contents),
205 agent_host_(DevToolsAgentHost::GetOrCreateFor(
206 web_contents->GetRenderViewHost())) {
209 // content::DevToolsTarget implementation:
210 virtual std::string GetId() const OVERRIDE {
211 return agent_host_->GetId();
214 virtual std::string GetType() const OVERRIDE {
215 if (TabModelList::begin() == TabModelList::end()) {
216 // If there are no tab models we must be running in ChromiumTestShell.
217 // Return the 'page' target type for backwards compatibility.
218 return kTargetTypePage;
220 return kTargetTypeOther;
223 virtual bool IsAttached() const OVERRIDE {
224 return agent_host_->IsAttached();
227 virtual scoped_refptr<DevToolsAgentHost> GetAgentHost() const OVERRIDE {
228 return agent_host_;
231 virtual bool Activate() const OVERRIDE {
232 RenderViewHost* rvh = agent_host_->GetRenderViewHost();
233 if (!rvh)
234 return false;
235 WebContents* web_contents = WebContents::FromRenderViewHost(rvh);
236 if (!web_contents)
237 return false;
238 web_contents->GetDelegate()->ActivateContents(web_contents);
239 return true;
242 virtual bool Close() const OVERRIDE {
243 RenderViewHost* rvh = agent_host_->GetRenderViewHost();
244 if (!rvh)
245 return false;
246 rvh->ClosePage();
247 return true;
250 private:
251 scoped_refptr<DevToolsAgentHost> agent_host_;
254 // Delegate implementation for the devtools http handler on android. A new
255 // instance of this gets created each time devtools is enabled.
256 class DevToolsServerDelegate : public content::DevToolsHttpHandlerDelegate {
257 public:
258 DevToolsServerDelegate()
259 : last_tethering_socket_(0) {
262 virtual std::string GetDiscoveryPageHTML() OVERRIDE {
263 // TopSites updates itself after a delay. Ask TopSites to update itself
264 // when we're about to show the remote debugging landing page.
265 content::BrowserThread::PostTask(
266 content::BrowserThread::UI,
267 FROM_HERE,
268 base::Bind(&DevToolsServerDelegate::PopulatePageThumbnails));
269 return ResourceBundle::GetSharedInstance().GetRawDataResource(
270 IDR_DEVTOOLS_DISCOVERY_PAGE_HTML).as_string();
273 virtual bool BundlesFrontendResources() OVERRIDE {
274 return false;
277 virtual base::FilePath GetDebugFrontendDir() OVERRIDE {
278 return base::FilePath();
281 virtual std::string GetPageThumbnailData(const GURL& url) OVERRIDE {
282 Profile* profile =
283 ProfileManager::GetLastUsedProfile()->GetOriginalProfile();
284 history::TopSites* top_sites = profile->GetTopSites();
285 if (top_sites) {
286 scoped_refptr<base::RefCountedMemory> data;
287 if (top_sites->GetPageThumbnail(url, false, &data))
288 return std::string(reinterpret_cast<const char*>(data->front()),
289 data->size());
291 return "";
294 virtual scoped_ptr<content::DevToolsTarget> CreateNewTarget(
295 const GURL& url) OVERRIDE {
296 Profile* profile = ProfileManager::GetActiveUserProfile();
297 TabModel* tab_model = TabModelList::GetTabModelWithProfile(profile);
298 if (!tab_model)
299 return scoped_ptr<content::DevToolsTarget>();
300 WebContents* web_contents = tab_model->CreateNewTabForDevTools(url);
301 if (!web_contents)
302 return scoped_ptr<content::DevToolsTarget>();
304 for (int i = 0; i < tab_model->GetTabCount(); ++i) {
305 if (web_contents != tab_model->GetWebContentsAt(i))
306 continue;
307 TabAndroid* tab = tab_model->GetTabAt(i);
308 return scoped_ptr<content::DevToolsTarget>(
309 TabTarget::CreateForWebContents(tab->GetAndroidId(), web_contents));
312 // Newly created tab not found, return no target.
313 return scoped_ptr<content::DevToolsTarget>();
316 virtual void EnumerateTargets(TargetCallback callback) OVERRIDE {
317 TargetList targets;
319 // Enumerate existing tabs, including the ones with no WebContents.
320 std::set<WebContents*> tab_web_contents;
321 for (TabModelList::const_iterator iter = TabModelList::begin();
322 iter != TabModelList::end(); ++iter) {
323 TabModel* model = *iter;
324 for (int i = 0; i < model->GetTabCount(); ++i) {
325 TabAndroid* tab = model->GetTabAt(i);
326 WebContents* web_contents = model->GetWebContentsAt(i);
327 if (web_contents) {
328 tab_web_contents.insert(web_contents);
329 targets.push_back(TabTarget::CreateForWebContents(tab->GetAndroidId(),
330 web_contents));
331 } else {
332 targets.push_back(TabTarget::CreateForUnloadedTab(tab->GetAndroidId(),
333 tab->GetTitle(),
334 tab->GetURL()));
339 // Add targets for WebContents not associated with any tabs.
340 std::vector<RenderViewHost*> rvh_list =
341 DevToolsAgentHost::GetValidRenderViewHosts();
342 for (std::vector<RenderViewHost*>::iterator it = rvh_list.begin();
343 it != rvh_list.end(); ++it) {
344 WebContents* web_contents = WebContents::FromRenderViewHost(*it);
345 if (!web_contents)
346 continue;
347 if (tab_web_contents.find(web_contents) != tab_web_contents.end())
348 continue;
349 targets.push_back(new NonTabTarget(web_contents));
352 callback.Run(targets);
355 virtual scoped_ptr<net::StreamListenSocket> CreateSocketForTethering(
356 net::StreamListenSocket::Delegate* delegate,
357 std::string* name) OVERRIDE {
358 *name = base::StringPrintf(
359 kTetheringSocketName, getpid(), ++last_tethering_socket_);
360 return net::UnixDomainSocket::CreateAndListenWithAbstractNamespace(
361 *name,
363 delegate,
364 base::Bind(&content::CanUserConnectToDevTools))
365 .PassAs<net::StreamListenSocket>();
368 private:
369 static void PopulatePageThumbnails() {
370 Profile* profile =
371 ProfileManager::GetLastUsedProfile()->GetOriginalProfile();
372 history::TopSites* top_sites = profile->GetTopSites();
373 if (top_sites)
374 top_sites->SyncWithHistory();
377 int last_tethering_socket_;
379 DISALLOW_COPY_AND_ASSIGN(DevToolsServerDelegate);
382 } // namespace
384 DevToolsServer::DevToolsServer()
385 : socket_name_(base::StringPrintf(kDevToolsChannelNameFormat,
386 kDefaultSocketNamePrefix)),
387 protocol_handler_(NULL) {
388 // Override the default socket name if one is specified on the command line.
389 const CommandLine& command_line = *CommandLine::ForCurrentProcess();
390 if (command_line.HasSwitch(switches::kRemoteDebuggingSocketName)) {
391 socket_name_ = command_line.GetSwitchValueASCII(
392 switches::kRemoteDebuggingSocketName);
396 DevToolsServer::DevToolsServer(const std::string& socket_name_prefix)
397 : socket_name_(base::StringPrintf(kDevToolsChannelNameFormat,
398 socket_name_prefix.c_str())),
399 protocol_handler_(NULL) {
400 // Override the socket name if one is specified on the command line.
401 const CommandLine& command_line = *CommandLine::ForCurrentProcess();
402 if (command_line.HasSwitch(switches::kRemoteDebuggingSocketName)) {
403 socket_name_ = command_line.GetSwitchValueASCII(
404 switches::kRemoteDebuggingSocketName);
408 DevToolsServer::~DevToolsServer() {
409 Stop();
412 void DevToolsServer::Start() {
413 if (protocol_handler_)
414 return;
416 protocol_handler_ = content::DevToolsHttpHandler::Start(
417 new net::UnixDomainSocketWithAbstractNamespaceFactory(
418 socket_name_,
419 base::StringPrintf("%s_%d", socket_name_.c_str(), getpid()),
420 base::Bind(&content::CanUserConnectToDevTools)),
421 base::StringPrintf(kFrontEndURL,
422 webkit_glue::GetWebKitRevision().c_str()),
423 new DevToolsServerDelegate());
426 void DevToolsServer::Stop() {
427 if (!protocol_handler_)
428 return;
429 // Note that the call to Stop() below takes care of |protocol_handler_|
430 // deletion.
431 protocol_handler_->Stop();
432 protocol_handler_ = NULL;
435 bool DevToolsServer::IsStarted() const {
436 return protocol_handler_;
439 bool RegisterDevToolsServer(JNIEnv* env) {
440 return RegisterNativesImpl(env);
443 static jint InitRemoteDebugging(JNIEnv* env,
444 jobject obj,
445 jstring socket_name_prefix) {
446 DevToolsServer* server = new DevToolsServer(
447 base::android::ConvertJavaStringToUTF8(env, socket_name_prefix));
448 return reinterpret_cast<jint>(server);
451 static void DestroyRemoteDebugging(JNIEnv* env, jobject obj, jint server) {
452 delete reinterpret_cast<DevToolsServer*>(server);
455 static jboolean IsRemoteDebuggingEnabled(JNIEnv* env,
456 jobject obj,
457 jint server) {
458 return reinterpret_cast<DevToolsServer*>(server)->IsStarted();
461 static void SetRemoteDebuggingEnabled(JNIEnv* env,
462 jobject obj,
463 jint server,
464 jboolean enabled) {
465 DevToolsServer* devtools_server = reinterpret_cast<DevToolsServer*>(server);
466 if (enabled) {
467 devtools_server->Start();
468 } else {
469 devtools_server->Stop();