Home | History | Annotate | Download | only in native
      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 "android_webview/native/aw_contents_io_thread_client_impl.h"
      6 
      7 #include <map>
      8 #include <utility>
      9 
     10 #include "android_webview/native/intercepted_request_data_impl.h"
     11 #include "base/android/jni_helper.h"
     12 #include "base/android/jni_string.h"
     13 #include "base/lazy_instance.h"
     14 #include "base/memory/linked_ptr.h"
     15 #include "base/memory/scoped_ptr.h"
     16 #include "base/synchronization/lock.h"
     17 #include "content/public/browser/browser_thread.h"
     18 #include "content/public/browser/render_process_host.h"
     19 #include "content/public/browser/render_view_host.h"
     20 #include "content/public/browser/resource_request_info.h"
     21 #include "content/public/browser/web_contents.h"
     22 #include "content/public/browser/web_contents_observer.h"
     23 #include "jni/AwContentsIoThreadClient_jni.h"
     24 #include "net/url_request/url_request.h"
     25 #include "url/gurl.h"
     26 
     27 using base::android::AttachCurrentThread;
     28 using base::android::ConvertUTF8ToJavaString;
     29 using base::android::JavaRef;
     30 using base::android::ScopedJavaLocalRef;
     31 using base::LazyInstance;
     32 using content::BrowserThread;
     33 using content::RenderViewHost;
     34 using content::WebContents;
     35 using std::map;
     36 using std::pair;
     37 
     38 namespace android_webview {
     39 
     40 namespace {
     41 
     42 struct IoThreadClientData {
     43   bool pending_association;
     44   JavaObjectWeakGlobalRef io_thread_client;
     45 
     46   IoThreadClientData();
     47 };
     48 
     49 IoThreadClientData::IoThreadClientData() : pending_association(false) {}
     50 
     51 typedef map<pair<int, int>, IoThreadClientData>
     52     RenderViewHostToIoThreadClientType;
     53 
     54 static pair<int, int> GetRenderViewHostIdPair(RenderViewHost* rvh) {
     55   return pair<int, int>(rvh->GetProcess()->GetID(), rvh->GetRoutingID());
     56 }
     57 
     58 // RvhToIoThreadClientMap -----------------------------------------------------
     59 class RvhToIoThreadClientMap {
     60  public:
     61   static RvhToIoThreadClientMap* GetInstance();
     62   void Set(pair<int, int> rvh_id, const IoThreadClientData& client);
     63   bool Get(pair<int, int> rvh_id, IoThreadClientData* client);
     64   void Erase(pair<int, int> rvh_id);
     65 
     66  private:
     67   static LazyInstance<RvhToIoThreadClientMap> g_instance_;
     68   base::Lock map_lock_;
     69   RenderViewHostToIoThreadClientType rvh_to_io_thread_client_;
     70 };
     71 
     72 // static
     73 LazyInstance<RvhToIoThreadClientMap> RvhToIoThreadClientMap::g_instance_ =
     74     LAZY_INSTANCE_INITIALIZER;
     75 
     76 // static
     77 RvhToIoThreadClientMap* RvhToIoThreadClientMap::GetInstance() {
     78   return g_instance_.Pointer();
     79 }
     80 
     81 void RvhToIoThreadClientMap::Set(pair<int, int> rvh_id,
     82                                  const IoThreadClientData& client) {
     83   base::AutoLock lock(map_lock_);
     84   rvh_to_io_thread_client_[rvh_id] = client;
     85 }
     86 
     87 bool RvhToIoThreadClientMap::Get(
     88     pair<int, int> rvh_id, IoThreadClientData* client) {
     89   base::AutoLock lock(map_lock_);
     90   RenderViewHostToIoThreadClientType::iterator iterator =
     91       rvh_to_io_thread_client_.find(rvh_id);
     92   if (iterator == rvh_to_io_thread_client_.end())
     93     return false;
     94 
     95   *client = iterator->second;
     96   return true;
     97 }
     98 
     99 void RvhToIoThreadClientMap::Erase(pair<int, int> rvh_id) {
    100   base::AutoLock lock(map_lock_);
    101   rvh_to_io_thread_client_.erase(rvh_id);
    102 }
    103 
    104 // ClientMapEntryUpdater ------------------------------------------------------
    105 
    106 class ClientMapEntryUpdater : public content::WebContentsObserver {
    107  public:
    108   ClientMapEntryUpdater(JNIEnv* env, WebContents* web_contents,
    109                         jobject jdelegate);
    110 
    111   virtual void RenderViewCreated(RenderViewHost* render_view_host) OVERRIDE;
    112   virtual void RenderViewDeleted(RenderViewHost* render_view_host) OVERRIDE;
    113   virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE;
    114 
    115  private:
    116   JavaObjectWeakGlobalRef jdelegate_;
    117 };
    118 
    119 ClientMapEntryUpdater::ClientMapEntryUpdater(JNIEnv* env,
    120                                              WebContents* web_contents,
    121                                              jobject jdelegate)
    122     : content::WebContentsObserver(web_contents),
    123       jdelegate_(env, jdelegate) {
    124   DCHECK(web_contents);
    125   DCHECK(jdelegate);
    126 
    127   if (web_contents->GetRenderViewHost())
    128     RenderViewCreated(web_contents->GetRenderViewHost());
    129 }
    130 
    131 void ClientMapEntryUpdater::RenderViewCreated(RenderViewHost* rvh) {
    132   IoThreadClientData client_data;
    133   client_data.io_thread_client = jdelegate_;
    134   client_data.pending_association = false;
    135   RvhToIoThreadClientMap::GetInstance()->Set(
    136       GetRenderViewHostIdPair(rvh), client_data);
    137 }
    138 
    139 void ClientMapEntryUpdater::RenderViewDeleted(RenderViewHost* rvh) {
    140   RvhToIoThreadClientMap::GetInstance()->Erase(GetRenderViewHostIdPair(rvh));
    141 }
    142 
    143 void ClientMapEntryUpdater::WebContentsDestroyed(WebContents* web_contents) {
    144   delete this;
    145 }
    146 
    147 } // namespace
    148 
    149 // AwContentsIoThreadClientImpl -----------------------------------------------
    150 
    151 // static
    152 scoped_ptr<AwContentsIoThreadClient>
    153 AwContentsIoThreadClient::FromID(int render_process_id, int render_view_id) {
    154   pair<int, int> rvh_id(render_process_id, render_view_id);
    155   IoThreadClientData client_data;
    156   if (!RvhToIoThreadClientMap::GetInstance()->Get(rvh_id, &client_data))
    157     return scoped_ptr<AwContentsIoThreadClient>();
    158 
    159   JNIEnv* env = AttachCurrentThread();
    160   ScopedJavaLocalRef<jobject> java_delegate =
    161       client_data.io_thread_client.get(env);
    162   DCHECK(!client_data.pending_association || java_delegate.is_null());
    163   return scoped_ptr<AwContentsIoThreadClient>(new AwContentsIoThreadClientImpl(
    164       client_data.pending_association, java_delegate));
    165 }
    166 
    167 // static
    168 void AwContentsIoThreadClientImpl::RegisterPendingContents(
    169     WebContents* web_contents) {
    170   IoThreadClientData client_data;
    171   client_data.pending_association = true;
    172   RvhToIoThreadClientMap::GetInstance()->Set(
    173       GetRenderViewHostIdPair(web_contents->GetRenderViewHost()), client_data);
    174 }
    175 
    176 // static
    177 void AwContentsIoThreadClientImpl::Associate(
    178     WebContents* web_contents,
    179     const JavaRef<jobject>& jclient) {
    180   JNIEnv* env = AttachCurrentThread();
    181   // The ClientMapEntryUpdater lifespan is tied to the WebContents.
    182   new ClientMapEntryUpdater(env, web_contents, jclient.obj());
    183 }
    184 
    185 AwContentsIoThreadClientImpl::AwContentsIoThreadClientImpl(
    186     bool pending_association,
    187     const JavaRef<jobject>& obj)
    188   : pending_association_(pending_association),
    189     java_object_(obj) {
    190 }
    191 
    192 AwContentsIoThreadClientImpl::~AwContentsIoThreadClientImpl() {
    193   // explict, out-of-line destructor.
    194 }
    195 
    196 bool AwContentsIoThreadClientImpl::PendingAssociation() const {
    197   return pending_association_;
    198 }
    199 
    200 AwContentsIoThreadClient::CacheMode
    201 AwContentsIoThreadClientImpl::GetCacheMode() const {
    202   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    203   if (java_object_.is_null())
    204     return AwContentsIoThreadClient::LOAD_DEFAULT;
    205 
    206   JNIEnv* env = AttachCurrentThread();
    207   return static_cast<AwContentsIoThreadClient::CacheMode>(
    208       Java_AwContentsIoThreadClient_getCacheMode(
    209           env, java_object_.obj()));
    210 }
    211 
    212 scoped_ptr<InterceptedRequestData>
    213 AwContentsIoThreadClientImpl::ShouldInterceptRequest(
    214     const GURL& location,
    215     const net::URLRequest* request) {
    216   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    217   if (java_object_.is_null())
    218     return scoped_ptr<InterceptedRequestData>();
    219   const content::ResourceRequestInfo* info =
    220       content::ResourceRequestInfo::ForRequest(request);
    221   bool is_main_frame = info &&
    222       info->GetResourceType() == ResourceType::MAIN_FRAME;
    223 
    224   JNIEnv* env = AttachCurrentThread();
    225   ScopedJavaLocalRef<jstring> jstring_url =
    226       ConvertUTF8ToJavaString(env, location.spec());
    227   ScopedJavaLocalRef<jobject> ret =
    228       Java_AwContentsIoThreadClient_shouldInterceptRequest(
    229           env, java_object_.obj(), jstring_url.obj(), is_main_frame);
    230   if (ret.is_null())
    231     return scoped_ptr<InterceptedRequestData>();
    232   return scoped_ptr<InterceptedRequestData>(
    233       new InterceptedRequestDataImpl(ret));
    234 }
    235 
    236 bool AwContentsIoThreadClientImpl::ShouldBlockContentUrls() const {
    237   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    238   if (java_object_.is_null())
    239     return false;
    240 
    241   JNIEnv* env = AttachCurrentThread();
    242   return Java_AwContentsIoThreadClient_shouldBlockContentUrls(
    243       env, java_object_.obj());
    244 }
    245 
    246 bool AwContentsIoThreadClientImpl::ShouldBlockFileUrls() const {
    247   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    248   if (java_object_.is_null())
    249     return false;
    250 
    251   JNIEnv* env = AttachCurrentThread();
    252   return Java_AwContentsIoThreadClient_shouldBlockFileUrls(
    253       env, java_object_.obj());
    254 }
    255 
    256 bool AwContentsIoThreadClientImpl::ShouldBlockNetworkLoads() const {
    257   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    258   if (java_object_.is_null())
    259     return false;
    260 
    261   JNIEnv* env = AttachCurrentThread();
    262   return Java_AwContentsIoThreadClient_shouldBlockNetworkLoads(
    263       env, java_object_.obj());
    264 }
    265 
    266 void AwContentsIoThreadClientImpl::NewDownload(
    267     const GURL& url,
    268     const std::string& user_agent,
    269     const std::string& content_disposition,
    270     const std::string& mime_type,
    271     int64 content_length) {
    272   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    273   if (java_object_.is_null())
    274     return;
    275 
    276   JNIEnv* env = AttachCurrentThread();
    277   ScopedJavaLocalRef<jstring> jstring_url =
    278       ConvertUTF8ToJavaString(env, url.spec());
    279   ScopedJavaLocalRef<jstring> jstring_user_agent =
    280       ConvertUTF8ToJavaString(env, user_agent);
    281   ScopedJavaLocalRef<jstring> jstring_content_disposition =
    282       ConvertUTF8ToJavaString(env, content_disposition);
    283   ScopedJavaLocalRef<jstring> jstring_mime_type =
    284       ConvertUTF8ToJavaString(env, mime_type);
    285 
    286   Java_AwContentsIoThreadClient_onDownloadStart(
    287       env,
    288       java_object_.obj(),
    289       jstring_url.obj(),
    290       jstring_user_agent.obj(),
    291       jstring_content_disposition.obj(),
    292       jstring_mime_type.obj(),
    293       content_length);
    294 }
    295 
    296 void AwContentsIoThreadClientImpl::NewLoginRequest(const std::string& realm,
    297                                                    const std::string& account,
    298                                                    const std::string& args) {
    299   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    300   if (java_object_.is_null())
    301     return;
    302 
    303   JNIEnv* env = AttachCurrentThread();
    304   ScopedJavaLocalRef<jstring> jrealm = ConvertUTF8ToJavaString(env, realm);
    305   ScopedJavaLocalRef<jstring> jargs = ConvertUTF8ToJavaString(env, args);
    306 
    307   ScopedJavaLocalRef<jstring> jaccount;
    308   if (!account.empty())
    309     jaccount = ConvertUTF8ToJavaString(env, account);
    310 
    311   Java_AwContentsIoThreadClient_newLoginRequest(
    312       env, java_object_.obj(), jrealm.obj(), jaccount.obj(), jargs.obj());
    313 }
    314 
    315 bool RegisterAwContentsIoThreadClientImpl(JNIEnv* env) {
    316   return RegisterNativesImpl(env);
    317 }
    318 
    319 } // namespace android_webview
    320