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