chromium/chrome/test/android/javatests/src/org/chromium/chrome/browser/tab/TabTestUtils.java

// Copyright 2015 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.tab;

import android.content.Context;
import android.view.View;

import androidx.annotation.Nullable;

import org.mockito.Mockito;

import org.chromium.base.ObserverList;
import org.chromium.base.ObserverList.RewindableIterator;
import org.chromium.base.ThreadUtils;
import org.chromium.base.test.util.JniMocker;
import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.content_public.browser.WebContents;
import org.chromium.content_public.common.ResourceRequestBody;
import org.chromium.url.GURL;

/** Exposes helper functions to be used in tests to instrument tab interaction. */
public class TabTestUtils {
    /**
     * @return The observers registered for the given tab.
     */
    public static ObserverList.RewindableIterator<TabObserver> getTabObservers(Tab tab) {
        return ((TabImpl) tab).getTabObservers();
    }

    /**
     * Initializes {@link Tab} with {@code webContents}. If {@code webContents} is {@code null} a
     * new {@link WebContents} will be created for this {@link Tab}.
     *
     * @see TabImpl#initialize()
     */
    public static void initialize(
            Tab tab,
            Tab parent,
            @Nullable @TabCreationState Integer creationState,
            @Nullable LoadUrlParams loadUrlParams,
            @Nullable String titleForLazyLoad,
            WebContents webContents,
            @Nullable TabDelegateFactory delegateFactory,
            boolean initiallyHidden,
            TabState tabState,
            boolean initializeRenderer) {
        ((TabImpl) tab)
                .initialize(
                        parent,
                        creationState,
                        loadUrlParams,
                        titleForLazyLoad,
                        webContents,
                        delegateFactory,
                        initiallyHidden,
                        tabState,
                        initializeRenderer);
    }

    /** Set the last hidden timestamp. */
    public static void setLastNavigationCommittedTimestampMillis(Tab tab, long ts) {
        ((TabImpl) tab).setLastNavigationCommittedTimestampMillis(ts);
    }

    /** Set a new {@link WebContentsState} to a given tab. */
    public static void setWebContentsState(Tab tab, WebContentsState webContentsState) {
        ((TabImpl) tab).setWebContentsState(webContentsState);
    }

    /**
     * Simulates the first visually non empty paint for the given |tab|.
     * @param tab Tab on which the simulated event will be sent.
     */
    public static void simulateFirstVisuallyNonEmptyPaint(Tab tab) {
        RewindableIterator<TabObserver> observers = ((TabImpl) tab).getTabObservers();
        while (observers.hasNext()) observers.next().didFirstVisuallyNonEmptyPaint(tab);
    }

    /**
     * Simulates page loaded for the given |tab|.
     * @param tab Tab on which the simulated event will be sent.
     */
    public static void simulatePageLoadFinished(Tab tab) {
        RewindableIterator<TabObserver> observers = ((TabImpl) tab).getTabObservers();
        while (observers.hasNext()) observers.next().onPageLoadFinished(tab, tab.getUrl());
    }

    /**
     * Simulates page load failed for the given |tab|.
     * @param tab Tab on which the simulated event will be sent.
     * @param errorCode Errorcode to send to the page.
     */
    public static void simulatePageLoadFailed(Tab tab, int errorCode) {
        RewindableIterator<TabObserver> observers = ((TabImpl) tab).getTabObservers();
        while (observers.hasNext()) observers.next().onPageLoadFailed(tab, errorCode);
    }

    /**
     * Simulates a crash of the given |tab|.
     * @param tab Tab on which the simulated event will be sent.
     * @param sadTabShown Whether the sad tab was shown.
     */
    public static void simulateCrash(Tab tab, boolean sadTabShown) {
        setupSadTab(tab, sadTabShown);
        RewindableIterator<TabObserver> observers = ((TabImpl) tab).getTabObservers();
        while (observers.hasNext()) observers.next().onCrash(tab);
    }

    private static void setupSadTab(Tab tab, boolean show) {
        boolean isShowing = SadTab.isShowing(tab);
        if (!show && isShowing) {
            SadTab.get(tab).removeIfPresent();
        } else if (show && !isShowing) {
            SadTab sadTab =
                    new SadTab(tab) {
                        @Override
                        public View createView(
                                Context context,
                                Runnable suggestionAction,
                                Runnable buttonAction,
                                boolean showSendFeedbackView,
                                boolean isIncognito) {
                            return new View(context);
                        }
                    };
            ThreadUtils.runOnUiThreadBlocking(
                    () -> {
                        SadTab.initForTesting(tab, sadTab);
                        sadTab.show(
                                ((TabImpl) tab).getThemedApplicationContext(), () -> {}, () -> {});
                    });
        }
    }

    /**
     * Simulates a change of theme color for the given |tab|.
     * @param tab Tab on which the simulated event will be sent.
     * @param color Color to send to the tab.
     */
    public static void simulateChangeThemeColor(Tab tab, int color) {
        RewindableIterator<TabObserver> observers = ((TabImpl) tab).getTabObservers();
        while (observers.hasNext()) observers.next().onDidChangeThemeColor(tab, color);
    }

    /**
     * Restore tab's internal states from a given {@link TabState}.
     * @param tab {@link Tab} to restore.
     * @param state {@link TabState} containing the state info to restore the tab with.
     */
    public static void restoreFieldsFromState(Tab tab, TabState state) {
        ((TabImpl) tab).restoreFieldsFromState(state);
    }

    /**
     * Swap {@link WebContents} object being used in a tab.
     * @param tab {@link Tab} object.
     * @param webContents {@link WebContents} to swap in.
     * @param didStartLoad Whether the content started loading.
     * @param didFinishLoad Whether the content finished loading.
     */
    public static void swapWebContents(
            Tab tab, WebContents webContents, boolean didStartLoad, boolean didFinishLoad) {
        ((TabImpl) tab).swapWebContents(webContents, didStartLoad, didFinishLoad);
    }

    /**
     * @param tab {@link Tab} object.
     * @return {@link TabDelegateFactory} for a given tab.
     */
    public static TabDelegateFactory getDelegateFactory(Tab tab) {
        return ((TabImpl) tab).getDelegateFactory();
    }

    /**
     * @param tab {@link Tab} object.
     * @return {@code true} if the current tab is a custom tab.
     */
    public static boolean isCustomTab(Tab tab) {
        return getTabWebContentsDelegate(tab).isCustomTab();
    }

    /**
     * @param tab {@link Tab} object.
     * @return {@link TabWebContentsDelegateAndroid} object for a given tab.
     */
    public static TabWebContentsDelegateAndroidImpl getTabWebContentsDelegate(Tab tab) {
        return ((TabImpl) tab).getTabWebContentsDelegateAndroid();
    }

    /**
     * Open a new tab.
     * @param tab {@link Tab} object.
     * @param url URL to open.
     * @param extraHeaders   Extra headers to apply when requesting the tab's URL.
     * @param postData       Post-data to include in the tab URL's request body.
     * @param disposition         The new tab disposition, defined in
     *                            //ui/base/mojo/window_open_disposition.mojom.
     * @param isRendererInitiated Whether or not the renderer initiated this action.
     */
    public static void openNewTab(
            Tab tab,
            GURL url,
            String extraHeaders,
            ResourceRequestBody postData,
            int disposition,
            boolean isRendererInitiated) {
        getTabWebContentsDelegate(tab)
                .openNewTab(url, extraHeaders, postData, disposition, isRendererInitiated);
    }

    /** Show {@link org.chromium.chrome.browser.infobar.FrameBustBlockInfoBar}. */
    public static void showFramebustBlockInfobarForTesting(Tab tab, String url) {
        getTabWebContentsDelegate(tab).showFramebustBlockInfobarForTesting(url);
    }

    /**
     * Sets whether the tab is showing an error page.  This is reset whenever the tab finishes a
     * navigation.
     * @param tab {@link Tab} object.
     * @param isShowingErrorPage Whether the tab shows an error page.
     */
    public static void setIsShowingErrorPage(Tab tab, boolean isShowingErrorPage) {
        ((TabImpl) tab).setIsShowingErrorPage(isShowingErrorPage);
    }

    /** Mock Tab interface impl JNI for testing. */
    public static void mockTabJni(JniMocker jniMocker) {
        TabImpl.Natives tabImplJni = Mockito.mock(TabImpl.Natives.class);
        jniMocker.mock(TabImplJni.TEST_HOOKS, tabImplJni);
    }
}