| OLD | NEW |
| (Empty) |
| 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/browser/aw_contents_io_thread_client_impl.h" | |
| 6 | |
| 7 #include <map> | |
| 8 #include <memory> | |
| 9 #include <utility> | |
| 10 | |
| 11 #include "android_webview/browser/aw_contents_background_thread_client.h" | |
| 12 #include "android_webview/browser/aw_web_resource_response_impl.h" | |
| 13 #include "android_webview/browser/net/aw_web_resource_request.h" | |
| 14 #include "android_webview/common/devtools_instrumentation.h" | |
| 15 #include "base/android/jni_array.h" | |
| 16 #include "base/android/jni_string.h" | |
| 17 #include "base/android/jni_weak_ref.h" | |
| 18 #include "base/lazy_instance.h" | |
| 19 #include "base/synchronization/lock.h" | |
| 20 #include "content/public/browser/browser_thread.h" | |
| 21 #include "content/public/browser/render_frame_host.h" | |
| 22 #include "content/public/browser/render_process_host.h" | |
| 23 #include "content/public/browser/render_view_host.h" | |
| 24 #include "content/public/browser/resource_request_info.h" | |
| 25 #include "content/public/browser/web_contents.h" | |
| 26 #include "content/public/browser/web_contents_observer.h" | |
| 27 #include "jni/AwContentsIoThreadClient_jni.h" | |
| 28 #include "net/url_request/url_request.h" | |
| 29 | |
| 30 using base::android::AttachCurrentThread; | |
| 31 using base::android::ConvertUTF8ToJavaString; | |
| 32 using base::android::JavaRef; | |
| 33 using base::android::ScopedJavaLocalRef; | |
| 34 using base::android::ToJavaArrayOfStrings; | |
| 35 using base::LazyInstance; | |
| 36 using content::BrowserThread; | |
| 37 using content::RenderFrameHost; | |
| 38 using content::ResourceType; | |
| 39 using content::WebContents; | |
| 40 using std::map; | |
| 41 using std::pair; | |
| 42 using std::string; | |
| 43 | |
| 44 namespace android_webview { | |
| 45 | |
| 46 namespace { | |
| 47 | |
| 48 struct IoThreadClientData { | |
| 49 bool pending_association; | |
| 50 JavaObjectWeakGlobalRef io_thread_client; | |
| 51 | |
| 52 IoThreadClientData(); | |
| 53 }; | |
| 54 | |
| 55 IoThreadClientData::IoThreadClientData() : pending_association(false) {} | |
| 56 | |
| 57 typedef map<pair<int, int>, IoThreadClientData> | |
| 58 RenderFrameHostToIoThreadClientType; | |
| 59 | |
| 60 // When browser side navigation is enabled, RenderFrameIDs do not have | |
| 61 // valid render process host and render frame ids for frame navigations. | |
| 62 // We need to identify these by using Frame Tree Node ids. | |
| 63 typedef map<int, IoThreadClientData> FrameTreeNodeToIoThreadClientType; | |
| 64 | |
| 65 static pair<int, int> GetRenderFrameHostIdPair(RenderFrameHost* rfh) { | |
| 66 return pair<int, int>(rfh->GetProcess()->GetID(), rfh->GetRoutingID()); | |
| 67 } | |
| 68 | |
| 69 // RfhToIoThreadClientMap ----------------------------------------------------- | |
| 70 class RfhToIoThreadClientMap { | |
| 71 public: | |
| 72 static RfhToIoThreadClientMap* GetInstance(); | |
| 73 void Set(pair<int, int> rfh_id, const IoThreadClientData& client); | |
| 74 bool Get(pair<int, int> rfh_id, IoThreadClientData* client); | |
| 75 void Erase(pair<int, int> rfh_id); | |
| 76 | |
| 77 void Set(int frame_tree_node_id, const IoThreadClientData& client); | |
| 78 bool Get(int frame_tree_node_id, IoThreadClientData* client); | |
| 79 void Erase(int frame_tree_node_id); | |
| 80 | |
| 81 private: | |
| 82 base::Lock map_lock_; | |
| 83 RenderFrameHostToIoThreadClientType rfh_to_io_thread_client_; | |
| 84 FrameTreeNodeToIoThreadClientType frame_tree_node_to_io_thread_client_; | |
| 85 }; | |
| 86 | |
| 87 // static | |
| 88 LazyInstance<RfhToIoThreadClientMap>::DestructorAtExit g_instance_ = | |
| 89 LAZY_INSTANCE_INITIALIZER; | |
| 90 | |
| 91 // static | |
| 92 LazyInstance<JavaObjectWeakGlobalRef>::DestructorAtExit g_sw_instance_ = | |
| 93 LAZY_INSTANCE_INITIALIZER; | |
| 94 | |
| 95 // static | |
| 96 RfhToIoThreadClientMap* RfhToIoThreadClientMap::GetInstance() { | |
| 97 return g_instance_.Pointer(); | |
| 98 } | |
| 99 | |
| 100 void RfhToIoThreadClientMap::Set(pair<int, int> rfh_id, | |
| 101 const IoThreadClientData& client) { | |
| 102 base::AutoLock lock(map_lock_); | |
| 103 rfh_to_io_thread_client_[rfh_id] = client; | |
| 104 } | |
| 105 | |
| 106 bool RfhToIoThreadClientMap::Get(pair<int, int> rfh_id, | |
| 107 IoThreadClientData* client) { | |
| 108 base::AutoLock lock(map_lock_); | |
| 109 RenderFrameHostToIoThreadClientType::iterator iterator = | |
| 110 rfh_to_io_thread_client_.find(rfh_id); | |
| 111 if (iterator == rfh_to_io_thread_client_.end()) | |
| 112 return false; | |
| 113 | |
| 114 *client = iterator->second; | |
| 115 return true; | |
| 116 } | |
| 117 | |
| 118 void RfhToIoThreadClientMap::Erase(pair<int, int> rfh_id) { | |
| 119 base::AutoLock lock(map_lock_); | |
| 120 rfh_to_io_thread_client_.erase(rfh_id); | |
| 121 } | |
| 122 | |
| 123 void RfhToIoThreadClientMap::Set(int frame_tree_node_id, | |
| 124 const IoThreadClientData& client) { | |
| 125 base::AutoLock lock(map_lock_); | |
| 126 frame_tree_node_to_io_thread_client_[frame_tree_node_id] = client; | |
| 127 } | |
| 128 | |
| 129 bool RfhToIoThreadClientMap::Get(int frame_tree_node_id, | |
| 130 IoThreadClientData* client) { | |
| 131 base::AutoLock lock(map_lock_); | |
| 132 FrameTreeNodeToIoThreadClientType::iterator iterator = | |
| 133 frame_tree_node_to_io_thread_client_.find(frame_tree_node_id); | |
| 134 if (iterator == frame_tree_node_to_io_thread_client_.end()) | |
| 135 return false; | |
| 136 | |
| 137 *client = iterator->second; | |
| 138 return true; | |
| 139 } | |
| 140 | |
| 141 void RfhToIoThreadClientMap::Erase(int frame_tree_node_id) { | |
| 142 base::AutoLock lock(map_lock_); | |
| 143 frame_tree_node_to_io_thread_client_.erase(frame_tree_node_id); | |
| 144 } | |
| 145 | |
| 146 // ClientMapEntryUpdater ------------------------------------------------------ | |
| 147 | |
| 148 class ClientMapEntryUpdater : public content::WebContentsObserver { | |
| 149 public: | |
| 150 ClientMapEntryUpdater(JNIEnv* env, | |
| 151 WebContents* web_contents, | |
| 152 jobject jdelegate); | |
| 153 | |
| 154 void RenderFrameCreated(RenderFrameHost* render_frame_host) override; | |
| 155 void RenderFrameDeleted(RenderFrameHost* render_frame_host) override; | |
| 156 void WebContentsDestroyed() override; | |
| 157 | |
| 158 private: | |
| 159 JavaObjectWeakGlobalRef jdelegate_; | |
| 160 }; | |
| 161 | |
| 162 ClientMapEntryUpdater::ClientMapEntryUpdater(JNIEnv* env, | |
| 163 WebContents* web_contents, | |
| 164 jobject jdelegate) | |
| 165 : content::WebContentsObserver(web_contents), jdelegate_(env, jdelegate) { | |
| 166 DCHECK(web_contents); | |
| 167 DCHECK(jdelegate); | |
| 168 | |
| 169 if (web_contents->GetMainFrame()) | |
| 170 RenderFrameCreated(web_contents->GetMainFrame()); | |
| 171 } | |
| 172 | |
| 173 void ClientMapEntryUpdater::RenderFrameCreated(RenderFrameHost* rfh) { | |
| 174 IoThreadClientData client_data; | |
| 175 client_data.io_thread_client = jdelegate_; | |
| 176 client_data.pending_association = false; | |
| 177 RfhToIoThreadClientMap::GetInstance()->Set(GetRenderFrameHostIdPair(rfh), | |
| 178 client_data); | |
| 179 RfhToIoThreadClientMap::GetInstance()->Set(rfh->GetFrameTreeNodeId(), | |
| 180 client_data); | |
| 181 } | |
| 182 | |
| 183 void ClientMapEntryUpdater::RenderFrameDeleted(RenderFrameHost* rfh) { | |
| 184 RfhToIoThreadClientMap::GetInstance()->Erase(GetRenderFrameHostIdPair(rfh)); | |
| 185 RfhToIoThreadClientMap::GetInstance()->Erase(rfh->GetFrameTreeNodeId()); | |
| 186 } | |
| 187 | |
| 188 void ClientMapEntryUpdater::WebContentsDestroyed() { | |
| 189 delete this; | |
| 190 } | |
| 191 | |
| 192 } // namespace | |
| 193 | |
| 194 // AwContentsIoThreadClientImpl ----------------------------------------------- | |
| 195 | |
| 196 // static | |
| 197 std::unique_ptr<AwContentsIoThreadClient> AwContentsIoThreadClient::FromID( | |
| 198 int render_process_id, | |
| 199 int render_frame_id) { | |
| 200 pair<int, int> rfh_id(render_process_id, render_frame_id); | |
| 201 IoThreadClientData client_data; | |
| 202 if (!RfhToIoThreadClientMap::GetInstance()->Get(rfh_id, &client_data)) | |
| 203 return std::unique_ptr<AwContentsIoThreadClient>(); | |
| 204 | |
| 205 JNIEnv* env = AttachCurrentThread(); | |
| 206 ScopedJavaLocalRef<jobject> java_delegate = | |
| 207 client_data.io_thread_client.get(env); | |
| 208 DCHECK(!client_data.pending_association || java_delegate.is_null()); | |
| 209 return std::unique_ptr<AwContentsIoThreadClient>( | |
| 210 new AwContentsIoThreadClientImpl(client_data.pending_association, | |
| 211 java_delegate)); | |
| 212 } | |
| 213 | |
| 214 std::unique_ptr<AwContentsIoThreadClient> AwContentsIoThreadClient::FromID( | |
| 215 int frame_tree_node_id) { | |
| 216 IoThreadClientData client_data; | |
| 217 if (!RfhToIoThreadClientMap::GetInstance()->Get(frame_tree_node_id, | |
| 218 &client_data)) | |
| 219 return std::unique_ptr<AwContentsIoThreadClient>(); | |
| 220 | |
| 221 JNIEnv* env = AttachCurrentThread(); | |
| 222 ScopedJavaLocalRef<jobject> java_delegate = | |
| 223 client_data.io_thread_client.get(env); | |
| 224 DCHECK(!client_data.pending_association || java_delegate.is_null()); | |
| 225 return std::unique_ptr<AwContentsIoThreadClient>( | |
| 226 new AwContentsIoThreadClientImpl(client_data.pending_association, | |
| 227 java_delegate)); | |
| 228 } | |
| 229 | |
| 230 // static | |
| 231 void AwContentsIoThreadClient::SubFrameCreated(int render_process_id, | |
| 232 int parent_render_frame_id, | |
| 233 int child_render_frame_id) { | |
| 234 pair<int, int> parent_rfh_id(render_process_id, parent_render_frame_id); | |
| 235 pair<int, int> child_rfh_id(render_process_id, child_render_frame_id); | |
| 236 IoThreadClientData client_data; | |
| 237 if (!RfhToIoThreadClientMap::GetInstance()->Get(parent_rfh_id, | |
| 238 &client_data)) { | |
| 239 NOTREACHED(); | |
| 240 return; | |
| 241 } | |
| 242 | |
| 243 RfhToIoThreadClientMap::GetInstance()->Set(child_rfh_id, client_data); | |
| 244 } | |
| 245 | |
| 246 // static | |
| 247 void AwContentsIoThreadClientImpl::RegisterPendingContents( | |
| 248 WebContents* web_contents) { | |
| 249 IoThreadClientData client_data; | |
| 250 client_data.pending_association = true; | |
| 251 RfhToIoThreadClientMap::GetInstance()->Set( | |
| 252 GetRenderFrameHostIdPair(web_contents->GetMainFrame()), client_data); | |
| 253 } | |
| 254 | |
| 255 // static | |
| 256 void AwContentsIoThreadClientImpl::Associate(WebContents* web_contents, | |
| 257 const JavaRef<jobject>& jclient) { | |
| 258 JNIEnv* env = AttachCurrentThread(); | |
| 259 // The ClientMapEntryUpdater lifespan is tied to the WebContents. | |
| 260 new ClientMapEntryUpdater(env, web_contents, jclient.obj()); | |
| 261 } | |
| 262 | |
| 263 // static | |
| 264 void AwContentsIoThreadClientImpl::SetServiceWorkerIoThreadClient( | |
| 265 const base::android::JavaRef<jobject>& jclient, | |
| 266 const base::android::JavaRef<jobject>& browser_context) { | |
| 267 // TODO: currently there is only one browser context so it is ok to | |
| 268 // store in a global variable, in the future use browser_context to | |
| 269 // obtain the correct instance. | |
| 270 JavaObjectWeakGlobalRef temp(AttachCurrentThread(), jclient.obj()); | |
| 271 g_sw_instance_.Get() = temp; | |
| 272 } | |
| 273 | |
| 274 // static | |
| 275 std::unique_ptr<AwContentsIoThreadClient> | |
| 276 AwContentsIoThreadClient::GetServiceWorkerIoThreadClient() { | |
| 277 JNIEnv* env = AttachCurrentThread(); | |
| 278 ScopedJavaLocalRef<jobject> java_delegate = g_sw_instance_.Get().get(env); | |
| 279 | |
| 280 if (java_delegate.is_null()) | |
| 281 return std::unique_ptr<AwContentsIoThreadClient>(); | |
| 282 | |
| 283 return std::unique_ptr<AwContentsIoThreadClient>( | |
| 284 new AwContentsIoThreadClientImpl(false, java_delegate)); | |
| 285 } | |
| 286 | |
| 287 AwContentsIoThreadClientImpl::AwContentsIoThreadClientImpl( | |
| 288 bool pending_association, | |
| 289 const JavaRef<jobject>& obj) | |
| 290 : pending_association_(pending_association), java_object_(obj) {} | |
| 291 | |
| 292 AwContentsIoThreadClientImpl::~AwContentsIoThreadClientImpl() { | |
| 293 // explict, out-of-line destructor. | |
| 294 } | |
| 295 | |
| 296 bool AwContentsIoThreadClientImpl::PendingAssociation() const { | |
| 297 return pending_association_; | |
| 298 } | |
| 299 | |
| 300 AwContentsIoThreadClient::CacheMode AwContentsIoThreadClientImpl::GetCacheMode() | |
| 301 const { | |
| 302 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 303 if (java_object_.is_null()) | |
| 304 return AwContentsIoThreadClient::LOAD_DEFAULT; | |
| 305 | |
| 306 JNIEnv* env = AttachCurrentThread(); | |
| 307 return static_cast<AwContentsIoThreadClient::CacheMode>( | |
| 308 Java_AwContentsIoThreadClient_getCacheMode(env, java_object_)); | |
| 309 } | |
| 310 | |
| 311 namespace { | |
| 312 | |
| 313 std::unique_ptr<AwWebResourceResponse> RunShouldInterceptRequest( | |
| 314 const AwWebResourceRequest& request, | |
| 315 JavaObjectWeakGlobalRef ref) { | |
| 316 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | |
| 317 JNIEnv* env = AttachCurrentThread(); | |
| 318 base::android::ScopedJavaLocalRef<jobject> obj = ref.get(env); | |
| 319 if (obj.is_null()) | |
| 320 return nullptr; | |
| 321 | |
| 322 AwWebResourceRequest::AwJavaWebResourceRequest java_web_resource_request; | |
| 323 AwWebResourceRequest::ConvertToJava(env, request, &java_web_resource_request); | |
| 324 | |
| 325 devtools_instrumentation::ScopedEmbedderCallbackTask embedder_callback( | |
| 326 "shouldInterceptRequest"); | |
| 327 ScopedJavaLocalRef<jobject> ret = | |
| 328 AwContentsBackgroundThreadClient::shouldInterceptRequest( | |
| 329 env, obj, java_web_resource_request.jurl, request.is_main_frame, | |
| 330 request.has_user_gesture, java_web_resource_request.jmethod, | |
| 331 java_web_resource_request.jheader_names, | |
| 332 java_web_resource_request.jheader_values); | |
| 333 return std::unique_ptr<AwWebResourceResponse>( | |
| 334 ret.is_null() ? nullptr : new AwWebResourceResponseImpl(ret)); | |
| 335 } | |
| 336 | |
| 337 std::unique_ptr<AwWebResourceResponse> ReturnNull() { | |
| 338 return std::unique_ptr<AwWebResourceResponse>(); | |
| 339 } | |
| 340 | |
| 341 } // namespace | |
| 342 | |
| 343 void AwContentsIoThreadClientImpl::ShouldInterceptRequestAsync( | |
| 344 const net::URLRequest* request, | |
| 345 const ShouldInterceptRequestResultCallback callback) { | |
| 346 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 347 base::Callback<std::unique_ptr<AwWebResourceResponse>()> get_response = | |
| 348 base::Bind(&ReturnNull); | |
| 349 JNIEnv* env = AttachCurrentThread(); | |
| 350 if (bg_thread_client_object_.is_null() && !java_object_.is_null()) { | |
| 351 bg_thread_client_object_.Reset( | |
| 352 Java_AwContentsIoThreadClient_getBackgroundThreadClient(env, | |
| 353 java_object_)); | |
| 354 } | |
| 355 if (!bg_thread_client_object_.is_null()) { | |
| 356 get_response = base::Bind( | |
| 357 &RunShouldInterceptRequest, AwWebResourceRequest(*request), | |
| 358 JavaObjectWeakGlobalRef(env, bg_thread_client_object_.obj())); | |
| 359 } | |
| 360 BrowserThread::PostTaskAndReplyWithResult(BrowserThread::FILE, FROM_HERE, | |
| 361 get_response, callback); | |
| 362 } | |
| 363 | |
| 364 bool AwContentsIoThreadClientImpl::ShouldBlockContentUrls() const { | |
| 365 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 366 if (java_object_.is_null()) | |
| 367 return false; | |
| 368 | |
| 369 JNIEnv* env = AttachCurrentThread(); | |
| 370 return Java_AwContentsIoThreadClient_shouldBlockContentUrls(env, | |
| 371 java_object_); | |
| 372 } | |
| 373 | |
| 374 bool AwContentsIoThreadClientImpl::ShouldBlockFileUrls() const { | |
| 375 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 376 if (java_object_.is_null()) | |
| 377 return false; | |
| 378 | |
| 379 JNIEnv* env = AttachCurrentThread(); | |
| 380 return Java_AwContentsIoThreadClient_shouldBlockFileUrls(env, java_object_); | |
| 381 } | |
| 382 | |
| 383 bool AwContentsIoThreadClientImpl::ShouldAcceptThirdPartyCookies() const { | |
| 384 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 385 if (java_object_.is_null()) | |
| 386 return false; | |
| 387 | |
| 388 JNIEnv* env = AttachCurrentThread(); | |
| 389 return Java_AwContentsIoThreadClient_shouldAcceptThirdPartyCookies( | |
| 390 env, java_object_); | |
| 391 } | |
| 392 | |
| 393 bool AwContentsIoThreadClientImpl::GetSafeBrowsingEnabled() const { | |
| 394 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 395 if (java_object_.is_null()) | |
| 396 return false; | |
| 397 | |
| 398 JNIEnv* env = AttachCurrentThread(); | |
| 399 return Java_AwContentsIoThreadClient_getSafeBrowsingEnabled(env, | |
| 400 java_object_); | |
| 401 } | |
| 402 | |
| 403 bool AwContentsIoThreadClientImpl::ShouldBlockNetworkLoads() const { | |
| 404 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 405 if (java_object_.is_null()) | |
| 406 return false; | |
| 407 | |
| 408 JNIEnv* env = AttachCurrentThread(); | |
| 409 return Java_AwContentsIoThreadClient_shouldBlockNetworkLoads(env, | |
| 410 java_object_); | |
| 411 } | |
| 412 | |
| 413 } // namespace android_webview | |
| OLD | NEW |