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 RenderViewForInterstitialPageCreated(
    113       RenderViewHost* render_view_host) OVERRIDE;
    114   virtual void RenderViewDeleted(RenderViewHost* render_view_host) OVERRIDE;
    115   virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE;
    116 
    117  private:
    118   JavaObjectWeakGlobalRef jdelegate_;
    119 };
    120 
    121 ClientMapEntryUpdater::ClientMapEntryUpdater(JNIEnv* env,
    122                                              WebContents* web_contents,
    123                                              jobject jdelegate)
    124     : content::WebContentsObserver(web_contents),
    125       jdelegate_(env, jdelegate) {
    126   DCHECK(web_contents);
    127   DCHECK(jdelegate);
    128 
    129   if (web_contents->GetRenderViewHost())
    130     RenderViewCreated(web_contents->GetRenderViewHost());
    131 }
    132 
    133 void ClientMapEntryUpdater::RenderViewCreated(RenderViewHost* rvh) {
    134   IoThreadClientData client_data;
    135   client_data.io_thread_client = jdelegate_;
    136   client_data.pending_association = false;
    137   RvhToIoThreadClientMap::GetInstance()->Set(
    138       GetRenderViewHostIdPair(rvh), client_data);
    139 }
    140 
    141 void ClientMapEntryUpdater::RenderViewForInterstitialPageCreated(
    142     RenderViewHost* rvh) {
    143   RenderViewCreated(rvh);
    144 }
    145 
    146 void ClientMapEntryUpdater::RenderViewDeleted(RenderViewHost* rvh) {
    147   RvhToIoThreadClientMap::GetInstance()->Erase(GetRenderViewHostIdPair(rvh));
    148 }
    149 
    150 void ClientMapEntryUpdater::WebContentsDestroyed(WebContents* web_contents) {
    151   if (web_contents->GetRenderViewHost())
    152     RenderViewDeleted(web_contents->GetRenderViewHost());
    153   delete this;
    154 }
    155 
    156 } // namespace
    157 
    158 // AwContentsIoThreadClientImpl -----------------------------------------------
    159 
    160 // static
    161 scoped_ptr<AwContentsIoThreadClient>
    162 AwContentsIoThreadClient::FromID(int render_process_id, int render_view_id) {
    163   pair<int, int> rvh_id(render_process_id, render_view_id);
    164   IoThreadClientData client_data;
    165   if (!RvhToIoThreadClientMap::GetInstance()->Get(rvh_id, &client_data))
    166     return scoped_ptr<AwContentsIoThreadClient>();
    167 
    168   JNIEnv* env = AttachCurrentThread();
    169   ScopedJavaLocalRef<jobject> java_delegate =
    170       client_data.io_thread_client.get(env);
    171   DCHECK(!client_data.pending_association || java_delegate.is_null());
    172   return scoped_ptr<AwContentsIoThreadClient>(new AwContentsIoThreadClientImpl(
    173       client_data.pending_association, java_delegate));
    174 }
    175 
    176 // static
    177 void AwContentsIoThreadClientImpl::RegisterPendingContents(
    178     WebContents* web_contents) {
    179   IoThreadClientData client_data;
    180   client_data.pending_association = true;
    181   RvhToIoThreadClientMap::GetInstance()->Set(
    182       GetRenderViewHostIdPair(web_contents->GetRenderViewHost()), client_data);
    183 }
    184 
    185 // static
    186 void AwContentsIoThreadClientImpl::Associate(
    187     WebContents* web_contents,
    188     const JavaRef<jobject>& jclient) {
    189   JNIEnv* env = AttachCurrentThread();
    190   // The ClientMapEntryUpdater lifespan is tied to the WebContents.
    191   new ClientMapEntryUpdater(env, web_contents, jclient.obj());
    192 }
    193 
    194 AwContentsIoThreadClientImpl::AwContentsIoThreadClientImpl(
    195     bool pending_association,
    196     const JavaRef<jobject>& obj)
    197   : pending_association_(pending_association),
    198     java_object_(obj) {
    199 }
    200 
    201 AwContentsIoThreadClientImpl::~AwContentsIoThreadClientImpl() {
    202   // explict, out-of-line destructor.
    203 }
    204 
    205 bool AwContentsIoThreadClientImpl::PendingAssociation() const {
    206   return pending_association_;
    207 }
    208 
    209 AwContentsIoThreadClient::CacheMode
    210 AwContentsIoThreadClientImpl::GetCacheMode() const {
    211   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    212   if (java_object_.is_null())
    213     return AwContentsIoThreadClient::LOAD_DEFAULT;
    214 
    215   JNIEnv* env = AttachCurrentThread();
    216   return static_cast<AwContentsIoThreadClient::CacheMode>(
    217       Java_AwContentsIoThreadClient_getCacheMode(
    218           env, java_object_.obj()));
    219 }
    220 
    221 scoped_ptr<InterceptedRequestData>
    222 AwContentsIoThreadClientImpl::ShouldInterceptRequest(
    223     const GURL& location,
    224     const net::URLRequest* request) {
    225   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    226   if (java_object_.is_null())
    227     return scoped_ptr<InterceptedRequestData>();
    228   const content::ResourceRequestInfo* info =
    229       content::ResourceRequestInfo::ForRequest(request);
    230   bool is_main_frame = info &&
    231       info->GetResourceType() == ResourceType::MAIN_FRAME;
    232 
    233   JNIEnv* env = AttachCurrentThread();
    234   ScopedJavaLocalRef<jstring> jstring_url =
    235       ConvertUTF8ToJavaString(env, location.spec());
    236   ScopedJavaLocalRef<jobject> ret =
    237       Java_AwContentsIoThreadClient_shouldInterceptRequest(
    238           env, java_object_.obj(), jstring_url.obj(), is_main_frame);
    239   if (ret.is_null())
    240     return scoped_ptr<InterceptedRequestData>();
    241   return scoped_ptr<InterceptedRequestData>(
    242       new InterceptedRequestDataImpl(ret));
    243 }
    244 
    245 bool AwContentsIoThreadClientImpl::ShouldBlockContentUrls() const {
    246   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    247   if (java_object_.is_null())
    248     return false;
    249 
    250   JNIEnv* env = AttachCurrentThread();
    251   return Java_AwContentsIoThreadClient_shouldBlockContentUrls(
    252       env, java_object_.obj());
    253 }
    254 
    255 bool AwContentsIoThreadClientImpl::ShouldBlockFileUrls() const {
    256   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    257   if (java_object_.is_null())
    258     return false;
    259 
    260   JNIEnv* env = AttachCurrentThread();
    261   return Java_AwContentsIoThreadClient_shouldBlockFileUrls(
    262       env, java_object_.obj());
    263 }
    264 
    265 bool AwContentsIoThreadClientImpl::ShouldBlockNetworkLoads() const {
    266   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    267   if (java_object_.is_null())
    268     return false;
    269 
    270   JNIEnv* env = AttachCurrentThread();
    271   return Java_AwContentsIoThreadClient_shouldBlockNetworkLoads(
    272       env, java_object_.obj());
    273 }
    274 
    275 void AwContentsIoThreadClientImpl::NewDownload(
    276     const GURL& url,
    277     const std::string& user_agent,
    278     const std::string& content_disposition,
    279     const std::string& mime_type,
    280     int64 content_length) {
    281   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    282   if (java_object_.is_null())
    283     return;
    284 
    285   JNIEnv* env = AttachCurrentThread();
    286   ScopedJavaLocalRef<jstring> jstring_url =
    287       ConvertUTF8ToJavaString(env, url.spec());
    288   ScopedJavaLocalRef<jstring> jstring_user_agent =
    289       ConvertUTF8ToJavaString(env, user_agent);
    290   ScopedJavaLocalRef<jstring> jstring_content_disposition =
    291       ConvertUTF8ToJavaString(env, content_disposition);
    292   ScopedJavaLocalRef<jstring> jstring_mime_type =
    293       ConvertUTF8ToJavaString(env, mime_type);
    294 
    295   Java_AwContentsIoThreadClient_onDownloadStart(
    296       env,
    297       java_object_.obj(),
    298       jstring_url.obj(),
    299       jstring_user_agent.obj(),
    300       jstring_content_disposition.obj(),
    301       jstring_mime_type.obj(),
    302       content_length);
    303 }
    304 
    305 void AwContentsIoThreadClientImpl::NewLoginRequest(const std::string& realm,
    306                                                    const std::string& account,
    307                                                    const std::string& args) {
    308   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    309   if (java_object_.is_null())
    310     return;
    311 
    312   JNIEnv* env = AttachCurrentThread();
    313   ScopedJavaLocalRef<jstring> jrealm = ConvertUTF8ToJavaString(env, realm);
    314   ScopedJavaLocalRef<jstring> jargs = ConvertUTF8ToJavaString(env, args);
    315 
    316   ScopedJavaLocalRef<jstring> jaccount;
    317   if (!account.empty())
    318     jaccount = ConvertUTF8ToJavaString(env, account);
    319 
    320   Java_AwContentsIoThreadClient_newLoginRequest(
    321       env, java_object_.obj(), jrealm.obj(), jaccount.obj(), jargs.obj());
    322 }
    323 
    324 bool RegisterAwContentsIoThreadClientImpl(JNIEnv* env) {
    325   return RegisterNativesImpl(env);
    326 }
    327 
    328 } // namespace android_webview
    329