// Copyright 2024 The Chromium Authors
// 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.chromium.android_webview.test.AwActivityTestRule.SCALED_WAIT_TIMEOUT_MS;
import android.webkit.JavascriptInterface;
import android.webkit.WebSettings;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
import com.google.common.util.concurrent.SettableFuture;
import org.json.JSONObject;
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.junit.runners.Parameterized;
import org.junit.runners.Parameterized.UseParametersRunnerFactory;
import org.chromium.android_webview.AwContents;
import org.chromium.android_webview.AwContentsStatics;
import org.chromium.android_webview.AwSettings;
import org.chromium.android_webview.client_hints.AwUserAgentMetadata;
import org.chromium.base.ThreadUtils;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.DoNotBatch;
import org.chromium.base.test.util.Feature;
import org.chromium.base.test.util.HistogramWatcher;
import org.chromium.content_public.browser.test.util.HistoryUtils;
import org.chromium.content_public.browser.test.util.TestCallbackHelperContainer.OnPageCommitVisibleHelper;
import org.chromium.content_public.browser.test.util.TestCallbackHelperContainer.OnPageFinishedHelper;
import org.chromium.content_public.browser.test.util.TestCallbackHelperContainer.OnPageStartedHelper;
import org.chromium.net.test.EmbeddedTestServer;
import java.util.ArrayList;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@RunWith(Parameterized.class)
@UseParametersRunnerFactory(AwJUnit4ClassRunnerWithParameters.Factory.class)
@DoNotBatch(reason = "Tests that need browser start are incompatible with @Batch")
public class AwBackForwardCacheTest extends AwParameterizedTest {
private static final String TAG = "AwBackForwardCacheTest";
@Rule public AwActivityTestRule mActivityTestRule;
private AwTestContainerView mTestContainerView;
private AwContents mAwContents;
private static final String INITIAL_URL = "/android_webview/test/data/verify_bfcache.html";
private static final String FORWARD_URL = "/android_webview/test/data/verify_bfcache2.html";
private static final String THIRD_URL = "/android_webview/test/data/green.html";
private String mInitialUrl;
private String mForwardUrl;
private String mThirdUrl;
private TestAwContentsClient mContentsClient = new TestAwContentsClient();
private EmbeddedTestServer mTestServer;
private TestPageLoadedNotifier mLoadedNotifier;
public AwBackForwardCacheTest(AwSettingsMutation param) {
this.mActivityTestRule = new AwActivityTestRule(param.getMutation());
}
@Before
public void setUp() throws Exception {
mContentsClient = new TestAwContentsClient();
mTestContainerView = mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
mAwContents = mTestContainerView.getAwContents();
AwActivityTestRule.enableJavaScriptOnUiThread(mAwContents);
mTestServer =
EmbeddedTestServer.createAndStartServer(
InstrumentationRegistry.getInstrumentation().getContext());
mInitialUrl = mTestServer.getURL(INITIAL_URL);
mForwardUrl = mTestServer.getURL(FORWARD_URL);
mThirdUrl = mTestServer.getURL(THIRD_URL);
// The future is for waiting until page fully loaded.
// We use this future instead of `DidFinishLoad` since this callback
// will not get called if a page is restored from BFCache.
mLoadedNotifier = new TestPageLoadedNotifier();
mLoadedNotifier.setFuture(SettableFuture.create());
String name = "awFullyLoadedFuture";
AwActivityTestRule.addJavascriptInterfaceOnUiThread(mAwContents, mLoadedNotifier, name);
}
@After
public void tearDown() {
mTestServer.stopAndDestroyServer();
}
private void navigateForward() throws Throwable {
mActivityTestRule.loadUrlSync(
mAwContents, mContentsClient.getOnPageFinishedHelper(), mForwardUrl);
}
private void navigateBack() throws Throwable {
navigateBackToUrl(mInitialUrl);
}
private void navigateBackToUrl(String url) throws Throwable {
// Create a new future to avoid the future set in the initial load.
SettableFuture<Boolean> pageFullyLoadedFuture = SettableFuture.create();
mLoadedNotifier.setFuture(pageFullyLoadedFuture);
// Traditionally we use onPageFinishedHelper which is no longer
// valid with BFCache working.
// The onPageFinishedHelper is called in `DidFinishLoad` callback
// in the web contents observer. If the page is restored from the
// BFCache, this function will not get called since the onload event
// is already fired when the page was navigated into for the first time.
// We use onPageStartedHelper instead. This function correspond to
// `didFinishNavigationInPrimaryMainFrame`.
OnPageStartedHelper startHelper = mContentsClient.getOnPageStartedHelper();
int originalCallCount = startHelper.getCallCount();
HistoryUtils.goBackSync(
InstrumentationRegistry.getInstrumentation(),
mAwContents.getWebContents(),
startHelper);
Assert.assertEquals(startHelper.getUrl(), url);
Assert.assertEquals(startHelper.getCallCount(), originalCallCount + 1);
// Wait for the page to be fully loaded
Assert.assertEquals(
true, pageFullyLoadedFuture.get(SCALED_WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
private void navigateForwardAndBack() throws Throwable {
navigateForward();
navigateBack();
}
private boolean isPageShowPersisted() throws Exception {
String isPersisted =
mActivityTestRule.executeJavaScriptAndWaitForResult(
mAwContents, mContentsClient, "isPageShowPersisted");
return isPersisted.equals("true");
}
private String getNotRestoredReasons() throws Exception {
// https://github.com/WICG/bfcache-not-restored-reason/blob/main/NotRestoredReason.md
// If a page is not restored from the BFCache. The notRestoredReasons will contain a
// detailed description about the reason. Otherwise it will be null (i.e. it's
// restored from the BFCache).
return mActivityTestRule.executeJavaScriptAndWaitForResult(
mAwContents,
mContentsClient,
"JSON.stringify(performance.getEntriesByType('navigation')[0].notRestoredReasons);");
}
private String extractSimpleReasonString(String notRestoredReasons) throws Exception {
// Remove the escape character and the beginning and trailing quotes
notRestoredReasons = notRestoredReasons.replace("\\", "");
notRestoredReasons = notRestoredReasons.substring(1, notRestoredReasons.length() - 1);
JSONObject json_obj = new JSONObject(notRestoredReasons);
return json_obj.getJSONArray("reasons").getJSONObject(0).getString("reason");
}
private HistogramWatcher getNotRestoredReasonsHistogramWatcher(int reason) {
return HistogramWatcher.newBuilder()
.expectIntRecord(
"BackForwardCache.HistoryNavigationOutcome.NotRestoredReason", reason)
.build();
}
@Test
@LargeTest
@Feature({"AndroidWebView"})
@CommandLineFlags.Add({"enable-features=WebViewBackForwardCache"})
public void testBFCacheEnabledWithFeatureFlag() throws Exception, Throwable {
mAwContents.getSettings().setBackForwardCacheEnabled(false);
mActivityTestRule.loadUrlSync(
mAwContents, mContentsClient.getOnPageFinishedHelper(), mInitialUrl);
navigateForwardAndBack();
Assert.assertEquals("\"null\"", getNotRestoredReasons());
Assert.assertTrue(isPageShowPersisted());
}
@Test
@LargeTest
@Feature({"AndroidWebView"})
public void testBFCacheWithMultiplePages() throws Exception, Throwable {
mAwContents.getSettings().setBackForwardCacheEnabled(true);
mActivityTestRule.loadUrlSync(
mAwContents, mContentsClient.getOnPageFinishedHelper(), mInitialUrl);
mActivityTestRule.loadUrlSync(
mAwContents, mContentsClient.getOnPageFinishedHelper(), mForwardUrl);
mActivityTestRule.loadUrlSync(
mAwContents, mContentsClient.getOnPageFinishedHelper(), mThirdUrl);
navigateBackToUrl(mForwardUrl);
Assert.assertEquals("\"null\"", getNotRestoredReasons());
Assert.assertTrue(isPageShowPersisted());
navigateBackToUrl(mInitialUrl);
Assert.assertEquals("\"null\"", getNotRestoredReasons());
Assert.assertTrue(isPageShowPersisted());
}
@Test
@LargeTest
@Feature({"AndroidWebView"})
@CommandLineFlags.Add({"disable-features=WebViewBackForwardCache"})
public void testBackNavigationFollowsSettings() throws Exception, Throwable {
mAwContents.getSettings().setBackForwardCacheEnabled(true);
mActivityTestRule.loadUrlSync(
mAwContents, mContentsClient.getOnPageFinishedHelper(), mInitialUrl);
navigateForwardAndBack();
Assert.assertEquals("\"null\"", getNotRestoredReasons());
Assert.assertTrue(isPageShowPersisted());
mAwContents.getSettings().setBackForwardCacheEnabled(false);
navigateForwardAndBack();
String notRestoredReasons = getNotRestoredReasons();
Assert.assertEquals(extractSimpleReasonString(notRestoredReasons), "masked");
Assert.assertFalse(isPageShowPersisted());
}
@Test
@LargeTest
@Feature({"AndroidWebView"})
public void testPageEvictedWhenModifyingJSInterface() throws Exception, Throwable {
mAwContents.getSettings().setBackForwardCacheEnabled(true);
mActivityTestRule.loadUrlSync(
mAwContents, mContentsClient.getOnPageFinishedHelper(), mInitialUrl);
// Test adding javascript interface
navigateForward();
HistogramWatcher histogramWatcher =
getNotRestoredReasonsHistogramWatcher(/*kWebViewJavaScriptObjectChanged*/ 65);
Object testInjectedObject =
new Object() {
@JavascriptInterface
public void mock() {}
};
AwActivityTestRule.addJavascriptInterfaceOnUiThread(
mAwContents, testInjectedObject, "testInjectedObject");
navigateBack();
String notRestoredReasons = getNotRestoredReasons();
Assert.assertEquals(extractSimpleReasonString(notRestoredReasons), "masked");
Assert.assertFalse(isPageShowPersisted());
histogramWatcher.assertExpected();
// Test removing javascript interface
histogramWatcher =
getNotRestoredReasonsHistogramWatcher(/*kWebViewJavaScriptObjectChanged*/ 65);
navigateForward();
ThreadUtils.runOnUiThreadBlocking(
() -> mAwContents.removeJavascriptInterface("testInjectedObject"));
navigateBack();
notRestoredReasons = getNotRestoredReasons();
Assert.assertEquals(extractSimpleReasonString(notRestoredReasons), "masked");
Assert.assertFalse(isPageShowPersisted());
histogramWatcher.assertExpected();
// Test BFCache can still work for future navigations
navigateForwardAndBack();
Assert.assertTrue(isPageShowPersisted());
}
@Test
@LargeTest
@Feature({"AndroidWebView"})
public void testPageEvictedWhenAddingWebMessageListener() throws Exception, Throwable {
mAwContents.getSettings().setBackForwardCacheEnabled(true);
mActivityTestRule.loadUrlSync(
mAwContents, mContentsClient.getOnPageFinishedHelper(), mInitialUrl);
HistogramWatcher histogramWatcher =
getNotRestoredReasonsHistogramWatcher(/*kWebViewMessageListenerInjected*/ 66);
navigateForward();
TestWebMessageListener listener = new TestWebMessageListener();
TestWebMessageListener.addWebMessageListenerOnUiThread(
mAwContents, "awMessagePort", new String[] {"*"}, listener);
navigateBack();
String notRestoredReasons = getNotRestoredReasons();
Assert.assertTrue(notRestoredReasons.indexOf("reasons") >= 0);
Assert.assertFalse(isPageShowPersisted());
histogramWatcher.assertExpected();
// Test BFCache can still work for future navigations
navigateForwardAndBack();
Assert.assertTrue(isPageShowPersisted());
}
// TODO(crbug.com/335767367): Consider calling onPageFinished for BFCache restores.
// For now `onPageFinished` callback will not be called. The clients
// shall listen for the web messages for BFCache related events.
@Test
@LargeTest
@Feature({"AndroidWebView"})
public void testPageFinishEventNotCalled() throws Exception, Throwable {
mAwContents.getSettings().setBackForwardCacheEnabled(true);
mActivityTestRule.loadUrlSync(
mAwContents, mContentsClient.getOnPageFinishedHelper(), mInitialUrl);
navigateForward();
final OnPageFinishedHelper finishHelper = mContentsClient.getOnPageFinishedHelper();
int originalCallCount = finishHelper.getCallCount();
navigateBack();
Assert.assertEquals("\"null\"", getNotRestoredReasons());
Assert.assertTrue(isPageShowPersisted());
Assert.assertEquals(finishHelper.getCallCount(), originalCallCount);
}
@Test
@LargeTest
@Feature({"AndroidWebView"})
public void testShouldInterceptRequestNotCalled() throws Exception, Throwable {
mAwContents.getSettings().setBackForwardCacheEnabled(true);
mActivityTestRule.loadUrlSync(
mAwContents, mContentsClient.getOnPageFinishedHelper(), mInitialUrl);
navigateForward();
final TestAwContentsClient.ShouldInterceptRequestHelper helper =
mContentsClient.getShouldInterceptRequestHelper();
int originalCallCount = helper.getCallCount();
navigateBack();
Assert.assertEquals("\"null\"", getNotRestoredReasons());
Assert.assertTrue(isPageShowPersisted());
Assert.assertEquals(helper.getCallCount(), originalCallCount);
}
@Test
@LargeTest
@Feature({"AndroidWebView"})
public void testShouldOverrideUrlLoadingNotCalled() throws Exception, Throwable {
mAwContents.getSettings().setBackForwardCacheEnabled(true);
mActivityTestRule.loadUrlSync(
mAwContents, mContentsClient.getOnPageFinishedHelper(), mInitialUrl);
navigateForward();
final TestAwContentsClient.ShouldOverrideUrlLoadingHelper helper =
mContentsClient.getShouldOverrideUrlLoadingHelper();
int originalCallCount = helper.getCallCount();
navigateBack();
Assert.assertEquals("\"null\"", getNotRestoredReasons());
Assert.assertTrue(isPageShowPersisted());
Assert.assertEquals(helper.getCallCount(), originalCallCount);
}
@Test
@LargeTest
@Feature({"AndroidWebView"})
public void testOnLoadResourceNotCalled() throws Exception, Throwable {
mAwContents.getSettings().setBackForwardCacheEnabled(true);
mActivityTestRule.loadUrlSync(
mAwContents, mContentsClient.getOnPageFinishedHelper(), mInitialUrl);
navigateForward();
final TestAwContentsClient.OnLoadResourceHelper helper =
mContentsClient.getOnLoadResourceHelper();
int originalCallCount = helper.getCallCount();
navigateBack();
Assert.assertEquals("\"null\"", getNotRestoredReasons());
Assert.assertTrue(isPageShowPersisted());
Assert.assertEquals(helper.getCallCount(), originalCallCount);
}
@Test
@LargeTest
@Feature({"AndroidWebView"})
public void testManualFlushCache() throws Exception, Throwable {
mAwContents.getSettings().setBackForwardCacheEnabled(true);
mActivityTestRule.loadUrlSync(
mAwContents, mContentsClient.getOnPageFinishedHelper(), mInitialUrl);
HistogramWatcher histogramWatcher =
getNotRestoredReasonsHistogramWatcher(/*kCacheFlushed*/ 21);
navigateForward();
ThreadUtils.runOnUiThreadBlocking(() -> mAwContents.flushBackForwardCache());
navigateBack();
String notRestoredReasons = getNotRestoredReasons();
Assert.assertTrue(notRestoredReasons.indexOf("reasons") >= 0);
Assert.assertFalse(isPageShowPersisted());
histogramWatcher.assertExpected();
// Test BFCache can still work for future navigations
navigateForwardAndBack();
Assert.assertTrue(isPageShowPersisted());
}
private void verifyPageEvictedWithSettingsChange(Runnable r) throws Exception, Throwable {
HistogramWatcher histogramWatcher =
getNotRestoredReasonsHistogramWatcher(/*kWebViewSettingsChanged*/ 64);
navigateForward();
r.run();
// wait for the page finished callback to avoid interfering with the next forward
// navigation.
final OnPageFinishedHelper finishHelper = mContentsClient.getOnPageFinishedHelper();
int callCount = finishHelper.getCallCount();
navigateBack();
finishHelper.waitForCallback(callCount, 1, SCALED_WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
Assert.assertFalse(isPageShowPersisted());
histogramWatcher.assertExpected();
}
@Test
@LargeTest
@Feature({"AndroidWebView"})
public void testPageEvictedWhenSettingsChanged() throws Exception, Throwable {
mAwContents.getSettings().setBackForwardCacheEnabled(true);
// Set some options before the test to ensure changes are triggered.
AwSettings settings = mAwContents.getSettings();
settings.setSafeBrowsingEnabled(false);
settings.setAllowContentAccess(false);
settings.setCSSHexAlphaColorEnabled(false);
settings.setScrollTopLeftInteropEnabled(false);
settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
settings.setAttributionBehavior(AwSettings.ATTRIBUTION_DISABLED);
settings.setForceDarkMode(AwSettings.FORCE_DARK_OFF);
settings.setForceDarkBehavior(AwSettings.FORCE_DARK_ONLY);
settings.setShouldFocusFirstNode(true);
settings.setSpatialNavigationEnabled(false);
settings.setEnableSupportedHardwareAcceleratedFeatures(false);
settings.setFullscreenSupported(false);
settings.setGeolocationEnabled(false);
settings.setBlockSpecialFileUrls(false);
settings.setDisabledActionModeMenuItems(WebSettings.MENU_ITEM_NONE);
mActivityTestRule.loadUrlSync(
mAwContents, mContentsClient.getOnPageFinishedHelper(), mInitialUrl);
verifyPageEvictedWithSettingsChange(
() -> {
settings.setBlockNetworkLoads(true);
settings.setBlockNetworkLoads(false);
});
// Test not restored reasons only for the first navigation
String notRestoredReasons = getNotRestoredReasons();
Assert.assertTrue(notRestoredReasons.indexOf("reasons") >= 0);
verifyPageEvictedWithSettingsChange(() -> settings.setAcceptThirdPartyCookies(false));
verifyPageEvictedWithSettingsChange(() -> settings.setSafeBrowsingEnabled(true));
verifyPageEvictedWithSettingsChange(() -> settings.setAllowFileAccess(true));
verifyPageEvictedWithSettingsChange(() -> settings.setAllowContentAccess(true));
verifyPageEvictedWithSettingsChange(
() -> settings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK));
verifyPageEvictedWithSettingsChange(() -> settings.setShouldFocusFirstNode(false));
verifyPageEvictedWithSettingsChange(() -> settings.setInitialPageScale(50));
verifyPageEvictedWithSettingsChange(() -> settings.setSpatialNavigationEnabled(true));
verifyPageEvictedWithSettingsChange(
() -> settings.setEnableSupportedHardwareAcceleratedFeatures(true));
verifyPageEvictedWithSettingsChange(() -> settings.setFullscreenSupported(true));
verifyPageEvictedWithSettingsChange(() -> settings.setGeolocationEnabled(true));
verifyPageEvictedWithSettingsChange(() -> settings.setUserAgentString("testUserAgent"));
verifyPageEvictedWithSettingsChange(
() -> {
settings.setUserAgentMetadataFromMap(
Map.of(AwUserAgentMetadata.MetadataKeys.PLATFORM, "fake_platform"));
});
verifyPageEvictedWithSettingsChange(
() -> settings.setLoadWithOverviewMode(!settings.getLoadWithOverviewMode()));
verifyPageEvictedWithSettingsChange(
() -> settings.setTextZoom(settings.getTextZoom() + 100));
verifyPageEvictedWithSettingsChange(() -> settings.setFixedFontFamily("cursive"));
verifyPageEvictedWithSettingsChange(() -> settings.setSansSerifFontFamily("cursive"));
verifyPageEvictedWithSettingsChange(() -> settings.setSerifFontFamily("cursive"));
verifyPageEvictedWithSettingsChange(() -> settings.setCursiveFontFamily("serif"));
verifyPageEvictedWithSettingsChange(() -> settings.setFantasyFontFamily("cursive"));
verifyPageEvictedWithSettingsChange(
() -> settings.setMinimumFontSize(settings.getMinimumFontSize() + 1));
verifyPageEvictedWithSettingsChange(
() -> {
settings.setMinimumLogicalFontSize(settings.getMinimumLogicalFontSize() + 1);
});
verifyPageEvictedWithSettingsChange(
() -> {
settings.setDefaultFontSize(settings.getDefaultFontSize() + 1);
});
verifyPageEvictedWithSettingsChange(
() -> {
settings.setDefaultFixedFontSize(settings.getDefaultFixedFontSize() + 1);
});
// Make sure javascript is enabled when navigating back so that we can
// receive the web message.
settings.setJavaScriptEnabled(false);
verifyPageEvictedWithSettingsChange(() -> settings.setJavaScriptEnabled(true));
verifyPageEvictedWithSettingsChange(
() -> {
settings.setAllowUniversalAccessFromFileURLs(
!settings.getAllowUniversalAccessFromFileURLs());
});
verifyPageEvictedWithSettingsChange(
() -> {
settings.setAllowFileAccessFromFileURLs(
!settings.getAllowFileAccessFromFileURLs());
});
verifyPageEvictedWithSettingsChange(
() -> {
settings.setLoadsImagesAutomatically(!settings.getLoadsImagesAutomatically());
});
verifyPageEvictedWithSettingsChange(
() -> settings.setImagesEnabled(!settings.getImagesEnabled()));
verifyPageEvictedWithSettingsChange(
() -> {
settings.setJavaScriptCanOpenWindowsAutomatically(
!settings.getJavaScriptCanOpenWindowsAutomatically());
});
verifyPageEvictedWithSettingsChange(
() -> settings.setLayoutAlgorithm(AwSettings.LAYOUT_ALGORITHM_SINGLE_COLUMN));
verifyPageEvictedWithSettingsChange(
() -> settings.setRequestedWithHeaderOriginAllowList(null));
verifyPageEvictedWithSettingsChange(
() -> settings.setSupportMultipleWindows(!settings.supportMultipleWindows()));
verifyPageEvictedWithSettingsChange(() -> settings.setBlockSpecialFileUrls(true));
verifyPageEvictedWithSettingsChange(() -> settings.setCSSHexAlphaColorEnabled(true));
verifyPageEvictedWithSettingsChange(() -> settings.setScrollTopLeftInteropEnabled(true));
verifyPageEvictedWithSettingsChange(
() -> settings.setUseWideViewPort(!settings.getUseWideViewPort()));
verifyPageEvictedWithSettingsChange(
() -> {
settings.setZeroLayoutHeightDisablesViewportQuirk(
!settings.getZeroLayoutHeightDisablesViewportQuirk());
});
verifyPageEvictedWithSettingsChange(
() -> settings.setForceZeroLayoutHeight(!settings.getForceZeroLayoutHeight()));
verifyPageEvictedWithSettingsChange(
() -> settings.setDomStorageEnabled(!settings.getDomStorageEnabled()));
verifyPageEvictedWithSettingsChange(
() -> settings.setDatabaseEnabled(!settings.getDatabaseEnabled()));
verifyPageEvictedWithSettingsChange(() -> settings.setDefaultTextEncodingName("Latin-1"));
verifyPageEvictedWithSettingsChange(
() -> {
settings.setMediaPlaybackRequiresUserGesture(
!settings.getMediaPlaybackRequiresUserGesture());
});
verifyPageEvictedWithSettingsChange(() -> settings.setSupportZoom(!settings.supportZoom()));
verifyPageEvictedWithSettingsChange(
() -> settings.setBuiltInZoomControls(!settings.getBuiltInZoomControls()));
verifyPageEvictedWithSettingsChange(
() -> settings.setDisplayZoomControls(!settings.getDisplayZoomControls()));
verifyPageEvictedWithSettingsChange(
() -> settings.setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE));
verifyPageEvictedWithSettingsChange(
() -> {
settings.setAttributionBehavior(
AwSettings.ATTRIBUTION_WEB_SOURCE_AND_WEB_TRIGGER);
});
verifyPageEvictedWithSettingsChange(
() -> settings.setForceDarkMode(AwSettings.FORCE_DARK_AUTO));
verifyPageEvictedWithSettingsChange(
() -> {
settings.setAlgorithmicDarkeningAllowed(
!settings.isAlgorithmicDarkeningAllowed());
});
verifyPageEvictedWithSettingsChange(
() -> settings.setForceDarkBehavior(AwSettings.MEDIA_QUERY_ONLY));
verifyPageEvictedWithSettingsChange(
() -> settings.setOffscreenPreRaster(!settings.getOffscreenPreRaster()));
verifyPageEvictedWithSettingsChange(
() -> settings.setDisabledActionModeMenuItems(WebSettings.MENU_ITEM_SHARE));
verifyPageEvictedWithSettingsChange(() -> settings.updateAcceptLanguages());
verifyPageEvictedWithSettingsChange(
() -> settings.setWillSuppressErrorPage(!settings.getWillSuppressErrorPage()));
verifyPageEvictedWithSettingsChange(
() -> settings.setDefaultVideoPosterURL("http://test_url"));
// Test BFCache can still work for future navigations
navigateForwardAndBack();
Assert.assertTrue(isPageShowPersisted());
}
@Test
@LargeTest
@Feature({"AndroidWebView"})
public void testDoUpdateVisitedHistory() throws Exception, Throwable {
mAwContents.getSettings().setBackForwardCacheEnabled(true);
mActivityTestRule.loadUrlSync(
mAwContents, mContentsClient.getOnPageFinishedHelper(), mInitialUrl);
navigateForward();
final TestAwContentsClient.DoUpdateVisitedHistoryHelper helper =
mContentsClient.getDoUpdateVisitedHistoryHelper();
int originalCallCount = helper.getCallCount();
navigateBack();
Assert.assertEquals("\"null\"", getNotRestoredReasons());
Assert.assertTrue(isPageShowPersisted());
helper.waitForCallback(originalCallCount, 1, SCALED_WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
Assert.assertEquals(helper.getUrl(), mInitialUrl);
Assert.assertEquals(helper.getIsReload(), false);
}
@Test
@LargeTest
@Feature({"AndroidWebView"})
public void testOnPageCommitVisible() throws Exception, Throwable {
mAwContents.getSettings().setBackForwardCacheEnabled(true);
final OnPageCommitVisibleHelper helper = mContentsClient.getOnPageCommitVisibleHelper();
int originalCallCount = helper.getCallCount();
mActivityTestRule.loadUrlSync(
mAwContents, mContentsClient.getOnPageFinishedHelper(), mInitialUrl);
helper.waitForCallback(originalCallCount, 1, SCALED_WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
Assert.assertEquals(helper.getUrl(), mInitialUrl);
originalCallCount = helper.getCallCount();
navigateForward();
helper.waitForCallback(originalCallCount, 1, SCALED_WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
Assert.assertEquals(helper.getUrl(), mForwardUrl);
originalCallCount = helper.getCallCount();
navigateBack();
Assert.assertEquals("\"null\"", getNotRestoredReasons());
Assert.assertTrue(isPageShowPersisted());
helper.waitForCallback(originalCallCount, 1, SCALED_WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
Assert.assertEquals(helper.getUrl(), mInitialUrl);
}
@Test
@LargeTest
@Feature({"AndroidWebView"})
public void testPageEvictedWhenSafeBrowsingAllowlistSet() throws Exception, Throwable {
mAwContents.getSettings().setBackForwardCacheEnabled(true);
mActivityTestRule.loadUrlSync(
mAwContents, mContentsClient.getOnPageFinishedHelper(), mInitialUrl);
HistogramWatcher histogramWatcher =
getNotRestoredReasonsHistogramWatcher(/*kWebViewSafeBrowsingAllowlistChanged*/ 67);
navigateForward();
ArrayList<String> allowlist = new ArrayList<>();
allowlist.add("google.com");
SettableFuture<Boolean> allowlistSetFuture = SettableFuture.create();
ThreadUtils.runOnUiThreadBlocking(
() ->
AwContentsStatics.setSafeBrowsingAllowlist(
allowlist,
result -> {
allowlistSetFuture.set(true);
}));
Assert.assertTrue(allowlistSetFuture.get(SCALED_WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS));
navigateBack();
String notRestoredReasons = getNotRestoredReasons();
Assert.assertEquals(extractSimpleReasonString(notRestoredReasons), "masked");
Assert.assertFalse(isPageShowPersisted());
histogramWatcher.assertExpected();
// Test BFCache can still work for future navigations
navigateForwardAndBack();
Assert.assertTrue(isPageShowPersisted());
}
@Test
@LargeTest
@Feature({"AndroidWebView"})
public void testPageEvictedWhenAddingDocumentStartJavascript() throws Exception, Throwable {
mAwContents.getSettings().setBackForwardCacheEnabled(true);
mActivityTestRule.loadUrlSync(
mAwContents, mContentsClient.getOnPageFinishedHelper(), mInitialUrl);
HistogramWatcher histogramWatcher =
getNotRestoredReasonsHistogramWatcher(
/*kWebViewDocumentStartJavascriptChanged */ 68);
navigateForward();
ThreadUtils.runOnUiThreadBlocking(
() ->
mAwContents.addDocumentStartJavaScript(
"console.log(\"hello world\");", new String[] {"*"}));
navigateBack();
String notRestoredReasons = getNotRestoredReasons();
Assert.assertEquals(extractSimpleReasonString(notRestoredReasons), "masked");
Assert.assertFalse(isPageShowPersisted());
histogramWatcher.assertExpected();
// Test BFCache can still work for future navigations
navigateForwardAndBack();
Assert.assertTrue(isPageShowPersisted());
}
}