chromium/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/tabmodel/MockTabModel.java

// Copyright 2014 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.test.util.browser.tabmodel;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import org.chromium.base.ObserverList;
import org.chromium.base.supplier.ObservableSupplier;
import org.chromium.base.supplier.ObservableSupplierImpl;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.tab.MockTab;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabCreationState;
import org.chromium.chrome.browser.tab.TabLaunchType;
import org.chromium.chrome.browser.tab.TabSelectionType;
import org.chromium.chrome.browser.tabmodel.EmptyTabModel;
import org.chromium.chrome.browser.tabmodel.IncognitoTabModel;
import org.chromium.chrome.browser.tabmodel.IncognitoTabModelObserver;
import org.chromium.chrome.browser.tabmodel.TabModel;
import org.chromium.chrome.browser.tabmodel.TabModelObserver;
import org.chromium.chrome.browser.tabmodel.TabModelUtils;

import java.util.ArrayList;

/** Almost empty implementation to mock a TabModel. It only handles tab creation and queries. */
public class MockTabModel extends EmptyTabModel implements IncognitoTabModel {
    /**
     * Used to create different kinds of Tabs.  If a MockTabModelDelegate is not provided, regular
     * Tabs are produced.
     */
    public interface MockTabModelDelegate {
        /**
         * Creates a Tab.
         * @param id ID of the Tab.
         * @param incognito Whether the Tab is incognito.
         * @return Tab that is created.
         */
        public MockTab createTab(int id, boolean incognito);
    }

    private int mIndex = TabModel.INVALID_TAB_INDEX;

    private final ObservableSupplierImpl<Tab> mCurrentTabSupplier = new ObservableSupplierImpl<>();
    private final ObservableSupplierImpl<Integer> mTabCountSupplier =
            new ObservableSupplierImpl<>();
    private final ObserverList<TabModelObserver> mObservers = new ObserverList<>();
    private final ArrayList<Tab> mTabs = new ArrayList<Tab>();
    private final Profile mProfile;
    private final MockTabModelDelegate mDelegate;
    private boolean mIsActiveModel;

    public MockTabModel(Profile profile, MockTabModelDelegate delegate) {
        mProfile = profile;
        mDelegate = delegate;
        mTabCountSupplier.set(0);
    }

    public MockTab addTab(int id) {
        MockTab tab =
                mDelegate == null
                        ? new MockTab(id, mProfile)
                        : mDelegate.createTab(id, isIncognito());

        addTab(
                tab,
                TabModel.INVALID_TAB_INDEX,
                TabLaunchType.FROM_CHROME_UI,
                TabCreationState.LIVE_IN_FOREGROUND);
        return tab;
    }

    @Override
    public @NonNull ObservableSupplier<Integer> getTabCountSupplier() {
        return mTabCountSupplier;
    }

    @Override
    public void addTab(
            Tab tab, int index, @TabLaunchType int type, @TabCreationState int creationState) {
        for (TabModelObserver observer : mObservers) observer.willAddTab(tab, type);

        if (index == TabModel.INVALID_TAB_INDEX) {
            mTabs.add(tab);
        } else {
            mTabs.add(index, tab);
            if (index <= mIndex) {
                mIndex++;
            }
        }
        mTabCountSupplier.set(mTabs.size());

        for (TabModelObserver observer : mObservers) {
            observer.didAddTab(tab, type, creationState, false);
        }
    }

    @Override
    public void removeTab(Tab tab) {
        if (mTabs.remove(tab)) {
            mTabCountSupplier.set(mTabs.size());
            for (TabModelObserver observer : mObservers) observer.tabRemoved(tab);
        }
    }

    @Override
    public Profile getProfile() {
        return mProfile;
    }

    @Override
    public boolean isIncognito() {
        return mProfile.isOffTheRecord();
    }

    @Override
    public int getCount() {
        return mTabs.size();
    }

    @Override
    public Tab getTabAt(int position) {
        return mTabs.get(position);
    }

    @Override
    public @Nullable Tab getTabById(int tabId) {
        return mTabs.stream().filter(t -> t.getId() == tabId).findAny().orElse(null);
    }

    @Override
    public int indexOf(Tab tab) {
        return mTabs.indexOf(tab);
    }

    @Override
    public @NonNull ObservableSupplier<Tab> getCurrentTabSupplier() {
        return mCurrentTabSupplier;
    }

    @Override
    public int index() {
        return mIndex;
    }

    @Override
    public void setIndex(int i, @TabSelectionType int type) {
        int lastIndex = mIndex;
        mIndex = i;
        mCurrentTabSupplier.set(TabModelUtils.getCurrentTab(this));
        if (mIndex == TabModel.INVALID_TAB_INDEX) return;

        int lastId = Tab.INVALID_TAB_ID;
        if (lastIndex >= 0 && lastIndex < mTabs.size()) {
            Tab lastTab = getTabAt(lastIndex);
            assert lastTab != null;
            lastId = lastTab.getId();
        }
        for (TabModelObserver observer : mObservers) {
            observer.didSelectTab(mTabs.get(mIndex), type, lastId);
        }
    }

    @Override
    public void addObserver(TabModelObserver observer) {
        mObservers.addObserver(observer);
    }

    @Override
    public void removeObserver(TabModelObserver observer) {
        mObservers.removeObserver(observer);
    }

    @Override
    public void addIncognitoObserver(IncognitoTabModelObserver observer) {}

    @Override
    public void removeIncognitoObserver(IncognitoTabModelObserver observer) {}

    @Override
    public void setActive(boolean active) {
        mIsActiveModel = active;
    }

    @Override
    public boolean isActiveModel() {
        return mIsActiveModel;
    }
}