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 <utility>
9
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"
29 #include "url/gurl.h"
30
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;
40 using std::map;
41 using std::pair;
42 using std::string;
43 using std::vector;
44
45 namespace android_webview {
46
47 namespace {
48
49 struct IoThreadClientData {
50 bool pending_association;
51 JavaObjectWeakGlobalRef io_thread_client;
52
53 IoThreadClientData();
54 };
55
IoThreadClientData()56 IoThreadClientData::IoThreadClientData() : pending_association(false) {}
57
58 typedef map<pair<int, int>, IoThreadClientData>
59 RenderFrameHostToIoThreadClientType;
60
GetRenderFrameHostIdPair(RenderFrameHost * rfh)61 static pair<int, int> GetRenderFrameHostIdPair(RenderFrameHost* rfh) {
62 return pair<int, int>(rfh->GetProcess()->GetID(), rfh->GetRoutingID());
63 }
64
65 // RfhToIoThreadClientMap -----------------------------------------------------
66 class RfhToIoThreadClientMap {
67 public:
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);
72
73 private:
74 static LazyInstance<RfhToIoThreadClientMap> g_instance_;
75 base::Lock map_lock_;
76 RenderFrameHostToIoThreadClientType rfh_to_io_thread_client_;
77 };
78
79 // static
80 LazyInstance<RfhToIoThreadClientMap> RfhToIoThreadClientMap::g_instance_ =
81 LAZY_INSTANCE_INITIALIZER;
82
83 // static
GetInstance()84 RfhToIoThreadClientMap* RfhToIoThreadClientMap::GetInstance() {
85 return g_instance_.Pointer();
86 }
87
Set(pair<int,int> rfh_id,const IoThreadClientData & client)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;
92 }
93
Get(pair<int,int> rfh_id,IoThreadClientData * 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())
100 return false;
101
102 *client = iterator->second;
103 return true;
104 }
105
Erase(pair<int,int> rfh_id)106 void RfhToIoThreadClientMap::Erase(pair<int, int> rfh_id) {
107 base::AutoLock lock(map_lock_);
108 rfh_to_io_thread_client_.erase(rfh_id);
109 }
110
111 // ClientMapEntryUpdater ------------------------------------------------------
112
113 class ClientMapEntryUpdater : public content::WebContentsObserver {
114 public:
115 ClientMapEntryUpdater(JNIEnv* env, WebContents* web_contents,
116 jobject jdelegate);
117
118 virtual void RenderFrameCreated(RenderFrameHost* render_frame_host) OVERRIDE;
119 virtual void RenderFrameDeleted(RenderFrameHost* render_frame_host) OVERRIDE;
120 virtual void WebContentsDestroyed() OVERRIDE;
121
122 private:
123 JavaObjectWeakGlobalRef jdelegate_;
124 };
125
ClientMapEntryUpdater(JNIEnv * env,WebContents * web_contents,jobject jdelegate)126 ClientMapEntryUpdater::ClientMapEntryUpdater(JNIEnv* env,
127 WebContents* web_contents,
128 jobject jdelegate)
129 : content::WebContentsObserver(web_contents),
130 jdelegate_(env, jdelegate) {
131 DCHECK(web_contents);
132 DCHECK(jdelegate);
133
134 if (web_contents->GetMainFrame())
135 RenderFrameCreated(web_contents->GetMainFrame());
136 }
137
RenderFrameCreated(RenderFrameHost * rfh)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);
144 }
145
RenderFrameDeleted(RenderFrameHost * rfh)146 void ClientMapEntryUpdater::RenderFrameDeleted(RenderFrameHost* rfh) {
147 RfhToIoThreadClientMap::GetInstance()->Erase(GetRenderFrameHostIdPair(rfh));
148 }
149
WebContentsDestroyed()150 void ClientMapEntryUpdater::WebContentsDestroyed() {
151 delete this;
152 }
153
154 } // namespace
155
156 // AwContentsIoThreadClientImpl -----------------------------------------------
157
158 // static
159 scoped_ptr<AwContentsIoThreadClient>
FromID(int render_process_id,int render_frame_id)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>();
165
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));
172 }
173
174 // static
SubFrameCreated(int render_process_id,int parent_render_frame_id,int child_render_frame_id)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,
182 &client_data)) {
183 NOTREACHED();
184 return;
185 }
186
187 RfhToIoThreadClientMap::GetInstance()->Set(child_rfh_id, client_data);
188 }
189
190 // static
RegisterPendingContents(WebContents * web_contents)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);
197 }
198
199 // static
Associate(WebContents * web_contents,const JavaRef<jobject> & jclient)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());
206 }
207
AwContentsIoThreadClientImpl(bool pending_association,const JavaRef<jobject> & obj)208 AwContentsIoThreadClientImpl::AwContentsIoThreadClientImpl(
209 bool pending_association,
210 const JavaRef<jobject>& obj)
211 : pending_association_(pending_association),
212 java_object_(obj) {
213 }
214
~AwContentsIoThreadClientImpl()215 AwContentsIoThreadClientImpl::~AwContentsIoThreadClientImpl() {
216 // explict, out-of-line destructor.
217 }
218
PendingAssociation() const219 bool AwContentsIoThreadClientImpl::PendingAssociation() const {
220 return pending_association_;
221 }
222
223 AwContentsIoThreadClient::CacheMode
GetCacheMode() const224 AwContentsIoThreadClientImpl::GetCacheMode() const {
225 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
226 if (java_object_.is_null())
227 return AwContentsIoThreadClient::LOAD_DEFAULT;
228
229 JNIEnv* env = AttachCurrentThread();
230 return static_cast<AwContentsIoThreadClient::CacheMode>(
231 Java_AwContentsIoThreadClient_getCacheMode(
232 env, java_object_.obj()));
233 }
234
235 scoped_ptr<AwWebResourceResponse>
ShouldInterceptRequest(const GURL & location,const net::URLRequest * request)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();
247
248 vector<string> headers_names;
249 vector<string> headers_values;
250 {
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());
258 }
259 }
260
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(
274 env,
275 java_object_.obj(),
276 jstring_url.obj(),
277 is_main_frame,
278 has_user_gesture,
279 jstring_method.obj(),
280 jstringArray_headers_names.obj(),
281 jstringArray_headers_values.obj());
282 if (ret.is_null())
283 return scoped_ptr<AwWebResourceResponse>();
284 return scoped_ptr<AwWebResourceResponse>(
285 new AwWebResourceResponseImpl(ret));
286 }
287
ShouldBlockContentUrls() const288 bool AwContentsIoThreadClientImpl::ShouldBlockContentUrls() const {
289 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
290 if (java_object_.is_null())
291 return false;
292
293 JNIEnv* env = AttachCurrentThread();
294 return Java_AwContentsIoThreadClient_shouldBlockContentUrls(
295 env, java_object_.obj());
296 }
297
ShouldBlockFileUrls() const298 bool AwContentsIoThreadClientImpl::ShouldBlockFileUrls() const {
299 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
300 if (java_object_.is_null())
301 return false;
302
303 JNIEnv* env = AttachCurrentThread();
304 return Java_AwContentsIoThreadClient_shouldBlockFileUrls(
305 env, java_object_.obj());
306 }
307
ShouldAcceptThirdPartyCookies() const308 bool AwContentsIoThreadClientImpl::ShouldAcceptThirdPartyCookies() const {
309 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
310 if (java_object_.is_null())
311 return false;
312
313 JNIEnv* env = AttachCurrentThread();
314 return Java_AwContentsIoThreadClient_shouldAcceptThirdPartyCookies(
315 env, java_object_.obj());
316 }
317
ShouldBlockNetworkLoads() const318 bool AwContentsIoThreadClientImpl::ShouldBlockNetworkLoads() const {
319 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
320 if (java_object_.is_null())
321 return false;
322
323 JNIEnv* env = AttachCurrentThread();
324 return Java_AwContentsIoThreadClient_shouldBlockNetworkLoads(
325 env, java_object_.obj());
326 }
327
NewDownload(const GURL & url,const string & user_agent,const string & content_disposition,const string & mime_type,int64 content_length)328 void AwContentsIoThreadClientImpl::NewDownload(
329 const GURL& url,
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())
336 return;
337
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);
347
348 Java_AwContentsIoThreadClient_onDownloadStart(
349 env,
350 java_object_.obj(),
351 jstring_url.obj(),
352 jstring_user_agent.obj(),
353 jstring_content_disposition.obj(),
354 jstring_mime_type.obj(),
355 content_length);
356 }
357
NewLoginRequest(const string & realm,const string & account,const string & args)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())
363 return;
364
365 JNIEnv* env = AttachCurrentThread();
366 ScopedJavaLocalRef<jstring> jrealm = ConvertUTF8ToJavaString(env, realm);
367 ScopedJavaLocalRef<jstring> jargs = ConvertUTF8ToJavaString(env, args);
368
369 ScopedJavaLocalRef<jstring> jaccount;
370 if (!account.empty())
371 jaccount = ConvertUTF8ToJavaString(env, account);
372
373 Java_AwContentsIoThreadClient_newLoginRequest(
374 env, java_object_.obj(), jrealm.obj(), jaccount.obj(), jargs.obj());
375 }
376
RegisterAwContentsIoThreadClientImpl(JNIEnv * env)377 bool RegisterAwContentsIoThreadClientImpl(JNIEnv* env) {
378 return RegisterNativesImpl(env);
379 }
380
381 } // namespace android_webview
382