chromium/chrome/android/java/src/org/chromium/chrome/browser/tab/TabBuilder.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.tab;

import androidx.annotation.Nullable;

import org.chromium.base.Callback;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.base.WindowAndroid;

/**
 * Builds {@link Tab} using builder pattern. All Tab classes should be instantiated
 * through this builder.
 */
public class TabBuilder {
    private final Profile mProfile;

    private int mId = Tab.INVALID_TAB_ID;
    private Tab mParent;
    private TabResolver mTabResolver;
    private WindowAndroid mWindow;
    private Integer mLaunchType;
    private Integer mCreationType;
    private boolean mFromFrozenState;
    private LoadUrlParams mLoadUrlParams;
    private String mTitle;

    private WebContents mWebContents;
    private TabDelegateFactory mDelegateFactory;
    private boolean mInitiallyHidden;
    private boolean mInitializeRenderer;
    private TabState mTabState;
    private Callback<Tab> mPreInitializeAction;

    public TabBuilder(Profile profile) {
        mProfile = profile;
    }

    /**
     * Sets the id with which the Tab to create should be identified.
     * @param id The id of the Tab.
     * @return {@link TabBuilder} creating the Tab.
     */
    public TabBuilder setId(int id) {
        mId = id;
        return this;
    }

    /**
     * Sets the tab from which the new one is opened.
     * @param parent The parent Tab.
     * @return {@link TabBuilder} creating the Tab.
     */
    public TabBuilder setParent(Tab parent) {
        mParent = parent;
        return this;
    }

    /**
     * Sets the tab resolver (tab id -> {@link Tab} mapping)
     * @param tabResolver the {@link TabResolver}
     * @return {@link TabBuilder} creating the Tab.
     */
    public TabBuilder setTabResolver(TabResolver tabResolver) {
        mTabResolver = tabResolver;
        return this;
    }

    /**
     * Sets window which the Tab will be attached to.
     * @param window An instance of a {@link WindowAndroid}.
     * @return {@link TabBuilder} creating the Tab.
     */
    public TabBuilder setWindow(WindowAndroid window) {
        mWindow = window;
        return this;
    }

    /**
     * Sets a flag indicating how this tab is launched (from a link, external app, etc).
     * @param type Launch type.
     * @return {@link TabBuilder} creating the Tab.
     */
    public TabBuilder setLaunchType(@TabLaunchType int type) {
        mLaunchType = type;
        return this;
    }

    /**
     * Sets a flag indicating to initialize renderer during WebContents creation.
     *
     * @param boolean initializeRenderer to initialize renderer or not.
     *
     * @return {@link TabBuilder} creating the Tab.
     */
    public TabBuilder setInitializeRenderer(boolean initializeRenderer) {
        mInitializeRenderer = initializeRenderer;
        return this;
    }

    /**
     * Sets a {@link WebContents} object to be used on the Tab. If not set, a new one
     * will be created.
     * @param webContents {@link WebContents} object.
     * @return {@link TabBuilder} creating the Tab.
     */
    public TabBuilder setWebContents(WebContents webContents) {
        mWebContents = webContents;
        return this;
    }

    /**
     * Sets a {@link TabDelegateFactory} object.
     * @param delegateFactory The factory delegated to create various Tab-related objects.
     * @return {@link TabBuilder} creating the Tab.
     */
    public TabBuilder setDelegateFactory(TabDelegateFactory delegateFactory) {
        mDelegateFactory = delegateFactory;
        return this;
    }

    /**
     * Sets a pre-initialization action to run.
     * @param action {@link Callback} object to invoke before {@link #initialize()}.
     * @return {@link TabBuilder} creating the Tab.
     */
    public TabBuilder setPreInitializeAction(Callback<Tab> action) {
        mPreInitializeAction = action;
        return this;
    }

    /**
     * Sets a flag indicating whether the Tab should start as hidden. Only used if
     * {@code webContents} is {@code null}.
     * @param initiallyHidden {@code true} if the newly created {@link WebContents} will be hidden.
     * @return {@link TabBuilder} creating the Tab.
     */
    public TabBuilder setInitiallyHidden(boolean initiallyHidden) {
        mInitiallyHidden = initiallyHidden;
        return this;
    }

    /**
     * Sets a {@link TabState} object containing information about this Tab, if it was persisted.
     * @param tabState State object.
     * @return {@link TabBuilder} creating the Tab.
     */
    public TabBuilder setTabState(TabState tabState) {
        mTabState = tabState;
        return this;
    }

    public Tab build() {
        // Pre-condition check
        if (mCreationType != null) {
            if (!mFromFrozenState) {
                assert mCreationType != TabCreationState.FROZEN_ON_RESTORE;
            } else {
                assert mLaunchType == TabLaunchType.FROM_RESTORE
                        && mCreationType == TabCreationState.FROZEN_ON_RESTORE;
            }
        } else {
            if (mFromFrozenState) assert mLaunchType == TabLaunchType.FROM_RESTORE;
        }

        TabImpl tab = new TabImpl(mId, mProfile, mLaunchType);
        Tab parent = null;
        if (mParent != null) {
            parent = mParent;
        } else if (mTabResolver != null) {
            if (mTabState != null) {
                parent = mTabResolver.resolve(mTabState.parentId);
            }
        }
        tab.updateWindowAndroid(mWindow);

        if (parent != null && mDelegateFactory == null) {
            mDelegateFactory = ((TabImpl) parent).getDelegateFactory();
        }

        if (mPreInitializeAction != null) mPreInitializeAction.onResult(tab);

        // Initializes Tab. Its user data objects are also initialized through the event
        // |onInitialized| of TabObserver they register.
        tab.initialize(
                parent,
                mCreationType,
                mLoadUrlParams,
                mTitle,
                mWebContents,
                mDelegateFactory,
                mInitiallyHidden,
                mTabState,
                mInitializeRenderer);
        return tab;
    }

    private TabBuilder setCreationType(@TabCreationState int type) {
        mCreationType = type;
        return this;
    }

    private TabBuilder setFromFrozenState(boolean frozenState) {
        mFromFrozenState = frozenState;
        return this;
    }

    private TabBuilder setLoadUrlParams(LoadUrlParams loadUrlParams) {
        mLoadUrlParams = loadUrlParams;
        return this;
    }

    private TabBuilder setTitle(String title) {
        mTitle = title;
        return this;
    }

    /**
     * Creates a TabBuilder for a new, "frozen" tab from a saved state. This can be used for
     * background tabs restored on cold start that should be loaded when switched to. initialize()
     * needs to be called afterwards to complete the second level initialization.
     *
     * @param profile The Profile associated with the Tab.
     */
    public static TabBuilder createFromFrozenState(Profile profile) {
        return new TabBuilder(profile)
                .setLaunchType(TabLaunchType.FROM_RESTORE)
                .setCreationType(TabCreationState.FROZEN_ON_RESTORE)
                .setFromFrozenState(true);
    }

    /**
     * Creates a TabBuilder for a new tab to be loaded lazily. This can be used for tabs opened in
     * the background that should be loaded when switched to. initialize() needs to be called
     * afterwards to complete the second level initialization.
     *
     * @param profile The Profile associated with the Tab.
     * @param loadUrlParams Params specifying the conditions for loading url.
     * @param title The title to use for the load.
     */
    public static TabBuilder createForLazyLoad(
            Profile profile, LoadUrlParams loadUrlParams, @Nullable String title) {
        return new TabBuilder(profile)
                .setLoadUrlParams(loadUrlParams)
                .setTitle(title)
                .setCreationType(TabCreationState.FROZEN_FOR_LAZY_LOAD);
    }

    /**
     * Creates a TabBuilder for a tab from a web contents with no renderer. initialize() needs to be
     * called afterwards to complete the second level initialization.
     *
     * @param profile The Profile associated with the Tab.
     */
    public static TabBuilder createLazyTabWithWebContents(Profile profile) {
        return new TabBuilder(profile).setCreationType(TabCreationState.FROZEN_FOR_LAZY_LOAD);
    }

    /**
     * Creates a TabBuilder for a fresh tab. initialize() needs to be called afterwards to complete
     * the second level initialization.
     *
     * @param profile The Profile associated with the Tab.
     * @param initiallyHidden true iff the tab being created is initially in background
     */
    public static TabBuilder createLiveTab(Profile profile, boolean initiallyHidden) {
        return new TabBuilder(profile)
                .setCreationType(
                        initiallyHidden
                                ? TabCreationState.LIVE_IN_BACKGROUND
                                : TabCreationState.LIVE_IN_FOREGROUND);
    }
}