[Android WebViewShell] Add inclusion test for webview exposed stable interfaces.
[chromium-blink-merge.git] / android_webview / native / aw_contents_io_thread_client_impl.cc
blob11ac6f0fd6278c98979593688c8f3a5e15b07110
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"
7 #include <map>
8 #include <utility>
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/base/net_errors.h"
28 #include "net/http/http_request_headers.h"
29 #include "net/http/http_response_headers.h"
30 #include "net/url_request/url_request.h"
31 #include "url/gurl.h"
33 using base::android::AttachCurrentThread;
34 using base::android::ConvertUTF8ToJavaString;
35 using base::android::JavaRef;
36 using base::android::ScopedJavaLocalRef;
37 using base::android::ToJavaArrayOfStrings;
38 using base::LazyInstance;
39 using content::BrowserThread;
40 using content::RenderFrameHost;
41 using content::ResourceType;
42 using content::WebContents;
43 using std::map;
44 using std::pair;
45 using std::string;
46 using std::vector;
48 namespace android_webview {
50 namespace {
52 struct IoThreadClientData {
53 bool pending_association;
54 JavaObjectWeakGlobalRef io_thread_client;
56 IoThreadClientData();
59 IoThreadClientData::IoThreadClientData() : pending_association(false) {}
61 typedef map<pair<int, int>, IoThreadClientData>
62 RenderFrameHostToIoThreadClientType;
64 static pair<int, int> GetRenderFrameHostIdPair(RenderFrameHost* rfh) {
65 return pair<int, int>(rfh->GetProcess()->GetID(), rfh->GetRoutingID());
68 // RfhToIoThreadClientMap -----------------------------------------------------
69 class RfhToIoThreadClientMap {
70 public:
71 static RfhToIoThreadClientMap* GetInstance();
72 void Set(pair<int, int> rfh_id, const IoThreadClientData& client);
73 bool Get(pair<int, int> rfh_id, IoThreadClientData* client);
74 void Erase(pair<int, int> rfh_id);
76 private:
77 static LazyInstance<RfhToIoThreadClientMap> g_instance_;
78 base::Lock map_lock_;
79 RenderFrameHostToIoThreadClientType rfh_to_io_thread_client_;
82 // static
83 LazyInstance<RfhToIoThreadClientMap> RfhToIoThreadClientMap::g_instance_ =
84 LAZY_INSTANCE_INITIALIZER;
86 // static
87 RfhToIoThreadClientMap* RfhToIoThreadClientMap::GetInstance() {
88 return g_instance_.Pointer();
91 void RfhToIoThreadClientMap::Set(pair<int, int> rfh_id,
92 const IoThreadClientData& client) {
93 base::AutoLock lock(map_lock_);
94 rfh_to_io_thread_client_[rfh_id] = client;
97 bool RfhToIoThreadClientMap::Get(
98 pair<int, int> rfh_id, IoThreadClientData* client) {
99 base::AutoLock lock(map_lock_);
100 RenderFrameHostToIoThreadClientType::iterator iterator =
101 rfh_to_io_thread_client_.find(rfh_id);
102 if (iterator == rfh_to_io_thread_client_.end())
103 return false;
105 *client = iterator->second;
106 return true;
109 void RfhToIoThreadClientMap::Erase(pair<int, int> rfh_id) {
110 base::AutoLock lock(map_lock_);
111 rfh_to_io_thread_client_.erase(rfh_id);
114 // ClientMapEntryUpdater ------------------------------------------------------
116 class ClientMapEntryUpdater : public content::WebContentsObserver {
117 public:
118 ClientMapEntryUpdater(JNIEnv* env, WebContents* web_contents,
119 jobject jdelegate);
121 void RenderFrameCreated(RenderFrameHost* render_frame_host) override;
122 void RenderFrameDeleted(RenderFrameHost* render_frame_host) override;
123 void WebContentsDestroyed() override;
125 private:
126 JavaObjectWeakGlobalRef jdelegate_;
129 ClientMapEntryUpdater::ClientMapEntryUpdater(JNIEnv* env,
130 WebContents* web_contents,
131 jobject jdelegate)
132 : content::WebContentsObserver(web_contents),
133 jdelegate_(env, jdelegate) {
134 DCHECK(web_contents);
135 DCHECK(jdelegate);
137 if (web_contents->GetMainFrame())
138 RenderFrameCreated(web_contents->GetMainFrame());
141 void ClientMapEntryUpdater::RenderFrameCreated(RenderFrameHost* rfh) {
142 IoThreadClientData client_data;
143 client_data.io_thread_client = jdelegate_;
144 client_data.pending_association = false;
145 RfhToIoThreadClientMap::GetInstance()->Set(
146 GetRenderFrameHostIdPair(rfh), client_data);
149 void ClientMapEntryUpdater::RenderFrameDeleted(RenderFrameHost* rfh) {
150 RfhToIoThreadClientMap::GetInstance()->Erase(GetRenderFrameHostIdPair(rfh));
153 void ClientMapEntryUpdater::WebContentsDestroyed() {
154 delete this;
157 struct WebResourceRequest {
158 ScopedJavaLocalRef<jstring> jstring_url;
159 bool is_main_frame;
160 bool has_user_gesture;
161 ScopedJavaLocalRef<jstring> jstring_method;
162 ScopedJavaLocalRef<jobjectArray> jstringArray_header_names;
163 ScopedJavaLocalRef<jobjectArray> jstringArray_header_values;
165 WebResourceRequest(JNIEnv* env, const net::URLRequest* request)
166 : jstring_url(ConvertUTF8ToJavaString(env, request->url().spec())),
167 jstring_method(ConvertUTF8ToJavaString(env, request->method())) {
168 const content::ResourceRequestInfo* info =
169 content::ResourceRequestInfo::ForRequest(request);
170 is_main_frame =
171 info && info->GetResourceType() == content::RESOURCE_TYPE_MAIN_FRAME;
172 has_user_gesture = info && info->HasUserGesture();
174 vector<string> header_names;
175 vector<string> header_values;
176 net::HttpRequestHeaders headers;
177 if (!request->GetFullRequestHeaders(&headers))
178 headers = request->extra_request_headers();
179 net::HttpRequestHeaders::Iterator headers_iterator(headers);
180 while (headers_iterator.GetNext()) {
181 header_names.push_back(headers_iterator.name());
182 header_values.push_back(headers_iterator.value());
184 jstringArray_header_names = ToJavaArrayOfStrings(env, header_names);
185 jstringArray_header_values = ToJavaArrayOfStrings(env, header_values);
189 } // namespace
191 // AwContentsIoThreadClientImpl -----------------------------------------------
193 // static
194 scoped_ptr<AwContentsIoThreadClient>
195 AwContentsIoThreadClient::FromID(int render_process_id, int render_frame_id) {
196 pair<int, int> rfh_id(render_process_id, render_frame_id);
197 IoThreadClientData client_data;
198 if (!RfhToIoThreadClientMap::GetInstance()->Get(rfh_id, &client_data))
199 return scoped_ptr<AwContentsIoThreadClient>();
201 JNIEnv* env = AttachCurrentThread();
202 ScopedJavaLocalRef<jobject> java_delegate =
203 client_data.io_thread_client.get(env);
204 DCHECK(!client_data.pending_association || java_delegate.is_null());
205 return scoped_ptr<AwContentsIoThreadClient>(new AwContentsIoThreadClientImpl(
206 client_data.pending_association, java_delegate));
209 // static
210 void AwContentsIoThreadClient::SubFrameCreated(int render_process_id,
211 int parent_render_frame_id,
212 int child_render_frame_id) {
213 pair<int, int> parent_rfh_id(render_process_id, parent_render_frame_id);
214 pair<int, int> child_rfh_id(render_process_id, child_render_frame_id);
215 IoThreadClientData client_data;
216 if (!RfhToIoThreadClientMap::GetInstance()->Get(parent_rfh_id,
217 &client_data)) {
218 NOTREACHED();
219 return;
222 RfhToIoThreadClientMap::GetInstance()->Set(child_rfh_id, client_data);
225 // static
226 void AwContentsIoThreadClientImpl::RegisterPendingContents(
227 WebContents* web_contents) {
228 IoThreadClientData client_data;
229 client_data.pending_association = true;
230 RfhToIoThreadClientMap::GetInstance()->Set(
231 GetRenderFrameHostIdPair(web_contents->GetMainFrame()), client_data);
234 // static
235 void AwContentsIoThreadClientImpl::Associate(
236 WebContents* web_contents,
237 const JavaRef<jobject>& jclient) {
238 JNIEnv* env = AttachCurrentThread();
239 // The ClientMapEntryUpdater lifespan is tied to the WebContents.
240 new ClientMapEntryUpdater(env, web_contents, jclient.obj());
243 AwContentsIoThreadClientImpl::AwContentsIoThreadClientImpl(
244 bool pending_association,
245 const JavaRef<jobject>& obj)
246 : pending_association_(pending_association),
247 java_object_(obj) {
250 AwContentsIoThreadClientImpl::~AwContentsIoThreadClientImpl() {
251 // explict, out-of-line destructor.
254 bool AwContentsIoThreadClientImpl::PendingAssociation() const {
255 return pending_association_;
258 AwContentsIoThreadClient::CacheMode
259 AwContentsIoThreadClientImpl::GetCacheMode() const {
260 DCHECK_CURRENTLY_ON(BrowserThread::IO);
261 if (java_object_.is_null())
262 return AwContentsIoThreadClient::LOAD_DEFAULT;
264 JNIEnv* env = AttachCurrentThread();
265 return static_cast<AwContentsIoThreadClient::CacheMode>(
266 Java_AwContentsIoThreadClient_getCacheMode(
267 env, java_object_.obj()));
270 scoped_ptr<AwWebResourceResponse>
271 AwContentsIoThreadClientImpl::ShouldInterceptRequest(
272 const net::URLRequest* request) {
273 DCHECK_CURRENTLY_ON(BrowserThread::IO);
274 if (java_object_.is_null())
275 return scoped_ptr<AwWebResourceResponse>();
277 JNIEnv* env = AttachCurrentThread();
278 WebResourceRequest web_request(env, request);
280 devtools_instrumentation::ScopedEmbedderCallbackTask embedder_callback(
281 "shouldInterceptRequest");
282 ScopedJavaLocalRef<jobject> ret =
283 Java_AwContentsIoThreadClient_shouldInterceptRequest(
284 env,
285 java_object_.obj(),
286 web_request.jstring_url.obj(),
287 web_request.is_main_frame,
288 web_request.has_user_gesture,
289 web_request.jstring_method.obj(),
290 web_request.jstringArray_header_names.obj(),
291 web_request.jstringArray_header_values.obj());
292 if (ret.is_null())
293 return scoped_ptr<AwWebResourceResponse>();
294 return scoped_ptr<AwWebResourceResponse>(
295 new AwWebResourceResponseImpl(ret));
298 bool AwContentsIoThreadClientImpl::ShouldBlockContentUrls() const {
299 DCHECK_CURRENTLY_ON(BrowserThread::IO);
300 if (java_object_.is_null())
301 return false;
303 JNIEnv* env = AttachCurrentThread();
304 return Java_AwContentsIoThreadClient_shouldBlockContentUrls(
305 env, java_object_.obj());
308 bool AwContentsIoThreadClientImpl::ShouldBlockFileUrls() const {
309 DCHECK_CURRENTLY_ON(BrowserThread::IO);
310 if (java_object_.is_null())
311 return false;
313 JNIEnv* env = AttachCurrentThread();
314 return Java_AwContentsIoThreadClient_shouldBlockFileUrls(
315 env, java_object_.obj());
318 bool AwContentsIoThreadClientImpl::ShouldAcceptThirdPartyCookies() const {
319 DCHECK_CURRENTLY_ON(BrowserThread::IO);
320 if (java_object_.is_null())
321 return false;
323 JNIEnv* env = AttachCurrentThread();
324 return Java_AwContentsIoThreadClient_shouldAcceptThirdPartyCookies(
325 env, java_object_.obj());
328 bool AwContentsIoThreadClientImpl::ShouldBlockNetworkLoads() const {
329 DCHECK_CURRENTLY_ON(BrowserThread::IO);
330 if (java_object_.is_null())
331 return false;
333 JNIEnv* env = AttachCurrentThread();
334 return Java_AwContentsIoThreadClient_shouldBlockNetworkLoads(
335 env, java_object_.obj());
338 void AwContentsIoThreadClientImpl::NewDownload(
339 const GURL& url,
340 const string& user_agent,
341 const string& content_disposition,
342 const string& mime_type,
343 int64 content_length) {
344 DCHECK_CURRENTLY_ON(BrowserThread::IO);
345 if (java_object_.is_null())
346 return;
348 JNIEnv* env = AttachCurrentThread();
349 ScopedJavaLocalRef<jstring> jstring_url =
350 ConvertUTF8ToJavaString(env, url.spec());
351 ScopedJavaLocalRef<jstring> jstring_user_agent =
352 ConvertUTF8ToJavaString(env, user_agent);
353 ScopedJavaLocalRef<jstring> jstring_content_disposition =
354 ConvertUTF8ToJavaString(env, content_disposition);
355 ScopedJavaLocalRef<jstring> jstring_mime_type =
356 ConvertUTF8ToJavaString(env, mime_type);
358 Java_AwContentsIoThreadClient_onDownloadStart(
359 env,
360 java_object_.obj(),
361 jstring_url.obj(),
362 jstring_user_agent.obj(),
363 jstring_content_disposition.obj(),
364 jstring_mime_type.obj(),
365 content_length);
368 void AwContentsIoThreadClientImpl::NewLoginRequest(const string& realm,
369 const string& account,
370 const string& args) {
371 DCHECK_CURRENTLY_ON(BrowserThread::IO);
372 if (java_object_.is_null())
373 return;
375 JNIEnv* env = AttachCurrentThread();
376 ScopedJavaLocalRef<jstring> jrealm = ConvertUTF8ToJavaString(env, realm);
377 ScopedJavaLocalRef<jstring> jargs = ConvertUTF8ToJavaString(env, args);
379 ScopedJavaLocalRef<jstring> jaccount;
380 if (!account.empty())
381 jaccount = ConvertUTF8ToJavaString(env, account);
383 Java_AwContentsIoThreadClient_newLoginRequest(
384 env, java_object_.obj(), jrealm.obj(), jaccount.obj(), jargs.obj());
387 void AwContentsIoThreadClientImpl::OnReceivedError(
388 const net::URLRequest* request) {
389 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
390 if (java_object_.is_null())
391 return;
393 JNIEnv* env = AttachCurrentThread();
394 WebResourceRequest web_request(env, request);
396 int error_code = request->status().error();
397 ScopedJavaLocalRef<jstring> jstring_description = ConvertUTF8ToJavaString(
398 env, net::ErrorToString(request->status().error()));
400 Java_AwContentsIoThreadClient_onReceivedError(
401 env,
402 java_object_.obj(),
403 web_request.jstring_url.obj(),
404 web_request.is_main_frame,
405 web_request.has_user_gesture,
406 web_request.jstring_method.obj(),
407 web_request.jstringArray_header_names.obj(),
408 web_request.jstringArray_header_values.obj(),
409 error_code,
410 jstring_description.obj());
413 void AwContentsIoThreadClientImpl::OnReceivedHttpError(
414 const net::URLRequest* request,
415 const net::HttpResponseHeaders* response_headers) {
416 DCHECK_CURRENTLY_ON(BrowserThread::IO);
417 if (java_object_.is_null())
418 return;
420 JNIEnv* env = AttachCurrentThread();
421 WebResourceRequest web_request(env, request);
423 vector<string> response_header_names;
424 vector<string> response_header_values;
426 void* headers_iterator = NULL;
427 string header_name, header_value;
428 while (response_headers->EnumerateHeaderLines(
429 &headers_iterator, &header_name, &header_value)) {
430 response_header_names.push_back(header_name);
431 response_header_values.push_back(header_value);
435 string mime_type, encoding;
436 response_headers->GetMimeTypeAndCharset(&mime_type, &encoding);
437 ScopedJavaLocalRef<jstring> jstring_mime_type =
438 ConvertUTF8ToJavaString(env, mime_type);
439 ScopedJavaLocalRef<jstring> jstring_encoding =
440 ConvertUTF8ToJavaString(env, encoding);
441 int status_code = response_headers->response_code();
442 ScopedJavaLocalRef<jstring> jstring_reason =
443 ConvertUTF8ToJavaString(env, response_headers->GetStatusText());
444 ScopedJavaLocalRef<jobjectArray> jstringArray_response_header_names =
445 ToJavaArrayOfStrings(env, response_header_names);
446 ScopedJavaLocalRef<jobjectArray> jstringArray_response_header_values =
447 ToJavaArrayOfStrings(env, response_header_values);
449 Java_AwContentsIoThreadClient_onReceivedHttpError(
450 env,
451 java_object_.obj(),
452 web_request.jstring_url.obj(),
453 web_request.is_main_frame,
454 web_request.has_user_gesture,
455 web_request.jstring_method.obj(),
456 web_request.jstringArray_header_names.obj(),
457 web_request.jstringArray_header_values.obj(),
458 jstring_mime_type.obj(),
459 jstring_encoding.obj(),
460 status_code,
461 jstring_reason.obj(),
462 jstringArray_response_header_names.obj(),
463 jstringArray_response_header_values.obj());
466 bool RegisterAwContentsIoThreadClientImpl(JNIEnv* env) {
467 return RegisterNativesImpl(env);
470 } // namespace android_webview