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
);
266 callback
.Run(false, base::string16());
270 int callback_id
= pending_js_dialog_callbacks_
.Add(
271 new content::JavaScriptDialogManager::DialogClosedCallback(callback
));
272 ScopedJavaLocalRef
<jstring
> jurl(
273 ConvertUTF8ToJavaString(env
, origin_url
.spec()));
274 ScopedJavaLocalRef
<jstring
> jmessage(
275 ConvertUTF16ToJavaString(env
, message_text
));
277 switch (message_type
) {
278 case content::JAVASCRIPT_MESSAGE_TYPE_ALERT
: {
279 devtools_instrumentation::ScopedEmbedderCallbackTask("onJsAlert");
280 Java_AwContentsClientBridge_handleJsAlert(
281 env
, obj
.obj(), jurl
.obj(), jmessage
.obj(), callback_id
);
284 case content::JAVASCRIPT_MESSAGE_TYPE_CONFIRM
: {
285 devtools_instrumentation::ScopedEmbedderCallbackTask("onJsConfirm");
286 Java_AwContentsClientBridge_handleJsConfirm(
287 env
, obj
.obj(), jurl
.obj(), jmessage
.obj(), callback_id
);
290 case content::JAVASCRIPT_MESSAGE_TYPE_PROMPT
: {
291 ScopedJavaLocalRef
<jstring
> jdefault_value(
292 ConvertUTF16ToJavaString(env
, default_prompt_text
));
293 devtools_instrumentation::ScopedEmbedderCallbackTask("onJsPrompt");
294 Java_AwContentsClientBridge_handleJsPrompt(env
,
298 jdefault_value
.obj(),
307 void AwContentsClientBridge::RunBeforeUnloadDialog(
308 const GURL
& origin_url
,
309 const base::string16
& message_text
,
310 const content::JavaScriptDialogManager::DialogClosedCallback
& callback
) {
311 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
312 JNIEnv
* env
= AttachCurrentThread();
314 ScopedJavaLocalRef
<jobject
> obj
= java_ref_
.get(env
);
316 callback
.Run(false, base::string16());
320 int callback_id
= pending_js_dialog_callbacks_
.Add(
321 new content::JavaScriptDialogManager::DialogClosedCallback(callback
));
322 ScopedJavaLocalRef
<jstring
> jurl(
323 ConvertUTF8ToJavaString(env
, origin_url
.spec()));
324 ScopedJavaLocalRef
<jstring
> jmessage(
325 ConvertUTF16ToJavaString(env
, message_text
));
327 devtools_instrumentation::ScopedEmbedderCallbackTask("onJsBeforeUnload");
328 Java_AwContentsClientBridge_handleJsBeforeUnload(
329 env
, obj
.obj(), jurl
.obj(), jmessage
.obj(), callback_id
);
332 bool AwContentsClientBridge::ShouldOverrideUrlLoading(
333 const base::string16
& url
) {
334 JNIEnv
* env
= AttachCurrentThread();
335 ScopedJavaLocalRef
<jobject
> obj
= java_ref_
.get(env
);
338 ScopedJavaLocalRef
<jstring
> jurl
= ConvertUTF16ToJavaString(env
, url
);
339 devtools_instrumentation::ScopedEmbedderCallbackTask(
340 "shouldOverrideUrlLoading");
341 return Java_AwContentsClientBridge_shouldOverrideUrlLoading(
346 void AwContentsClientBridge::ConfirmJsResult(JNIEnv
* env
,
350 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
351 content::JavaScriptDialogManager::DialogClosedCallback
* callback
=
352 pending_js_dialog_callbacks_
.Lookup(id
);
354 LOG(WARNING
) << "Unexpected JS dialog confirm. " << id
;
357 base::string16 prompt_text
;
359 prompt_text
= ConvertJavaStringToUTF16(env
, prompt
);
361 callback
->Run(true, prompt_text
);
362 pending_js_dialog_callbacks_
.Remove(id
);
365 void AwContentsClientBridge::CancelJsResult(JNIEnv
*, jobject
, int id
) {
366 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
367 content::JavaScriptDialogManager::DialogClosedCallback
* callback
=
368 pending_js_dialog_callbacks_
.Lookup(id
);
370 LOG(WARNING
) << "Unexpected JS dialog cancel. " << id
;
373 callback
->Run(false, base::string16());
374 pending_js_dialog_callbacks_
.Remove(id
);
377 // Use to cleanup if there is an error in client certificate response.
378 void AwContentsClientBridge::HandleErrorInClientCertificateResponse(
380 SelectCertificateCallback
* callback
=
381 pending_client_cert_request_callbacks_
.Lookup(request_id
);
382 callback
->Run(scoped_refptr
<net::X509Certificate
>());
383 pending_client_cert_request_callbacks_
.Remove(request_id
);
386 bool RegisterAwContentsClientBridge(JNIEnv
* env
) {
387 return RegisterNativesImpl(env
);
390 } // namespace android_webview