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.
5 #include "android_webview/native/aw_contents_io_thread_client_impl.h"
10 #include "android_webview/common/devtools_instrumentation.h"
11 #include "android_webview/native/aw_web_resource_response_impl.h"
12 #include "base/android/jni_array.h"
13 #include "base/android/jni_string.h"
14 #include "base/android/jni_weak_ref.h"
15 #include "base/lazy_instance.h"
16 #include "base/memory/linked_ptr.h"
17 #include "base/memory/scoped_ptr.h"
18 #include "base/synchronization/lock.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "content/public/browser/render_frame_host.h"
21 #include "content/public/browser/render_process_host.h"
22 #include "content/public/browser/render_view_host.h"
23 #include "content/public/browser/resource_request_info.h"
24 #include "content/public/browser/web_contents.h"
25 #include "content/public/browser/web_contents_observer.h"
26 #include "jni/AwContentsIoThreadClient_jni.h"
27 #include "net/base/net_errors.h"
28 #include "net/http/http_request_headers.h"
29 #include "net/http/http_response_headers.h"
30 #include "net/url_request/url_request.h"
33 using base::android::AttachCurrentThread
;
34 using base::android::ConvertUTF8ToJavaString
;
35 using base::android::JavaRef
;
36 using base::android::ScopedJavaLocalRef
;
37 using base::android::ToJavaArrayOfStrings
;
38 using base::LazyInstance
;
39 using content::BrowserThread
;
40 using content::RenderFrameHost
;
41 using content::ResourceType
;
42 using content::WebContents
;
48 namespace android_webview
{
52 struct IoThreadClientData
{
53 bool pending_association
;
54 JavaObjectWeakGlobalRef io_thread_client
;
59 IoThreadClientData::IoThreadClientData() : pending_association(false) {}
61 typedef map
<pair
<int, int>, IoThreadClientData
>
62 RenderFrameHostToIoThreadClientType
;
64 static pair
<int, int> GetRenderFrameHostIdPair(RenderFrameHost
* rfh
) {
65 return pair
<int, int>(rfh
->GetProcess()->GetID(), rfh
->GetRoutingID());
68 // RfhToIoThreadClientMap -----------------------------------------------------
69 class RfhToIoThreadClientMap
{
71 static RfhToIoThreadClientMap
* GetInstance();
72 void Set(pair
<int, int> rfh_id
, const IoThreadClientData
& client
);
73 bool Get(pair
<int, int> rfh_id
, IoThreadClientData
* client
);
74 void Erase(pair
<int, int> rfh_id
);
77 static LazyInstance
<RfhToIoThreadClientMap
> g_instance_
;
79 RenderFrameHostToIoThreadClientType rfh_to_io_thread_client_
;
83 LazyInstance
<RfhToIoThreadClientMap
> RfhToIoThreadClientMap::g_instance_
=
84 LAZY_INSTANCE_INITIALIZER
;
87 RfhToIoThreadClientMap
* RfhToIoThreadClientMap::GetInstance() {
88 return g_instance_
.Pointer();
91 void RfhToIoThreadClientMap::Set(pair
<int, int> rfh_id
,
92 const IoThreadClientData
& client
) {
93 base::AutoLock
lock(map_lock_
);
94 rfh_to_io_thread_client_
[rfh_id
] = client
;
97 bool RfhToIoThreadClientMap::Get(
98 pair
<int, int> rfh_id
, IoThreadClientData
* client
) {
99 base::AutoLock
lock(map_lock_
);
100 RenderFrameHostToIoThreadClientType::iterator iterator
=
101 rfh_to_io_thread_client_
.find(rfh_id
);
102 if (iterator
== rfh_to_io_thread_client_
.end())
105 *client
= iterator
->second
;
109 void RfhToIoThreadClientMap::Erase(pair
<int, int> rfh_id
) {
110 base::AutoLock
lock(map_lock_
);
111 rfh_to_io_thread_client_
.erase(rfh_id
);
114 // ClientMapEntryUpdater ------------------------------------------------------
116 class ClientMapEntryUpdater
: public content::WebContentsObserver
{
118 ClientMapEntryUpdater(JNIEnv
* env
, WebContents
* web_contents
,
121 void RenderFrameCreated(RenderFrameHost
* render_frame_host
) override
;
122 void RenderFrameDeleted(RenderFrameHost
* render_frame_host
) override
;
123 void WebContentsDestroyed() override
;
126 JavaObjectWeakGlobalRef jdelegate_
;
129 ClientMapEntryUpdater::ClientMapEntryUpdater(JNIEnv
* env
,
130 WebContents
* web_contents
,
132 : content::WebContentsObserver(web_contents
),
133 jdelegate_(env
, jdelegate
) {
134 DCHECK(web_contents
);
137 if (web_contents
->GetMainFrame())
138 RenderFrameCreated(web_contents
->GetMainFrame());
141 void ClientMapEntryUpdater::RenderFrameCreated(RenderFrameHost
* rfh
) {
142 IoThreadClientData client_data
;
143 client_data
.io_thread_client
= jdelegate_
;
144 client_data
.pending_association
= false;
145 RfhToIoThreadClientMap::GetInstance()->Set(
146 GetRenderFrameHostIdPair(rfh
), client_data
);
149 void ClientMapEntryUpdater::RenderFrameDeleted(RenderFrameHost
* rfh
) {
150 RfhToIoThreadClientMap::GetInstance()->Erase(GetRenderFrameHostIdPair(rfh
));
153 void ClientMapEntryUpdater::WebContentsDestroyed() {
157 struct WebResourceRequest
{
158 ScopedJavaLocalRef
<jstring
> jstring_url
;
160 bool has_user_gesture
;
161 ScopedJavaLocalRef
<jstring
> jstring_method
;
162 ScopedJavaLocalRef
<jobjectArray
> jstringArray_header_names
;
163 ScopedJavaLocalRef
<jobjectArray
> jstringArray_header_values
;
165 WebResourceRequest(JNIEnv
* env
, const net::URLRequest
* request
)
166 : jstring_url(ConvertUTF8ToJavaString(env
, request
->url().spec())),
167 jstring_method(ConvertUTF8ToJavaString(env
, request
->method())) {
168 const content::ResourceRequestInfo
* info
=
169 content::ResourceRequestInfo::ForRequest(request
);
171 info
&& info
->GetResourceType() == content::RESOURCE_TYPE_MAIN_FRAME
;
172 has_user_gesture
= info
&& info
->HasUserGesture();
174 vector
<string
> header_names
;
175 vector
<string
> header_values
;
176 net::HttpRequestHeaders headers
;
177 if (!request
->GetFullRequestHeaders(&headers
))
178 headers
= request
->extra_request_headers();
179 net::HttpRequestHeaders::Iterator
headers_iterator(headers
);
180 while (headers_iterator
.GetNext()) {
181 header_names
.push_back(headers_iterator
.name());
182 header_values
.push_back(headers_iterator
.value());
184 jstringArray_header_names
= ToJavaArrayOfStrings(env
, header_names
);
185 jstringArray_header_values
= ToJavaArrayOfStrings(env
, header_values
);
191 // AwContentsIoThreadClientImpl -----------------------------------------------
194 scoped_ptr
<AwContentsIoThreadClient
>
195 AwContentsIoThreadClient::FromID(int render_process_id
, int render_frame_id
) {
196 pair
<int, int> rfh_id(render_process_id
, render_frame_id
);
197 IoThreadClientData client_data
;
198 if (!RfhToIoThreadClientMap::GetInstance()->Get(rfh_id
, &client_data
))
199 return scoped_ptr
<AwContentsIoThreadClient
>();
201 JNIEnv
* env
= AttachCurrentThread();
202 ScopedJavaLocalRef
<jobject
> java_delegate
=
203 client_data
.io_thread_client
.get(env
);
204 DCHECK(!client_data
.pending_association
|| java_delegate
.is_null());
205 return scoped_ptr
<AwContentsIoThreadClient
>(new AwContentsIoThreadClientImpl(
206 client_data
.pending_association
, java_delegate
));
210 void AwContentsIoThreadClient::SubFrameCreated(int render_process_id
,
211 int parent_render_frame_id
,
212 int child_render_frame_id
) {
213 pair
<int, int> parent_rfh_id(render_process_id
, parent_render_frame_id
);
214 pair
<int, int> child_rfh_id(render_process_id
, child_render_frame_id
);
215 IoThreadClientData client_data
;
216 if (!RfhToIoThreadClientMap::GetInstance()->Get(parent_rfh_id
,
222 RfhToIoThreadClientMap::GetInstance()->Set(child_rfh_id
, client_data
);
226 void AwContentsIoThreadClientImpl::RegisterPendingContents(
227 WebContents
* web_contents
) {
228 IoThreadClientData client_data
;
229 client_data
.pending_association
= true;
230 RfhToIoThreadClientMap::GetInstance()->Set(
231 GetRenderFrameHostIdPair(web_contents
->GetMainFrame()), client_data
);
235 void AwContentsIoThreadClientImpl::Associate(
236 WebContents
* web_contents
,
237 const JavaRef
<jobject
>& jclient
) {
238 JNIEnv
* env
= AttachCurrentThread();
239 // The ClientMapEntryUpdater lifespan is tied to the WebContents.
240 new ClientMapEntryUpdater(env
, web_contents
, jclient
.obj());
243 AwContentsIoThreadClientImpl::AwContentsIoThreadClientImpl(
244 bool pending_association
,
245 const JavaRef
<jobject
>& obj
)
246 : pending_association_(pending_association
),
250 AwContentsIoThreadClientImpl::~AwContentsIoThreadClientImpl() {
251 // explict, out-of-line destructor.
254 bool AwContentsIoThreadClientImpl::PendingAssociation() const {
255 return pending_association_
;
258 AwContentsIoThreadClient::CacheMode
259 AwContentsIoThreadClientImpl::GetCacheMode() const {
260 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
261 if (java_object_
.is_null())
262 return AwContentsIoThreadClient::LOAD_DEFAULT
;
264 JNIEnv
* env
= AttachCurrentThread();
265 return static_cast<AwContentsIoThreadClient::CacheMode
>(
266 Java_AwContentsIoThreadClient_getCacheMode(
267 env
, java_object_
.obj()));
270 scoped_ptr
<AwWebResourceResponse
>
271 AwContentsIoThreadClientImpl::ShouldInterceptRequest(
272 const net::URLRequest
* request
) {
273 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
274 if (java_object_
.is_null())
275 return scoped_ptr
<AwWebResourceResponse
>();
277 JNIEnv
* env
= AttachCurrentThread();
278 WebResourceRequest
web_request(env
, request
);
280 devtools_instrumentation::ScopedEmbedderCallbackTask
embedder_callback(
281 "shouldInterceptRequest");
282 ScopedJavaLocalRef
<jobject
> ret
=
283 Java_AwContentsIoThreadClient_shouldInterceptRequest(
286 web_request
.jstring_url
.obj(),
287 web_request
.is_main_frame
,
288 web_request
.has_user_gesture
,
289 web_request
.jstring_method
.obj(),
290 web_request
.jstringArray_header_names
.obj(),
291 web_request
.jstringArray_header_values
.obj());
293 return scoped_ptr
<AwWebResourceResponse
>();
294 return scoped_ptr
<AwWebResourceResponse
>(
295 new AwWebResourceResponseImpl(ret
));
298 bool AwContentsIoThreadClientImpl::ShouldBlockContentUrls() const {
299 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
300 if (java_object_
.is_null())
303 JNIEnv
* env
= AttachCurrentThread();
304 return Java_AwContentsIoThreadClient_shouldBlockContentUrls(
305 env
, java_object_
.obj());
308 bool AwContentsIoThreadClientImpl::ShouldBlockFileUrls() const {
309 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
310 if (java_object_
.is_null())
313 JNIEnv
* env
= AttachCurrentThread();
314 return Java_AwContentsIoThreadClient_shouldBlockFileUrls(
315 env
, java_object_
.obj());
318 bool AwContentsIoThreadClientImpl::ShouldAcceptThirdPartyCookies() const {
319 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
320 if (java_object_
.is_null())
323 JNIEnv
* env
= AttachCurrentThread();
324 return Java_AwContentsIoThreadClient_shouldAcceptThirdPartyCookies(
325 env
, java_object_
.obj());
328 bool AwContentsIoThreadClientImpl::ShouldBlockNetworkLoads() const {
329 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
330 if (java_object_
.is_null())
333 JNIEnv
* env
= AttachCurrentThread();
334 return Java_AwContentsIoThreadClient_shouldBlockNetworkLoads(
335 env
, java_object_
.obj());
338 void AwContentsIoThreadClientImpl::NewDownload(
340 const string
& user_agent
,
341 const string
& content_disposition
,
342 const string
& mime_type
,
343 int64 content_length
) {
344 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
345 if (java_object_
.is_null())
348 JNIEnv
* env
= AttachCurrentThread();
349 ScopedJavaLocalRef
<jstring
> jstring_url
=
350 ConvertUTF8ToJavaString(env
, url
.spec());
351 ScopedJavaLocalRef
<jstring
> jstring_user_agent
=
352 ConvertUTF8ToJavaString(env
, user_agent
);
353 ScopedJavaLocalRef
<jstring
> jstring_content_disposition
=
354 ConvertUTF8ToJavaString(env
, content_disposition
);
355 ScopedJavaLocalRef
<jstring
> jstring_mime_type
=
356 ConvertUTF8ToJavaString(env
, mime_type
);
358 Java_AwContentsIoThreadClient_onDownloadStart(
362 jstring_user_agent
.obj(),
363 jstring_content_disposition
.obj(),
364 jstring_mime_type
.obj(),
368 void AwContentsIoThreadClientImpl::NewLoginRequest(const string
& realm
,
369 const string
& account
,
370 const string
& args
) {
371 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
372 if (java_object_
.is_null())
375 JNIEnv
* env
= AttachCurrentThread();
376 ScopedJavaLocalRef
<jstring
> jrealm
= ConvertUTF8ToJavaString(env
, realm
);
377 ScopedJavaLocalRef
<jstring
> jargs
= ConvertUTF8ToJavaString(env
, args
);
379 ScopedJavaLocalRef
<jstring
> jaccount
;
380 if (!account
.empty())
381 jaccount
= ConvertUTF8ToJavaString(env
, account
);
383 Java_AwContentsIoThreadClient_newLoginRequest(
384 env
, java_object_
.obj(), jrealm
.obj(), jaccount
.obj(), jargs
.obj());
387 void AwContentsIoThreadClientImpl::OnReceivedError(
388 const net::URLRequest
* request
) {
389 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
390 if (java_object_
.is_null())
393 JNIEnv
* env
= AttachCurrentThread();
394 WebResourceRequest
web_request(env
, request
);
396 int error_code
= request
->status().error();
397 ScopedJavaLocalRef
<jstring
> jstring_description
= ConvertUTF8ToJavaString(
398 env
, net::ErrorToString(request
->status().error()));
400 Java_AwContentsIoThreadClient_onReceivedError(
403 web_request
.jstring_url
.obj(),
404 web_request
.is_main_frame
,
405 web_request
.has_user_gesture
,
406 web_request
.jstring_method
.obj(),
407 web_request
.jstringArray_header_names
.obj(),
408 web_request
.jstringArray_header_values
.obj(),
410 jstring_description
.obj());
413 void AwContentsIoThreadClientImpl::OnReceivedHttpError(
414 const net::URLRequest
* request
,
415 const net::HttpResponseHeaders
* response_headers
) {
416 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
417 if (java_object_
.is_null())
420 JNIEnv
* env
= AttachCurrentThread();
421 WebResourceRequest
web_request(env
, request
);
423 vector
<string
> response_header_names
;
424 vector
<string
> response_header_values
;
426 void* headers_iterator
= NULL
;
427 string header_name
, header_value
;
428 while (response_headers
->EnumerateHeaderLines(
429 &headers_iterator
, &header_name
, &header_value
)) {
430 response_header_names
.push_back(header_name
);
431 response_header_values
.push_back(header_value
);
435 string mime_type
, encoding
;
436 response_headers
->GetMimeTypeAndCharset(&mime_type
, &encoding
);
437 ScopedJavaLocalRef
<jstring
> jstring_mime_type
=
438 ConvertUTF8ToJavaString(env
, mime_type
);
439 ScopedJavaLocalRef
<jstring
> jstring_encoding
=
440 ConvertUTF8ToJavaString(env
, encoding
);
441 int status_code
= response_headers
->response_code();
442 ScopedJavaLocalRef
<jstring
> jstring_reason
=
443 ConvertUTF8ToJavaString(env
, response_headers
->GetStatusText());
444 ScopedJavaLocalRef
<jobjectArray
> jstringArray_response_header_names
=
445 ToJavaArrayOfStrings(env
, response_header_names
);
446 ScopedJavaLocalRef
<jobjectArray
> jstringArray_response_header_values
=
447 ToJavaArrayOfStrings(env
, response_header_values
);
449 Java_AwContentsIoThreadClient_onReceivedHttpError(
452 web_request
.jstring_url
.obj(),
453 web_request
.is_main_frame
,
454 web_request
.has_user_gesture
,
455 web_request
.jstring_method
.obj(),
456 web_request
.jstringArray_header_names
.obj(),
457 web_request
.jstringArray_header_values
.obj(),
458 jstring_mime_type
.obj(),
459 jstring_encoding
.obj(),
461 jstring_reason
.obj(),
462 jstringArray_response_header_names
.obj(),
463 jstringArray_response_header_values
.obj());
466 bool RegisterAwContentsIoThreadClientImpl(JNIEnv
* env
) {
467 return RegisterNativesImpl(env
);
470 } // namespace android_webview