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/show_desktop_notification_params.h"
17 #include "jni/XWalkContentsClientBridge_jni.h"
18 #include "net/cert/x509_certificate.h"
19 #include "third_party/skia/include/core/SkBitmap.h"
21 #include "ui/gfx/android/java_bitmap.h"
23 using base::android::AttachCurrentThread;
24 using base::android::ConvertJavaStringToUTF16;
25 using base::android::ConvertUTF8ToJavaString;
26 using base::android::ConvertUTF16ToJavaString;
27 using base::android::JavaRef;
28 using base::android::ScopedJavaLocalRef;
29 using content::BrowserThread;
30 using content::RenderViewHost;
31 using content::WebContents;
37 void RunUpdateNotificationIconOnUIThread(
41 const SkBitmap& icon) {
42 XWalkContentsClientBridgeBase* bridge =
43 XWalkContentsClientBridgeBase::FromRenderViewID(process_id, route_id);
45 bridge->UpdateNotificationIcon(notification_id, icon);
50 XWalkContentsClientBridge::XWalkContentsClientBridge(JNIEnv* env, jobject obj)
51 : java_ref_(env, obj) {
53 Java_XWalkContentsClientBridge_setNativeContentsClientBridge(
54 env, obj, reinterpret_cast<jint>(this));
57 XWalkContentsClientBridge::~XWalkContentsClientBridge() {
58 JNIEnv* env = AttachCurrentThread();
60 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
63 // Clear the weak reference from the java peer to the native object since
64 // it is possible that java object lifetime can exceed the XWalkViewContents.
65 Java_XWalkContentsClientBridge_setNativeContentsClientBridge(
69 void XWalkContentsClientBridge::AllowCertificateError(
71 net::X509Certificate* cert,
72 const GURL& request_url,
73 const base::Callback<void(bool)>& callback,
74 bool* cancel_request) {
76 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
77 JNIEnv* env = AttachCurrentThread();
79 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
83 std::string der_string;
84 net::X509Certificate::GetDEREncoded(cert->os_cert_handle(), &der_string);
85 ScopedJavaLocalRef<jbyteArray> jcert = base::android::ToJavaByteArray(
87 reinterpret_cast<const uint8*>(der_string.data()),
89 ScopedJavaLocalRef<jstring> jurl(ConvertUTF8ToJavaString(
90 env, request_url.spec()));
91 // We need to add the callback before making the call to java side,
92 // as it may do a synchronous callback prior to returning.
93 int request_id = pending_cert_error_callbacks_.Add(
94 new CertErrorCallback(callback));
95 *cancel_request = !Java_XWalkContentsClientBridge_allowCertificateError(
96 env, obj.obj(), cert_error, jcert.obj(), jurl.obj(), request_id);
97 // if the request is cancelled, then cancel the stored callback
98 if (*cancel_request) {
99 pending_cert_error_callbacks_.Remove(request_id);
103 void XWalkContentsClientBridge::ProceedSslError(JNIEnv* env, jobject obj,
104 jboolean proceed, jint id) {
105 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
106 CertErrorCallback* callback = pending_cert_error_callbacks_.Lookup(id);
107 if (!callback || callback->is_null()) {
108 LOG(WARNING) << "Ignoring unexpected ssl error proceed callback";
111 callback->Run(proceed);
112 pending_cert_error_callbacks_.Remove(id);
115 void XWalkContentsClientBridge::RunJavaScriptDialog(
116 content::JavaScriptMessageType message_type,
117 const GURL& origin_url,
118 const base::string16& message_text,
119 const base::string16& default_prompt_text,
120 const content::JavaScriptDialogManager::DialogClosedCallback& callback) {
121 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
122 JNIEnv* env = AttachCurrentThread();
124 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
128 int callback_id = pending_js_dialog_callbacks_.Add(
129 new content::JavaScriptDialogManager::DialogClosedCallback(callback));
130 ScopedJavaLocalRef<jstring> jurl(
131 ConvertUTF8ToJavaString(env, origin_url.spec()));
132 ScopedJavaLocalRef<jstring> jmessage(
133 ConvertUTF16ToJavaString(env, message_text));
135 switch (message_type) {
136 case content::JAVASCRIPT_MESSAGE_TYPE_ALERT:
137 Java_XWalkContentsClientBridge_handleJsAlert(
138 env, obj.obj(), jurl.obj(), jmessage.obj(), callback_id);
140 case content::JAVASCRIPT_MESSAGE_TYPE_CONFIRM:
141 Java_XWalkContentsClientBridge_handleJsConfirm(
142 env, obj.obj(), jurl.obj(), jmessage.obj(), callback_id);
144 case content::JAVASCRIPT_MESSAGE_TYPE_PROMPT: {
145 ScopedJavaLocalRef<jstring> jdefault_value(
146 ConvertUTF16ToJavaString(env, default_prompt_text));
147 Java_XWalkContentsClientBridge_handleJsPrompt(env,
151 jdefault_value.obj(),
160 void XWalkContentsClientBridge::RunBeforeUnloadDialog(
161 const GURL& origin_url,
162 const base::string16& message_text,
163 const content::JavaScriptDialogManager::DialogClosedCallback& callback) {
164 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
165 JNIEnv* env = AttachCurrentThread();
167 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
171 int callback_id = pending_js_dialog_callbacks_.Add(
172 new content::JavaScriptDialogManager::DialogClosedCallback(callback));
173 ScopedJavaLocalRef<jstring> jurl(
174 ConvertUTF8ToJavaString(env, origin_url.spec()));
175 ScopedJavaLocalRef<jstring> jmessage(
176 ConvertUTF16ToJavaString(env, message_text));
178 Java_XWalkContentsClientBridge_handleJsBeforeUnload(
179 env, obj.obj(), jurl.obj(), jmessage.obj(), callback_id);
182 bool XWalkContentsClientBridge::OnReceivedHttpAuthRequest(
183 const JavaRef<jobject>& handler,
184 const std::string& host,
185 const std::string& realm) {
186 JNIEnv* env = AttachCurrentThread();
187 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
191 ScopedJavaLocalRef<jstring> jhost = ConvertUTF8ToJavaString(env, host);
192 ScopedJavaLocalRef<jstring> jrealm = ConvertUTF8ToJavaString(env, realm);
193 Java_XWalkContentsClientBridge_onReceivedHttpAuthRequest(
194 env, obj.obj(), handler.obj(), jhost.obj(), jrealm.obj());
198 void XWalkContentsClientBridge::OnNotificationIconDownloaded(
200 int http_status_code,
201 const GURL& icon_url,
202 const std::vector<SkBitmap>& bitmaps,
203 const std::vector<gfx::Size>& original_bitmap_sizes) {
204 if (bitmaps.empty() && http_status_code == 404) {
205 LOG(WARNING) << "Failed to download notification icon from "
208 NotificationDownloadRequestIdMap::iterator iter =
209 downloading_icon_notifications_.find(id);
210 if (iter == downloading_icon_notifications_.end() ||
211 iter->second.size() != 3) {
214 int notification_id = iter->second[0];
215 int process_id = iter->second[1];
216 int route_id = iter->second[2];
217 // This will lead to a second call of ShowNotification for the
218 // same notification id to update the icon. On Android, when
219 // the notification which is already shown is fired again, it will
220 // silently update the content only.
221 BrowserThread::PostTask(
224 base::Bind(&RunUpdateNotificationIconOnUIThread,
230 downloading_icon_notifications_.erase(id);
233 void XWalkContentsClientBridge::UpdateNotificationIcon(
234 int notification_id, const SkBitmap& icon) {
235 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
236 JNIEnv* env = AttachCurrentThread();
238 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
242 ScopedJavaLocalRef<jobject> jicon = gfx::ConvertToJavaBitmap(&icon);
243 Java_XWalkContentsClientBridge_updateNotificationIcon(
244 env, obj.obj(), notification_id, jicon.obj());
247 void XWalkContentsClientBridge::ShowNotification(
248 const content::ShowDesktopNotificationHostMsgParams& params,
252 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
253 JNIEnv* env = AttachCurrentThread();
255 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
259 ScopedJavaLocalRef<jstring> jtitle(
260 ConvertUTF16ToJavaString(env, params.title));
261 ScopedJavaLocalRef<jstring> jbody(
262 ConvertUTF16ToJavaString(env, params.body));
263 ScopedJavaLocalRef<jstring> jreplace_id(
264 ConvertUTF16ToJavaString(env, params.replace_id));
266 Java_XWalkContentsClientBridge_showNotification(
267 env, obj.obj(), jtitle.obj(), jbody.obj(),
268 jreplace_id.obj(), params.notification_id,
269 process_id, route_id);
271 if (params.icon_url.is_valid()) {
272 RenderViewHost* rvh = RenderViewHost::FromID(process_id, route_id);
274 WebContents* web_contents = WebContents::FromRenderViewHost(rvh);
276 int download_request_id = web_contents->DownloadImage(
281 &XWalkContentsClientBridge::OnNotificationIconDownloaded,
282 base::Unretained(this)));
283 std::vector<int> ids;
284 ids.push_back(params.notification_id);
285 ids.push_back(process_id);
286 ids.push_back(route_id);
287 downloading_icon_notifications_[download_request_id] = ids;
293 void XWalkContentsClientBridge::CancelNotification(
297 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
298 JNIEnv* env = AttachCurrentThread();
300 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
304 Java_XWalkContentsClientBridge_cancelNotification(
305 env, obj.obj(), notification_id,
306 process_id, route_id);
309 void XWalkContentsClientBridge::ConfirmJsResult(JNIEnv* env,
313 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
314 content::JavaScriptDialogManager::DialogClosedCallback* callback =
315 pending_js_dialog_callbacks_.Lookup(id);
316 base::string16 prompt_text;
318 prompt_text = ConvertJavaStringToUTF16(env, prompt);
321 callback->Run(true, prompt_text);
322 pending_js_dialog_callbacks_.Remove(id);
325 void XWalkContentsClientBridge::CancelJsResult(JNIEnv*, jobject, int id) {
326 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
327 content::JavaScriptDialogManager::DialogClosedCallback* callback =
328 pending_js_dialog_callbacks_.Lookup(id);
330 callback->Run(false, base::string16());
331 pending_js_dialog_callbacks_.Remove(id);
334 void XWalkContentsClientBridge::ExitFullscreen(
335 JNIEnv*, jobject, jint j_web_contents) {
336 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
337 WebContents* web_contents = reinterpret_cast<WebContents*>(j_web_contents);
339 RenderViewHost* rvh = web_contents->GetRenderViewHost();
341 rvh->ExitFullscreen();
345 void XWalkContentsClientBridge::NotificationDisplayed(
346 JNIEnv*, jobject, int id, int process_id, int route_id) {
347 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
348 RenderViewHost* rvh = RenderViewHost::FromID(
349 process_id, route_id);
352 rvh->DesktopNotificationPostDisplay(id);
355 void XWalkContentsClientBridge::NotificationError(
356 JNIEnv* env, jobject, int id, jstring error,
357 int process_id, int route_id) {
358 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
359 RenderViewHost* rvh = RenderViewHost::FromID(
360 process_id, route_id);
363 base::string16 error_text;
365 error_text = ConvertJavaStringToUTF16(env, error);
366 rvh->DesktopNotificationPostError(id, error_text);
369 void XWalkContentsClientBridge::NotificationClicked(
370 JNIEnv*, jobject, int id, int process_id, int route_id) {
371 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
372 RenderViewHost* rvh = RenderViewHost::FromID(
373 process_id, route_id);
376 rvh->DesktopNotificationPostClick(id);
379 void XWalkContentsClientBridge::NotificationClosed(
380 JNIEnv*, jobject, int id, bool by_user,
381 int process_id, int route_id) {
382 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
383 RenderViewHost* rvh = RenderViewHost::FromID(
384 process_id, route_id);
387 rvh->DesktopNotificationPostClose(id, by_user);
390 bool RegisterXWalkContentsClientBridge(JNIEnv* env) {
391 return RegisterNativesImpl(env) >= 0;