chromium/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ArchivedTabModelSelectorImpl.java

// Copyright 2024 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.tabmodel;

import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;

import org.chromium.chrome.browser.flags.ActivityType;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabSelectionType;
import org.chromium.chrome.browser.tab_ui.TabContentManager;
import org.chromium.chrome.browser.tabmodel.NextTabPolicy.NextTabPolicySupplier;
import org.chromium.ui.base.WindowAndroid;
import org.chromium.url.GURL;

import java.util.concurrent.atomic.AtomicBoolean;

/** {@link TabModelSelector} for archived tabs. Must be instantiated and used on the UI thread. */
public class ArchivedTabModelSelectorImpl extends TabModelSelectorBase implements TabModelDelegate {
    /** Flag set to false when the asynchronous loading of tabs is finished. */
    private final AtomicBoolean mSessionRestoreCompleted = new AtomicBoolean(true);

    private final Profile mProfile;
    private final TabModelOrderController mOrderController;
    private final NextTabPolicySupplier mNextTabPolicySupplier;
    private final AsyncTabParamsManager mAsyncTabParamsManager;

    private TabContentManager mTabContentManager;

    /**
     * Builds a {@link ArchivedTabModelSelectorImpl} instance.
     *
     * @param profile The {@link Profile} used.
     * @param tabCreatorManager A {@link TabCreatorManager} instance.
     * @param tabModelFilterFactory The factory for setting up {@link TabModelFilter}s.
     * @param nextTabPolicySupplier A policy for next tab selection.
     * @param asyncTabParamsManager Manager of async params for reparenting.
     */
    public ArchivedTabModelSelectorImpl(
            Profile profile,
            TabCreatorManager tabCreatorManager,
            TabModelFilterFactory tabModelFilterFactory,
            NextTabPolicySupplier nextTabPolicySupplier,
            AsyncTabParamsManager asyncTabParamsManager) {
        super(tabCreatorManager, tabModelFilterFactory, /* startIncognito= */ false);
        mProfile = profile;
        mOrderController = new TabModelOrderControllerImpl(this);
        mNextTabPolicySupplier = nextTabPolicySupplier;
        mAsyncTabParamsManager = asyncTabParamsManager;
    }

    @Override
    public void markTabStateInitialized() {
        super.markTabStateInitialized();
        if (!mSessionRestoreCompleted.getAndSet(false)) return;

        // This is the first time we set
        // |mSessionRestoreCompleted|, so we need to broadcast.
        TabModelImpl model = (TabModelImpl) getModel(false);
        model.broadcastSessionRestoreComplete();
    }

    /**
     * Should be called once the native library is loaded so that the actual internals of this class
     * can be initialized.
     *
     * @param tabContentProvider A {@link TabContentManager} instance.
     */
    @Override
    public void onNativeLibraryReady(TabContentManager tabContentProvider) {
        assert mTabContentManager == null : "onNativeLibraryReady called twice!";

        TabCreator tabCreator = getTabCreatorManager().getTabCreator(false);
        // TODO(crbug.com/331688951): Consider using a custom TabModel.
        TabModelImpl normalModel =
                new TabModelImpl(
                        mProfile,
                        ActivityType.TABBED,
                        tabCreator,
                        /* incognitoTabCreator= */ null,
                        mOrderController,
                        tabContentProvider,
                        mNextTabPolicySupplier,
                        mAsyncTabParamsManager,
                        this,
                        /* supportUndo= */ true,
                        /* isArchivedTabModel= */ true) {
                    @Override
                    public int index() {
                        // Intentional noop.
                        return INVALID_TAB_INDEX;
                    }

                    @Override
                    public void setIndex(int i, final @TabSelectionType int type) {
                        // Intentional noop.
                    }

                    @Override
                    public Tab getNextTabIfClosed(int id, boolean uponExit) {
                        return null;
                    }
                };
        ((ArchivedTabCreator) tabCreator).setTabModel(normalModel);

        onNativeLibraryReadyInternal(
                tabContentProvider,
                normalModel,
                EmptyTabModel.getInstance(/* isIncognito= */ true));
    }

    @VisibleForTesting
    void onNativeLibraryReadyInternal(
            TabContentManager tabContentProvider,
            TabModel normalModel,
            IncognitoTabModel incognitoModel) {
        mTabContentManager = tabContentProvider;
        initialize(normalModel, incognitoModel);

        new TabModelSelectorTabObserver(this) {
            @Override
            public void onPageLoadStarted(Tab tab, GURL url) {
                assert false : "Tabs in the archived tab model should not be navigated.";
            }

            @Override
            public void onActivityAttachmentChanged(Tab tab, @Nullable WindowAndroid window) {
                if (window == null && !isReparentingInProgress()) {
                    getModel(tab.isIncognito()).removeTab(tab);
                }
            }
        };
    }

    @Override
    public void openMostRecentlyClosedEntry(TabModel tabModel) {
        assert false : "Not reached.";
    }

    /**
     * Exposed to allow tests to initialize the selector with different tab models.
     *
     * @param normalModel The normal tab model.
     * @param incognitoModel The incognito tab model.
     */
    public void initializeForTesting(TabModel normalModel, IncognitoTabModel incognitoModel) {
        initialize(normalModel, incognitoModel);
    }

    @Override
    public void selectModel(boolean incognito) {
        assert !incognito : "The archived tab model selector has no incognito mode.";
        assert !getCurrentModel().isIncognito()
                : "The incognito model of the archived tab model selector was selected.";
        // Intentional no-op.
    }

    @Override
    public void requestToShowTab(Tab tab, @TabSelectionType int type) {
        // Intentional noop.
    }

    private void cacheTabBitmap(Tab tabToCache) {
        // Intentional noop.
    }

    @Override
    public boolean isSessionRestoreInProgress() {
        return mSessionRestoreCompleted.get();
    }
}