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 |