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 RenderViewDeleted(RenderViewHost* render_view_host) OVERRIDE; 113 virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE; 114 115 private: 116 JavaObjectWeakGlobalRef jdelegate_; 117 }; 118 119 ClientMapEntryUpdater::ClientMapEntryUpdater(JNIEnv* env, 120 WebContents* web_contents, 121 jobject jdelegate) 122 : content::WebContentsObserver(web_contents), 123 jdelegate_(env, jdelegate) { 124 DCHECK(web_contents); 125 DCHECK(jdelegate); 126 127 if (web_contents->GetRenderViewHost()) 128 RenderViewCreated(web_contents->GetRenderViewHost()); 129 } 130 131 void ClientMapEntryUpdater::RenderViewCreated(RenderViewHost* rvh) { 132 IoThreadClientData client_data; 133 client_data.io_thread_client = jdelegate_; 134 client_data.pending_association = false; 135 RvhToIoThreadClientMap::GetInstance()->Set( 136 GetRenderViewHostIdPair(rvh), client_data); 137 } 138 139 void ClientMapEntryUpdater::RenderViewDeleted(RenderViewHost* rvh) { 140 RvhToIoThreadClientMap::GetInstance()->Erase(GetRenderViewHostIdPair(rvh)); 141 } 142 143 void ClientMapEntryUpdater::WebContentsDestroyed(WebContents* web_contents) { 144 delete this; 145 } 146 147 } // namespace 148 149 // AwContentsIoThreadClientImpl ----------------------------------------------- 150 151 // static 152 scoped_ptr<AwContentsIoThreadClient> 153 AwContentsIoThreadClient::FromID(int render_process_id, int render_view_id) { 154 pair<int, int> rvh_id(render_process_id, render_view_id); 155 IoThreadClientData client_data; 156 if (!RvhToIoThreadClientMap::GetInstance()->Get(rvh_id, &client_data)) 157 return scoped_ptr<AwContentsIoThreadClient>(); 158 159 JNIEnv* env = AttachCurrentThread(); 160 ScopedJavaLocalRef<jobject> java_delegate = 161 client_data.io_thread_client.get(env); 162 DCHECK(!client_data.pending_association || java_delegate.is_null()); 163 return scoped_ptr<AwContentsIoThreadClient>(new AwContentsIoThreadClientImpl( 164 client_data.pending_association, java_delegate)); 165 } 166 167 // static 168 void AwContentsIoThreadClientImpl::RegisterPendingContents( 169 WebContents* web_contents) { 170 IoThreadClientData client_data; 171 client_data.pending_association = true; 172 RvhToIoThreadClientMap::GetInstance()->Set( 173 GetRenderViewHostIdPair(web_contents->GetRenderViewHost()), client_data); 174 } 175 176 // static 177 void AwContentsIoThreadClientImpl::Associate( 178 WebContents* web_contents, 179 const JavaRef<jobject>& jclient) { 180 JNIEnv* env = AttachCurrentThread(); 181 // The ClientMapEntryUpdater lifespan is tied to the WebContents. 182 new ClientMapEntryUpdater(env, web_contents, jclient.obj()); 183 } 184 185 AwContentsIoThreadClientImpl::AwContentsIoThreadClientImpl( 186 bool pending_association, 187 const JavaRef<jobject>& obj) 188 : pending_association_(pending_association), 189 java_object_(obj) { 190 } 191 192 AwContentsIoThreadClientImpl::~AwContentsIoThreadClientImpl() { 193 // explict, out-of-line destructor. 194 } 195 196 bool AwContentsIoThreadClientImpl::PendingAssociation() const { 197 return pending_association_; 198 } 199 200 AwContentsIoThreadClient::CacheMode 201 AwContentsIoThreadClientImpl::GetCacheMode() const { 202 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 203 if (java_object_.is_null()) 204 return AwContentsIoThreadClient::LOAD_DEFAULT; 205 206 JNIEnv* env = AttachCurrentThread(); 207 return static_cast<AwContentsIoThreadClient::CacheMode>( 208 Java_AwContentsIoThreadClient_getCacheMode( 209 env, java_object_.obj())); 210 } 211 212 scoped_ptr<InterceptedRequestData> 213 AwContentsIoThreadClientImpl::ShouldInterceptRequest( 214 const GURL& location, 215 const net::URLRequest* request) { 216 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 217 if (java_object_.is_null()) 218 return scoped_ptr<InterceptedRequestData>(); 219 const content::ResourceRequestInfo* info = 220 content::ResourceRequestInfo::ForRequest(request); 221 bool is_main_frame = info && 222 info->GetResourceType() == ResourceType::MAIN_FRAME; 223 224 JNIEnv* env = AttachCurrentThread(); 225 ScopedJavaLocalRef<jstring> jstring_url = 226 ConvertUTF8ToJavaString(env, location.spec()); 227 ScopedJavaLocalRef<jobject> ret = 228 Java_AwContentsIoThreadClient_shouldInterceptRequest( 229 env, java_object_.obj(), jstring_url.obj(), is_main_frame); 230 if (ret.is_null()) 231 return scoped_ptr<InterceptedRequestData>(); 232 return scoped_ptr<InterceptedRequestData>( 233 new InterceptedRequestDataImpl(ret)); 234 } 235 236 bool AwContentsIoThreadClientImpl::ShouldBlockContentUrls() const { 237 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 238 if (java_object_.is_null()) 239 return false; 240 241 JNIEnv* env = AttachCurrentThread(); 242 return Java_AwContentsIoThreadClient_shouldBlockContentUrls( 243 env, java_object_.obj()); 244 } 245 246 bool AwContentsIoThreadClientImpl::ShouldBlockFileUrls() const { 247 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 248 if (java_object_.is_null()) 249 return false; 250 251 JNIEnv* env = AttachCurrentThread(); 252 return Java_AwContentsIoThreadClient_shouldBlockFileUrls( 253 env, java_object_.obj()); 254 } 255 256 bool AwContentsIoThreadClientImpl::ShouldBlockNetworkLoads() const { 257 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 258 if (java_object_.is_null()) 259 return false; 260 261 JNIEnv* env = AttachCurrentThread(); 262 return Java_AwContentsIoThreadClient_shouldBlockNetworkLoads( 263 env, java_object_.obj()); 264 } 265 266 void AwContentsIoThreadClientImpl::NewDownload( 267 const GURL& url, 268 const std::string& user_agent, 269 const std::string& content_disposition, 270 const std::string& mime_type, 271 int64 content_length) { 272 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 273 if (java_object_.is_null()) 274 return; 275 276 JNIEnv* env = AttachCurrentThread(); 277 ScopedJavaLocalRef<jstring> jstring_url = 278 ConvertUTF8ToJavaString(env, url.spec()); 279 ScopedJavaLocalRef<jstring> jstring_user_agent = 280 ConvertUTF8ToJavaString(env, user_agent); 281 ScopedJavaLocalRef<jstring> jstring_content_disposition = 282 ConvertUTF8ToJavaString(env, content_disposition); 283 ScopedJavaLocalRef<jstring> jstring_mime_type = 284 ConvertUTF8ToJavaString(env, mime_type); 285 286 Java_AwContentsIoThreadClient_onDownloadStart( 287 env, 288 java_object_.obj(), 289 jstring_url.obj(), 290 jstring_user_agent.obj(), 291 jstring_content_disposition.obj(), 292 jstring_mime_type.obj(), 293 content_length); 294 } 295 296 void AwContentsIoThreadClientImpl::NewLoginRequest(const std::string& realm, 297 const std::string& account, 298 const std::string& args) { 299 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 300 if (java_object_.is_null()) 301 return; 302 303 JNIEnv* env = AttachCurrentThread(); 304 ScopedJavaLocalRef<jstring> jrealm = ConvertUTF8ToJavaString(env, realm); 305 ScopedJavaLocalRef<jstring> jargs = ConvertUTF8ToJavaString(env, args); 306 307 ScopedJavaLocalRef<jstring> jaccount; 308 if (!account.empty()) 309 jaccount = ConvertUTF8ToJavaString(env, account); 310 311 Java_AwContentsIoThreadClient_newLoginRequest( 312 env, java_object_.obj(), jrealm.obj(), jaccount.obj(), jargs.obj()); 313 } 314 315 bool RegisterAwContentsIoThreadClientImpl(JNIEnv* env) { 316 return RegisterNativesImpl(env); 317 } 318 319 } // namespace android_webview 320