Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / android_webview / native / aw_contents_client_bridge.cc
blob9e570af6761308a0efd7d293ce26c41705c4701a
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 "base/macros.h"
14 #include "content/public/browser/browser_thread.h"
15 #include "content/public/browser/client_certificate_delegate.h"
16 #include "content/public/browser/render_process_host.h"
17 #include "content/public/browser/render_view_host.h"
18 #include "content/public/browser/web_contents.h"
19 #include "crypto/scoped_openssl_types.h"
20 #include "jni/AwContentsClientBridge_jni.h"
21 #include "net/android/keystore_openssl.h"
22 #include "net/cert/x509_certificate.h"
23 #include "net/ssl/openssl_client_key_store.h"
24 #include "net/ssl/ssl_cert_request_info.h"
25 #include "net/ssl/ssl_client_cert_type.h"
26 #include "url/gurl.h"
28 using base::android::AttachCurrentThread;
29 using base::android::ConvertJavaStringToUTF16;
30 using base::android::ConvertUTF8ToJavaString;
31 using base::android::ConvertUTF16ToJavaString;
32 using base::android::JavaRef;
33 using base::android::ScopedJavaLocalRef;
34 using content::BrowserThread;
36 namespace android_webview {
38 namespace {
40 // Must be called on the I/O thread to record a client certificate
41 // and its private key in the OpenSSLClientKeyStore.
42 void RecordClientCertificateKey(
43 const scoped_refptr<net::X509Certificate>& client_cert,
44 crypto::ScopedEVP_PKEY private_key) {
45 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
46 net::OpenSSLClientKeyStore::GetInstance()->RecordClientCertPrivateKey(
47 client_cert.get(), private_key.get());
50 } // namespace
52 AwContentsClientBridge::AwContentsClientBridge(JNIEnv* env, jobject obj)
53 : java_ref_(env, obj) {
54 DCHECK(obj);
55 Java_AwContentsClientBridge_setNativeContentsClientBridge(
56 env, obj, reinterpret_cast<intptr_t>(this));
59 AwContentsClientBridge::~AwContentsClientBridge() {
60 JNIEnv* env = AttachCurrentThread();
62 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
63 if (!obj.is_null()) {
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(),
67 0);
70 for (IDMap<content::ClientCertificateDelegate>::iterator iter(
71 &pending_client_cert_request_delegates_);
72 !iter.IsAtEnd(); iter.Advance()) {
73 delete iter.GetCurrentValue();
77 void AwContentsClientBridge::AllowCertificateError(
78 int cert_error,
79 net::X509Certificate* cert,
80 const GURL& request_url,
81 const base::Callback<void(bool)>& callback,
82 bool* cancel_request) {
84 DCHECK_CURRENTLY_ON(BrowserThread::UI);
85 JNIEnv* env = AttachCurrentThread();
87 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
88 if (obj.is_null())
89 return;
91 std::string der_string;
92 net::X509Certificate::GetDEREncoded(cert->os_cert_handle(), &der_string);
93 ScopedJavaLocalRef<jbyteArray> jcert = base::android::ToJavaByteArray(
94 env,
95 reinterpret_cast<const uint8*>(der_string.data()),
96 der_string.length());
97 ScopedJavaLocalRef<jstring> jurl(ConvertUTF8ToJavaString(
98 env, request_url.spec()));
99 // We need to add the callback before making the call to java side,
100 // as it may do a synchronous callback prior to returning.
101 int request_id = pending_cert_error_callbacks_.Add(
102 new CertErrorCallback(callback));
103 *cancel_request = !Java_AwContentsClientBridge_allowCertificateError(
104 env, obj.obj(), cert_error, jcert.obj(), jurl.obj(), request_id);
105 // if the request is cancelled, then cancel the stored callback
106 if (*cancel_request) {
107 pending_cert_error_callbacks_.Remove(request_id);
111 void AwContentsClientBridge::ProceedSslError(JNIEnv* env, jobject obj,
112 jboolean proceed, jint id) {
113 DCHECK_CURRENTLY_ON(BrowserThread::UI);
114 CertErrorCallback* callback = pending_cert_error_callbacks_.Lookup(id);
115 if (!callback || callback->is_null()) {
116 LOG(WARNING) << "Ignoring unexpected ssl error proceed callback";
117 return;
119 callback->Run(proceed);
120 pending_cert_error_callbacks_.Remove(id);
123 // This method is inspired by SelectClientCertificate() in
124 // chrome/browser/ui/android/ssl_client_certificate_request.cc
125 void AwContentsClientBridge::SelectClientCertificate(
126 net::SSLCertRequestInfo* cert_request_info,
127 scoped_ptr<content::ClientCertificateDelegate> delegate) {
128 DCHECK_CURRENTLY_ON(BrowserThread::UI);
130 // Add the callback to id map.
131 int request_id =
132 pending_client_cert_request_delegates_.Add(delegate.release());
133 // Make sure callback is run on error.
134 base::ScopedClosureRunner guard(base::Bind(
135 &AwContentsClientBridge::HandleErrorInClientCertificateResponse,
136 base::Unretained(this),
137 request_id));
139 JNIEnv* env = base::android::AttachCurrentThread();
140 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
141 if (obj.is_null())
142 return;
144 // Build the |key_types| JNI parameter, as a String[]
145 std::vector<std::string> key_types;
146 for (size_t i = 0; i < cert_request_info->cert_key_types.size(); ++i) {
147 switch (cert_request_info->cert_key_types[i]) {
148 case net::CLIENT_CERT_RSA_SIGN:
149 key_types.push_back("RSA");
150 break;
151 case net::CLIENT_CERT_ECDSA_SIGN:
152 key_types.push_back("ECDSA");
153 break;
154 default:
155 // Ignore unknown types.
156 break;
160 ScopedJavaLocalRef<jobjectArray> key_types_ref =
161 base::android::ToJavaArrayOfStrings(env, key_types);
162 if (key_types_ref.is_null()) {
163 LOG(ERROR) << "Could not create key types array (String[])";
164 return;
167 // Build the |encoded_principals| JNI parameter, as a byte[][]
168 ScopedJavaLocalRef<jobjectArray> principals_ref =
169 base::android::ToJavaArrayOfByteArray(
170 env, cert_request_info->cert_authorities);
171 if (principals_ref.is_null()) {
172 LOG(ERROR) << "Could not create principals array (byte[][])";
173 return;
176 // Build the |host_name| and |port| JNI parameters, as a String and
177 // a jint.
178 ScopedJavaLocalRef<jstring> host_name_ref =
179 base::android::ConvertUTF8ToJavaString(
180 env, cert_request_info->host_and_port.host());
182 Java_AwContentsClientBridge_selectClientCertificate(
183 env,
184 obj.obj(),
185 request_id,
186 key_types_ref.obj(),
187 principals_ref.obj(),
188 host_name_ref.obj(),
189 cert_request_info->host_and_port.port());
191 // Release the guard.
192 ignore_result(guard.Release());
195 // This method is inspired by OnSystemRequestCompletion() in
196 // chrome/browser/ui/android/ssl_client_certificate_request.cc
197 void AwContentsClientBridge::ProvideClientCertificateResponse(
198 JNIEnv* env,
199 jobject obj,
200 int request_id,
201 jobjectArray encoded_chain_ref,
202 jobject private_key_ref) {
203 DCHECK_CURRENTLY_ON(BrowserThread::UI);
205 content::ClientCertificateDelegate* delegate =
206 pending_client_cert_request_delegates_.Lookup(request_id);
207 DCHECK(delegate);
209 if (encoded_chain_ref == NULL || private_key_ref == NULL) {
210 LOG(ERROR) << "No client certificate selected";
211 pending_client_cert_request_delegates_.Remove(request_id);
212 delegate->ContinueWithCertificate(nullptr);
213 delete delegate;
214 return;
217 // Make sure callback is run on error.
218 base::ScopedClosureRunner guard(base::Bind(
219 &AwContentsClientBridge::HandleErrorInClientCertificateResponse,
220 base::Unretained(this),
221 request_id));
223 // Convert the encoded chain to a vector of strings.
224 std::vector<std::string> encoded_chain_strings;
225 if (encoded_chain_ref) {
226 base::android::JavaArrayOfByteArrayToStringVector(
227 env, encoded_chain_ref, &encoded_chain_strings);
230 std::vector<base::StringPiece> encoded_chain;
231 for (size_t i = 0; i < encoded_chain_strings.size(); ++i)
232 encoded_chain.push_back(encoded_chain_strings[i]);
234 // Create the X509Certificate object from the encoded chain.
235 scoped_refptr<net::X509Certificate> client_cert(
236 net::X509Certificate::CreateFromDERCertChain(encoded_chain));
237 if (!client_cert.get()) {
238 LOG(ERROR) << "Could not decode client certificate chain";
239 return;
242 // Create an EVP_PKEY wrapper for the private key JNI reference.
243 crypto::ScopedEVP_PKEY private_key(
244 net::android::GetOpenSSLPrivateKeyWrapper(private_key_ref));
245 if (!private_key.get()) {
246 LOG(ERROR) << "Could not create OpenSSL wrapper for private key";
247 return;
250 // Release the guard and |pending_client_cert_request_delegates_| references
251 // to |delegate|.
252 pending_client_cert_request_delegates_.Remove(request_id);
253 ignore_result(guard.Release());
255 // RecordClientCertificateKey() must be called on the I/O thread,
256 // before the delegate is called with the selected certificate on
257 // the UI thread.
258 content::BrowserThread::PostTaskAndReply(
259 content::BrowserThread::IO, FROM_HERE,
260 base::Bind(&RecordClientCertificateKey, client_cert,
261 base::Passed(&private_key)),
262 base::Bind(&content::ClientCertificateDelegate::ContinueWithCertificate,
263 base::Owned(delegate), client_cert));
266 void AwContentsClientBridge::RunJavaScriptDialog(
267 content::JavaScriptMessageType message_type,
268 const GURL& origin_url,
269 const base::string16& message_text,
270 const base::string16& default_prompt_text,
271 const content::JavaScriptDialogManager::DialogClosedCallback& callback) {
272 DCHECK_CURRENTLY_ON(BrowserThread::UI);
273 JNIEnv* env = AttachCurrentThread();
275 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
276 if (obj.is_null()) {
277 callback.Run(false, base::string16());
278 return;
281 int callback_id = pending_js_dialog_callbacks_.Add(
282 new content::JavaScriptDialogManager::DialogClosedCallback(callback));
283 ScopedJavaLocalRef<jstring> jurl(
284 ConvertUTF8ToJavaString(env, origin_url.spec()));
285 ScopedJavaLocalRef<jstring> jmessage(
286 ConvertUTF16ToJavaString(env, message_text));
288 switch (message_type) {
289 case content::JAVASCRIPT_MESSAGE_TYPE_ALERT: {
290 devtools_instrumentation::ScopedEmbedderCallbackTask("onJsAlert");
291 Java_AwContentsClientBridge_handleJsAlert(
292 env, obj.obj(), jurl.obj(), jmessage.obj(), callback_id);
293 break;
295 case content::JAVASCRIPT_MESSAGE_TYPE_CONFIRM: {
296 devtools_instrumentation::ScopedEmbedderCallbackTask("onJsConfirm");
297 Java_AwContentsClientBridge_handleJsConfirm(
298 env, obj.obj(), jurl.obj(), jmessage.obj(), callback_id);
299 break;
301 case content::JAVASCRIPT_MESSAGE_TYPE_PROMPT: {
302 ScopedJavaLocalRef<jstring> jdefault_value(
303 ConvertUTF16ToJavaString(env, default_prompt_text));
304 devtools_instrumentation::ScopedEmbedderCallbackTask("onJsPrompt");
305 Java_AwContentsClientBridge_handleJsPrompt(env,
306 obj.obj(),
307 jurl.obj(),
308 jmessage.obj(),
309 jdefault_value.obj(),
310 callback_id);
311 break;
313 default:
314 NOTREACHED();
318 void AwContentsClientBridge::RunBeforeUnloadDialog(
319 const GURL& origin_url,
320 const base::string16& message_text,
321 const content::JavaScriptDialogManager::DialogClosedCallback& callback) {
322 DCHECK_CURRENTLY_ON(BrowserThread::UI);
323 JNIEnv* env = AttachCurrentThread();
325 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
326 if (obj.is_null()) {
327 callback.Run(false, base::string16());
328 return;
331 int callback_id = pending_js_dialog_callbacks_.Add(
332 new content::JavaScriptDialogManager::DialogClosedCallback(callback));
333 ScopedJavaLocalRef<jstring> jurl(
334 ConvertUTF8ToJavaString(env, origin_url.spec()));
335 ScopedJavaLocalRef<jstring> jmessage(
336 ConvertUTF16ToJavaString(env, message_text));
338 devtools_instrumentation::ScopedEmbedderCallbackTask("onJsBeforeUnload");
339 Java_AwContentsClientBridge_handleJsBeforeUnload(
340 env, obj.obj(), jurl.obj(), jmessage.obj(), callback_id);
343 void AwContentsClientBridge::ConfirmJsResult(JNIEnv* env,
344 jobject,
345 int id,
346 jstring prompt) {
347 DCHECK_CURRENTLY_ON(BrowserThread::UI);
348 content::JavaScriptDialogManager::DialogClosedCallback* callback =
349 pending_js_dialog_callbacks_.Lookup(id);
350 if (!callback) {
351 LOG(WARNING) << "Unexpected JS dialog confirm. " << id;
352 return;
354 base::string16 prompt_text;
355 if (prompt) {
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_CURRENTLY_ON(BrowserThread::UI);
364 content::JavaScriptDialogManager::DialogClosedCallback* callback =
365 pending_js_dialog_callbacks_.Lookup(id);
366 if (!callback) {
367 LOG(WARNING) << "Unexpected JS dialog cancel. " << id;
368 return;
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(
376 int request_id) {
377 content::ClientCertificateDelegate* delegate =
378 pending_client_cert_request_delegates_.Lookup(request_id);
379 pending_client_cert_request_delegates_.Remove(request_id);
381 delete delegate;
384 bool RegisterAwContentsClientBridge(JNIEnv* env) {
385 return RegisterNativesImpl(env);
388 } // namespace android_webview