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/native/aw_contents_io_thread_client_impl.h" | |
6 | |
7 #include <map> | |
8 #include <memory> | |
9 #include <utility> | |
10 | |
11 #include "android_webview/browser/net/aw_web_resource_request.h" | |
12 #include "android_webview/common/devtools_instrumentation.h" | |
13 #include "android_webview/native/aw_contents_background_thread_client.h" | |
14 #include "android_webview/native/aw_web_resource_response_impl.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( | |
107 pair<int, int> rfh_id, 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, WebContents* web_contents, | |
151 jobject jdelegate); | |
152 | |
153 void RenderFrameCreated(RenderFrameHost* render_frame_host) override; | |
154 void RenderFrameDeleted(RenderFrameHost* render_frame_host) override; | |
155 void WebContentsDestroyed() override; | |
156 | |
157 private: | |
158 JavaObjectWeakGlobalRef jdelegate_; | |
159 }; | |
160 | |
161 ClientMapEntryUpdater::ClientMapEntryUpdater(JNIEnv* env, | |
162 WebContents* web_contents, | |
163 jobject jdelegate) | |
164 : content::WebContentsObserver(web_contents), | |
165 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( | |
257 WebContents* web_contents, | |
258 const JavaRef<jobject>& jclient) { | |
259 JNIEnv* env = AttachCurrentThread(); | |
260 // The ClientMapEntryUpdater lifespan is tied to the WebContents. | |
261 new ClientMapEntryUpdater(env, web_contents, jclient.obj()); | |
262 } | |
263 | |
264 // static | |
265 void AwContentsIoThreadClientImpl::SetServiceWorkerIoThreadClient( | |
266 const base::android::JavaRef<jobject>& jclient, | |
267 const base::android::JavaRef<jobject>& browser_context) { | |
268 // TODO: currently there is only one browser context so it is ok to | |
269 // store in a global variable, in the future use browser_context to | |
270 // obtain the correct instance. | |
271 JavaObjectWeakGlobalRef temp(AttachCurrentThread(), jclient.obj()); | |
272 g_sw_instance_.Get() = temp; | |
273 } | |
274 | |
275 // static | |
276 std::unique_ptr<AwContentsIoThreadClient> | |
277 AwContentsIoThreadClient::GetServiceWorkerIoThreadClient() { | |
278 JNIEnv* env = AttachCurrentThread(); | |
279 ScopedJavaLocalRef<jobject> java_delegate = g_sw_instance_.Get().get(env); | |
280 | |
281 if (java_delegate.is_null()) | |
282 return std::unique_ptr<AwContentsIoThreadClient>(); | |
283 | |
284 return std::unique_ptr<AwContentsIoThreadClient>( | |
285 new AwContentsIoThreadClientImpl(false, java_delegate)); | |
286 } | |
287 | |
288 AwContentsIoThreadClientImpl::AwContentsIoThreadClientImpl( | |
289 bool pending_association, | |
290 const JavaRef<jobject>& obj) | |
291 : pending_association_(pending_association), | |
292 java_object_(obj) { | |
293 } | |
294 | |
295 AwContentsIoThreadClientImpl::~AwContentsIoThreadClientImpl() { | |
296 // explict, out-of-line destructor. | |
297 } | |
298 | |
299 bool AwContentsIoThreadClientImpl::PendingAssociation() const { | |
300 return pending_association_; | |
301 } | |
302 | |
303 AwContentsIoThreadClient::CacheMode | |
304 AwContentsIoThreadClientImpl::GetCacheMode() const { | |
305 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
306 if (java_object_.is_null()) | |
307 return AwContentsIoThreadClient::LOAD_DEFAULT; | |
308 | |
309 JNIEnv* env = AttachCurrentThread(); | |
310 return static_cast<AwContentsIoThreadClient::CacheMode>( | |
311 Java_AwContentsIoThreadClient_getCacheMode(env, java_object_)); | |
312 } | |
313 | |
314 | |
315 namespace { | |
316 | |
317 std::unique_ptr<AwWebResourceResponse> RunShouldInterceptRequest( | |
318 const AwWebResourceRequest& request, | |
319 JavaObjectWeakGlobalRef ref) { | |
320 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | |
321 JNIEnv* env = AttachCurrentThread(); | |
322 base::android::ScopedJavaLocalRef<jobject> obj = ref.get(env); | |
323 if (obj.is_null()) | |
324 return nullptr; | |
325 | |
326 AwWebResourceRequest::AwJavaWebResourceRequest java_web_resource_request; | |
327 AwWebResourceRequest::ConvertToJava(env, request, &java_web_resource_request); | |
328 | |
329 devtools_instrumentation::ScopedEmbedderCallbackTask embedder_callback( | |
330 "shouldInterceptRequest"); | |
331 ScopedJavaLocalRef<jobject> ret = | |
332 AwContentsBackgroundThreadClient::shouldInterceptRequest( | |
333 env, obj, java_web_resource_request.jurl, request.is_main_frame, | |
334 request.has_user_gesture, java_web_resource_request.jmethod, | |
335 java_web_resource_request.jheader_names, | |
336 java_web_resource_request.jheader_values); | |
337 return std::unique_ptr<AwWebResourceResponse>( | |
338 ret.is_null() ? nullptr : new AwWebResourceResponseImpl(ret)); | |
339 } | |
340 | |
341 std::unique_ptr<AwWebResourceResponse> ReturnNull() { | |
342 return std::unique_ptr<AwWebResourceResponse>(); | |
343 } | |
344 | |
345 } // namespace | |
346 | |
347 void AwContentsIoThreadClientImpl::ShouldInterceptRequestAsync( | |
348 const net::URLRequest* request, | |
349 const ShouldInterceptRequestResultCallback callback) { | |
350 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
351 base::Callback<std::unique_ptr<AwWebResourceResponse>()> get_response = | |
352 base::Bind(&ReturnNull); | |
353 JNIEnv* env = AttachCurrentThread(); | |
354 if (bg_thread_client_object_.is_null() && !java_object_.is_null()) { | |
355 bg_thread_client_object_.Reset( | |
356 Java_AwContentsIoThreadClient_getBackgroundThreadClient(env, | |
357 java_object_)); | |
358 } | |
359 if (!bg_thread_client_object_.is_null()) { | |
360 get_response = base::Bind( | |
361 &RunShouldInterceptRequest, AwWebResourceRequest(*request), | |
362 JavaObjectWeakGlobalRef(env, bg_thread_client_object_.obj())); | |
363 } | |
364 BrowserThread::PostTaskAndReplyWithResult(BrowserThread::FILE, FROM_HERE, | |
365 get_response, callback); | |
366 } | |
367 | |
368 bool AwContentsIoThreadClientImpl::ShouldBlockContentUrls() const { | |
369 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
370 if (java_object_.is_null()) | |
371 return false; | |
372 | |
373 JNIEnv* env = AttachCurrentThread(); | |
374 return Java_AwContentsIoThreadClient_shouldBlockContentUrls(env, | |
375 java_object_); | |
376 } | |
377 | |
378 bool AwContentsIoThreadClientImpl::ShouldBlockFileUrls() const { | |
379 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
380 if (java_object_.is_null()) | |
381 return false; | |
382 | |
383 JNIEnv* env = AttachCurrentThread(); | |
384 return Java_AwContentsIoThreadClient_shouldBlockFileUrls(env, java_object_); | |
385 } | |
386 | |
387 bool AwContentsIoThreadClientImpl::ShouldAcceptThirdPartyCookies() const { | |
388 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
389 if (java_object_.is_null()) | |
390 return false; | |
391 | |
392 JNIEnv* env = AttachCurrentThread(); | |
393 return Java_AwContentsIoThreadClient_shouldAcceptThirdPartyCookies( | |
394 env, java_object_); | |
395 } | |
396 | |
397 bool AwContentsIoThreadClientImpl::GetSafeBrowsingEnabled() const { | |
398 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
399 if (java_object_.is_null()) | |
400 return false; | |
401 | |
402 JNIEnv* env = AttachCurrentThread(); | |
403 return Java_AwContentsIoThreadClient_getSafeBrowsingEnabled(env, | |
404 java_object_); | |
405 } | |
406 | |
407 bool AwContentsIoThreadClientImpl::ShouldBlockNetworkLoads() const { | |
408 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
409 if (java_object_.is_null()) | |
410 return false; | |
411 | |
412 JNIEnv* env = AttachCurrentThread(); | |
413 return Java_AwContentsIoThreadClient_shouldBlockNetworkLoads(env, | |
414 java_object_); | |
415 } | |
416 | |
417 } // namespace android_webview | |
OLD | NEW |