chromium/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewTest.java

// Copyright 2020 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.chrome.browser.page_info;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.RootMatchers.isDialog;
import static androidx.test.espresso.matcher.ViewMatchers.Visibility.GONE;
import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
import static androidx.test.espresso.matcher.ViewMatchers.hasSibling;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withParent;
import static androidx.test.espresso.matcher.ViewMatchers.withText;

import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.Matchers.not;
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.chromium.components.content_settings.PrefNames.COOKIE_CONTROLS_MODE;
import static org.chromium.components.content_settings.PrefNames.IN_CONTEXT_COOKIE_CONTROLS_OPENED;
import static org.chromium.ui.test.util.ViewUtils.hasBackgroundColor;
import static org.chromium.ui.test.util.ViewUtils.onViewWaiting;

import android.content.Context;
import android.content.res.Resources;
import android.os.Build;
import android.text.format.DateUtils;
import android.view.View;

import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.MediumTest;

import org.junit.After;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.chromium.base.ThreadUtils;
import org.chromium.base.test.params.ParameterAnnotations;
import org.chromium.base.test.params.ParameterProvider;
import org.chromium.base.test.params.ParameterSet;
import org.chromium.base.test.params.ParameterizedRunner;
import org.chromium.base.test.util.CallbackHelper;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.DisabledTest;
import org.chromium.base.test.util.Feature;
import org.chromium.base.test.util.Features;
import org.chromium.base.test.util.Features.DisableFeatures;
import org.chromium.chrome.browser.FederatedIdentityTestUtils;
import org.chromium.chrome.browser.app.ChromeActivity;
import org.chromium.chrome.browser.browsing_data.BrowsingDataBridge;
import org.chromium.chrome.browser.browsing_data.BrowsingDataType;
import org.chromium.chrome.browser.browsing_data.TimePeriod;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.browser.history.HistoryContentManager;
import org.chromium.chrome.browser.history.StubbedHistoryProvider;
import org.chromium.chrome.browser.notifications.channels.SiteChannelsManager;
import org.chromium.chrome.browser.offlinepages.OfflinePageUtils;
import org.chromium.chrome.browser.pdf.PdfUtils.PdfPageType;
import org.chromium.chrome.browser.preferences.Pref;
import org.chromium.chrome.browser.profiles.ProfileManager;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.util.ChromeAccessibilityUtil;
import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate;
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
import org.chromium.chrome.test.R;
import org.chromium.chrome.test.batch.BlankCTATabInitialStateRule;
import org.chromium.components.browser_ui.site_settings.ContentSettingException;
import org.chromium.components.browser_ui.site_settings.WebsitePreferenceBridge;
import org.chromium.components.browser_ui.site_settings.WebsitePreferenceBridgeJni;
import org.chromium.components.browser_ui.util.date.CalendarUtils;
import org.chromium.components.browser_ui.util.date.StringUtils;
import org.chromium.components.content_settings.ContentSettingValues;
import org.chromium.components.content_settings.ContentSettingsType;
import org.chromium.components.content_settings.CookieControlsMode;
import org.chromium.components.location.LocationUtils;
import org.chromium.components.page_info.PageInfoAdPersonalizationController;
import org.chromium.components.page_info.PageInfoController;
import org.chromium.components.user_prefs.UserPrefs;
import org.chromium.content_public.browser.ContentFeatureList;
import org.chromium.content_public.browser.NavigationHandle;
import org.chromium.content_public.browser.WebContentsObserver;
import org.chromium.content_public.browser.test.util.JavaScriptUtils;
import org.chromium.content_public.common.ContentSwitches;
import org.chromium.net.GURLUtils;
import org.chromium.net.test.EmbeddedTestServerRule;
import org.chromium.net.test.ServerCertificate;
import org.chromium.ui.test.util.RenderTestRule;
import org.chromium.url.GURL;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeoutException;

/**
 * Tests for PageInfoView. Uses pixel tests to ensure the UI handles different configurations
 * correctly.
 */
@RunWith(ParameterizedRunner.class)
@ParameterAnnotations.UseRunnerDelegate(ChromeJUnit4RunnerDelegate.class)
@CommandLineFlags.Add({
    ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
    ChromeSwitches.DISABLE_STARTUP_PROMOS,
    ContentSwitches.HOST_RESOLVER_RULES + "=MAP * 127.0.0.1"
})
// TODO(crbug.com/344672095): Failing when batched, batch this again.
// Disable TrackingProtection3pcd as we use prefs instead of the feature in
// these tests.
@DisableFeatures(ChromeFeatureList.TRACKING_PROTECTION_3PCD)
public class PageInfoViewTest {
    private static final String sSimpleHtml = "/chrome/test/data/android/simple.html";
    private static final String sSiteDataHtml = "/content/test/data/browsing_data/site_data.html";

    private static String[] sCookieDataTypes = {
        "Cookie", "LocalStorage", "ServiceWorker", "CacheStorage", "IndexedDb", "FileSystem"
    };

    // June 4, 2021 12:00:00 GMT+00:00
    private static long sTimestampJune4 = 1622808000000L;
    // April 4, 2021 12:00:00 GMT+00:00
    private static long sTimestampApril4 = 1617537600000L;

    /**
     * Parameter provider for testing the different timestamps for the history section's "last
     * visited" text.
     */
    public static class HistorySummaryTestParams implements ParameterProvider {
        @Override
        public Iterable<ParameterSet> getParameters() {
            Resources res = ApplicationProvider.getApplicationContext().getResources();
            Random random = new Random();
            long timestamp;

            List<ParameterSet> parameters = new ArrayList<>();
            // ParameterSet = {timestamp, string}
            timestamp = CalendarUtils.getStartOfDay(sTimestampJune4).getTime().getTime();
            parameters.add(
                    new ParameterSet()
                            .value(
                                    timestamp,
                                    res.getString(R.string.page_info_history_last_visit_today))
                            .name("Today"));
            timestamp = sTimestampJune4 - 1 * DateUtils.DAY_IN_MILLIS;
            parameters.add(
                    new ParameterSet()
                            .value(
                                    timestamp,
                                    res.getString(R.string.page_info_history_last_visit_yesterday))
                            .name("Yesterday"));
            int offset = random.nextInt(6) + 2;
            timestamp = sTimestampJune4 - offset * DateUtils.DAY_IN_MILLIS;
            parameters.add(
                    new ParameterSet()
                            .value(
                                    timestamp,
                                    res.getString(
                                            R.string.page_info_history_last_visit_days, offset))
                            .name("XDaysAgo"));
            parameters.add(
                    new ParameterSet()
                            .value(
                                    sTimestampApril4,
                                    res.getString(
                                            R.string.page_info_history_last_visit_date,
                                            StringUtils.dateToHeaderString(
                                                    new Date(sTimestampApril4))))
                            .name("ExactDay"));
            return parameters;
        }
    }

    @ClassRule
    public static final ChromeTabbedActivityTestRule sActivityTestRule =
            new ChromeTabbedActivityTestRule();

    @Rule
    public final BlankCTATabInitialStateRule mInitialStateRule =
            new BlankCTATabInitialStateRule(sActivityTestRule, false);

    @Rule public EmbeddedTestServerRule mTestServerRule = new EmbeddedTestServerRule();

    @Rule
    public RenderTestRule mRenderTestRule =
            RenderTestRule.Builder.withPublicCorpus()
                    .setRevision(8)
                    .setDescription("Red interstitial color, icon, and string facelift")
                    .setBugComponent(RenderTestRule.Component.UI_BROWSER_BUBBLES_PAGE_INFO)
                    .build();

    private boolean mIsSystemLocationSettingEnabled = true;

    private class TestLocationUtils extends LocationUtils {
        @Override
        public boolean isSystemLocationSettingEnabled() {
            return mIsSystemLocationSettingEnabled;
        }
    }

    private void loadUrlAndOpenPageInfo(String url) {
        loadUrlAndOpenPageInfoWithPermission(url, PageInfoController.NO_HIGHLIGHTED_PERMISSION);
    }

    private void loadUrlAndOpenPageInfoWithPermission(
            String url, @ContentSettingsType.EnumType int highlightedPermission) {
        sActivityTestRule.loadUrl(url);
        openPageInfo(highlightedPermission);
    }

    private void openPageInfo(@ContentSettingsType.EnumType int highlightedPermission) {
        ChromeActivity activity = sActivityTestRule.getActivity();
        Tab tab = activity.getActivityTab();
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    new ChromePageInfo(
                                    activity.getModalDialogManagerSupplier(),
                                    null,
                                    PageInfoController.OpenedFromSource.TOOLBAR,
                                    null,
                                    null,
                                    null)
                            .show(
                                    tab,
                                    ChromePageInfoHighlight.forPermission(highlightedPermission));
                });
        onViewWaiting(allOf(withId(R.id.page_info_url_wrapper), isDisplayed()), true);
    }

    private View getPageInfoView() {
        PageInfoController controller = PageInfoController.getLastPageInfoControllerForTesting();
        assertNotNull(controller);
        View view = controller.getPageInfoViewForTesting();
        assertNotNull(view);
        return view;
    }

    private void enableTrackingProtectionFixedExpiration() {
        PageInfoController controller = PageInfoController.getLastPageInfoControllerForTesting();
        assertNotNull(controller);
        if (ChromeFeatureList.isEnabled(ChromeFeatureList.IP_PROTECTION_USER_BYPASS)
                || ChromeFeatureList.isEnabled(
                        ChromeFeatureList.FINGERPRINTING_PROTECTION_USER_BYPASS)) {
            var tpController = controller.getTrackingProtectionLaunchControllerForTesting();
            tpController.setFixedExceptionExpirationForTesting(true);
        } else {
            var tpController = controller.getTrackingProtectionControllerForTesting();
            tpController.setFixedExceptionExpirationForTesting(true);
        }
    }

    private void setThirdPartyCookieBlocking(@CookieControlsMode int value) {
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    UserPrefs.get(ProfileManager.getLastUsedRegularProfile())
                            .setInteger(COOKIE_CONTROLS_MODE, value);
                });
    }

    private void enableTrackingProtection() {
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    UserPrefs.get(ProfileManager.getLastUsedRegularProfile())
                            .setBoolean(Pref.TRACKING_PROTECTION3PCD_ENABLED, true);
                });
    }

    private void setBlockAll3PC(boolean value) {
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    UserPrefs.get(ProfileManager.getLastUsedRegularProfile())
                            .setBoolean(Pref.BLOCK_ALL3PC_TOGGLE_ENABLED, value);
                });
    }

    private String runJavascriptAsync(String type) throws TimeoutException {
        return JavaScriptUtils.runJavascriptWithAsyncResult(
                sActivityTestRule.getWebContents(), type);
    }

    private void expectHasCookies(boolean hasData) throws TimeoutException {
        for (String type : sCookieDataTypes) {
            assertEquals(hasData ? "true" : "false", runJavascriptAsync("has" + type + "Async()"));
        }
    }

    private void createCookies() throws TimeoutException {
        for (String type : sCookieDataTypes) {
            runJavascriptAsync("set" + type + "Async()");
        }
    }

    private void addSomePermissions(String urlString) {
        GURL url = new GURL(urlString);
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    WebsitePreferenceBridge.setContentSettingDefaultScope(
                            ProfileManager.getLastUsedRegularProfile(),
                            ContentSettingsType.GEOLOCATION,
                            url,
                            url,
                            ContentSettingValues.ALLOW);
                    WebsitePreferenceBridge.setContentSettingDefaultScope(
                            ProfileManager.getLastUsedRegularProfile(),
                            ContentSettingsType.NOTIFICATIONS,
                            url,
                            url,
                            ContentSettingValues.BLOCK);
                });
    }

    private void expectHasPermissions(String url, boolean hasPermissions) {
        // The default value for these types is ask.
        @ContentSettingValues
        int expectAllow = hasPermissions ? ContentSettingValues.ALLOW : ContentSettingValues.ASK;
        @ContentSettingValues
        int expectBlock = hasPermissions ? ContentSettingValues.BLOCK : ContentSettingValues.ASK;
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    assertEquals(
                            expectBlock,
                            WebsitePreferenceBridgeJni.get()
                                    .getPermissionSettingForOrigin(
                                            ProfileManager.getLastUsedRegularProfile(),
                                            ContentSettingsType.NOTIFICATIONS,
                                            url,
                                            url));
                    assertEquals(
                            expectAllow,
                            WebsitePreferenceBridgeJni.get()
                                    .getPermissionSettingForOrigin(
                                            ProfileManager.getLastUsedRegularProfile(),
                                            ContentSettingsType.GEOLOCATION,
                                            url,
                                            "*"));
                });
    }

    private void addDefaultSettingPermissions(String urlString) {
        GURL url = new GURL(urlString);
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    WebsitePreferenceBridge.setContentSettingDefaultScope(
                            ProfileManager.getLastUsedRegularProfile(),
                            ContentSettingsType.MEDIASTREAM_MIC,
                            url,
                            url,
                            ContentSettingValues.DEFAULT);
                    WebsitePreferenceBridge.setContentSettingDefaultScope(
                            ProfileManager.getLastUsedRegularProfile(),
                            ContentSettingsType.MEDIASTREAM_CAMERA,
                            url,
                            url,
                            ContentSettingValues.ASK);
                });
    }

    private void clearPermissions() throws TimeoutException {
        CallbackHelper helper = new CallbackHelper();
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    BrowsingDataBridge.getForProfile(ProfileManager.getLastUsedRegularProfile())
                            .clearBrowsingData(
                                    helper::notifyCalled,
                                    new int[] {BrowsingDataType.SITE_SETTINGS},
                                    TimePeriod.ALL_TIME);
                });
        helper.waitForCallback(0);
    }

    private List<ContentSettingException> getNonWildcardContentSettingExceptions(
            @ContentSettingsType.EnumType int type) {
        return ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    List<ContentSettingException> exceptions =
                            new ArrayList<ContentSettingException>();
                    WebsitePreferenceBridgeJni.get()
                            .getContentSettingsExceptions(
                                    ProfileManager.getLastUsedRegularProfile(), type, exceptions);
                    Iterator<ContentSettingException> exceptionIt = exceptions.iterator();
                    while (exceptionIt.hasNext()) {
                        ContentSettingException exception = exceptionIt.next();
                        if (WebsitePreferenceBridge.SITE_WILDCARD.equals(
                                        exception.getPrimaryPattern())
                                && WebsitePreferenceBridge.SITE_WILDCARD.equals(
                                        exception.getSecondaryPattern())) {
                            exceptionIt.remove();
                        }
                    }
                    return exceptions;
                });
    }

    private void addSomeHistoryEntries() {
        StubbedHistoryProvider historyProvider = new StubbedHistoryProvider();
        // Need to always have the same dates for render tests.
        historyProvider.addItem(StubbedHistoryProvider.createHistoryItem(1, sTimestampApril4));
        historyProvider.addItem(StubbedHistoryProvider.createHistoryItem(1, sTimestampJune4));
        HistoryContentManager.setProviderForTests(historyProvider);
        PageInfoHistoryController.setProviderForTests(historyProvider);
    }

    @Before
    public void setUp() throws InterruptedException {
        // Some test devices have geolocation disabled. Override LocationUtils for a
        // stable result.
        LocationUtils.setFactory(TestLocationUtils::new);

        // Choose a fixed, "random" port to create stable screenshots.
        mTestServerRule.setServerPort(424242);
        mTestServerRule.setServerUsesHttps(true);

        PageInfoAdPersonalizationController.setTopicsForTesting(Arrays.asList("Testing topic"));
    }

    @After
    public void tearDown() throws TimeoutException {
        LocationUtils.setFactory(null);
        // Notification channels don't get cleaned up automatically.
        // TODO(crbug.com/41452182): Find a general solution to avoid leaking channels
        // between
        // tests.
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            ThreadUtils.runOnUiThreadBlocking(
                    () -> {
                        SiteChannelsManager manager = SiteChannelsManager.getInstance();
                        manager.deleteAllSiteChannels();
                    });
        }

        setThirdPartyCookieBlocking(CookieControlsMode.INCOGNITO_ONLY);
        clearPermissions();
    }

    /** Tests that PageInfoController converts safe URLs to Unicode. */
    @Test
    @MediumTest
    @Feature({"PageInfoController"})
    public void testPageInfoUrl() {
        String testUrl =
                mTestServerRule.getServer().getURLWithHostName("xn--allestrungen-9ib.ch", "/");
        loadUrlAndOpenPageInfo(testUrl);
        onView(
                withText(
                        allOf(
                                containsString("allestörungen.ch"),
                                not(containsString("https://")))));
        // Expand to full URL.
        onView(withId(R.id.page_info_url_wrapper)).perform(click());
        onView(withText(allOf(containsString("allestörungen.ch"), containsString("https://"))));
    }

    /** Tests PageInfo on an insecure website. */
    @Test
    @MediumTest
    public void testShowOnInsecureHttpWebsite() throws IOException {
        mTestServerRule.setServerUsesHttps(false);
        loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(sSimpleHtml));
        onViewWaiting(allOf(withId(R.id.page_info_connection_row), isDisplayed()));
        onView(withText("Connection is not secure")).check(matches(isDisplayed()));
    }

    /** Tests PageInfo on a secure website. */
    @Test
    @MediumTest
    public void testShowOnSecureWebsite() throws IOException {
        loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(sSimpleHtml));
        onViewWaiting(allOf(withId(R.id.page_info_connection_row), isDisplayed()));
        onView(withText("Connection is secure")).check(matches(isDisplayed()));
    }

    /** Tests PageInfo on a website with expired certificate. */
    @Test
    @MediumTest
    public void testShowOnExpiredCertificateWebsite() throws IOException {
        mTestServerRule.setCertificateType(ServerCertificate.CERT_EXPIRED);
        loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(sSimpleHtml));
        onViewWaiting(allOf(withId(R.id.page_info_connection_row), isDisplayed()));
        onView(withText("Connection is not secure")).check(matches(isDisplayed()));
    }

    /** Tests PageInfo on internal page. */
    @Test
    @MediumTest
    @Feature({"RenderTest"})
    public void testChromePage() throws IOException {
        loadUrlAndOpenPageInfo("chrome://version/");
        mRenderTestRule.render(getPageInfoView(), "PageInfo_InternalSite");
    }

    /**
     * Tests PageInfo on a website with permissions. Geolocation is blocked system wide in this
     * test.
     */
    @Test
    @MediumTest
    @Feature({"RenderTest"})
    public void testShowWithPermissionsTurnedOffForDevice() throws IOException {
        mIsSystemLocationSettingEnabled = false;
        addSomePermissions(mTestServerRule.getServer().getURL("/"));
        loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(sSimpleHtml));
        mRenderTestRule.render(getPageInfoView(), "PageInfo_PermissionsTurnedOffForDevice");
    }

    /** Tests PageInfo on a website with cookie controls and permissions. */
    @Test
    @MediumTest
    @Feature({"RenderTest"})
    public void testShowWithPermissionsAndCookieBlocking() throws IOException {
        addSomePermissions(mTestServerRule.getServer().getURL("/"));
        loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(sSimpleHtml));
        mRenderTestRule.render(getPageInfoView(), "PageInfo_Permissions");
    }

    /**
     * Tests PageInfo on a website with cookie controls and permissions with User Bypass enabled.
     */
    @Test
    @MediumTest
    @Feature({"RenderTest"})
    public void testShowWithPermissionsAndCookieBlockingUserBypass() throws IOException {
        addSomePermissions(mTestServerRule.getServer().getURL("/"));
        loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(sSimpleHtml));
        mRenderTestRule.render(getPageInfoView(), "PageInfo_Permissions_UserBypass");
    }

    /** Tests PageInfo on a website with 3PC and permissions with Tracking Protection enabled. */
    @Test
    @MediumTest
    @Feature({"RenderTest"})
    public void testShowWithPermissionsAndCookieBlockingTrackingProtection() throws IOException {
        enableTrackingProtection();
        addSomePermissions(mTestServerRule.getServer().getURL("/"));
        loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(sSimpleHtml));
        mRenderTestRule.render(getPageInfoView(), "PageInfo_Permissions_TrackingProtection");
    }

    /** Tests PageInfo on a website with default setting permissions. */
    @Test
    @MediumTest
    @Feature({"RenderTest"})
    public void testShowWithDefaultSettingPermissions() throws IOException {
        addDefaultSettingPermissions(mTestServerRule.getServer().getURL("/"));
        loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(sSimpleHtml));
        mRenderTestRule.render(getPageInfoView(), "PageInfo_DefaultSettingPermissions");
    }

    /** Tests PageInfo on a website with previous history entries. */
    @Test
    @MediumTest
    @Feature({"RenderTest"})
    public void testShowWithHistory() throws IOException {
        addSomeHistoryEntries();
        loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(sSimpleHtml));
        mRenderTestRule.render(getPageInfoView(), "PageInfo_History");
    }

    /** Tests the connection info page of the PageInfo UI - insecure website. */
    @Test
    @MediumTest
    @Feature({"RenderTest"})
    @DisabledTest(message = "Icon rendering blurry at times: crbug.com/1491905")
    public void testShowConnectionInfoSubpageInsecure() throws IOException {
        mTestServerRule.setServerUsesHttps(false);
        loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(sSimpleHtml));
        onView(withId(R.id.page_info_connection_row)).perform(click());
        onViewWaiting(
                allOf(
                        withText(containsString("The identity of this website isn't verified.")),
                        isDisplayed()));
        mRenderTestRule.render(getPageInfoView(), "PageInfo_ConnectionInfoSubpageInsecure");
    }

    /** Tests the connection info page of the PageInfo UI - secure website. */
    @Test
    @MediumTest
    @Feature({"RenderTest"})
    public void testShowConnectionInfoSubpageSecure() throws IOException {
        loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(sSimpleHtml));
        onView(withId(R.id.page_info_connection_row)).perform(click());
        onViewWaiting(
                allOf(
                        withText(containsString("Test Root CA issued this website's certificate.")),
                        isDisplayed()));
        mRenderTestRule.render(getPageInfoView(), "PageInfo_ConnectionInfoSubpageSecure");
    }

    /** Tests the connection info page of the PageInfo UI - expired certificate. */
    @Test
    @MediumTest
    @Feature({"RenderTest"})
    public void testShowConnectionInfoSubpageExpiredCert() throws IOException {
        mTestServerRule.setCertificateType(ServerCertificate.CERT_EXPIRED);
        loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(sSimpleHtml));
        onView(withId(R.id.page_info_connection_row)).perform(click());
        onViewWaiting(
                allOf(
                        withText(containsString("Server's certificate has expired.")),
                        isDisplayed()));
        mRenderTestRule.render(getPageInfoView(), "PageInfo_ConnectionInfoSubpageExpiredCert");
    }

    /** Tests the permissions page of the PageInfo UI with permissions. */
    @Test
    @MediumTest
    @Feature({"RenderTest"})
    public void testShowPermissionsSubpage() throws IOException {
        addSomePermissions(mTestServerRule.getServer().getURL("/"));
        loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(sSimpleHtml));
        onView(withId(R.id.page_info_permissions_row)).perform(click());
        onViewWaiting(allOf(withText("Control this site's access to your device"), isDisplayed()));
        mRenderTestRule.render(getPageInfoView(), "PageInfo_PermissionsSubpage");
    }

    /** Tests the permissions page of the PageInfo UI with sound permissions. */
    @Test
    @MediumTest
    public void testShowPermissionsSubpageWithSound() throws IOException {
        GURL url = new GURL(mTestServerRule.getServer().getURL("/"));
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    WebsitePreferenceBridge.setContentSettingDefaultScope(
                            ProfileManager.getLastUsedRegularProfile(),
                            ContentSettingsType.SOUND,
                            url,
                            url,
                            ContentSettingValues.BLOCK);
                });
        loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(sSimpleHtml));
        onView(withId(R.id.page_info_permissions_row)).perform(click());
        onViewWaiting(allOf(withText("Control this site's access to your device"), isDisplayed()));
        onView(allOf(withText(containsString("Sound")), isDisplayed()));
    }

    @Test
    @MediumTest
    @Features.EnableFeatures(ContentFeatureList.ONE_TIME_PERMISSION)
    public void testShowPermissionsSubpageWithEphemeralGrantAndPersistentGrant()
            throws IOException {
        GURL url = new GURL(mTestServerRule.getServer().getURL("/"));
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    WebsitePreferenceBridgeJni.get()
                            .setEphemeralGrantForTesting(
                                    ProfileManager.getLastUsedRegularProfile(),
                                    ContentSettingsType.GEOLOCATION,
                                    url,
                                    url);
                    WebsitePreferenceBridge.setContentSettingDefaultScope(
                            ProfileManager.getLastUsedRegularProfile(),
                            ContentSettingsType.MEDIASTREAM_CAMERA,
                            url,
                            url,
                            ContentSettingValues.ALLOW);
                });
        loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(sSimpleHtml));
        onView(withId(R.id.page_info_permissions_row)).perform(click());
        onViewWaiting(allOf(withText("Control this site's access to your device"), isDisplayed()));
        onView(withText("Location")).check(matches(hasSibling(withText("Allowed this time"))));
        onView(withText("Camera")).check(matches(hasSibling(withText("Allowed"))));
    }

    /** Tests the cookies page of the PageInfo UI with the Cookie Controls UI enabled. */
    @Test
    @MediumTest
    @Feature({"RenderTest"})
    @DisabledTest(message = "https://crbug.com/1510968")
    public void testShowCookiesSubpageUserBypassOn() throws IOException {
        setThirdPartyCookieBlocking(CookieControlsMode.BLOCK_THIRD_PARTY);
        loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(sSimpleHtml));
        onView(withId(R.id.page_info_cookies_row)).perform(click());
        onViewWaiting(
                allOf(
                        withText(containsString("Cookies and other site data are used")),
                        isDisplayed()));
        // Verify that the pref was recorded successfully.
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    assertTrue(
                            UserPrefs.get(ProfileManager.getLastUsedRegularProfile())
                                    .getBoolean(IN_CONTEXT_COOKIE_CONTROLS_OPENED));
                });
        mRenderTestRule.render(getPageInfoView(), "PageInfo_CookiesSubpage_Toggle_Off");
        // Check that the cookie toggle is displayed and try clicking it.
        onViewWaiting(allOf(withText(containsString("Third-party cookies")), isDisplayed()));
        onView(withText(containsString("Third-party cookies"))).perform(click());
        mRenderTestRule.render(getPageInfoView(), "PageInfo_CookiesSubpage_Toggle_On");
    }

    /** Tests the cookies page of the PageInfo UI with the Tracking Protection UI enabled. */
    @Test
    @MediumTest
    @Feature({"RenderTest"})
    public void testShowCookiesSubpageTrackingProtection() throws IOException {
        enableTrackingProtection();
        setBlockAll3PC(false);
        setThirdPartyCookieBlocking(CookieControlsMode.BLOCK_THIRD_PARTY);
        loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(sSimpleHtml));
        enableTrackingProtectionFixedExpiration();
        onView(withId(R.id.page_info_cookies_row)).perform(click());
        onViewWaiting(
                allOf(
                        withText(containsString("Chrome limits most sites from using")),
                        isDisplayed()));
        // Verify that the pref was recorded successfully.
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    assertTrue(
                            UserPrefs.get(ProfileManager.getLastUsedRegularProfile())
                                    .getBoolean(IN_CONTEXT_COOKIE_CONTROLS_OPENED));
                });
        mRenderTestRule.render(getPageInfoView(), "PageInfo_TrackingProtectionSubpage_Toggle_Off");
        // Check that the cookie toggle is displayed and try clicking it.
        onViewWaiting(allOf(withText(containsString("Third-party cookies")), isDisplayed()));
        onView(withText(containsString("Third-party cookies"))).perform(click());
        mRenderTestRule.render(getPageInfoView(), "PageInfo_TrackingProtectionSubpage_Toggle_On");
    }

    private void launchAndCheckTrackingProtectionLaunchUI() {
        setThirdPartyCookieBlocking(CookieControlsMode.BLOCK_THIRD_PARTY);
        loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(sSimpleHtml));
        enableTrackingProtectionFixedExpiration();
        onView(withId(R.id.page_info_cookies_row)).perform(click());
        onViewWaiting(
                allOf(
                        withText(containsString("Chrome limits most sites from using")),
                        isDisplayed()));
        // Verify that the pref was recorded successfully.
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    assertTrue(
                            UserPrefs.get(ProfileManager.getLastUsedRegularProfile())
                                    .getBoolean(IN_CONTEXT_COOKIE_CONTROLS_OPENED));
                });
    }

    /** Same as the previous one but with IP Protection feature enabled. */
    @Test
    @MediumTest
    @Features.EnableFeatures({
        ChromeFeatureList.IP_PROTECTION_USER_BYPASS,
        ChromeFeatureList.IP_PROTECTION_V1
    })
    @Feature({"RenderTest"})
    @DisabledTest(message = "crbug.com/330745124: only 3PC status is implemented in the TPF UI")
    public void testShowCookiesSubpageTrackingProtectionLaunchIPP() throws IOException {
        setBlockAll3PC(false);
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    UserPrefs.get(ProfileManager.getLastUsedRegularProfile())
                            .setBoolean(Pref.IP_PROTECTION_ENABLED, true);
                });
        launchAndCheckTrackingProtectionLaunchUI();
        mRenderTestRule.render(
                getPageInfoView(), "PageInfo_TrackingProtectionLaunchIPP_Toggle_Off");
        // Check that the cookie toggle is displayed and try clicking it.
        onViewWaiting(allOf(withText(containsString("You have extra protections")), isDisplayed()));
        onView(withText(containsString("You have extra protections"))).perform(click());
        mRenderTestRule.render(getPageInfoView(), "PageInfo_TrackingProtectionLaunchIPP_Toggle_On");
    }

    /** Same as the previous one but with Fingerprinting Protection feature enabled. */
    @Test
    @MediumTest
    @Features.EnableFeatures({
        ChromeFeatureList.FINGERPRINTING_PROTECTION_USER_BYPASS,
    })
    @Features.DisableFeatures(ChromeFeatureList.IP_PROTECTION_V1)
    @Feature({"RenderTest"})
    @DisabledTest(message = "crbug.com/330745124: only 3PC status is implemented in the TPF UI")
    public void testShowCookiesSubpageTrackingProtectionLaunchFPP() throws IOException {
        setBlockAll3PC(false);
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    UserPrefs.get(ProfileManager.getLastUsedRegularProfile())
                            .setBoolean(Pref.FINGERPRINTING_PROTECTION_ENABLED, true);
                });
        launchAndCheckTrackingProtectionLaunchUI();
        mRenderTestRule.render(
                getPageInfoView(), "PageInfo_TrackingProtectionLaunchFPP_Toggle_Off");
        // Check that the cookie toggle is displayed and try clicking it.
        onViewWaiting(allOf(withText(containsString("You have extra protections")), isDisplayed()));
        onView(withText(containsString("You have extra protections"))).perform(click());
        mRenderTestRule.render(getPageInfoView(), "PageInfo_TrackingProtectionLaunchFPP_Toggle_On");
    }

    /** Same as the previous one but with both IP and Fingerprinting Protection features enabled. */
    @Test
    @MediumTest
    @Features.EnableFeatures({
        ChromeFeatureList.IP_PROTECTION_USER_BYPASS,
        ChromeFeatureList.IP_PROTECTION_V1,
        ChromeFeatureList.FINGERPRINTING_PROTECTION_USER_BYPASS,
    })
    @Feature({"RenderTest"})
    @DisabledTest(message = "crbug.com/330745124: only 3PC status is implemented in the TPF UI")
    public void testShowCookiesSubpageTrackingProtectionLaunchFPPIPP() throws IOException {
        setBlockAll3PC(false);
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    UserPrefs.get(ProfileManager.getLastUsedRegularProfile())
                            .setBoolean(Pref.IP_PROTECTION_ENABLED, true);
                    UserPrefs.get(ProfileManager.getLastUsedRegularProfile())
                            .setBoolean(Pref.FINGERPRINTING_PROTECTION_ENABLED, true);
                });
        launchAndCheckTrackingProtectionLaunchUI();
        mRenderTestRule.render(
                getPageInfoView(), "PageInfo_TrackingProtectionLaunchFPPIPP_Toggle_Off");
        // Check that the cookie toggle is displayed and try clicking it.
        onViewWaiting(allOf(withText(containsString("You have extra protections")), isDisplayed()));
        onView(withText(containsString("You have extra protections"))).perform(click());
        mRenderTestRule.render(
                getPageInfoView(), "PageInfo_TrackingProtectionLaunchFPPIPP_Toggle_On");
    }

    /** Tests the cookies page of the PageInfo UI with the Tracking Protection UI enabled. */
    @Test
    @MediumTest
    @Feature({"RenderTest"})
    public void testShowCookiesSubpageTrackingProtectionBlockAll() throws IOException {
        enableTrackingProtection();
        setBlockAll3PC(true);
        setThirdPartyCookieBlocking(CookieControlsMode.BLOCK_THIRD_PARTY);
        loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(sSimpleHtml));
        enableTrackingProtectionFixedExpiration();
        onView(withId(R.id.page_info_cookies_row)).perform(click());
        onViewWaiting(
                allOf(withText(containsString("You blocked sites from using")), isDisplayed()));
        // Verify that the pref was recorded successfully.
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    assertTrue(
                            UserPrefs.get(ProfileManager.getLastUsedRegularProfile())
                                    .getBoolean(IN_CONTEXT_COOKIE_CONTROLS_OPENED));
                });
        mRenderTestRule.render(
                getPageInfoView(), "PageInfo_TrackingProtectionSubpage_Block_All_Toggle_Off");
        // Check that the cookie toggle is displayed and try clicking it.
        onViewWaiting(allOf(withText(containsString("Third-party cookies")), isDisplayed()));
        onView(withText(containsString("Third-party cookies"))).perform(click());
        mRenderTestRule.render(
                getPageInfoView(), "PageInfo_TrackingProtectionSubpage_Block_All_Toggle_On");
    }

    /** Tests the history page of the PageInfo UI. */
    @Test
    @MediumTest
    @Feature({"RenderTest"})
    public void testShowHistorySubpage() throws IOException {
        addSomeHistoryEntries();
        loadUrlAndOpenPageInfo(
                mTestServerRule.getServer().getURLWithHostName("www.example.com", "/"));
        onViewWaiting(allOf(withText(containsString("Last visited")), isDisplayed()));
        onView(withId(PageInfoHistoryController.HISTORY_ROW_ID)).perform(click());
        onViewWaiting(allOf(withText(containsString("Jun 4, 2021")), isDisplayed()));
        mRenderTestRule.render(getPageInfoView(), "PageInfo_HistorySubpage");
    }

    /**
     * Tests that the permissions page of the PageInfo UI is gone when there are no permissions set.
     */
    @Test
    @MediumTest
    public void testNoPermissionsSubpage() throws IOException {
        loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(sSimpleHtml));
        onView(withId(R.id.page_info_permissions_row))
                .check(matches(withEffectiveVisibility(GONE)));
    }

    /** Tests clearing cookies on the cookies page of the PageInfo UI. */
    @Test
    @MediumTest
    public void testClearCookiesOnSubpage() throws Exception {
        sActivityTestRule.loadUrl(mTestServerRule.getServer().getURL(sSiteDataHtml));
        // Create cookies.
        expectHasCookies(false);
        createCookies();
        expectHasCookies(true);
        // Go to cookies subpage.
        openPageInfo(PageInfoController.NO_HIGHLIGHTED_PERMISSION);
        onView(withId(R.id.page_info_cookies_row)).perform(click());
        // Check that cookies usage is displayed.
        onViewWaiting(allOf(withText(containsString("stored data")), isDisplayed()));
        // Clear cookies in page info.
        onView(withText(containsString("stored data"))).perform(click());
        onView(withText("Delete")).perform(click());
        // Wait until the UI navigates back and check cookies are deleted.
        onViewWaiting(allOf(withId(R.id.page_info_cookies_row), isDisplayed()));
        expectHasCookies(false);
    }

    /** Tests clearing cookies on the cookies page of the PageInfo UI with User Bypass enabled. */
    @Test
    @MediumTest
    public void testClearCookiesOnSubpageUserBypass() throws Exception {
        setThirdPartyCookieBlocking(CookieControlsMode.BLOCK_THIRD_PARTY);
        sActivityTestRule.loadUrl(mTestServerRule.getServer().getURL(sSiteDataHtml));
        // Create cookies.
        expectHasCookies(false);
        createCookies();
        expectHasCookies(true);
        // Go to cookies subpage.
        openPageInfo(PageInfoController.NO_HIGHLIGHTED_PERMISSION);
        onView(withId(R.id.page_info_cookies_row)).perform(click());
        // Check that cookies usage is displayed.
        onViewWaiting(allOf(withText(containsString("stored data")), isDisplayed()));
        // Check that the cookie toggle is displayed and try clicking it.
        onViewWaiting(allOf(withText(containsString("Third-party cookies")), isDisplayed()));
        onView(withText(containsString("Third-party cookies"))).perform(click());
        // Clear cookies in page info.
        onView(withText(containsString("stored data"))).perform(click());
        onViewWaiting(allOf(withText("Delete"), isDisplayed()));
        onView(withText("Delete")).perform(click());
        // Wait until the UI navigates back and check cookies are deleted.
        onViewWaiting(allOf(withId(R.id.page_info_cookies_row), isDisplayed()));
        expectHasCookies(false);
    }

    /** Tests clearing cookies on the Tracking Protection page of the PageInfo UI. */
    @Test
    @MediumTest
    public void testClearCookiesOnSubpageTrackingProtection() throws Exception {
        enableTrackingProtection();
        setThirdPartyCookieBlocking(CookieControlsMode.BLOCK_THIRD_PARTY);
        sActivityTestRule.loadUrl(mTestServerRule.getServer().getURL(sSiteDataHtml));
        // Create cookies.
        expectHasCookies(false);
        createCookies();
        expectHasCookies(true);
        // Go to cookies subpage.
        openPageInfo(PageInfoController.NO_HIGHLIGHTED_PERMISSION);
        onView(withId(R.id.page_info_cookies_row)).perform(click());
        // Check that cookies usage is displayed.
        onViewWaiting(allOf(withText(containsString("stored data")), isDisplayed()));
        // Check that the cookie toggle is displayed and try clicking it.
        onViewWaiting(allOf(withText(containsString("Third-party cookies")), isDisplayed()));
        onView(withText(containsString("Third-party cookies"))).perform(click());
        // Clear cookies in page info.
        onView(withText(containsString("stored data"))).perform(click());
        onViewWaiting(allOf(withText("Delete"), isDisplayed()));
        onView(withText("Delete")).perform(click());
        // Wait until the UI navigates back and check cookies are deleted.
        onViewWaiting(allOf(withId(R.id.page_info_cookies_row), isDisplayed()));
        expectHasCookies(false);
    }

    /** Tests resetting permissions on the permissions page of the PageInfo UI. */
    @Test
    @MediumTest
    public void testResetPermissionsOnSubpage() throws Exception {
        sActivityTestRule.loadUrl(mTestServerRule.getServer().getURL(sSiteDataHtml));
        String url = mTestServerRule.getServer().getURL("/");
        // Create permissions.
        expectHasPermissions(url, false);
        addSomePermissions(url);
        expectHasPermissions(url, true);
        // Go to permissions subpage.
        openPageInfo(PageInfoController.NO_HIGHLIGHTED_PERMISSION);
        onView(withId(R.id.page_info_permissions_row)).inRoot(isDialog()).perform(click());
        // Clear permissions in page info.
        onViewWaiting(allOf(withText("Reset permissions"), isDisplayed())).perform(click());
        onView(withText("Reset")).perform(click());
        // Wait until the UI navigates back and check permissions are reset.
        onViewWaiting(allOf(withId(R.id.page_info_row_wrapper), isDisplayed()));
        // Make sure that the permission section is gone because there are no longer
        // exceptions.
        onView(withId(R.id.page_info_permissions_row))
                .check(matches(withEffectiveVisibility(GONE)));
        expectHasPermissions(url, false);
    }

    /**
     * Test that enabling the federated identity permission in the PageInfo UI clears the embargo.
     */
    @Test
    @MediumTest
    public void testClearFederatedIdentityEmbargoOnSubpage() throws Exception {
        String rpUrl = mTestServerRule.getServer().getURL(sSimpleHtml);
        sActivityTestRule.loadUrl(rpUrl);

        assertTrue(
                getNonWildcardContentSettingExceptions(ContentSettingsType.FEDERATED_IDENTITY_API)
                        .isEmpty());
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    FederatedIdentityTestUtils.embargoFedCmForRelyingParty(new GURL(rpUrl));
                });
        {
            List<ContentSettingException> exceptions =
                    getNonWildcardContentSettingExceptions(
                            ContentSettingsType.FEDERATED_IDENTITY_API);
            assertEquals(1, exceptions.size());
            assertEquals(GURLUtils.getOrigin(rpUrl), exceptions.get(0).getPrimaryPattern() + "/");
            assertEquals(
                    ContentSettingValues.BLOCK, exceptions.get(0).getContentSetting().intValue());
        }

        // Toggle the federated identity permission.
        openPageInfo(PageInfoController.NO_HIGHLIGHTED_PERMISSION);
        onView(withId(R.id.page_info_permissions_row)).perform(click());
        onView(withId(R.id.switchWidget)).perform(click());

        {
            List<ContentSettingException> exceptions =
                    getNonWildcardContentSettingExceptions(
                            ContentSettingsType.FEDERATED_IDENTITY_API);
            assertEquals(1, exceptions.size());
            assertEquals(GURLUtils.getOrigin(rpUrl), exceptions.get(0).getPrimaryPattern() + "/");
            assertEquals(
                    ContentSettingValues.ALLOW, exceptions.get(0).getContentSetting().intValue());
        }
    }

    /** Tests that page info view is shown correctly for paint preview pages. */
    @Test
    @MediumTest
    public void testPaintPreview() {
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    final ChromeActivity activity = sActivityTestRule.getActivity();
                    final Tab tab = activity.getActivityTab();
                    ChromePageInfoControllerDelegate pageInfoControllerDelegate =
                            new ChromePageInfoControllerDelegate(
                                    activity,
                                    tab.getWebContents(),
                                    activity::getModalDialogManager,
                                    new OfflinePageUtils.TabOfflinePageLoadUrlDelegate(tab),
                                    null,
                                    null,
                                    ChromePageInfoHighlight.noHighlight(),
                                    null) {
                                @Override
                                public boolean isShowingPaintPreviewPage() {
                                    return true;
                                }
                            };
                    PageInfoController.show(
                            sActivityTestRule.getActivity(),
                            tab.getWebContents(),
                            null,
                            PageInfoController.OpenedFromSource.MENU,
                            pageInfoControllerDelegate,
                            ChromePageInfoHighlight.noHighlight());
                });
        onViewWaiting(
                allOf(withText(R.string.page_info_connection_paint_preview), isDisplayed()), true);
    }

    /** Tests that page info view is shown correctly for transient pdf pages. */
    @Test
    @MediumTest
    public void testTransientPdfPage() {
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    final ChromeActivity activity = sActivityTestRule.getActivity();
                    final Tab tab = activity.getActivityTab();
                    ChromePageInfoControllerDelegate pageInfoControllerDelegate =
                            new ChromePageInfoControllerDelegate(
                                    activity,
                                    tab.getWebContents(),
                                    activity::getModalDialogManager,
                                    new OfflinePageUtils.TabOfflinePageLoadUrlDelegate(tab),
                                    null,
                                    null,
                                    ChromePageInfoHighlight.noHighlight(),
                                    null) {
                                @Override
                                public @PdfPageType int getPdfPageType() {
                                    return PdfPageType.TRANSIENT_SECURE;
                                }
                            };
                    PageInfoController.show(
                            sActivityTestRule.getActivity(),
                            tab.getWebContents(),
                            null,
                            PageInfoController.OpenedFromSource.MENU,
                            pageInfoControllerDelegate,
                            ChromePageInfoHighlight.noHighlight());
                });
        onViewWaiting(
                allOf(withText(R.string.page_info_connection_transient_pdf), isDisplayed()), true);
    }

    /** Tests that page info view is shown correctly for insecure transient pdf pages. */
    @Test
    @MediumTest
    public void testInsecureTransientPdfPage() {
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    final ChromeActivity activity = sActivityTestRule.getActivity();
                    final Tab tab = activity.getActivityTab();
                    ChromePageInfoControllerDelegate pageInfoControllerDelegate =
                            new ChromePageInfoControllerDelegate(
                                    activity,
                                    tab.getWebContents(),
                                    activity::getModalDialogManager,
                                    new OfflinePageUtils.TabOfflinePageLoadUrlDelegate(tab),
                                    null,
                                    null,
                                    ChromePageInfoHighlight.noHighlight(),
                                    null) {
                                @Override
                                public @PdfPageType int getPdfPageType() {
                                    return PdfPageType.TRANSIENT_INSECURE;
                                }
                            };
                    PageInfoController.show(
                            sActivityTestRule.getActivity(),
                            tab.getWebContents(),
                            null,
                            PageInfoController.OpenedFromSource.MENU,
                            pageInfoControllerDelegate,
                            ChromePageInfoHighlight.noHighlight());
                });
        onViewWaiting(
                allOf(
                        withText(R.string.page_info_connection_transient_pdf_insecure),
                        isDisplayed()),
                true);
    }

    /** Tests that page info view is shown correctly for local pdf pages. */
    @Test
    @MediumTest
    public void testLocalPdfPage() {
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    final ChromeActivity activity = sActivityTestRule.getActivity();
                    final Tab tab = activity.getActivityTab();
                    ChromePageInfoControllerDelegate pageInfoControllerDelegate =
                            new ChromePageInfoControllerDelegate(
                                    activity,
                                    tab.getWebContents(),
                                    activity::getModalDialogManager,
                                    new OfflinePageUtils.TabOfflinePageLoadUrlDelegate(tab),
                                    null,
                                    null,
                                    ChromePageInfoHighlight.noHighlight(),
                                    null) {
                                @Override
                                public @PdfPageType int getPdfPageType() {
                                    return PdfPageType.LOCAL;
                                }
                            };
                    PageInfoController.show(
                            sActivityTestRule.getActivity(),
                            tab.getWebContents(),
                            null,
                            PageInfoController.OpenedFromSource.MENU,
                            pageInfoControllerDelegate,
                            ChromePageInfoHighlight.noHighlight());
                });
        onViewWaiting(
                allOf(withText(R.string.page_info_connection_local_pdf), isDisplayed()), true);
    }

    /** Tests PageInfo on a website with permissions and no particular row highlight. */
    @Test
    @MediumTest
    public void testShowWithPermissionsAndWithoutHighlight() throws IOException {
        addSomePermissions(mTestServerRule.getServer().getURL("/"));
        loadUrlAndOpenPageInfoWithPermission(
                mTestServerRule.getServer().getURL(sSimpleHtml),
                PageInfoController.NO_HIGHLIGHTED_PERMISSION);
        onView(withId(R.id.page_info_permissions_row))
                .inRoot(isDialog())
                .check(matches(not(hasBackgroundColor(R.color.iph_highlight_blue))));
    }

    /**
     * Tests PageInfo on a website with permissions and a particular permission row highlight.
     * Geolocation is blocked system wide in this test.
     */
    @Test
    @MediumTest
    public void testShowWithPermissionsAndHighlight() throws IOException {
        addSomePermissions(mTestServerRule.getServer().getURL("/"));
        loadUrlAndOpenPageInfoWithPermission(
                mTestServerRule.getServer().getURL(sSimpleHtml), ContentSettingsType.GEOLOCATION);
        onView(withId(R.id.page_info_permissions_row))
                .check(matches(hasBackgroundColor(R.color.iph_highlight_blue)));
    }

    /**
     * Tests the permissions page of the PageInfo UI with permissions and a particular permission
     * row highlight.
     */
    @Test
    @MediumTest
    public void testShowPermissionsSubpageWithHighlight() throws IOException {
        addSomePermissions(mTestServerRule.getServer().getURL("/"));
        loadUrlAndOpenPageInfoWithPermission(
                mTestServerRule.getServer().getURL(sSimpleHtml), ContentSettingsType.GEOLOCATION);
        onView(withId(R.id.page_info_permissions_row)).perform(click());
        onViewWaiting(allOf(withText("Control this site's access to your device"), isDisplayed()));
        Context context = ApplicationProvider.getApplicationContext();
        // Find the preference and check its background color.
        onView(
                        allOf(
                                withParent(withId(R.id.recycler_view)),
                                hasDescendant(
                                        withText(
                                                context.getString(
                                                        R.string
                                                                .website_settings_device_location)))))
                .check(matches(hasBackgroundColor(R.color.iph_highlight_blue)));
    }

    /**
     * Tests that a close button is shown with accessibility enabled and that it closes the dialog.
     */
    @Test
    @MediumTest
    public void testCloseButton() {
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    ChromeAccessibilityUtil.get().setAccessibilityEnabledForTesting(true);
                });
        loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(sSimpleHtml));
        PageInfoController controller = PageInfoController.getLastPageInfoControllerForTesting();
        assertTrue(controller.isDialogShowingForTesting());
        onView(withId(R.id.page_info_close)).perform(click());
        assertFalse(controller.isDialogShowingForTesting());
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    ChromeAccessibilityUtil.get().setAccessibilityEnabledForTesting(null);
                });
    }

    /** Tests the summary string of the history page of the PageInfo UI. */
    @Test
    @MediumTest
    @ParameterAnnotations.UseMethodParameter(HistorySummaryTestParams.class)
    public void testHistorySummaryText(long timestamp, String expectedSummary) throws IOException {
        StubbedHistoryProvider historyProvider = new StubbedHistoryProvider();
        historyProvider.addItem(StubbedHistoryProvider.createHistoryItem(1, timestamp));
        PageInfoHistoryController.setProviderForTests(historyProvider);
        PageInfoHistoryController.setClockForTesting(
                () -> {
                    return sTimestampJune4;
                });

        loadUrlAndOpenPageInfo(
                mTestServerRule.getServer().getURLWithHostName("www.example.com", "/"));
        onViewWaiting(allOf(withText(containsString(expectedSummary)), isDisplayed()));
    }

    /** Tests clicking on a history item from the history page of the PageInfo UI. */
    @Test
    @MediumTest
    public void testHistorySubpageItemClick() throws Exception {
        StubbedHistoryProvider historyProvider = new StubbedHistoryProvider();
        historyProvider.addItem(StubbedHistoryProvider.createHistoryItem(1, sTimestampJune4));
        HistoryContentManager.setProviderForTests(historyProvider);
        PageInfoHistoryController.setProviderForTests(historyProvider);
        loadUrlAndOpenPageInfo(
                mTestServerRule.getServer().getURLWithHostName("www.example.com", "/"));

        final CallbackHelper onDidStartNavigationHelper = new CallbackHelper();
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    return new WebContentsObserver(sActivityTestRule.getWebContents()) {
                        @Override
                        public void didStartNavigationInPrimaryMainFrame(
                                NavigationHandle navigationHandle) {
                            if (navigationHandle.getUrl().getHost().equals("www.example.com")) {
                                onDidStartNavigationHelper.notifyCalled();
                            }
                        }
                    };
                });
        onViewWaiting(allOf(withText(containsString("Last visited")), isDisplayed()));
        onView(withId(PageInfoHistoryController.HISTORY_ROW_ID)).perform(click());
        onViewWaiting(allOf(withText(containsString("Jun 4, 2021")), isDisplayed()));
        int callCount = onDidStartNavigationHelper.getCallCount();
        onView(withText("www.example.com")).perform(click());
        onDidStartNavigationHelper.waitForCallback(callCount);
    }

    /** Tests PageInfo on a website with ad personalization info. */
    @Test
    @MediumTest
    @Feature({"RenderTest"})
    public void testShowAdPersonalizationInfo() throws IOException {
        loadUrlAndOpenPageInfo(
                mTestServerRule.getServer().getURLWithHostName("example.com", sSimpleHtml));
        mRenderTestRule.render(getPageInfoView(), "PageInfo_AdPersonalization");
    }

    /** Tests ad personalization subpage. */
    @Test
    @MediumTest
    @Feature({"RenderTest"})
    public void testShowAdPersonalizationInfoSubPageV4() throws IOException {
        loadUrlAndOpenPageInfo(
                mTestServerRule.getServer().getURLWithHostName("example.com", sSimpleHtml));
        onView(withId(PageInfoAdPersonalizationController.ROW_ID))
                .inRoot(isDialog())
                .perform(click());
        onViewWaiting(
                allOf(
                        withText(R.string.page_info_ad_privacy_subpage_manage_button),
                        isDisplayed()));
        mRenderTestRule.render(getPageInfoView(), "PageInfo_AdPersonalizationSubPageV4");
    }

    /** Tests opening ad personalization settings. */
    @Test
    @MediumTest
    public void testOpenAdPersonalizationSettingsV4() throws IOException {
        loadUrlAndOpenPageInfo(
                mTestServerRule.getServer().getURLWithHostName("example.com", sSimpleHtml));
        onView(withId(PageInfoAdPersonalizationController.ROW_ID)).perform(click());
        onViewWaiting(
                        allOf(
                                withText(R.string.page_info_ad_privacy_subpage_manage_button),
                                isDisplayed()))
                .perform(click());
        // Check that settings are displayed.
        onView(withText(R.string.ad_privacy_page_topics_link_row_label))
                .check(matches(isDisplayed()));
        // Leave settings view.
        onView(withContentDescription("Navigate up")).perform(click());
        onView(withText(R.string.ad_privacy_page_topics_link_row_label)).check(doesNotExist());
    }

    // TODO(crbug.com/40685274): Add tests for preview pages, offline pages, offline
    // state and other
    // states.
}