Upstream version 11.39.244.0
[platform/framework/web/crosswalk.git] / src / xwalk / runtime / android / core_internal / src / org / xwalk / core / internal / XWalkContentsClientBridge.java
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Copyright (c) 2013-2014 Intel Corporation. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5
6 package org.xwalk.core.internal;
7
8 import android.content.ActivityNotFoundException;
9 import android.content.ContentResolver;
10 import android.content.Intent;
11 import android.database.Cursor;
12 import android.graphics.Bitmap;
13 import android.graphics.Picture;
14 import android.net.Uri;
15 import android.net.http.SslCertificate;
16 import android.net.http.SslError;
17 import android.os.Message;
18 import android.os.Handler;
19 import android.provider.MediaStore;
20 import android.util.Log;
21 import android.view.KeyEvent;
22 import android.view.View;
23 import android.webkit.ConsoleMessage;
24 import android.webkit.ValueCallback;
25 import android.webkit.WebResourceResponse;
26
27 import org.chromium.base.CalledByNative;
28 import org.chromium.base.JNINamespace;
29 import org.chromium.base.ThreadUtils;
30 import org.chromium.components.navigation_interception.InterceptNavigationDelegate;
31 import org.chromium.components.navigation_interception.NavigationParams;
32 import org.chromium.content.browser.ContentVideoViewClient;
33 import org.chromium.content.browser.ContentViewDownloadDelegate;
34 import org.chromium.content.browser.DownloadInfo;
35 import org.xwalk.core.internal.XWalkUIClientInternal.LoadStatusInternal;
36
37 // Help bridge callback in XWalkContentsClient to XWalkViewClient and
38 // XWalkWebChromeClient; Also handle the JNI conmmunication logic.
39 @JNINamespace("xwalk")
40 class XWalkContentsClientBridge extends XWalkContentsClient
41         implements ContentViewDownloadDelegate {
42     private static final String TAG = XWalkContentsClientBridge.class.getName();
43     private static final int NEW_XWALKVIEW_CREATED = 100;
44     private static final int NEW_ICON_DOWNLOAD     = 101;
45
46     private XWalkViewInternal mXWalkView;
47     private XWalkUIClientInternal mXWalkUIClient;
48     private XWalkResourceClientInternal mXWalkResourceClient;
49     private XWalkClient mXWalkClient;
50     private XWalkWebChromeClient mXWalkWebChromeClient;
51     private Bitmap mFavicon;
52     private DownloadListener mDownloadListener;
53     private InterceptNavigationDelegate mInterceptNavigationDelegate;
54     private PageLoadListener mPageLoadListener;
55     private XWalkNavigationHandler mNavigationHandler;
56     private XWalkNotificationService mNotificationService;
57     private Handler mUiThreadHandler;
58
59     /** State recording variables */
60     // For fullscreen state.
61     private boolean mIsFullscreen = false;
62     // For load state.
63     private LoadStatusInternal mLoadStatus = LoadStatusInternal.FINISHED;
64     private String mLoadingUrl = null;
65
66     // The native peer of the object
67     private long mNativeContentsClientBridge;
68
69     private float mPageScaleFactor;
70
71     private class InterceptNavigationDelegateImpl implements InterceptNavigationDelegate {
72         private XWalkContentsClient mContentsClient;
73
74         public InterceptNavigationDelegateImpl(XWalkContentsClient client) {
75             mContentsClient = client;
76         }
77
78         public boolean shouldIgnoreNavigation(NavigationParams navigationParams) {
79             final String url = navigationParams.url;
80             boolean ignoreNavigation = shouldOverrideUrlLoading(url) ||
81                      (mNavigationHandler != null &&
82                       mNavigationHandler.handleNavigation(navigationParams));
83
84             if (!ignoreNavigation) {
85                 // Post a message to UI thread to notify the page is starting to load.
86                 mContentsClient.getCallbackHelper().postOnPageStarted(url);
87             }
88
89             return ignoreNavigation;
90         }
91     }
92
93     public XWalkContentsClientBridge(XWalkViewInternal xwView) {
94         mXWalkView = xwView;
95
96         mInterceptNavigationDelegate = new InterceptNavigationDelegateImpl(this);
97
98         mUiThreadHandler = new Handler() {
99             @Override
100             public void handleMessage(Message msg) {
101                 switch(msg.what) {
102                     case NEW_XWALKVIEW_CREATED:
103                         XWalkViewInternal newXWalkView = (XWalkViewInternal) msg.obj;
104                         if (newXWalkView == mXWalkView) {
105                             throw new IllegalArgumentException("Parent XWalkView cannot host it's own popup window");
106                         }
107
108                         if (newXWalkView != null && newXWalkView.getNavigationHistory().size() != 0) {
109                             throw new IllegalArgumentException("New WebView for popup window must not have been previously navigated.");
110                         }
111
112                         mXWalkView.completeWindowCreation(newXWalkView);
113                         break;
114                     case NEW_ICON_DOWNLOAD:
115                         String url = (String) msg.obj;
116                         nativeDownloadIcon(mNativeContentsClientBridge, url);
117                         break;
118                     default:
119                         throw new IllegalStateException();
120                 }
121             }
122         };
123
124     }
125
126     public void setUIClient(XWalkUIClientInternal client) {
127         // If it's null, use Crosswalk implementation.
128         if (client != null) {
129             mXWalkUIClient = client;
130             return;
131         }
132         mXWalkUIClient = new XWalkUIClientInternal(mXWalkView);
133     }
134
135     public void setResourceClient(XWalkResourceClientInternal client) {
136         // If it's null, use Crosswalk implementation.
137         if (client != null) {
138             mXWalkResourceClient = client;
139             return;
140         }
141         mXWalkResourceClient = new XWalkResourceClientInternal(mXWalkView);
142     }
143
144
145     public void setXWalkWebChromeClient(XWalkWebChromeClient client) {
146         // If it's null, use Crosswalk implementation.
147         if (client == null) return;
148         client.setContentsClient(this);
149         mXWalkWebChromeClient = client;
150     }
151
152     public XWalkWebChromeClient getXWalkWebChromeClient() {
153         return mXWalkWebChromeClient;
154     }
155
156     public void setXWalkClient(XWalkClient client) {
157         mXWalkClient = client;
158     }
159
160     public void setNavigationHandler(XWalkNavigationHandler handler) {
161         mNavigationHandler = handler;
162     }
163
164     void registerPageLoadListener(PageLoadListener listener) {
165         mPageLoadListener = listener;
166     }
167
168     public void setNotificationService(XWalkNotificationService service) {
169         if (mNotificationService != null) mNotificationService.shutdown();
170         mNotificationService = service;
171         if (mNotificationService != null) mNotificationService.setBridge(this);
172     }
173
174     public boolean onNewIntent(Intent intent) {
175         return mNotificationService.maybeHandleIntent(intent);
176     }
177
178     public InterceptNavigationDelegate getInterceptNavigationDelegate() {
179         return mInterceptNavigationDelegate;
180     }
181
182     private boolean isOwnerActivityRunning() {
183         if (mXWalkView != null && mXWalkView.isOwnerActivityRunning()) {
184             return true;
185         }
186         return false;
187     }
188
189     // TODO(Xingnan): All the empty functions need to be implemented.
190     @Override
191     public boolean shouldOverrideUrlLoading(String url) {
192         if (mXWalkResourceClient != null && mXWalkView != null) {
193             return mXWalkResourceClient.shouldOverrideUrlLoading(mXWalkView, url);
194         }
195         return false;
196     }
197
198     @Override
199     public boolean shouldOverrideKeyEvent(KeyEvent event) {
200         boolean overridden = false;
201         if (mXWalkUIClient != null && mXWalkView != null) {
202             overridden = mXWalkUIClient.shouldOverrideKeyEvent(mXWalkView, event);
203         }
204         if (!overridden) {
205             return super.shouldOverrideKeyEvent(event);
206         }
207         return overridden;
208     }
209
210     @Override
211     public void onUnhandledKeyEvent(KeyEvent event) {
212         if (mXWalkUIClient != null && mXWalkView != null) {
213             mXWalkUIClient.onUnhandledKeyEvent(mXWalkView, event);
214         }
215     }
216
217     @Override
218     public void getVisitedHistory(ValueCallback<String[]> callback) {
219     }
220
221     @Override
222     public void doUpdateVisitedHistory(String url, boolean isReload) {
223     }
224
225     @Override
226     public void onProgressChanged(int progress) {
227         if (isOwnerActivityRunning()) {
228             mXWalkResourceClient.onProgressChanged(mXWalkView, progress);
229         }
230     }
231
232     @Override
233     public WebResourceResponse shouldInterceptRequest(String url) {
234         if (isOwnerActivityRunning()) {
235             return mXWalkResourceClient.shouldInterceptLoadRequest(mXWalkView, url);
236         }
237         return null;
238     }
239
240     @Override
241     public void onResourceLoadStarted(String url) {
242         if (isOwnerActivityRunning()) {
243             mXWalkResourceClient.onLoadStarted(mXWalkView, url);
244         }
245     }
246
247     @Override
248     public void onResourceLoadFinished(String url) {
249         if (isOwnerActivityRunning()) {
250             mXWalkResourceClient.onLoadFinished(mXWalkView, url);
251         }
252     }
253
254     @Override
255     public void onLoadResource(String url) {
256         if (mXWalkClient != null && isOwnerActivityRunning()) {
257             mXWalkClient.onLoadResource(mXWalkView, url);
258         }
259     }
260
261     @Override
262     public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
263         return false;
264     }
265
266     @CalledByNative
267     public void onReceivedHttpAuthRequest(XWalkHttpAuthHandler handler, String host, String realm) {
268         if (mXWalkClient != null && isOwnerActivityRunning()) {
269             mXWalkClient.onReceivedHttpAuthRequest(mXWalkView, handler, host, realm);
270         }
271     }
272
273     @Override
274     public void onReceivedSslError(ValueCallback<Boolean> callback, SslError error) {
275         if (mXWalkResourceClient != null && isOwnerActivityRunning()) {
276             mXWalkResourceClient.onReceivedSslError(mXWalkView, callback, error);
277         }
278     }
279
280     @Override
281     public void onReceivedLoginRequest(String realm, String account, String args) {
282     }
283
284     @Override
285     public void onGeolocationPermissionsShowPrompt(String origin,
286             XWalkGeolocationPermissions.Callback callback) {
287         if (mXWalkWebChromeClient != null && isOwnerActivityRunning()) {
288             mXWalkWebChromeClient.onGeolocationPermissionsShowPrompt(origin, callback);
289         }
290     }
291
292     @Override
293     public void onGeolocationPermissionsHidePrompt() {
294         if (mXWalkWebChromeClient != null && isOwnerActivityRunning()) {
295             mXWalkWebChromeClient.onGeolocationPermissionsHidePrompt();
296         }
297     }
298
299     @Override
300     public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches,
301             boolean isDoneCounting) {
302     }
303
304     @Override
305     public void onNewPicture(Picture picture) {
306     }
307
308     @Override
309     public void onPageStarted(String url) {
310         if (mXWalkUIClient != null && isOwnerActivityRunning()) {
311             mLoadingUrl = url;
312             mLoadStatus = LoadStatusInternal.FINISHED;
313             mXWalkUIClient.onPageLoadStarted(mXWalkView, url);
314         }
315     }
316
317     @Override
318     public void onPageFinished(String url) {
319         if (!isOwnerActivityRunning()) return;
320         if (mPageLoadListener != null) mPageLoadListener.onPageFinished(url);
321         if (mXWalkUIClient != null) {
322             if (mLoadStatus == LoadStatusInternal.CANCELLED && mLoadingUrl != null) {
323                 mXWalkUIClient.onPageLoadStopped(mXWalkView, mLoadingUrl, mLoadStatus);
324             } else {
325                 mXWalkUIClient.onPageLoadStopped(mXWalkView, url, mLoadStatus);
326             }
327             mLoadingUrl = null;
328         }
329
330         // This isn't the accurate point to notify a resource loading is finished,
331         // but it's a workable way. We could enhance this by extending Content
332         // API in future if we have the demand.
333         onResourceLoadFinished(url);
334     }
335
336     @Override
337     protected void onStopLoading() {
338         mLoadStatus = LoadStatusInternal.CANCELLED;
339     }
340
341     @Override
342     public void onReceivedError(int errorCode, String description, String failingUrl) {
343         if (isOwnerActivityRunning()) {
344             if (mLoadingUrl != null && mLoadingUrl.equals(failingUrl)) {
345                 mLoadStatus = LoadStatusInternal.FAILED;
346             }
347             mXWalkResourceClient.onReceivedLoadError(mXWalkView, errorCode, description, failingUrl);
348         }
349     }
350
351     @Override
352     public void onRendererUnresponsive() {
353         if (mXWalkClient != null && isOwnerActivityRunning()) {
354             mXWalkClient.onRendererUnresponsive(mXWalkView);
355         }
356     }
357
358     @Override
359     public void onRendererResponsive() {
360         if (mXWalkClient != null && isOwnerActivityRunning()) {
361             mXWalkClient.onRendererResponsive(mXWalkView);
362         }
363     }
364
365     @Override
366     public void onFormResubmission(Message dontResend, Message resend) {
367         dontResend.sendToTarget();
368     }
369
370     @Override
371     public void onDownloadStart(String url,
372                                 String userAgent,
373                                 String contentDisposition,
374                                 String mimeType,
375                                 long contentLength) {
376     }
377
378     @Override
379     public boolean onCreateWindow(boolean isDialog, boolean isUserGesture) {
380         if (isDialog) return false;
381         
382         XWalkUIClientInternal.InitiateByInternal initiator =
383                 XWalkUIClientInternal.InitiateByInternal.BY_JAVASCRIPT;
384         if (isUserGesture) {
385             initiator = XWalkUIClientInternal.InitiateByInternal.BY_USER_GESTURE;
386         }
387
388         ValueCallback<XWalkViewInternal> callback = new ValueCallback<XWalkViewInternal>() {
389             @Override
390             public void onReceiveValue(XWalkViewInternal newXWalkView) {
391                 Message m = mUiThreadHandler.obtainMessage(NEW_XWALKVIEW_CREATED, newXWalkView);
392                 m.sendToTarget();
393             }
394         };
395
396         return mXWalkUIClient.onCreateWindowRequested(mXWalkView, initiator, callback);
397     }
398
399     @Override
400     public void onRequestFocus() {
401         if (isOwnerActivityRunning()) {
402             mXWalkUIClient.onRequestFocus(mXWalkView);
403         }
404     }
405
406     @Override
407     public void onCloseWindow() {
408         if (isOwnerActivityRunning()) {
409             mXWalkUIClient.onJavascriptCloseWindow(mXWalkView);
410         }
411     }
412
413     @Override
414     public void onReceivedIcon(Bitmap bitmap) {
415         if (mXWalkWebChromeClient != null && mXWalkView != null) {
416             mXWalkWebChromeClient.onReceivedIcon(mXWalkView, bitmap);
417         }
418         mFavicon = bitmap;
419     }
420
421     @Override
422     public void onShowCustomView(View view, XWalkWebChromeClient.CustomViewCallback callback) {
423         if (mXWalkWebChromeClient != null && isOwnerActivityRunning()) {
424             mXWalkWebChromeClient.onShowCustomView(view, callback);
425         }
426     }
427
428     @Override
429     public void onHideCustomView() {
430         if (mXWalkWebChromeClient != null && isOwnerActivityRunning()) {
431             mXWalkWebChromeClient.onHideCustomView();
432         }
433     }
434
435     @Override
436     public void onScaleChangedScaled(float oldScale, float newScale) {
437         if (isOwnerActivityRunning()) {
438             mXWalkUIClient.onScaleChanged(mXWalkView, oldScale, newScale);
439         }
440     }
441
442     @Override
443     public void didFinishLoad(String url) {
444     }
445
446     @Override
447     public void onTitleChanged(String title) {
448         if (mXWalkUIClient != null && isOwnerActivityRunning()) {
449             mXWalkUIClient.onReceivedTitle(mXWalkView, title);
450         }
451     }
452
453     @Override
454     public void onToggleFullscreen(boolean enterFullscreen) {
455         if (isOwnerActivityRunning()) {
456             mIsFullscreen = enterFullscreen;
457             mXWalkUIClient.onFullscreenToggled(mXWalkView, enterFullscreen);
458         }
459     }
460
461     @Override
462     public boolean hasEnteredFullscreen() {
463         return mIsFullscreen;
464     }
465
466     @Override
467     public boolean shouldOpenWithDefaultBrowser(String contentUrl) {
468         Intent intent = new Intent();
469         intent.setAction("android.intent.action.VIEW");
470         Uri url = Uri.parse(contentUrl);
471         intent.setData(url);
472         try {
473             mXWalkView.getActivity().startActivity(intent);
474         } catch (ActivityNotFoundException exception) {
475             Log.w(TAG, "Activity not found for Intent:");
476             return false;
477         }
478
479         return true;
480     }
481
482     @Override
483     public boolean shouldOverrideRunFileChooser(
484             final int processId, final int renderId, final int modeFlags,
485             String acceptTypes, boolean capture) {
486         if (!isOwnerActivityRunning()) return false;
487         abstract class UriCallback implements ValueCallback<Uri> {
488             boolean syncNullReceived = false;
489             boolean syncCallFinished = false;
490             protected String resolveFileName(Uri uri, ContentResolver contentResolver) {
491                 if (contentResolver == null || uri == null) return "";
492                 Cursor cursor = null;
493                 try {
494                     cursor = contentResolver.query(uri, null, null, null, null);
495
496                     if (cursor != null && cursor.getCount() >= 1) {
497                         cursor.moveToFirst();
498                         int index = cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME);
499                         if (index > -1) return cursor.getString(index);
500                     }
501                 } catch (NullPointerException e) {
502                     // Some android models don't handle the provider call correctly.
503                     // see crbug.com/345393
504                     return "";
505                 } finally {
506                     if (cursor != null) cursor.close();
507                 }
508                 return "";
509             }
510         }
511         UriCallback uploadFile = new UriCallback() {
512             boolean completed = false;
513             @Override
514             public void onReceiveValue(Uri value) {
515                 if (completed) {
516                     throw new IllegalStateException("Duplicate openFileChooser result");
517                 }
518                 if (value == null && !syncCallFinished) {
519                     syncNullReceived = true;
520                     return;
521                 }
522                 completed = true;
523                 if (value == null) {
524                     nativeOnFilesNotSelected(mNativeContentsClientBridge,
525                             processId, renderId, modeFlags);
526                 } else {
527                     String result = "";
528                     String displayName = null;
529                     if (ContentResolver.SCHEME_FILE.equals(value.getScheme())) {
530                         result = value.getSchemeSpecificPart();
531                         displayName = value.getLastPathSegment();
532                     } else if (ContentResolver.SCHEME_CONTENT.equals(value.getScheme())) {
533                         result = value.toString();
534                         displayName = resolveFileName(
535                                 value, mXWalkView.getActivity().getContentResolver());
536                     } else {
537                         result = value.getPath();
538                         displayName = value.getLastPathSegment();
539                     }
540                     if (displayName == null || displayName.isEmpty()) displayName = result;
541                     nativeOnFilesSelected(mNativeContentsClientBridge,
542                             processId, renderId, modeFlags, result, displayName);
543                 }
544             }
545         };
546         mXWalkUIClient.openFileChooser(
547                 mXWalkView, uploadFile, acceptTypes, Boolean.toString(capture));
548         uploadFile.syncCallFinished = true;
549         // File chooser requires user interaction, valid derives should handle it in async process.
550         // If the ValueCallback receive a sync result with null value, it is considered the
551         // file chooser is not overridden.
552         if (uploadFile.syncNullReceived) {
553             return mXWalkView.showFileChooser(uploadFile, acceptTypes, Boolean.toString(capture));
554         }
555         return !uploadFile.syncNullReceived;
556     }
557
558     @Override
559     public ContentVideoViewClient getContentVideoViewClient() {
560         return new XWalkContentVideoViewClient(this, mXWalkView.getActivity(), mXWalkView);
561     }
562
563     // Used by the native peer to set/reset a weak ref to the native peer.
564     @CalledByNative
565     private void setNativeContentsClientBridge(long nativeContentsClientBridge) {
566         mNativeContentsClientBridge = nativeContentsClientBridge;
567     }
568
569     // If returns false, the request is immediately canceled, and any call to proceedSslError
570     // has no effect. If returns true, the request should be canceled or proceeded using
571     // proceedSslError().
572     // Unlike the webview classic, we do not keep keep a database of certificates that
573     // are allowed by the user, because this functionality is already handled via
574     // ssl_policy in native layers.
575     @CalledByNative
576     private boolean allowCertificateError(int certError, byte[] derBytes, final String url,
577             final int id) {
578         final SslCertificate cert = SslUtil.getCertificateFromDerBytes(derBytes);
579         if (cert == null) {
580             // if the certificate or the client is null, cancel the request
581             return false;
582         }
583         final SslError sslError = SslUtil.sslErrorFromNetErrorCode(certError, cert, url);
584         ValueCallback<Boolean> callback = new ValueCallback<Boolean>() {
585             @Override
586             public void onReceiveValue(Boolean value) {
587                 proceedSslError(value.booleanValue(), id);
588             }
589         };
590         onReceivedSslError(callback, sslError);
591         return true;
592     }
593
594     private void proceedSslError(boolean proceed, int id) {
595         if (mNativeContentsClientBridge == 0) return;
596         nativeProceedSslError(mNativeContentsClientBridge, proceed, id);
597     }
598
599     @CalledByNative
600     private void handleJsAlert(String url, String message, int id) {
601         if (isOwnerActivityRunning()) {
602             XWalkJavascriptResultHandlerInternal result =
603                     new XWalkJavascriptResultHandlerInternal(this, id);
604             mXWalkUIClient.onJavascriptModalDialog(mXWalkView,
605                     XWalkUIClientInternal.JavascriptMessageTypeInternal.JAVASCRIPT_ALERT,
606                     url, message, "", result);
607         }
608     }
609
610     @CalledByNative
611     private void handleJsConfirm(String url, String message, int id) {
612         if (isOwnerActivityRunning()) {
613             XWalkJavascriptResultHandlerInternal result =
614                     new XWalkJavascriptResultHandlerInternal(this, id);
615             mXWalkUIClient.onJavascriptModalDialog(mXWalkView,
616                     XWalkUIClientInternal.JavascriptMessageTypeInternal.JAVASCRIPT_CONFIRM,
617                     url, message, "", result);
618         }
619     }
620
621     @CalledByNative
622     private void handleJsPrompt(String url, String message, String defaultValue, int id) {
623         if (isOwnerActivityRunning()) {
624             XWalkJavascriptResultHandlerInternal result =
625                     new XWalkJavascriptResultHandlerInternal(this, id);
626             mXWalkUIClient.onJavascriptModalDialog(mXWalkView,
627                     XWalkUIClientInternal.JavascriptMessageTypeInternal.JAVASCRIPT_PROMPT,
628                     url, message, defaultValue, result);
629         }
630     }
631
632     @CalledByNative
633     private void handleJsBeforeUnload(String url, String message, int id) {
634         if (isOwnerActivityRunning()) {
635             XWalkJavascriptResultHandlerInternal result =
636                     new XWalkJavascriptResultHandlerInternal(this, id);
637             mXWalkUIClient.onJavascriptModalDialog(mXWalkView,
638                     XWalkUIClientInternal.JavascriptMessageTypeInternal.JAVASCRIPT_BEFOREUNLOAD,
639                     url, message, "", result);
640         }
641     }
642
643     @CalledByNative
644     private void updateNotificationIcon(int notificationId, Bitmap icon) {
645         mNotificationService.updateNotificationIcon(notificationId, icon);
646     }
647
648     @CalledByNative
649     private void showNotification(String title, String message, String replaceId,
650             int notificationId) {
651         mNotificationService.showNotification(
652                 title, message, replaceId, notificationId);
653     }
654
655     @CalledByNative
656     private void cancelNotification(int notificationId) {
657         mNotificationService.cancelNotification(notificationId);
658     }
659
660     void confirmJsResult(int id, String prompt) {
661         if (mNativeContentsClientBridge == 0) return;
662         nativeConfirmJsResult(mNativeContentsClientBridge, id, prompt);
663     }
664
665     void cancelJsResult(int id) {
666         if (mNativeContentsClientBridge == 0) return;
667         nativeCancelJsResult(mNativeContentsClientBridge, id);
668     }
669
670     void exitFullscreen(long nativeWebContents) {
671         if (mNativeContentsClientBridge == 0) return;
672         nativeExitFullscreen(mNativeContentsClientBridge, nativeWebContents);
673     }
674
675     public void notificationDisplayed(int id) {
676         if (mNativeContentsClientBridge == 0) return;
677         nativeNotificationDisplayed(mNativeContentsClientBridge, id);
678     }
679
680     public void notificationError(int id) {
681         if (mNativeContentsClientBridge == 0) return;
682         nativeNotificationError(mNativeContentsClientBridge, id);
683     }
684
685     public void notificationClicked(int id) {
686         if (mNativeContentsClientBridge == 0) return;
687         nativeNotificationClicked(mNativeContentsClientBridge, id);
688     }
689
690     public void notificationClosed(int id, boolean byUser) {
691         if (mNativeContentsClientBridge == 0) return;
692         nativeNotificationClosed(mNativeContentsClientBridge, id, byUser);
693     }
694
695     void setDownloadListener(DownloadListener listener) {
696         mDownloadListener = listener;
697     }
698
699     // Implement ContentViewDownloadDelegate methods.
700     public void requestHttpGetDownload(DownloadInfo downloadInfo) {
701         if (mDownloadListener != null) {
702             mDownloadListener.onDownloadStart(downloadInfo.getUrl(), downloadInfo.getUserAgent(),
703             downloadInfo.getContentDisposition(), downloadInfo.getMimeType(), downloadInfo.getContentLength());
704         }
705     }
706
707     public void onDownloadStarted(String filename, String mimeType) {
708     }
709
710     public void onDangerousDownload(String filename, int downloadId) {
711     }
712
713     @CalledByNative
714     public void onWebLayoutPageScaleFactorChanged(float pageScaleFactor) {
715         if (mPageScaleFactor == pageScaleFactor) return;
716
717         float oldPageScaleFactor = mPageScaleFactor;
718         mPageScaleFactor = pageScaleFactor;
719         onScaleChanged(oldPageScaleFactor, mPageScaleFactor);
720     }
721
722     @CalledByNative
723     public void onIconAvailable(String url) {
724         Message m = mUiThreadHandler.obtainMessage(NEW_ICON_DOWNLOAD, url);
725         mXWalkUIClient.onIconAvailable(mXWalkView, url, m);
726     }
727
728     @CalledByNative
729     public void onReceivedIcon(String url, Bitmap icon) {
730         mXWalkUIClient.onReceivedIcon(mXWalkView, url, icon);
731     }
732
733     //--------------------------------------------------------------------------------------------
734     //  Native methods
735     //--------------------------------------------------------------------------------------------
736     private native void nativeProceedSslError(long nativeXWalkContentsClientBridge,
737             boolean proceed, int id);
738
739     private native void nativeConfirmJsResult(long nativeXWalkContentsClientBridge, int id,
740             String prompt);
741     private native void nativeCancelJsResult(long nativeXWalkContentsClientBridge, int id);
742     private native void nativeExitFullscreen(long nativeXWalkContentsClientBridge, long nativeWebContents);
743     private native void nativeNotificationDisplayed(long nativeXWalkContentsClientBridge, int id);
744     private native void nativeNotificationError(long nativeXWalkContentsClientBridge, int id);
745     private native void nativeNotificationClicked(long nativeXWalkContentsClientBridge, int id);
746     private native void nativeNotificationClosed(long nativeXWalkContentsClientBridge, int id, boolean byUser);
747     private native void nativeOnFilesSelected(long nativeXWalkContentsClientBridge,
748             int processId, int renderId, int mode_flags, String filepath, String displayName);
749     private native void nativeOnFilesNotSelected(long nativeXWalkContentsClientBridge,
750             int processId, int renderId, int mode_flags);
751     private native void nativeDownloadIcon(long nativeXWalkContentsClientBridge, String url);
752 }