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