[WebView Support Library] Add support for static WebView methods.
Add support for the support-library version of
WebViewFactoryProvider.Statics, used for implementing static WebView
methods.
Bug: 73151403
Test: run androidx.webkit tests (existing + newly added ones) on an L
device and a P device.
Change-Id: I8d161a67fba78b3793a5d7091358ed429da28487
diff --git a/webkit/api/current.txt b/webkit/api/current.txt
index 7a3fe25..2a39d42 100644
--- a/webkit/api/current.txt
+++ b/webkit/api/current.txt
@@ -10,7 +10,10 @@
}
public class WebViewCompat {
+ method public static android.net.Uri getSafeBrowsingPrivacyPolicyUrl();
method public static void postVisualStateCallback(android.webkit.WebView, long, androidx.webkit.WebViewCompat.VisualStateCallback);
+ method public static void setSafeBrowsingWhitelist(java.util.List<java.lang.String>, android.webkit.ValueCallback<java.lang.Boolean>);
+ method public static void startSafeBrowsing(android.content.Context, android.webkit.ValueCallback<java.lang.Boolean>);
}
public static abstract interface WebViewCompat.VisualStateCallback {
diff --git a/webkit/src/androidTest/java/androidx/webkit/WebViewCompatTest.java b/webkit/src/androidTest/java/androidx/webkit/WebViewCompatTest.java
index 8b38d99..a9ffead 100644
--- a/webkit/src/androidTest/java/androidx/webkit/WebViewCompatTest.java
+++ b/webkit/src/androidTest/java/androidx/webkit/WebViewCompatTest.java
@@ -17,20 +17,38 @@
package androidx.webkit;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.os.Build;
+import android.os.Looper;
+import android.support.test.InstrumentationRegistry;
import android.support.test.filters.MediumTest;
import android.support.test.runner.AndroidJUnit4;
import android.support.v4.os.BuildCompat;
+import android.webkit.SafeBrowsingResponse;
+import android.webkit.ValueCallback;
+import android.webkit.WebResourceRequest;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+@MediumTest
@RunWith(AndroidJUnit4.class)
public class WebViewCompatTest {
WebViewOnUiThread mWebViewOnUiThread;
@@ -42,7 +60,6 @@
mWebViewOnUiThread = new androidx.webkit.WebViewOnUiThread();
}
- @MediumTest
@Test
public void testVisualStateCallbackCalled() throws Exception {
// TODO(gsennton) activate this test for pre-P devices when we can pre-install a WebView APK
@@ -65,7 +82,6 @@
assertTrue(callbackLatch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
}
- @MediumTest
@Test
public void testCheckThread() {
try {
@@ -80,4 +96,142 @@
}
fail("Calling a WebViewCompat method on the wrong thread must cause a run-time exception");
}
+
+ private static class MockContext extends ContextWrapper {
+ private boolean mGetApplicationContextWasCalled;
+
+ MockContext(Context context) {
+ super(context);
+ }
+
+ public Context getApplicationContext() {
+ mGetApplicationContextWasCalled = true;
+ return super.getApplicationContext();
+ }
+
+ public boolean wasGetApplicationContextCalled() {
+ return mGetApplicationContextWasCalled;
+ }
+ }
+
+ @Test
+ public void testStartSafeBrowsingUseApplicationContext() throws Exception {
+ // TODO(gsennton) activate this test for pre-P devices when we can pre-install a WebView APK
+ // containing support for the WebView Support Library, see b/73454652.
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O_MR1) return;
+
+ final MockContext ctx =
+ new MockContext(InstrumentationRegistry.getTargetContext().getApplicationContext());
+ final CountDownLatch resultLatch = new CountDownLatch(1);
+ WebViewCompat.startSafeBrowsing(ctx, new ValueCallback<Boolean>() {
+ @Override
+ public void onReceiveValue(Boolean value) {
+ assertTrue(ctx.wasGetApplicationContextCalled());
+ resultLatch.countDown();
+ return;
+ }
+ });
+ assertTrue(resultLatch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testStartSafeBrowsingWithNullCallbackDoesntCrash() throws Exception {
+ // TODO(gsennton) activate this test for pre-P devices when we can pre-install a WebView APK
+ // containing support for the WebView Support Library, see b/73454652.
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O_MR1) return;
+
+ WebViewCompat.startSafeBrowsing(InstrumentationRegistry.getTargetContext(), null);
+ }
+
+ @Test
+ public void testStartSafeBrowsingInvokesCallback() throws Exception {
+ // TODO(gsennton) activate this test for pre-P devices when we can pre-install a WebView APK
+ // containing support for the WebView Support Library, see b/73454652.
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O_MR1) return;
+
+ final CountDownLatch resultLatch = new CountDownLatch(1);
+ WebViewCompat.startSafeBrowsing(
+ InstrumentationRegistry.getTargetContext().getApplicationContext(),
+ new ValueCallback<Boolean>() {
+ @Override
+ public void onReceiveValue(Boolean value) {
+ assertTrue(Looper.getMainLooper().isCurrentThread());
+ resultLatch.countDown();
+ return;
+ }
+ });
+ assertTrue(resultLatch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testSetSafeBrowsingWhitelistWithMalformedList() throws Exception {
+ // TODO(gsennton) activate this test for pre-P devices when we can pre-install a WebView APK
+ // containing support for the WebView Support Library, see b/73454652.
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O_MR1) return;
+
+ List whitelist = new ArrayList<String>();
+ // Protocols are not supported in the whitelist
+ whitelist.add("http://google.com");
+ final CountDownLatch resultLatch = new CountDownLatch(1);
+ WebViewCompat.setSafeBrowsingWhitelist(whitelist, new ValueCallback<Boolean>() {
+ @Override
+ public void onReceiveValue(Boolean success) {
+ assertFalse(success);
+ resultLatch.countDown();
+ }
+ });
+ assertTrue(resultLatch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testSetSafeBrowsingWhitelistWithValidList() throws Exception {
+ // TODO(gsennton) activate this test for pre-P devices when we can pre-install a WebView APK
+ // containing support for the WebView Support Library, see b/73454652.
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O_MR1) return;
+
+ List whitelist = new ArrayList<String>();
+ whitelist.add("safe-browsing");
+ final CountDownLatch resultLatch = new CountDownLatch(1);
+ WebViewCompat.setSafeBrowsingWhitelist(whitelist, new ValueCallback<Boolean>() {
+ @Override
+ public void onReceiveValue(Boolean success) {
+ assertTrue(success);
+ resultLatch.countDown();
+ }
+ });
+ assertTrue(resultLatch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
+
+ final CountDownLatch resultLatch2 = new CountDownLatch(1);
+ mWebViewOnUiThread.setWebViewClient(new WebViewClient() {
+ @Override
+ public void onPageFinished(WebView view, String url) {
+ resultLatch2.countDown();
+ }
+
+ @Override
+ public void onSafeBrowsingHit(WebView view, WebResourceRequest request, int threatType,
+ SafeBrowsingResponse callback) {
+ Assert.fail("Should not invoke onSafeBrowsingHit");
+ }
+ });
+
+ mWebViewOnUiThread.loadUrl("chrome://safe-browsing/match?type=malware");
+
+ // Wait until page load has completed
+ assertTrue(resultLatch2.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testGetSafeBrowsingPrivacyPolicyUrl() throws Exception {
+ // TODO(gsennton) activate this test for pre-P devices when we can pre-install a WebView APK
+ // containing support for the WebView Support Library, see b/73454652.
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O_MR1) return;
+
+ assertNotNull(WebViewCompat.getSafeBrowsingPrivacyPolicyUrl());
+ try {
+ new URL(WebViewCompat.getSafeBrowsingPrivacyPolicyUrl().toString());
+ } catch (MalformedURLException e) {
+ Assert.fail("The privacy policy URL should be a well-formed URL");
+ }
+ }
}
diff --git a/webkit/src/androidTest/java/androidx/webkit/WebViewOnUiThread.java b/webkit/src/androidTest/java/androidx/webkit/WebViewOnUiThread.java
index a86775b..9b4c9e9 100644
--- a/webkit/src/androidTest/java/androidx/webkit/WebViewOnUiThread.java
+++ b/webkit/src/androidTest/java/androidx/webkit/WebViewOnUiThread.java
@@ -19,6 +19,7 @@
import android.support.test.InstrumentationRegistry;
import android.webkit.WebSettings;
import android.webkit.WebView;
+import android.webkit.WebViewClient;
public class WebViewOnUiThread {
private WebView mWebView;
@@ -41,6 +42,15 @@
});
}
+ public void setWebViewClient(final WebViewClient webviewClient) {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mWebView.setWebViewClient(webviewClient);
+ }
+ });
+ }
+
public void postVisualStateCallbackCompat(final long requestId,
final WebViewCompat.VisualStateCallback callback) {
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
diff --git a/webkit/src/main/java/androidx/webkit/WebViewCompat.java b/webkit/src/main/java/androidx/webkit/WebViewCompat.java
index 3141918..ee328aa 100644
--- a/webkit/src/main/java/androidx/webkit/WebViewCompat.java
+++ b/webkit/src/main/java/androidx/webkit/WebViewCompat.java
@@ -16,16 +16,21 @@
package androidx.webkit;
+import android.content.Context;
+import android.net.Uri;
import android.os.Build;
import android.os.Looper;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.support.v4.os.BuildCompat;
+import android.webkit.ValueCallback;
import android.webkit.WebView;
import org.chromium.support_lib_boundary.WebViewProviderBoundaryInterface;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import java.util.List;
import androidx.webkit.internal.WebViewGlueCommunicator;
import androidx.webkit.internal.WebViewProviderAdapter;
@@ -120,6 +125,79 @@
}
}
+ /**
+ * Starts Safe Browsing initialization.
+ * <p>
+ * URL loads are not guaranteed to be protected by Safe Browsing until after {@code callback} is
+ * invoked with {@code true}. Safe Browsing is not fully supported on all devices. For those
+ * devices {@code callback} will receive {@code false}.
+ * <p>
+ * This should not be called if Safe Browsing has been disabled by manifest tag or {@link
+ * android.webkit.WebSettings#setSafeBrowsingEnabled}. This prepares resources used for Safe
+ * Browsing.
+ * <p>
+ * This should be called with the Application Context (and will always use the Application
+ * context to do its work regardless).
+ *
+ * @param context Application Context.
+ * @param callback will be called on the UI thread with {@code true} if initialization is
+ * successful, {@code false} otherwise.
+ */
+ public static void startSafeBrowsing(@NonNull Context context,
+ @Nullable ValueCallback<Boolean> callback) {
+ if (Build.VERSION.SDK_INT >= 27) {
+ WebView.startSafeBrowsing(context, callback);
+ } else { // TODO(gsennton): guard with WebViewApk.hasFeature(SafeBrowsing)
+ getFactory().getStatics().initSafeBrowsing(context, callback);
+ }
+ }
+
+ /**
+ * Sets the list of hosts (domain names/IP addresses) that are exempt from SafeBrowsing checks.
+ * The list is global for all the WebViews.
+ * <p>
+ * Each rule should take one of these:
+ * <table>
+ * <tr><th> Rule </th> <th> Example </th> <th> Matches Subdomain</th> </tr>
+ * <tr><td> HOSTNAME </td> <td> example.com </td> <td> Yes </td> </tr>
+ * <tr><td> .HOSTNAME </td> <td> .example.com </td> <td> No </td> </tr>
+ * <tr><td> IPV4_LITERAL </td> <td> 192.168.1.1 </td> <td> No </td></tr>
+ * <tr><td> IPV6_LITERAL_WITH_BRACKETS </td><td>[10:20:30:40:50:60:70:80]</td><td>No</td></tr>
+ * </table>
+ * <p>
+ * All other rules, including wildcards, are invalid.
+ * <p>
+ * The correct syntax for hosts is defined by <a
+ * href="https://tools.ietf.org/html/rfc3986#section-3.2.2">RFC 3986</a>.
+ *
+ * @param hosts the list of hosts
+ * @param callback will be called with {@code true} if hosts are successfully added to the
+ * whitelist. It will be called with {@code false} if any hosts are malformed. The callback
+ * will be run on the UI thread
+ */
+ public static void setSafeBrowsingWhitelist(@NonNull List<String> hosts,
+ @Nullable ValueCallback<Boolean> callback) {
+ if (Build.VERSION.SDK_INT >= 27) {
+ WebView.setSafeBrowsingWhitelist(hosts, callback);
+ } else { // TODO(gsennton): guard with WebViewApk.hasFeature(SafeBrowsing)
+ getFactory().getStatics().setSafeBrowsingWhitelist(hosts, callback);
+ }
+ }
+
+ /**
+ * Returns a URL pointing to the privacy policy for Safe Browsing reporting.
+ *
+ * @return the url pointing to a privacy policy document which can be displayed to users.
+ */
+ @NonNull
+ public static Uri getSafeBrowsingPrivacyPolicyUrl() {
+ if (Build.VERSION.SDK_INT >= 27) {
+ return WebView.getSafeBrowsingPrivacyPolicyUrl();
+ } else { // TODO(gsennton): guard with WebViewApk.hasFeature(SafeBrowsing)
+ return getFactory().getStatics().getSafeBrowsingPrivacyPolicyUrl();
+ }
+ }
+
private static WebViewProviderAdapter getProvider(WebView webview) {
return new WebViewProviderAdapter(createProvider(webview));
}
diff --git a/webkit/src/main/java/androidx/webkit/internal/WebViewProviderFactoryAdapter.java b/webkit/src/main/java/androidx/webkit/internal/WebViewProviderFactoryAdapter.java
index fe98a56..80067ed 100644
--- a/webkit/src/main/java/androidx/webkit/internal/WebViewProviderFactoryAdapter.java
+++ b/webkit/src/main/java/androidx/webkit/internal/WebViewProviderFactoryAdapter.java
@@ -19,6 +19,7 @@
import android.webkit.WebView;
import org.chromium.support_lib_boundary.BoundaryInterfaceReflectionUtil;
+import org.chromium.support_lib_boundary.StaticsBoundaryInterface;
import org.chromium.support_lib_boundary.WebViewProviderBoundaryInterface;
import org.chromium.support_lib_boundary.WebViewProviderFactoryBoundaryInterface;
import org.chromium.support_lib_boundary.WebkitToCompatConverterBoundaryInterface;
@@ -53,4 +54,13 @@
return BoundaryInterfaceReflectionUtil.castToSuppLibClass(
WebkitToCompatConverterBoundaryInterface.class, mImpl.getWebkitToCompatConverter());
}
+
+ /**
+ * Adapter method for fetching the support library class representing
+ * {@link android.webkit.WebViewFactoryProvider#Statics}.
+ */
+ public StaticsBoundaryInterface getStatics() {
+ return BoundaryInterfaceReflectionUtil.castToSuppLibClass(
+ StaticsBoundaryInterface.class, mImpl.getStatics());
+ }
}