Files.app: Use core-icon for the dropdown button of combobutton.
[chromium-blink-merge.git] / android_webview / native / aw_contents_client_bridge.cc
blobb7de4cb20f4fbd633176da789181e0b5ed6966ec
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_DSS_SIGN:
152 key_types.push_back("DSA");
153 break;
154 case net::CLIENT_CERT_ECDSA_SIGN:
155 key_types.push_back("ECDSA");
156 break;
157 default:
158 // Ignore unknown types.
159 break;
163 ScopedJavaLocalRef<jobjectArray> key_types_ref =
164 base::android::ToJavaArrayOfStrings(env, key_types);
165 if (key_types_ref.is_null()) {
166 LOG(ERROR) << "Could not create key types array (String[])";
167 return;
170 // Build the |encoded_principals| JNI parameter, as a byte[][]
171 ScopedJavaLocalRef<jobjectArray> principals_ref =
172 base::android::ToJavaArrayOfByteArray(
173 env, cert_request_info->cert_authorities);
174 if (principals_ref.is_null()) {
175 LOG(ERROR) << "Could not create principals array (byte[][])";
176 return;
179 // Build the |host_name| and |port| JNI parameters, as a String and
180 // a jint.
181 ScopedJavaLocalRef<jstring> host_name_ref =
182 base::android::ConvertUTF8ToJavaString(
183 env, cert_request_info->host_and_port.host());
185 Java_AwContentsClientBridge_selectClientCertificate(
186 env,
187 obj.obj(),
188 request_id,
189 key_types_ref.obj(),
190 principals_ref.obj(),
191 host_name_ref.obj(),
192 cert_request_info->host_and_port.port());
194 // Release the guard.
195 ignore_result(guard.Release());
198 // This method is inspired by OnSystemRequestCompletion() in
199 // chrome/browser/ui/android/ssl_client_certificate_request.cc
200 void AwContentsClientBridge::ProvideClientCertificateResponse(
201 JNIEnv* env,
202 jobject obj,
203 int request_id,
204 jobjectArray encoded_chain_ref,
205 jobject private_key_ref) {
206 DCHECK_CURRENTLY_ON(BrowserThread::UI);
208 content::ClientCertificateDelegate* delegate =
209 pending_client_cert_request_delegates_.Lookup(request_id);
210 DCHECK(delegate);
212 if (encoded_chain_ref == NULL || private_key_ref == NULL) {
213 LOG(ERROR) << "No client certificate selected";
214 pending_client_cert_request_delegates_.Remove(request_id);
215 delegate->ContinueWithCertificate(nullptr);
216 delete delegate;
217 return;
220 // Make sure callback is run on error.
221 base::ScopedClosureRunner guard(base::Bind(
222 &AwContentsClientBridge::HandleErrorInClientCertificateResponse,
223 base::Unretained(this),
224 request_id));
226 // Convert the encoded chain to a vector of strings.
227 std::vector<std::string> encoded_chain_strings;
228 if (encoded_chain_ref) {
229 base::android::JavaArrayOfByteArrayToStringVector(
230 env, encoded_chain_ref, &encoded_chain_strings);
233 std::vector<base::StringPiece> encoded_chain;
234 for (size_t i = 0; i < encoded_chain_strings.size(); ++i)
235 encoded_chain.push_back(encoded_chain_strings[i]);
237 // Create the X509Certificate object from the encoded chain.
238 scoped_refptr<net::X509Certificate> client_cert(
239 net::X509Certificate::CreateFromDERCertChain(encoded_chain));
240 if (!client_cert.get()) {
241 LOG(ERROR) << "Could not decode client certificate chain";
242 return;
245 // Create an EVP_PKEY wrapper for the private key JNI reference.
246 crypto::ScopedEVP_PKEY private_key(
247 net::android::GetOpenSSLPrivateKeyWrapper(private_key_ref));
248 if (!private_key.get()) {
249 LOG(ERROR) << "Could not create OpenSSL wrapper for private key";
250 return;
253 // Release the guard and |pending_client_cert_request_delegates_| references
254 // to |delegate|.
255 pending_client_cert_request_delegates_.Remove(request_id);
256 ignore_result(guard.Release());
258 // RecordClientCertificateKey() must be called on the I/O thread,
259 // before the delegate is called with the selected certificate on
260 // the UI thread.
261 content::BrowserThread::PostTaskAndReply(
262 content::BrowserThread::IO, FROM_HERE,
263 base::Bind(&RecordClientCertificateKey, client_cert,
264 base::Passed(&private_key)),
265 base::Bind(&content::ClientCertificateDelegate::ContinueWithCertificate,
266 base::Owned(delegate), client_cert));
269 void AwContentsClientBridge::RunJavaScriptDialog(
270 content::JavaScriptMessageType message_type,
271 const GURL& origin_url,
272 const base::string16& message_text,
273 const base::string16& default_prompt_text,
274 const content::JavaScriptDialogManager::DialogClosedCallback& callback) {
275 DCHECK_CURRENTLY_ON(BrowserThread::UI);
276 JNIEnv* env = AttachCurrentThread();
278 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
279 if (obj.is_null()) {
280 callback.Run(false, base::string16());
281 return;
284 int callback_id = pending_js_dialog_callbacks_.Add(
285 new content::JavaScriptDialogManager::DialogClosedCallback(callback));
286 ScopedJavaLocalRef<jstring> jurl(
287 ConvertUTF8ToJavaString(env, origin_url.spec()));
288 ScopedJavaLocalRef<jstring> jmessage(
289 ConvertUTF16ToJavaString(env, message_text));
291 switch (message_type) {
292 case content::JAVASCRIPT_MESSAGE_TYPE_ALERT: {
293 devtools_instrumentation::ScopedEmbedderCallbackTask("onJsAlert");
294 Java_AwContentsClientBridge_handleJsAlert(
295 env, obj.obj(), jurl.obj(), jmessage.obj(), callback_id);
296 break;
298 case content::JAVASCRIPT_MESSAGE_TYPE_CONFIRM: {
299 devtools_instrumentation::ScopedEmbedderCallbackTask("onJsConfirm");
300 Java_AwContentsClientBridge_handleJsConfirm(
301 env, obj.obj(), jurl.obj(), jmessage.obj(), callback_id);
302 break;
304 case content::JAVASCRIPT_MESSAGE_TYPE_PROMPT: {
305 ScopedJavaLocalRef<jstring> jdefault_value(
306 ConvertUTF16ToJavaString(env, default_prompt_text));
307 devtools_instrumentation::ScopedEmbedderCallbackTask("onJsPrompt");
308 Java_AwContentsClientBridge_handleJsPrompt(env,
309 obj.obj(),
310 jurl.obj(),
311 jmessage.obj(),
312 jdefault_value.obj(),
313 callback_id);
314 break;
316 default:
317 NOTREACHED();
321 void AwContentsClientBridge::RunBeforeUnloadDialog(
322 const GURL& origin_url,
323 const base::string16& message_text,
324 const content::JavaScriptDialogManager::DialogClosedCallback& callback) {
325 DCHECK_CURRENTLY_ON(BrowserThread::UI);
326 JNIEnv* env = AttachCurrentThread();
328 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
329 if (obj.is_null()) {
330 callback.Run(false, base::string16());
331 return;
334 int callback_id = pending_js_dialog_callbacks_.Add(
335 new content::JavaScriptDialogManager::DialogClosedCallback(callback));
336 ScopedJavaLocalRef<jstring> jurl(
337 ConvertUTF8ToJavaString(env, origin_url.spec()));
338 ScopedJavaLocalRef<jstring> jmessage(
339 ConvertUTF16ToJavaString(env, message_text));
341 devtools_instrumentation::ScopedEmbedderCallbackTask("onJsBeforeUnload");
342 Java_AwContentsClientBridge_handleJsBeforeUnload(
343 env, obj.obj(), jurl.obj(), jmessage.obj(), callback_id);
346 bool AwContentsClientBridge::ShouldOverrideUrlLoading(
347 const base::string16& url) {
348 JNIEnv* env = AttachCurrentThread();
349 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
350 if (obj.is_null())
351 return false;
352 ScopedJavaLocalRef<jstring> jurl = ConvertUTF16ToJavaString(env, url);
353 devtools_instrumentation::ScopedEmbedderCallbackTask(
354 "shouldOverrideUrlLoading");
355 return Java_AwContentsClientBridge_shouldOverrideUrlLoading(
356 env, obj.obj(),
357 jurl.obj());
360 void AwContentsClientBridge::ConfirmJsResult(JNIEnv* env,
361 jobject,
362 int id,
363 jstring prompt) {
364 DCHECK_CURRENTLY_ON(BrowserThread::UI);
365 content::JavaScriptDialogManager::DialogClosedCallback* callback =
366 pending_js_dialog_callbacks_.Lookup(id);
367 if (!callback) {
368 LOG(WARNING) << "Unexpected JS dialog confirm. " << id;
369 return;
371 base::string16 prompt_text;
372 if (prompt) {
373 prompt_text = ConvertJavaStringToUTF16(env, prompt);
375 callback->Run(true, prompt_text);
376 pending_js_dialog_callbacks_.Remove(id);
379 void AwContentsClientBridge::CancelJsResult(JNIEnv*, jobject, int id) {
380 DCHECK_CURRENTLY_ON(BrowserThread::UI);
381 content::JavaScriptDialogManager::DialogClosedCallback* callback =
382 pending_js_dialog_callbacks_.Lookup(id);
383 if (!callback) {
384 LOG(WARNING) << "Unexpected JS dialog cancel. " << id;
385 return;
387 callback->Run(false, base::string16());
388 pending_js_dialog_callbacks_.Remove(id);
391 // Use to cleanup if there is an error in client certificate response.
392 void AwContentsClientBridge::HandleErrorInClientCertificateResponse(
393 int request_id) {
394 content::ClientCertificateDelegate* delegate =
395 pending_client_cert_request_delegates_.Lookup(request_id);
396 pending_client_cert_request_delegates_.Remove(request_id);
398 delete delegate;
401 bool RegisterAwContentsClientBridge(JNIEnv* env) {
402 return RegisterNativesImpl(env);
405 } // namespace android_webview