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.
5 #include "xwalk/runtime/browser/android/xwalk_contents_client_bridge.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.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"
27 #include "ui/gfx/android/java_bitmap.h"
28 #include "ui/shell_dialogs/selected_file_info.h"
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;
46 void RunUpdateNotificationIconOnUIThread(
48 content::RenderFrameHost* render_frame_host,
49 const SkBitmap& icon) {
50 XWalkContentsClientBridgeBase* bridge =
51 XWalkContentsClientBridgeBase::FromRenderFrameHost(render_frame_host);
53 bridge->UpdateNotificationIcon(notification_id, icon);
56 int g_next_notification_id_ = 1;
58 ScopedPtrHashMap<int, content::DesktopNotificationDelegate> g_notification_map_;
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)) {
69 Java_XWalkContentsClientBridge_setNativeContentsClientBridge(
70 env, obj, reinterpret_cast<intptr_t>(this));
71 icon_helper_->SetListener(this);
74 XWalkContentsClientBridge::~XWalkContentsClientBridge() {
75 JNIEnv* env = AttachCurrentThread();
77 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
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(
86 void XWalkContentsClientBridge::AllowCertificateError(
88 net::X509Certificate* cert,
89 const GURL& request_url,
90 const base::Callback<void(bool)>& callback, // NOLINT
91 bool* cancel_request) {
93 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
94 JNIEnv* env = AttachCurrentThread();
96 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
100 std::string der_string;
101 net::X509Certificate::GetDEREncoded(cert->os_cert_handle(), &der_string);
102 ScopedJavaLocalRef<jbyteArray> jcert = base::android::ToJavaByteArray(
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);
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";
128 callback->Run(proceed);
129 pending_cert_error_callbacks_.Remove(id);
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();
141 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
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));
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);
157 case content::JAVASCRIPT_MESSAGE_TYPE_CONFIRM:
158 Java_XWalkContentsClientBridge_handleJsConfirm(
159 env, obj.obj(), jurl.obj(), jmessage.obj(), callback_id);
161 case content::JAVASCRIPT_MESSAGE_TYPE_PROMPT: {
162 ScopedJavaLocalRef<jstring> jdefault_value(
163 ConvertUTF16ToJavaString(env, default_prompt_text));
164 Java_XWalkContentsClientBridge_handleJsPrompt(env,
168 jdefault_value.obj(),
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();
184 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
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));
195 Java_XWalkContentsClientBridge_handleJsBeforeUnload(
196 env, obj.obj(), jurl.obj(), jmessage.obj(), callback_id);
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);
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());
215 void XWalkContentsClientBridge::OnNotificationIconDownloaded(
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 "
225 NotificationDownloadRequestIdMap::iterator iter =
226 downloading_icon_notifications_.find(id);
227 if (iter == downloading_icon_notifications_.end())
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(
239 base::Bind(&RunUpdateNotificationIconOnUIThread,
244 downloading_icon_notifications_.erase(id);
247 void XWalkContentsClientBridge::UpdateNotificationIcon(
248 int notification_id, const SkBitmap& icon) {
249 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
250 JNIEnv* env = AttachCurrentThread();
252 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
256 ScopedJavaLocalRef<jobject> jicon = gfx::ConvertToJavaBitmap(&icon);
257 Java_XWalkContentsClientBridge_updateNotificationIcon(
258 env, obj.obj(), notification_id, jicon.obj());
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);
269 Java_XWalkContentsClientBridge_cancelNotification(
270 env, obj.obj(), notification_id);
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();
281 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
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));
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);
299 *cancel_callback = base::Bind(
300 &CancelNotification, java_ref_, notification_id);
302 if (params.icon_url.is_valid()) {
303 WebContents* web_contents =
304 WebContents::FromRenderFrameHost(render_frame_host);
306 int download_request_id = web_contents->DownloadImage(
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;
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);
327 Java_XWalkContentsClientBridge_onWebLayoutPageScaleFactorChanged(
328 env, obj.obj(), page_scale_factor);
331 void XWalkContentsClientBridge::ConfirmJsResult(JNIEnv* env,
335 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
336 content::JavaScriptDialogManager::DialogClosedCallback* callback =
337 pending_js_dialog_callbacks_.Lookup(id);
338 base::string16 prompt_text;
340 prompt_text = ConvertJavaStringToUTF16(env, prompt);
343 callback->Run(true, prompt_text);
344 pending_js_dialog_callbacks_.Remove(id);
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);
352 callback->Run(false, base::string16());
353 pending_js_dialog_callbacks_.Remove(id);
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);
361 RenderViewHost* rvh = web_contents->GetRenderViewHost();
363 rvh->ExitFullscreen();
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();
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();
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();
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);
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);
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);
423 rvh->FilesSelectedInChooser(
424 files, static_cast<content::FileChooserParams::Mode>(mode));
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);
434 std::vector<ui::SelectedFileInfo> files;
436 rvh->FilesSelectedInChooser(
437 files, static_cast<content::FileChooserParams::Mode>(mode));
440 void XWalkContentsClientBridge::DownloadIcon(JNIEnv* env,
443 std::string url_str = base::android::ConvertJavaStringToUTF8(env, url);
444 icon_helper_->DownloadIcon(GURL(url_str));
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);
452 ScopedJavaLocalRef<jstring> jurl(
453 ConvertUTF8ToJavaString(env, icon_url.spec()));
455 Java_XWalkContentsClientBridge_onIconAvailable(env, obj.obj(), jurl.obj());
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);
464 ScopedJavaLocalRef<jstring> jurl(
465 ConvertUTF8ToJavaString(env, icon_url.spec()));
466 ScopedJavaLocalRef<jobject> jicon = gfx::ConvertToJavaBitmap(&bitmap);
468 Java_XWalkContentsClientBridge_onReceivedIcon(
469 env, obj.obj(), jurl.obj(), jicon.obj());
472 bool RegisterXWalkContentsClientBridge(JNIEnv* env) {
473 return RegisterNativesImpl(env);