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/native/intercepted_request_data_impl.h"
11 #include "base/android/jni_helper.h"
12 #include "base/android/jni_string.h"
13 #include "base/lazy_instance.h"
14 #include "base/memory/linked_ptr.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/synchronization/lock.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "content/public/browser/render_process_host.h"
19 #include "content/public/browser/render_view_host.h"
20 #include "content/public/browser/resource_request_info.h"
21 #include "content/public/browser/web_contents.h"
22 #include "content/public/browser/web_contents_observer.h"
23 #include "jni/AwContentsIoThreadClient_jni.h"
24 #include "net/url_request/url_request.h"
25 #include "url/gurl.h"
26
27 using base::android::AttachCurrentThread;
28 using base::android::ConvertUTF8ToJavaString;
29 using base::android::JavaRef;
30 using base::android::ScopedJavaLocalRef;
31 using base::LazyInstance;
32 using content::BrowserThread;
33 using content::RenderViewHost;
34 using content::WebContents;
35 using std::map;
36 using std::pair;
37
38 namespace android_webview {
39
40 namespace {
41
42 struct IoThreadClientData {
43 bool pending_association;
44 JavaObjectWeakGlobalRef io_thread_client;
45
46 IoThreadClientData();
47 };
48
IoThreadClientData()49 IoThreadClientData::IoThreadClientData() : pending_association(false) {}
50
51 typedef map<pair<int, int>, IoThreadClientData>
52 RenderViewHostToIoThreadClientType;
53
GetRenderViewHostIdPair(RenderViewHost * rvh)54 static pair<int, int> GetRenderViewHostIdPair(RenderViewHost* rvh) {
55 return pair<int, int>(rvh->GetProcess()->GetID(), rvh->GetRoutingID());
56 }
57
58 // RvhToIoThreadClientMap -----------------------------------------------------
59 class RvhToIoThreadClientMap {
60 public:
61 static RvhToIoThreadClientMap* GetInstance();
62 void Set(pair<int, int> rvh_id, const IoThreadClientData& client);
63 bool Get(pair<int, int> rvh_id, IoThreadClientData* client);
64 void Erase(pair<int, int> rvh_id);
65
66 private:
67 static LazyInstance<RvhToIoThreadClientMap> g_instance_;
68 base::Lock map_lock_;
69 RenderViewHostToIoThreadClientType rvh_to_io_thread_client_;
70 };
71
72 // static
73 LazyInstance<RvhToIoThreadClientMap> RvhToIoThreadClientMap::g_instance_ =
74 LAZY_INSTANCE_INITIALIZER;
75
76 // static
GetInstance()77 RvhToIoThreadClientMap* RvhToIoThreadClientMap::GetInstance() {
78 return g_instance_.Pointer();
79 }
80
Set(pair<int,int> rvh_id,const IoThreadClientData & client)81 void RvhToIoThreadClientMap::Set(pair<int, int> rvh_id,
82 const IoThreadClientData& client) {
83 base::AutoLock lock(map_lock_);
84 rvh_to_io_thread_client_[rvh_id] = client;
85 }
86
Get(pair<int,int> rvh_id,IoThreadClientData * client)87 bool RvhToIoThreadClientMap::Get(
88 pair<int, int> rvh_id, IoThreadClientData* client) {
89 base::AutoLock lock(map_lock_);
90 RenderViewHostToIoThreadClientType::iterator iterator =
91 rvh_to_io_thread_client_.find(rvh_id);
92 if (iterator == rvh_to_io_thread_client_.end())
93 return false;
94
95 *client = iterator->second;
96 return true;
97 }
98
Erase(pair<int,int> rvh_id)99 void RvhToIoThreadClientMap::Erase(pair<int, int> rvh_id) {
100 base::AutoLock lock(map_lock_);
101 rvh_to_io_thread_client_.erase(rvh_id);
102 }
103
104 // ClientMapEntryUpdater ------------------------------------------------------
105
106 class ClientMapEntryUpdater : public content::WebContentsObserver {
107 public:
108 ClientMapEntryUpdater(JNIEnv* env, WebContents* web_contents,
109 jobject jdelegate);
110
111 virtual void RenderViewCreated(RenderViewHost* render_view_host) OVERRIDE;
112 virtual void RenderViewDeleted(RenderViewHost* render_view_host) OVERRIDE;
113 virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE;
114
115 private:
116 JavaObjectWeakGlobalRef jdelegate_;
117 };
118
ClientMapEntryUpdater(JNIEnv * env,WebContents * web_contents,jobject jdelegate)119 ClientMapEntryUpdater::ClientMapEntryUpdater(JNIEnv* env,
120 WebContents* web_contents,
121 jobject jdelegate)
122 : content::WebContentsObserver(web_contents),
123 jdelegate_(env, jdelegate) {
124 DCHECK(web_contents);
125 DCHECK(jdelegate);
126
127 if (web_contents->GetRenderViewHost())
128 RenderViewCreated(web_contents->GetRenderViewHost());
129 }
130
RenderViewCreated(RenderViewHost * rvh)131 void ClientMapEntryUpdater::RenderViewCreated(RenderViewHost* rvh) {
132 IoThreadClientData client_data;
133 client_data.io_thread_client = jdelegate_;
134 client_data.pending_association = false;
135 RvhToIoThreadClientMap::GetInstance()->Set(
136 GetRenderViewHostIdPair(rvh), client_data);
137 }
138
RenderViewDeleted(RenderViewHost * rvh)139 void ClientMapEntryUpdater::RenderViewDeleted(RenderViewHost* rvh) {
140 RvhToIoThreadClientMap::GetInstance()->Erase(GetRenderViewHostIdPair(rvh));
141 }
142
WebContentsDestroyed(WebContents * web_contents)143 void ClientMapEntryUpdater::WebContentsDestroyed(WebContents* web_contents) {
144 delete this;
145 }
146
147 } // namespace
148
149 // AwContentsIoThreadClientImpl -----------------------------------------------
150
151 // static
152 scoped_ptr<AwContentsIoThreadClient>
FromID(int render_process_id,int render_view_id)153 AwContentsIoThreadClient::FromID(int render_process_id, int render_view_id) {
154 pair<int, int> rvh_id(render_process_id, render_view_id);
155 IoThreadClientData client_data;
156 if (!RvhToIoThreadClientMap::GetInstance()->Get(rvh_id, &client_data))
157 return scoped_ptr<AwContentsIoThreadClient>();
158
159 JNIEnv* env = AttachCurrentThread();
160 ScopedJavaLocalRef<jobject> java_delegate =
161 client_data.io_thread_client.get(env);
162 DCHECK(!client_data.pending_association || java_delegate.is_null());
163 return scoped_ptr<AwContentsIoThreadClient>(new AwContentsIoThreadClientImpl(
164 client_data.pending_association, java_delegate));
165 }
166
167 // static
RegisterPendingContents(WebContents * web_contents)168 void AwContentsIoThreadClientImpl::RegisterPendingContents(
169 WebContents* web_contents) {
170 IoThreadClientData client_data;
171 client_data.pending_association = true;
172 RvhToIoThreadClientMap::GetInstance()->Set(
173 GetRenderViewHostIdPair(web_contents->GetRenderViewHost()), client_data);
174 }
175
176 // static
Associate(WebContents * web_contents,const JavaRef<jobject> & jclient)177 void AwContentsIoThreadClientImpl::Associate(
178 WebContents* web_contents,
179 const JavaRef<jobject>& jclient) {
180 JNIEnv* env = AttachCurrentThread();
181 // The ClientMapEntryUpdater lifespan is tied to the WebContents.
182 new ClientMapEntryUpdater(env, web_contents, jclient.obj());
183 }
184
AwContentsIoThreadClientImpl(bool pending_association,const JavaRef<jobject> & obj)185 AwContentsIoThreadClientImpl::AwContentsIoThreadClientImpl(
186 bool pending_association,
187 const JavaRef<jobject>& obj)
188 : pending_association_(pending_association),
189 java_object_(obj) {
190 }
191
~AwContentsIoThreadClientImpl()192 AwContentsIoThreadClientImpl::~AwContentsIoThreadClientImpl() {
193 // explict, out-of-line destructor.
194 }
195
PendingAssociation() const196 bool AwContentsIoThreadClientImpl::PendingAssociation() const {
197 return pending_association_;
198 }
199
200 AwContentsIoThreadClient::CacheMode
GetCacheMode() const201 AwContentsIoThreadClientImpl::GetCacheMode() const {
202 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
203 if (java_object_.is_null())
204 return AwContentsIoThreadClient::LOAD_DEFAULT;
205
206 JNIEnv* env = AttachCurrentThread();
207 return static_cast<AwContentsIoThreadClient::CacheMode>(
208 Java_AwContentsIoThreadClient_getCacheMode(
209 env, java_object_.obj()));
210 }
211
212 scoped_ptr<InterceptedRequestData>
ShouldInterceptRequest(const GURL & location,const net::URLRequest * request)213 AwContentsIoThreadClientImpl::ShouldInterceptRequest(
214 const GURL& location,
215 const net::URLRequest* request) {
216 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
217 if (java_object_.is_null())
218 return scoped_ptr<InterceptedRequestData>();
219 const content::ResourceRequestInfo* info =
220 content::ResourceRequestInfo::ForRequest(request);
221 bool is_main_frame = info &&
222 info->GetResourceType() == ResourceType::MAIN_FRAME;
223
224 JNIEnv* env = AttachCurrentThread();
225 ScopedJavaLocalRef<jstring> jstring_url =
226 ConvertUTF8ToJavaString(env, location.spec());
227 ScopedJavaLocalRef<jobject> ret =
228 Java_AwContentsIoThreadClient_shouldInterceptRequest(
229 env, java_object_.obj(), jstring_url.obj(), is_main_frame);
230 if (ret.is_null())
231 return scoped_ptr<InterceptedRequestData>();
232 return scoped_ptr<InterceptedRequestData>(
233 new InterceptedRequestDataImpl(ret));
234 }
235
ShouldBlockContentUrls() const236 bool AwContentsIoThreadClientImpl::ShouldBlockContentUrls() const {
237 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
238 if (java_object_.is_null())
239 return false;
240
241 JNIEnv* env = AttachCurrentThread();
242 return Java_AwContentsIoThreadClient_shouldBlockContentUrls(
243 env, java_object_.obj());
244 }
245
ShouldBlockFileUrls() const246 bool AwContentsIoThreadClientImpl::ShouldBlockFileUrls() const {
247 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
248 if (java_object_.is_null())
249 return false;
250
251 JNIEnv* env = AttachCurrentThread();
252 return Java_AwContentsIoThreadClient_shouldBlockFileUrls(
253 env, java_object_.obj());
254 }
255
ShouldBlockNetworkLoads() const256 bool AwContentsIoThreadClientImpl::ShouldBlockNetworkLoads() const {
257 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
258 if (java_object_.is_null())
259 return false;
260
261 JNIEnv* env = AttachCurrentThread();
262 return Java_AwContentsIoThreadClient_shouldBlockNetworkLoads(
263 env, java_object_.obj());
264 }
265
NewDownload(const GURL & url,const std::string & user_agent,const std::string & content_disposition,const std::string & mime_type,int64 content_length)266 void AwContentsIoThreadClientImpl::NewDownload(
267 const GURL& url,
268 const std::string& user_agent,
269 const std::string& content_disposition,
270 const std::string& mime_type,
271 int64 content_length) {
272 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
273 if (java_object_.is_null())
274 return;
275
276 JNIEnv* env = AttachCurrentThread();
277 ScopedJavaLocalRef<jstring> jstring_url =
278 ConvertUTF8ToJavaString(env, url.spec());
279 ScopedJavaLocalRef<jstring> jstring_user_agent =
280 ConvertUTF8ToJavaString(env, user_agent);
281 ScopedJavaLocalRef<jstring> jstring_content_disposition =
282 ConvertUTF8ToJavaString(env, content_disposition);
283 ScopedJavaLocalRef<jstring> jstring_mime_type =
284 ConvertUTF8ToJavaString(env, mime_type);
285
286 Java_AwContentsIoThreadClient_onDownloadStart(
287 env,
288 java_object_.obj(),
289 jstring_url.obj(),
290 jstring_user_agent.obj(),
291 jstring_content_disposition.obj(),
292 jstring_mime_type.obj(),
293 content_length);
294 }
295
NewLoginRequest(const std::string & realm,const std::string & account,const std::string & args)296 void AwContentsIoThreadClientImpl::NewLoginRequest(const std::string& realm,
297 const std::string& account,
298 const std::string& args) {
299 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
300 if (java_object_.is_null())
301 return;
302
303 JNIEnv* env = AttachCurrentThread();
304 ScopedJavaLocalRef<jstring> jrealm = ConvertUTF8ToJavaString(env, realm);
305 ScopedJavaLocalRef<jstring> jargs = ConvertUTF8ToJavaString(env, args);
306
307 ScopedJavaLocalRef<jstring> jaccount;
308 if (!account.empty())
309 jaccount = ConvertUTF8ToJavaString(env, account);
310
311 Java_AwContentsIoThreadClient_newLoginRequest(
312 env, java_object_.obj(), jrealm.obj(), jaccount.obj(), jargs.obj());
313 }
314
RegisterAwContentsIoThreadClientImpl(JNIEnv * env)315 bool RegisterAwContentsIoThreadClientImpl(JNIEnv* env) {
316 return RegisterNativesImpl(env);
317 }
318
319 } // namespace android_webview
320