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.
6 package org.xwalk.core;
8 import android.content.Intent;
9 import android.graphics.Bitmap;
10 import android.graphics.Picture;
11 import android.net.http.SslCertificate;
12 import android.net.http.SslError;
13 import android.os.Message;
14 import android.util.Log;
15 import android.view.KeyEvent;
16 import android.view.View;
17 import android.webkit.ConsoleMessage;
18 import android.webkit.ValueCallback;
19 import android.webkit.WebResourceResponse;
21 import org.chromium.base.CalledByNative;
22 import org.chromium.base.JNINamespace;
23 import org.chromium.base.ThreadUtils;
24 import org.chromium.components.navigation_interception.InterceptNavigationDelegate;
25 import org.chromium.components.navigation_interception.NavigationParams;
26 import org.chromium.content.browser.ContentVideoViewClient;
27 import org.chromium.content.browser.ContentViewDownloadDelegate;
28 import org.chromium.content.browser.DownloadInfo;
30 // Help bridge callback in XWalkContentsClient to XWalkViewClient and
31 // XWalkWebChromeClient; Also handle the JNI conmmunication logic.
32 @JNINamespace("xwalk")
33 class XWalkContentsClientBridge extends XWalkContentsClient
34 implements ContentViewDownloadDelegate {
36 private XWalkView mXWalkView;
37 private XWalkUIClient mXWalkUIClient;
38 private XWalkResourceClient mXWalkResourceClient;
39 private XWalkClient mXWalkClient;
40 private XWalkWebChromeClient mXWalkWebChromeClient;
41 private Bitmap mFavicon;
42 private DownloadListener mDownloadListener;
43 private InterceptNavigationDelegate mInterceptNavigationDelegate;
44 private PageLoadListener mPageLoadListener;
45 private XWalkNavigationHandler mNavigationHandler;
46 private XWalkNotificationService mNotificationService;
47 private boolean mIsFullscreen = false;
49 // The native peer of the object
50 private int mNativeContentsClientBridge;
52 private class InterceptNavigationDelegateImpl implements InterceptNavigationDelegate {
53 private XWalkContentsClient mContentsClient;
55 public InterceptNavigationDelegateImpl(XWalkContentsClient client) {
56 mContentsClient = client;
59 public boolean shouldIgnoreNavigation(NavigationParams navigationParams) {
60 final String url = navigationParams.url;
61 boolean ignoreNavigation = shouldOverrideUrlLoading(url) ||
62 (mNavigationHandler != null &&
63 mNavigationHandler.handleNavigation(navigationParams));
65 if (!ignoreNavigation) {
66 // Post a message to UI thread to notify the page is starting to load.
67 mContentsClient.getCallbackHelper().postOnPageStarted(url);
70 return ignoreNavigation;
74 public XWalkContentsClientBridge(XWalkView xwView) {
77 mInterceptNavigationDelegate = new InterceptNavigationDelegateImpl(this);
80 public void setUIClient(XWalkUIClient client) {
81 // If it's null, use Crosswalk implementation.
83 mXWalkUIClient = client;
86 mXWalkUIClient = new XWalkUIClientImpl(mXWalkView.getContext(), mXWalkView);
89 public void setResourceClient(XWalkResourceClient client) {
90 // If it's null, use Crosswalk implementation.
92 mXWalkResourceClient = client;
95 mXWalkResourceClient = new XWalkResourceClientImpl(mXWalkView.getContext(), mXWalkView);
99 public void setXWalkWebChromeClient(XWalkWebChromeClient client) {
100 // If it's null, use Crosswalk implementation.
101 if (client == null) return;
102 client.setContentsClient(this);
103 mXWalkWebChromeClient = client;
106 public XWalkWebChromeClient getXWalkWebChromeClient() {
107 return mXWalkWebChromeClient;
110 public void setXWalkClient(XWalkClient client) {
111 mXWalkClient = client;
114 public void setNavigationHandler(XWalkNavigationHandler handler) {
115 mNavigationHandler = handler;
118 void registerPageLoadListener(PageLoadListener listener) {
119 mPageLoadListener = listener;
122 public void setNotificationService(XWalkNotificationService service) {
123 if (mNotificationService != null) mNotificationService.shutdown();
124 mNotificationService = service;
125 if (mNotificationService != null) mNotificationService.setBridge(this);
128 public boolean onNewIntent(Intent intent) {
129 return mNotificationService.maybeHandleIntent(intent);
132 public InterceptNavigationDelegate getInterceptNavigationDelegate() {
133 return mInterceptNavigationDelegate;
136 // TODO(Xingnan): All the empty functions need to be implemented.
138 public boolean shouldOverrideUrlLoading(String url) {
139 if (mXWalkClient != null && mXWalkView != null)
140 return mXWalkClient.shouldOverrideUrlLoading(mXWalkView, url);
145 public void onUnhandledKeyEvent(KeyEvent event) {
149 public void getVisitedHistory(ValueCallback<String[]> callback) {
153 public void doUpdateVisitedHistory(String url, boolean isReload) {
157 public void onProgressChanged(int progress) {
158 mXWalkResourceClient.onProgressChanged(mXWalkView, progress);
162 public WebResourceResponse shouldInterceptRequest(String url) {
163 return mXWalkResourceClient.shouldInterceptLoadRequest(mXWalkView, url);
166 public void onResourceLoadStarted(String url) {
167 mXWalkResourceClient.onLoadStarted(mXWalkView, url);
170 public void onResourceLoadFinished(String url) {
171 // TODO(yongsheng): this method is never called. Where should it be hooked?
172 mXWalkResourceClient.onLoadFinished(mXWalkView, url);
176 public void onLoadResource(String url) {
177 if (mXWalkClient != null && mXWalkView != null) {
178 mXWalkClient.onLoadResource(mXWalkView, url);
183 public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
188 public void onReceivedHttpAuthRequest(XWalkHttpAuthHandler handler, String host, String realm) {
189 if (mXWalkClient != null && mXWalkView != null) {
190 mXWalkClient.onReceivedHttpAuthRequest(mXWalkView, handler, host, realm);
195 public void onReceivedSslError(ValueCallback<Boolean> callback, SslError error) {
196 if (mXWalkClient != null && mXWalkView != null) {
197 mXWalkClient.onReceivedSslError(mXWalkView, callback, error);
202 public void onReceivedLoginRequest(String realm, String account, String args) {
206 public void onGeolocationPermissionsShowPrompt(String origin,
207 XWalkGeolocationPermissions.Callback callback) {
208 if (mXWalkWebChromeClient != null) {
209 mXWalkWebChromeClient.onGeolocationPermissionsShowPrompt(origin, callback);
214 public void onGeolocationPermissionsHidePrompt() {
215 if (mXWalkWebChromeClient != null) {
216 mXWalkWebChromeClient.onGeolocationPermissionsHidePrompt();
221 public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches,
222 boolean isDoneCounting) {
226 public void onNewPicture(Picture picture) {
230 public void onPageStarted(String url) {
231 if (mXWalkClient != null && mXWalkView != null) {
232 mXWalkClient.onPageStarted(mXWalkView, url);
237 public void onPageFinished(String url) {
238 if (mPageLoadListener != null) mPageLoadListener.onPageFinished(url);
239 if (mXWalkClient != null && mXWalkView != null) {
240 mXWalkClient.onPageFinished(mXWalkView, url);
245 public void onReceivedError(int errorCode, String description, String failingUrl) {
246 mXWalkResourceClient.onReceivedLoadError(mXWalkView, errorCode, description, failingUrl);
250 public void onRendererUnresponsive() {
251 if (mXWalkClient != null && mXWalkView != null) {
252 mXWalkClient.onRendererUnresponsive(mXWalkView);
257 public void onRendererResponsive() {
258 if (mXWalkClient != null && mXWalkView != null) {
259 mXWalkClient.onRendererResponsive(mXWalkView);
264 public void onFormResubmission(Message dontResend, Message resend) {
265 dontResend.sendToTarget();
269 public void onDownloadStart(String url,
271 String contentDisposition,
273 long contentLength) {
277 public boolean onCreateWindow(boolean isDialog, boolean isUserGesture) {
282 public void onRequestFocus() {
283 mXWalkUIClient.onRequestFocus(mXWalkView);
287 public void onCloseWindow() {
288 mXWalkUIClient.onJavascriptCloseWindow(mXWalkView);
292 public void onReceivedTouchIconUrl(String url, boolean precomposed) {
293 if (mXWalkWebChromeClient != null && mXWalkView != null) {
294 mXWalkWebChromeClient.onReceivedTouchIconUrl(mXWalkView, url, precomposed);
299 public void onReceivedIcon(Bitmap bitmap) {
300 if (mXWalkWebChromeClient != null && mXWalkView != null) {
301 mXWalkWebChromeClient.onReceivedIcon(mXWalkView, bitmap);
307 public void onShowCustomView(View view, XWalkWebChromeClient.CustomViewCallback callback) {
308 if (mXWalkWebChromeClient != null) {
309 mXWalkWebChromeClient.onShowCustomView(view, callback);
314 public void onHideCustomView() {
315 if (mXWalkWebChromeClient != null) {
316 mXWalkWebChromeClient.onHideCustomView();
321 public void onScaleChangedScaled(float oldScale, float newScale) {
322 mXWalkUIClient.onScaleChanged(mXWalkView, oldScale, newScale);
326 protected View getVideoLoadingProgressView() {
327 if (mXWalkWebChromeClient != null)
328 return mXWalkWebChromeClient.getVideoLoadingProgressView();
333 public Bitmap getDefaultVideoPoster() {
338 public void didFinishLoad(String url) {
342 public void onTitleChanged(String title) {
343 if (mXWalkWebChromeClient != null && mXWalkView != null) {
344 mXWalkWebChromeClient.onReceivedTitle(mXWalkView, title);
349 public void onToggleFullscreen(boolean enterFullscreen) {
350 mIsFullscreen = enterFullscreen;
351 mXWalkUIClient.onFullscreenToggled(mXWalkView, enterFullscreen);
355 public boolean hasEnteredFullscreen() {
356 return mIsFullscreen;
360 public ContentVideoViewClient getContentVideoViewClient() {
361 return new XWalkContentVideoViewClient(this, mXWalkView.getActivity());
364 // Used by the native peer to set/reset a weak ref to the native peer.
366 private void setNativeContentsClientBridge(int nativeContentsClientBridge) {
367 mNativeContentsClientBridge = nativeContentsClientBridge;
370 // If returns false, the request is immediately canceled, and any call to proceedSslError
371 // has no effect. If returns true, the request should be canceled or proceeded using
372 // proceedSslError().
373 // Unlike the webview classic, we do not keep keep a database of certificates that
374 // are allowed by the user, because this functionality is already handled via
375 // ssl_policy in native layers.
377 private boolean allowCertificateError(int certError, byte[] derBytes, final String url,
379 final SslCertificate cert = SslUtil.getCertificateFromDerBytes(derBytes);
381 // if the certificate or the client is null, cancel the request
384 final SslError sslError = SslUtil.sslErrorFromNetErrorCode(certError, cert, url);
385 ValueCallback<Boolean> callback = new ValueCallback<Boolean>() {
387 public void onReceiveValue(Boolean value) {
388 proceedSslError(value.booleanValue(), id);
391 onReceivedSslError(callback, sslError);
395 private void proceedSslError(boolean proceed, int id) {
396 if (mNativeContentsClientBridge == 0) return;
397 nativeProceedSslError(mNativeContentsClientBridge, proceed, id);
401 private void handleJsAlert(String url, String message, int id) {
402 XWalkJavascriptResultHandler result = new XWalkJavascriptResultHandler(this, id);
403 mXWalkUIClient.onJavascriptModalDialog(mXWalkView,
404 XWalkUIClient.JavascriptMessageType.JAVASCRIPT_ALERT, url, message, "", result);
408 private void handleJsConfirm(String url, String message, int id) {
409 XWalkJavascriptResultHandler result = new XWalkJavascriptResultHandler(this, id);
410 mXWalkUIClient.onJavascriptModalDialog(mXWalkView,
411 XWalkUIClient.JavascriptMessageType.JAVASCRIPT_CONFIRM, url, message, "", result);
415 private void handleJsPrompt(String url, String message, String defaultValue, int id) {
416 XWalkJavascriptResultHandler result = new XWalkJavascriptResultHandler(this, id);
417 mXWalkUIClient.onJavascriptModalDialog(mXWalkView,
418 XWalkUIClient.JavascriptMessageType.JAVASCRIPT_PROMPT, url, message, defaultValue,
423 private void handleJsBeforeUnload(String url, String message, int id) {
424 XWalkJavascriptResultHandler result = new XWalkJavascriptResultHandler(this, id);
425 mXWalkUIClient.onJavascriptModalDialog(mXWalkView,
426 XWalkUIClient.JavascriptMessageType.JAVASCRIPT_BEFOREUNLOAD, url, message, "",
431 // TODO(yongsheng): Native side should call file chooser.
432 public void runFileChooser(final int processId, final int renderId, final int mode_flags,
433 String acceptTypes, String title, String defaultFilename, boolean capture) {
434 // TODO(yongsheng): Implement it.
435 // mXWalkUIClient.openFileChooser(mXWalkView, ...);
436 // See https://crosswalk-project.org/jira/browse/XWALK-1241.
440 private void updateNotificationIcon(int notificationId, Bitmap icon) {
441 mNotificationService.updateNotificationIcon(notificationId, icon);
445 private void showNotification(String title, String message, String replaceId,
446 int notificationId, int processId, int routeId) {
447 // FIXME(wang16): use replaceId to replace exist notification. It happens when
448 // a notification with same name and tag fires.
449 mNotificationService.showNotification(
450 title, message, notificationId, processId, routeId);
454 private void cancelNotification(int notificationId, int processId, int routeId) {
455 mNotificationService.cancelNotification(notificationId, processId, routeId);
458 void confirmJsResult(int id, String prompt) {
459 if (mNativeContentsClientBridge == 0) return;
460 nativeConfirmJsResult(mNativeContentsClientBridge, id, prompt);
463 void cancelJsResult(int id) {
464 if (mNativeContentsClientBridge == 0) return;
465 nativeCancelJsResult(mNativeContentsClientBridge, id);
468 void exitFullscreen(int nativeWebContents) {
469 if (mNativeContentsClientBridge == 0) return;
470 nativeExitFullscreen(mNativeContentsClientBridge, nativeWebContents);
473 public void notificationDisplayed(int id, int processId, int routeId) {
474 if (mNativeContentsClientBridge == 0) return;
475 nativeNotificationDisplayed(mNativeContentsClientBridge, id, processId, routeId);
478 public void notificationError(int id, String error, int processId, int routeId) {
479 if (mNativeContentsClientBridge == 0) return;
480 nativeNotificationError(mNativeContentsClientBridge, id, error, processId, routeId);
483 public void notificationClicked(int id, int processId, int routeId) {
484 if (mNativeContentsClientBridge == 0) return;
485 nativeNotificationClicked(mNativeContentsClientBridge, id, processId, routeId);
488 public void notificationClosed(int id, boolean byUser, int processId, int routeId) {
489 if (mNativeContentsClientBridge == 0) return;
490 nativeNotificationClosed(mNativeContentsClientBridge, id, byUser, processId, routeId);
493 void setDownloadListener(DownloadListener listener) {
494 mDownloadListener = listener;
497 // Implement ContentViewDownloadDelegate methods.
498 public void requestHttpGetDownload(DownloadInfo downloadInfo) {
499 if (mDownloadListener != null) {
500 mDownloadListener.onDownloadStart(downloadInfo.getUrl(), downloadInfo.getUserAgent(),
501 downloadInfo.getContentDisposition(), downloadInfo.getMimeType(), downloadInfo.getContentLength());
505 public void onDownloadStarted(String filename, String mimeType) {
508 public void onDangerousDownload(String filename, int downloadId) {
511 //--------------------------------------------------------------------------------------------
513 //--------------------------------------------------------------------------------------------
514 private native void nativeProceedSslError(int nativeXWalkContentsClientBridge,
515 boolean proceed, int id);
517 private native void nativeConfirmJsResult(int nativeXWalkContentsClientBridge, int id,
519 private native void nativeCancelJsResult(int nativeXWalkContentsClientBridge, int id);
520 private native void nativeExitFullscreen(int nativeXWalkContentsClientBridge, int nativeWebContents);
521 private native void nativeNotificationDisplayed(int nativeXWalkContentsClientBridge, int id,
522 int processId, int routeId);
523 private native void nativeNotificationError(int nativeXWalkContentsClientBridge, int id,
524 String error, int processId, int routeId);
525 private native void nativeNotificationClicked(int nativeXWalkContentsClientBridge, int id,
526 int processId, int routeId);
527 private native void nativeNotificationClosed(int nativeXWalkContentsClientBridge, int id,
528 boolean byUser, int processId, int routeId);