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 31using base::android::AttachCurrentThread; 32using base::android::ConvertUTF8ToJavaString; 33using base::android::JavaRef; 34using base::android::ScopedJavaLocalRef; 35using base::android::ToJavaArrayOfStrings; 36using base::LazyInstance; 37using content::BrowserThread; 38using content::RenderFrameHost; 39using content::ResourceType; 40using content::WebContents; 41using std::map; 42using std::pair; 43using std::string; 44using std::vector; 45 46namespace android_webview { 47 48namespace { 49 50struct IoThreadClientData { 51 bool pending_association; 52 JavaObjectWeakGlobalRef io_thread_client; 53 54 IoThreadClientData(); 55}; 56 57IoThreadClientData::IoThreadClientData() : pending_association(false) {} 58 59typedef map<pair<int, int>, IoThreadClientData> 60 RenderFrameHostToIoThreadClientType; 61 62static pair<int, int> GetRenderFrameHostIdPair(RenderFrameHost* rfh) { 63 return pair<int, int>(rfh->GetProcess()->GetID(), rfh->GetRoutingID()); 64} 65 66// RfhToIoThreadClientMap ----------------------------------------------------- 67class 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 81LazyInstance<RfhToIoThreadClientMap> RfhToIoThreadClientMap::g_instance_ = 82 LAZY_INSTANCE_INITIALIZER; 83 84// static 85RfhToIoThreadClientMap* RfhToIoThreadClientMap::GetInstance() { 86 return g_instance_.Pointer(); 87} 88 89void 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 95bool 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 107void 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 114class 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 127ClientMapEntryUpdater::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 139void 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 147void ClientMapEntryUpdater::RenderFrameDeleted(RenderFrameHost* rfh) { 148 RfhToIoThreadClientMap::GetInstance()->Erase(GetRenderFrameHostIdPair(rfh)); 149} 150 151void ClientMapEntryUpdater::WebContentsDestroyed() { 152 delete this; 153} 154 155} // namespace 156 157// AwContentsIoThreadClientImpl ----------------------------------------------- 158 159// static 160scoped_ptr<AwContentsIoThreadClient> 161AwContentsIoThreadClient::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 176void 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 192void 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 201void 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 209AwContentsIoThreadClientImpl::AwContentsIoThreadClientImpl( 210 bool pending_association, 211 const JavaRef<jobject>& obj) 212 : pending_association_(pending_association), 213 java_object_(obj) { 214} 215 216AwContentsIoThreadClientImpl::~AwContentsIoThreadClientImpl() { 217 // explict, out-of-line destructor. 218} 219 220bool AwContentsIoThreadClientImpl::PendingAssociation() const { 221 return pending_association_; 222} 223 224AwContentsIoThreadClient::CacheMode 225AwContentsIoThreadClientImpl::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 236scoped_ptr<AwWebResourceResponse> 237AwContentsIoThreadClientImpl::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 289bool 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 299bool 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 309bool 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 319bool 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 329void 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 359void 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 378bool RegisterAwContentsIoThreadClientImpl(JNIEnv* env) { 379 return RegisterNativesImpl(env); 380} 381 382} // namespace android_webview 383