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