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