Upstream version 7.36.149.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 "base/android/jni_android.h"
9 #include "base/android/jni_array.h"
10 #include "base/android/jni_string.h"
11 #include "base/callback_helpers.h"
12 #include "content/public/browser/browser_thread.h"
13 #include "jni/AwContentsClientBridge_jni.h"
14 #include "net/android/keystore_openssl.h"
15 #include "net/cert/x509_certificate.h"
16 #include "net/ssl/openssl_client_key_store.h"
17 #include "net/ssl/ssl_cert_request_info.h"
18 #include "net/ssl/ssl_client_cert_type.h"
19 #include "url/gurl.h"
20
21 using base::android::AttachCurrentThread;
22 using base::android::ConvertJavaStringToUTF16;
23 using base::android::ConvertUTF8ToJavaString;
24 using base::android::ConvertUTF16ToJavaString;
25 using base::android::JavaRef;
26 using base::android::ScopedJavaLocalRef;
27 using content::BrowserThread;
28
29 namespace android_webview {
30
31 typedef net::OpenSSLClientKeyStore::ScopedEVP_PKEY ScopedEVP_PKEY;
32
33 namespace {
34
35 // Must be called on the I/O thread to record a client certificate
36 // and its private key in the OpenSSLClientKeyStore.
37 void RecordClientCertificateKey(
38     const scoped_refptr<net::X509Certificate>& client_cert,
39     ScopedEVP_PKEY private_key) {
40   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
41   net::OpenSSLClientKeyStore::GetInstance()->RecordClientCertPrivateKey(
42       client_cert.get(), private_key.get());
43 }
44
45 }  // namespace
46
47 AwContentsClientBridge::AwContentsClientBridge(JNIEnv* env, jobject obj)
48     : java_ref_(env, obj) {
49   DCHECK(obj);
50   Java_AwContentsClientBridge_setNativeContentsClientBridge(
51       env, obj, reinterpret_cast<intptr_t>(this));
52 }
53
54 AwContentsClientBridge::~AwContentsClientBridge() {
55   JNIEnv* env = AttachCurrentThread();
56
57   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
58   if (obj.is_null())
59     return;
60   // Clear the weak reference from the java peer to the native object since
61   // it is possible that java object lifetime can exceed the AwContens.
62   Java_AwContentsClientBridge_setNativeContentsClientBridge(env, obj.obj(), 0);
63 }
64
65 void AwContentsClientBridge::AllowCertificateError(
66     int cert_error,
67     net::X509Certificate* cert,
68     const GURL& request_url,
69     const base::Callback<void(bool)>& callback,
70     bool* cancel_request) {
71
72   DCHECK_CURRENTLY_ON(BrowserThread::UI);
73   JNIEnv* env = AttachCurrentThread();
74
75   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
76   if (obj.is_null())
77     return;
78
79   std::string der_string;
80   net::X509Certificate::GetDEREncoded(cert->os_cert_handle(), &der_string);
81   ScopedJavaLocalRef<jbyteArray> jcert = base::android::ToJavaByteArray(
82       env,
83       reinterpret_cast<const uint8*>(der_string.data()),
84       der_string.length());
85   ScopedJavaLocalRef<jstring> jurl(ConvertUTF8ToJavaString(
86       env, request_url.spec()));
87   // We need to add the callback before making the call to java side,
88   // as it may do a synchronous callback prior to returning.
89   int request_id = pending_cert_error_callbacks_.Add(
90       new CertErrorCallback(callback));
91   *cancel_request = !Java_AwContentsClientBridge_allowCertificateError(
92       env, obj.obj(), cert_error, jcert.obj(), jurl.obj(), request_id);
93   // if the request is cancelled, then cancel the stored callback
94   if (*cancel_request) {
95     pending_cert_error_callbacks_.Remove(request_id);
96   }
97 }
98
99 void AwContentsClientBridge::ProceedSslError(JNIEnv* env, jobject obj,
100                                              jboolean proceed, jint id) {
101   DCHECK_CURRENTLY_ON(BrowserThread::UI);
102   CertErrorCallback* callback = pending_cert_error_callbacks_.Lookup(id);
103   if (!callback || callback->is_null()) {
104     LOG(WARNING) << "Ignoring unexpected ssl error proceed callback";
105     return;
106   }
107   callback->Run(proceed);
108   pending_cert_error_callbacks_.Remove(id);
109 }
110
111 // This method is inspired by SelectClientCertificate() in
112 // chrome/browser/ui/android/ssl_client_certificate_request.cc
113 void AwContentsClientBridge::SelectClientCertificate(
114       net::SSLCertRequestInfo* cert_request_info,
115       const SelectCertificateCallback& callback) {
116   DCHECK_CURRENTLY_ON(BrowserThread::UI);
117
118   // Add the callback to id map.
119   int request_id = pending_client_cert_request_callbacks_.Add(
120       new SelectCertificateCallback(callback));
121   // Make sure callback is run on error.
122   base::ScopedClosureRunner guard(base::Bind(
123       &AwContentsClientBridge::HandleErrorInClientCertificateResponse,
124       base::Unretained(this),
125       request_id));
126
127   JNIEnv* env = base::android::AttachCurrentThread();
128   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
129   if (obj.is_null())
130     return;
131
132   // Build the |key_types| JNI parameter, as a String[]
133   std::vector<std::string> key_types;
134   for (size_t i = 0; i < cert_request_info->cert_key_types.size(); ++i) {
135     switch (cert_request_info->cert_key_types[i]) {
136       case net::CLIENT_CERT_RSA_SIGN:
137         key_types.push_back("RSA");
138         break;
139       case net::CLIENT_CERT_DSS_SIGN:
140         key_types.push_back("DSA");
141         break;
142       case net::CLIENT_CERT_ECDSA_SIGN:
143         key_types.push_back("ECDSA");
144         break;
145       default:
146         // Ignore unknown types.
147         break;
148     }
149   }
150
151   ScopedJavaLocalRef<jobjectArray> key_types_ref =
152       base::android::ToJavaArrayOfStrings(env, key_types);
153   if (key_types_ref.is_null()) {
154     LOG(ERROR) << "Could not create key types array (String[])";
155     return;
156   }
157
158   // Build the |encoded_principals| JNI parameter, as a byte[][]
159   ScopedJavaLocalRef<jobjectArray> principals_ref =
160       base::android::ToJavaArrayOfByteArray(
161           env, cert_request_info->cert_authorities);
162   if (principals_ref.is_null()) {
163     LOG(ERROR) << "Could not create principals array (byte[][])";
164     return;
165   }
166
167   // Build the |host_name| and |port| JNI parameters, as a String and
168   // a jint.
169   ScopedJavaLocalRef<jstring> host_name_ref =
170       base::android::ConvertUTF8ToJavaString(
171           env, cert_request_info->host_and_port.host());
172
173   Java_AwContentsClientBridge_selectClientCertificate(
174       env,
175       obj.obj(),
176       request_id,
177       key_types_ref.obj(),
178       principals_ref.obj(),
179       host_name_ref.obj(),
180       cert_request_info->host_and_port.port());
181
182   // Release the guard.
183   ignore_result(guard.Release());
184 }
185
186 // This method is inspired by OnSystemRequestCompletion() in
187 // chrome/browser/ui/android/ssl_client_certificate_request.cc
188 void AwContentsClientBridge::ProvideClientCertificateResponse(
189     JNIEnv* env,
190     jobject obj,
191     int request_id,
192     jobjectArray encoded_chain_ref,
193     jobject private_key_ref) {
194   DCHECK_CURRENTLY_ON(BrowserThread::UI);
195
196   SelectCertificateCallback* callback =
197       pending_client_cert_request_callbacks_.Lookup(request_id);
198   DCHECK(callback);
199
200   // Make sure callback is run on error.
201   base::ScopedClosureRunner guard(base::Bind(
202       &AwContentsClientBridge::HandleErrorInClientCertificateResponse,
203       base::Unretained(this),
204       request_id));
205   if (encoded_chain_ref == NULL || private_key_ref == NULL) {
206     LOG(ERROR) << "Client certificate request cancelled";
207     return;
208   }
209   // Convert the encoded chain to a vector of strings.
210   std::vector<std::string> encoded_chain_strings;
211   if (encoded_chain_ref) {
212     base::android::JavaArrayOfByteArrayToStringVector(
213         env, encoded_chain_ref, &encoded_chain_strings);
214   }
215
216   std::vector<base::StringPiece> encoded_chain;
217   for (size_t i = 0; i < encoded_chain_strings.size(); ++i)
218     encoded_chain.push_back(encoded_chain_strings[i]);
219
220   // Create the X509Certificate object from the encoded chain.
221   scoped_refptr<net::X509Certificate> client_cert(
222       net::X509Certificate::CreateFromDERCertChain(encoded_chain));
223   if (!client_cert.get()) {
224     LOG(ERROR) << "Could not decode client certificate chain";
225     return;
226   }
227
228   // Create an EVP_PKEY wrapper for the private key JNI reference.
229   ScopedEVP_PKEY private_key(
230       net::android::GetOpenSSLPrivateKeyWrapper(private_key_ref));
231   if (!private_key.get()) {
232     LOG(ERROR) << "Could not create OpenSSL wrapper for private key";
233     return;
234   }
235
236   // RecordClientCertificateKey() must be called on the I/O thread,
237   // before the callback is called with the selected certificate on
238   // the UI thread.
239   content::BrowserThread::PostTaskAndReply(
240       content::BrowserThread::IO,
241       FROM_HERE,
242       base::Bind(&RecordClientCertificateKey,
243                  client_cert,
244                  base::Passed(&private_key)),
245       base::Bind(*callback, client_cert));
246   pending_client_cert_request_callbacks_.Remove(request_id);
247
248   // Release the guard.
249   ignore_result(guard.Release());
250 }
251
252 void AwContentsClientBridge::RunJavaScriptDialog(
253     content::JavaScriptMessageType message_type,
254     const GURL& origin_url,
255     const base::string16& message_text,
256     const base::string16& default_prompt_text,
257     const content::JavaScriptDialogManager::DialogClosedCallback& callback) {
258   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
259   JNIEnv* env = AttachCurrentThread();
260
261   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
262   if (obj.is_null())
263     return;
264
265   int callback_id = pending_js_dialog_callbacks_.Add(
266       new content::JavaScriptDialogManager::DialogClosedCallback(callback));
267   ScopedJavaLocalRef<jstring> jurl(
268       ConvertUTF8ToJavaString(env, origin_url.spec()));
269   ScopedJavaLocalRef<jstring> jmessage(
270       ConvertUTF16ToJavaString(env, message_text));
271
272   switch (message_type) {
273     case content::JAVASCRIPT_MESSAGE_TYPE_ALERT: {
274       devtools_instrumentation::ScopedEmbedderCallbackTask("onJsAlert");
275       Java_AwContentsClientBridge_handleJsAlert(
276           env, obj.obj(), jurl.obj(), jmessage.obj(), callback_id);
277       break;
278     }
279     case content::JAVASCRIPT_MESSAGE_TYPE_CONFIRM: {
280       devtools_instrumentation::ScopedEmbedderCallbackTask("onJsConfirm");
281       Java_AwContentsClientBridge_handleJsConfirm(
282           env, obj.obj(), jurl.obj(), jmessage.obj(), callback_id);
283       break;
284     }
285     case content::JAVASCRIPT_MESSAGE_TYPE_PROMPT: {
286       ScopedJavaLocalRef<jstring> jdefault_value(
287           ConvertUTF16ToJavaString(env, default_prompt_text));
288       devtools_instrumentation::ScopedEmbedderCallbackTask("onJsPrompt");
289       Java_AwContentsClientBridge_handleJsPrompt(env,
290                                                  obj.obj(),
291                                                  jurl.obj(),
292                                                  jmessage.obj(),
293                                                  jdefault_value.obj(),
294                                                  callback_id);
295       break;
296     }
297     default:
298        NOTREACHED();
299   }
300 }
301
302 void AwContentsClientBridge::RunBeforeUnloadDialog(
303     const GURL& origin_url,
304     const base::string16& message_text,
305     const content::JavaScriptDialogManager::DialogClosedCallback& callback) {
306   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
307   JNIEnv* env = AttachCurrentThread();
308
309   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
310   if (obj.is_null())
311     return;
312
313   int callback_id = pending_js_dialog_callbacks_.Add(
314       new content::JavaScriptDialogManager::DialogClosedCallback(callback));
315   ScopedJavaLocalRef<jstring> jurl(
316       ConvertUTF8ToJavaString(env, origin_url.spec()));
317   ScopedJavaLocalRef<jstring> jmessage(
318       ConvertUTF16ToJavaString(env, message_text));
319
320   devtools_instrumentation::ScopedEmbedderCallbackTask("onJsBeforeUnload");
321   Java_AwContentsClientBridge_handleJsBeforeUnload(
322       env, obj.obj(), jurl.obj(), jmessage.obj(), callback_id);
323 }
324
325 bool AwContentsClientBridge::ShouldOverrideUrlLoading(
326     const base::string16& url) {
327   JNIEnv* env = AttachCurrentThread();
328   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
329   if (obj.is_null())
330     return false;
331   ScopedJavaLocalRef<jstring> jurl = ConvertUTF16ToJavaString(env, url);
332   devtools_instrumentation::ScopedEmbedderCallbackTask(
333       "shouldOverrideUrlLoading");
334   return Java_AwContentsClientBridge_shouldOverrideUrlLoading(
335       env, obj.obj(),
336       jurl.obj());
337 }
338
339 void AwContentsClientBridge::ConfirmJsResult(JNIEnv* env,
340                                              jobject,
341                                              int id,
342                                              jstring prompt) {
343   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
344   content::JavaScriptDialogManager::DialogClosedCallback* callback =
345       pending_js_dialog_callbacks_.Lookup(id);
346   if (!callback) {
347     LOG(WARNING) << "Unexpected JS dialog confirm. " << id;
348     return;
349   }
350   base::string16 prompt_text;
351   if (prompt) {
352     prompt_text = ConvertJavaStringToUTF16(env, prompt);
353   }
354   callback->Run(true, prompt_text);
355   pending_js_dialog_callbacks_.Remove(id);
356 }
357
358 void AwContentsClientBridge::CancelJsResult(JNIEnv*, jobject, int id) {
359   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
360   content::JavaScriptDialogManager::DialogClosedCallback* callback =
361       pending_js_dialog_callbacks_.Lookup(id);
362   if (!callback) {
363     LOG(WARNING) << "Unexpected JS dialog cancel. " << id;
364     return;
365   }
366   callback->Run(false, base::string16());
367   pending_js_dialog_callbacks_.Remove(id);
368 }
369
370 // Use to cleanup if there is an error in client certificate response.
371 void AwContentsClientBridge::HandleErrorInClientCertificateResponse(
372     int request_id) {
373   SelectCertificateCallback* callback =
374       pending_client_cert_request_callbacks_.Lookup(request_id);
375   callback->Run(scoped_refptr<net::X509Certificate>());
376   pending_client_cert_request_callbacks_.Remove(request_id);
377 }
378
379 bool RegisterAwContentsClientBridge(JNIEnv* env) {
380   return RegisterNativesImpl(env);
381 }
382
383 }  // namespace android_webview