Fix race condition in gyp/ninja builds.
[chromium-blink-merge.git] / android_webview / native / aw_contents_client_bridge.cc
bloba4f87d30fd429beef7453804c7a8a1ff1aad411f
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"
23 #include "url/gurl.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;
37 namespace {
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());
49 } // namespace
51 AwContentsClientBridge::AwContentsClientBridge(JNIEnv* env, jobject obj)
52 : java_ref_(env, obj) {
53 DCHECK(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);
62 if (obj.is_null())
63 return;
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(
70 int cert_error,
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);
80 if (obj.is_null())
81 return;
83 std::string der_string;
84 net::X509Certificate::GetDEREncoded(cert->os_cert_handle(), &der_string);
85 ScopedJavaLocalRef<jbyteArray> jcert = base::android::ToJavaByteArray(
86 env,
87 reinterpret_cast<const uint8*>(der_string.data()),
88 der_string.length());
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";
109 return;
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),
129 request_id));
131 JNIEnv* env = base::android::AttachCurrentThread();
132 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
133 if (obj.is_null())
134 return;
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");
142 break;
143 case net::CLIENT_CERT_DSS_SIGN:
144 key_types.push_back("DSA");
145 break;
146 case net::CLIENT_CERT_ECDSA_SIGN:
147 key_types.push_back("ECDSA");
148 break;
149 default:
150 // Ignore unknown types.
151 break;
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[])";
159 return;
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[][])";
168 return;
171 // Build the |host_name| and |port| JNI parameters, as a String and
172 // a jint.
173 ScopedJavaLocalRef<jstring> host_name_ref =
174 base::android::ConvertUTF8ToJavaString(
175 env, cert_request_info->host_and_port.host());
177 Java_AwContentsClientBridge_selectClientCertificate(
178 env,
179 obj.obj(),
180 request_id,
181 key_types_ref.obj(),
182 principals_ref.obj(),
183 host_name_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(
193 JNIEnv* env,
194 jobject obj,
195 int request_id,
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);
202 DCHECK(callback);
204 // Make sure callback is run on error.
205 base::ScopedClosureRunner guard(base::Bind(
206 &AwContentsClientBridge::HandleErrorInClientCertificateResponse,
207 base::Unretained(this),
208 request_id));
209 if (encoded_chain_ref == NULL || private_key_ref == NULL) {
210 LOG(ERROR) << "Client certificate request cancelled";
211 return;
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";
229 return;
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";
237 return;
240 // RecordClientCertificateKey() must be called on the I/O thread,
241 // before the callback is called with the selected certificate on
242 // the UI thread.
243 content::BrowserThread::PostTaskAndReply(
244 content::BrowserThread::IO,
245 FROM_HERE,
246 base::Bind(&RecordClientCertificateKey,
247 client_cert,
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);
266 if (obj.is_null())
267 return;
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);
281 break;
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);
287 break;
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,
294 obj.obj(),
295 jurl.obj(),
296 jmessage.obj(),
297 jdefault_value.obj(),
298 callback_id);
299 break;
301 default:
302 NOTREACHED();
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);
314 if (obj.is_null())
315 return;
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);
333 if (obj.is_null())
334 return false;
335 ScopedJavaLocalRef<jstring> jurl = ConvertUTF16ToJavaString(env, url);
336 devtools_instrumentation::ScopedEmbedderCallbackTask(
337 "shouldOverrideUrlLoading");
338 return Java_AwContentsClientBridge_shouldOverrideUrlLoading(
339 env, obj.obj(),
340 jurl.obj());
343 void AwContentsClientBridge::ConfirmJsResult(JNIEnv* env,
344 jobject,
345 int id,
346 jstring prompt) {
347 DCHECK(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(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 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