Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / android_webview / native / aw_contents_client_bridge.cc
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 "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"
25
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;
33
34 namespace android_webview {
35
36 namespace {
37
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());
46 }
47
48 }  // namespace
49
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));
55 }
56
57 AwContentsClientBridge::~AwContentsClientBridge() {
58   JNIEnv* env = AttachCurrentThread();
59
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);
66 }
67
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) {
74
75   DCHECK_CURRENTLY_ON(BrowserThread::UI);
76   JNIEnv* env = AttachCurrentThread();
77
78   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
79   if (obj.is_null())
80     return;
81
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);
99   }
100 }
101
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;
109   }
110   callback->Run(proceed);
111   pending_cert_error_callbacks_.Remove(id);
112 }
113
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);
120
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));
129
130   JNIEnv* env = base::android::AttachCurrentThread();
131   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
132   if (obj.is_null())
133     return;
134
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;
151     }
152   }
153
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;
159   }
160
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;
168   }
169
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());
175
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());
184
185   // Release the guard.
186   ignore_result(guard.Release());
187 }
188
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);
198
199   SelectCertificateCallback* callback =
200       pending_client_cert_request_callbacks_.Lookup(request_id);
201   DCHECK(callback);
202
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;
211   }
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);
217   }
218
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]);
222
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;
229   }
230
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;
237   }
238
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);
250
251   // Release the guard.
252   ignore_result(guard.Release());
253 }
254
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();
263
264   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
265   if (obj.is_null())
266     return;
267
268   int callback_id = pending_js_dialog_callbacks_.Add(
269       new content::JavaScriptDialogManager::DialogClosedCallback(callback));
270   ScopedJavaLocalRef<jstring> jurl(
271       ConvertUTF8ToJavaString(env, origin_url.spec()));
272   ScopedJavaLocalRef<jstring> jmessage(
273       ConvertUTF16ToJavaString(env, message_text));
274
275   switch (message_type) {
276     case content::JAVASCRIPT_MESSAGE_TYPE_ALERT: {
277       devtools_instrumentation::ScopedEmbedderCallbackTask("onJsAlert");
278       Java_AwContentsClientBridge_handleJsAlert(
279           env, obj.obj(), jurl.obj(), jmessage.obj(), callback_id);
280       break;
281     }
282     case content::JAVASCRIPT_MESSAGE_TYPE_CONFIRM: {
283       devtools_instrumentation::ScopedEmbedderCallbackTask("onJsConfirm");
284       Java_AwContentsClientBridge_handleJsConfirm(
285           env, obj.obj(), jurl.obj(), jmessage.obj(), callback_id);
286       break;
287     }
288     case content::JAVASCRIPT_MESSAGE_TYPE_PROMPT: {
289       ScopedJavaLocalRef<jstring> jdefault_value(
290           ConvertUTF16ToJavaString(env, default_prompt_text));
291       devtools_instrumentation::ScopedEmbedderCallbackTask("onJsPrompt");
292       Java_AwContentsClientBridge_handleJsPrompt(env,
293                                                  obj.obj(),
294                                                  jurl.obj(),
295                                                  jmessage.obj(),
296                                                  jdefault_value.obj(),
297                                                  callback_id);
298       break;
299     }
300     default:
301        NOTREACHED();
302   }
303 }
304
305 void AwContentsClientBridge::RunBeforeUnloadDialog(
306     const GURL& origin_url,
307     const base::string16& message_text,
308     const content::JavaScriptDialogManager::DialogClosedCallback& callback) {
309   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
310   JNIEnv* env = AttachCurrentThread();
311
312   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
313   if (obj.is_null())
314     return;
315
316   int callback_id = pending_js_dialog_callbacks_.Add(
317       new content::JavaScriptDialogManager::DialogClosedCallback(callback));
318   ScopedJavaLocalRef<jstring> jurl(
319       ConvertUTF8ToJavaString(env, origin_url.spec()));
320   ScopedJavaLocalRef<jstring> jmessage(
321       ConvertUTF16ToJavaString(env, message_text));
322
323   devtools_instrumentation::ScopedEmbedderCallbackTask("onJsBeforeUnload");
324   Java_AwContentsClientBridge_handleJsBeforeUnload(
325       env, obj.obj(), jurl.obj(), jmessage.obj(), callback_id);
326 }
327
328 bool AwContentsClientBridge::ShouldOverrideUrlLoading(
329     const base::string16& url) {
330   JNIEnv* env = AttachCurrentThread();
331   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
332   if (obj.is_null())
333     return false;
334   ScopedJavaLocalRef<jstring> jurl = ConvertUTF16ToJavaString(env, url);
335   devtools_instrumentation::ScopedEmbedderCallbackTask(
336       "shouldOverrideUrlLoading");
337   return Java_AwContentsClientBridge_shouldOverrideUrlLoading(
338       env, obj.obj(),
339       jurl.obj());
340 }
341
342 void AwContentsClientBridge::ConfirmJsResult(JNIEnv* env,
343                                              jobject,
344                                              int id,
345                                              jstring prompt) {
346   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
347   content::JavaScriptDialogManager::DialogClosedCallback* callback =
348       pending_js_dialog_callbacks_.Lookup(id);
349   if (!callback) {
350     LOG(WARNING) << "Unexpected JS dialog confirm. " << id;
351     return;
352   }
353   base::string16 prompt_text;
354   if (prompt) {
355     prompt_text = ConvertJavaStringToUTF16(env, prompt);
356   }
357   callback->Run(true, prompt_text);
358   pending_js_dialog_callbacks_.Remove(id);
359 }
360
361 void AwContentsClientBridge::CancelJsResult(JNIEnv*, jobject, int id) {
362   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
363   content::JavaScriptDialogManager::DialogClosedCallback* callback =
364       pending_js_dialog_callbacks_.Lookup(id);
365   if (!callback) {
366     LOG(WARNING) << "Unexpected JS dialog cancel. " << id;
367     return;
368   }
369   callback->Run(false, base::string16());
370   pending_js_dialog_callbacks_.Remove(id);
371 }
372
373 // Use to cleanup if there is an error in client certificate response.
374 void AwContentsClientBridge::HandleErrorInClientCertificateResponse(
375     int request_id) {
376   SelectCertificateCallback* callback =
377       pending_client_cert_request_callbacks_.Lookup(request_id);
378   callback->Run(scoped_refptr<net::X509Certificate>());
379   pending_client_cert_request_callbacks_.Remove(request_id);
380 }
381
382 bool RegisterAwContentsClientBridge(JNIEnv* env) {
383   return RegisterNativesImpl(env);
384 }
385
386 }  // namespace android_webview