chromium/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/CookieControlsBridgeTest.java

// Copyright 2019 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.site_settings;

import static org.junit.Assert.assertEquals;

import androidx.test.filters.SmallTest;

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.util.CallbackHelper;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.DisabledTest;
import org.chromium.base.test.util.Features.DisableFeatures;
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.profiles.Profile;
import org.chromium.chrome.browser.profiles.ProfileManager;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
import org.chromium.chrome.test.batch.BlankCTATabInitialStateRule;
import org.chromium.components.browser_ui.site_settings.WebsitePreferenceBridge;
import org.chromium.components.content_settings.ContentSettingValues;
import org.chromium.components.content_settings.ContentSettingsType;
import org.chromium.components.content_settings.CookieBlocking3pcdStatus;
import org.chromium.components.content_settings.CookieControlsBridge;
import org.chromium.components.content_settings.CookieControlsEnforcement;
import org.chromium.components.content_settings.CookieControlsMode;
import org.chromium.components.content_settings.CookieControlsObserver;
import org.chromium.components.content_settings.PrefNames;
import org.chromium.components.user_prefs.UserPrefs;
import org.chromium.content_public.browser.test.util.JavaScriptUtils;
import org.chromium.net.test.EmbeddedTestServer;

import java.util.concurrent.TimeoutException;

/** Integration tests for CookieControlsBridge. */
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
// TODO(crbug.com/344669865): Failing when batched, batch this again.
public class CookieControlsBridgeTest {
    public static final String COOKIE_CONTROLS_BATCH_NAME = "cookie_controls";

    private class TestCallbackHandler implements CookieControlsObserver {
        private CallbackHelper mHelper;

        public TestCallbackHandler(CallbackHelper helper) {
            mHelper = helper;
        }

        @Override
        public void onStatusChanged(
                boolean controlsVisible,
                boolean protectionsOn,
                @CookieControlsEnforcement int enforcement,
                @CookieBlocking3pcdStatus int blockingStatus,
                long expiration) {
            mCookieControlsVisible = controlsVisible;
            mThirdPartyCookiesBlocked = protectionsOn;
            mEnforcement = enforcement;
            mExpiration = expiration;
            mHelper.notifyCalled();
        }

        @Override
        public void onHighlightCookieControl(boolean shouldHighlight) {
            mShouldHighlight = shouldHighlight;
            mHelper.notifyCalled();
        }
    }

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

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

    private EmbeddedTestServer mTestServer;
    private CallbackHelper mCallbackHelper;
    private TestCallbackHandler mCallbackHandler;
    private CookieControlsBridge mCookieControlsBridge;
    private boolean mCookieControlsVisible;
    private boolean mThirdPartyCookiesBlocked;
    private int mEnforcement;
    private long mExpiration;
    private boolean mShouldHighlight;

    @Before
    public void setUp() throws Exception {
        mCallbackHelper = new CallbackHelper();
        mCallbackHandler = new TestCallbackHandler(mCallbackHelper);
        mTestServer = sActivityTestRule.getTestServer();
        mCookieControlsVisible = false;
        mThirdPartyCookiesBlocked = false;
        mExpiration = -1;
        mShouldHighlight = false;
    }

    @After
    public void tearDown() throws TimeoutException {
        // Reset cookies and cookie settings.
        CallbackHelper helper = new CallbackHelper();
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    Profile profile = ProfileManager.getLastUsedRegularProfile();
                    UserPrefs.get(profile).clearPref(PrefNames.COOKIE_CONTROLS_MODE);
                    WebsitePreferenceBridge.setDefaultContentSetting(
                            profile, ContentSettingsType.COOKIES, ContentSettingValues.DEFAULT);
                    BrowsingDataBridge.getForProfile(profile)
                            .clearBrowsingData(
                                    helper::notifyCalled,
                                    new int[] {BrowsingDataType.SITE_DATA},
                                    TimePeriod.ALL_TIME);
                });
        helper.waitForCallback(0);
    }

    @Test
    @SmallTest
    // This test will become obsolete when 3PCD is rolled out.
    @DisableFeatures(ChromeFeatureList.TRACKING_PROTECTION_3PCD)
    public void testCookieBridgeWithTPCookiesDisabledUserBypass() throws Exception {
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    // Set CookieControlsMode Pref to Off
                    UserPrefs.get(ProfileManager.getLastUsedRegularProfile())
                            .setInteger(PrefNames.COOKIE_CONTROLS_MODE, CookieControlsMode.OFF);
                });
        int currentCallCount = mCallbackHelper.getCallCount();

        // Navigate to a page
        final String url = mTestServer.getURL("/chrome/test/data/android/cookie.html");
        Tab tab = sActivityTestRule.loadUrlInNewTab(url, false);

        // Create cookie bridge and wait for desired callbacks.
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mCookieControlsBridge =
                            new CookieControlsBridge(mCallbackHandler, tab.getWebContents(), null);
                });

        mCallbackHelper.waitForCallback(currentCallCount, 2);
        assertEquals(false, mCookieControlsVisible);
        assertEquals(false, mThirdPartyCookiesBlocked);
        assertEquals(CookieControlsEnforcement.NO_ENFORCEMENT, mEnforcement);
    }

    @Test
    @SmallTest
    public void testCookieBridgeWith3PCookiesEnabledUserBypass() throws Exception {
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    UserPrefs.get(ProfileManager.getLastUsedRegularProfile())
                            .setInteger(
                                    PrefNames.COOKIE_CONTROLS_MODE,
                                    CookieControlsMode.BLOCK_THIRD_PARTY);
                });
        int currentCallCount = mCallbackHelper.getCallCount();

        // Navigate to a page
        final String url = mTestServer.getURL("/chrome/test/data/android/cookie.html");
        Tab tab = sActivityTestRule.loadUrlInNewTab(url, false);

        // Create cookie bridge and wait for desired callbacks.
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mCookieControlsBridge =
                            new CookieControlsBridge(mCallbackHandler, tab.getWebContents(), null);
                });

        mCallbackHelper.waitForCallback(currentCallCount, 2);
        assertEquals(true, mCookieControlsVisible);
        assertEquals(true, mThirdPartyCookiesBlocked);
        assertEquals(CookieControlsEnforcement.NO_ENFORCEMENT, mEnforcement);
    }

    @Test
    @SmallTest
    @DisabledTest(
            message = "TODO(crbug.com/40068942): Cookies need to be set in third-party context.")
    public void testCookieBridgeWithChangingAllowedCookiesCountUserBypass() throws Exception {
        int currentCallCount = mCallbackHelper.getCallCount();

        // Navigate to a page
        final String url = mTestServer.getURL("/chrome/test/data/android/cookie.html");
        Tab tab = sActivityTestRule.loadUrlInNewTab(url, false);

        // Create cookie bridge and wait for desired callbacks.
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mCookieControlsBridge =
                            new CookieControlsBridge(mCallbackHandler, tab.getWebContents(), null);
                });

        mCallbackHelper.waitForCallback(currentCallCount, 2);
        assertEquals(false, mCookieControlsVisible);
        assertEquals(false, mThirdPartyCookiesBlocked);
        assertEquals(CookieControlsEnforcement.NO_ENFORCEMENT, mEnforcement);

        // Try to set a cookie on the page when cookies are allowed.
        currentCallCount = mCallbackHelper.getCallCount();
        JavaScriptUtils.executeJavaScriptAndWaitForResult(tab.getWebContents(), "setCookie()");
        mCallbackHelper.waitForCallback(currentCallCount, 1);
    }

    @Test
    @SmallTest
    @DisabledTest(
            message = "TODO(crbug.com/40068942): Cookies need to be set in third-party context.")
    public void testCookieBridgeWithChangingBlockedCookiesCountUserBypass() throws Exception {
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    UserPrefs.get(ProfileManager.getLastUsedRegularProfile())
                            .setInteger(
                                    PrefNames.COOKIE_CONTROLS_MODE,
                                    CookieControlsMode.BLOCK_THIRD_PARTY);
                    // Block all cookies
                    WebsitePreferenceBridge.setCategoryEnabled(
                            ProfileManager.getLastUsedRegularProfile(),
                            ContentSettingsType.COOKIES,
                            false);
                });
        int currentCallCount = mCallbackHelper.getCallCount();

        // Navigate to a page
        final String url = mTestServer.getURL("/chrome/test/data/android/cookie.html");
        Tab tab = sActivityTestRule.loadUrlInNewTab(url, false);

        // Create cookie bridge and wait for desired callbacks.
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mCookieControlsBridge =
                            new CookieControlsBridge(mCallbackHandler, tab.getWebContents(), null);
                });

        mCallbackHelper.waitForCallback(currentCallCount, 2);
        assertEquals(true, mCookieControlsVisible);
        assertEquals(true, mThirdPartyCookiesBlocked);
        assertEquals(CookieControlsEnforcement.NO_ENFORCEMENT, mEnforcement);

        // Try to set a cookie on the page when cookies are blocked.
        currentCallCount = mCallbackHelper.getCallCount();
        JavaScriptUtils.executeJavaScriptAndWaitForResult(tab.getWebContents(), "setCookie()");
        mCallbackHelper.waitForCallback(currentCallCount, 1);
    }

    @Test
    @SmallTest
    // This test will become obsolete when 3PCD is rolled out.
    @DisableFeatures(ChromeFeatureList.TRACKING_PROTECTION_3PCD)
    public void testCookieBridgeWithIncognitoSettingUserBypass() throws Exception {
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    // Set CookieControlsMode Pref to IncognitoOnly
                    UserPrefs.get(ProfileManager.getLastUsedRegularProfile())
                            .setInteger(
                                    PrefNames.COOKIE_CONTROLS_MODE,
                                    CookieControlsMode.INCOGNITO_ONLY);
                });
        int currentCallCount = mCallbackHelper.getCallCount();

        // Navigate to a normal page
        final String url = mTestServer.getURL("/chrome/test/data/android/cookie.html");
        Tab tab = sActivityTestRule.loadUrlInNewTab(url, false);

        // Create cookie bridge and wait for desired callbacks.
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mCookieControlsBridge =
                            new CookieControlsBridge(mCallbackHandler, tab.getWebContents(), null);
                });

        mCallbackHelper.waitForCallback(currentCallCount, 2);
        assertEquals(false, mCookieControlsVisible);
        assertEquals(false, mThirdPartyCookiesBlocked);
        assertEquals(CookieControlsEnforcement.NO_ENFORCEMENT, mEnforcement);

        // Make new incognito page now
        Tab incognitoTab = sActivityTestRule.loadUrlInNewTab(url, true);
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mCookieControlsBridge =
                            new CookieControlsBridge(
                                    mCallbackHandler,
                                    incognitoTab.getWebContents(),
                                    incognitoTab.getProfile().getOriginalProfile());
                });
        mCallbackHelper.waitForCallback(currentCallCount, 2);
        assertEquals(true, mCookieControlsVisible);
        assertEquals(true, mThirdPartyCookiesBlocked);
        assertEquals(CookieControlsEnforcement.NO_ENFORCEMENT, mEnforcement);
    }
}