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