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/http/http_request_headers.h"
28 #include "net/url_request/url_request.h"
31 using base::android::AttachCurrentThread
;
32 using base::android::ConvertUTF8ToJavaString
;
33 using base::android::JavaRef
;
34 using base::android::ScopedJavaLocalRef
;
35 using base::android::ToJavaArrayOfStrings
;
36 using base::LazyInstance
;
37 using content::BrowserThread
;
38 using content::RenderFrameHost
;
39 using content::ResourceType
;
40 using content::WebContents
;
46 namespace android_webview
{
50 struct IoThreadClientData
{
51 bool pending_association
;
52 JavaObjectWeakGlobalRef io_thread_client
;
57 IoThreadClientData::IoThreadClientData() : pending_association(false) {}
59 typedef map
<pair
<int, int>, IoThreadClientData
>
60 RenderFrameHostToIoThreadClientType
;
62 static pair
<int, int> GetRenderFrameHostIdPair(RenderFrameHost
* rfh
) {
63 return pair
<int, int>(rfh
->GetProcess()->GetID(), rfh
->GetRoutingID());
66 // RfhToIoThreadClientMap -----------------------------------------------------
67 class RfhToIoThreadClientMap
{
69 static RfhToIoThreadClientMap
* GetInstance();
70 void Set(pair
<int, int> rfh_id
, const IoThreadClientData
& client
);
71 bool Get(pair
<int, int> rfh_id
, IoThreadClientData
* client
);
72 void Erase(pair
<int, int> rfh_id
);
75 static LazyInstance
<RfhToIoThreadClientMap
> g_instance_
;
77 RenderFrameHostToIoThreadClientType rfh_to_io_thread_client_
;
81 LazyInstance
<RfhToIoThreadClientMap
> RfhToIoThreadClientMap::g_instance_
=
82 LAZY_INSTANCE_INITIALIZER
;
85 RfhToIoThreadClientMap
* RfhToIoThreadClientMap::GetInstance() {
86 return g_instance_
.Pointer();
89 void RfhToIoThreadClientMap::Set(pair
<int, int> rfh_id
,
90 const IoThreadClientData
& client
) {
91 base::AutoLock
lock(map_lock_
);
92 rfh_to_io_thread_client_
[rfh_id
] = client
;
95 bool RfhToIoThreadClientMap::Get(
96 pair
<int, int> rfh_id
, IoThreadClientData
* client
) {
97 base::AutoLock
lock(map_lock_
);
98 RenderFrameHostToIoThreadClientType::iterator iterator
=
99 rfh_to_io_thread_client_
.find(rfh_id
);
100 if (iterator
== rfh_to_io_thread_client_
.end())
103 *client
= iterator
->second
;
107 void RfhToIoThreadClientMap::Erase(pair
<int, int> rfh_id
) {
108 base::AutoLock
lock(map_lock_
);
109 rfh_to_io_thread_client_
.erase(rfh_id
);
112 // ClientMapEntryUpdater ------------------------------------------------------
114 class ClientMapEntryUpdater
: public content::WebContentsObserver
{
116 ClientMapEntryUpdater(JNIEnv
* env
, WebContents
* web_contents
,
119 virtual void RenderFrameCreated(RenderFrameHost
* render_frame_host
) OVERRIDE
;
120 virtual void RenderFrameDeleted(RenderFrameHost
* render_frame_host
) OVERRIDE
;
121 virtual void WebContentsDestroyed() OVERRIDE
;
124 JavaObjectWeakGlobalRef jdelegate_
;
127 ClientMapEntryUpdater::ClientMapEntryUpdater(JNIEnv
* env
,
128 WebContents
* web_contents
,
130 : content::WebContentsObserver(web_contents
),
131 jdelegate_(env
, jdelegate
) {
132 DCHECK(web_contents
);
135 if (web_contents
->GetMainFrame())
136 RenderFrameCreated(web_contents
->GetMainFrame());
139 void ClientMapEntryUpdater::RenderFrameCreated(RenderFrameHost
* rfh
) {
140 IoThreadClientData client_data
;
141 client_data
.io_thread_client
= jdelegate_
;
142 client_data
.pending_association
= false;
143 RfhToIoThreadClientMap::GetInstance()->Set(
144 GetRenderFrameHostIdPair(rfh
), client_data
);
147 void ClientMapEntryUpdater::RenderFrameDeleted(RenderFrameHost
* rfh
) {
148 RfhToIoThreadClientMap::GetInstance()->Erase(GetRenderFrameHostIdPair(rfh
));
151 void ClientMapEntryUpdater::WebContentsDestroyed() {
157 // AwContentsIoThreadClientImpl -----------------------------------------------
160 scoped_ptr
<AwContentsIoThreadClient
>
161 AwContentsIoThreadClient::FromID(int render_process_id
, int render_frame_id
) {
162 pair
<int, int> rfh_id(render_process_id
, render_frame_id
);
163 IoThreadClientData client_data
;
164 if (!RfhToIoThreadClientMap::GetInstance()->Get(rfh_id
, &client_data
))
165 return scoped_ptr
<AwContentsIoThreadClient
>();
167 JNIEnv
* env
= AttachCurrentThread();
168 ScopedJavaLocalRef
<jobject
> java_delegate
=
169 client_data
.io_thread_client
.get(env
);
170 DCHECK(!client_data
.pending_association
|| java_delegate
.is_null());
171 return scoped_ptr
<AwContentsIoThreadClient
>(new AwContentsIoThreadClientImpl(
172 client_data
.pending_association
, java_delegate
));
176 void AwContentsIoThreadClient::SubFrameCreated(int render_process_id
,
177 int parent_render_frame_id
,
178 int child_render_frame_id
) {
179 pair
<int, int> parent_rfh_id(render_process_id
, parent_render_frame_id
);
180 pair
<int, int> child_rfh_id(render_process_id
, child_render_frame_id
);
181 IoThreadClientData client_data
;
182 if (!RfhToIoThreadClientMap::GetInstance()->Get(parent_rfh_id
,
188 RfhToIoThreadClientMap::GetInstance()->Set(child_rfh_id
, client_data
);
192 void AwContentsIoThreadClientImpl::RegisterPendingContents(
193 WebContents
* web_contents
) {
194 IoThreadClientData client_data
;
195 client_data
.pending_association
= true;
196 RfhToIoThreadClientMap::GetInstance()->Set(
197 GetRenderFrameHostIdPair(web_contents
->GetMainFrame()), client_data
);
201 void AwContentsIoThreadClientImpl::Associate(
202 WebContents
* web_contents
,
203 const JavaRef
<jobject
>& jclient
) {
204 JNIEnv
* env
= AttachCurrentThread();
205 // The ClientMapEntryUpdater lifespan is tied to the WebContents.
206 new ClientMapEntryUpdater(env
, web_contents
, jclient
.obj());
209 AwContentsIoThreadClientImpl::AwContentsIoThreadClientImpl(
210 bool pending_association
,
211 const JavaRef
<jobject
>& obj
)
212 : pending_association_(pending_association
),
216 AwContentsIoThreadClientImpl::~AwContentsIoThreadClientImpl() {
217 // explict, out-of-line destructor.
220 bool AwContentsIoThreadClientImpl::PendingAssociation() const {
221 return pending_association_
;
224 AwContentsIoThreadClient::CacheMode
225 AwContentsIoThreadClientImpl::GetCacheMode() const {
226 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
227 if (java_object_
.is_null())
228 return AwContentsIoThreadClient::LOAD_DEFAULT
;
230 JNIEnv
* env
= AttachCurrentThread();
231 return static_cast<AwContentsIoThreadClient::CacheMode
>(
232 Java_AwContentsIoThreadClient_getCacheMode(
233 env
, java_object_
.obj()));
236 scoped_ptr
<AwWebResourceResponse
>
237 AwContentsIoThreadClientImpl::ShouldInterceptRequest(
238 const GURL
& location
,
239 const net::URLRequest
* request
) {
240 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
241 if (java_object_
.is_null())
242 return scoped_ptr
<AwWebResourceResponse
>();
243 const content::ResourceRequestInfo
* info
=
244 content::ResourceRequestInfo::ForRequest(request
);
245 bool is_main_frame
= info
&&
246 info
->GetResourceType() == content::RESOURCE_TYPE_MAIN_FRAME
;
247 bool has_user_gesture
= info
&& info
->HasUserGesture();
249 vector
<string
> headers_names
;
250 vector
<string
> headers_values
;
252 net::HttpRequestHeaders headers
;
253 if (!request
->GetFullRequestHeaders(&headers
))
254 headers
= request
->extra_request_headers();
255 net::HttpRequestHeaders::Iterator
headers_iterator(headers
);
256 while (headers_iterator
.GetNext()) {
257 headers_names
.push_back(headers_iterator
.name());
258 headers_values
.push_back(headers_iterator
.value());
262 JNIEnv
* env
= AttachCurrentThread();
263 ScopedJavaLocalRef
<jstring
> jstring_url
=
264 ConvertUTF8ToJavaString(env
, location
.spec());
265 ScopedJavaLocalRef
<jstring
> jstring_method
=
266 ConvertUTF8ToJavaString(env
, request
->method());
267 ScopedJavaLocalRef
<jobjectArray
> jstringArray_headers_names
=
268 ToJavaArrayOfStrings(env
, headers_names
);
269 ScopedJavaLocalRef
<jobjectArray
> jstringArray_headers_values
=
270 ToJavaArrayOfStrings(env
, headers_values
);
271 devtools_instrumentation::ScopedEmbedderCallbackTask
embedder_callback(
272 "shouldInterceptRequest");
273 ScopedJavaLocalRef
<jobject
> ret
=
274 Java_AwContentsIoThreadClient_shouldInterceptRequest(
280 jstring_method
.obj(),
281 jstringArray_headers_names
.obj(),
282 jstringArray_headers_values
.obj());
284 return scoped_ptr
<AwWebResourceResponse
>();
285 return scoped_ptr
<AwWebResourceResponse
>(
286 new AwWebResourceResponseImpl(ret
));
289 bool AwContentsIoThreadClientImpl::ShouldBlockContentUrls() const {
290 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
291 if (java_object_
.is_null())
294 JNIEnv
* env
= AttachCurrentThread();
295 return Java_AwContentsIoThreadClient_shouldBlockContentUrls(
296 env
, java_object_
.obj());
299 bool AwContentsIoThreadClientImpl::ShouldBlockFileUrls() const {
300 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
301 if (java_object_
.is_null())
304 JNIEnv
* env
= AttachCurrentThread();
305 return Java_AwContentsIoThreadClient_shouldBlockFileUrls(
306 env
, java_object_
.obj());
309 bool AwContentsIoThreadClientImpl::ShouldAcceptThirdPartyCookies() const {
310 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
311 if (java_object_
.is_null())
314 JNIEnv
* env
= AttachCurrentThread();
315 return Java_AwContentsIoThreadClient_shouldAcceptThirdPartyCookies(
316 env
, java_object_
.obj());
319 bool AwContentsIoThreadClientImpl::ShouldBlockNetworkLoads() const {
320 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
321 if (java_object_
.is_null())
324 JNIEnv
* env
= AttachCurrentThread();
325 return Java_AwContentsIoThreadClient_shouldBlockNetworkLoads(
326 env
, java_object_
.obj());
329 void AwContentsIoThreadClientImpl::NewDownload(
331 const string
& user_agent
,
332 const string
& content_disposition
,
333 const string
& mime_type
,
334 int64 content_length
) {
335 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
336 if (java_object_
.is_null())
339 JNIEnv
* env
= AttachCurrentThread();
340 ScopedJavaLocalRef
<jstring
> jstring_url
=
341 ConvertUTF8ToJavaString(env
, url
.spec());
342 ScopedJavaLocalRef
<jstring
> jstring_user_agent
=
343 ConvertUTF8ToJavaString(env
, user_agent
);
344 ScopedJavaLocalRef
<jstring
> jstring_content_disposition
=
345 ConvertUTF8ToJavaString(env
, content_disposition
);
346 ScopedJavaLocalRef
<jstring
> jstring_mime_type
=
347 ConvertUTF8ToJavaString(env
, mime_type
);
349 Java_AwContentsIoThreadClient_onDownloadStart(
353 jstring_user_agent
.obj(),
354 jstring_content_disposition
.obj(),
355 jstring_mime_type
.obj(),
359 void AwContentsIoThreadClientImpl::NewLoginRequest(const string
& realm
,
360 const string
& account
,
361 const string
& args
) {
362 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
363 if (java_object_
.is_null())
366 JNIEnv
* env
= AttachCurrentThread();
367 ScopedJavaLocalRef
<jstring
> jrealm
= ConvertUTF8ToJavaString(env
, realm
);
368 ScopedJavaLocalRef
<jstring
> jargs
= ConvertUTF8ToJavaString(env
, args
);
370 ScopedJavaLocalRef
<jstring
> jaccount
;
371 if (!account
.empty())
372 jaccount
= ConvertUTF8ToJavaString(env
, account
);
374 Java_AwContentsIoThreadClient_newLoginRequest(
375 env
, java_object_
.obj(), jrealm
.obj(), jaccount
.obj(), jargs
.obj());
378 bool RegisterAwContentsIoThreadClientImpl(JNIEnv
* env
) {
379 return RegisterNativesImpl(env
);
382 } // namespace android_webview