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