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"
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
{
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());
50 AwContentsClientBridge::AwContentsClientBridge(JNIEnv
* env
, jobject obj
)
51 : java_ref_(env
, 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
);
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(
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
);
82 std::string der_string
;
83 net::X509Certificate::GetDEREncoded(cert
->os_cert_handle(), &der_string
);
84 ScopedJavaLocalRef
<jbyteArray
> jcert
= base::android::ToJavaByteArray(
86 reinterpret_cast<const uint8
*>(der_string
.data()),
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";
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),
130 JNIEnv
* env
= base::android::AttachCurrentThread();
131 ScopedJavaLocalRef
<jobject
> obj
= java_ref_
.get(env
);
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");
142 case net::CLIENT_CERT_DSS_SIGN
:
143 key_types
.push_back("DSA");
145 case net::CLIENT_CERT_ECDSA_SIGN
:
146 key_types
.push_back("ECDSA");
149 // Ignore unknown types.
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[])";
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[][])";
170 // Build the |host_name| and |port| JNI parameters, as a String and
172 ScopedJavaLocalRef
<jstring
> host_name_ref
=
173 base::android::ConvertUTF8ToJavaString(
174 env
, cert_request_info
->host_and_port
.host());
176 Java_AwContentsClientBridge_selectClientCertificate(
181 principals_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(
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
);
203 // Make sure callback is run on error.
204 base::ScopedClosureRunner
guard(base::Bind(
205 &AwContentsClientBridge::HandleErrorInClientCertificateResponse
,
206 base::Unretained(this),
208 if (encoded_chain_ref
== NULL
|| private_key_ref
== NULL
) {
209 LOG(ERROR
) << "Client certificate request cancelled";
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";
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";
239 // RecordClientCertificateKey() must be called on the I/O thread,
240 // before the callback is called with the selected certificate on
242 content::BrowserThread::PostTaskAndReply(
243 content::BrowserThread::IO
,
245 base::Bind(&RecordClientCertificateKey
,
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
);
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
);
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
);
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
,
296 jdefault_value
.obj(),
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
);
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
);
334 ScopedJavaLocalRef
<jstring
> jurl
= ConvertUTF16ToJavaString(env
, url
);
335 devtools_instrumentation::ScopedEmbedderCallbackTask(
336 "shouldOverrideUrlLoading");
337 return Java_AwContentsClientBridge_shouldOverrideUrlLoading(
342 void AwContentsClientBridge::ConfirmJsResult(JNIEnv
* env
,
346 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
347 content::JavaScriptDialogManager::DialogClosedCallback
* callback
=
348 pending_js_dialog_callbacks_
.Lookup(id
);
350 LOG(WARNING
) << "Unexpected JS dialog confirm. " << id
;
353 base::string16 prompt_text
;
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
);
366 LOG(WARNING
) << "Unexpected JS dialog cancel. " << id
;
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(
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