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::WebContents;
45 namespace android_webview {
49 struct IoThreadClientData {
50 bool pending_association;
51 JavaObjectWeakGlobalRef io_thread_client;
56 IoThreadClientData::IoThreadClientData() : pending_association(false) {}
58 typedef map<pair<int, int>, IoThreadClientData>
59 RenderFrameHostToIoThreadClientType;
61 static pair<int, int> GetRenderFrameHostIdPair(RenderFrameHost* rfh) {
62 return pair<int, int>(rfh->GetProcess()->GetID(), rfh->GetRoutingID());
65 // RfhToIoThreadClientMap -----------------------------------------------------
66 class RfhToIoThreadClientMap {
68 static RfhToIoThreadClientMap* GetInstance();
69 void Set(pair<int, int> rfh_id, const IoThreadClientData& client);
70 bool Get(pair<int, int> rfh_id, IoThreadClientData* client);
71 void Erase(pair<int, int> rfh_id);
74 static LazyInstance<RfhToIoThreadClientMap> g_instance_;
76 RenderFrameHostToIoThreadClientType rfh_to_io_thread_client_;
80 LazyInstance<RfhToIoThreadClientMap> RfhToIoThreadClientMap::g_instance_ =
81 LAZY_INSTANCE_INITIALIZER;
84 RfhToIoThreadClientMap* RfhToIoThreadClientMap::GetInstance() {
85 return g_instance_.Pointer();
88 void RfhToIoThreadClientMap::Set(pair<int, int> rfh_id,
89 const IoThreadClientData& client) {
90 base::AutoLock lock(map_lock_);
91 rfh_to_io_thread_client_[rfh_id] = client;
94 bool RfhToIoThreadClientMap::Get(
95 pair<int, int> rfh_id, IoThreadClientData* client) {
96 base::AutoLock lock(map_lock_);
97 RenderFrameHostToIoThreadClientType::iterator iterator =
98 rfh_to_io_thread_client_.find(rfh_id);
99 if (iterator == rfh_to_io_thread_client_.end())
102 *client = iterator->second;
106 void RfhToIoThreadClientMap::Erase(pair<int, int> rfh_id) {
107 base::AutoLock lock(map_lock_);
108 rfh_to_io_thread_client_.erase(rfh_id);
111 // ClientMapEntryUpdater ------------------------------------------------------
113 class ClientMapEntryUpdater : public content::WebContentsObserver {
115 ClientMapEntryUpdater(JNIEnv* env, WebContents* web_contents,
118 virtual void RenderFrameCreated(RenderFrameHost* render_frame_host) OVERRIDE;
119 virtual void RenderFrameDeleted(RenderFrameHost* render_frame_host) OVERRIDE;
120 virtual void WebContentsDestroyed() OVERRIDE;
123 JavaObjectWeakGlobalRef jdelegate_;
126 ClientMapEntryUpdater::ClientMapEntryUpdater(JNIEnv* env,
127 WebContents* web_contents,
129 : content::WebContentsObserver(web_contents),
130 jdelegate_(env, jdelegate) {
131 DCHECK(web_contents);
134 if (web_contents->GetMainFrame())
135 RenderFrameCreated(web_contents->GetMainFrame());
138 void ClientMapEntryUpdater::RenderFrameCreated(RenderFrameHost* rfh) {
139 IoThreadClientData client_data;
140 client_data.io_thread_client = jdelegate_;
141 client_data.pending_association = false;
142 RfhToIoThreadClientMap::GetInstance()->Set(
143 GetRenderFrameHostIdPair(rfh), client_data);
146 void ClientMapEntryUpdater::RenderFrameDeleted(RenderFrameHost* rfh) {
147 RfhToIoThreadClientMap::GetInstance()->Erase(GetRenderFrameHostIdPair(rfh));
150 void ClientMapEntryUpdater::WebContentsDestroyed() {
156 // AwContentsIoThreadClientImpl -----------------------------------------------
159 scoped_ptr<AwContentsIoThreadClient>
160 AwContentsIoThreadClient::FromID(int render_process_id, int render_frame_id) {
161 pair<int, int> rfh_id(render_process_id, render_frame_id);
162 IoThreadClientData client_data;
163 if (!RfhToIoThreadClientMap::GetInstance()->Get(rfh_id, &client_data))
164 return scoped_ptr<AwContentsIoThreadClient>();
166 JNIEnv* env = AttachCurrentThread();
167 ScopedJavaLocalRef<jobject> java_delegate =
168 client_data.io_thread_client.get(env);
169 DCHECK(!client_data.pending_association || java_delegate.is_null());
170 return scoped_ptr<AwContentsIoThreadClient>(new AwContentsIoThreadClientImpl(
171 client_data.pending_association, java_delegate));
175 void AwContentsIoThreadClient::SubFrameCreated(int render_process_id,
176 int parent_render_frame_id,
177 int child_render_frame_id) {
178 pair<int, int> parent_rfh_id(render_process_id, parent_render_frame_id);
179 pair<int, int> child_rfh_id(render_process_id, child_render_frame_id);
180 IoThreadClientData client_data;
181 if (!RfhToIoThreadClientMap::GetInstance()->Get(parent_rfh_id,
187 RfhToIoThreadClientMap::GetInstance()->Set(child_rfh_id, client_data);
191 void AwContentsIoThreadClientImpl::RegisterPendingContents(
192 WebContents* web_contents) {
193 IoThreadClientData client_data;
194 client_data.pending_association = true;
195 RfhToIoThreadClientMap::GetInstance()->Set(
196 GetRenderFrameHostIdPair(web_contents->GetMainFrame()), client_data);
200 void AwContentsIoThreadClientImpl::Associate(
201 WebContents* web_contents,
202 const JavaRef<jobject>& jclient) {
203 JNIEnv* env = AttachCurrentThread();
204 // The ClientMapEntryUpdater lifespan is tied to the WebContents.
205 new ClientMapEntryUpdater(env, web_contents, jclient.obj());
208 AwContentsIoThreadClientImpl::AwContentsIoThreadClientImpl(
209 bool pending_association,
210 const JavaRef<jobject>& obj)
211 : pending_association_(pending_association),
215 AwContentsIoThreadClientImpl::~AwContentsIoThreadClientImpl() {
216 // explict, out-of-line destructor.
219 bool AwContentsIoThreadClientImpl::PendingAssociation() const {
220 return pending_association_;
223 AwContentsIoThreadClient::CacheMode
224 AwContentsIoThreadClientImpl::GetCacheMode() const {
225 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
226 if (java_object_.is_null())
227 return AwContentsIoThreadClient::LOAD_DEFAULT;
229 JNIEnv* env = AttachCurrentThread();
230 return static_cast<AwContentsIoThreadClient::CacheMode>(
231 Java_AwContentsIoThreadClient_getCacheMode(
232 env, java_object_.obj()));
235 scoped_ptr<AwWebResourceResponse>
236 AwContentsIoThreadClientImpl::ShouldInterceptRequest(
237 const GURL& location,
238 const net::URLRequest* request) {
239 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
240 if (java_object_.is_null())
241 return scoped_ptr<AwWebResourceResponse>();
242 const content::ResourceRequestInfo* info =
243 content::ResourceRequestInfo::ForRequest(request);
244 bool is_main_frame = info &&
245 info->GetResourceType() == ResourceType::MAIN_FRAME;
246 bool has_user_gesture = info && info->HasUserGesture();
248 vector<string> headers_names;
249 vector<string> headers_values;
251 net::HttpRequestHeaders headers;
252 if (!request->GetFullRequestHeaders(&headers))
253 headers = request->extra_request_headers();
254 net::HttpRequestHeaders::Iterator headers_iterator(headers);
255 while (headers_iterator.GetNext()) {
256 headers_names.push_back(headers_iterator.name());
257 headers_values.push_back(headers_iterator.value());
261 JNIEnv* env = AttachCurrentThread();
262 ScopedJavaLocalRef<jstring> jstring_url =
263 ConvertUTF8ToJavaString(env, location.spec());
264 ScopedJavaLocalRef<jstring> jstring_method =
265 ConvertUTF8ToJavaString(env, request->method());
266 ScopedJavaLocalRef<jobjectArray> jstringArray_headers_names =
267 ToJavaArrayOfStrings(env, headers_names);
268 ScopedJavaLocalRef<jobjectArray> jstringArray_headers_values =
269 ToJavaArrayOfStrings(env, headers_values);
270 devtools_instrumentation::ScopedEmbedderCallbackTask embedder_callback(
271 "shouldInterceptRequest");
272 ScopedJavaLocalRef<jobject> ret =
273 Java_AwContentsIoThreadClient_shouldInterceptRequest(
279 jstring_method.obj(),
280 jstringArray_headers_names.obj(),
281 jstringArray_headers_values.obj());
283 return scoped_ptr<AwWebResourceResponse>();
284 return scoped_ptr<AwWebResourceResponse>(
285 new AwWebResourceResponseImpl(ret));
288 bool AwContentsIoThreadClientImpl::ShouldBlockContentUrls() const {
289 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
290 if (java_object_.is_null())
293 JNIEnv* env = AttachCurrentThread();
294 return Java_AwContentsIoThreadClient_shouldBlockContentUrls(
295 env, java_object_.obj());
298 bool AwContentsIoThreadClientImpl::ShouldBlockFileUrls() const {
299 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
300 if (java_object_.is_null())
303 JNIEnv* env = AttachCurrentThread();
304 return Java_AwContentsIoThreadClient_shouldBlockFileUrls(
305 env, java_object_.obj());
308 bool AwContentsIoThreadClientImpl::ShouldAcceptThirdPartyCookies() const {
309 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
310 if (java_object_.is_null())
313 JNIEnv* env = AttachCurrentThread();
314 return Java_AwContentsIoThreadClient_shouldAcceptThirdPartyCookies(
315 env, java_object_.obj());
318 bool AwContentsIoThreadClientImpl::ShouldBlockNetworkLoads() const {
319 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
320 if (java_object_.is_null())
323 JNIEnv* env = AttachCurrentThread();
324 return Java_AwContentsIoThreadClient_shouldBlockNetworkLoads(
325 env, java_object_.obj());
328 void AwContentsIoThreadClientImpl::NewDownload(
330 const string& user_agent,
331 const string& content_disposition,
332 const string& mime_type,
333 int64 content_length) {
334 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
335 if (java_object_.is_null())
338 JNIEnv* env = AttachCurrentThread();
339 ScopedJavaLocalRef<jstring> jstring_url =
340 ConvertUTF8ToJavaString(env, url.spec());
341 ScopedJavaLocalRef<jstring> jstring_user_agent =
342 ConvertUTF8ToJavaString(env, user_agent);
343 ScopedJavaLocalRef<jstring> jstring_content_disposition =
344 ConvertUTF8ToJavaString(env, content_disposition);
345 ScopedJavaLocalRef<jstring> jstring_mime_type =
346 ConvertUTF8ToJavaString(env, mime_type);
348 Java_AwContentsIoThreadClient_onDownloadStart(
352 jstring_user_agent.obj(),
353 jstring_content_disposition.obj(),
354 jstring_mime_type.obj(),
358 void AwContentsIoThreadClientImpl::NewLoginRequest(const string& realm,
359 const string& account,
360 const string& args) {
361 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
362 if (java_object_.is_null())
365 JNIEnv* env = AttachCurrentThread();
366 ScopedJavaLocalRef<jstring> jrealm = ConvertUTF8ToJavaString(env, realm);
367 ScopedJavaLocalRef<jstring> jargs = ConvertUTF8ToJavaString(env, args);
369 ScopedJavaLocalRef<jstring> jaccount;
370 if (!account.empty())
371 jaccount = ConvertUTF8ToJavaString(env, account);
373 Java_AwContentsIoThreadClient_newLoginRequest(
374 env, java_object_.obj(), jrealm.obj(), jaccount.obj(), jargs.obj());
377 bool RegisterAwContentsIoThreadClientImpl(JNIEnv* env) {
378 return RegisterNativesImpl(env);
381 } // namespace android_webview