Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(185)

Side by Side Diff: android_webview/native/aw_contents_client_bridge.cc

Issue 2863233002: [WebView] Move files from native to browser (Closed)
Patch Set: Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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.
4
5 #include "android_webview/native/aw_contents_client_bridge.h"
6
7 #include <memory>
8 #include <utility>
9
10 #include "android_webview/common/devtools_instrumentation.h"
11 #include "android_webview/grit/components_strings.h"
12 #include "android_webview/native/aw_contents.h"
13 #include "base/android/jni_android.h"
14 #include "base/android/jni_array.h"
15 #include "base/android/jni_string.h"
16 #include "base/callback_helpers.h"
17 #include "base/macros.h"
18 #include "base/memory/ptr_util.h"
19 #include "base/memory/ref_counted.h"
20 #include "base/message_loop/message_loop.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "content/public/browser/client_certificate_delegate.h"
23 #include "content/public/browser/render_process_host.h"
24 #include "content/public/browser/render_view_host.h"
25 #include "content/public/browser/web_contents.h"
26 #include "jni/AwContentsClientBridge_jni.h"
27 #include "net/cert/x509_certificate.h"
28 #include "net/http/http_response_headers.h"
29 #include "net/ssl/openssl_client_key_store.h"
30 #include "net/ssl/ssl_cert_request_info.h"
31 #include "net/ssl/ssl_client_cert_type.h"
32 #include "net/ssl/ssl_platform_key_android.h"
33 #include "net/ssl/ssl_private_key.h"
34 #include "ui/base/l10n/l10n_util.h"
35 #include "url/gurl.h"
36
37 using base::android::AttachCurrentThread;
38 using base::android::ConvertJavaStringToUTF16;
39 using base::android::ConvertUTF8ToJavaString;
40 using base::android::ConvertUTF16ToJavaString;
41 using base::android::HasException;
42 using base::android::JavaRef;
43 using base::android::ScopedJavaLocalRef;
44 using base::android::ToJavaArrayOfStrings;
45 using content::BrowserThread;
46 using std::vector;
47
48 namespace android_webview {
49
50 namespace {
51
52 // Must be called on the I/O thread to record a client certificate
53 // and its private key in the OpenSSLClientKeyStore.
54 void RecordClientCertificateKey(net::X509Certificate* client_cert,
55 scoped_refptr<net::SSLPrivateKey> private_key) {
56 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
57 net::OpenSSLClientKeyStore::GetInstance()->RecordClientCertPrivateKey(
58 client_cert, std::move(private_key));
59 }
60
61 } // namespace
62
63 AwContentsClientBridge::AwContentsClientBridge(JNIEnv* env,
64 const JavaRef<jobject>& obj)
65 : java_ref_(env, obj) {
66 DCHECK(!obj.is_null());
67 Java_AwContentsClientBridge_setNativeContentsClientBridge(
68 env, obj, reinterpret_cast<intptr_t>(this));
69 }
70
71 AwContentsClientBridge::~AwContentsClientBridge() {
72 JNIEnv* env = AttachCurrentThread();
73
74 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
75 if (!obj.is_null()) {
76 // Clear the weak reference from the java peer to the native object since
77 // it is possible that java object lifetime can exceed the AwContens.
78 Java_AwContentsClientBridge_setNativeContentsClientBridge(env, obj, 0);
79 }
80
81 for (IDMap<content::ClientCertificateDelegate*>::iterator iter(
82 &pending_client_cert_request_delegates_);
83 !iter.IsAtEnd(); iter.Advance()) {
84 delete iter.GetCurrentValue();
85 }
86 }
87
88 void AwContentsClientBridge::AllowCertificateError(
89 int cert_error,
90 net::X509Certificate* cert,
91 const GURL& request_url,
92 const base::Callback<void(content::CertificateRequestResultType)>& callback,
93 bool* cancel_request) {
94 DCHECK_CURRENTLY_ON(BrowserThread::UI);
95 JNIEnv* env = AttachCurrentThread();
96
97 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
98 if (obj.is_null())
99 return;
100
101 std::string der_string;
102 net::X509Certificate::GetDEREncoded(cert->os_cert_handle(), &der_string);
103 ScopedJavaLocalRef<jbyteArray> jcert = base::android::ToJavaByteArray(
104 env, reinterpret_cast<const uint8_t*>(der_string.data()),
105 der_string.length());
106 ScopedJavaLocalRef<jstring> jurl(ConvertUTF8ToJavaString(
107 env, request_url.spec()));
108 // We need to add the callback before making the call to java side,
109 // as it may do a synchronous callback prior to returning.
110 int request_id = pending_cert_error_callbacks_.Add(
111 base::MakeUnique<CertErrorCallback>(callback));
112 *cancel_request = !Java_AwContentsClientBridge_allowCertificateError(
113 env, obj, cert_error, jcert, jurl, request_id);
114 // if the request is cancelled, then cancel the stored callback
115 if (*cancel_request) {
116 pending_cert_error_callbacks_.Remove(request_id);
117 }
118 }
119
120 void AwContentsClientBridge::ProceedSslError(JNIEnv* env,
121 const JavaRef<jobject>& obj,
122 jboolean proceed,
123 jint id) {
124 DCHECK_CURRENTLY_ON(BrowserThread::UI);
125 CertErrorCallback* callback = pending_cert_error_callbacks_.Lookup(id);
126 if (!callback || callback->is_null()) {
127 LOG(WARNING) << "Ignoring unexpected ssl error proceed callback";
128 return;
129 }
130 callback->Run(proceed ? content::CERTIFICATE_REQUEST_RESULT_TYPE_CONTINUE
131 : content::CERTIFICATE_REQUEST_RESULT_TYPE_CANCEL);
132 pending_cert_error_callbacks_.Remove(id);
133 }
134
135 // This method is inspired by SelectClientCertificate() in
136 // chrome/browser/ui/android/ssl_client_certificate_request.cc
137 void AwContentsClientBridge::SelectClientCertificate(
138 net::SSLCertRequestInfo* cert_request_info,
139 std::unique_ptr<content::ClientCertificateDelegate> delegate) {
140 DCHECK_CURRENTLY_ON(BrowserThread::UI);
141
142 // Add the callback to id map.
143 int request_id =
144 pending_client_cert_request_delegates_.Add(delegate.release());
145 // Make sure callback is run on error.
146 base::ScopedClosureRunner guard(base::Bind(
147 &AwContentsClientBridge::HandleErrorInClientCertificateResponse,
148 base::Unretained(this),
149 request_id));
150
151 JNIEnv* env = base::android::AttachCurrentThread();
152 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
153 if (obj.is_null())
154 return;
155
156 // Build the |key_types| JNI parameter, as a String[]
157 std::vector<std::string> key_types;
158 for (size_t i = 0; i < cert_request_info->cert_key_types.size(); ++i) {
159 switch (cert_request_info->cert_key_types[i]) {
160 case net::CLIENT_CERT_RSA_SIGN:
161 key_types.push_back("RSA");
162 break;
163 case net::CLIENT_CERT_ECDSA_SIGN:
164 key_types.push_back("ECDSA");
165 break;
166 default:
167 // Ignore unknown types.
168 break;
169 }
170 }
171
172 ScopedJavaLocalRef<jobjectArray> key_types_ref =
173 base::android::ToJavaArrayOfStrings(env, key_types);
174 if (key_types_ref.is_null()) {
175 LOG(ERROR) << "Could not create key types array (String[])";
176 return;
177 }
178
179 // Build the |encoded_principals| JNI parameter, as a byte[][]
180 ScopedJavaLocalRef<jobjectArray> principals_ref =
181 base::android::ToJavaArrayOfByteArray(
182 env, cert_request_info->cert_authorities);
183 if (principals_ref.is_null()) {
184 LOG(ERROR) << "Could not create principals array (byte[][])";
185 return;
186 }
187
188 // Build the |host_name| and |port| JNI parameters, as a String and
189 // a jint.
190 ScopedJavaLocalRef<jstring> host_name_ref =
191 base::android::ConvertUTF8ToJavaString(
192 env, cert_request_info->host_and_port.host());
193
194 Java_AwContentsClientBridge_selectClientCertificate(
195 env, obj, request_id, key_types_ref, principals_ref, host_name_ref,
196 cert_request_info->host_and_port.port());
197
198 // Release the guard.
199 ignore_result(guard.Release());
200 }
201
202 // This method is inspired by OnSystemRequestCompletion() in
203 // chrome/browser/ui/android/ssl_client_certificate_request.cc
204 void AwContentsClientBridge::ProvideClientCertificateResponse(
205 JNIEnv* env,
206 const JavaRef<jobject>& obj,
207 int request_id,
208 const JavaRef<jobjectArray>& encoded_chain_ref,
209 const JavaRef<jobject>& private_key_ref) {
210 DCHECK_CURRENTLY_ON(BrowserThread::UI);
211
212 content::ClientCertificateDelegate* delegate =
213 pending_client_cert_request_delegates_.Lookup(request_id);
214 DCHECK(delegate);
215
216 if (encoded_chain_ref.is_null() || private_key_ref.is_null()) {
217 LOG(ERROR) << "No client certificate selected";
218 pending_client_cert_request_delegates_.Remove(request_id);
219 delegate->ContinueWithCertificate(nullptr);
220 delete delegate;
221 return;
222 }
223
224 // Make sure callback is run on error.
225 base::ScopedClosureRunner guard(base::Bind(
226 &AwContentsClientBridge::HandleErrorInClientCertificateResponse,
227 base::Unretained(this),
228 request_id));
229
230 // Convert the encoded chain to a vector of strings.
231 std::vector<std::string> encoded_chain_strings;
232 if (!encoded_chain_ref.is_null()) {
233 base::android::JavaArrayOfByteArrayToStringVector(
234 env, encoded_chain_ref.obj(), &encoded_chain_strings);
235 }
236
237 std::vector<base::StringPiece> encoded_chain;
238 for (size_t i = 0; i < encoded_chain_strings.size(); ++i)
239 encoded_chain.push_back(encoded_chain_strings[i]);
240
241 // Create the X509Certificate object from the encoded chain.
242 scoped_refptr<net::X509Certificate> client_cert(
243 net::X509Certificate::CreateFromDERCertChain(encoded_chain));
244 if (!client_cert.get()) {
245 LOG(ERROR) << "Could not decode client certificate chain";
246 return;
247 }
248
249 // Create an SSLPrivateKey wrapper for the private key JNI reference.
250 scoped_refptr<net::SSLPrivateKey> private_key =
251 net::WrapJavaPrivateKey(client_cert.get(), private_key_ref);
252 if (!private_key) {
253 LOG(ERROR) << "Could not create OpenSSL wrapper for private key";
254 return;
255 }
256
257 // Release the guard and |pending_client_cert_request_delegates_| references
258 // to |delegate|.
259 pending_client_cert_request_delegates_.Remove(request_id);
260 ignore_result(guard.Release());
261
262 // RecordClientCertificateKey() must be called on the I/O thread,
263 // before the delegate is called with the selected certificate on
264 // the UI thread.
265 content::BrowserThread::PostTaskAndReply(
266 content::BrowserThread::IO, FROM_HERE,
267 base::Bind(&RecordClientCertificateKey, base::RetainedRef(client_cert),
268 base::Passed(&private_key)),
269 base::Bind(&content::ClientCertificateDelegate::ContinueWithCertificate,
270 base::Owned(delegate), base::RetainedRef(client_cert)));
271 }
272
273 void AwContentsClientBridge::RunJavaScriptDialog(
274 content::JavaScriptDialogType dialog_type,
275 const GURL& origin_url,
276 const base::string16& message_text,
277 const base::string16& default_prompt_text,
278 const content::JavaScriptDialogManager::DialogClosedCallback& callback) {
279 DCHECK_CURRENTLY_ON(BrowserThread::UI);
280 JNIEnv* env = AttachCurrentThread();
281
282 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
283 if (obj.is_null()) {
284 callback.Run(false, base::string16());
285 return;
286 }
287
288 int callback_id = pending_js_dialog_callbacks_.Add(
289 base::MakeUnique<content::JavaScriptDialogManager::DialogClosedCallback>(
290 callback));
291 ScopedJavaLocalRef<jstring> jurl(
292 ConvertUTF8ToJavaString(env, origin_url.spec()));
293 ScopedJavaLocalRef<jstring> jmessage(
294 ConvertUTF16ToJavaString(env, message_text));
295
296 switch (dialog_type) {
297 case content::JAVASCRIPT_DIALOG_TYPE_ALERT: {
298 devtools_instrumentation::ScopedEmbedderCallbackTask("onJsAlert");
299 Java_AwContentsClientBridge_handleJsAlert(env, obj, jurl, jmessage,
300 callback_id);
301 break;
302 }
303 case content::JAVASCRIPT_DIALOG_TYPE_CONFIRM: {
304 devtools_instrumentation::ScopedEmbedderCallbackTask("onJsConfirm");
305 Java_AwContentsClientBridge_handleJsConfirm(env, obj, jurl, jmessage,
306 callback_id);
307 break;
308 }
309 case content::JAVASCRIPT_DIALOG_TYPE_PROMPT: {
310 ScopedJavaLocalRef<jstring> jdefault_value(
311 ConvertUTF16ToJavaString(env, default_prompt_text));
312 devtools_instrumentation::ScopedEmbedderCallbackTask("onJsPrompt");
313 Java_AwContentsClientBridge_handleJsPrompt(env, obj, jurl, jmessage,
314 jdefault_value, callback_id);
315 break;
316 }
317 default:
318 NOTREACHED();
319 }
320 }
321
322 void AwContentsClientBridge::RunBeforeUnloadDialog(
323 const GURL& origin_url,
324 const content::JavaScriptDialogManager::DialogClosedCallback& callback) {
325 DCHECK_CURRENTLY_ON(BrowserThread::UI);
326 JNIEnv* env = AttachCurrentThread();
327
328 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
329 if (obj.is_null()) {
330 callback.Run(false, base::string16());
331 return;
332 }
333
334 const base::string16 message_text =
335 l10n_util::GetStringUTF16(IDS_BEFOREUNLOAD_MESSAGEBOX_MESSAGE);
336
337 int callback_id = pending_js_dialog_callbacks_.Add(
338 base::MakeUnique<content::JavaScriptDialogManager::DialogClosedCallback>(
339 callback));
340 ScopedJavaLocalRef<jstring> jurl(
341 ConvertUTF8ToJavaString(env, origin_url.spec()));
342 ScopedJavaLocalRef<jstring> jmessage(
343 ConvertUTF16ToJavaString(env, message_text));
344
345 devtools_instrumentation::ScopedEmbedderCallbackTask("onJsBeforeUnload");
346 Java_AwContentsClientBridge_handleJsBeforeUnload(env, obj, jurl, jmessage,
347 callback_id);
348 }
349
350 bool AwContentsClientBridge::ShouldOverrideUrlLoading(const base::string16& url,
351 bool has_user_gesture,
352 bool is_redirect,
353 bool is_main_frame) {
354 JNIEnv* env = AttachCurrentThread();
355 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
356 if (obj.is_null())
357 return false;
358 ScopedJavaLocalRef<jstring> jurl = ConvertUTF16ToJavaString(env, url);
359 devtools_instrumentation::ScopedEmbedderCallbackTask(
360 "shouldOverrideUrlLoading");
361 bool did_override = Java_AwContentsClientBridge_shouldOverrideUrlLoading(
362 env, obj, jurl, has_user_gesture, is_redirect, is_main_frame);
363 if (HasException(env)) {
364 // Tell the chromium message loop to not perform any tasks after the current
365 // one - we want to make sure we return to Java cleanly without first making
366 // any new JNI calls.
367 base::MessageLoopForUI::current()->Abort();
368 // If we crashed we don't want to continue the navigation.
369 return true;
370 }
371 return did_override;
372 }
373
374 void AwContentsClientBridge::NewDownload(const GURL& url,
375 const std::string& user_agent,
376 const std::string& content_disposition,
377 const std::string& mime_type,
378 int64_t content_length) {
379 DCHECK_CURRENTLY_ON(BrowserThread::UI);
380 JNIEnv* env = AttachCurrentThread();
381 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
382 if (obj.is_null())
383 return;
384
385 ScopedJavaLocalRef<jstring> jstring_url =
386 ConvertUTF8ToJavaString(env, url.spec());
387 ScopedJavaLocalRef<jstring> jstring_user_agent =
388 ConvertUTF8ToJavaString(env, user_agent);
389 ScopedJavaLocalRef<jstring> jstring_content_disposition =
390 ConvertUTF8ToJavaString(env, content_disposition);
391 ScopedJavaLocalRef<jstring> jstring_mime_type =
392 ConvertUTF8ToJavaString(env, mime_type);
393
394 Java_AwContentsClientBridge_newDownload(
395 env, obj, jstring_url, jstring_user_agent, jstring_content_disposition,
396 jstring_mime_type, content_length);
397 }
398
399 void AwContentsClientBridge::NewLoginRequest(const std::string& realm,
400 const std::string& account,
401 const std::string& args) {
402 DCHECK_CURRENTLY_ON(BrowserThread::UI);
403 JNIEnv* env = AttachCurrentThread();
404 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
405 if (obj.is_null())
406 return;
407
408 ScopedJavaLocalRef<jstring> jrealm = ConvertUTF8ToJavaString(env, realm);
409 ScopedJavaLocalRef<jstring> jargs = ConvertUTF8ToJavaString(env, args);
410
411 ScopedJavaLocalRef<jstring> jaccount;
412 if (!account.empty())
413 jaccount = ConvertUTF8ToJavaString(env, account);
414
415 Java_AwContentsClientBridge_newLoginRequest(env, obj, jrealm, jaccount,
416 jargs);
417 }
418
419 void AwContentsClientBridge::OnReceivedError(
420 const AwWebResourceRequest& request,
421 int error_code) {
422 DCHECK_CURRENTLY_ON(BrowserThread::UI);
423 JNIEnv* env = AttachCurrentThread();
424 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
425 if (obj.is_null())
426 return;
427
428 ScopedJavaLocalRef<jstring> jstring_description =
429 ConvertUTF8ToJavaString(env, net::ErrorToString(error_code));
430
431 AwWebResourceRequest::AwJavaWebResourceRequest java_web_resource_request;
432 AwWebResourceRequest::ConvertToJava(env, request, &java_web_resource_request);
433
434 Java_AwContentsClientBridge_onReceivedError(
435 env, obj, java_web_resource_request.jurl, request.is_main_frame,
436 request.has_user_gesture, java_web_resource_request.jmethod,
437 java_web_resource_request.jheader_names,
438 java_web_resource_request.jheader_values, error_code,
439 jstring_description);
440 }
441
442 void AwContentsClientBridge::OnReceivedHttpError(
443 const AwWebResourceRequest& request,
444 const scoped_refptr<const net::HttpResponseHeaders>& response_headers) {
445 DCHECK_CURRENTLY_ON(BrowserThread::UI);
446 JNIEnv* env = AttachCurrentThread();
447 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
448 if (obj.is_null())
449 return;
450
451 AwWebResourceRequest::AwJavaWebResourceRequest java_web_resource_request;
452 AwWebResourceRequest::ConvertToJava(env, request, &java_web_resource_request);
453
454 vector<std::string> response_header_names;
455 vector<std::string> response_header_values;
456
457 {
458 size_t headers_iterator = 0;
459 std::string header_name, header_value;
460 while (response_headers->EnumerateHeaderLines(
461 &headers_iterator, &header_name, &header_value)) {
462 response_header_names.push_back(header_name);
463 response_header_values.push_back(header_value);
464 }
465 }
466
467 std::string mime_type, encoding;
468 response_headers->GetMimeTypeAndCharset(&mime_type, &encoding);
469 ScopedJavaLocalRef<jstring> jstring_mime_type =
470 ConvertUTF8ToJavaString(env, mime_type);
471 ScopedJavaLocalRef<jstring> jstring_encoding =
472 ConvertUTF8ToJavaString(env, encoding);
473 int status_code = response_headers->response_code();
474 ScopedJavaLocalRef<jstring> jstring_reason =
475 ConvertUTF8ToJavaString(env, response_headers->GetStatusText());
476 ScopedJavaLocalRef<jobjectArray> jstringArray_response_header_names =
477 ToJavaArrayOfStrings(env, response_header_names);
478 ScopedJavaLocalRef<jobjectArray> jstringArray_response_header_values =
479 ToJavaArrayOfStrings(env, response_header_values);
480
481 Java_AwContentsClientBridge_onReceivedHttpError(
482 env, obj, java_web_resource_request.jurl, request.is_main_frame,
483 request.has_user_gesture, java_web_resource_request.jmethod,
484 java_web_resource_request.jheader_names,
485 java_web_resource_request.jheader_values, jstring_mime_type,
486 jstring_encoding, status_code, jstring_reason,
487 jstringArray_response_header_names, jstringArray_response_header_values);
488 }
489
490 void AwContentsClientBridge::ConfirmJsResult(JNIEnv* env,
491 const JavaRef<jobject>&,
492 int id,
493 const JavaRef<jstring>& prompt) {
494 DCHECK_CURRENTLY_ON(BrowserThread::UI);
495 content::JavaScriptDialogManager::DialogClosedCallback* callback =
496 pending_js_dialog_callbacks_.Lookup(id);
497 if (!callback) {
498 LOG(WARNING) << "Unexpected JS dialog confirm. " << id;
499 return;
500 }
501 base::string16 prompt_text;
502 if (!prompt.is_null()) {
503 prompt_text = ConvertJavaStringToUTF16(env, prompt);
504 }
505 callback->Run(true, prompt_text);
506 pending_js_dialog_callbacks_.Remove(id);
507 }
508
509 void AwContentsClientBridge::CancelJsResult(JNIEnv*,
510 const JavaRef<jobject>&,
511 int id) {
512 DCHECK_CURRENTLY_ON(BrowserThread::UI);
513 content::JavaScriptDialogManager::DialogClosedCallback* callback =
514 pending_js_dialog_callbacks_.Lookup(id);
515 if (!callback) {
516 LOG(WARNING) << "Unexpected JS dialog cancel. " << id;
517 return;
518 }
519 callback->Run(false, base::string16());
520 pending_js_dialog_callbacks_.Remove(id);
521 }
522
523 // Use to cleanup if there is an error in client certificate response.
524 void AwContentsClientBridge::HandleErrorInClientCertificateResponse(
525 int request_id) {
526 content::ClientCertificateDelegate* delegate =
527 pending_client_cert_request_delegates_.Lookup(request_id);
528 pending_client_cert_request_delegates_.Remove(request_id);
529
530 delete delegate;
531 }
532
533 bool RegisterAwContentsClientBridge(JNIEnv* env) {
534 return RegisterNativesImpl(env);
535 }
536
537 } // namespace android_webview
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698