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/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"
26 #include "ui/gfx/android/java_bitmap.h"
27 #include "ui/shell_dialogs/selected_file_info.h"
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;
44 void RunUpdateNotificationIconOnUIThread(
46 content::RenderFrameHost* render_frame_host,
47 const SkBitmap& icon) {
48 XWalkContentsClientBridgeBase* bridge =
49 XWalkContentsClientBridgeBase::FromRenderFrameHost(render_frame_host);
51 bridge->UpdateNotificationIcon(notification_id, icon);
56 static IDMap<content::DesktopNotificationDelegate> notifications_;
58 XWalkContentsClientBridge::XWalkContentsClientBridge(JNIEnv* env, jobject obj)
59 : java_ref_(env, obj) {
61 Java_XWalkContentsClientBridge_setNativeContentsClientBridge(
62 env, obj, reinterpret_cast<intptr_t>(this));
65 XWalkContentsClientBridge::~XWalkContentsClientBridge() {
66 JNIEnv* env = AttachCurrentThread();
68 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
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(
77 void XWalkContentsClientBridge::AllowCertificateError(
79 net::X509Certificate* cert,
80 const GURL& request_url,
81 const base::Callback<void(bool)>& callback, // NOLINT
82 bool* cancel_request) {
84 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
85 JNIEnv* env = AttachCurrentThread();
87 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
91 std::string der_string;
92 net::X509Certificate::GetDEREncoded(cert->os_cert_handle(), &der_string);
93 ScopedJavaLocalRef<jbyteArray> jcert = base::android::ToJavaByteArray(
95 reinterpret_cast<const uint8*>(der_string.data()),
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);
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";
119 callback->Run(proceed);
120 pending_cert_error_callbacks_.Remove(id);
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();
132 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
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));
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);
148 case content::JAVASCRIPT_MESSAGE_TYPE_CONFIRM:
149 Java_XWalkContentsClientBridge_handleJsConfirm(
150 env, obj.obj(), jurl.obj(), jmessage.obj(), callback_id);
152 case content::JAVASCRIPT_MESSAGE_TYPE_PROMPT: {
153 ScopedJavaLocalRef<jstring> jdefault_value(
154 ConvertUTF16ToJavaString(env, default_prompt_text));
155 Java_XWalkContentsClientBridge_handleJsPrompt(env,
159 jdefault_value.obj(),
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();
175 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
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));
186 Java_XWalkContentsClientBridge_handleJsBeforeUnload(
187 env, obj.obj(), jurl.obj(), jmessage.obj(), callback_id);
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);
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());
206 void XWalkContentsClientBridge::OnNotificationIconDownloaded(
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 "
216 NotificationDownloadRequestIdMap::iterator iter =
217 downloading_icon_notifications_.find(id);
218 if (iter == downloading_icon_notifications_.end())
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(
230 base::Bind(&RunUpdateNotificationIconOnUIThread,
235 downloading_icon_notifications_.erase(id);
238 void XWalkContentsClientBridge::UpdateNotificationIcon(
239 int notification_id, const SkBitmap& icon) {
240 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
241 JNIEnv* env = AttachCurrentThread();
243 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
247 ScopedJavaLocalRef<jobject> jicon = gfx::ConvertToJavaBitmap(&icon);
248 Java_XWalkContentsClientBridge_updateNotificationIcon(
249 env, obj.obj(), notification_id, jicon.obj());
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);
261 Java_XWalkContentsClientBridge_cancelNotification(
262 env, obj.obj(), notification_id, reinterpret_cast<intptr_t>(delegate));
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();
273 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
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));
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));
292 base::Bind(&CancelNotification, java_ref_, notification_id, delegate);
294 if (params.icon_url.is_valid()) {
295 WebContents* web_contents =
296 WebContents::FromRenderFrameHost(render_frame_host);
298 int download_request_id = web_contents->DownloadImage(
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;
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);
319 Java_XWalkContentsClientBridge_onWebLayoutPageScaleFactorChanged(
320 env, obj.obj(), page_scale_factor);
323 void XWalkContentsClientBridge::ConfirmJsResult(JNIEnv* env,
327 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
328 content::JavaScriptDialogManager::DialogClosedCallback* callback =
329 pending_js_dialog_callbacks_.Lookup(id);
330 base::string16 prompt_text;
332 prompt_text = ConvertJavaStringToUTF16(env, prompt);
335 callback->Run(true, prompt_text);
336 pending_js_dialog_callbacks_.Remove(id);
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);
344 callback->Run(false, base::string16());
345 pending_js_dialog_callbacks_.Remove(id);
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);
353 RenderViewHost* rvh = web_contents->GetRenderViewHost();
355 rvh->ExitFullscreen();
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();
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();
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();
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);
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);
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);
413 rvh->FilesSelectedInChooser(
414 files, static_cast<content::FileChooserParams::Mode>(mode));
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);
424 std::vector<ui::SelectedFileInfo> files;
426 rvh->FilesSelectedInChooser(
427 files, static_cast<content::FileChooserParams::Mode>(mode));
430 bool RegisterXWalkContentsClientBridge(JNIEnv* env) {
431 return RegisterNativesImpl(env) >= 0;