Upstream version 8.37.187.0
[platform/framework/web/crosswalk.git] / src / xwalk / runtime / browser / android / xwalk_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 "xwalk/runtime/browser/android/xwalk_contents_client_bridge.h"
6
7 #include <string>
8
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.h"
13 #include "base/guid.h"
14 #include "content/public/browser/browser_thread.h"
15 #include "content/public/browser/desktop_notification_delegate.h"
16 #include "content/public/browser/render_frame_host.h"
17 #include "content/public/browser/render_process_host.h"
18 #include "content/public/browser/render_view_host.h"
19 #include "content/public/browser/web_contents.h"
20 #include "content/public/common/file_chooser_params.h"
21 #include "content/public/common/show_desktop_notification_params.h"
22 #include "jni/XWalkContentsClientBridge_jni.h"
23 #include "net/cert/x509_certificate.h"
24 #include "third_party/skia/include/core/SkBitmap.h"
25 #include "url/gurl.h"
26 #include "ui/gfx/android/java_bitmap.h"
27 #include "ui/shell_dialogs/selected_file_info.h"
28
29 using base::android::AttachCurrentThread;
30 using base::android::ConvertJavaStringToUTF16;
31 using base::android::ConvertUTF8ToJavaString;
32 using base::android::ConvertUTF16ToJavaString;
33 using base::android::JavaRef;
34 using base::android::ScopedJavaLocalRef;
35 using content::BrowserThread;
36 using content::FileChooserParams;
37 using content::RenderViewHost;
38 using content::WebContents;
39
40 namespace xwalk {
41
42 namespace {
43
44 void RunUpdateNotificationIconOnUIThread(
45     int notification_id,
46     content::RenderFrameHost* render_frame_host,
47     const SkBitmap& icon) {
48   XWalkContentsClientBridgeBase* bridge =
49       XWalkContentsClientBridgeBase::FromRenderFrameHost(render_frame_host);
50   if (bridge)
51     bridge->UpdateNotificationIcon(notification_id, icon);
52 }
53
54 }  // namespace
55
56 static IDMap<content::DesktopNotificationDelegate> notifications_;
57
58 XWalkContentsClientBridge::XWalkContentsClientBridge(JNIEnv* env, jobject obj)
59     : java_ref_(env, obj) {
60   DCHECK(obj);
61   Java_XWalkContentsClientBridge_setNativeContentsClientBridge(
62       env, obj, reinterpret_cast<intptr_t>(this));
63 }
64
65 XWalkContentsClientBridge::~XWalkContentsClientBridge() {
66   JNIEnv* env = AttachCurrentThread();
67
68   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
69   if (obj.is_null())
70     return;
71   // Clear the weak reference from the java peer to the native object since
72   // it is possible that java object lifetime can exceed the XWalkViewContents.
73   Java_XWalkContentsClientBridge_setNativeContentsClientBridge(
74       env, obj.obj(), 0);
75 }
76
77 void XWalkContentsClientBridge::AllowCertificateError(
78     int cert_error,
79     net::X509Certificate* cert,
80     const GURL& request_url,
81     const base::Callback<void(bool)>& callback, // NOLINT
82     bool* cancel_request) {
83
84   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
85   JNIEnv* env = AttachCurrentThread();
86
87   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
88   if (obj.is_null())
89     return;
90
91   std::string der_string;
92   net::X509Certificate::GetDEREncoded(cert->os_cert_handle(), &der_string);
93   ScopedJavaLocalRef<jbyteArray> jcert = base::android::ToJavaByteArray(
94       env,
95       reinterpret_cast<const uint8*>(der_string.data()),
96       der_string.length());
97   ScopedJavaLocalRef<jstring> jurl(ConvertUTF8ToJavaString(
98       env, request_url.spec()));
99   // We need to add the callback before making the call to java side,
100   // as it may do a synchronous callback prior to returning.
101   int request_id = pending_cert_error_callbacks_.Add(
102       new CertErrorCallback(callback));
103   *cancel_request = !Java_XWalkContentsClientBridge_allowCertificateError(
104       env, obj.obj(), cert_error, jcert.obj(), jurl.obj(), request_id);
105   // if the request is cancelled, then cancel the stored callback
106   if (*cancel_request) {
107     pending_cert_error_callbacks_.Remove(request_id);
108   }
109 }
110
111 void XWalkContentsClientBridge::ProceedSslError(JNIEnv* env, jobject obj,
112                                                 jboolean proceed, jint id) {
113   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
114   CertErrorCallback* callback = pending_cert_error_callbacks_.Lookup(id);
115   if (!callback || callback->is_null()) {
116     LOG(WARNING) << "Ignoring unexpected ssl error proceed callback";
117     return;
118   }
119   callback->Run(proceed);
120   pending_cert_error_callbacks_.Remove(id);
121 }
122
123 void XWalkContentsClientBridge::RunJavaScriptDialog(
124     content::JavaScriptMessageType message_type,
125     const GURL& origin_url,
126     const base::string16& message_text,
127     const base::string16& default_prompt_text,
128     const content::JavaScriptDialogManager::DialogClosedCallback& callback) {
129   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
130   JNIEnv* env = AttachCurrentThread();
131
132   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
133   if (obj.is_null())
134     return;
135
136   int callback_id = pending_js_dialog_callbacks_.Add(
137       new content::JavaScriptDialogManager::DialogClosedCallback(callback));
138   ScopedJavaLocalRef<jstring> jurl(
139       ConvertUTF8ToJavaString(env, origin_url.spec()));
140   ScopedJavaLocalRef<jstring> jmessage(
141       ConvertUTF16ToJavaString(env, message_text));
142
143   switch (message_type) {
144     case content::JAVASCRIPT_MESSAGE_TYPE_ALERT:
145       Java_XWalkContentsClientBridge_handleJsAlert(
146           env, obj.obj(), jurl.obj(), jmessage.obj(), callback_id);
147       break;
148     case content::JAVASCRIPT_MESSAGE_TYPE_CONFIRM:
149       Java_XWalkContentsClientBridge_handleJsConfirm(
150           env, obj.obj(), jurl.obj(), jmessage.obj(), callback_id);
151       break;
152     case content::JAVASCRIPT_MESSAGE_TYPE_PROMPT: {
153       ScopedJavaLocalRef<jstring> jdefault_value(
154           ConvertUTF16ToJavaString(env, default_prompt_text));
155       Java_XWalkContentsClientBridge_handleJsPrompt(env,
156                                                     obj.obj(),
157                                                     jurl.obj(),
158                                                     jmessage.obj(),
159                                                     jdefault_value.obj(),
160                                                     callback_id);
161       break;
162     }
163     default:
164        NOTREACHED();
165   }
166 }
167
168 void XWalkContentsClientBridge::RunBeforeUnloadDialog(
169     const GURL& origin_url,
170     const base::string16& message_text,
171     const content::JavaScriptDialogManager::DialogClosedCallback& callback) {
172   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
173   JNIEnv* env = AttachCurrentThread();
174
175   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
176   if (obj.is_null())
177     return;
178
179   int callback_id = pending_js_dialog_callbacks_.Add(
180       new content::JavaScriptDialogManager::DialogClosedCallback(callback));
181   ScopedJavaLocalRef<jstring> jurl(
182       ConvertUTF8ToJavaString(env, origin_url.spec()));
183   ScopedJavaLocalRef<jstring> jmessage(
184       ConvertUTF16ToJavaString(env, message_text));
185
186   Java_XWalkContentsClientBridge_handleJsBeforeUnload(
187       env, obj.obj(), jurl.obj(), jmessage.obj(), callback_id);
188 }
189
190 bool XWalkContentsClientBridge::OnReceivedHttpAuthRequest(
191     const JavaRef<jobject>& handler,
192     const std::string& host,
193     const std::string& realm) {
194   JNIEnv* env = AttachCurrentThread();
195   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
196   if (obj.is_null())
197     return false;
198
199   ScopedJavaLocalRef<jstring> jhost = ConvertUTF8ToJavaString(env, host);
200   ScopedJavaLocalRef<jstring> jrealm = ConvertUTF8ToJavaString(env, realm);
201   Java_XWalkContentsClientBridge_onReceivedHttpAuthRequest(
202       env, obj.obj(), handler.obj(), jhost.obj(), jrealm.obj());
203   return true;
204 }
205
206 void XWalkContentsClientBridge::OnNotificationIconDownloaded(
207     int id,
208     int http_status_code,
209     const GURL& icon_url,
210     const std::vector<SkBitmap>& bitmaps,
211     const std::vector<gfx::Size>& original_bitmap_sizes) {
212   if (bitmaps.empty() && http_status_code == 404) {
213     LOG(WARNING) << "Failed to download notification icon from "
214                  << icon_url.spec();
215   } else {
216     NotificationDownloadRequestIdMap::iterator iter =
217         downloading_icon_notifications_.find(id);
218     if (iter == downloading_icon_notifications_.end())
219       return;
220
221     int notification_id = iter->second.first;
222     content::RenderFrameHost* render_frame_host = iter->second.second;
223     // This will lead to a second call of ShowNotification for the
224     // same notification id to update the icon. On Android, when
225     // the notification which is already shown is fired again, it will
226     // silently update the content only.
227     BrowserThread::PostTask(
228         BrowserThread::UI,
229         FROM_HERE,
230         base::Bind(&RunUpdateNotificationIconOnUIThread,
231                    notification_id,
232                    render_frame_host,
233                    bitmaps[0]));
234   }
235   downloading_icon_notifications_.erase(id);
236 }
237
238 void XWalkContentsClientBridge::UpdateNotificationIcon(
239     int notification_id, const SkBitmap& icon) {
240   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
241   JNIEnv* env = AttachCurrentThread();
242
243   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
244   if (obj.is_null())
245     return;
246
247   ScopedJavaLocalRef<jobject> jicon = gfx::ConvertToJavaBitmap(&icon);
248   Java_XWalkContentsClientBridge_updateNotificationIcon(
249       env, obj.obj(), notification_id, jicon.obj());
250 }
251
252 static void CancelNotification(
253     JavaObjectWeakGlobalRef java_ref,
254     int notification_id, content::DesktopNotificationDelegate* delegate) {
255   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
256   JNIEnv* env = AttachCurrentThread();
257   ScopedJavaLocalRef<jobject> obj = java_ref.get(env);
258   if (obj.is_null())
259     return;
260
261   Java_XWalkContentsClientBridge_cancelNotification(
262       env, obj.obj(), notification_id, reinterpret_cast<intptr_t>(delegate));
263 }
264
265 void XWalkContentsClientBridge::ShowNotification(
266     const content::ShowDesktopNotificationHostMsgParams& params,
267     content::RenderFrameHost* render_frame_host,
268     content::DesktopNotificationDelegate* delegate,
269     base::Closure* cancel_callback) {
270   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
271   JNIEnv* env = AttachCurrentThread();
272
273   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
274   if (obj.is_null())
275     return;
276
277   ScopedJavaLocalRef<jstring> jtitle(
278     ConvertUTF16ToJavaString(env, params.title));
279   ScopedJavaLocalRef<jstring> jbody(
280     ConvertUTF16ToJavaString(env, params.body));
281   ScopedJavaLocalRef<jstring> jreplace_id(
282     ConvertUTF16ToJavaString(env, params.replace_id));
283
284   int notification_id = notifications_.Add(delegate);
285   Java_XWalkContentsClientBridge_showNotification(
286       env, obj.obj(), jtitle.obj(), jbody.obj(),
287       jreplace_id.obj(), notification_id,
288       reinterpret_cast<intptr_t>(delegate));
289
290   if (cancel_callback)
291     *cancel_callback =
292         base::Bind(&CancelNotification, java_ref_, notification_id, delegate);
293
294   if (params.icon_url.is_valid()) {
295     WebContents* web_contents =
296       WebContents::FromRenderFrameHost(render_frame_host);
297     if (web_contents) {
298       int download_request_id = web_contents->DownloadImage(
299           params.icon_url,
300           false,
301           0,
302           base::Bind(
303               &XWalkContentsClientBridge::OnNotificationIconDownloaded,
304               base::Unretained(this)));
305       NotificationDownloadRequestInfos info =
306           std::make_pair(notification_id, render_frame_host);
307       downloading_icon_notifications_[download_request_id] = info;
308     }
309   }
310 }
311
312 void XWalkContentsClientBridge::OnWebLayoutPageScaleFactorChanged(
313     float page_scale_factor) {
314   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
315   JNIEnv* env = AttachCurrentThread();
316   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
317   if (obj.is_null())
318     return;
319   Java_XWalkContentsClientBridge_onWebLayoutPageScaleFactorChanged(
320       env, obj.obj(), page_scale_factor);
321 }
322
323 void XWalkContentsClientBridge::ConfirmJsResult(JNIEnv* env,
324                                                 jobject,
325                                                 int id,
326                                                 jstring prompt) {
327   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
328   content::JavaScriptDialogManager::DialogClosedCallback* callback =
329       pending_js_dialog_callbacks_.Lookup(id);
330   base::string16 prompt_text;
331   if (prompt) {
332     prompt_text = ConvertJavaStringToUTF16(env, prompt);
333   }
334   if (callback)
335     callback->Run(true, prompt_text);
336   pending_js_dialog_callbacks_.Remove(id);
337 }
338
339 void XWalkContentsClientBridge::CancelJsResult(JNIEnv*, jobject, int id) {
340   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
341   content::JavaScriptDialogManager::DialogClosedCallback* callback =
342       pending_js_dialog_callbacks_.Lookup(id);
343   if (callback)
344     callback->Run(false, base::string16());
345   pending_js_dialog_callbacks_.Remove(id);
346 }
347
348 void XWalkContentsClientBridge::ExitFullscreen(
349     JNIEnv*, jobject, jlong j_web_contents) {
350   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
351   WebContents* web_contents = reinterpret_cast<WebContents*>(j_web_contents);
352   if (web_contents) {
353     RenderViewHost* rvh = web_contents->GetRenderViewHost();
354     if (rvh)
355       rvh->ExitFullscreen();
356   }
357 }
358
359 void XWalkContentsClientBridge::NotificationDisplayed(
360     JNIEnv*, jobject, jlong delegate) {
361   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
362   content::DesktopNotificationDelegate* notification_delegate =
363     reinterpret_cast<content::DesktopNotificationDelegate*> (delegate);
364   notification_delegate->NotificationDisplayed();
365 }
366
367 void XWalkContentsClientBridge::NotificationError(
368     JNIEnv* env, jobject, jlong delegate) {
369   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
370   content::DesktopNotificationDelegate* notification_delegate =
371     reinterpret_cast<content::DesktopNotificationDelegate*> (delegate);
372   notification_delegate->NotificationError();
373 }
374
375 void XWalkContentsClientBridge::NotificationClicked(
376     JNIEnv*, jobject, jint id, jlong delegate) {
377   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
378   notifications_.Remove(id);
379   content::DesktopNotificationDelegate* notification_delegate =
380     reinterpret_cast<content::DesktopNotificationDelegate*> (delegate);
381   notification_delegate->NotificationClick();
382 }
383
384 void XWalkContentsClientBridge::NotificationClosed(
385     JNIEnv*, jobject, jint id, bool by_user, jlong delegate) {
386   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
387   notifications_.Remove(id);
388   content::DesktopNotificationDelegate* notification_delegate =
389     reinterpret_cast<content::DesktopNotificationDelegate*> (delegate);
390   notification_delegate->NotificationClosed(by_user);
391 }
392
393 void XWalkContentsClientBridge::OnFilesSelected(
394     JNIEnv* env, jobject, int process_id, int render_id,
395     int mode, jstring filepath, jstring display_name) {
396   content::RenderViewHost* rvh =
397       content::RenderViewHost::FromID(process_id, render_id);
398   if (!rvh)
399     return;
400
401   std::string path = base::android::ConvertJavaStringToUTF8(env, filepath);
402   std::string file_name =
403       base::android::ConvertJavaStringToUTF8(env, display_name);
404   base::FilePath file_path = base::FilePath(path);
405   ui::SelectedFileInfo file_info;
406   file_info.file_path = file_path;
407   file_info.local_path = file_path;
408   if (!file_name.empty())
409     file_info.display_name = file_name;
410   std::vector<ui::SelectedFileInfo> files;
411   files.push_back(file_info);
412
413   rvh->FilesSelectedInChooser(
414       files, static_cast<content::FileChooserParams::Mode>(mode));
415 }
416
417 void XWalkContentsClientBridge::OnFilesNotSelected(
418     JNIEnv* env, jobject, int process_id, int render_id, int mode) {
419   content::RenderViewHost* rvh =
420       content::RenderViewHost::FromID(process_id, render_id);
421   if (!rvh)
422     return;
423
424   std::vector<ui::SelectedFileInfo> files;
425
426   rvh->FilesSelectedInChooser(
427       files, static_cast<content::FileChooserParams::Mode>(mode));
428 }
429
430 bool RegisterXWalkContentsClientBridge(JNIEnv* env) {
431   return RegisterNativesImpl(env) >= 0;
432 }
433
434 }  // namespace xwalk