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(JNIEnv* env, jobject obj)
64 : java_ref_(env, obj) {
66 Java_XWalkContentsClientBridge_setNativeContentsClientBridge(
67 env, obj, reinterpret_cast<intptr_t>(this));
70 XWalkContentsClientBridge::~XWalkContentsClientBridge() {
71 JNIEnv* env = AttachCurrentThread();
73 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
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(
82 void XWalkContentsClientBridge::AllowCertificateError(
84 net::X509Certificate* cert,
85 const GURL& request_url,
86 const base::Callback<void(bool)>& callback, // NOLINT
87 bool* cancel_request) {
89 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
90 JNIEnv* env = AttachCurrentThread();
92 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
96 std::string der_string;
97 net::X509Certificate::GetDEREncoded(cert->os_cert_handle(), &der_string);
98 ScopedJavaLocalRef<jbyteArray> jcert = base::android::ToJavaByteArray(
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);
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";
124 callback->Run(proceed);
125 pending_cert_error_callbacks_.Remove(id);
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();
137 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
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));
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);
153 case content::JAVASCRIPT_MESSAGE_TYPE_CONFIRM:
154 Java_XWalkContentsClientBridge_handleJsConfirm(
155 env, obj.obj(), jurl.obj(), jmessage.obj(), callback_id);
157 case content::JAVASCRIPT_MESSAGE_TYPE_PROMPT: {
158 ScopedJavaLocalRef<jstring> jdefault_value(
159 ConvertUTF16ToJavaString(env, default_prompt_text));
160 Java_XWalkContentsClientBridge_handleJsPrompt(env,
164 jdefault_value.obj(),
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();
180 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
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));
191 Java_XWalkContentsClientBridge_handleJsBeforeUnload(
192 env, obj.obj(), jurl.obj(), jmessage.obj(), callback_id);
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);
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());
211 void XWalkContentsClientBridge::OnNotificationIconDownloaded(
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 "
221 NotificationDownloadRequestIdMap::iterator iter =
222 downloading_icon_notifications_.find(id);
223 if (iter == downloading_icon_notifications_.end())
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(
235 base::Bind(&RunUpdateNotificationIconOnUIThread,
240 downloading_icon_notifications_.erase(id);
243 void XWalkContentsClientBridge::UpdateNotificationIcon(
244 int notification_id, const SkBitmap& icon) {
245 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
246 JNIEnv* env = AttachCurrentThread();
248 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
252 ScopedJavaLocalRef<jobject> jicon = gfx::ConvertToJavaBitmap(&icon);
253 Java_XWalkContentsClientBridge_updateNotificationIcon(
254 env, obj.obj(), notification_id, jicon.obj());
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);
265 Java_XWalkContentsClientBridge_cancelNotification(
266 env, obj.obj(), notification_id);
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();
277 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
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));
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);
295 *cancel_callback = base::Bind(
296 &CancelNotification, java_ref_, notification_id);
298 if (params.icon_url.is_valid()) {
299 WebContents* web_contents =
300 WebContents::FromRenderFrameHost(render_frame_host);
302 int download_request_id = web_contents->DownloadImage(
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;
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);
323 Java_XWalkContentsClientBridge_onWebLayoutPageScaleFactorChanged(
324 env, obj.obj(), page_scale_factor);
327 void XWalkContentsClientBridge::ConfirmJsResult(JNIEnv* env,
331 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
332 content::JavaScriptDialogManager::DialogClosedCallback* callback =
333 pending_js_dialog_callbacks_.Lookup(id);
334 base::string16 prompt_text;
336 prompt_text = ConvertJavaStringToUTF16(env, prompt);
339 callback->Run(true, prompt_text);
340 pending_js_dialog_callbacks_.Remove(id);
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);
348 callback->Run(false, base::string16());
349 pending_js_dialog_callbacks_.Remove(id);
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);
357 RenderViewHost* rvh = web_contents->GetRenderViewHost();
359 rvh->ExitFullscreen();
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();
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();
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();
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);
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);
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);
419 rvh->FilesSelectedInChooser(
420 files, static_cast<content::FileChooserParams::Mode>(mode));
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);
430 std::vector<ui::SelectedFileInfo> files;
432 rvh->FilesSelectedInChooser(
433 files, static_cast<content::FileChooserParams::Mode>(mode));
436 bool RegisterXWalkContentsClientBridge(JNIEnv* env) {
437 return RegisterNativesImpl(env) >= 0;