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