chromium/chrome/android/javatests/src/org/chromium/chrome/browser/incognito/IncognitoHistoryLeakageTest.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.incognito;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import android.content.Intent;

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

import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Before;
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.UseMethodParameter;
import org.chromium.base.test.params.ParameterAnnotations.UseRunnerDelegate;
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.CommandLineFlags;
import org.chromium.base.test.util.Criteria;
import org.chromium.base.test.util.CriteriaHelper;
import org.chromium.chrome.browser.customtabs.CustomTabsIntentTestUtils;
import org.chromium.chrome.browser.customtabs.IncognitoCustomTabActivityTestRule;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.browser.history.BrowsingHistoryBridge;
import org.chromium.chrome.browser.history.HistoryItem;
import org.chromium.chrome.browser.history.TestBrowsingHistoryObserver;
import org.chromium.chrome.browser.incognito.IncognitoDataTestUtils.ActivityType;
import org.chromium.chrome.browser.incognito.IncognitoDataTestUtils.TestParams;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.profiles.ProfileManager;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate;
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
import org.chromium.content_public.browser.NavigationEntry;
import org.chromium.content_public.browser.NavigationHistory;
import org.chromium.net.test.EmbeddedTestServer;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeoutException;

/**
 * Testing browsing and navigation history leaks. Browsing history leaks are checked from incognito
 * to regular mode. Navigation history leaks are checked across all tabbed and CCT activity types.
 */
@RunWith(ParameterizedRunner.class)
@UseRunnerDelegate(ChromeJUnit4RunnerDelegate.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
public class IncognitoHistoryLeakageTest {
    private static final String TEST_PAGE_1 = "/chrome/test/data/android/google.html";
    private static final String TEST_PAGE_2 = "/chrome/test/data/android/test.html";

    private EmbeddedTestServer mTestServer;
    private String mTestPage1;
    private String mTestPage2;

    @Rule
    public ChromeTabbedActivityTestRule mChromeActivityTestRule =
            new ChromeTabbedActivityTestRule();

    @Rule
    public IncognitoCustomTabActivityTestRule mCustomTabActivityTestRule =
            new IncognitoCustomTabActivityTestRule();

    @Before
    public void setUp() throws TimeoutException {
        mTestServer =
                EmbeddedTestServer.createAndStartServer(
                        ApplicationProvider.getApplicationContext());
        mTestPage1 = mTestServer.getURL(TEST_PAGE_1);
        mTestPage2 = mTestServer.getURL(TEST_PAGE_2);
    }

    @After
    public void tearDown() {
        ThreadUtils.runOnUiThreadBlocking(
                () -> IncognitoDataTestUtils.closeTabs(mChromeActivityTestRule));
    }

    /**
     * Returns browsing history for the profile related to |tab|. If |tab| is null, the regular
     * profile is used.
     */
    private static List<HistoryItem> getBrowsingHistory(Tab tab) throws TimeoutException {
        final TestBrowsingHistoryObserver historyObserver = new TestBrowsingHistoryObserver();
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    Profile profile =
                            (tab == null)
                                    ? ProfileManager.getLastUsedRegularProfile()
                                    : tab.getProfile();
                    BrowsingHistoryBridge historyService = new BrowsingHistoryBridge(profile);
                    historyService.setObserver(historyObserver);
                    String historyQueryFilter = "";
                    historyService.queryHistory(historyQueryFilter, null);
                });
        historyObserver.getQueryCallback().waitForCallback(0);
        return historyObserver.getHistoryQueryResults();
    }

    /**
     * A general class providing test parameters encapsulating different Activity type pairs spliced
     * on Regular and Incognito mode between whom we want to test leakage.
     */
    public static class AllTypesToAllTypes implements ParameterProvider {
        @Override
        public List<ParameterSet> getParameters() {
            List<ParameterSet> result = new ArrayList<>();
            result.addAll(new TestParams.IncognitoToIncognito().getParameters());
            result.addAll(new TestParams.IncognitoToRegular().getParameters());
            result.addAll(new TestParams.RegularToIncognito().getParameters());
            return result;
        }
    }

    @Test
    @LargeTest
    public void testBrowsingHistoryDoNotLeakFromIncognitoTabbedActivity() throws TimeoutException {
        mChromeActivityTestRule.startMainActivityOnBlankPage();
        mChromeActivityTestRule.loadUrlInNewTab(mTestPage1, /* incognito= */ true);
        List<HistoryItem> historyEntriesOfIncognitoMode =
                getBrowsingHistory(mChromeActivityTestRule.getActivity().getActivityTab());
        assertTrue(historyEntriesOfIncognitoMode.isEmpty());
    }

    @Test
    @LargeTest
    public void testBrowsingHistoryDoNotLeakFromIncognitoCustomTabActivity()
            throws TimeoutException {
        Intent intent =
                CustomTabsIntentTestUtils.createMinimalIncognitoCustomTabIntent(
                        ApplicationProvider.getApplicationContext(), mTestPage1);
        mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
        List<HistoryItem> historyEntriesOfIncognitoMode =
                getBrowsingHistory(mCustomTabActivityTestRule.getActivity().getActivityTab());
        assertTrue(historyEntriesOfIncognitoMode.isEmpty());
    }

    @Test
    @LargeTest
    @UseMethodParameter(AllTypesToAllTypes.class)
    public void testTabNavigationHistoryDoNotLeakBetweenActivities(
            String activityType1, String activityType2) throws TimeoutException {
        ActivityType activity1 = ActivityType.valueOf(activityType1);
        ActivityType activity2 = ActivityType.valueOf(activityType2);

        Tab tab1 =
                activity1.launchUrl(
                        mChromeActivityTestRule, mCustomTabActivityTestRule, mTestPage1);
        CriteriaHelper.pollUiThread(
                () -> Criteria.checkThat(tab1.getWebContents(), Matchers.notNullValue()));
        NavigationHistory navigationHistory1 =
                tab1.getWebContents().getNavigationController().getNavigationHistory();

        Tab tab2 =
                activity2.launchUrl(
                        mChromeActivityTestRule, mCustomTabActivityTestRule, mTestPage2);
        CriteriaHelper.pollUiThread(
                () -> Criteria.checkThat(tab2.getWebContents(), Matchers.notNullValue()));
        NavigationHistory navigationHistory2 =
                tab2.getWebContents().getNavigationController().getNavigationHistory();

        assertEquals(1, navigationHistory1.getEntryCount());
        assertEquals(1, navigationHistory2.getEntryCount());

        NavigationEntry entry1 = navigationHistory1.getEntryAtIndex(0);
        NavigationEntry entry2 = navigationHistory2.getEntryAtIndex(0);

        assertEquals(mTestPage1, entry1.getOriginalUrl().getSpec());
        assertEquals(mTestPage2, entry2.getOriginalUrl().getSpec());
    }
}