MSE: Reduce spurious buffered range discontinuities
[chromium-blink-merge.git] / android_webview / native / aw_contents_client_bridge.cc
blobf5bccdffd86bc6a5b25f4c1c3d1df2e9796dc373
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"
24 #include "url/gurl.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 {
36 namespace {
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());
48 } // namespace
50 AwContentsClientBridge::AwContentsClientBridge(JNIEnv* env, jobject obj)
51 : java_ref_(env, obj) {
52 DCHECK(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);
61 if (obj.is_null())
62 return;
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(
69 int cert_error,
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);
79 if (obj.is_null())
80 return;
82 std::string der_string;
83 net::X509Certificate::GetDEREncoded(cert->os_cert_handle(), &der_string);
84 ScopedJavaLocalRef<jbyteArray> jcert = base::android::ToJavaByteArray(
85 env,
86 reinterpret_cast<const uint8*>(der_string.data()),
87 der_string.length());
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";
108 return;
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),
128 request_id));
130 JNIEnv* env = base::android::AttachCurrentThread();
131 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
132 if (obj.is_null())
133 return;
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");
141 break;
142 case net::CLIENT_CERT_DSS_SIGN:
143 key_types.push_back("DSA");
144 break;
145 case net::CLIENT_CERT_ECDSA_SIGN:
146 key_types.push_back("ECDSA");
147 break;
148 default:
149 // Ignore unknown types.
150 break;
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[])";
158 return;
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[][])";
167 return;
170 // Build the |host_name| and |port| JNI parameters, as a String and
171 // a jint.
172 ScopedJavaLocalRef<jstring> host_name_ref =
173 base::android::ConvertUTF8ToJavaString(
174 env, cert_request_info->host_and_port.host());
176 Java_AwContentsClientBridge_selectClientCertificate(
177 env,
178 obj.obj(),
179 request_id,
180 key_types_ref.obj(),
181 principals_ref.obj(),
182 host_name_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(
192 JNIEnv* env,
193 jobject obj,
194 int request_id,
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);
201 DCHECK(callback);
203 // Make sure callback is run on error.
204 base::ScopedClosureRunner guard(base::Bind(
205 &AwContentsClientBridge::HandleErrorInClientCertificateResponse,
206 base::Unretained(this),
207 request_id));
208 if (encoded_chain_ref == NULL || private_key_ref == NULL) {
209 LOG(ERROR) << "Client certificate request cancelled";
210 return;
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";
228 return;
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";
236 return;
239 // RecordClientCertificateKey() must be called on the I/O thread,
240 // before the callback is called with the selected certificate on
241 // the UI thread.
242 content::BrowserThread::PostTaskAndReply(
243 content::BrowserThread::IO,
244 FROM_HERE,
245 base::Bind(&RecordClientCertificateKey,
246 client_cert,
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);
265 if (obj.is_null()) {
266 callback.Run(false, base::string16());
267 return;
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);
282 break;
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);
288 break;
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,
295 obj.obj(),
296 jurl.obj(),
297 jmessage.obj(),
298 jdefault_value.obj(),
299 callback_id);
300 break;
302 default:
303 NOTREACHED();
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);
315 if (obj.is_null()) {
316 callback.Run(false, base::string16());
317 return;
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);
336 if (obj.is_null())
337 return false;
338 ScopedJavaLocalRef<jstring> jurl = ConvertUTF16ToJavaString(env, url);
339 devtools_instrumentation::ScopedEmbedderCallbackTask(
340 "shouldOverrideUrlLoading");
341 return Java_AwContentsClientBridge_shouldOverrideUrlLoading(
342 env, obj.obj(),
343 jurl.obj());
346 void AwContentsClientBridge::ConfirmJsResult(JNIEnv* env,
347 jobject,
348 int id,
349 jstring prompt) {
350 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
351 content::JavaScriptDialogManager::DialogClosedCallback* callback =
352 pending_js_dialog_callbacks_.Lookup(id);
353 if (!callback) {
354 LOG(WARNING) << "Unexpected JS dialog confirm. " << id;
355 return;
357 base::string16 prompt_text;
358 if (prompt) {
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);
369 if (!callback) {
370 LOG(WARNING) << "Unexpected JS dialog cancel. " << id;
371 return;
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(
379 int request_id) {
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