Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / android_webview / native / aw_contents_client_bridge.cc
blob5431be749a6302a14e7762d752257d4e648ba6c7
1 // Copyright (c) 2013 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_client_bridge.h"
7 #include "android_webview/common/devtools_instrumentation.h"
8 #include "android_webview/native/aw_contents.h"
9 #include "base/android/jni_android.h"
10 #include "base/android/jni_array.h"
11 #include "base/android/jni_string.h"
12 #include "base/callback_helpers.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "content/public/browser/render_process_host.h"
15 #include "content/public/browser/render_view_host.h"
16 #include "content/public/browser/web_contents.h"
17 #include "crypto/scoped_openssl_types.h"
18 #include "jni/AwContentsClientBridge_jni.h"
19 #include "net/android/keystore_openssl.h"
20 #include "net/cert/x509_certificate.h"
21 #include "net/ssl/openssl_client_key_store.h"
22 #include "net/ssl/ssl_cert_request_info.h"
23 #include "net/ssl/ssl_client_cert_type.h"
24 #include "url/gurl.h"
26 using base::android::AttachCurrentThread;
27 using base::android::ConvertJavaStringToUTF16;
28 using base::android::ConvertUTF8ToJavaString;
29 using base::android::ConvertUTF16ToJavaString;
30 using base::android::JavaRef;
31 using base::android::ScopedJavaLocalRef;
32 using content::BrowserThread;
34 namespace android_webview {
36 namespace {
38 // Must be called on the I/O thread to record a client certificate
39 // and its private key in the OpenSSLClientKeyStore.
40 void RecordClientCertificateKey(
41 const scoped_refptr<net::X509Certificate>& client_cert,
42 crypto::ScopedEVP_PKEY private_key) {
43 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
44 net::OpenSSLClientKeyStore::GetInstance()->RecordClientCertPrivateKey(
45 client_cert.get(), private_key.get());
48 } // namespace
50 AwContentsClientBridge::AwContentsClientBridge(JNIEnv* env, jobject obj)
51 : java_ref_(env, obj) {
52 DCHECK(obj);
53 Java_AwContentsClientBridge_setNativeContentsClientBridge(
54 env, obj, reinterpret_cast<intptr_t>(this));
57 AwContentsClientBridge::~AwContentsClientBridge() {
58 JNIEnv* env = AttachCurrentThread();
60 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
61 if (obj.is_null())
62 return;
63 // Clear the weak reference from the java peer to the native object since
64 // it is possible that java object lifetime can exceed the AwContens.
65 Java_AwContentsClientBridge_setNativeContentsClientBridge(env, obj.obj(), 0);
68 void AwContentsClientBridge::AllowCertificateError(
69 int cert_error,
70 net::X509Certificate* cert,
71 const GURL& request_url,
72 const base::Callback<void(bool)>& callback,
73 bool* cancel_request) {
75 DCHECK_CURRENTLY_ON(BrowserThread::UI);
76 JNIEnv* env = AttachCurrentThread();
78 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
79 if (obj.is_null())
80 return;
82 std::string der_string;
83 net::X509Certificate::GetDEREncoded(cert->os_cert_handle(), &der_string);
84 ScopedJavaLocalRef<jbyteArray> jcert = base::android::ToJavaByteArray(
85 env,
86 reinterpret_cast<const uint8*>(der_string.data()),
87 der_string.length());
88 ScopedJavaLocalRef<jstring> jurl(ConvertUTF8ToJavaString(
89 env, request_url.spec()));
90 // We need to add the callback before making the call to java side,
91 // as it may do a synchronous callback prior to returning.
92 int request_id = pending_cert_error_callbacks_.Add(
93 new CertErrorCallback(callback));
94 *cancel_request = !Java_AwContentsClientBridge_allowCertificateError(
95 env, obj.obj(), cert_error, jcert.obj(), jurl.obj(), request_id);
96 // if the request is cancelled, then cancel the stored callback
97 if (*cancel_request) {
98 pending_cert_error_callbacks_.Remove(request_id);
102 void AwContentsClientBridge::ProceedSslError(JNIEnv* env, jobject obj,
103 jboolean proceed, jint id) {
104 DCHECK_CURRENTLY_ON(BrowserThread::UI);
105 CertErrorCallback* callback = pending_cert_error_callbacks_.Lookup(id);
106 if (!callback || callback->is_null()) {
107 LOG(WARNING) << "Ignoring unexpected ssl error proceed callback";
108 return;
110 callback->Run(proceed);
111 pending_cert_error_callbacks_.Remove(id);
114 // This method is inspired by SelectClientCertificate() in
115 // chrome/browser/ui/android/ssl_client_certificate_request.cc
116 void AwContentsClientBridge::SelectClientCertificate(
117 net::SSLCertRequestInfo* cert_request_info,
118 const SelectCertificateCallback& callback) {
119 DCHECK_CURRENTLY_ON(BrowserThread::UI);
121 // Add the callback to id map.
122 int request_id = pending_client_cert_request_callbacks_.Add(
123 new SelectCertificateCallback(callback));
124 // Make sure callback is run on error.
125 base::ScopedClosureRunner guard(base::Bind(
126 &AwContentsClientBridge::HandleErrorInClientCertificateResponse,
127 base::Unretained(this),
128 request_id));
130 JNIEnv* env = base::android::AttachCurrentThread();
131 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
132 if (obj.is_null())
133 return;
135 // Build the |key_types| JNI parameter, as a String[]
136 std::vector<std::string> key_types;
137 for (size_t i = 0; i < cert_request_info->cert_key_types.size(); ++i) {
138 switch (cert_request_info->cert_key_types[i]) {
139 case net::CLIENT_CERT_RSA_SIGN:
140 key_types.push_back("RSA");
141 break;
142 case net::CLIENT_CERT_DSS_SIGN:
143 key_types.push_back("DSA");
144 break;
145 case net::CLIENT_CERT_ECDSA_SIGN:
146 key_types.push_back("ECDSA");
147 break;
148 default:
149 // Ignore unknown types.
150 break;
154 ScopedJavaLocalRef<jobjectArray> key_types_ref =
155 base::android::ToJavaArrayOfStrings(env, key_types);
156 if (key_types_ref.is_null()) {
157 LOG(ERROR) << "Could not create key types array (String[])";
158 return;
161 // Build the |encoded_principals| JNI parameter, as a byte[][]
162 ScopedJavaLocalRef<jobjectArray> principals_ref =
163 base::android::ToJavaArrayOfByteArray(
164 env, cert_request_info->cert_authorities);
165 if (principals_ref.is_null()) {
166 LOG(ERROR) << "Could not create principals array (byte[][])";
167 return;
170 // Build the |host_name| and |port| JNI parameters, as a String and
171 // a jint.
172 ScopedJavaLocalRef<jstring> host_name_ref =
173 base::android::ConvertUTF8ToJavaString(
174 env, cert_request_info->host_and_port.host());
176 Java_AwContentsClientBridge_selectClientCertificate(
177 env,
178 obj.obj(),
179 request_id,
180 key_types_ref.obj(),
181 principals_ref.obj(),
182 host_name_ref.obj(),
183 cert_request_info->host_and_port.port());
185 // Release the guard.
186 ignore_result(guard.Release());
189 // This method is inspired by OnSystemRequestCompletion() in
190 // chrome/browser/ui/android/ssl_client_certificate_request.cc
191 void AwContentsClientBridge::ProvideClientCertificateResponse(
192 JNIEnv* env,
193 jobject obj,
194 int request_id,
195 jobjectArray encoded_chain_ref,
196 jobject private_key_ref) {
197 DCHECK_CURRENTLY_ON(BrowserThread::UI);
199 SelectCertificateCallback* callback =
200 pending_client_cert_request_callbacks_.Lookup(request_id);
201 DCHECK(callback);
203 // Make sure callback is run on error.
204 base::ScopedClosureRunner guard(base::Bind(
205 &AwContentsClientBridge::HandleErrorInClientCertificateResponse,
206 base::Unretained(this),
207 request_id));
208 if (encoded_chain_ref == NULL || private_key_ref == NULL) {
209 LOG(ERROR) << "Client certificate request cancelled";
210 return;
212 // Convert the encoded chain to a vector of strings.
213 std::vector<std::string> encoded_chain_strings;
214 if (encoded_chain_ref) {
215 base::android::JavaArrayOfByteArrayToStringVector(
216 env, encoded_chain_ref, &encoded_chain_strings);
219 std::vector<base::StringPiece> encoded_chain;
220 for (size_t i = 0; i < encoded_chain_strings.size(); ++i)
221 encoded_chain.push_back(encoded_chain_strings[i]);
223 // Create the X509Certificate object from the encoded chain.
224 scoped_refptr<net::X509Certificate> client_cert(
225 net::X509Certificate::CreateFromDERCertChain(encoded_chain));
226 if (!client_cert.get()) {
227 LOG(ERROR) << "Could not decode client certificate chain";
228 return;
231 // Create an EVP_PKEY wrapper for the private key JNI reference.
232 crypto::ScopedEVP_PKEY private_key(
233 net::android::GetOpenSSLPrivateKeyWrapper(private_key_ref));
234 if (!private_key.get()) {
235 LOG(ERROR) << "Could not create OpenSSL wrapper for private key";
236 return;
239 // RecordClientCertificateKey() must be called on the I/O thread,
240 // before the callback is called with the selected certificate on
241 // the UI thread.
242 content::BrowserThread::PostTaskAndReply(
243 content::BrowserThread::IO,
244 FROM_HERE,
245 base::Bind(&RecordClientCertificateKey,
246 client_cert,
247 base::Passed(&private_key)),
248 base::Bind(*callback, client_cert));
249 pending_client_cert_request_callbacks_.Remove(request_id);
251 // Release the guard.
252 ignore_result(guard.Release());
255 void AwContentsClientBridge::RunJavaScriptDialog(
256 content::JavaScriptMessageType message_type,
257 const GURL& origin_url,
258 const base::string16& message_text,
259 const base::string16& default_prompt_text,
260 const content::JavaScriptDialogManager::DialogClosedCallback& callback) {
261 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
262 JNIEnv* env = AttachCurrentThread();
264 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
265 if (obj.is_null())
266 return;
268 int callback_id = pending_js_dialog_callbacks_.Add(
269 new content::JavaScriptDialogManager::DialogClosedCallback(callback));
270 ScopedJavaLocalRef<jstring> jurl(
271 ConvertUTF8ToJavaString(env, origin_url.spec()));
272 ScopedJavaLocalRef<jstring> jmessage(
273 ConvertUTF16ToJavaString(env, message_text));
275 switch (message_type) {
276 case content::JAVASCRIPT_MESSAGE_TYPE_ALERT: {
277 devtools_instrumentation::ScopedEmbedderCallbackTask("onJsAlert");
278 Java_AwContentsClientBridge_handleJsAlert(
279 env, obj.obj(), jurl.obj(), jmessage.obj(), callback_id);
280 break;
282 case content::JAVASCRIPT_MESSAGE_TYPE_CONFIRM: {
283 devtools_instrumentation::ScopedEmbedderCallbackTask("onJsConfirm");
284 Java_AwContentsClientBridge_handleJsConfirm(
285 env, obj.obj(), jurl.obj(), jmessage.obj(), callback_id);
286 break;
288 case content::JAVASCRIPT_MESSAGE_TYPE_PROMPT: {
289 ScopedJavaLocalRef<jstring> jdefault_value(
290 ConvertUTF16ToJavaString(env, default_prompt_text));
291 devtools_instrumentation::ScopedEmbedderCallbackTask("onJsPrompt");
292 Java_AwContentsClientBridge_handleJsPrompt(env,
293 obj.obj(),
294 jurl.obj(),
295 jmessage.obj(),
296 jdefault_value.obj(),
297 callback_id);
298 break;
300 default:
301 NOTREACHED();
305 void AwContentsClientBridge::RunBeforeUnloadDialog(
306 const GURL& origin_url,
307 const base::string16& message_text,
308 const content::JavaScriptDialogManager::DialogClosedCallback& callback) {
309 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
310 JNIEnv* env = AttachCurrentThread();
312 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
313 if (obj.is_null())
314 return;
316 int callback_id = pending_js_dialog_callbacks_.Add(
317 new content::JavaScriptDialogManager::DialogClosedCallback(callback));
318 ScopedJavaLocalRef<jstring> jurl(
319 ConvertUTF8ToJavaString(env, origin_url.spec()));
320 ScopedJavaLocalRef<jstring> jmessage(
321 ConvertUTF16ToJavaString(env, message_text));
323 devtools_instrumentation::ScopedEmbedderCallbackTask("onJsBeforeUnload");
324 Java_AwContentsClientBridge_handleJsBeforeUnload(
325 env, obj.obj(), jurl.obj(), jmessage.obj(), callback_id);
328 bool AwContentsClientBridge::ShouldOverrideUrlLoading(
329 const base::string16& url) {
330 JNIEnv* env = AttachCurrentThread();
331 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
332 if (obj.is_null())
333 return false;
334 ScopedJavaLocalRef<jstring> jurl = ConvertUTF16ToJavaString(env, url);
335 devtools_instrumentation::ScopedEmbedderCallbackTask(
336 "shouldOverrideUrlLoading");
337 return Java_AwContentsClientBridge_shouldOverrideUrlLoading(
338 env, obj.obj(),
339 jurl.obj());
342 void AwContentsClientBridge::ConfirmJsResult(JNIEnv* env,
343 jobject,
344 int id,
345 jstring prompt) {
346 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
347 content::JavaScriptDialogManager::DialogClosedCallback* callback =
348 pending_js_dialog_callbacks_.Lookup(id);
349 if (!callback) {
350 LOG(WARNING) << "Unexpected JS dialog confirm. " << id;
351 return;
353 base::string16 prompt_text;
354 if (prompt) {
355 prompt_text = ConvertJavaStringToUTF16(env, prompt);
357 callback->Run(true, prompt_text);
358 pending_js_dialog_callbacks_.Remove(id);
361 void AwContentsClientBridge::CancelJsResult(JNIEnv*, jobject, int id) {
362 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
363 content::JavaScriptDialogManager::DialogClosedCallback* callback =
364 pending_js_dialog_callbacks_.Lookup(id);
365 if (!callback) {
366 LOG(WARNING) << "Unexpected JS dialog cancel. " << id;
367 return;
369 callback->Run(false, base::string16());
370 pending_js_dialog_callbacks_.Remove(id);
373 // Use to cleanup if there is an error in client certificate response.
374 void AwContentsClientBridge::HandleErrorInClientCertificateResponse(
375 int request_id) {
376 SelectCertificateCallback* callback =
377 pending_client_cert_request_callbacks_.Lookup(request_id);
378 callback->Run(scoped_refptr<net::X509Certificate>());
379 pending_client_cert_request_callbacks_.Remove(request_id);
382 bool RegisterAwContentsClientBridge(JNIEnv* env) {
383 return RegisterNativesImpl(env);
386 } // namespace android_webview