Home | History | Annotate | Download | only in android
      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 #include "chrome/browser/android/dev_tools_server.h"
      6 
      7 #include <pwd.h>
      8 #include <cstring>
      9 
     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/stringprintf.h"
     18 #include "chrome/browser/browser_process.h"
     19 #include "chrome/browser/devtools/devtools_adb_bridge.h"
     20 #include "chrome/browser/history/top_sites.h"
     21 #include "chrome/browser/profiles/profile_manager.h"
     22 #include "chrome/browser/ui/android/tab_model/tab_model.h"
     23 #include "chrome/browser/ui/android/tab_model/tab_model_list.h"
     24 #include "content/public/browser/android/devtools_auth.h"
     25 #include "content/public/browser/browser_thread.h"
     26 #include "content/public/browser/devtools_http_handler.h"
     27 #include "content/public/browser/devtools_http_handler_delegate.h"
     28 #include "content/public/browser/web_contents.h"
     29 #include "content/public/common/content_switches.h"
     30 #include "content/public/common/url_constants.h"
     31 #include "grit/devtools_discovery_page_resources.h"
     32 #include "jni/DevToolsServer_jni.h"
     33 #include "net/socket/unix_domain_socket_posix.h"
     34 #include "net/url_request/url_request_context_getter.h"
     35 #include "ui/base/resource/resource_bundle.h"
     36 #include "webkit/common/user_agent/user_agent_util.h"
     37 
     38 namespace {
     39 
     40 const char kFrontEndURL[] =
     41     "http://chrome-devtools-frontend.appspot.com/serve_rev/%s/devtools.html";
     42 const char kDefaultSocketNamePrefix[] = "chrome";
     43 const char kTetheringSocketName[] = "chrome_devtools_tethering_%d_%d";
     44 
     45 // Delegate implementation for the devtools http handler on android. A new
     46 // instance of this gets created each time devtools is enabled.
     47 class DevToolsServerDelegate : public content::DevToolsHttpHandlerDelegate {
     48  public:
     49   DevToolsServerDelegate()
     50       : last_tethering_socket_(0) {
     51   }
     52 
     53   virtual std::string GetDiscoveryPageHTML() OVERRIDE {
     54     // TopSites updates itself after a delay. Ask TopSites to update itself
     55     // when we're about to show the remote debugging landing page.
     56     content::BrowserThread::PostTask(
     57         content::BrowserThread::UI,
     58         FROM_HERE,
     59         base::Bind(&DevToolsServerDelegate::PopulatePageThumbnails));
     60     return ResourceBundle::GetSharedInstance().GetRawDataResource(
     61         IDR_DEVTOOLS_DISCOVERY_PAGE_HTML).as_string();
     62   }
     63 
     64   virtual bool BundlesFrontendResources() OVERRIDE {
     65     return false;
     66   }
     67 
     68   virtual base::FilePath GetDebugFrontendDir() OVERRIDE {
     69     return base::FilePath();
     70   }
     71 
     72   virtual std::string GetPageThumbnailData(const GURL& url) OVERRIDE {
     73     Profile* profile =
     74         ProfileManager::GetLastUsedProfile()->GetOriginalProfile();
     75     history::TopSites* top_sites = profile->GetTopSites();
     76     if (top_sites) {
     77       scoped_refptr<base::RefCountedMemory> data;
     78       if (top_sites->GetPageThumbnail(url, &data))
     79         return std::string(reinterpret_cast<const char*>(data->front()),
     80                            data->size());
     81     }
     82     return "";
     83   }
     84 
     85   virtual content::RenderViewHost* CreateNewTarget() OVERRIDE {
     86     Profile* profile =
     87         g_browser_process->profile_manager()->GetDefaultProfile();
     88     TabModel* tab_model = TabModelList::GetTabModelWithProfile(profile);
     89     if (!tab_model)
     90       return NULL;
     91     content::WebContents* web_contents =
     92         tab_model->CreateTabForTesting(GURL(content::kAboutBlankURL));
     93     if (!web_contents)
     94       return NULL;
     95     return web_contents->GetRenderViewHost();
     96   }
     97 
     98   virtual TargetType GetTargetType(content::RenderViewHost*) OVERRIDE {
     99     return kTargetTypeTab;
    100   }
    101 
    102   virtual std::string GetViewDescription(content::RenderViewHost*) OVERRIDE {
    103     return "";
    104   }
    105 
    106   virtual scoped_refptr<net::StreamListenSocket> CreateSocketForTethering(
    107       net::StreamListenSocket::Delegate* delegate,
    108       std::string* name) OVERRIDE {
    109     *name = base::StringPrintf(
    110         kTetheringSocketName, getpid(), ++last_tethering_socket_);
    111     return net::UnixDomainSocket::CreateAndListenWithAbstractNamespace(
    112         *name,
    113         "",
    114         delegate,
    115         base::Bind(&content::CanUserConnectToDevTools));
    116   }
    117 
    118  private:
    119   static void PopulatePageThumbnails() {
    120     Profile* profile =
    121         ProfileManager::GetLastUsedProfile()->GetOriginalProfile();
    122     history::TopSites* top_sites = profile->GetTopSites();
    123     if (top_sites)
    124       top_sites->SyncWithHistory();
    125   }
    126 
    127   int last_tethering_socket_;
    128 
    129   DISALLOW_COPY_AND_ASSIGN(DevToolsServerDelegate);
    130 };
    131 
    132 }  // namespace
    133 
    134 DevToolsServer::DevToolsServer()
    135     : socket_name_(base::StringPrintf(kDevToolsChannelNameFormat,
    136                                       kDefaultSocketNamePrefix)),
    137       protocol_handler_(NULL) {
    138   // Override the default socket name if one is specified on the command line.
    139   const CommandLine& command_line = *CommandLine::ForCurrentProcess();
    140   if (command_line.HasSwitch(switches::kRemoteDebuggingSocketName)) {
    141     socket_name_ = command_line.GetSwitchValueASCII(
    142         switches::kRemoteDebuggingSocketName);
    143   }
    144 }
    145 
    146 DevToolsServer::DevToolsServer(const std::string& socket_name_prefix)
    147     : socket_name_(base::StringPrintf(kDevToolsChannelNameFormat,
    148                                       socket_name_prefix.c_str())),
    149       protocol_handler_(NULL) {
    150 }
    151 
    152 DevToolsServer::~DevToolsServer() {
    153   Stop();
    154 }
    155 
    156 void DevToolsServer::Start() {
    157   if (protocol_handler_)
    158     return;
    159 
    160   protocol_handler_ = content::DevToolsHttpHandler::Start(
    161       new net::UnixDomainSocketWithAbstractNamespaceFactory(
    162           socket_name_,
    163           base::StringPrintf("%s_%d", socket_name_.c_str(), getpid()),
    164           base::Bind(&content::CanUserConnectToDevTools)),
    165       base::StringPrintf(kFrontEndURL,
    166                          webkit_glue::GetWebKitRevision().c_str()),
    167       new DevToolsServerDelegate());
    168 }
    169 
    170 void DevToolsServer::Stop() {
    171   if (!protocol_handler_)
    172     return;
    173   // Note that the call to Stop() below takes care of |protocol_handler_|
    174   // deletion.
    175   protocol_handler_->Stop();
    176   protocol_handler_ = NULL;
    177 }
    178 
    179 bool DevToolsServer::IsStarted() const {
    180   return protocol_handler_;
    181 }
    182 
    183 bool RegisterDevToolsServer(JNIEnv* env) {
    184   return RegisterNativesImpl(env);
    185 }
    186 
    187 static jint InitRemoteDebugging(JNIEnv* env,
    188                                 jobject obj,
    189                                 jstring socket_name_prefix) {
    190   DevToolsServer* server = new DevToolsServer(
    191       base::android::ConvertJavaStringToUTF8(env, socket_name_prefix));
    192   return reinterpret_cast<jint>(server);
    193 }
    194 
    195 static void DestroyRemoteDebugging(JNIEnv* env, jobject obj, jint server) {
    196   delete reinterpret_cast<DevToolsServer*>(server);
    197 }
    198 
    199 static jboolean IsRemoteDebuggingEnabled(JNIEnv* env,
    200                                          jobject obj,
    201                                          jint server) {
    202   return reinterpret_cast<DevToolsServer*>(server)->IsStarted();
    203 }
    204 
    205 static void SetRemoteDebuggingEnabled(JNIEnv* env,
    206                                       jobject obj,
    207                                       jint server,
    208                                       jboolean enabled) {
    209   DevToolsServer* devtools_server = reinterpret_cast<DevToolsServer*>(server);
    210   if (enabled) {
    211     devtools_server->Start();
    212   } else {
    213     devtools_server->Stop();
    214   }
    215 }
    216