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