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 "content/public/browser/browser_thread.h"
14 #include "content/public/browser/render_view_host.h"
15 #include "content/public/browser/web_contents.h"
16 #include "content/public/common/file_chooser_params.h"
17 #include "content/public/common/show_desktop_notification_params.h"
18 #include "jni/XWalkContentsClientBridge_jni.h"
19 #include "net/cert/x509_certificate.h"
20 #include "third_party/skia/include/core/SkBitmap.h"
22 #include "ui/gfx/android/java_bitmap.h"
23 #include "ui/shell_dialogs/selected_file_info.h"
25 using base::android::AttachCurrentThread;
26 using base::android::ConvertJavaStringToUTF16;
27 using base::android::ConvertUTF8ToJavaString;
28 using base::android::ConvertUTF16ToJavaString;
29 using base::android::JavaRef;
30 using base::android::ScopedJavaLocalRef;
31 using content::BrowserThread;
32 using content::FileChooserParams;
33 using content::RenderViewHost;
34 using content::WebContents;
40 void RunUpdateNotificationIconOnUIThread(
44 const SkBitmap& icon) {
45 XWalkContentsClientBridgeBase* bridge =
46 XWalkContentsClientBridgeBase::FromRenderViewID(process_id, route_id);
48 bridge->UpdateNotificationIcon(notification_id, icon);
53 XWalkContentsClientBridge::XWalkContentsClientBridge(JNIEnv* env, jobject obj)
54 : java_ref_(env, obj) {
56 Java_XWalkContentsClientBridge_setNativeContentsClientBridge(
57 env, obj, reinterpret_cast<intptr_t>(this));
60 XWalkContentsClientBridge::~XWalkContentsClientBridge() {
61 JNIEnv* env = AttachCurrentThread();
63 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
66 // Clear the weak reference from the java peer to the native object since
67 // it is possible that java object lifetime can exceed the XWalkViewContents.
68 Java_XWalkContentsClientBridge_setNativeContentsClientBridge(
72 void XWalkContentsClientBridge::AllowCertificateError(
74 net::X509Certificate* cert,
75 const GURL& request_url,
76 const base::Callback<void(bool)>& callback, // NOLINT
77 bool* cancel_request) {
79 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
80 JNIEnv* env = AttachCurrentThread();
82 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
86 std::string der_string;
87 net::X509Certificate::GetDEREncoded(cert->os_cert_handle(), &der_string);
88 ScopedJavaLocalRef<jbyteArray> jcert = base::android::ToJavaByteArray(
90 reinterpret_cast<const uint8*>(der_string.data()),
92 ScopedJavaLocalRef<jstring> jurl(ConvertUTF8ToJavaString(
93 env, request_url.spec()));
94 // We need to add the callback before making the call to java side,
95 // as it may do a synchronous callback prior to returning.
96 int request_id = pending_cert_error_callbacks_.Add(
97 new CertErrorCallback(callback));
98 *cancel_request = !Java_XWalkContentsClientBridge_allowCertificateError(
99 env, obj.obj(), cert_error, jcert.obj(), jurl.obj(), request_id);
100 // if the request is cancelled, then cancel the stored callback
101 if (*cancel_request) {
102 pending_cert_error_callbacks_.Remove(request_id);
106 void XWalkContentsClientBridge::ProceedSslError(JNIEnv* env, jobject obj,
107 jboolean proceed, jint id) {
108 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
109 CertErrorCallback* callback = pending_cert_error_callbacks_.Lookup(id);
110 if (!callback || callback->is_null()) {
111 LOG(WARNING) << "Ignoring unexpected ssl error proceed callback";
114 callback->Run(proceed);
115 pending_cert_error_callbacks_.Remove(id);
118 void XWalkContentsClientBridge::RunJavaScriptDialog(
119 content::JavaScriptMessageType message_type,
120 const GURL& origin_url,
121 const base::string16& message_text,
122 const base::string16& default_prompt_text,
123 const content::JavaScriptDialogManager::DialogClosedCallback& callback) {
124 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
125 JNIEnv* env = AttachCurrentThread();
127 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
131 int callback_id = pending_js_dialog_callbacks_.Add(
132 new content::JavaScriptDialogManager::DialogClosedCallback(callback));
133 ScopedJavaLocalRef<jstring> jurl(
134 ConvertUTF8ToJavaString(env, origin_url.spec()));
135 ScopedJavaLocalRef<jstring> jmessage(
136 ConvertUTF16ToJavaString(env, message_text));
138 switch (message_type) {
139 case content::JAVASCRIPT_MESSAGE_TYPE_ALERT:
140 Java_XWalkContentsClientBridge_handleJsAlert(
141 env, obj.obj(), jurl.obj(), jmessage.obj(), callback_id);
143 case content::JAVASCRIPT_MESSAGE_TYPE_CONFIRM:
144 Java_XWalkContentsClientBridge_handleJsConfirm(
145 env, obj.obj(), jurl.obj(), jmessage.obj(), callback_id);
147 case content::JAVASCRIPT_MESSAGE_TYPE_PROMPT: {
148 ScopedJavaLocalRef<jstring> jdefault_value(
149 ConvertUTF16ToJavaString(env, default_prompt_text));
150 Java_XWalkContentsClientBridge_handleJsPrompt(env,
154 jdefault_value.obj(),
163 void XWalkContentsClientBridge::RunBeforeUnloadDialog(
164 const GURL& origin_url,
165 const base::string16& message_text,
166 const content::JavaScriptDialogManager::DialogClosedCallback& callback) {
167 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
168 JNIEnv* env = AttachCurrentThread();
170 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
174 int callback_id = pending_js_dialog_callbacks_.Add(
175 new content::JavaScriptDialogManager::DialogClosedCallback(callback));
176 ScopedJavaLocalRef<jstring> jurl(
177 ConvertUTF8ToJavaString(env, origin_url.spec()));
178 ScopedJavaLocalRef<jstring> jmessage(
179 ConvertUTF16ToJavaString(env, message_text));
181 Java_XWalkContentsClientBridge_handleJsBeforeUnload(
182 env, obj.obj(), jurl.obj(), jmessage.obj(), callback_id);
185 bool XWalkContentsClientBridge::OnReceivedHttpAuthRequest(
186 const JavaRef<jobject>& handler,
187 const std::string& host,
188 const std::string& realm) {
189 JNIEnv* env = AttachCurrentThread();
190 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
194 ScopedJavaLocalRef<jstring> jhost = ConvertUTF8ToJavaString(env, host);
195 ScopedJavaLocalRef<jstring> jrealm = ConvertUTF8ToJavaString(env, realm);
196 Java_XWalkContentsClientBridge_onReceivedHttpAuthRequest(
197 env, obj.obj(), handler.obj(), jhost.obj(), jrealm.obj());
201 void XWalkContentsClientBridge::OnNotificationIconDownloaded(
203 int http_status_code,
204 const GURL& icon_url,
205 const std::vector<SkBitmap>& bitmaps,
206 const std::vector<gfx::Size>& original_bitmap_sizes) {
207 if (bitmaps.empty() && http_status_code == 404) {
208 LOG(WARNING) << "Failed to download notification icon from "
211 NotificationDownloadRequestIdMap::iterator iter =
212 downloading_icon_notifications_.find(id);
213 if (iter == downloading_icon_notifications_.end() ||
214 iter->second.size() != 3) {
217 int notification_id = iter->second[0];
218 int process_id = iter->second[1];
219 int route_id = iter->second[2];
220 // This will lead to a second call of ShowNotification for the
221 // same notification id to update the icon. On Android, when
222 // the notification which is already shown is fired again, it will
223 // silently update the content only.
224 BrowserThread::PostTask(
227 base::Bind(&RunUpdateNotificationIconOnUIThread,
233 downloading_icon_notifications_.erase(id);
236 void XWalkContentsClientBridge::UpdateNotificationIcon(
237 int notification_id, const SkBitmap& icon) {
238 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
239 JNIEnv* env = AttachCurrentThread();
241 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
245 ScopedJavaLocalRef<jobject> jicon = gfx::ConvertToJavaBitmap(&icon);
246 Java_XWalkContentsClientBridge_updateNotificationIcon(
247 env, obj.obj(), notification_id, jicon.obj());
250 void XWalkContentsClientBridge::ShowNotification(
251 const content::ShowDesktopNotificationHostMsgParams& params,
255 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
256 JNIEnv* env = AttachCurrentThread();
258 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
262 ScopedJavaLocalRef<jstring> jtitle(
263 ConvertUTF16ToJavaString(env, params.title));
264 ScopedJavaLocalRef<jstring> jbody(
265 ConvertUTF16ToJavaString(env, params.body));
266 ScopedJavaLocalRef<jstring> jreplace_id(
267 ConvertUTF16ToJavaString(env, params.replace_id));
269 Java_XWalkContentsClientBridge_showNotification(
270 env, obj.obj(), jtitle.obj(), jbody.obj(),
271 jreplace_id.obj(), params.notification_id,
272 process_id, route_id);
274 if (params.icon_url.is_valid()) {
275 RenderViewHost* rvh = RenderViewHost::FromID(process_id, route_id);
277 WebContents* web_contents = WebContents::FromRenderViewHost(rvh);
279 int download_request_id = web_contents->DownloadImage(
284 &XWalkContentsClientBridge::OnNotificationIconDownloaded,
285 base::Unretained(this)));
286 std::vector<int> ids;
287 ids.push_back(params.notification_id);
288 ids.push_back(process_id);
289 ids.push_back(route_id);
290 downloading_icon_notifications_[download_request_id] = ids;
296 void XWalkContentsClientBridge::CancelNotification(
300 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
301 JNIEnv* env = AttachCurrentThread();
303 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
307 Java_XWalkContentsClientBridge_cancelNotification(
308 env, obj.obj(), notification_id,
309 process_id, route_id);
312 void XWalkContentsClientBridge::ConfirmJsResult(JNIEnv* env,
316 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
317 content::JavaScriptDialogManager::DialogClosedCallback* callback =
318 pending_js_dialog_callbacks_.Lookup(id);
319 base::string16 prompt_text;
321 prompt_text = ConvertJavaStringToUTF16(env, prompt);
324 callback->Run(true, prompt_text);
325 pending_js_dialog_callbacks_.Remove(id);
328 void XWalkContentsClientBridge::CancelJsResult(JNIEnv*, jobject, int id) {
329 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
330 content::JavaScriptDialogManager::DialogClosedCallback* callback =
331 pending_js_dialog_callbacks_.Lookup(id);
333 callback->Run(false, base::string16());
334 pending_js_dialog_callbacks_.Remove(id);
337 void XWalkContentsClientBridge::ExitFullscreen(
338 JNIEnv*, jobject, jlong j_web_contents) {
339 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
340 WebContents* web_contents = reinterpret_cast<WebContents*>(j_web_contents);
342 RenderViewHost* rvh = web_contents->GetRenderViewHost();
344 rvh->ExitFullscreen();
348 void XWalkContentsClientBridge::NotificationDisplayed(
349 JNIEnv*, jobject, int id, int process_id, int route_id) {
350 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
351 RenderViewHost* rvh = RenderViewHost::FromID(
352 process_id, route_id);
355 rvh->DesktopNotificationPostDisplay(id);
358 void XWalkContentsClientBridge::NotificationError(
359 JNIEnv* env, jobject, int id, jstring error,
360 int process_id, int route_id) {
361 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
362 RenderViewHost* rvh = RenderViewHost::FromID(
363 process_id, route_id);
366 base::string16 error_text;
368 error_text = ConvertJavaStringToUTF16(env, error);
369 rvh->DesktopNotificationPostError(id, error_text);
372 void XWalkContentsClientBridge::NotificationClicked(
373 JNIEnv*, jobject, int id, int process_id, int route_id) {
374 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
375 RenderViewHost* rvh = RenderViewHost::FromID(
376 process_id, route_id);
379 rvh->DesktopNotificationPostClick(id);
382 void XWalkContentsClientBridge::NotificationClosed(
383 JNIEnv*, jobject, int id, bool by_user,
384 int process_id, int route_id) {
385 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
386 RenderViewHost* rvh = RenderViewHost::FromID(
387 process_id, route_id);
390 rvh->DesktopNotificationPostClose(id, 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;