blob: a485bd2bbacf0fc391963fe6175d6efd0588e0d7 [file] [log] [blame]
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.webview.chromium;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Picture;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.net.http.SslCertificate;
import android.os.Build;
import android.os.Bundle;
import android.os.Looper;
import android.os.Message;
import android.print.PrintDocumentAdapter;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;
import android.view.HardwareCanvas;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.MeasureSpec;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeProvider;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.webkit.DownloadListener;
import android.webkit.FindActionModeCallback;
import android.webkit.JavascriptInterface;
import android.webkit.ValueCallback;
import android.webkit.WebBackForwardList;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.webkit.WebViewFactory;
import android.webkit.WebViewProvider;
import android.webkit.WebChromeClient.CustomViewCallback;
import android.widget.TextView;
import org.chromium.android_webview.AwBrowserContext;
import org.chromium.android_webview.AwContents;
import org.chromium.android_webview.AwContentsStatics;
import org.chromium.android_webview.AwLayoutSizer;
import org.chromium.android_webview.AwSettings;
import org.chromium.android_webview.AwPrintDocumentAdapter;
import org.chromium.base.ThreadUtils;
import org.chromium.content.browser.LoadUrlParams;
import org.chromium.net.NetworkChangeNotifier;
import java.io.BufferedWriter;
import java.io.File;
import java.lang.annotation.Annotation;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.HashMap;
import java.util.Map;
import java.util.Queue;
/**
* This class is the delegate to which WebViewProxy forwards all API calls.
*
* Most of the actual functionality is implemented by AwContents (or ContentViewCore within
* it). This class also contains WebView-specific APIs that require the creation of other
* adapters (otherwise org.chromium.content would depend on the webview.chromium package)
* and a small set of no-op deprecated APIs.
*/
class WebViewChromium implements WebViewProvider,
WebViewProvider.ScrollDelegate, WebViewProvider.ViewDelegate {
private class WebViewChromiumRunQueue {
public WebViewChromiumRunQueue() {
mQueue = new ConcurrentLinkedQueue<Runnable>();
}
public void addTask(Runnable task) {
mQueue.add(task);
if (mFactory.hasStarted()) {
ThreadUtils.runOnUiThread(new Runnable() {
@Override
public void run() {
drainQueue();
}
});
}
}
public void drainQueue() {
if (mQueue == null || mQueue.isEmpty()) {
return;
}
Runnable task = mQueue.poll();
while(task != null) {
task.run();
task = mQueue.poll();
}
}
private Queue<Runnable> mQueue;
}
private WebViewChromiumRunQueue mRunQueue;
private static final String TAG = WebViewChromium.class.getSimpleName();
// The WebView that this WebViewChromium is the provider for.
WebView mWebView;
// Lets us access protected View-derived methods on the WebView instance we're backing.
WebView.PrivateAccess mWebViewPrivate;
// The client adapter class.
private WebViewContentsClientAdapter mContentsClientAdapter;
// Variables for functionality provided by this adapter ---------------------------------------
private ContentSettingsAdapter mWebSettings;
// The WebView wrapper for ContentViewCore and required browser compontents.
private AwContents mAwContents;
// Non-null if this webview is using the GL accelerated draw path.
private DrawGLFunctor mGLfunctor;
private final WebView.HitTestResult mHitTestResult;
private final int mAppTargetSdkVersion;
private WebViewChromiumFactoryProvider mFactory;
private static boolean sRecordWholeDocumentEnabledByApi = false;
static void enableSlowWholeDocumentDraw() {
sRecordWholeDocumentEnabledByApi = true;
}
// This does not touch any global / non-threadsafe state, but note that
// init is ofter called right after and is NOT threadsafe.
public WebViewChromium(WebViewChromiumFactoryProvider factory, WebView webView,
WebView.PrivateAccess webViewPrivate) {
mWebView = webView;
mWebViewPrivate = webViewPrivate;
mHitTestResult = new WebView.HitTestResult();
mAppTargetSdkVersion = mWebView.getContext().getApplicationInfo().targetSdkVersion;
mFactory = factory;
mRunQueue = new WebViewChromiumRunQueue();
String webViewAssetPath = WebViewFactory.getLoadedPackageInfo().applicationInfo.sourceDir;
mWebView.getContext().getAssets().addAssetPath(webViewAssetPath);
}
static void completeWindowCreation(WebView parent, WebView child) {
AwContents parentContents = ((WebViewChromium) parent.getWebViewProvider()).mAwContents;
AwContents childContents =
child == null ? null : ((WebViewChromium) child.getWebViewProvider()).mAwContents;
parentContents.supplyContentsForPopup(childContents);
}
private <T> T runBlockingFuture(FutureTask<T> task) {
if (!mFactory.hasStarted()) throw new RuntimeException("Must be started before we block!");
if (ThreadUtils.runningOnUiThread()) {
throw new IllegalStateException("This method should only be called off the UI thread");
}
mRunQueue.addTask(task);
try {
return task.get(4, TimeUnit.SECONDS);
} catch (java.util.concurrent.TimeoutException e) {
throw new RuntimeException("Probable deadlock detected due to WebView API being called "
+ "on incorrect thread while the UI thread is blocked.", e);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
// We have a 4 second timeout to try to detect deadlocks to detect and aid in debuggin
// deadlocks.
// Do not call this method while on the UI thread!
private void runVoidTaskOnUiThreadBlocking(Runnable r) {
FutureTask<Void> task = new FutureTask<Void>(r, null);
runBlockingFuture(task);
}
private <T> T runOnUiThreadBlocking(Callable<T> c) {
return runBlockingFuture(new FutureTask<T>(c));
}
// WebViewProvider methods --------------------------------------------------------------------
@Override
// BUG=6790250 |javaScriptInterfaces| was only ever used by the obsolete DumpRenderTree
// so is ignored. TODO: remove it from WebViewProvider.
public void init(final Map<String, Object> javaScriptInterfaces,
final boolean privateBrowsing) {
if (privateBrowsing) {
mFactory.startYourEngines(true);
final String msg = "Private browsing is not supported in WebView.";
if (mAppTargetSdkVersion >= Build.VERSION_CODES.KITKAT) {
throw new IllegalArgumentException(msg);
} else {
Log.w(TAG, msg);
TextView warningLabel = new TextView(mWebView.getContext());
warningLabel.setText(mWebView.getContext().getString(
R.string.webviewchromium_private_browsing_warning));
mWebView.addView(warningLabel);
}
}
// We will defer real initialization until we know which thread to do it on, unless:
// - we are on the main thread already (common case),
// - the app is targeting >= JB MR2, in which case checkThread enforces that all usage
// comes from a single thread. (Note in JB MR2 this exception was in WebView.java).
if (mAppTargetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
mFactory.startYourEngines(false);
checkThread();
} else if (!mFactory.hasStarted()) {
if (Looper.myLooper() == Looper.getMainLooper()) {
mFactory.startYourEngines(true);
}
}
final boolean isAccessFromFileURLsGrantedByDefault =
mAppTargetSdkVersion < Build.VERSION_CODES.JELLY_BEAN;
final boolean areLegacyQuirksEnabled =
mAppTargetSdkVersion < Build.VERSION_CODES.KITKAT;
mContentsClientAdapter = new WebViewContentsClientAdapter(mWebView);
mWebSettings = new ContentSettingsAdapter(new AwSettings(
mWebView.getContext(), isAccessFromFileURLsGrantedByDefault,
areLegacyQuirksEnabled));
if (mAppTargetSdkVersion <= Build.VERSION_CODES.KITKAT) {
mWebSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
// On KK and older versions we always allowed third party cookies.
mWebSettings.setAcceptThirdPartyCookies(true);
mWebSettings.getAwSettings().setZeroLayoutHeightDisablesViewportQuirk(true);
}
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
initForReal();
if (privateBrowsing) {
// Intentionally irreversibly disable the webview instance, so that private
// user data cannot leak through misuse of a non-privateBrowing WebView
// instance. Can't just null out mAwContents as we never null-check it
// before use.
destroy();
}
}
});
}
// Wrap Context so that we can use resources from the webview resource apk.
private static Context resourcesContextWrapper(final Context ctx) {
return new ContextWrapper(ctx) {
@Override
public ClassLoader getClassLoader() {
final ClassLoader appCl = getBaseContext().getClassLoader();
final ClassLoader webViewCl = this.getClass().getClassLoader();
return new ClassLoader() {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// First look in the WebViewProvider class loader.
try {
return webViewCl.loadClass(name);
} catch (ClassNotFoundException e) {
// Look in the app class loader; allowing it to throw ClassNotFoundException.
return appCl.loadClass(name);
}
}
};
}
@Override
public Object getSystemService(String name) {
if (name.equals(Context.LAYOUT_INFLATER_SERVICE)) {
LayoutInflater i = (LayoutInflater) getBaseContext().getSystemService(name);
return i.cloneInContext(this);
} else {
return getBaseContext().getSystemService(name);
}
}
};
}
private void initForReal() {
Context ctx = resourcesContextWrapper(mWebView.getContext());
mAwContents = new AwContents(mFactory.getBrowserContext(), mWebView, ctx,
new InternalAccessAdapter(), new WebViewNativeGLDelegate(),
mContentsClientAdapter, mWebSettings.getAwSettings());
if (mAppTargetSdkVersion >= Build.VERSION_CODES.KITKAT) {
// On KK and above, favicons are automatically downloaded as the method
// old apps use to enable that behavior is deprecated.
AwContents.setShouldDownloadFavicons();
}
AwContentsStatics.setRecordFullDocument(sRecordWholeDocumentEnabledByApi ||
mAppTargetSdkVersion < Build.VERSION_CODES.L);
if (mAppTargetSdkVersion <= Build.VERSION_CODES.KITKAT) {
// On KK and older versions, JavaScript objects injected via addJavascriptInterface
// were not inspectable.
mAwContents.disableJavascriptInterfacesInspection();
}
// TODO: This assumes AwContents ignores second Paint param.
mAwContents.setLayerType(mWebView.getLayerType(), null);
}
void startYourEngine() {
mRunQueue.drainQueue();
}
private RuntimeException createThreadException() {
return new IllegalStateException(
"Calling View methods on another thread than the UI thread.");
}
private boolean checkNeedsPost() {
boolean needsPost = !mFactory.hasStarted() || !ThreadUtils.runningOnUiThread();
if (!needsPost && mAwContents == null) {
throw new IllegalStateException(
"AwContents must be created if we are not posting!");
}
return needsPost;
}
// Intentionally not static, as no need to check thread on static methods
private void checkThread() {
if (!ThreadUtils.runningOnUiThread()) {
final RuntimeException threadViolation = createThreadException();
ThreadUtils.postOnUiThread(new Runnable() {
@Override
public void run() {
throw threadViolation;
}
});
throw createThreadException();
}
}
@Override
public void setHorizontalScrollbarOverlay(final boolean overlay) {
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
setHorizontalScrollbarOverlay(overlay);
}
});
return;
}
mAwContents.setHorizontalScrollbarOverlay(overlay);
}
@Override
public void setVerticalScrollbarOverlay(final boolean overlay) {
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
setVerticalScrollbarOverlay(overlay);
}
});
return;
}
mAwContents.setVerticalScrollbarOverlay(overlay);
}
@Override
public boolean overlayHorizontalScrollbar() {
mFactory.startYourEngines(false);
if (checkNeedsPost()) {
boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
@Override
public Boolean call() {
return overlayHorizontalScrollbar();
}
});
return ret;
}
return mAwContents.overlayHorizontalScrollbar();
}
@Override
public boolean overlayVerticalScrollbar() {
mFactory.startYourEngines(false);
if (checkNeedsPost()) {
boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
@Override
public Boolean call() {
return overlayVerticalScrollbar();
}
});
return ret;
}
return mAwContents.overlayVerticalScrollbar();
}
@Override
public int getVisibleTitleHeight() {
// This is deprecated in WebView and should always return 0.
return 0;
}
@Override
public SslCertificate getCertificate() {
mFactory.startYourEngines(true);
if (checkNeedsPost()) {
SslCertificate ret = runOnUiThreadBlocking(new Callable<SslCertificate>() {
@Override
public SslCertificate call() {
return getCertificate();
}
});
return ret;
}
return mAwContents.getCertificate();
}
@Override
public void setCertificate(SslCertificate certificate) {
// intentional no-op
}
@Override
public void savePassword(String host, String username, String password) {
// This is a deprecated API: intentional no-op.
}
@Override
public void setHttpAuthUsernamePassword(final String host, final String realm,
final String username, final String password) {
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
setHttpAuthUsernamePassword(host, realm, username, password);
}
});
return;
}
mAwContents.setHttpAuthUsernamePassword(host, realm, username, password);
}
@Override
public String[] getHttpAuthUsernamePassword(final String host, final String realm) {
mFactory.startYourEngines(true);
if (checkNeedsPost()) {
String[] ret = runOnUiThreadBlocking(new Callable<String[]>() {
@Override
public String[] call() {
return getHttpAuthUsernamePassword(host, realm);
}
});
return ret;
}
return mAwContents.getHttpAuthUsernamePassword(host, realm);
}
@Override
public void destroy() {
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
destroy();
}
});
return;
}
mAwContents.destroy();
if (mGLfunctor != null) {
mGLfunctor.destroy();
mGLfunctor = null;
}
}
@Override
public void setNetworkAvailable(final boolean networkUp) {
// Note that this purely toggles the JS navigator.online property.
// It does not in affect chromium or network stack state in any way.
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
setNetworkAvailable(networkUp);
}
});
return;
}
mAwContents.setNetworkAvailable(networkUp);
}
@Override
public WebBackForwardList saveState(final Bundle outState) {
mFactory.startYourEngines(true);
if (checkNeedsPost()) {
WebBackForwardList ret = runOnUiThreadBlocking(new Callable<WebBackForwardList>() {
@Override
public WebBackForwardList call() {
return saveState(outState);
}
});
return ret;
}
if (outState == null) return null;
if (!mAwContents.saveState(outState)) return null;
return copyBackForwardList();
}
@Override
public boolean savePicture(Bundle b, File dest) {
// Intentional no-op: hidden method on WebView.
return false;
}
@Override
public boolean restorePicture(Bundle b, File src) {
// Intentional no-op: hidden method on WebView.
return false;
}
@Override
public WebBackForwardList restoreState(final Bundle inState) {
mFactory.startYourEngines(true);
if (checkNeedsPost()) {
WebBackForwardList ret = runOnUiThreadBlocking(new Callable<WebBackForwardList>() {
@Override
public WebBackForwardList call() {
return restoreState(inState);
}
});
return ret;
}
if (inState == null) return null;
if (!mAwContents.restoreState(inState)) return null;
return copyBackForwardList();
}
@Override
public void loadUrl(final String url, Map<String, String> additionalHttpHeaders) {
// TODO: We may actually want to do some sanity checks here (like filter about://chrome).
// For backwards compatibility, apps targeting less than K will have JS URLs evaluated
// directly and any result of the evaluation will not replace the current page content.
// Matching Chrome behavior more closely; apps targetting >= K that load a JS URL will
// have the result of that URL replace the content of the current page.
final String JAVASCRIPT_SCHEME = "javascript:";
if (mAppTargetSdkVersion < Build.VERSION_CODES.KITKAT &&
url != null && url.startsWith(JAVASCRIPT_SCHEME)) {
mFactory.startYourEngines(true);
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
mAwContents.evaluateJavaScriptEvenIfNotYetNavigated(
url.substring(JAVASCRIPT_SCHEME.length()));
}
});
} else {
mAwContents.evaluateJavaScriptEvenIfNotYetNavigated(
url.substring(JAVASCRIPT_SCHEME.length()));
}
return;
}
LoadUrlParams params = new LoadUrlParams(url);
if (additionalHttpHeaders != null) params.setExtraHeaders(additionalHttpHeaders);
loadUrlOnUiThread(params);
}
@Override
public void loadUrl(String url) {
// Early out to match old WebView implementation
if (url == null) {
return;
}
loadUrl(url, null);
}
@Override
public void postUrl(String url, byte[] postData) {
LoadUrlParams params = LoadUrlParams.createLoadHttpPostParams(url, postData);
Map<String,String> headers = new HashMap<String,String>();
headers.put("Content-Type", "application/x-www-form-urlencoded");
params.setExtraHeaders(headers);
loadUrlOnUiThread(params);
}
private static String fixupMimeType(String mimeType) {
return TextUtils.isEmpty(mimeType) ? "text/html" : mimeType;
}
private static String fixupData(String data) {
return TextUtils.isEmpty(data) ? "" : data;
}
private static String fixupBase(String url) {
return TextUtils.isEmpty(url) ? "about:blank" : url;
}
private static String fixupHistory(String url) {
return TextUtils.isEmpty(url) ? "about:blank" : url;
}
private static boolean isBase64Encoded(String encoding) {
return "base64".equals(encoding);
}
@Override
public void loadData(String data, String mimeType, String encoding) {
loadUrlOnUiThread(LoadUrlParams.createLoadDataParams(
fixupData(data), fixupMimeType(mimeType), isBase64Encoded(encoding)));
}
@Override
public void loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding,
String historyUrl) {
data = fixupData(data);
mimeType = fixupMimeType(mimeType);
LoadUrlParams loadUrlParams;
baseUrl = fixupBase(baseUrl);
historyUrl = fixupHistory(historyUrl);
if (baseUrl.startsWith("data:")) {
// For backwards compatibility with WebViewClassic, we use the value of |encoding|
// as the charset, as long as it's not "base64".
boolean isBase64 = isBase64Encoded(encoding);
loadUrlParams = LoadUrlParams.createLoadDataParamsWithBaseUrl(
data, mimeType, isBase64, baseUrl, historyUrl, isBase64 ? null : encoding);
} else {
// When loading data with a non-data: base URL, the classic WebView would effectively
// "dump" that string of data into the WebView without going through regular URL
// loading steps such as decoding URL-encoded entities. We achieve this same behavior by
// base64 encoding the data that is passed here and then loading that as a data: URL.
try {
loadUrlParams = LoadUrlParams.createLoadDataParamsWithBaseUrl(
Base64.encodeToString(data.getBytes("utf-8"), Base64.DEFAULT), mimeType,
true, baseUrl, historyUrl, "utf-8");
} catch (java.io.UnsupportedEncodingException e) {
Log.wtf(TAG, "Unable to load data string " + data, e);
return;
}
}
loadUrlOnUiThread(loadUrlParams);
}
private void loadUrlOnUiThread(final LoadUrlParams loadUrlParams) {
// This is the last point that we can delay starting the Chromium backend up
// and if the app has not caused us to bind the Chromium UI thread to a background thread
// we now bind Chromium's notion of the UI thread to the app main thread.
mFactory.startYourEngines(true);
if (checkNeedsPost()) {
// Disallowed in WebView API for apps targetting a new SDK
assert mAppTargetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2;
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
mAwContents.loadUrl(loadUrlParams);
}
});
return;
}
mAwContents.loadUrl(loadUrlParams);
}
public void evaluateJavaScript(String script, ValueCallback<String> resultCallback) {
checkThread();
mAwContents.evaluateJavaScript(script, resultCallback);
}
@Override
public void saveWebArchive(String filename) {
saveWebArchive(filename, false, null);
}
@Override
public void saveWebArchive(final String basename, final boolean autoname,
final ValueCallback<String> callback) {
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
saveWebArchive(basename, autoname, callback);
}
});
return;
}
mAwContents.saveWebArchive(basename, autoname, callback);
}
@Override
public void stopLoading() {
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
stopLoading();
}
});
return;
}
mAwContents.stopLoading();
}
@Override
public void reload() {
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
reload();
}
});
return;
}
mAwContents.reload();
}
@Override
public boolean canGoBack() {
mFactory.startYourEngines(true);
if (checkNeedsPost()) {
Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
@Override
public Boolean call() {
return canGoBack();
}
});
return ret;
}
return mAwContents.canGoBack();
}
@Override
public void goBack() {
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
goBack();
}
});
return;
}
mAwContents.goBack();
}
@Override
public boolean canGoForward() {
mFactory.startYourEngines(true);
if (checkNeedsPost()) {
Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
@Override
public Boolean call() {
return canGoForward();
}
});
return ret;
}
return mAwContents.canGoForward();
}
@Override
public void goForward() {
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
goForward();
}
});
return;
}
mAwContents.goForward();
}
@Override
public boolean canGoBackOrForward(final int steps) {
mFactory.startYourEngines(true);
if (checkNeedsPost()) {
Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
@Override
public Boolean call() {
return canGoBackOrForward(steps);
}
});
return ret;
}
return mAwContents.canGoBackOrForward(steps);
}
@Override
public void goBackOrForward(final int steps) {
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
goBackOrForward(steps);
}
});
return;
}
mAwContents.goBackOrForward(steps);
}
@Override
public boolean isPrivateBrowsingEnabled() {
// Not supported in this WebView implementation.
return false;
}
@Override
public boolean pageUp(final boolean top) {
mFactory.startYourEngines(true);
if (checkNeedsPost()) {
Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
@Override
public Boolean call() {
return pageUp(top);
}
});
return ret;
}
return mAwContents.pageUp(top);
}
@Override
public boolean pageDown(final boolean bottom) {
mFactory.startYourEngines(true);
if (checkNeedsPost()) {
Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
@Override
public Boolean call() {
return pageDown(bottom);
}
});
return ret;
}
return mAwContents.pageDown(bottom);
}
@Override
public void clearView() {
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
clearView();
}
});
return;
}
mAwContents.clearView();
}
@Override
public Picture capturePicture() {
mFactory.startYourEngines(true);
if (checkNeedsPost()) {
Picture ret = runOnUiThreadBlocking(new Callable<Picture>() {
@Override
public Picture call() {
return capturePicture();
}
});
return ret;
}
return mAwContents.capturePicture();
}
@Override
public float getScale() {
// No checkThread() as it is mostly thread safe (workaround for b/10652991).
mFactory.startYourEngines(true);
return mAwContents.getScale();
}
@Override
public void setInitialScale(final int scaleInPercent) {
// No checkThread() as it is thread safe
mWebSettings.getAwSettings().setInitialPageScale(scaleInPercent);
}
@Override
public void invokeZoomPicker() {
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
invokeZoomPicker();
}
});
return;
}
mAwContents.invokeZoomPicker();
}
@Override
public WebView.HitTestResult getHitTestResult() {
mFactory.startYourEngines(true);
if (checkNeedsPost()) {
WebView.HitTestResult ret = runOnUiThreadBlocking(
new Callable<WebView.HitTestResult>() {
@Override
public WebView.HitTestResult call() {
return getHitTestResult();
}
});
return ret;
}
AwContents.HitTestData data = mAwContents.getLastHitTestResult();
mHitTestResult.setType(data.hitTestResultType);
mHitTestResult.setExtra(data.hitTestResultExtraData);
return mHitTestResult;
}
@Override
public void requestFocusNodeHref(final Message hrefMsg) {
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
requestFocusNodeHref(hrefMsg);
}
});
return;
}
mAwContents.requestFocusNodeHref(hrefMsg);
}
@Override
public void requestImageRef(final Message msg) {
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
requestImageRef(msg);
}
});
return;
}
mAwContents.requestImageRef(msg);
}
@Override
public String getUrl() {
mFactory.startYourEngines(true);
if (checkNeedsPost()) {
String ret = runOnUiThreadBlocking(new Callable<String>() {
@Override
public String call() {
return getUrl();
}
});
return ret;
}
String url = mAwContents.getUrl();
if (url == null || url.trim().isEmpty()) return null;
return url;
}
@Override
public String getOriginalUrl() {
mFactory.startYourEngines(true);
if (checkNeedsPost()) {
String ret = runOnUiThreadBlocking(new Callable<String>() {
@Override
public String call() {
return getOriginalUrl();
}
});
return ret;
}
String url = mAwContents.getOriginalUrl();
if (url == null || url.trim().isEmpty()) return null;
return url;
}
@Override
public String getTitle() {
mFactory.startYourEngines(true);
if (checkNeedsPost()) {
String ret = runOnUiThreadBlocking(new Callable<String>() {
@Override
public String call() {
return getTitle();
}
});
return ret;
}
return mAwContents.getTitle();
}
@Override
public Bitmap getFavicon() {
mFactory.startYourEngines(true);
if (checkNeedsPost()) {
Bitmap ret = runOnUiThreadBlocking(new Callable<Bitmap>() {
@Override
public Bitmap call() {
return getFavicon();
}
});
return ret;
}
return mAwContents.getFavicon();
}
@Override
public String getTouchIconUrl() {
// Intentional no-op: hidden method on WebView.
return null;
}
@Override
public int getProgress() {
if (mAwContents == null) return 100;
// No checkThread() because the value is cached java side (workaround for b/10533304).
return mAwContents.getMostRecentProgress();
}
@Override
public int getContentHeight() {
if (mAwContents == null) return 0;
// No checkThread() as it is mostly thread safe (workaround for b/10594869).
return mAwContents.getContentHeightCss();
}
@Override
public int getContentWidth() {
if (mAwContents == null) return 0;
// No checkThread() as it is mostly thread safe (workaround for b/10594869).
return mAwContents.getContentWidthCss();
}
@Override
public void pauseTimers() {
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
pauseTimers();
}
});
return;
}
mAwContents.pauseTimers();
}
@Override
public void resumeTimers() {
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
resumeTimers();
}
});
return;
}
mAwContents.resumeTimers();
}
@Override
public void onPause() {
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
onPause();
}
});
return;
}
mAwContents.onPause();
}
@Override
public void onResume() {
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
onResume();
}
});
return;
}
mAwContents.onResume();
}
@Override
public boolean isPaused() {
mFactory.startYourEngines(true);
if (checkNeedsPost()) {
Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
@Override
public Boolean call() {
return isPaused();
}
});
return ret;
}
return mAwContents.isPaused();
}
@Override
public void freeMemory() {
// Intentional no-op. Memory is managed automatically by Chromium.
}
@Override
public void clearCache(final boolean includeDiskFiles) {
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
clearCache(includeDiskFiles);
}
});
return;
}
mAwContents.clearCache(includeDiskFiles);
}
/**
* This is a poorly named method, but we keep it for historical reasons.
*/
@Override
public void clearFormData() {
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
clearFormData();
}
});
return;
}
mAwContents.hideAutofillPopup();
}
@Override
public void clearHistory() {
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
clearHistory();
}
});
return;
}
mAwContents.clearHistory();
}
@Override
public void clearSslPreferences() {
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
clearSslPreferences();
}
});
return;
}
mAwContents.clearSslPreferences();
}
@Override
public WebBackForwardList copyBackForwardList() {
mFactory.startYourEngines(true);
if (checkNeedsPost()) {
WebBackForwardList ret = runOnUiThreadBlocking(new Callable<WebBackForwardList>() {
@Override
public WebBackForwardList call() {
return copyBackForwardList();
}
});
return ret;
}
return new WebBackForwardListChromium(
mAwContents.getNavigationHistory());
}
@Override
public void setFindListener(WebView.FindListener listener) {
mContentsClientAdapter.setFindListener(listener);
}
@Override
public void findNext(final boolean forwards) {
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
findNext(forwards);
}
});
return;
}
mAwContents.findNext(forwards);
}
@Override
public int findAll(final String searchString) {
findAllAsync(searchString);
return 0;
}
@Override
public void findAllAsync(final String searchString) {
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
findAllAsync(searchString);
}
});
return;
}
mAwContents.findAllAsync(searchString);
}
@Override
public boolean showFindDialog(final String text, final boolean showIme) {
mFactory.startYourEngines(false);
if (checkNeedsPost()) {
return false;
}
if (mWebView.getParent() == null) {
return false;
}
FindActionModeCallback findAction = new FindActionModeCallback(mWebView.getContext());
if (findAction == null) {
return false;
}
mWebView.startActionMode(findAction);
findAction.setWebView(mWebView);
if (showIme) {
findAction.showSoftInput();
}
if (text != null) {
findAction.setText(text);
findAction.findAll();
}
return true;
}
@Override
public void notifyFindDialogDismissed() {
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
notifyFindDialogDismissed();
}
});
return;
}
clearMatches();
}
@Override
public void clearMatches() {
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
clearMatches();
}
});
return;
}
mAwContents.clearMatches();
}
@Override
public void documentHasImages(final Message response) {
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
documentHasImages(response);
}
});
return;
}
mAwContents.documentHasImages(response);
}
@Override
public void setWebViewClient(WebViewClient client) {
mContentsClientAdapter.setWebViewClient(client);
}
@Override
public void setDownloadListener(DownloadListener listener) {
mContentsClientAdapter.setDownloadListener(listener);
}
@Override
public void setWebChromeClient(WebChromeClient client) {
mWebSettings.getAwSettings().setFullscreenSupported(doesSupportFullscreen(client));
mContentsClientAdapter.setWebChromeClient(client);
}
/**
* Returns true if the supplied {@link WebChromeClient} supports fullscreen.
*
* <p>For fullscreen support, implementations of {@link WebChromeClient#onShowCustomView}
* and {@link WebChromeClient#onHideCustomView()} are required.
*/
private boolean doesSupportFullscreen(WebChromeClient client) {
if (client == null) {
return false;
}
// If client is not a subclass of WebChromeClient then the methods have not been
// implemented because WebChromeClient has empty implementations.
if (client.getClass().isAssignableFrom(WebChromeClient.class)) {
return false;
}
try {
client.getClass().getDeclaredMethod("onShowCustomView", View.class,
CustomViewCallback.class);
client.getClass().getDeclaredMethod("onHideCustomView");
return true;
} catch (NoSuchMethodException e) {
return false;
}
}
@Override
public void setPictureListener(final WebView.PictureListener listener) {
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
setPictureListener(listener);
}
});
return;
}
mContentsClientAdapter.setPictureListener(listener);
mAwContents.enableOnNewPicture(listener != null,
mAppTargetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN_MR2);
}
@Override
public void addJavascriptInterface(final Object obj, final String interfaceName) {
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
addJavascriptInterface(obj, interfaceName);
}
});
return;
}
Class<? extends Annotation> requiredAnnotation = null;
if (mAppTargetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
requiredAnnotation = JavascriptInterface.class;
}
mAwContents.addPossiblyUnsafeJavascriptInterface(obj, interfaceName, requiredAnnotation);
}
@Override
public void removeJavascriptInterface(final String interfaceName) {
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
removeJavascriptInterface(interfaceName);
}
});
return;
}
mAwContents.removeJavascriptInterface(interfaceName);
}
@Override
public WebSettings getSettings() {
return mWebSettings;
}
@Override
public void setMapTrackballToArrowKeys(boolean setMap) {
// This is a deprecated API: intentional no-op.
}
@Override
public void flingScroll(final int vx, final int vy) {
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
flingScroll(vx, vy);
}
});
return;
}
mAwContents.flingScroll(vx, vy);
}
@Override
public View getZoomControls() {
mFactory.startYourEngines(false);
if (checkNeedsPost()) {
return null;
}
// This was deprecated in 2009 and hidden in JB MR1, so just provide the minimum needed
// to stop very out-dated applications from crashing.
Log.w(TAG, "WebView doesn't support getZoomControls");
return mAwContents.getSettings().supportZoom() ? new View(mWebView.getContext()) : null;
}
@Override
public boolean canZoomIn() {
if (checkNeedsPost()) {
return false;
}
return mAwContents.canZoomIn();
}
@Override
public boolean canZoomOut() {
if (checkNeedsPost()) {
return false;
}
return mAwContents.canZoomOut();
}
@Override
public boolean zoomIn() {
mFactory.startYourEngines(true);
if (checkNeedsPost()) {
boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
@Override
public Boolean call() {
return zoomIn();
}
});
return ret;
}
return mAwContents.zoomIn();
}
@Override
public boolean zoomOut() {
mFactory.startYourEngines(true);
if (checkNeedsPost()) {
boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
@Override
public Boolean call() {
return zoomOut();
}
});
return ret;
}
return mAwContents.zoomOut();
}
@Override
public boolean zoomBy(float factor) {
mFactory.startYourEngines(true);
// This is an L API and therefore we can enforce stricter threading constraints.
checkThread();
return mAwContents.zoomBy(factor);
}
@Override
public void dumpViewHierarchyWithProperties(BufferedWriter out, int level) {
// Intentional no-op
}
@Override
public View findHierarchyView(String className, int hashCode) {
// Intentional no-op
return null;
}
// WebViewProvider glue methods ---------------------------------------------------------------
@Override
// This needs to be kept thread safe!
public WebViewProvider.ViewDelegate getViewDelegate() {
return this;
}
@Override
// This needs to be kept thread safe!
public WebViewProvider.ScrollDelegate getScrollDelegate() {
return this;
}
// WebViewProvider.ViewDelegate implementation ------------------------------------------------
// TODO: remove from WebViewProvider and use default implementation from
// ViewGroup.
// @Override
public boolean shouldDelayChildPressedState() {
mFactory.startYourEngines(false);
if (checkNeedsPost()) {
boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
@Override
public Boolean call() {
return shouldDelayChildPressedState();
}
});
return ret;
}
return true;
}
// @Override
public AccessibilityNodeProvider getAccessibilityNodeProvider() {
mFactory.startYourEngines(false);
if (checkNeedsPost()) {
AccessibilityNodeProvider ret = runOnUiThreadBlocking(
new Callable<AccessibilityNodeProvider>() {
@Override
public AccessibilityNodeProvider call() {
return getAccessibilityNodeProvider();
}
});
return ret;
}
return mAwContents.getAccessibilityNodeProvider();
}
@Override
public void onInitializeAccessibilityNodeInfo(final AccessibilityNodeInfo info) {
mFactory.startYourEngines(false);
if (checkNeedsPost()) {
runVoidTaskOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
onInitializeAccessibilityNodeInfo(info);
}
});
return;
}
mAwContents.onInitializeAccessibilityNodeInfo(info);
}
@Override
public void onInitializeAccessibilityEvent(final AccessibilityEvent event) {
mFactory.startYourEngines(false);
if (checkNeedsPost()) {
runVoidTaskOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
onInitializeAccessibilityEvent(event);
}
});
return;
}
mAwContents.onInitializeAccessibilityEvent(event);
}
@Override
public boolean performAccessibilityAction(final int action, final Bundle arguments) {
mFactory.startYourEngines(false);
if (checkNeedsPost()) {
boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
@Override
public Boolean call() {
return performAccessibilityAction(action, arguments);
}
});
return ret;
}
if (mAwContents.supportsAccessibilityAction(action)) {
return mAwContents.performAccessibilityAction(action, arguments);
}
return mWebViewPrivate.super_performAccessibilityAction(action, arguments);
}
@Override
public void setOverScrollMode(final int mode) {
// This gets called from the android.view.View c'tor that WebView inherits from. This
// causes the method to be called when mAwContents == null.
// It's safe to ignore these calls however since AwContents will read the current value of
// this setting when it's created.
if (mAwContents == null) return;
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
setOverScrollMode(mode);
}
});
return;
}
mAwContents.setOverScrollMode(mode);
}
@Override
public void setScrollBarStyle(final int style) {
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
setScrollBarStyle(style);
}
});
return;
}
mAwContents.setScrollBarStyle(style);
}
@Override
public void onDrawVerticalScrollBar(final Canvas canvas, final Drawable scrollBar, final int l,
final int t, final int r, final int b) {
// WebViewClassic was overriding this method to handle rubberband over-scroll. Since
// WebViewChromium doesn't support that the vanilla implementation of this method can be
// used.
mWebViewPrivate.super_onDrawVerticalScrollBar(canvas, scrollBar, l, t, r, b);
}
@Override
public void onOverScrolled(final int scrollX, final int scrollY, final boolean clampedX,
final boolean clampedY) {
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
onOverScrolled(scrollX, scrollY, clampedX, clampedY);
}
});
return;
}
mAwContents.onContainerViewOverScrolled(scrollX, scrollY, clampedX, clampedY);
}
@Override
public void onWindowVisibilityChanged(final int visibility) {
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
onWindowVisibilityChanged(visibility);
}
});
return;
}
mAwContents.onWindowVisibilityChanged(visibility);
}
@Override
public void onDraw(final Canvas canvas) {
mFactory.startYourEngines(true);
if (checkNeedsPost()) {
runVoidTaskOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
onDraw(canvas);
}
});
return;
}
mAwContents.onDraw(canvas);
}
@Override
public void setLayoutParams(final ViewGroup.LayoutParams layoutParams) {
// This API is our strongest signal from the View system that this
// WebView is going to be bound to a View hierarchy and so at this
// point we must bind Chromium's UI thread to the current thread.
mFactory.startYourEngines(false);
checkThread();
mWebViewPrivate.super_setLayoutParams(layoutParams);
}
@Override
public boolean performLongClick() {
// Return false unless the WebView is attached to a View with a parent
return mWebView.getParent() != null ? mWebViewPrivate.super_performLongClick() : false;
}
@Override
public void onConfigurationChanged(final Configuration newConfig) {
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
onConfigurationChanged(newConfig);
}
});
return;
}
mAwContents.onConfigurationChanged(newConfig);
}
@Override
public InputConnection onCreateInputConnection(final EditorInfo outAttrs) {
mFactory.startYourEngines(false);
if (checkNeedsPost()) {
return null;
}
return mAwContents.onCreateInputConnection(outAttrs);
}
@Override
public boolean onKeyMultiple(final int keyCode, final int repeatCount, final KeyEvent event) {
mFactory.startYourEngines(false);
if (checkNeedsPost()) {
boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
@Override
public Boolean call() {
return onKeyMultiple(keyCode, repeatCount, event);
}
});
return ret;
}
UnimplementedWebViewApi.invoke();
return false;
}
@Override
public boolean onKeyDown(final int keyCode, final KeyEvent event) {
mFactory.startYourEngines(false);
if (checkNeedsPost()) {
boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
@Override
public Boolean call() {
return onKeyDown(keyCode, event);
}
});
return ret;
}
UnimplementedWebViewApi.invoke();
return false;
}
@Override
public boolean onKeyUp(final int keyCode, final KeyEvent event) {
mFactory.startYourEngines(false);
if (checkNeedsPost()) {
boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
@Override
public Boolean call() {
return onKeyUp(keyCode, event);
}
});
return ret;
}
return mAwContents.onKeyUp(keyCode, event);
}
@Override
public void onAttachedToWindow() {
// This API is our strongest signal from the View system that this
// WebView is going to be bound to a View hierarchy and so at this
// point we must bind Chromium's UI thread to the current thread.
mFactory.startYourEngines(false);
checkThread();
mAwContents.onAttachedToWindow();
}
@Override
public void onDetachedFromWindow() {
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
onDetachedFromWindow();
}
});
return;
}
mAwContents.onDetachedFromWindow();
}
@Override
public void onVisibilityChanged(final View changedView, final int visibility) {
// The AwContents will find out the container view visibility before the first draw so we
// can safely ignore onVisibilityChanged callbacks that happen before init().
if (mAwContents == null) return;
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
onVisibilityChanged(changedView, visibility);
}
});
return;
}
mAwContents.onVisibilityChanged(changedView, visibility);
}
@Override
public void onWindowFocusChanged(final boolean hasWindowFocus) {
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
onWindowFocusChanged(hasWindowFocus);
}
});
return;
}
mAwContents.onWindowFocusChanged(hasWindowFocus);
}
@Override
public void onFocusChanged(final boolean focused, final int direction,
final Rect previouslyFocusedRect) {
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
onFocusChanged(focused, direction, previouslyFocusedRect);
}
});
return;
}
mAwContents.onFocusChanged(focused, direction, previouslyFocusedRect);
}
@Override
public boolean setFrame(final int left, final int top, final int right, final int bottom) {
return mWebViewPrivate.super_setFrame(left, top, right, bottom);
}
@Override
public void onSizeChanged(final int w, final int h, final int ow, final int oh) {
if (checkNeedsPost()) {
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
onSizeChanged(w, h, ow, oh);
}
});
return;
}
mAwContents.onSizeChanged(w, h, ow, oh);
}
@Override
public void onScrollChanged(int l, int t, int oldl, int oldt) {
}
@Override
public boolean dispatchKeyEvent(final KeyEvent event) {
mFactory.startYourEngines(false);
if (checkNeedsPost()) {
boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
@Override
public Boolean call() {
return dispatchKeyEvent(event);
}
});
return ret;
}
return mAwContents.dispatchKeyEvent(event);
}
@Override
public boolean onTouchEvent(final MotionEvent ev) {
mFactory.startYourEngines(false);
if (checkNeedsPost()) {
boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
@Override
public Boolean call() {
return onTouchEvent(ev);
}
});
return ret;
}
return mAwContents.onTouchEvent(ev);
}
@Override
public boolean onHoverEvent(final MotionEvent event) {
mFactory.startYourEngines(false);
if (checkNeedsPost()) {
boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
@Override
public Boolean call() {
return onHoverEvent(event);
}
});
return ret;
}
return mAwContents.onHoverEvent(event);
}
@Override
public boolean onGenericMotionEvent(final MotionEvent event) {
mFactory.startYourEngines(false);
if (checkNeedsPost()) {
boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
@Override
public Boolean call() {
return onGenericMotionEvent(event);
}
});
return ret;
}
return mAwContents.onGenericMotionEvent(event);
}
@Override
public boolean onTrackballEvent(MotionEvent ev) {
// Trackball event not handled, which eventually gets converted to DPAD keyevents
return false;
}
@Override
public boolean requestFocus(final int direction, final Rect previouslyFocusedRect) {
mFactory.startYourEngines(false);
if (checkNeedsPost()) {
boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
@Override
public Boolean call() {
return requestFocus(direction, previouslyFocusedRect);
}
});
return ret;
}
mAwContents.requestFocus();
return mWebViewPrivate.super_requestFocus(direction, previouslyFocusedRect);
}
@Override
public void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
mFactory.startYourEngines(false);
if (checkNeedsPost()) {
runVoidTaskOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
onMeasure(widthMeasureSpec, heightMeasureSpec);
}
});
return;
}
mAwContents.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
public boolean requestChildRectangleOnScreen(final View child, final Rect rect,
final boolean immediate) {
mFactory.startYourEngines(false);
if (checkNeedsPost()) {
boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
@Override
public Boolean call() {
return requestChildRectangleOnScreen(child, rect, immediate);
}
});
return ret;
}
return mAwContents.requestChildRectangleOnScreen(child, rect, immediate);
}
@Override
public void setBackgroundColor(final int color) {
mFactory.startYourEngines(false);
if (checkNeedsPost()) {
ThreadUtils.postOnUiThread(new Runnable() {
@Override
public void run() {
setBackgroundColor(color);
}
});
return;
}
mAwContents.setBackgroundColor(color);
}
@Override
public void setLayerType(final int layerType, final Paint paint) {
// This can be called from WebView constructor in which case mAwContents
// is still null. We set the layer type in initForReal in that case.
if (mAwContents == null) return;
if (checkNeedsPost()) {
ThreadUtils.postOnUiThread(new Runnable() {
@Override
public void run() {
setLayerType(layerType, paint);
}
});
return;
}
mAwContents.setLayerType(layerType, paint);
}
// Remove from superclass
public void preDispatchDraw(Canvas canvas) {
// TODO(leandrogracia): remove this method from WebViewProvider if we think
// we won't need it again.
}
public void onStartTemporaryDetach() {
mAwContents.onStartTemporaryDetach();
}
public void onFinishTemporaryDetach() {
mAwContents.onFinishTemporaryDetach();
}
// WebViewProvider.ScrollDelegate implementation ----------------------------------------------
@Override
public int computeHorizontalScrollRange() {
mFactory.startYourEngines(false);
if (checkNeedsPost()) {
int ret = runOnUiThreadBlocking(new Callable<Integer>() {
@Override
public Integer call() {
return computeHorizontalScrollRange();
}
});
return ret;
}
return mAwContents.computeHorizontalScrollRange();
}
@Override
public int computeHorizontalScrollOffset() {
mFactory.startYourEngines(false);
if (checkNeedsPost()) {
int ret = runOnUiThreadBlocking(new Callable<Integer>() {
@Override
public Integer call() {
return computeHorizontalScrollOffset();
}
});
return ret;
}
return mAwContents.computeHorizontalScrollOffset();
}
@Override
public int computeVerticalScrollRange() {
mFactory.startYourEngines(false);
if (checkNeedsPost()) {
int ret = runOnUiThreadBlocking(new Callable<Integer>() {
@Override
public Integer call() {
return computeVerticalScrollRange();
}
});
return ret;
}
return mAwContents.computeVerticalScrollRange();
}
@Override
public int computeVerticalScrollOffset() {
mFactory.startYourEngines(false);
if (checkNeedsPost()) {
int ret = runOnUiThreadBlocking(new Callable<Integer>() {
@Override
public Integer call() {
return computeVerticalScrollOffset();
}
});
return ret;
}
return mAwContents.computeVerticalScrollOffset();
}
@Override
public int computeVerticalScrollExtent() {
mFactory.startYourEngines(false);
if (checkNeedsPost()) {
int ret = runOnUiThreadBlocking(new Callable<Integer>() {
@Override
public Integer call() {
return computeVerticalScrollExtent();
}
});
return ret;
}
return mAwContents.computeVerticalScrollExtent();
}
@Override
public void computeScroll() {
mFactory.startYourEngines(false);
if (checkNeedsPost()) {
runVoidTaskOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
computeScroll();
}
});
return;
}
mAwContents.computeScroll();
}
// TODO(sgurun) this is only to have master-gpl compiling.
public PrintDocumentAdapter createPrintDocumentAdapter() {
return createPrintDocumentAdapter("default");
}
//@Override TODO(sgurun) commenting this out to have master-gpl compiling.
public PrintDocumentAdapter createPrintDocumentAdapter(String documentName) {
checkThread();
return new AwPrintDocumentAdapter(mAwContents.getPdfExporter(), documentName);
}
// AwContents.NativeGLDelegate implementation --------------------------------------
private class WebViewNativeGLDelegate implements AwContents.NativeGLDelegate {
@Override
public boolean requestDrawGL(Canvas canvas, boolean waitForCompletion,
View containerView) {
if (mGLfunctor == null) {
mGLfunctor = new DrawGLFunctor(mAwContents.getAwDrawGLViewContext());
}
return mGLfunctor.requestDrawGL(
(HardwareCanvas) canvas, containerView.getViewRootImpl(), waitForCompletion);
}
@Override
public void detachGLFunctor() {
if (mGLfunctor != null) {
mGLfunctor.detach();
}
}
}
// AwContents.InternalAccessDelegate implementation --------------------------------------
private class InternalAccessAdapter implements AwContents.InternalAccessDelegate {
@Override
public boolean drawChild(Canvas arg0, View arg1, long arg2) {
UnimplementedWebViewApi.invoke();
return false;
}
@Override
public boolean super_onKeyUp(int arg0, KeyEvent arg1) {
// Intentional no-op
return false;
}
@Override
public boolean super_dispatchKeyEventPreIme(KeyEvent arg0) {
UnimplementedWebViewApi.invoke();
return false;
}
@Override
public boolean super_dispatchKeyEvent(KeyEvent event) {
return mWebViewPrivate.super_dispatchKeyEvent(event);
}
@Override
public boolean super_onGenericMotionEvent(MotionEvent arg0) {
return mWebViewPrivate.super_onGenericMotionEvent(arg0);
}
@Override
public void super_onConfigurationChanged(Configuration arg0) {
// Intentional no-op
}
@Override
public int super_getScrollBarStyle() {
return mWebViewPrivate.super_getScrollBarStyle();
}
@Override
public boolean awakenScrollBars() {
mWebViewPrivate.awakenScrollBars(0);
// TODO: modify the WebView.PrivateAccess to provide a return value.
return true;
}
@Override
public boolean super_awakenScrollBars(int arg0, boolean arg1) {
// TODO: need method on WebView.PrivateAccess?
UnimplementedWebViewApi.invoke();
return false;
}
@Override
public void onScrollChanged(int l, int t, int oldl, int oldt) {
// Intentional no-op.
// Chromium calls this directly to trigger accessibility events. That isn't needed
// for WebView since super_scrollTo invokes onScrollChanged for us.
}
@Override
public void overScrollBy(int deltaX, int deltaY,
int scrollX, int scrollY,
int scrollRangeX, int scrollRangeY,
int maxOverScrollX, int maxOverScrollY,
boolean isTouchEvent) {
mWebViewPrivate.overScrollBy(deltaX, deltaY, scrollX, scrollY,
scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
}
@Override
public void super_scrollTo(int scrollX, int scrollY) {
mWebViewPrivate.super_scrollTo(scrollX, scrollY);
}
@Override
public void setMeasuredDimension(int measuredWidth, int measuredHeight) {
mWebViewPrivate.setMeasuredDimension(measuredWidth, measuredHeight);
}
// @Override
public boolean super_onHoverEvent(MotionEvent event) {
return mWebViewPrivate.super_onHoverEvent(event);
}
}
}