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/common/devtools_instrumentation.h"
     11 #include "android_webview/native/aw_web_resource_response_impl.h"
     12 #include "base/android/jni_array.h"
     13 #include "base/android/jni_string.h"
     14 #include "base/android/jni_weak_ref.h"
     15 #include "base/lazy_instance.h"
     16 #include "base/memory/linked_ptr.h"
     17 #include "base/memory/scoped_ptr.h"
     18 #include "base/synchronization/lock.h"
     19 #include "content/public/browser/browser_thread.h"
     20 #include "content/public/browser/render_frame_host.h"
     21 #include "content/public/browser/render_process_host.h"
     22 #include "content/public/browser/render_view_host.h"
     23 #include "content/public/browser/resource_request_info.h"
     24 #include "content/public/browser/web_contents.h"
     25 #include "content/public/browser/web_contents_observer.h"
     26 #include "jni/AwContentsIoThreadClient_jni.h"
     27 #include "net/http/http_request_headers.h"
     28 #include "net/url_request/url_request.h"
     29 #include "url/gurl.h"
     30 
     31 using base::android::AttachCurrentThread;
     32 using base::android::ConvertUTF8ToJavaString;
     33 using base::android::JavaRef;
     34 using base::android::ScopedJavaLocalRef;
     35 using base::android::ToJavaArrayOfStrings;
     36 using base::LazyInstance;
     37 using content::BrowserThread;
     38 using content::RenderFrameHost;
     39 using content::ResourceType;
     40 using content::WebContents;
     41 using std::map;
     42 using std::pair;
     43 using std::string;
     44 using std::vector;
     45 
     46 namespace android_webview {
     47 
     48 namespace {
     49 
     50 struct IoThreadClientData {
     51   bool pending_association;
     52   JavaObjectWeakGlobalRef io_thread_client;
     53 
     54   IoThreadClientData();
     55 };
     56 
     57 IoThreadClientData::IoThreadClientData() : pending_association(false) {}
     58 
     59 typedef map<pair<int, int>, IoThreadClientData>
     60     RenderFrameHostToIoThreadClientType;
     61 
     62 static pair<int, int> GetRenderFrameHostIdPair(RenderFrameHost* rfh) {
     63   return pair<int, int>(rfh->GetProcess()->GetID(), rfh->GetRoutingID());
     64 }
     65 
     66 // RfhToIoThreadClientMap -----------------------------------------------------
     67 class RfhToIoThreadClientMap {
     68  public:
     69   static RfhToIoThreadClientMap* GetInstance();
     70   void Set(pair<int, int> rfh_id, const IoThreadClientData& client);
     71   bool Get(pair<int, int> rfh_id, IoThreadClientData* client);
     72   void Erase(pair<int, int> rfh_id);
     73 
     74  private:
     75   static LazyInstance<RfhToIoThreadClientMap> g_instance_;
     76   base::Lock map_lock_;
     77   RenderFrameHostToIoThreadClientType rfh_to_io_thread_client_;
     78 };
     79 
     80 // static
     81 LazyInstance<RfhToIoThreadClientMap> RfhToIoThreadClientMap::g_instance_ =
     82     LAZY_INSTANCE_INITIALIZER;
     83 
     84 // static
     85 RfhToIoThreadClientMap* RfhToIoThreadClientMap::GetInstance() {
     86   return g_instance_.Pointer();
     87 }
     88 
     89 void RfhToIoThreadClientMap::Set(pair<int, int> rfh_id,
     90                                  const IoThreadClientData& client) {
     91   base::AutoLock lock(map_lock_);
     92   rfh_to_io_thread_client_[rfh_id] = client;
     93 }
     94 
     95 bool RfhToIoThreadClientMap::Get(
     96     pair<int, int> rfh_id, IoThreadClientData* client) {
     97   base::AutoLock lock(map_lock_);
     98   RenderFrameHostToIoThreadClientType::iterator iterator =
     99       rfh_to_io_thread_client_.find(rfh_id);
    100   if (iterator == rfh_to_io_thread_client_.end())
    101     return false;
    102 
    103   *client = iterator->second;
    104   return true;
    105 }
    106 
    107 void RfhToIoThreadClientMap::Erase(pair<int, int> rfh_id) {
    108   base::AutoLock lock(map_lock_);
    109   rfh_to_io_thread_client_.erase(rfh_id);
    110 }
    111 
    112 // ClientMapEntryUpdater ------------------------------------------------------
    113 
    114 class ClientMapEntryUpdater : public content::WebContentsObserver {
    115  public:
    116   ClientMapEntryUpdater(JNIEnv* env, WebContents* web_contents,
    117                         jobject jdelegate);
    118 
    119   virtual void RenderFrameCreated(RenderFrameHost* render_frame_host) OVERRIDE;
    120   virtual void RenderFrameDeleted(RenderFrameHost* render_frame_host) OVERRIDE;
    121   virtual void WebContentsDestroyed() OVERRIDE;
    122 
    123  private:
    124   JavaObjectWeakGlobalRef jdelegate_;
    125 };
    126 
    127 ClientMapEntryUpdater::ClientMapEntryUpdater(JNIEnv* env,
    128                                              WebContents* web_contents,
    129                                              jobject jdelegate)
    130     : content::WebContentsObserver(web_contents),
    131       jdelegate_(env, jdelegate) {
    132   DCHECK(web_contents);
    133   DCHECK(jdelegate);
    134 
    135   if (web_contents->GetMainFrame())
    136     RenderFrameCreated(web_contents->GetMainFrame());
    137 }
    138 
    139 void ClientMapEntryUpdater::RenderFrameCreated(RenderFrameHost* rfh) {
    140   IoThreadClientData client_data;
    141   client_data.io_thread_client = jdelegate_;
    142   client_data.pending_association = false;
    143   RfhToIoThreadClientMap::GetInstance()->Set(
    144       GetRenderFrameHostIdPair(rfh), client_data);
    145 }
    146 
    147 void ClientMapEntryUpdater::RenderFrameDeleted(RenderFrameHost* rfh) {
    148   RfhToIoThreadClientMap::GetInstance()->Erase(GetRenderFrameHostIdPair(rfh));
    149 }
    150 
    151 void ClientMapEntryUpdater::WebContentsDestroyed() {
    152   delete this;
    153 }
    154 
    155 } // namespace
    156 
    157 // AwContentsIoThreadClientImpl -----------------------------------------------
    158 
    159 // static
    160 scoped_ptr<AwContentsIoThreadClient>
    161 AwContentsIoThreadClient::FromID(int render_process_id, int render_frame_id) {
    162   pair<int, int> rfh_id(render_process_id, render_frame_id);
    163   IoThreadClientData client_data;
    164   if (!RfhToIoThreadClientMap::GetInstance()->Get(rfh_id, &client_data))
    165     return scoped_ptr<AwContentsIoThreadClient>();
    166 
    167   JNIEnv* env = AttachCurrentThread();
    168   ScopedJavaLocalRef<jobject> java_delegate =
    169       client_data.io_thread_client.get(env);
    170   DCHECK(!client_data.pending_association || java_delegate.is_null());
    171   return scoped_ptr<AwContentsIoThreadClient>(new AwContentsIoThreadClientImpl(
    172       client_data.pending_association, java_delegate));
    173 }
    174 
    175 // static
    176 void AwContentsIoThreadClient::SubFrameCreated(int render_process_id,
    177                                                int parent_render_frame_id,
    178                                                int child_render_frame_id) {
    179   pair<int, int> parent_rfh_id(render_process_id, parent_render_frame_id);
    180   pair<int, int> child_rfh_id(render_process_id, child_render_frame_id);
    181   IoThreadClientData client_data;
    182   if (!RfhToIoThreadClientMap::GetInstance()->Get(parent_rfh_id,
    183                                                   &client_data)) {
    184     NOTREACHED();
    185     return;
    186   }
    187 
    188   RfhToIoThreadClientMap::GetInstance()->Set(child_rfh_id, client_data);
    189 }
    190 
    191 // static
    192 void AwContentsIoThreadClientImpl::RegisterPendingContents(
    193     WebContents* web_contents) {
    194   IoThreadClientData client_data;
    195   client_data.pending_association = true;
    196   RfhToIoThreadClientMap::GetInstance()->Set(
    197       GetRenderFrameHostIdPair(web_contents->GetMainFrame()), client_data);
    198 }
    199 
    200 // static
    201 void AwContentsIoThreadClientImpl::Associate(
    202     WebContents* web_contents,
    203     const JavaRef<jobject>& jclient) {
    204   JNIEnv* env = AttachCurrentThread();
    205   // The ClientMapEntryUpdater lifespan is tied to the WebContents.
    206   new ClientMapEntryUpdater(env, web_contents, jclient.obj());
    207 }
    208 
    209 AwContentsIoThreadClientImpl::AwContentsIoThreadClientImpl(
    210     bool pending_association,
    211     const JavaRef<jobject>& obj)
    212   : pending_association_(pending_association),
    213     java_object_(obj) {
    214 }
    215 
    216 AwContentsIoThreadClientImpl::~AwContentsIoThreadClientImpl() {
    217   // explict, out-of-line destructor.
    218 }
    219 
    220 bool AwContentsIoThreadClientImpl::PendingAssociation() const {
    221   return pending_association_;
    222 }
    223 
    224 AwContentsIoThreadClient::CacheMode
    225 AwContentsIoThreadClientImpl::GetCacheMode() const {
    226   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    227   if (java_object_.is_null())
    228     return AwContentsIoThreadClient::LOAD_DEFAULT;
    229 
    230   JNIEnv* env = AttachCurrentThread();
    231   return static_cast<AwContentsIoThreadClient::CacheMode>(
    232       Java_AwContentsIoThreadClient_getCacheMode(
    233           env, java_object_.obj()));
    234 }
    235 
    236 scoped_ptr<AwWebResourceResponse>
    237 AwContentsIoThreadClientImpl::ShouldInterceptRequest(
    238     const GURL& location,
    239     const net::URLRequest* request) {
    240   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    241   if (java_object_.is_null())
    242     return scoped_ptr<AwWebResourceResponse>();
    243   const content::ResourceRequestInfo* info =
    244       content::ResourceRequestInfo::ForRequest(request);
    245   bool is_main_frame = info &&
    246       info->GetResourceType() == content::RESOURCE_TYPE_MAIN_FRAME;
    247   bool has_user_gesture = info && info->HasUserGesture();
    248 
    249   vector<string> headers_names;
    250   vector<string> headers_values;
    251   {
    252     net::HttpRequestHeaders headers;
    253     if (!request->GetFullRequestHeaders(&headers))
    254       headers = request->extra_request_headers();
    255     net::HttpRequestHeaders::Iterator headers_iterator(headers);
    256     while (headers_iterator.GetNext()) {
    257       headers_names.push_back(headers_iterator.name());
    258       headers_values.push_back(headers_iterator.value());
    259     }
    260   }
    261 
    262   JNIEnv* env = AttachCurrentThread();
    263   ScopedJavaLocalRef<jstring> jstring_url =
    264       ConvertUTF8ToJavaString(env, location.spec());
    265   ScopedJavaLocalRef<jstring> jstring_method =
    266       ConvertUTF8ToJavaString(env, request->method());
    267   ScopedJavaLocalRef<jobjectArray> jstringArray_headers_names =
    268       ToJavaArrayOfStrings(env, headers_names);
    269   ScopedJavaLocalRef<jobjectArray> jstringArray_headers_values =
    270       ToJavaArrayOfStrings(env, headers_values);
    271   devtools_instrumentation::ScopedEmbedderCallbackTask embedder_callback(
    272       "shouldInterceptRequest");
    273   ScopedJavaLocalRef<jobject> ret =
    274       Java_AwContentsIoThreadClient_shouldInterceptRequest(
    275           env,
    276           java_object_.obj(),
    277           jstring_url.obj(),
    278           is_main_frame,
    279           has_user_gesture,
    280           jstring_method.obj(),
    281           jstringArray_headers_names.obj(),
    282           jstringArray_headers_values.obj());
    283   if (ret.is_null())
    284     return scoped_ptr<AwWebResourceResponse>();
    285   return scoped_ptr<AwWebResourceResponse>(
    286       new AwWebResourceResponseImpl(ret));
    287 }
    288 
    289 bool AwContentsIoThreadClientImpl::ShouldBlockContentUrls() const {
    290   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    291   if (java_object_.is_null())
    292     return false;
    293 
    294   JNIEnv* env = AttachCurrentThread();
    295   return Java_AwContentsIoThreadClient_shouldBlockContentUrls(
    296       env, java_object_.obj());
    297 }
    298 
    299 bool AwContentsIoThreadClientImpl::ShouldBlockFileUrls() const {
    300   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    301   if (java_object_.is_null())
    302     return false;
    303 
    304   JNIEnv* env = AttachCurrentThread();
    305   return Java_AwContentsIoThreadClient_shouldBlockFileUrls(
    306       env, java_object_.obj());
    307 }
    308 
    309 bool AwContentsIoThreadClientImpl::ShouldAcceptThirdPartyCookies() const {
    310   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    311   if (java_object_.is_null())
    312     return false;
    313 
    314   JNIEnv* env = AttachCurrentThread();
    315   return Java_AwContentsIoThreadClient_shouldAcceptThirdPartyCookies(
    316       env, java_object_.obj());
    317 }
    318 
    319 bool AwContentsIoThreadClientImpl::ShouldBlockNetworkLoads() const {
    320   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    321   if (java_object_.is_null())
    322     return false;
    323 
    324   JNIEnv* env = AttachCurrentThread();
    325   return Java_AwContentsIoThreadClient_shouldBlockNetworkLoads(
    326       env, java_object_.obj());
    327 }
    328 
    329 void AwContentsIoThreadClientImpl::NewDownload(
    330     const GURL& url,
    331     const string& user_agent,
    332     const string& content_disposition,
    333     const string& mime_type,
    334     int64 content_length) {
    335   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    336   if (java_object_.is_null())
    337     return;
    338 
    339   JNIEnv* env = AttachCurrentThread();
    340   ScopedJavaLocalRef<jstring> jstring_url =
    341       ConvertUTF8ToJavaString(env, url.spec());
    342   ScopedJavaLocalRef<jstring> jstring_user_agent =
    343       ConvertUTF8ToJavaString(env, user_agent);
    344   ScopedJavaLocalRef<jstring> jstring_content_disposition =
    345       ConvertUTF8ToJavaString(env, content_disposition);
    346   ScopedJavaLocalRef<jstring> jstring_mime_type =
    347       ConvertUTF8ToJavaString(env, mime_type);
    348 
    349   Java_AwContentsIoThreadClient_onDownloadStart(
    350       env,
    351       java_object_.obj(),
    352       jstring_url.obj(),
    353       jstring_user_agent.obj(),
    354       jstring_content_disposition.obj(),
    355       jstring_mime_type.obj(),
    356       content_length);
    357 }
    358 
    359 void AwContentsIoThreadClientImpl::NewLoginRequest(const string& realm,
    360                                                    const string& account,
    361                                                    const string& args) {
    362   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    363   if (java_object_.is_null())
    364     return;
    365 
    366   JNIEnv* env = AttachCurrentThread();
    367   ScopedJavaLocalRef<jstring> jrealm = ConvertUTF8ToJavaString(env, realm);
    368   ScopedJavaLocalRef<jstring> jargs = ConvertUTF8ToJavaString(env, args);
    369 
    370   ScopedJavaLocalRef<jstring> jaccount;
    371   if (!account.empty())
    372     jaccount = ConvertUTF8ToJavaString(env, account);
    373 
    374   Java_AwContentsIoThreadClient_newLoginRequest(
    375       env, java_object_.obj(), jrealm.obj(), jaccount.obj(), jargs.obj());
    376 }
    377 
    378 bool RegisterAwContentsIoThreadClientImpl(JNIEnv* env) {
    379   return RegisterNativesImpl(env);
    380 }
    381 
    382 } // namespace android_webview
    383