Fix infinite recursion on hiding panel when created during fullscreen mode.
[chromium-blink-merge.git] / chrome / browser / android / dev_tools_server.cc
blobfdad1397ce0432c7b5e78a39f0c0f2ced0768ae0
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 "content/public/common/user_agent.h"
41 #include "grit/browser_resources.h"
42 #include "jni/DevToolsServer_jni.h"
43 #include "net/socket/unix_domain_socket_posix.h"
44 #include "net/url_request/url_request_context_getter.h"
45 #include "ui/base/resource/resource_bundle.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->GetLastActiveTime()) {
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 ChromeShell.
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(data->front_as<char>(), data->size());
290 return "";
293 virtual scoped_ptr<content::DevToolsTarget> CreateNewTarget(
294 const GURL& url) OVERRIDE {
295 if (TabModelList::empty())
296 return scoped_ptr<content::DevToolsTarget>();
297 TabModel* tab_model = TabModelList::get(0);
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 TabAndroid* tab = TabAndroid::FromWebContents(web_contents);
305 if (!tab)
306 return scoped_ptr<content::DevToolsTarget>();
308 return scoped_ptr<content::DevToolsTarget>(
309 TabTarget::CreateForWebContents(tab->GetAndroidId(), web_contents));
312 virtual void EnumerateTargets(TargetCallback callback) OVERRIDE {
313 TargetList targets;
315 // Enumerate existing tabs, including the ones with no WebContents.
316 std::set<WebContents*> tab_web_contents;
317 for (TabModelList::const_iterator iter = TabModelList::begin();
318 iter != TabModelList::end(); ++iter) {
319 TabModel* model = *iter;
320 for (int i = 0; i < model->GetTabCount(); ++i) {
321 TabAndroid* tab = model->GetTabAt(i);
322 WebContents* web_contents = model->GetWebContentsAt(i);
323 if (web_contents) {
324 tab_web_contents.insert(web_contents);
325 targets.push_back(TabTarget::CreateForWebContents(tab->GetAndroidId(),
326 web_contents));
327 } else {
328 targets.push_back(TabTarget::CreateForUnloadedTab(tab->GetAndroidId(),
329 tab->GetTitle(),
330 tab->GetURL()));
335 // Add targets for WebContents not associated with any tabs.
336 std::vector<RenderViewHost*> rvh_list =
337 DevToolsAgentHost::GetValidRenderViewHosts();
338 for (std::vector<RenderViewHost*>::iterator it = rvh_list.begin();
339 it != rvh_list.end(); ++it) {
340 WebContents* web_contents = WebContents::FromRenderViewHost(*it);
341 if (!web_contents)
342 continue;
343 if (tab_web_contents.find(web_contents) != tab_web_contents.end())
344 continue;
345 targets.push_back(new NonTabTarget(web_contents));
348 callback.Run(targets);
351 virtual scoped_ptr<net::StreamListenSocket> CreateSocketForTethering(
352 net::StreamListenSocket::Delegate* delegate,
353 std::string* name) OVERRIDE {
354 *name = base::StringPrintf(
355 kTetheringSocketName, getpid(), ++last_tethering_socket_);
356 return net::UnixDomainSocket::CreateAndListenWithAbstractNamespace(
357 *name,
359 delegate,
360 base::Bind(&content::CanUserConnectToDevTools))
361 .PassAs<net::StreamListenSocket>();
364 private:
365 static void PopulatePageThumbnails() {
366 Profile* profile =
367 ProfileManager::GetLastUsedProfile()->GetOriginalProfile();
368 history::TopSites* top_sites = profile->GetTopSites();
369 if (top_sites)
370 top_sites->SyncWithHistory();
373 int last_tethering_socket_;
375 DISALLOW_COPY_AND_ASSIGN(DevToolsServerDelegate);
378 } // namespace
380 DevToolsServer::DevToolsServer()
381 : socket_name_(base::StringPrintf(kDevToolsChannelNameFormat,
382 kDefaultSocketNamePrefix)),
383 protocol_handler_(NULL) {
384 // Override the default socket name if one is specified on the command line.
385 const CommandLine& command_line = *CommandLine::ForCurrentProcess();
386 if (command_line.HasSwitch(switches::kRemoteDebuggingSocketName)) {
387 socket_name_ = command_line.GetSwitchValueASCII(
388 switches::kRemoteDebuggingSocketName);
392 DevToolsServer::DevToolsServer(const std::string& socket_name_prefix)
393 : socket_name_(base::StringPrintf(kDevToolsChannelNameFormat,
394 socket_name_prefix.c_str())),
395 protocol_handler_(NULL) {
396 // Override the socket name if one is specified on the command line.
397 const CommandLine& command_line = *CommandLine::ForCurrentProcess();
398 if (command_line.HasSwitch(switches::kRemoteDebuggingSocketName)) {
399 socket_name_ = command_line.GetSwitchValueASCII(
400 switches::kRemoteDebuggingSocketName);
404 DevToolsServer::~DevToolsServer() {
405 Stop();
408 void DevToolsServer::Start() {
409 if (protocol_handler_)
410 return;
412 protocol_handler_ = content::DevToolsHttpHandler::Start(
413 new net::UnixDomainSocketWithAbstractNamespaceFactory(
414 socket_name_,
415 base::StringPrintf("%s_%d", socket_name_.c_str(), getpid()),
416 base::Bind(&content::CanUserConnectToDevTools)),
417 base::StringPrintf(kFrontEndURL, content::GetWebKitRevision().c_str()),
418 new DevToolsServerDelegate());
421 void DevToolsServer::Stop() {
422 if (!protocol_handler_)
423 return;
424 // Note that the call to Stop() below takes care of |protocol_handler_|
425 // deletion.
426 protocol_handler_->Stop();
427 protocol_handler_ = NULL;
430 bool DevToolsServer::IsStarted() const {
431 return protocol_handler_;
434 bool RegisterDevToolsServer(JNIEnv* env) {
435 return RegisterNativesImpl(env);
438 static jlong InitRemoteDebugging(JNIEnv* env,
439 jobject obj,
440 jstring socket_name_prefix) {
441 DevToolsServer* server = new DevToolsServer(
442 base::android::ConvertJavaStringToUTF8(env, socket_name_prefix));
443 return reinterpret_cast<intptr_t>(server);
446 static void DestroyRemoteDebugging(JNIEnv* env, jobject obj, jlong server) {
447 delete reinterpret_cast<DevToolsServer*>(server);
450 static jboolean IsRemoteDebuggingEnabled(JNIEnv* env,
451 jobject obj,
452 jlong server) {
453 return reinterpret_cast<DevToolsServer*>(server)->IsStarted();
456 static void SetRemoteDebuggingEnabled(JNIEnv* env,
457 jobject obj,
458 jlong server,
459 jboolean enabled) {
460 DevToolsServer* devtools_server = reinterpret_cast<DevToolsServer*>(server);
461 if (enabled) {
462 devtools_server->Start();
463 } else {
464 devtools_server->Stop();