blob: d04d10d899934a7146f9bc605622e0ec4fcdb470 [file] [log] [blame]
// Copyright 2017 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.test;
import static org.junit.Assert.assertNotEquals;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.support.test.InstrumentationRegistry;
import android.view.ViewGroup;
import androidx.test.filters.SmallTest;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.android_webview.AwBrowserContext;
import org.chromium.android_webview.AwContents;
import org.chromium.android_webview.AwContents.DependencyFactory;
import org.chromium.android_webview.AwContents.InternalAccessDelegate;
import org.chromium.android_webview.AwContents.NativeDrawFunctorFactory;
import org.chromium.android_webview.AwContentsClient;
import org.chromium.android_webview.AwContentsStatics;
import org.chromium.android_webview.AwSettings;
import org.chromium.android_webview.SafeBrowsingAction;
import org.chromium.android_webview.WebviewErrorCode;
import org.chromium.android_webview.common.AwSwitches;
import org.chromium.android_webview.safe_browsing.AwSafeBrowsingConfigHelper;
import org.chromium.android_webview.safe_browsing.AwSafeBrowsingConversionHelper;
import org.chromium.android_webview.safe_browsing.AwSafeBrowsingResponse;
import org.chromium.android_webview.test.TestAwContentsClient.OnReceivedError2Helper;
import org.chromium.android_webview.test.util.GraphicsTestUtils;
import org.chromium.base.Callback;
import org.chromium.base.ContextUtils;
import org.chromium.base.ThreadUtils;
import org.chromium.base.task.PostTask;
import org.chromium.base.test.util.CallbackHelper;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.Criteria;
import org.chromium.base.test.util.CriteriaHelper;
import org.chromium.base.test.util.CriteriaNotSatisfiedException;
import org.chromium.base.test.util.DisabledTest;
import org.chromium.base.test.util.Feature;
import org.chromium.base.test.util.InMemorySharedPreferences;
import org.chromium.components.safe_browsing.SafeBrowsingApiBridge;
import org.chromium.components.safe_browsing.SafeBrowsingApiHandler;
import org.chromium.content_public.browser.UiThreadTaskTraits;
import org.chromium.content_public.browser.test.util.TestThreadUtils;
import org.chromium.content_public.common.ContentUrlConstants;
import org.chromium.net.test.EmbeddedTestServer;
import org.chromium.url.GURL;
import java.util.ArrayList;
import java.util.Arrays;
/**
* Test suite for SafeBrowsing.
*
* Ensures that interstitials can be successfully created for malicous pages.
*/
@RunWith(AwJUnit4ClassRunner.class)
public class SafeBrowsingTest {
@Rule
public AwActivityTestRule mActivityTestRule = new AwActivityTestRule() {
/**
* Creates a special BrowserContext that has a safebrowsing api handler which always says
* sites are malicious
*/
@Override
public AwBrowserContext createAwBrowserContextOnUiThread(InMemorySharedPreferences prefs) {
return new MockAwBrowserContext(prefs);
}
};
private SafeBrowsingContentsClient mContentsClient;
private AwTestContainerView mContainerView;
private MockAwContents mAwContents;
private EmbeddedTestServer mTestServer;
// Used to check which thread a callback is invoked on.
private volatile boolean mOnUiThread;
// Used to verify the getSafeBrowsingPrivacyPolicyUrl() API.
private volatile Uri mPrivacyPolicyUrl;
// These colors correspond to the body.background attribute in GREEN_HTML_PATH, SAFE_HTML_PATH,
// MALWARE_HTML_PATH, IFRAME_HTML_PATH, etc. They should only be changed if those values are
// changed as well
private static final int GREEN_PAGE_BACKGROUND_COLOR = Color.rgb(0, 255, 0);
private static final int SAFE_PAGE_BACKGROUND_COLOR = Color.rgb(0, 0, 255);
private static final int PHISHING_PAGE_BACKGROUND_COLOR = Color.rgb(0, 0, 255);
private static final int MALWARE_PAGE_BACKGROUND_COLOR = Color.rgb(0, 0, 255);
private static final int UNWANTED_SOFTWARE_PAGE_BACKGROUND_COLOR = Color.rgb(0, 0, 255);
private static final int BILLING_PAGE_BACKGROUND_COLOR = Color.rgb(0, 0, 255);
private static final int IFRAME_EMBEDDER_BACKGROUND_COLOR = Color.rgb(10, 10, 10);
private static final String RESOURCE_PATH = "/android_webview/test/data";
// A blank green page
private static final String GREEN_HTML_PATH = RESOURCE_PATH + "/green.html";
// Blank blue pages
private static final String SAFE_HTML_PATH = RESOURCE_PATH + "/safe.html";
private static final String PHISHING_HTML_PATH = RESOURCE_PATH + "/phishing.html";
private static final String MALWARE_HTML_PATH = RESOURCE_PATH + "/malware.html";
private static final String MALWARE_WITH_IMAGE_HTML_PATH =
RESOURCE_PATH + "/malware_with_image.html";
private static final String UNWANTED_SOFTWARE_HTML_PATH =
RESOURCE_PATH + "/unwanted_software.html";
private static final String BILLING_HTML_PATH = RESOURCE_PATH + "/billing.html";
// A gray page with an iframe to MALWARE_HTML_PATH
private static final String IFRAME_HTML_PATH = RESOURCE_PATH + "/iframe.html";
// These URLs will be CTS-tested and should not be changed.
private static final String WEB_UI_MALWARE_URL = "chrome://safe-browsing/match?type=malware";
private static final String WEB_UI_PHISHING_URL = "chrome://safe-browsing/match?type=phishing";
private static final String WEB_UI_HOST = "safe-browsing";
/**
* A fake SafeBrowsingApiHandler which treats URLs ending in MALWARE_HTML_PATH as malicious URLs
* that should be blocked.
*/
public static class MockSafeBrowsingApiHandler implements SafeBrowsingApiHandler {
private Observer mObserver;
private static final String SAFE_METADATA = "{}";
// These codes are defined in "safebrowsing.proto".
private static final int PHISHING_CODE = 5;
private static final int MALWARE_CODE = 4;
private static final int UNWANTED_SOFTWARE_CODE = 3;
private static final int BILLING_CODE = 15;
// Mock time it takes for a lookup request to complete.
private static final long CHECK_DELTA_US = 10;
@Override
public boolean init(Observer result) {
mObserver = result;
return true;
}
private String buildMetadataFromCode(int code) {
return "{\"matches\":[{\"threat_type\":\"" + code + "\"}]}";
}
@Override
public void startUriLookup(final long callbackId, String uri, int[] threatsOfInterest) {
final String metadata;
Arrays.sort(threatsOfInterest);
if (uri.endsWith(PHISHING_HTML_PATH)
&& Arrays.binarySearch(threatsOfInterest, PHISHING_CODE) >= 0) {
metadata = buildMetadataFromCode(PHISHING_CODE);
} else if (uri.endsWith(MALWARE_HTML_PATH)
&& Arrays.binarySearch(threatsOfInterest, MALWARE_CODE) >= 0) {
metadata = buildMetadataFromCode(MALWARE_CODE);
} else if (uri.endsWith(UNWANTED_SOFTWARE_HTML_PATH)
&& Arrays.binarySearch(threatsOfInterest, UNWANTED_SOFTWARE_CODE) >= 0) {
metadata = buildMetadataFromCode(UNWANTED_SOFTWARE_CODE);
} else if (uri.endsWith(BILLING_HTML_PATH)
&& Arrays.binarySearch(threatsOfInterest, BILLING_CODE) >= 0) {
metadata = buildMetadataFromCode(BILLING_CODE);
} else {
metadata = SAFE_METADATA;
}
// clang-format off
PostTask.runOrPostTask(UiThreadTaskTraits.DEFAULT,
(Runnable) () -> mObserver.onUrlCheckDone(
callbackId, SafeBrowsingResult.SUCCESS, metadata, CHECK_DELTA_US));
// clang-format on
}
@Override
public boolean startAllowlistLookup(final String uri, int threatType) {
return false;
}
}
/**
* A fake AwBrowserContext which loads the MockSafeBrowsingApiHandler instead of the real one.
*/
private static class MockAwBrowserContext extends AwBrowserContext {
public MockAwBrowserContext(SharedPreferences sharedPreferences) {
super(sharedPreferences, 0, true);
SafeBrowsingApiBridge.setSafeBrowsingHandlerType(MockSafeBrowsingApiHandler.class);
}
}
private static class MockAwContents extends TestAwContents {
private boolean mCanShowInterstitial;
private boolean mCanShowBigInterstitial;
public MockAwContents(AwBrowserContext browserContext, ViewGroup containerView,
Context context, InternalAccessDelegate internalAccessAdapter,
NativeDrawFunctorFactory nativeDrawFunctorFactory, AwContentsClient contentsClient,
AwSettings settings, DependencyFactory dependencyFactory) {
super(browserContext, containerView, context, internalAccessAdapter,
nativeDrawFunctorFactory, contentsClient, settings, dependencyFactory);
mCanShowInterstitial = true;
mCanShowBigInterstitial = true;
}
public void setCanShowInterstitial(boolean able) {
mCanShowInterstitial = able;
}
public void setCanShowBigInterstitial(boolean able) {
mCanShowBigInterstitial = able;
}
@Override
protected boolean canShowInterstitial() {
return mCanShowInterstitial;
}
@Override
protected boolean canShowBigInterstitial() {
return mCanShowBigInterstitial;
}
}
/**
* An AwContentsClient with customizable behavior for onSafeBrowsingHit().
*/
private static class SafeBrowsingContentsClient extends TestAwContentsClient {
private AwWebResourceRequest mLastRequest;
private int mLastThreatType;
private int mAction = SafeBrowsingAction.SHOW_INTERSTITIAL;
private int mOnSafeBrowsingHitCount;
private boolean mReporting = true;
@Override
public void onSafeBrowsingHit(AwWebResourceRequest request, int threatType,
Callback<AwSafeBrowsingResponse> callback) {
mLastRequest = request;
mLastThreatType = threatType;
mOnSafeBrowsingHitCount++;
callback.onResult(new AwSafeBrowsingResponse(mAction, mReporting));
}
public AwWebResourceRequest getLastRequest() {
return mLastRequest;
}
public int getLastThreatType() {
return mLastThreatType;
}
public int getOnSafeBrowsingHitCount() {
return mOnSafeBrowsingHitCount;
}
public void setSafeBrowsingAction(int action) {
mAction = action;
}
public void setReporting(boolean value) {
mReporting = value;
}
}
private static class SafeBrowsingDependencyFactory
extends AwActivityTestRule.TestDependencyFactory {
@Override
public AwContents createAwContents(AwBrowserContext browserContext, ViewGroup containerView,
Context context, InternalAccessDelegate internalAccessAdapter,
NativeDrawFunctorFactory nativeDrawFunctorFactory, AwContentsClient contentsClient,
AwSettings settings, DependencyFactory dependencyFactory) {
return new MockAwContents(browserContext, containerView, context, internalAccessAdapter,
nativeDrawFunctorFactory, contentsClient, settings, dependencyFactory);
}
}
private static class JavaScriptHelper extends CallbackHelper {
private String mValue;
public void setValue(String s) {
mValue = s;
}
public String getValue() {
return mValue;
}
}
private static class AllowlistHelper extends CallbackHelper implements Callback<Boolean> {
public boolean success;
@Override
public void onResult(Boolean success) {
this.success = success;
notifyCalled();
}
}
@Before
public void setUp() {
mContentsClient = new SafeBrowsingContentsClient();
mContainerView = mActivityTestRule.createAwTestContainerViewOnMainSync(
mContentsClient, false, new SafeBrowsingDependencyFactory());
mAwContents = (MockAwContents) mContainerView.getAwContents();
mTestServer = EmbeddedTestServer.createAndStartServer(
InstrumentationRegistry.getInstrumentation().getContext());
// Need to configure user opt-in, otherwise WebView won't perform Safe Browsing checks.
AwSafeBrowsingConfigHelper.setSafeBrowsingUserOptIn(true);
}
@After
public void tearDown() {
mTestServer.stopAndDestroyServer();
}
private int getPageColor() {
Bitmap bitmap = GraphicsTestUtils.drawAwContentsOnUiThread(
mAwContents, mContainerView.getWidth(), mContainerView.getHeight());
return bitmap.getPixel(0, 0);
}
private void loadGreenPage() throws Exception {
mActivityTestRule.loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(),
mTestServer.getURL(GREEN_HTML_PATH));
// Make sure we actually wait for the page to be visible
mActivityTestRule.waitForVisualStateCallback(mAwContents);
}
private void evaluateJavaScriptOnInterstitialOnUiThread(
final String script, final Callback<String> callback) {
PostTask.runOrPostTask(
UiThreadTaskTraits.DEFAULT, () -> mAwContents.evaluateJavaScript(script, callback));
}
private String evaluateJavaScriptOnInterstitialOnUiThreadSync(final String script)
throws Exception {
final JavaScriptHelper helper = new JavaScriptHelper();
final Callback<String> callback = value -> {
helper.setValue(value);
helper.notifyCalled();
};
final int count = helper.getCallCount();
evaluateJavaScriptOnInterstitialOnUiThread(script, callback);
helper.waitForCallback(count);
return helper.getValue();
}
private void waitForInterstitialDomToLoad() {
final String script = "document.readyState;";
final String expected = "\"complete\"";
CriteriaHelper.pollInstrumentationThread(() -> {
try {
Criteria.checkThat(evaluateJavaScriptOnInterstitialOnUiThreadSync(script),
Matchers.is(expected));
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
private void clickBackToSafety() {
clickLinkById("primary-button");
}
private void clickVisitUnsafePageQuietInterstitial() {
clickLinkById("details-link");
clickLinkById("proceed-link");
}
private void clickVisitUnsafePage() {
clickLinkById("details-button");
clickLinkById("proceed-link");
}
private void clickLinkById(String id) {
final String script = "document.getElementById('" + id + "').click();";
evaluateJavaScriptOnInterstitialOnUiThread(script, null);
}
private void loadPathAndWaitForInterstitial(final String path) throws Exception {
final String responseUrl = mTestServer.getURL(path);
mActivityTestRule.loadUrlAsync(mAwContents, responseUrl);
// Subresource triggered interstitials will trigger after the page containing the
// subresource has loaded (and displayed), so we first wait for the interstitial to be
// triggered, then for a visual state callback to allow the interstitial to render.
CriteriaHelper.pollUiThread(() -> mAwContents.isDisplayingInterstitialForTesting());
// Wait for the interstitial to actually render.
mActivityTestRule.waitForVisualStateCallback(mAwContents);
}
private void assertTargetPageHasLoaded(int pageColor) throws Exception {
mActivityTestRule.waitForVisualStateCallback(mAwContents);
Assert.assertEquals("Target page should be visible", colorToString(pageColor),
colorToString(GraphicsTestUtils.getPixelColorAtCenterOfView(
mAwContents, mContainerView)));
}
private void assertGreenPageShowing() {
Assert.assertEquals("Original page should be showing",
colorToString(GREEN_PAGE_BACKGROUND_COLOR),
colorToString(GraphicsTestUtils.getPixelColorAtCenterOfView(
mAwContents, mContainerView)));
}
private void assertGreenPageNotShowing() {
assertNotEquals("Original page should not be showing",
colorToString(GREEN_PAGE_BACKGROUND_COLOR),
colorToString(GraphicsTestUtils.getPixelColorAtCenterOfView(
mAwContents, mContainerView)));
}
private void assertTargetPageNotShowing(int pageColor) {
assertNotEquals("Target page should not be showing", colorToString(pageColor),
colorToString(GraphicsTestUtils.getPixelColorAtCenterOfView(
mAwContents, mContainerView)));
}
/**
* Converts a color from the confusing integer representation to a more readable string
* respresentation. There is a 1:1 mapping between integer and string representations, so it's
* valid to compare strings directly. The string representation is better for assert output.
*
* @param color integer representation of the color
* @return a String representation of the color in RGBA format
*/
private String colorToString(int color) {
return "(" + Color.red(color) + "," + Color.green(color) + "," + Color.blue(color) + ","
+ Color.alpha(color) + ")";
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingGetterAndSetter() throws Throwable {
Assert.assertTrue("Getter API should follow manifest tag by default",
mActivityTestRule.getAwSettingsOnUiThread(mAwContents).getSafeBrowsingEnabled());
mActivityTestRule.getAwSettingsOnUiThread(mAwContents).setSafeBrowsingEnabled(false);
Assert.assertFalse("setSafeBrowsingEnabled(false) should change the getter",
mActivityTestRule.getAwSettingsOnUiThread(mAwContents).getSafeBrowsingEnabled());
mActivityTestRule.getAwSettingsOnUiThread(mAwContents).setSafeBrowsingEnabled(true);
Assert.assertTrue("setSafeBrowsingEnabled(true) should change the getter",
mActivityTestRule.getAwSettingsOnUiThread(mAwContents).getSafeBrowsingEnabled());
AwSafeBrowsingConfigHelper.setSafeBrowsingUserOptIn(false);
Assert.assertTrue("Getter API should ignore user opt-out",
mActivityTestRule.getAwSettingsOnUiThread(mAwContents).getSafeBrowsingEnabled());
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingDoesNotBlockSafePages() throws Throwable {
loadGreenPage();
final String responseUrl = mTestServer.getURL(SAFE_HTML_PATH);
mActivityTestRule.loadUrlSync(
mAwContents, mContentsClient.getOnPageFinishedHelper(), responseUrl);
assertTargetPageHasLoaded(SAFE_PAGE_BACKGROUND_COLOR);
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingBlocksUnwantedSoftwarePages() throws Throwable {
loadGreenPage();
loadPathAndWaitForInterstitial(UNWANTED_SOFTWARE_HTML_PATH);
assertGreenPageNotShowing();
assertTargetPageNotShowing(UNWANTED_SOFTWARE_PAGE_BACKGROUND_COLOR);
// Assume that we are rendering the interstitial, since we see neither the previous page nor
// the target page
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingBlocksBillingPages() throws Throwable {
loadGreenPage();
loadPathAndWaitForInterstitial(BILLING_HTML_PATH);
assertGreenPageNotShowing();
assertTargetPageNotShowing(BILLING_PAGE_BACKGROUND_COLOR);
// Assume that we are rendering the interstitial, since we see neither the previous page nor
// the target page
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingOnSafeBrowsingHitBillingCode() throws Throwable {
loadGreenPage();
loadPathAndWaitForInterstitial(BILLING_HTML_PATH);
// Check onSafeBrowsingHit arguments
final String responseUrl = mTestServer.getURL(BILLING_HTML_PATH);
Assert.assertEquals(responseUrl, mContentsClient.getLastRequest().url);
// The expectedCode intentionally depends on targetSdk (and is disconnected from SDK_INT).
// This is for backwards compatibility with apps with a lower targetSdk.
int expectedCode =
ContextUtils.getApplicationContext().getApplicationInfo().targetSdkVersion
>= Build.VERSION_CODES.Q
? AwSafeBrowsingConversionHelper.SAFE_BROWSING_THREAT_BILLING
: AwSafeBrowsingConversionHelper.SAFE_BROWSING_THREAT_UNKNOWN;
Assert.assertEquals(expectedCode, mContentsClient.getLastThreatType());
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingBlocksPhishingPages() throws Throwable {
loadGreenPage();
loadPathAndWaitForInterstitial(PHISHING_HTML_PATH);
assertGreenPageNotShowing();
assertTargetPageNotShowing(PHISHING_PAGE_BACKGROUND_COLOR);
// Assume that we are rendering the interstitial, since we see neither the previous page nor
// the target page
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingAllowlistedUnsafePagesDontShowInterstitial() throws Throwable {
loadGreenPage();
final String responseUrl = mTestServer.getURL(MALWARE_HTML_PATH);
verifyAllowlistRule(Uri.parse(responseUrl).getHost(), true);
mActivityTestRule.loadUrlSync(
mAwContents, mContentsClient.getOnPageFinishedHelper(), responseUrl);
assertTargetPageHasLoaded(MALWARE_PAGE_BACKGROUND_COLOR);
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingAllowlistHardcodedWebUiPages() throws Throwable {
loadGreenPage();
verifyAllowlistRule(WEB_UI_HOST, true);
mActivityTestRule.loadUrlSync(
mAwContents, mContentsClient.getOnPageFinishedHelper(), WEB_UI_MALWARE_URL);
mActivityTestRule.loadUrlSync(
mAwContents, mContentsClient.getOnPageFinishedHelper(), WEB_UI_PHISHING_URL);
// Assume the pages are allowed, since we successfully loaded them.
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingAllowlistHardcodedWebUiPageBackToSafety() throws Throwable {
mContentsClient.setSafeBrowsingAction(SafeBrowsingAction.BACK_TO_SAFETY);
loadGreenPage();
OnReceivedError2Helper errorHelper = mContentsClient.getOnReceivedError2Helper();
int errorCount = errorHelper.getCallCount();
mActivityTestRule.loadUrlSync(
mAwContents, mContentsClient.getOnPageFinishedHelper(), WEB_UI_MALWARE_URL);
errorHelper.waitForCallback(errorCount);
Assert.assertEquals(
WebviewErrorCode.ERROR_UNSAFE_RESOURCE, errorHelper.getError().errorCode);
Assert.assertEquals("Network error is for the malicious page", WEB_UI_MALWARE_URL,
errorHelper.getRequest().url);
assertGreenPageShowing();
// Check onSafeBrowsingHit arguments
Assert.assertEquals(WEB_UI_MALWARE_URL, mContentsClient.getLastRequest().url);
Assert.assertEquals(AwSafeBrowsingConversionHelper.SAFE_BROWSING_THREAT_MALWARE,
mContentsClient.getLastThreatType());
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testCallbackCalledOnSafeBrowsingBadAllowlistRule() throws Throwable {
verifyAllowlistRule("http://www.google.com", false);
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testCallbackCalledOnSafeBrowsingGoodAllowlistRule() throws Throwable {
verifyAllowlistRule("www.google.com", true);
}
private void verifyAllowlistRule(final String rule, boolean expected) throws Throwable {
final AllowlistHelper helper = new AllowlistHelper();
final int count = helper.getCallCount();
TestThreadUtils.runOnUiThreadBlocking(() -> {
ArrayList<String> s = new ArrayList<String>();
s.add(rule);
AwContentsStatics.setSafeBrowsingAllowlist(s, helper);
});
helper.waitForCallback(count);
Assert.assertEquals(expected, helper.success);
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingShowsInterstitialForMainFrame() throws Throwable {
loadGreenPage();
loadPathAndWaitForInterstitial(MALWARE_HTML_PATH);
assertGreenPageNotShowing();
assertTargetPageNotShowing(MALWARE_PAGE_BACKGROUND_COLOR);
// Assume that we are rendering the interstitial, since we see neither the previous page
// nor the target page
}
@Test
@DisabledTest(message = "Wait for interstitial is flaky. crbug.com/1107540")
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingShowsInterstitialForSubresource() throws Throwable {
loadGreenPage();
loadPathAndWaitForInterstitial(IFRAME_HTML_PATH);
assertGreenPageNotShowing();
assertTargetPageNotShowing(IFRAME_EMBEDDER_BACKGROUND_COLOR);
// Assume that we are rendering the interstitial, since we see neither the previous page
// nor the target page
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingProceedThroughInterstitialForMainFrame() throws Throwable {
int pageFinishedCount = mContentsClient.getOnPageFinishedHelper().getCallCount();
loadPathAndWaitForInterstitial(MALWARE_HTML_PATH);
waitForInterstitialDomToLoad();
int onSafeBrowsingCountBeforeClick = mContentsClient.getOnSafeBrowsingHitCount();
clickVisitUnsafePage();
mContentsClient.getOnPageFinishedHelper().waitForCallback(pageFinishedCount);
assertTargetPageHasLoaded(MALWARE_PAGE_BACKGROUND_COLOR);
// Check there is not an extra onSafeBrowsingHit call after proceeding.
Assert.assertEquals(
onSafeBrowsingCountBeforeClick, mContentsClient.getOnSafeBrowsingHitCount());
}
@Test
@DisabledTest(message = "Wait for interstitial is flaky. crbug.com/1107540")
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingProceedThroughInterstitialForSubresource() throws Throwable {
int pageFinishedCount = mContentsClient.getOnPageFinishedHelper().getCallCount();
loadPathAndWaitForInterstitial(IFRAME_HTML_PATH);
waitForInterstitialDomToLoad();
clickVisitUnsafePage();
// For subresources, the initial site finishes loading before the interstitial is shown,
// causing an extra onPageFinished call if committed interstitials are enabled (since the
// proceed action triggers a reload).
mContentsClient.getOnPageFinishedHelper().waitForCallback(pageFinishedCount, 2);
assertTargetPageHasLoaded(IFRAME_EMBEDDER_BACKGROUND_COLOR);
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingDontProceedCausesNetworkErrorForMainFrame() throws Throwable {
loadGreenPage();
loadPathAndWaitForInterstitial(MALWARE_HTML_PATH);
OnReceivedError2Helper errorHelper = mContentsClient.getOnReceivedError2Helper();
int errorCount = errorHelper.getCallCount();
waitForInterstitialDomToLoad();
clickBackToSafety();
errorHelper.waitForCallback(errorCount);
Assert.assertEquals(
WebviewErrorCode.ERROR_UNSAFE_RESOURCE, errorHelper.getError().errorCode);
final String responseUrl = mTestServer.getURL(MALWARE_HTML_PATH);
Assert.assertEquals("Network error is for the malicious page", responseUrl,
errorHelper.getRequest().url);
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingDontProceedNavigatesBackForMainFrame() throws Throwable {
loadGreenPage();
loadPathAndWaitForInterstitial(MALWARE_HTML_PATH);
waitForInterstitialDomToLoad();
OnReceivedError2Helper errorHelper = mContentsClient.getOnReceivedError2Helper();
int errorCount = errorHelper.getCallCount();
clickBackToSafety();
errorHelper.waitForCallback(errorCount);
mActivityTestRule.waitForVisualStateCallback(mAwContents);
assertTargetPageNotShowing(MALWARE_PAGE_BACKGROUND_COLOR);
assertGreenPageShowing();
}
@Test
@DisabledTest(message = "Wait for interstitial is flaky. crbug.com/1107540")
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingDontProceedNavigatesBackForSubResource() throws Throwable {
loadGreenPage();
loadPathAndWaitForInterstitial(IFRAME_HTML_PATH);
waitForInterstitialDomToLoad();
OnReceivedError2Helper errorHelper = mContentsClient.getOnReceivedError2Helper();
int errorCount = errorHelper.getCallCount();
clickBackToSafety();
errorHelper.waitForCallback(errorCount);
mActivityTestRule.waitForVisualStateCallback(mAwContents);
assertTargetPageNotShowing(IFRAME_EMBEDDER_BACKGROUND_COLOR);
assertGreenPageShowing();
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingCanBeDisabledPerWebview() throws Throwable {
mActivityTestRule.getAwSettingsOnUiThread(mAwContents).setSafeBrowsingEnabled(false);
final String responseUrl = mTestServer.getURL(MALWARE_HTML_PATH);
mActivityTestRule.loadUrlSync(
mAwContents, mContentsClient.getOnPageFinishedHelper(), responseUrl);
assertTargetPageHasLoaded(MALWARE_PAGE_BACKGROUND_COLOR);
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingCanBeDisabledPerWebview_withImage() throws Throwable {
// In particular this test checks that there is no crash when network service
// is enabled, safebrowsing is disabled and the RendererURLLoaderThrottle
// attempts to check a url through loading an image (crbug.com/889479).
mActivityTestRule.getAwSettingsOnUiThread(mAwContents).setSafeBrowsingEnabled(false);
final String responseUrl = mTestServer.getURL(MALWARE_WITH_IMAGE_HTML_PATH);
mActivityTestRule.loadUrlSync(
mAwContents, mContentsClient.getOnPageFinishedHelper(), responseUrl);
assertTargetPageHasLoaded(MALWARE_PAGE_BACKGROUND_COLOR);
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
@CommandLineFlags.Add(AwSwitches.WEBVIEW_DISABLE_SAFEBROWSING_SUPPORT)
public void testSafeBrowsingCanBeEnabledPerWebview() throws Throwable {
final String responseUrl = mTestServer.getURL(MALWARE_HTML_PATH);
mActivityTestRule.loadUrlSync(
mAwContents, mContentsClient.getOnPageFinishedHelper(), responseUrl);
assertTargetPageHasLoaded(MALWARE_PAGE_BACKGROUND_COLOR);
mActivityTestRule.getAwSettingsOnUiThread(mAwContents).setSafeBrowsingEnabled(true);
loadGreenPage();
loadPathAndWaitForInterstitial(MALWARE_HTML_PATH);
assertGreenPageNotShowing();
assertTargetPageNotShowing(MALWARE_PAGE_BACKGROUND_COLOR);
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingShowsNetworkErrorForInvisibleViews() throws Throwable {
mAwContents.setCanShowInterstitial(false);
mAwContents.setCanShowBigInterstitial(false);
final String responseUrl = mTestServer.getURL(MALWARE_HTML_PATH);
OnReceivedError2Helper errorHelper = mContentsClient.getOnReceivedError2Helper();
int errorCount = errorHelper.getCallCount();
mActivityTestRule.loadUrlAsync(mAwContents, responseUrl);
errorHelper.waitForCallback(errorCount);
Assert.assertEquals(
WebviewErrorCode.ERROR_UNSAFE_RESOURCE, errorHelper.getError().errorCode);
Assert.assertEquals("Network error is for the malicious page", responseUrl,
errorHelper.getRequest().url);
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingShowsQuietInterstitialForOddSizedViews() throws Throwable {
mAwContents.setCanShowBigInterstitial(false);
loadGreenPage();
loadPathAndWaitForInterstitial(MALWARE_HTML_PATH);
assertGreenPageNotShowing();
assertTargetPageNotShowing(MALWARE_PAGE_BACKGROUND_COLOR);
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingCanShowQuietPhishingInterstitial() throws Throwable {
mAwContents.setCanShowBigInterstitial(false);
loadGreenPage();
loadPathAndWaitForInterstitial(PHISHING_HTML_PATH);
assertGreenPageNotShowing();
assertTargetPageNotShowing(PHISHING_PAGE_BACKGROUND_COLOR);
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingCanShowQuietUnwantedSoftwareInterstitial() throws Throwable {
mAwContents.setCanShowBigInterstitial(false);
loadGreenPage();
loadPathAndWaitForInterstitial(UNWANTED_SOFTWARE_HTML_PATH);
assertGreenPageNotShowing();
assertTargetPageNotShowing(UNWANTED_SOFTWARE_PAGE_BACKGROUND_COLOR);
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingCanShowQuietBillingInterstitial() throws Throwable {
mAwContents.setCanShowBigInterstitial(false);
loadGreenPage();
loadPathAndWaitForInterstitial(BILLING_HTML_PATH);
assertGreenPageNotShowing();
assertTargetPageNotShowing(BILLING_PAGE_BACKGROUND_COLOR);
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingProceedQuietInterstitial() throws Throwable {
mAwContents.setCanShowBigInterstitial(false);
int pageFinishedCount = mContentsClient.getOnPageFinishedHelper().getCallCount();
loadPathAndWaitForInterstitial(PHISHING_HTML_PATH);
waitForInterstitialDomToLoad();
clickVisitUnsafePageQuietInterstitial();
mContentsClient.getOnPageFinishedHelper().waitForCallback(pageFinishedCount);
assertTargetPageHasLoaded(PHISHING_PAGE_BACKGROUND_COLOR);
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingOnSafeBrowsingHitShowInterstitial() throws Throwable {
mContentsClient.setSafeBrowsingAction(SafeBrowsingAction.SHOW_INTERSTITIAL);
loadGreenPage();
loadPathAndWaitForInterstitial(PHISHING_HTML_PATH);
assertGreenPageNotShowing();
assertTargetPageNotShowing(PHISHING_PAGE_BACKGROUND_COLOR);
// Assume that we are rendering the interstitial, since we see neither the previous page nor
// the target page
// Check onSafeBrowsingHit arguments
final String responseUrl = mTestServer.getURL(PHISHING_HTML_PATH);
Assert.assertEquals(responseUrl, mContentsClient.getLastRequest().url);
Assert.assertEquals(AwSafeBrowsingConversionHelper.SAFE_BROWSING_THREAT_PHISHING,
mContentsClient.getLastThreatType());
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingOnSafeBrowsingHitProceed() throws Throwable {
mContentsClient.setSafeBrowsingAction(SafeBrowsingAction.PROCEED);
loadGreenPage();
final String responseUrl = mTestServer.getURL(PHISHING_HTML_PATH);
mActivityTestRule.loadUrlSync(
mAwContents, mContentsClient.getOnPageFinishedHelper(), responseUrl);
mActivityTestRule.waitForVisualStateCallback(mAwContents);
assertTargetPageHasLoaded(PHISHING_PAGE_BACKGROUND_COLOR);
// Check onSafeBrowsingHit arguments
Assert.assertEquals(responseUrl, mContentsClient.getLastRequest().url);
Assert.assertEquals(AwSafeBrowsingConversionHelper.SAFE_BROWSING_THREAT_PHISHING,
mContentsClient.getLastThreatType());
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingOnSafeBrowsingHitBackToSafety() throws Throwable {
mContentsClient.setSafeBrowsingAction(SafeBrowsingAction.BACK_TO_SAFETY);
loadGreenPage();
final String responseUrl = mTestServer.getURL(MALWARE_HTML_PATH);
OnReceivedError2Helper errorHelper = mContentsClient.getOnReceivedError2Helper();
int errorCount = errorHelper.getCallCount();
mActivityTestRule.loadUrlAsync(mAwContents, responseUrl);
errorHelper.waitForCallback(errorCount);
Assert.assertEquals(
WebviewErrorCode.ERROR_UNSAFE_RESOURCE, errorHelper.getError().errorCode);
Assert.assertEquals("Network error is for the malicious page", responseUrl,
errorHelper.getRequest().url);
assertGreenPageShowing();
// Check onSafeBrowsingHit arguments
Assert.assertEquals(responseUrl, mContentsClient.getLastRequest().url);
Assert.assertEquals(AwSafeBrowsingConversionHelper.SAFE_BROWSING_THREAT_MALWARE,
mContentsClient.getLastThreatType());
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingOnSafeBrowsingHitForSubresourceNoPreviousPage() throws Throwable {
mContentsClient.setSafeBrowsingAction(SafeBrowsingAction.BACK_TO_SAFETY);
final String responseUrl = mTestServer.getURL(IFRAME_HTML_PATH);
final String subresourceUrl = mTestServer.getURL(MALWARE_HTML_PATH);
int pageFinishedCount = mContentsClient.getOnPageFinishedHelper().getCallCount();
mActivityTestRule.loadUrlAsync(mAwContents, responseUrl);
// We'll successfully load IFRAME_HTML_PATH, and will soon call onSafeBrowsingHit
mContentsClient.getOnPageFinishedHelper().waitForCallback(pageFinishedCount);
final GURL aboutBlank = new GURL(ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
// Wait for the onSafeBrowsingHit to call BACK_TO_SAFETY and navigate back
mActivityTestRule.pollUiThread(() -> aboutBlank.equals(mAwContents.getUrl()));
// Check onSafeBrowsingHit arguments
Assert.assertFalse(mContentsClient.getLastRequest().isMainFrame);
Assert.assertEquals(subresourceUrl, mContentsClient.getLastRequest().url);
Assert.assertEquals(AwSafeBrowsingConversionHelper.SAFE_BROWSING_THREAT_MALWARE,
mContentsClient.getLastThreatType());
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingOnSafeBrowsingHitForSubresource() throws Throwable {
mContentsClient.setSafeBrowsingAction(SafeBrowsingAction.BACK_TO_SAFETY);
loadGreenPage();
final String responseUrl = mTestServer.getURL(IFRAME_HTML_PATH);
final String subresourceUrl = mTestServer.getURL(MALWARE_HTML_PATH);
int pageFinishedCount = mContentsClient.getOnPageFinishedHelper().getCallCount();
mActivityTestRule.loadUrlAsync(mAwContents, responseUrl);
// We'll successfully load IFRAME_HTML_PATH, and will soon call onSafeBrowsingHit
mContentsClient.getOnPageFinishedHelper().waitForCallback(pageFinishedCount);
// Wait for the onSafeBrowsingHit to call BACK_TO_SAFETY and navigate back
// clang-format off
mActivityTestRule.pollUiThread(() -> colorToString(GREEN_PAGE_BACKGROUND_COLOR).equals(
colorToString(GraphicsTestUtils.getPixelColorAtCenterOfView(mAwContents,
mContainerView))));
// clang-format on
// Check onSafeBrowsingHit arguments
Assert.assertFalse(mContentsClient.getLastRequest().isMainFrame);
Assert.assertEquals(subresourceUrl, mContentsClient.getLastRequest().url);
Assert.assertEquals(AwSafeBrowsingConversionHelper.SAFE_BROWSING_THREAT_MALWARE,
mContentsClient.getLastThreatType());
mContentsClient.setSafeBrowsingAction(SafeBrowsingAction.PROCEED);
mActivityTestRule.loadUrlSync(
mAwContents, mContentsClient.getOnPageFinishedHelper(), responseUrl);
mActivityTestRule.waitForVisualStateCallback(mAwContents);
assertTargetPageHasLoaded(IFRAME_EMBEDDER_BACKGROUND_COLOR);
Assert.assertFalse(mContentsClient.getLastRequest().isMainFrame);
Assert.assertEquals(subresourceUrl, mContentsClient.getLastRequest().url);
Assert.assertEquals(AwSafeBrowsingConversionHelper.SAFE_BROWSING_THREAT_MALWARE,
mContentsClient.getLastThreatType());
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingOnSafeBrowsingHitHideReportingCheckbox() throws Throwable {
mContentsClient.setReporting(false);
loadGreenPage();
loadPathAndWaitForInterstitial(PHISHING_HTML_PATH);
waitForInterstitialDomToLoad();
Assert.assertFalse(getVisibilityByIdOnInterstitial("extended-reporting-opt-in"));
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingReportingCheckboxVisibleByDefault() throws Throwable {
loadGreenPage();
loadPathAndWaitForInterstitial(PHISHING_HTML_PATH);
waitForInterstitialDomToLoad();
Assert.assertTrue(getVisibilityByIdOnInterstitial("extended-reporting-opt-in"));
}
/**
* @return whether {@code domNodeId} is visible on the interstitial page.
* @throws Exception if the node cannot be found in the interstitial DOM or unable to evaluate
* JS.
*/
private boolean getVisibilityByIdOnInterstitial(String domNodeId) throws Exception {
// clang-format off
final String script =
"(function isNodeVisible(node) {"
+ " if (!node) return 'node not found';"
+ " return !node.classList.contains('hidden');"
+ "})(document.getElementById('" + domNodeId + "'))";
// clang-format on
String value = evaluateJavaScriptOnInterstitialOnUiThreadSync(script);
if (value.equals("true")) {
return true;
} else if (value.equals("false")) {
return false;
} else {
throw new Exception("Node not found");
}
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingUserOptOutOverridesManifest() throws Throwable {
AwSafeBrowsingConfigHelper.setSafeBrowsingUserOptIn(false);
loadGreenPage();
final String responseUrl = mTestServer.getURL(MALWARE_HTML_PATH);
mActivityTestRule.loadUrlSync(
mAwContents, mContentsClient.getOnPageFinishedHelper(), responseUrl);
assertTargetPageHasLoaded(MALWARE_PAGE_BACKGROUND_COLOR);
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingUserOptOutOverridesPerWebView() throws Throwable {
AwSafeBrowsingConfigHelper.setSafeBrowsingUserOptIn(false);
mActivityTestRule.getAwSettingsOnUiThread(mAwContents).setSafeBrowsingEnabled(true);
loadGreenPage();
final String responseUrl = mTestServer.getURL(MALWARE_HTML_PATH);
mActivityTestRule.loadUrlSync(
mAwContents, mContentsClient.getOnPageFinishedHelper(), responseUrl);
assertTargetPageHasLoaded(MALWARE_PAGE_BACKGROUND_COLOR);
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingHardcodedMalwareUrl() throws Throwable {
loadGreenPage();
mActivityTestRule.loadUrlAsync(mAwContents, WEB_UI_MALWARE_URL);
// Wait for the interstitial to actually render.
mActivityTestRule.waitForVisualStateCallback(mAwContents);
waitForInterstitialDomToLoad();
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingHardcodedPhishingUrl() throws Throwable {
loadGreenPage();
mActivityTestRule.loadUrlAsync(mAwContents, WEB_UI_PHISHING_URL);
// Wait for the interstitial to actually render.
mActivityTestRule.waitForVisualStateCallback(mAwContents);
waitForInterstitialDomToLoad();
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingHardcodedUrlsIgnoreUserOptOut() throws Throwable {
AwSafeBrowsingConfigHelper.setSafeBrowsingUserOptIn(false);
loadGreenPage();
mActivityTestRule.loadUrlAsync(mAwContents, WEB_UI_MALWARE_URL);
// Wait for the interstitial to actually render.
mActivityTestRule.waitForVisualStateCallback(mAwContents);
waitForInterstitialDomToLoad();
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingHardcodedUrlsRespectPerWebviewToggle() throws Throwable {
mActivityTestRule.getAwSettingsOnUiThread(mAwContents).setSafeBrowsingEnabled(false);
mActivityTestRule.loadUrlSync(
mAwContents, mContentsClient.getOnPageFinishedHelper(), WEB_UI_MALWARE_URL);
// If we get here, it means the navigation was not blocked by an interstitial.
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingClickLearnMoreLink() throws Throwable {
loadInterstitialAndClickLink(PHISHING_HTML_PATH, "learn-more-link",
appendLocale("https://support.google.com/chrome/?p=cpn_safe_browsing_wv"));
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingClickReportErrorLink() throws Throwable {
// Only phishing interstitials have the report-error-link
final String reportErrorUrl =
Uri.parse("https://safebrowsing.google.com/safebrowsing/report_error/")
.buildUpon()
.appendQueryParameter(
"url", mTestServer.getURL(PHISHING_HTML_PATH).toString())
.appendQueryParameter("hl", getSafeBrowsingLocaleOnUiThreadForTesting())
.toString();
loadInterstitialAndClickLink(PHISHING_HTML_PATH, "report-error-link", reportErrorUrl);
}
private String appendLocale(String url) throws Exception {
return Uri.parse(url)
.buildUpon()
.appendQueryParameter("hl", getSafeBrowsingLocaleOnUiThreadForTesting())
.toString();
}
private String getSafeBrowsingLocaleOnUiThreadForTesting() throws Exception {
return TestThreadUtils.runOnUiThreadBlocking(
() -> AwContents.getSafeBrowsingLocaleForTesting());
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingClickDiagnosticLink() throws Throwable {
final String responseUrl = mTestServer.getURL(MALWARE_HTML_PATH);
final String diagnosticUrl =
Uri.parse("https://transparencyreport.google.com/safe-browsing/search")
.buildUpon()
.appendQueryParameter("url", responseUrl)
.appendQueryParameter("hl", getSafeBrowsingLocaleOnUiThreadForTesting())
.toString();
loadInterstitialAndClickLink(MALWARE_HTML_PATH, "diagnostic-link", diagnosticUrl);
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingClickWhitePaperLink() throws Throwable {
final String whitepaperUrl =
Uri.parse("https://www.google.com/chrome/browser/privacy/whitepaper.html")
.buildUpon()
.appendQueryParameter("hl", getSafeBrowsingLocaleOnUiThreadForTesting())
.fragment("extendedreport")
.toString();
loadInterstitialAndClickLink(PHISHING_HTML_PATH, "whitepaper-link", whitepaperUrl);
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSafeBrowsingClickPrivacyPolicy() throws Throwable {
final String privacyPolicyUrl =
Uri.parse("https://www.google.com/chrome/browser/privacy/")
.buildUpon()
.appendQueryParameter("hl", getSafeBrowsingLocaleOnUiThreadForTesting())
.fragment("safe-browsing-policies")
.toString();
loadInterstitialAndClickLink(PHISHING_HTML_PATH, "privacy-link", privacyPolicyUrl);
}
private void loadInterstitialAndClickLink(String path, String linkId, String linkUrl)
throws Exception {
loadPathAndWaitForInterstitial(path);
waitForInterstitialDomToLoad();
int pageFinishedCount = mContentsClient.getOnPageFinishedHelper().getCallCount();
clickLinkById(linkId);
mContentsClient.getOnPageFinishedHelper().waitForCallback(pageFinishedCount);
// Some click tests involve URLs that redirect and mAwContents.getUrl() sometimes
// returns the post-redirect URL, so we instead check with ShouldInterceptRequest.
AwContentsClient.AwWebResourceRequest requestsForUrl =
mContentsClient.getShouldInterceptRequestHelper().getRequestsForUrl(linkUrl);
// Make sure the URL was seen for a main frame navigation.
Assert.assertTrue(requestsForUrl.isMainFrame);
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testInitSafeBrowsingCallbackOnUIThread() throws Throwable {
Context ctx = InstrumentationRegistry.getInstrumentation()
.getTargetContext()
.getApplicationContext();
CallbackHelper helper = new CallbackHelper();
int count = helper.getCallCount();
mOnUiThread = false;
AwContentsStatics.initSafeBrowsing(ctx, b -> {
mOnUiThread = ThreadUtils.runningOnUiThread();
helper.notifyCalled();
});
helper.waitForCallback(count);
// Don't run the assert on the callback's thread, since the test runner loses the stack
// trace unless on the instrumentation thread.
Assert.assertTrue("Callback should run on UI Thread", mOnUiThread);
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testInitSafeBrowsingUsesAppContext() throws Throwable {
MockContext ctx =
new MockContext(InstrumentationRegistry.getInstrumentation().getTargetContext());
CallbackHelper helper = new CallbackHelper();
int count = helper.getCallCount();
AwContentsStatics.initSafeBrowsing(ctx, b -> helper.notifyCalled());
helper.waitForCallback(count);
Assert.assertTrue(
"Should only use application context", ctx.wasGetApplicationContextCalled());
}
private static class MockContext extends ContextWrapper {
private boolean mGetApplicationContextWasCalled;
public MockContext(Context context) {
super(context);
}
@Override
public Context getApplicationContext() {
mGetApplicationContextWasCalled = true;
return super.getApplicationContext();
}
public boolean wasGetApplicationContextCalled() {
return mGetApplicationContextWasCalled;
}
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testGetSafeBrowsingPrivacyPolicyUrl() throws Throwable {
final Uri privacyPolicyUrl =
Uri.parse("https://www.google.com/chrome/browser/privacy/")
.buildUpon()
.appendQueryParameter("hl", getSafeBrowsingLocaleOnUiThreadForTesting())
.fragment("safe-browsing-policies")
.build();
TestThreadUtils.runOnUiThreadBlocking(
() -> { mPrivacyPolicyUrl = AwContentsStatics.getSafeBrowsingPrivacyPolicyUrl(); });
Assert.assertEquals(privacyPolicyUrl, this.mPrivacyPolicyUrl);
Assert.assertNotNull(this.mPrivacyPolicyUrl);
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testDestroyWebViewWithInterstitialShowing() throws Throwable {
loadPathAndWaitForInterstitial(MALWARE_HTML_PATH);
destroyOnMainSync();
// As long as we've reached this line without crashing, there should be no bug.
}
private void destroyOnMainSync() {
// The AwActivityTestRule method invokes AwContents#destroy() on the main thread, but
// Awcontents#destroy() posts an asynchronous task itself to destroy natives. Therefore, we
// still need to wait for the real work to actually finish.
mActivityTestRule.destroyAwContentsOnMainSync(mAwContents);
CriteriaHelper.pollUiThread(() -> {
try {
int awContentsCount = TestThreadUtils.runOnUiThreadBlocking(
() -> AwContents.getNativeInstanceCount());
Criteria.checkThat(awContentsCount, Matchers.is(0));
} catch (Exception e) {
throw new CriteriaNotSatisfiedException(e);
}
});
}
}