Commit f3b8b74e authored by Nate Fischer's avatar Nate Fischer Committed by Commit Bot

AW: implement support lib callbacks

This implements support library callbacks (WebViewclientCompat). This
creates a new layer (support_library/callback) which the glue layer
depends on. This dependency lets us instantiate a
SupportLibWebViewContentsClientAdapter inside setWebViewClient(), and
benefit from the glue layer's parameter cleanup code (e.g., in
onReceivedError2()).

The support_library/callback glue must be a separate layer from
support_library/ glue, as that already depends on the webkit glue to
initiate state. Implementing callbacks as a separate target avoids the
circular dependency.

This refactors the (post-L) glue layer callback methods to take the
following precedence:

 1. SupportLibWebViewContentsClientAdapter (if it supports the callback)
 2. WebViewClient (if on the appropriate platform level)
 3. Default behavior (implementation provided by the glue layer)

This implements both category 1 and 2 APIs.

Design doc: http://go/wv-support-library-callbacks

Bug: 781764
Test: manual - built test application with latest support-lib changes
Change-Id: I21e28493873e670cfd428c7eeef12b0e212aeec4
Reviewed-on: https://chromium-review.googlesource.com/989015Reviewed-by: default avatarRichard Coles <torne@chromium.org>
Reviewed-by: default avatarGustav Sennton <gsennton@chromium.org>
Commit-Queue: Nate Fischer <ntfschr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#548538}
parent 58f68d3c
...@@ -9,6 +9,8 @@ glue_library_deps = [ ...@@ -9,6 +9,8 @@ glue_library_deps = [
"//android_webview:android_webview_commandline_java", "//android_webview:android_webview_commandline_java",
"//android_webview:android_webview_platform_services_java", "//android_webview:android_webview_platform_services_java",
"//android_webview:system_webview_manifest", "//android_webview:system_webview_manifest",
"//android_webview/support_library/boundary_interfaces:boundary_interface_java",
"//android_webview/support_library/callback:callback_java",
"//base:base_java", "//base:base_java",
"//components/autofill/android:autofill_java", "//components/autofill/android:autofill_java",
"//components/autofill/android:provider_java", "//components/autofill/android:provider_java",
......
...@@ -60,6 +60,8 @@ import org.chromium.base.Callback; ...@@ -60,6 +60,8 @@ import org.chromium.base.Callback;
import org.chromium.base.Log; import org.chromium.base.Log;
import org.chromium.base.ThreadUtils; import org.chromium.base.ThreadUtils;
import org.chromium.base.TraceEvent; import org.chromium.base.TraceEvent;
import org.chromium.support_lib_boundary.util.Features;
import org.chromium.support_lib_callback_glue.SupportLibWebViewContentsClientAdapter;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.security.Principal; import java.security.Principal;
...@@ -101,6 +103,8 @@ class WebViewContentsClientAdapter extends AwContentsClient { ...@@ -101,6 +103,8 @@ class WebViewContentsClientAdapter extends AwContentsClient {
private final Context mContext; private final Context mContext;
// The WebViewClient instance that was passed to WebView.setWebViewClient(). // The WebViewClient instance that was passed to WebView.setWebViewClient().
protected WebViewClient mWebViewClient = sNullWebViewClient; protected WebViewClient mWebViewClient = sNullWebViewClient;
// Some callbacks will be forwarded to this client for apps using the support library.
private SupportLibWebViewContentsClientAdapter mSupportLibClient;
// The WebChromeClient instance that was passed to WebView.setContentViewClient(). // The WebChromeClient instance that was passed to WebView.setContentViewClient().
private WebChromeClient mWebChromeClient; private WebChromeClient mWebChromeClient;
// The listener receiving find-in-page API results. // The listener receiving find-in-page API results.
...@@ -176,6 +180,9 @@ class WebViewContentsClientAdapter extends AwContentsClient { ...@@ -176,6 +180,9 @@ class WebViewContentsClientAdapter extends AwContentsClient {
} else { } else {
mWebViewClient = sNullWebViewClient; mWebViewClient = sNullWebViewClient;
} }
// Always reset mSupportLibClient, since the WebViewClient may no longer be a
// WebViewClientCompat, or may support a different set of Features.
mSupportLibClient = new SupportLibWebViewContentsClientAdapter(mWebViewClient);
} }
WebViewClient getWebViewClient() { WebViewClient getWebViewClient() {
...@@ -319,7 +326,10 @@ class WebViewContentsClientAdapter extends AwContentsClient { ...@@ -319,7 +326,10 @@ class WebViewContentsClientAdapter extends AwContentsClient {
TraceEvent.begin("WebViewContentsClientAdapter.shouldOverrideUrlLoading"); TraceEvent.begin("WebViewContentsClientAdapter.shouldOverrideUrlLoading");
if (TRACE) Log.i(TAG, "shouldOverrideUrlLoading=" + request.url); if (TRACE) Log.i(TAG, "shouldOverrideUrlLoading=" + request.url);
boolean result; boolean result;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { if (mSupportLibClient.isFeatureAvailable(Features.SHOULD_OVERRIDE_WITH_REDIRECTS)) {
result = mSupportLibClient.shouldOverrideUrlLoading(
mWebView, new WebResourceRequestAdapter(request));
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
result = mWebViewClient.shouldOverrideUrlLoading( result = mWebViewClient.shouldOverrideUrlLoading(
mWebView, new WebResourceRequestAdapter(request)); mWebView, new WebResourceRequestAdapter(request));
} else { } else {
...@@ -547,11 +557,15 @@ class WebViewContentsClientAdapter extends AwContentsClient { ...@@ -547,11 +557,15 @@ class WebViewContentsClientAdapter extends AwContentsClient {
*/ */
@Override @Override
public void onPageCommitVisible(String url) { public void onPageCommitVisible(String url) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return;
try { try {
TraceEvent.begin("WebViewContentsClientAdapter.onPageCommitVisible"); TraceEvent.begin("WebViewContentsClientAdapter.onPageCommitVisible");
if (TRACE) Log.i(TAG, "onPageCommitVisible=" + url); if (TRACE) Log.i(TAG, "onPageCommitVisible=" + url);
if (mSupportLibClient.isFeatureAvailable(Features.VISUAL_STATE_CALLBACK)) {
mSupportLibClient.onPageCommitVisible(mWebView, url);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mWebViewClient.onPageCommitVisible(mWebView, url); mWebViewClient.onPageCommitVisible(mWebView, url);
}
// Otherwise, the API does not exist, so do nothing.
} finally { } finally {
TraceEvent.end("WebViewContentsClientAdapter.onPageCommitVisible"); TraceEvent.end("WebViewContentsClientAdapter.onPageCommitVisible");
} }
...@@ -563,6 +577,10 @@ class WebViewContentsClientAdapter extends AwContentsClient { ...@@ -563,6 +577,10 @@ class WebViewContentsClientAdapter extends AwContentsClient {
@Override @Override
public void onReceivedError(int errorCode, String description, String failingUrl) { public void onReceivedError(int errorCode, String description, String failingUrl) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) return; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) return;
// This event is handled by the support lib in {@link #onReceivedError2}.
if (mSupportLibClient.isFeatureAvailable(Features.WEB_RESOURCE_ERROR)) return;
try { try {
TraceEvent.begin("WebViewContentsClientAdapter.onReceivedError"); TraceEvent.begin("WebViewContentsClientAdapter.onReceivedError");
if (description == null || description.isEmpty()) { if (description == null || description.isEmpty()) {
...@@ -584,7 +602,6 @@ class WebViewContentsClientAdapter extends AwContentsClient { ...@@ -584,7 +602,6 @@ class WebViewContentsClientAdapter extends AwContentsClient {
*/ */
@Override @Override
public void onReceivedError2(AwWebResourceRequest request, AwWebResourceError error) { public void onReceivedError2(AwWebResourceRequest request, AwWebResourceError error) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return;
try { try {
TraceEvent.begin("WebViewContentsClientAdapter.onReceivedError"); TraceEvent.begin("WebViewContentsClientAdapter.onReceivedError");
if (error.description == null || error.description.isEmpty()) { if (error.description == null || error.description.isEmpty()) {
...@@ -594,8 +611,15 @@ class WebViewContentsClientAdapter extends AwContentsClient { ...@@ -594,8 +611,15 @@ class WebViewContentsClientAdapter extends AwContentsClient {
error.description = mWebViewDelegate.getErrorString(mContext, error.errorCode); error.description = mWebViewDelegate.getErrorString(mContext, error.errorCode);
} }
if (TRACE) Log.i(TAG, "onReceivedError=" + request.url); if (TRACE) Log.i(TAG, "onReceivedError=" + request.url);
if (mSupportLibClient.isFeatureAvailable(Features.WEB_RESOURCE_ERROR)) {
// Note: we must pass AwWebResourceError, since this class was introduced after L.
mSupportLibClient.onReceivedError(
mWebView, new WebResourceRequestAdapter(request), error);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mWebViewClient.onReceivedError(mWebView, new WebResourceRequestAdapter(request), mWebViewClient.onReceivedError(mWebView, new WebResourceRequestAdapter(request),
new WebResourceErrorImpl(error)); new WebResourceErrorImpl(error));
}
// Otherwise, this is handled by {@link #onReceivedError}.
} finally { } finally {
TraceEvent.end("WebViewContentsClientAdapter.onReceivedError"); TraceEvent.end("WebViewContentsClientAdapter.onReceivedError");
} }
...@@ -608,14 +632,12 @@ class WebViewContentsClientAdapter extends AwContentsClient { ...@@ -608,14 +632,12 @@ class WebViewContentsClientAdapter extends AwContentsClient {
@TargetApi(Build.VERSION_CODES.O_MR1) @TargetApi(Build.VERSION_CODES.O_MR1)
public void onSafeBrowsingHit(AwWebResourceRequest request, int threatType, public void onSafeBrowsingHit(AwWebResourceRequest request, int threatType,
final Callback<AwSafeBrowsingResponse> callback) { final Callback<AwSafeBrowsingResponse> callback) {
// WebViewClient.onSafeBrowsingHit was added in O_MR1.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O_MR1) {
callback.onResult(new AwSafeBrowsingResponse(SafeBrowsingAction.SHOW_INTERSTITIAL,
/* reporting */ true));
return;
}
try { try {
TraceEvent.begin("WebViewContentsClientAdapter.onSafeBrowsingHit"); TraceEvent.begin("WebViewContentsClientAdapter.onSafeBrowsingHit");
if (mSupportLibClient.isFeatureAvailable(Features.SAFE_BROWSING_HIT)) {
mSupportLibClient.onSafeBrowsingHit(
mWebView, new WebResourceRequestAdapter(request), threatType, callback);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
mWebViewClient.onSafeBrowsingHit(mWebView, new WebResourceRequestAdapter(request), mWebViewClient.onSafeBrowsingHit(mWebView, new WebResourceRequestAdapter(request),
threatType, new SafeBrowsingResponse() { threatType, new SafeBrowsingResponse() {
@Override @Override
...@@ -626,8 +648,8 @@ class WebViewContentsClientAdapter extends AwContentsClient { ...@@ -626,8 +648,8 @@ class WebViewContentsClientAdapter extends AwContentsClient {
@Override @Override
public void proceed(boolean report) { public void proceed(boolean report) {
callback.onResult( callback.onResult(new AwSafeBrowsingResponse(
new AwSafeBrowsingResponse(SafeBrowsingAction.PROCEED, report)); SafeBrowsingAction.PROCEED, report));
} }
@Override @Override
...@@ -636,6 +658,10 @@ class WebViewContentsClientAdapter extends AwContentsClient { ...@@ -636,6 +658,10 @@ class WebViewContentsClientAdapter extends AwContentsClient {
SafeBrowsingAction.BACK_TO_SAFETY, report)); SafeBrowsingAction.BACK_TO_SAFETY, report));
} }
}); });
} else {
callback.onResult(new AwSafeBrowsingResponse(SafeBrowsingAction.SHOW_INTERSTITIAL,
/* reporting */ true));
}
} finally { } finally {
TraceEvent.end("WebViewContentsClientAdapter.onRenderProcessGone"); TraceEvent.end("WebViewContentsClientAdapter.onRenderProcessGone");
} }
...@@ -643,14 +669,24 @@ class WebViewContentsClientAdapter extends AwContentsClient { ...@@ -643,14 +669,24 @@ class WebViewContentsClientAdapter extends AwContentsClient {
@Override @Override
public void onReceivedHttpError(AwWebResourceRequest request, AwWebResourceResponse response) { public void onReceivedHttpError(AwWebResourceRequest request, AwWebResourceResponse response) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return;
try { try {
TraceEvent.begin("WebViewContentsClientAdapter.onReceivedHttpError"); TraceEvent.begin("WebViewContentsClientAdapter.onReceivedHttpError");
if (TRACE) Log.i(TAG, "onReceivedHttpError=" + request.url); if (TRACE) Log.i(TAG, "onReceivedHttpError=" + request.url);
if (mSupportLibClient.isFeatureAvailable(Features.RECEIVE_HTTP_ERROR)) {
// Note: we do not create an immutable instance here, because that constructor is
// not available on L.
mSupportLibClient.onReceivedHttpError(mWebView,
new WebResourceRequestAdapter(request),
new WebResourceResponse(response.getMimeType(), response.getCharset(),
response.getStatusCode(), response.getReasonPhrase(),
response.getResponseHeaders(), response.getData()));
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mWebViewClient.onReceivedHttpError(mWebView, new WebResourceRequestAdapter(request), mWebViewClient.onReceivedHttpError(mWebView, new WebResourceRequestAdapter(request),
new WebResourceResponse(true, response.getMimeType(), response.getCharset(), new WebResourceResponse(true, response.getMimeType(), response.getCharset(),
response.getStatusCode(), response.getReasonPhrase(), response.getStatusCode(), response.getReasonPhrase(),
response.getResponseHeaders(), response.getData())); response.getResponseHeaders(), response.getData()));
}
// Otherwise, the API does not exist, so do nothing.
} finally { } finally {
TraceEvent.end("WebViewContentsClientAdapter.onReceivedHttpError"); TraceEvent.end("WebViewContentsClientAdapter.onReceivedHttpError");
} }
......
...@@ -13,6 +13,19 @@ public class Features { ...@@ -13,6 +13,19 @@ public class Features {
// This class just contains constants representing features. // This class just contains constants representing features.
private Features() {} private Features() {}
// WebViewCompat.postVisualStateCallback // WebViewCompat#postVisualStateCallback
// WebViewClientCompat#onPageCommitVisible
public static final String VISUAL_STATE_CALLBACK = "VISUAL_STATE_CALLBACK"; public static final String VISUAL_STATE_CALLBACK = "VISUAL_STATE_CALLBACK";
// WebViewClientCompat#onReceivedError(WebView, WebResourceRequest, WebResourceError)
public static final String WEB_RESOURCE_ERROR = "WEB_RESOURCE_ERROR";
// WebViewClientCompat#onReceivedHttpError
public static final String RECEIVE_HTTP_ERROR = "RECEIVE_HTTP_ERROR";
// WebViewClientCompat#onSafeBrowsingHit
public static final String SAFE_BROWSING_HIT = "SAFE_BROWSING_HIT";
// WebViewClientCompat#shouldOverrideUrlLoading
public static final String SHOULD_OVERRIDE_WITH_REDIRECTS = "SHOULD_OVERRIDE_WITH_REDIRECTS";
} }
# Copyright 2018 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.
import("//build/config/android/config.gni")
import("//build/config/android/rules.gni")
android_library("callback_java") {
java_files = [ "java/src/org/chromium/support_lib_callback_glue/SupportLibWebViewContentsClientAdapter.java" ]
deps = [
"//android_webview:android_webview_commandline_java",
"//android_webview:android_webview_java",
"//android_webview/support_library/boundary_interfaces:boundary_interface_java",
"//base:base_java",
]
}
// Copyright 2018 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.support_lib_callback_glue;
import android.support.annotation.Nullable;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import org.chromium.android_webview.AwContentsClient.AwWebResourceError;
import org.chromium.android_webview.AwSafeBrowsingResponse;
import org.chromium.android_webview.SafeBrowsingAction;
import org.chromium.base.Callback;
import org.chromium.support_lib_boundary.SafeBrowsingResponseBoundaryInterface;
import org.chromium.support_lib_boundary.WebResourceErrorBoundaryInterface;
import org.chromium.support_lib_boundary.WebViewClientBoundaryInterface;
import org.chromium.support_lib_boundary.util.BoundaryInterfaceReflectionUtil;
import org.chromium.support_lib_boundary.util.Features;
import java.lang.reflect.InvocationHandler;
/**
* Support library glue version of WebViewContentsClientAdapter.
*/
public class SupportLibWebViewContentsClientAdapter {
private static final String WEBVIEW_CLIENT_COMPAT_NAME = "androidx.webkit.WebViewClientCompat";
// If {@code null}, this indicates the WebViewClient is not a WebViewClientCompat. Otherwise,
// this is a Proxy for the WebViewClientCompat.
@Nullable
private WebViewClientBoundaryInterface mWebViewClient;
private static class SafeBrowsingResponseDelegate
implements SafeBrowsingResponseBoundaryInterface {
private Callback<AwSafeBrowsingResponse> mCallback;
SafeBrowsingResponseDelegate(Callback<AwSafeBrowsingResponse> callback) {
mCallback = callback;
}
@Override
public void showInterstitial(boolean allowReporting) {
mCallback.onResult(new AwSafeBrowsingResponse(
SafeBrowsingAction.SHOW_INTERSTITIAL, allowReporting));
}
@Override
public void proceed(boolean report) {
mCallback.onResult(new AwSafeBrowsingResponse(SafeBrowsingAction.PROCEED, report));
}
@Override
public void backToSafety(boolean report) {
mCallback.onResult(
new AwSafeBrowsingResponse(SafeBrowsingAction.BACK_TO_SAFETY, report));
}
};
private static class WebResourceErrorDelegate implements WebResourceErrorBoundaryInterface {
private AwWebResourceError mError;
WebResourceErrorDelegate(AwWebResourceError error) {
mError = error;
}
@Override
public int getErrorCode() {
return mError.errorCode;
}
@Override
public CharSequence getDescription() {
return mError.description;
}
};
public SupportLibWebViewContentsClientAdapter(WebViewClient possiblyCompatClient) {
mWebViewClient = convertCompatClient(possiblyCompatClient);
}
@Nullable
private WebViewClientBoundaryInterface convertCompatClient(WebViewClient possiblyCompatClient) {
if (!clientIsCompat(possiblyCompatClient)) return null;
InvocationHandler handler =
BoundaryInterfaceReflectionUtil.createInvocationHandlerFor(possiblyCompatClient);
return BoundaryInterfaceReflectionUtil.castToSuppLibClass(
WebViewClientBoundaryInterface.class, handler);
}
private boolean clientIsCompat(WebViewClient possiblyCompatClient) {
try {
Class compatClass = Class.forName(WEBVIEW_CLIENT_COMPAT_NAME, false,
possiblyCompatClient.getClass().getClassLoader());
return compatClass.isInstance(possiblyCompatClient);
} catch (ClassNotFoundException e) {
// If WEBVIEW_CLIENT_COMPAT_NAME is not in the ClassLoader, then this cannot be an
// instance of WebViewClientCompat.
return false;
}
}
/**
* Indicates whether this client can handle the callback(s) assocated with {@param featureName}.
* This should be called with the correct feature name before invoking the corresponding
* callback, and the callback must not be called if this returns {@code false} for the feature.
*
* @param featureName the feature for the desired callback.
* @return {@code true} if this client can handle the feature.
*/
public boolean isFeatureAvailable(String featureName) {
if (mWebViewClient == null) return false;
// TODO(ntfschr): provide a real implementation, which consults the WebViewClientCompat.
return true;
}
public void onPageCommitVisible(WebView webView, String url) {
assert isFeatureAvailable(Features.VISUAL_STATE_CALLBACK);
mWebViewClient.onPageCommitVisible(webView, url);
}
public void onReceivedError(
WebView webView, WebResourceRequest request, final AwWebResourceError error) {
assert isFeatureAvailable(Features.WEB_RESOURCE_ERROR);
WebResourceErrorBoundaryInterface errorDelegate = new WebResourceErrorDelegate(error);
InvocationHandler errorHandler =
BoundaryInterfaceReflectionUtil.createInvocationHandlerFor(errorDelegate);
mWebViewClient.onReceivedError(webView, request, errorHandler);
}
public void onReceivedHttpError(
WebView webView, WebResourceRequest request, WebResourceResponse response) {
assert isFeatureAvailable(Features.RECEIVE_HTTP_ERROR);
mWebViewClient.onReceivedHttpError(webView, request, response);
}
public void onSafeBrowsingHit(WebView webView, WebResourceRequest request, int threatType,
Callback<AwSafeBrowsingResponse> callback) {
assert isFeatureAvailable(Features.SAFE_BROWSING_HIT);
SafeBrowsingResponseBoundaryInterface responseDelegate =
new SafeBrowsingResponseDelegate(callback);
InvocationHandler responseHandler =
BoundaryInterfaceReflectionUtil.createInvocationHandlerFor(responseDelegate);
mWebViewClient.onSafeBrowsingHit(webView, request, threatType, responseHandler);
}
public boolean shouldOverrideUrlLoading(WebView webView, WebResourceRequest request) {
assert isFeatureAvailable(Features.SHOULD_OVERRIDE_WITH_REDIRECTS);
return mWebViewClient.shouldOverrideUrlLoading(webView, request);
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment