chromium/chrome/android/javatests/src/org/chromium/chrome/browser/TabThemeTest.java

// Copyright 2016 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;

import android.graphics.Color;

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

import org.junit.Assert;
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.util.CallbackHelper;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.CriteriaHelper;
import org.chromium.base.test.util.DisabledTest;
import org.chromium.base.test.util.Feature;
import org.chromium.base.test.util.Restriction;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.browser.ntp.NewTabPage;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabSelectionType;
import org.chromium.chrome.browser.tabmodel.TabModel;
import org.chromium.chrome.browser.theme.ThemeColorProvider.ThemeColorObserver;
import org.chromium.chrome.browser.theme.TopUiThemeColorProvider;
import org.chromium.chrome.browser.ui.native_page.FrozenNativePage;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
import org.chromium.chrome.test.util.NewTabPageTestUtils;
import org.chromium.components.browser_ui.styles.ChromeColors;
import org.chromium.components.embedder_support.util.UrlConstants;
import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.content_public.browser.test.util.JavaScriptUtils;
import org.chromium.net.test.EmbeddedTestServer;
import org.chromium.ui.test.util.UiRestriction;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;

/** Tests related to the Tab's theme color. */
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
public class TabThemeTest {
    @Rule
    public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();

    private static final String TEST_PAGE = "/chrome/test/data/android/simple.html";
    private static final String THEMED_TEST_PAGE =
            "/chrome/test/data/android/theme_color_test.html";

    // The theme_color_test.html page uses a pure red theme color.
    private static final int THEME_COLOR = 0xffff0000;

    /** A WebContentsObserver for watching changes in the theme color. */
    private static class ThemeColorWebContentsObserver implements ThemeColorObserver {
        private CallbackHelper mCallbackHelper;
        private int mColor;

        public ThemeColorWebContentsObserver() {
            mCallbackHelper = new CallbackHelper();
        }

        @Override
        public void onThemeColorChanged(int color, boolean shouldAnimate) {
            mColor = color;
            mCallbackHelper.notifyCalled();
        }

        public CallbackHelper getCallbackHelper() {
            return mCallbackHelper;
        }

        public int getColor() {
            return mColor;
        }
    }

    @Before
    public void setUp() throws InterruptedException {
        mActivityTestRule.startMainActivityOnBlankPage();
    }

    /** AssertEquals two colors as strings so the text output shows their hex value. */
    private void assertColorsEqual(int color1, int color2) {
        Assert.assertEquals(Integer.toHexString(color1), Integer.toHexString(color2));
    }

    private int getThemeColor() throws ExecutionException {
        return ThreadUtils.runOnUiThreadBlocking(
                mActivityTestRule
                                .getActivity()
                                .getRootUiCoordinatorForTesting()
                                .getTopUiThemeColorProvider()
                        ::getThemeColor);
    }

    private static int getDefaultThemeColor(Tab tab) throws ExecutionException {
        return ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    return ChromeColors.getDefaultThemeColor(tab.getContext(), tab.isIncognito());
                });
    }

    /** Test that the toolbar has the correct color set. */
    @Test
    @Feature({"Toolbar-Theme-Color"})
    @MediumTest
    @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
    @DisabledTest(message = "Flaky: https://crbug.com/1355516")
    public void testThemeColorIsCorrect() throws ExecutionException, TimeoutException {
        EmbeddedTestServer testServer =
                EmbeddedTestServer.createAndStartServer(
                        ApplicationProvider.getApplicationContext());

        final Tab tab = mActivityTestRule.getActivity().getActivityTab();

        ThemeColorWebContentsObserver colorObserver = new ThemeColorWebContentsObserver();
        CallbackHelper themeColorHelper = colorObserver.getCallbackHelper();
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mActivityTestRule
                            .getActivity()
                            .getRootUiCoordinatorForTesting()
                            .getTopUiThemeColorProvider()
                            .addThemeColorObserver(colorObserver);
                });

        // Navigate to a themed page.
        int curCallCount = themeColorHelper.getCallCount();
        mActivityTestRule.loadUrl(testServer.getURL(THEMED_TEST_PAGE));
        themeColorHelper.waitForCallback(curCallCount, 1);
        assertColorsEqual(THEME_COLOR, getThemeColor());

        // Setting page theme color to white is forbidden.
        JavaScriptUtils.executeJavaScriptAndWaitForResult(
                mActivityTestRule.getActivity().getActivityTab().getWebContents(),
                "document.querySelector(meta).setAttribute('content', 'white');");
        themeColorHelper.waitForCallback(curCallCount, 1);
        assertColorsEqual(THEME_COLOR, colorObserver.getColor());
        assertColorsEqual(THEME_COLOR, getThemeColor());

        // Navigate to a native page from a themed page.
        mActivityTestRule.loadUrl("chrome://newtab/");
        // WebContents does not set theme color for native pages, so don't wait for the call.
        assertColorsEqual(getDefaultThemeColor(tab), getThemeColor());

        // Navigate to a themed page from a native page.
        curCallCount = themeColorHelper.getCallCount();
        mActivityTestRule.loadUrl(testServer.getURL(THEMED_TEST_PAGE));
        themeColorHelper.waitForCallback(curCallCount, 1);
        assertColorsEqual(THEME_COLOR, colorObserver.getColor());
        assertColorsEqual(THEME_COLOR, getThemeColor());

        // Navigate to a non-native non-themed page.
        curCallCount = themeColorHelper.getCallCount();
        mActivityTestRule.loadUrl(testServer.getURL(TEST_PAGE));
        themeColorHelper.waitForCallback(curCallCount, 1);
        assertColorsEqual(getDefaultThemeColor(tab), colorObserver.getColor());
        assertColorsEqual(getDefaultThemeColor(tab), getThemeColor());

        // Navigate to a themed page from a non-native page.
        curCallCount = themeColorHelper.getCallCount();
        mActivityTestRule.loadUrl(testServer.getURL(THEMED_TEST_PAGE));
        themeColorHelper.waitForCallback(curCallCount, 1);
        assertColorsEqual(THEME_COLOR, colorObserver.getColor());
        assertColorsEqual(THEME_COLOR, getThemeColor());
    }

    @Test
    @Feature({"Toolbar-Theme-Color"})
    @MediumTest
    @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
    public void testOverlayColorOnNativePages() throws ExecutionException, TimeoutException {
        EmbeddedTestServer testServer =
                EmbeddedTestServer.createAndStartServer(
                        ApplicationProvider.getApplicationContext());

        final Tab tab = mActivityTestRule.getActivity().getActivityTab();

        final TopUiThemeColorProvider colorProvider =
                mActivityTestRule
                        .getActivity()
                        .getRootUiCoordinatorForTesting()
                        .getTopUiThemeColorProvider();

        ThemeColorObserver colorObserver =
                (color, animate) -> {
                    Assert.assertNotSame(
                            "Theme color should never be 0 or TRANSPARENT!",
                            Color.TRANSPARENT,
                            colorProvider.getSceneLayerBackground(tab));
                };

        ThreadUtils.runOnUiThreadBlocking(() -> colorProvider.addThemeColorObserver(colorObserver));

        // Load the ntp.
        ThreadUtils.runOnUiThreadBlocking(
                () -> tab.loadUrl(new LoadUrlParams(UrlConstants.NTP_URL)));

        NewTabPageTestUtils.waitForNtpLoaded(tab);

        // Load a second tab so we can freeze the first.
        mActivityTestRule.loadUrlInNewTab(testServer.getURL(THEMED_TEST_PAGE));

        // Freeze the ntp on the first tab.
        ThreadUtils.runOnUiThreadBlocking(() -> tab.freezeNativePage());

        CriteriaHelper.pollUiThread(() -> tab.getNativePage() instanceof FrozenNativePage);

        // Switch back to the native tab.
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    TabModel model =
                            mActivityTestRule
                                    .getActivity()
                                    .getTabModelSelectorSupplier()
                                    .get()
                                    .getCurrentModel();
                    model.setIndex(model.indexOf(tab), TabSelectionType.FROM_USER);
                });

        Assert.assertEquals(
                "The tab shouldn't be frozen!",
                mActivityTestRule
                        .getActivity()
                        .getActivityTabProvider()
                        .get()
                        .getNativePage()
                        .getClass(),
                NewTabPage.class);

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    Assert.assertNotSame(
                            "Theme color should never be 0 or TRANSPARENT!",
                            Color.TRANSPARENT,
                            colorProvider.getSceneLayerBackground(tab));
                });
    }
}