b0779d6a9436ecd74f5acaffd5c7f3983038fac2
[platform/framework/web/crosswalk.git] / src / xwalk / runtime / browser / android / xwalk_contents_client_bridge.cc
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.
4
5 #include "xwalk/runtime/browser/android/xwalk_contents_client_bridge.h"
6
7 #include <string>
8
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"
20 #include "url/gurl.h"
21 #include "ui/gfx/android/java_bitmap.h"
22
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;
32
33 namespace xwalk {
34
35 namespace {
36
37 void RunUpdateNotificationIconOnUIThread(
38     int notification_id,
39     int process_id,
40     int route_id,
41     const SkBitmap& icon) {
42   XWalkContentsClientBridgeBase* bridge =
43       XWalkContentsClientBridgeBase::FromRenderViewID(process_id, route_id);
44   if (bridge)
45     bridge->UpdateNotificationIcon(notification_id, icon);
46 }
47
48 }  // namespace
49
50 XWalkContentsClientBridge::XWalkContentsClientBridge(JNIEnv* env, jobject obj)
51     : java_ref_(env, obj) {
52   DCHECK(obj);
53   Java_XWalkContentsClientBridge_setNativeContentsClientBridge(
54       env, obj, reinterpret_cast<jint>(this));
55 }
56
57 XWalkContentsClientBridge::~XWalkContentsClientBridge() {
58   JNIEnv* env = AttachCurrentThread();
59
60   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
61   if (obj.is_null())
62     return;
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(
66       env, obj.obj(), 0);
67 }
68
69 void XWalkContentsClientBridge::AllowCertificateError(
70     int cert_error,
71     net::X509Certificate* cert,
72     const GURL& request_url,
73     const base::Callback<void(bool)>& callback,
74     bool* cancel_request) {
75
76   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
77   JNIEnv* env = AttachCurrentThread();
78
79   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
80   if (obj.is_null())
81     return;
82
83   std::string der_string;
84   net::X509Certificate::GetDEREncoded(cert->os_cert_handle(), &der_string);
85   ScopedJavaLocalRef<jbyteArray> jcert = base::android::ToJavaByteArray(
86       env,
87       reinterpret_cast<const uint8*>(der_string.data()),
88       der_string.length());
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);
100   }
101 }
102
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";
109     return;
110   }
111   callback->Run(proceed);
112   pending_cert_error_callbacks_.Remove(id);
113 }
114
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();
123
124   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
125   if (obj.is_null())
126     return;
127
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));
134
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);
139       break;
140     case content::JAVASCRIPT_MESSAGE_TYPE_CONFIRM:
141       Java_XWalkContentsClientBridge_handleJsConfirm(
142           env, obj.obj(), jurl.obj(), jmessage.obj(), callback_id);
143       break;
144     case content::JAVASCRIPT_MESSAGE_TYPE_PROMPT: {
145       ScopedJavaLocalRef<jstring> jdefault_value(
146           ConvertUTF16ToJavaString(env, default_prompt_text));
147       Java_XWalkContentsClientBridge_handleJsPrompt(env,
148                                                     obj.obj(),
149                                                     jurl.obj(),
150                                                     jmessage.obj(),
151                                                     jdefault_value.obj(),
152                                                     callback_id);
153       break;
154     }
155     default:
156        NOTREACHED();
157   }
158 }
159
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();
166
167   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
168   if (obj.is_null())
169     return;
170
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));
177
178   Java_XWalkContentsClientBridge_handleJsBeforeUnload(
179       env, obj.obj(), jurl.obj(), jmessage.obj(), callback_id);
180 }
181
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);
188   if (obj.is_null())
189     return false;
190
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());
195   return true;
196 }
197
198 void XWalkContentsClientBridge::OnNotificationIconDownloaded(
199     int id,
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 "
206                  << icon_url.spec();
207   } else {
208     NotificationDownloadRequestIdMap::iterator iter =
209         downloading_icon_notifications_.find(id);
210     if (iter == downloading_icon_notifications_.end() ||
211         iter->second.size() != 3) {
212       return;
213     }
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(
222         BrowserThread::UI,
223         FROM_HERE,
224         base::Bind(&RunUpdateNotificationIconOnUIThread,
225                    notification_id,
226                    process_id,
227                    route_id,
228                    bitmaps[0]));
229   }
230   downloading_icon_notifications_.erase(id);
231 }
232
233 void XWalkContentsClientBridge::UpdateNotificationIcon(
234     int notification_id, const SkBitmap& icon) {
235   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
236   JNIEnv* env = AttachCurrentThread();
237
238   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
239   if (obj.is_null())
240     return;
241
242   ScopedJavaLocalRef<jobject> jicon = gfx::ConvertToJavaBitmap(&icon);
243   Java_XWalkContentsClientBridge_updateNotificationIcon(
244       env, obj.obj(), notification_id, jicon.obj());
245 }
246
247 void XWalkContentsClientBridge::ShowNotification(
248     const content::ShowDesktopNotificationHostMsgParams& params,
249     bool worker,
250     int process_id,
251     int route_id) {
252   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
253   JNIEnv* env = AttachCurrentThread();
254
255   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
256   if (obj.is_null())
257     return;
258
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));
265
266   Java_XWalkContentsClientBridge_showNotification(
267       env, obj.obj(), jtitle.obj(), jbody.obj(),
268       jreplace_id.obj(), params.notification_id,
269       process_id, route_id);
270
271   if (params.icon_url.is_valid()) {
272     RenderViewHost* rvh = RenderViewHost::FromID(process_id, route_id);
273     if (rvh) {
274       WebContents* web_contents = WebContents::FromRenderViewHost(rvh);
275       if (web_contents) {
276         int download_request_id = web_contents->DownloadImage(
277             params.icon_url,
278             false,
279             0,
280             base::Bind(
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;
288       }
289     }
290   }
291 }
292
293 void XWalkContentsClientBridge::CancelNotification(
294     int notification_id,
295     int process_id,
296     int route_id) {
297   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
298   JNIEnv* env = AttachCurrentThread();
299
300   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
301   if (obj.is_null())
302     return;
303
304   Java_XWalkContentsClientBridge_cancelNotification(
305       env, obj.obj(), notification_id,
306       process_id, route_id);
307 }
308
309 void XWalkContentsClientBridge::ConfirmJsResult(JNIEnv* env,
310                                                 jobject,
311                                                 int id,
312                                                 jstring prompt) {
313   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
314   content::JavaScriptDialogManager::DialogClosedCallback* callback =
315       pending_js_dialog_callbacks_.Lookup(id);
316   base::string16 prompt_text;
317   if (prompt) {
318     prompt_text = ConvertJavaStringToUTF16(env, prompt);
319   }
320   if (callback)
321     callback->Run(true, prompt_text);
322   pending_js_dialog_callbacks_.Remove(id);
323 }
324
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);
329   if (callback)
330     callback->Run(false, base::string16());
331   pending_js_dialog_callbacks_.Remove(id);
332 }
333
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);
338   if (web_contents) {
339     RenderViewHost* rvh = web_contents->GetRenderViewHost();
340     if (rvh)
341       rvh->ExitFullscreen();
342   }
343 }
344
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);
350   if (!rvh)
351     return;
352   rvh->DesktopNotificationPostDisplay(id);
353 }
354
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);
361   if (!rvh)
362     return;
363   base::string16 error_text;
364   if (error)
365     error_text = ConvertJavaStringToUTF16(env, error);
366   rvh->DesktopNotificationPostError(id, error_text);
367 }
368
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);
374   if (!rvh)
375     return;
376   rvh->DesktopNotificationPostClick(id);
377 }
378
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);
385   if (!rvh)
386     return;
387   rvh->DesktopNotificationPostClose(id, by_user);
388 }
389
390 bool RegisterXWalkContentsClientBridge(JNIEnv* env) {
391   return RegisterNativesImpl(env) >= 0;
392 }
393
394 }  // namespace xwalk