blob: a3070ba41a22384e6a9e56fac723abc9e0d9cb79 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.android_webview;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.graphics.Picture;
import android.graphics.Rect;
import android.graphics.RectF;
import android.net.http.SslError;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.webkit.ConsoleMessage;
import android.webkit.GeolocationPermissions;
import android.webkit.SslErrorHandler;
import android.webkit.URLUtil;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import org.chromium.base.ThreadUtils;
import org.chromium.content.browser.ContentVideoView;
import org.chromium.content.browser.ContentVideoViewClient;
import org.chromium.content.browser.ContentVideoViewControls;
import org.chromium.content.browser.ContentViewClient;
import org.chromium.content.browser.ContentViewCore;
import org.chromium.content.browser.WebContentsObserverAndroid;
import org.chromium.net.NetError;
/**
* Base-class that an AwContents embedder derives from to receive callbacks.
* This extends ContentViewClient, as in many cases we want to pass-thru ContentViewCore
* callbacks right to our embedder, and this setup facilities that.
* For any other callbacks we need to make transformations of (e.g. adapt parameters
* or perform filtering) we can provide final overrides for methods here, and then introduce
* new abstract methods that the our own client must implement.
* i.e.: all methods in this class should either be final, or abstract.
*/
public abstract class AwContentsClient {
private static final String TAG = "AwContentsClient";
private final AwContentsClientCallbackHelper mCallbackHelper =
new AwContentsClientCallbackHelper(this);
private AwWebContentsObserver mWebContentsObserver;
private AwContentViewClient mContentViewClient = new AwContentViewClient();
// Last background color reported from the renderer. Holds the sentinal value INVALID_COLOR
// if not valid.
private int mCachedRendererBackgroundColor = INVALID_COLOR;
private static final int INVALID_COLOR = 0;
private AwSettings mSettings;
class AwWebContentsObserver extends WebContentsObserverAndroid {
public AwWebContentsObserver(ContentViewCore contentViewCore) {
super(contentViewCore);
}
@Override
public void didStopLoading(final String url) {
ThreadUtils.postOnUiThread(new Runnable() {
@Override
public void run() {
AwContentsClient.this.onPageFinished(url);
}
});
}
@Override
public void didFailLoad(boolean isProvisionalLoad,
boolean isMainFrame, int errorCode, String description, String failingUrl) {
if (errorCode == NetError.ERR_ABORTED) {
// This error code is generated for the following reasons:
// - WebView.stopLoading is called,
// - the navigation is intercepted by the embedder via shouldOverrideNavigation.
//
// The Android WebView does not notify the embedder of these situations using this
// error code with the WebViewClient.onReceivedError callback.
return;
}
if (!isMainFrame) {
// The Android WebView does not notify the embedder of sub-frame failures.
return;
}
AwContentsClient.this.onReceivedError(
ErrorCodeConversionHelper.convertErrorCode(errorCode), description, failingUrl);
}
@Override
public void didNavigateAnyFrame(String url, String baseUrl, boolean isReload) {
AwContentsClient.this.doUpdateVisitedHistory(url, isReload);
}
}
private class AwContentViewClient extends ContentViewClient {
@Override
public void onBackgroundColorChanged(int color) {
// Avoid storing the sentinal INVALID_COLOR (note that both 0 and 1 are both
// fully transparent so this transpose makes no visible difference).
mCachedRendererBackgroundColor = color == INVALID_COLOR ? 1 : color;
}
@Override
public void onStartContentIntent(Context context, String contentUrl) {
// Callback when detecting a click on a content link.
AwContentsClient.this.shouldOverrideUrlLoading(contentUrl);
}
@Override
public void onRendererCrash(boolean crashedWhileOomProtected) {
// This is not possible so long as the webview is run single process!
throw new RuntimeException("Renderer crash reported.");
}
@Override
public void onUpdateTitle(String title) {
AwContentsClient.this.onReceivedTitle(title);
}
@Override
public boolean shouldOverrideKeyEvent(KeyEvent event) {
return AwContentsClient.this.shouldOverrideKeyEvent(event);
}
@Override
final public ContentVideoViewClient getContentVideoViewClient() {
return new AwContentVideoViewClient();
}
@Override
public boolean shouldBlockMediaRequest(String url) {
return mSettings != null ?
mSettings.getBlockNetworkLoads() && URLUtil.isNetworkUrl(url) : true;
}
}
final void installWebContentsObserver(ContentViewCore contentViewCore, AwSettings settings) {
if (mWebContentsObserver != null) {
mWebContentsObserver.detachFromWebContents();
}
mWebContentsObserver = new AwWebContentsObserver(contentViewCore);
mSettings = settings;
}
private class AwContentVideoViewClient implements ContentVideoViewClient {
@Override
public void onShowCustomView(View view) {
WebChromeClient.CustomViewCallback cb = new WebChromeClient.CustomViewCallback() {
@Override
public void onCustomViewHidden() {
ContentVideoView contentVideoView = ContentVideoView.getContentVideoView();
if (contentVideoView != null)
contentVideoView.exitFullscreen(false);
}
};
AwContentsClient.this.onShowCustomView(view, cb);
}
@Override
public void onDestroyContentVideoView() {
AwContentsClient.this.onHideCustomView();
}
@Override
public View getVideoLoadingProgressView() {
return AwContentsClient.this.getVideoLoadingProgressView();
}
@Override
public ContentVideoViewControls createControls() {
return null;
}
}
final AwContentsClientCallbackHelper getCallbackHelper() {
return mCallbackHelper;
}
final ContentViewClient getContentViewClient() {
return mContentViewClient;
}
final int getCachedRendererBackgroundColor() {
assert isCachedRendererBackgroundColorValid();
return mCachedRendererBackgroundColor;
}
final boolean isCachedRendererBackgroundColorValid() {
return mCachedRendererBackgroundColor != INVALID_COLOR;
}
//--------------------------------------------------------------------------------------------
// WebView specific methods that map directly to WebViewClient / WebChromeClient
//--------------------------------------------------------------------------------------------
public static class FileChooserParams {
public int mode;
public String acceptTypes;
public String title;
public String defaultFilename;
public boolean capture;
}
public abstract void getVisitedHistory(ValueCallback<String[]> callback);
public abstract void doUpdateVisitedHistory(String url, boolean isReload);
public abstract void onProgressChanged(int progress);
public abstract InterceptedRequestData shouldInterceptRequest(String url);
public abstract boolean shouldOverrideKeyEvent(KeyEvent event);
public abstract boolean shouldOverrideUrlLoading(String url);
public abstract void onLoadResource(String url);
public abstract void onUnhandledKeyEvent(KeyEvent event);
public abstract boolean onConsoleMessage(ConsoleMessage consoleMessage);
public abstract void onReceivedHttpAuthRequest(AwHttpAuthHandler handler,
String host, String realm);
public abstract void onReceivedSslError(ValueCallback<Boolean> callback, SslError error);
public abstract void onReceivedLoginRequest(String realm, String account, String args);
public abstract void onFormResubmission(Message dontResend, Message resend);
public abstract void onDownloadStart(String url, String userAgent, String contentDisposition,
String mimeType, long contentLength);
// TODO(joth): Make abstract once this has rolled in downstream.
public /*abstract*/ void showFileChooser(ValueCallback<String[]> uploadFilePathsCallback,
FileChooserParams fileChooserParams) { }
public abstract void onGeolocationPermissionsShowPrompt(String origin,
GeolocationPermissions.Callback callback);
public abstract void onGeolocationPermissionsHidePrompt();
public abstract void onScaleChangedScaled(float oldScale, float newScale);
protected abstract void handleJsAlert(String url, String message, JsResultReceiver receiver);
protected abstract void handleJsBeforeUnload(String url, String message,
JsResultReceiver receiver);
protected abstract void handleJsConfirm(String url, String message, JsResultReceiver receiver);
protected abstract void handleJsPrompt(String url, String message, String defaultValue,
JsPromptResultReceiver receiver);
protected abstract boolean onCreateWindow(boolean isDialog, boolean isUserGesture);
protected abstract void onCloseWindow();
public abstract void onReceivedTouchIconUrl(String url, boolean precomposed);
public abstract void onReceivedIcon(Bitmap bitmap);
public abstract void onReceivedTitle(String title);
protected abstract void onRequestFocus();
protected abstract View getVideoLoadingProgressView();
public abstract void onPageStarted(String url);
public abstract void onPageFinished(String url);
public abstract void onReceivedError(int errorCode, String description, String failingUrl);
// TODO (michaelbai): Remove this method once the same method remove from
// WebViewContentsClientAdapter.
public void onShowCustomView(View view,
int requestedOrientation, WebChromeClient.CustomViewCallback callback) {
}
// TODO (michaelbai): This method should be abstract, having empty body here
// makes the merge to the Android easy.
public void onShowCustomView(View view, WebChromeClient.CustomViewCallback callback) {
onShowCustomView(view, ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED, callback);
}
public abstract void onHideCustomView();
public abstract Bitmap getDefaultVideoPoster();
//--------------------------------------------------------------------------------------------
// Other WebView-specific methods
//--------------------------------------------------------------------------------------------
//
public abstract void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches,
boolean isDoneCounting);
/**
* Called whenever there is a new content picture available.
* @param picture New picture.
*/
public abstract void onNewPicture(Picture picture);
}