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 "jni/AwContentsClientBridge_jni.h"
18 #include "net/android/keystore_openssl.h"
19 #include "net/cert/x509_certificate.h"
20 #include "net/ssl/openssl_client_key_store.h"
21 #include "net/ssl/ssl_cert_request_info.h"
22 #include "net/ssl/ssl_client_cert_type.h"
25 using base::android::AttachCurrentThread
;
26 using base::android::ConvertJavaStringToUTF16
;
27 using base::android::ConvertUTF8ToJavaString
;
28 using base::android::ConvertUTF16ToJavaString
;
29 using base::android::JavaRef
;
30 using base::android::ScopedJavaLocalRef
;
31 using content::BrowserThread
;
33 namespace android_webview
{
35 typedef net::OpenSSLClientKeyStore::ScopedEVP_PKEY ScopedEVP_PKEY
;
39 // Must be called on the I/O thread to record a client certificate
40 // and its private key in the OpenSSLClientKeyStore.
41 void RecordClientCertificateKey(
42 const scoped_refptr
<net::X509Certificate
>& client_cert
,
43 ScopedEVP_PKEY private_key
) {
44 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
45 net::OpenSSLClientKeyStore::GetInstance()->RecordClientCertPrivateKey(
46 client_cert
.get(), private_key
.get());
51 AwContentsClientBridge::AwContentsClientBridge(JNIEnv
* env
, jobject obj
)
52 : java_ref_(env
, obj
) {
54 Java_AwContentsClientBridge_setNativeContentsClientBridge(
55 env
, obj
, reinterpret_cast<intptr_t>(this));
58 AwContentsClientBridge::~AwContentsClientBridge() {
59 JNIEnv
* env
= AttachCurrentThread();
61 ScopedJavaLocalRef
<jobject
> obj
= java_ref_
.get(env
);
64 // Clear the weak reference from the java peer to the native object since
65 // it is possible that java object lifetime can exceed the AwContens.
66 Java_AwContentsClientBridge_setNativeContentsClientBridge(env
, obj
.obj(), 0);
69 void AwContentsClientBridge::AllowCertificateError(
71 net::X509Certificate
* cert
,
72 const GURL
& request_url
,
73 const base::Callback
<void(bool)>& callback
,
74 bool* cancel_request
) {
76 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
77 JNIEnv
* env
= AttachCurrentThread();
79 ScopedJavaLocalRef
<jobject
> obj
= java_ref_
.get(env
);
83 std::string der_string
;
84 net::X509Certificate::GetDEREncoded(cert
->os_cert_handle(), &der_string
);
85 ScopedJavaLocalRef
<jbyteArray
> jcert
= base::android::ToJavaByteArray(
87 reinterpret_cast<const uint8
*>(der_string
.data()),
89 ScopedJavaLocalRef
<jstring
> jurl(ConvertUTF8ToJavaString(
90 env
, request_url
.spec()));
91 // We need to add the callback before making the call to java side,
92 // as it may do a synchronous callback prior to returning.
93 int request_id
= pending_cert_error_callbacks_
.Add(
94 new CertErrorCallback(callback
));
95 *cancel_request
= !Java_AwContentsClientBridge_allowCertificateError(
96 env
, obj
.obj(), cert_error
, jcert
.obj(), jurl
.obj(), request_id
);
97 // if the request is cancelled, then cancel the stored callback
98 if (*cancel_request
) {
99 pending_cert_error_callbacks_
.Remove(request_id
);
103 void AwContentsClientBridge::ProceedSslError(JNIEnv
* env
, jobject obj
,
104 jboolean proceed
, jint id
) {
105 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
106 CertErrorCallback
* callback
= pending_cert_error_callbacks_
.Lookup(id
);
107 if (!callback
|| callback
->is_null()) {
108 LOG(WARNING
) << "Ignoring unexpected ssl error proceed callback";
111 callback
->Run(proceed
);
112 pending_cert_error_callbacks_
.Remove(id
);
115 // This method is inspired by SelectClientCertificate() in
116 // chrome/browser/ui/android/ssl_client_certificate_request.cc
117 void AwContentsClientBridge::SelectClientCertificate(
118 net::SSLCertRequestInfo
* cert_request_info
,
119 const SelectCertificateCallback
& callback
) {
120 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
122 // Add the callback to id map.
123 int request_id
= pending_client_cert_request_callbacks_
.Add(
124 new SelectCertificateCallback(callback
));
125 // Make sure callback is run on error.
126 base::ScopedClosureRunner
guard(base::Bind(
127 &AwContentsClientBridge::HandleErrorInClientCertificateResponse
,
128 base::Unretained(this),
131 JNIEnv
* env
= base::android::AttachCurrentThread();
132 ScopedJavaLocalRef
<jobject
> obj
= java_ref_
.get(env
);
136 // Build the |key_types| JNI parameter, as a String[]
137 std::vector
<std::string
> key_types
;
138 for (size_t i
= 0; i
< cert_request_info
->cert_key_types
.size(); ++i
) {
139 switch (cert_request_info
->cert_key_types
[i
]) {
140 case net::CLIENT_CERT_RSA_SIGN
:
141 key_types
.push_back("RSA");
143 case net::CLIENT_CERT_DSS_SIGN
:
144 key_types
.push_back("DSA");
146 case net::CLIENT_CERT_ECDSA_SIGN
:
147 key_types
.push_back("ECDSA");
150 // Ignore unknown types.
155 ScopedJavaLocalRef
<jobjectArray
> key_types_ref
=
156 base::android::ToJavaArrayOfStrings(env
, key_types
);
157 if (key_types_ref
.is_null()) {
158 LOG(ERROR
) << "Could not create key types array (String[])";
162 // Build the |encoded_principals| JNI parameter, as a byte[][]
163 ScopedJavaLocalRef
<jobjectArray
> principals_ref
=
164 base::android::ToJavaArrayOfByteArray(
165 env
, cert_request_info
->cert_authorities
);
166 if (principals_ref
.is_null()) {
167 LOG(ERROR
) << "Could not create principals array (byte[][])";
171 // Build the |host_name| and |port| JNI parameters, as a String and
173 ScopedJavaLocalRef
<jstring
> host_name_ref
=
174 base::android::ConvertUTF8ToJavaString(
175 env
, cert_request_info
->host_and_port
.host());
177 Java_AwContentsClientBridge_selectClientCertificate(
182 principals_ref
.obj(),
184 cert_request_info
->host_and_port
.port());
186 // Release the guard.
187 ignore_result(guard
.Release());
190 // This method is inspired by OnSystemRequestCompletion() in
191 // chrome/browser/ui/android/ssl_client_certificate_request.cc
192 void AwContentsClientBridge::ProvideClientCertificateResponse(
196 jobjectArray encoded_chain_ref
,
197 jobject private_key_ref
) {
198 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
200 SelectCertificateCallback
* callback
=
201 pending_client_cert_request_callbacks_
.Lookup(request_id
);
204 // Make sure callback is run on error.
205 base::ScopedClosureRunner
guard(base::Bind(
206 &AwContentsClientBridge::HandleErrorInClientCertificateResponse
,
207 base::Unretained(this),
209 if (encoded_chain_ref
== NULL
|| private_key_ref
== NULL
) {
210 LOG(ERROR
) << "Client certificate request cancelled";
213 // Convert the encoded chain to a vector of strings.
214 std::vector
<std::string
> encoded_chain_strings
;
215 if (encoded_chain_ref
) {
216 base::android::JavaArrayOfByteArrayToStringVector(
217 env
, encoded_chain_ref
, &encoded_chain_strings
);
220 std::vector
<base::StringPiece
> encoded_chain
;
221 for (size_t i
= 0; i
< encoded_chain_strings
.size(); ++i
)
222 encoded_chain
.push_back(encoded_chain_strings
[i
]);
224 // Create the X509Certificate object from the encoded chain.
225 scoped_refptr
<net::X509Certificate
> client_cert(
226 net::X509Certificate::CreateFromDERCertChain(encoded_chain
));
227 if (!client_cert
.get()) {
228 LOG(ERROR
) << "Could not decode client certificate chain";
232 // Create an EVP_PKEY wrapper for the private key JNI reference.
233 ScopedEVP_PKEY
private_key(
234 net::android::GetOpenSSLPrivateKeyWrapper(private_key_ref
));
235 if (!private_key
.get()) {
236 LOG(ERROR
) << "Could not create OpenSSL wrapper for private key";
240 // RecordClientCertificateKey() must be called on the I/O thread,
241 // before the callback is called with the selected certificate on
243 content::BrowserThread::PostTaskAndReply(
244 content::BrowserThread::IO
,
246 base::Bind(&RecordClientCertificateKey
,
248 base::Passed(&private_key
)),
249 base::Bind(*callback
, client_cert
));
250 pending_client_cert_request_callbacks_
.Remove(request_id
);
252 // Release the guard.
253 ignore_result(guard
.Release());
256 void AwContentsClientBridge::RunJavaScriptDialog(
257 content::JavaScriptMessageType message_type
,
258 const GURL
& origin_url
,
259 const base::string16
& message_text
,
260 const base::string16
& default_prompt_text
,
261 const content::JavaScriptDialogManager::DialogClosedCallback
& callback
) {
262 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
263 JNIEnv
* env
= AttachCurrentThread();
265 ScopedJavaLocalRef
<jobject
> obj
= java_ref_
.get(env
);
269 int callback_id
= pending_js_dialog_callbacks_
.Add(
270 new content::JavaScriptDialogManager::DialogClosedCallback(callback
));
271 ScopedJavaLocalRef
<jstring
> jurl(
272 ConvertUTF8ToJavaString(env
, origin_url
.spec()));
273 ScopedJavaLocalRef
<jstring
> jmessage(
274 ConvertUTF16ToJavaString(env
, message_text
));
276 switch (message_type
) {
277 case content::JAVASCRIPT_MESSAGE_TYPE_ALERT
: {
278 devtools_instrumentation::ScopedEmbedderCallbackTask("onJsAlert");
279 Java_AwContentsClientBridge_handleJsAlert(
280 env
, obj
.obj(), jurl
.obj(), jmessage
.obj(), callback_id
);
283 case content::JAVASCRIPT_MESSAGE_TYPE_CONFIRM
: {
284 devtools_instrumentation::ScopedEmbedderCallbackTask("onJsConfirm");
285 Java_AwContentsClientBridge_handleJsConfirm(
286 env
, obj
.obj(), jurl
.obj(), jmessage
.obj(), callback_id
);
289 case content::JAVASCRIPT_MESSAGE_TYPE_PROMPT
: {
290 ScopedJavaLocalRef
<jstring
> jdefault_value(
291 ConvertUTF16ToJavaString(env
, default_prompt_text
));
292 devtools_instrumentation::ScopedEmbedderCallbackTask("onJsPrompt");
293 Java_AwContentsClientBridge_handleJsPrompt(env
,
297 jdefault_value
.obj(),
306 void AwContentsClientBridge::RunBeforeUnloadDialog(
307 const GURL
& origin_url
,
308 const base::string16
& message_text
,
309 const content::JavaScriptDialogManager::DialogClosedCallback
& callback
) {
310 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
311 JNIEnv
* env
= AttachCurrentThread();
313 ScopedJavaLocalRef
<jobject
> obj
= java_ref_
.get(env
);
317 int callback_id
= pending_js_dialog_callbacks_
.Add(
318 new content::JavaScriptDialogManager::DialogClosedCallback(callback
));
319 ScopedJavaLocalRef
<jstring
> jurl(
320 ConvertUTF8ToJavaString(env
, origin_url
.spec()));
321 ScopedJavaLocalRef
<jstring
> jmessage(
322 ConvertUTF16ToJavaString(env
, message_text
));
324 devtools_instrumentation::ScopedEmbedderCallbackTask("onJsBeforeUnload");
325 Java_AwContentsClientBridge_handleJsBeforeUnload(
326 env
, obj
.obj(), jurl
.obj(), jmessage
.obj(), callback_id
);
329 bool AwContentsClientBridge::ShouldOverrideUrlLoading(
330 const base::string16
& url
) {
331 JNIEnv
* env
= AttachCurrentThread();
332 ScopedJavaLocalRef
<jobject
> obj
= java_ref_
.get(env
);
335 ScopedJavaLocalRef
<jstring
> jurl
= ConvertUTF16ToJavaString(env
, url
);
336 devtools_instrumentation::ScopedEmbedderCallbackTask(
337 "shouldOverrideUrlLoading");
338 return Java_AwContentsClientBridge_shouldOverrideUrlLoading(
343 void AwContentsClientBridge::ConfirmJsResult(JNIEnv
* env
,
347 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
348 content::JavaScriptDialogManager::DialogClosedCallback
* callback
=
349 pending_js_dialog_callbacks_
.Lookup(id
);
351 LOG(WARNING
) << "Unexpected JS dialog confirm. " << id
;
354 base::string16 prompt_text
;
356 prompt_text
= ConvertJavaStringToUTF16(env
, prompt
);
358 callback
->Run(true, prompt_text
);
359 pending_js_dialog_callbacks_
.Remove(id
);
362 void AwContentsClientBridge::CancelJsResult(JNIEnv
*, jobject
, int id
) {
363 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
364 content::JavaScriptDialogManager::DialogClosedCallback
* callback
=
365 pending_js_dialog_callbacks_
.Lookup(id
);
367 LOG(WARNING
) << "Unexpected JS dialog cancel. " << id
;
370 callback
->Run(false, base::string16());
371 pending_js_dialog_callbacks_
.Remove(id
);
374 // Use to cleanup if there is an error in client certificate response.
375 void AwContentsClientBridge::HandleErrorInClientCertificateResponse(
377 SelectCertificateCallback
* callback
=
378 pending_client_cert_request_callbacks_
.Lookup(request_id
);
379 callback
->Run(scoped_refptr
<net::X509Certificate
>());
380 pending_client_cert_request_callbacks_
.Remove(request_id
);
383 bool RegisterAwContentsClientBridge(JNIEnv
* env
) {
384 return RegisterNativesImpl(env
);
387 } // namespace android_webview