| Index: chrome/android/java_staging/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImpl.java
|
| diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImpl.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImpl.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..4a24a4daf66ccb219963ba63296a48a9c056bdd9
|
| --- /dev/null
|
| +++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImpl.java
|
| @@ -0,0 +1,431 @@
|
| +// Copyright 2015 The Chromium Authors. All rights reserved.
|
| +// 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 android.content.Context;
|
| +import android.os.Handler;
|
| +
|
| +import org.chromium.base.metrics.RecordHistogram;
|
| +import org.chromium.chrome.browser.ChromeActivity;
|
| +import org.chromium.chrome.browser.Tab;
|
| +import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior;
|
| +import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
|
| +import org.chromium.chrome.browser.ntp.NativePageFactory;
|
| +import org.chromium.chrome.browser.tabmodel.OffTheRecordTabModel.OffTheRecordTabModelDelegate;
|
| +import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType;
|
| +import org.chromium.chrome.browser.tabmodel.TabModel.TabSelectionType;
|
| +import org.chromium.chrome.browser.tabmodel.TabPersistentStore.TabPersistentStoreObserver;
|
| +import org.chromium.content_public.browser.LoadUrlParams;
|
| +import org.chromium.ui.base.WindowAndroid;
|
| +
|
| +import java.util.concurrent.atomic.AtomicBoolean;
|
| +
|
| +/**
|
| + * This class manages all the ContentViews in the app. As it manipulates views, it must be
|
| + * instantiated and used in the UI Thread. It acts as a TabModel which delegates all
|
| + * TabModel methods to the active model that it contains.
|
| + */
|
| +public class TabModelSelectorImpl extends TabModelSelectorBase implements TabModelDelegate {
|
| +
|
| + private final ChromeActivity mActivity;
|
| +
|
| + /** Flag set to false when the asynchronous loading of tabs is finished. */
|
| + private final AtomicBoolean mSessionRestoreInProgress =
|
| + new AtomicBoolean(true);
|
| + private final TabPersistentStore mTabSaver;
|
| +
|
| + // This flag signifies the object has gotten an onNativeReady callback and
|
| + // has not been destroyed.
|
| + private boolean mActiveState = false;
|
| +
|
| + private final TabModelOrderController mOrderController;
|
| +
|
| + private OverviewModeBehavior mOverviewModeBehavior;
|
| +
|
| + private TabContentManager mTabContentManager;
|
| +
|
| + private Tab mVisibleTab;
|
| +
|
| + private final TabModelSelectorUma mUma;
|
| +
|
| + private CloseAllTabsDelegate mCloseAllTabsDelegate;
|
| +
|
| + private static class TabModelImplCreator implements OffTheRecordTabModelDelegate {
|
| + private final ChromeActivity mActivity;
|
| + private final TabModelSelectorUma mUma;
|
| + private final TabModelOrderController mOrderController;
|
| + private final TabContentManager mTabContentManager;
|
| + private final TabPersistentStore mTabSaver;
|
| + private final TabModelDelegate mModelDelegate;
|
| +
|
| + /**
|
| + * Constructor for an Incognito TabModelImpl.
|
| + *
|
| + * @param activity The activity owning this TabModel.
|
| + * @param uma Handles UMA tracking for the model.
|
| + * @param orderController Determines the order for inserting new Tabs.
|
| + * @param tabContentManager Manages the display content of the tab.
|
| + * @param tabSaver Handler for saving tabs.
|
| + * @param modelDelegate Delegate to handle external dependencies and interactions.
|
| + */
|
| + public TabModelImplCreator(ChromeActivity activity, TabModelSelectorUma uma,
|
| + TabModelOrderController orderController, TabContentManager tabContentManager,
|
| + TabPersistentStore tabSaver, TabModelDelegate modelDelegate) {
|
| + mActivity = activity;
|
| + mUma = uma;
|
| + mOrderController = orderController;
|
| + mTabContentManager = tabContentManager;
|
| + mTabSaver = tabSaver;
|
| + mModelDelegate = modelDelegate;
|
| + }
|
| +
|
| + @Override
|
| + public TabModel createTabModel() {
|
| + return new TabModelImpl(true, mActivity, mUma, mOrderController,
|
| + mTabContentManager, mTabSaver, mModelDelegate);
|
| + }
|
| +
|
| + @Override
|
| + public boolean doOffTheRecordTabsExist() {
|
| + return TabWindowManager.getInstance().getIncognitoTabCount() > 0;
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Builds a {@link TabModelSelectorImpl} instance.
|
| + * @param activity The {@link ChromeActivity} this model selector lives in.
|
| + * @param selectorIndex The index this selector represents in the list of selectors.
|
| + * @param windowAndroid The {@link WindowAndroid} associated with this model selector.
|
| + */
|
| + public TabModelSelectorImpl(ChromeActivity activity, int selectorIndex,
|
| + WindowAndroid windowAndroid) {
|
| + super();
|
| + mActivity = activity;
|
| + mUma = new TabModelSelectorUma(mActivity);
|
| + final TabPersistentStoreObserver persistentStoreObserver =
|
| + new TabPersistentStoreObserver() {
|
| + @Override
|
| + public void onStateLoaded(Context context) {
|
| + markTabStateInitialized();
|
| + }
|
| +
|
| + @Override
|
| + public void onInitialized(int tabCountAtStartup) {
|
| + RecordHistogram.recordCountHistogram("Tabs.CountAtStartup", tabCountAtStartup);
|
| + }
|
| + };
|
| + mTabSaver = new TabPersistentStore(this, selectorIndex, mActivity, mActivity,
|
| + persistentStoreObserver);
|
| + mOrderController = new TabModelOrderController(this);
|
| + ChromeTabCreator regularTabCreator = new ChromeTabCreator(mActivity, windowAndroid,
|
| + mOrderController, mTabSaver, false);
|
| + ChromeTabCreator incognitoTabCreator = new ChromeTabCreator(mActivity, windowAndroid,
|
| + mOrderController, mTabSaver, true);
|
| + mActivity.setTabCreators(regularTabCreator, incognitoTabCreator);
|
| + }
|
| +
|
| + @Override
|
| + protected void markTabStateInitialized() {
|
| + super.markTabStateInitialized();
|
| + if (!mSessionRestoreInProgress.getAndSet(false)) return;
|
| +
|
| + // This is the first time we set
|
| + // |mSessionRestoreInProgress|, so we need to broadcast.
|
| + TabModelImpl model = (TabModelImpl) getModel(false);
|
| +
|
| + if (model != null) {
|
| + model.broadcastSessionRestoreComplete();
|
| + } else {
|
| + assert false : "Normal tab model is null after tab state loaded.";
|
| + }
|
| + }
|
| +
|
| + private void handleOnPageLoadStopped(Tab tab) {
|
| + if (tab != null) mTabSaver.addTabToSaveQueue(tab);
|
| + }
|
| +
|
| + /**
|
| + *
|
| + * @param overviewModeBehavior The {@link OverviewModeBehavior} that should be used to determine
|
| + * when the app is in overview mode or not.
|
| + */
|
| + public void setOverviewModeBehavior(OverviewModeBehavior overviewModeBehavior) {
|
| + assert overviewModeBehavior != null;
|
| + mOverviewModeBehavior = overviewModeBehavior;
|
| + }
|
| +
|
| + /**
|
| + * Should be called when the app starts showing a view with multiple tabs.
|
| + */
|
| + public void onTabsViewShown() {
|
| + mUma.onTabsViewShown();
|
| + }
|
| +
|
| + /**
|
| + * 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.
|
| + */
|
| + public void onNativeLibraryReady(TabContentManager tabContentProvider) {
|
| + assert !mActiveState : "onNativeLibraryReady called twice!";
|
| + mTabContentManager = tabContentProvider;
|
| +
|
| + TabModel normalModel = new TabModelImpl(false, mActivity, mUma, mOrderController,
|
| + mTabContentManager, mTabSaver, this);
|
| + TabModel incognitoModel = new OffTheRecordTabModel(new TabModelImplCreator(
|
| + mActivity, mUma, mOrderController, mTabContentManager, mTabSaver, this));
|
| + initialize(isIncognitoSelected(), normalModel, incognitoModel);
|
| + mActivity.getTabCreator(false).setTabModel(normalModel, mTabContentManager);
|
| + mActivity.getTabCreator(true).setTabModel(incognitoModel, mTabContentManager);
|
| +
|
| + mTabSaver.setTabContentManager(tabContentProvider);
|
| +
|
| + addObserver(new EmptyTabModelSelectorObserver() {
|
| + @Override
|
| + public void onNewTabCreated(Tab tab) {
|
| + // Only invalidate if the tab exists in the currently selected model.
|
| + if (TabModelUtils.getTabById(getCurrentModel(), tab.getId()) != null) {
|
| + mTabContentManager.invalidateIfChanged(tab.getId(), tab.getUrl());
|
| + }
|
| + }
|
| + });
|
| +
|
| + mActiveState = true;
|
| +
|
| + new TabModelSelectorTabObserver(this) {
|
| + @Override
|
| + public void onUrlUpdated(Tab tab) {
|
| + TabModel model = getModelForTabId(tab.getId());
|
| + if (model == getCurrentModel()) {
|
| + mTabContentManager.invalidateIfChanged(tab.getId(), tab.getUrl());
|
| + }
|
| + }
|
| +
|
| + @Override
|
| + public void onLoadStopped(Tab tab) {
|
| + handleOnPageLoadStopped(tab);
|
| + }
|
| +
|
| + @Override
|
| + public void onPageLoadStarted(Tab tab) {
|
| + String url = tab.getUrl();
|
| + if (NativePageFactory.isNativePageUrl(url, tab.isIncognito())) {
|
| + mTabContentManager.invalidateTabThumbnail(tab.getId(), url);
|
| + } else {
|
| + mTabContentManager.removeTabThumbnail(tab.getId());
|
| + }
|
| + }
|
| +
|
| + @Override
|
| + public void onPageLoadFinished(Tab tab) {
|
| + mUma.onPageLoadFinished(tab.getId());
|
| + }
|
| +
|
| + @Override
|
| + public void onPageLoadFailed(Tab tab, int errorCode) {
|
| + mUma.onPageLoadFailed(tab.getId());
|
| + }
|
| +
|
| + @Override
|
| + public void onCrash(Tab tab, boolean sadTabShown) {
|
| + if (sadTabShown) {
|
| + mTabContentManager.removeTabThumbnail(tab.getId());
|
| + }
|
| + mUma.onTabCrashed(tab.getId());
|
| + }
|
| + };
|
| + }
|
| +
|
| + @Override
|
| + public void setCloseAllTabsDelegate(CloseAllTabsDelegate delegate) {
|
| + mCloseAllTabsDelegate = delegate;
|
| + }
|
| +
|
| + @Override
|
| + public TabModel getModelAt(int index) {
|
| + return mActiveState ? super.getModelAt(index) : EmptyTabModel.getInstance();
|
| + }
|
| +
|
| + @Override
|
| + public void selectModel(boolean incognito) {
|
| + TabModel oldModel = getCurrentModel();
|
| + super.selectModel(incognito);
|
| + TabModel newModel = getCurrentModel();
|
| + if (oldModel != newModel) {
|
| + TabModelUtils.setIndex(newModel, newModel.index());
|
| +
|
| + // Make the call to notifyDataSetChanged() after any delayed events
|
| + // have had a chance to fire. Otherwise, this may result in some
|
| + // drawing to occur before animations have a chance to work.
|
| + new Handler().post(new Runnable() {
|
| + @Override
|
| + public void run() {
|
| + notifyChanged();
|
| + }
|
| + });
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Commits all pending tab closures for all {@link TabModel}s in this {@link TabModelSelector}.
|
| + */
|
| + @Override
|
| + public void commitAllTabClosures() {
|
| + for (int i = 0; i < getModels().size(); i++) {
|
| + getModelAt(i).commitAllTabClosures();
|
| + }
|
| + }
|
| +
|
| + @Override
|
| + public boolean closeAllTabsRequest(boolean incognito) {
|
| + return mCloseAllTabsDelegate.closeAllTabsRequest(incognito);
|
| + }
|
| +
|
| + public void saveState() {
|
| + commitAllTabClosures();
|
| + mTabSaver.saveState();
|
| + }
|
| +
|
| + /**
|
| + * Load the saved tab state. This should be called before any new tabs are created. The saved
|
| + * tabs shall not be restored until {@link #restoreTabs} is called.
|
| + */
|
| + public void loadState() {
|
| + int nextId = mTabSaver.loadState();
|
| + if (nextId >= 0) Tab.incrementIdCounterTo(nextId);
|
| + }
|
| +
|
| + /**
|
| + * Restore the saved tabs which were loaded by {@link #loadState}.
|
| + *
|
| + * @param setActiveTab If true, synchronously load saved active tab and set it as the current
|
| + * active tab.
|
| + */
|
| + public void restoreTabs(boolean setActiveTab) {
|
| + mTabSaver.restoreTabs(setActiveTab);
|
| + }
|
| +
|
| + /**
|
| + * If there is an asynchronous session restore in-progress, try to synchronously restore
|
| + * the state of a tab with the given url as a frozen tab. This method has no effect if
|
| + * there isn't a tab being restored with this url, or the tab has already been restored.
|
| + *
|
| + * @return true if there exists a tab with the url.
|
| + */
|
| + public boolean tryToRestoreTabStateForUrl(String url) {
|
| + if (!isSessionRestoreInProgress()) return false;
|
| + return mTabSaver.restoreTabStateForUrl(url);
|
| + }
|
| +
|
| + /**
|
| + * If there is an asynchronous session restore in-progress, try to synchronously restore
|
| + * the state of a tab with the given id as a frozen tab. This method has no effect if
|
| + * there isn't a tab being restored with this id, or the tab has already been restored.
|
| + *
|
| + * @return true if there exists a tab with the id.
|
| + */
|
| + public boolean tryToRestoreTabStateForId(int id) {
|
| + if (!isSessionRestoreInProgress()) return false;
|
| + return mTabSaver.restoreTabStateForId(id);
|
| + }
|
| +
|
| + public void clearState() {
|
| + mTabSaver.clearState();
|
| + }
|
| +
|
| + public void clearEncryptedState() {
|
| + mTabSaver.clearEncryptedState();
|
| + }
|
| +
|
| + @Override
|
| + public void destroy() {
|
| + mTabSaver.destroy();
|
| + mUma.destroy();
|
| + super.destroy();
|
| + mActiveState = false;
|
| + }
|
| +
|
| + @Override
|
| + public Tab openNewTab(LoadUrlParams loadUrlParams, TabLaunchType type, Tab parent,
|
| + boolean incognito) {
|
| + return mActivity.getTabCreator(incognito).createNewTab(loadUrlParams, type, parent);
|
| + }
|
| +
|
| + /**
|
| + * @return Number of restored tabs on cold startup.
|
| + */
|
| + public int getRestoredTabCount() {
|
| + return mTabSaver.getRestoredTabCount();
|
| + }
|
| +
|
| + @Override
|
| + public void requestToShowTab(Tab tab, TabSelectionType type) {
|
| + boolean isFromExternalApp = tab != null
|
| + && tab.getLaunchType() == TabLaunchType.FROM_EXTERNAL_APP;
|
| +
|
| + if (mVisibleTab != tab && tab != null && !tab.isNativePage()) {
|
| + TabModelBase.startTabSwitchLatencyTiming(type);
|
| + }
|
| + if (mVisibleTab != null && mVisibleTab != tab && !mVisibleTab.needsReload()) {
|
| + // TODO(dtrainor): Once we figure out why we can't grab a snapshot from the current
|
| + // tab when we have other tabs loading from external apps remove the checks for
|
| + // FROM_EXTERNAL_APP/FROM_NEW.
|
| + if (!mVisibleTab.isClosing()
|
| + && (!isFromExternalApp || type != TabSelectionType.FROM_NEW)) {
|
| + cacheTabBitmap(mVisibleTab);
|
| + }
|
| + mVisibleTab.hide();
|
| + mVisibleTab.setFullscreenManager(null);
|
| + mTabSaver.addTabToSaveQueue(mVisibleTab);
|
| + mVisibleTab = null;
|
| + }
|
| +
|
| + if (tab == null) {
|
| + notifyChanged();
|
| + return;
|
| + }
|
| +
|
| + // We hit this case when the user enters tab switcher and comes back to the current tab
|
| + // without actual tab switch.
|
| + if (mVisibleTab == tab && !mVisibleTab.isHidden()) {
|
| + // The current tab might have been killed by the os while in tab switcher.
|
| + tab.loadIfNeeded();
|
| + return;
|
| + }
|
| +
|
| + tab.setFullscreenManager(mActivity.getFullscreenManager());
|
| + mVisibleTab = tab;
|
| +
|
| + // Don't execute the tab display part if Chrome has just been sent to background. This
|
| + // avoids uneccessary work (tab restore) and prevents pollution of tab display metrics - see
|
| + // http://crbug.com/316166.
|
| + if (type != TabSelectionType.FROM_EXIT) {
|
| + tab.show(type);
|
| + mUma.onShowTab(tab.getId(), tab.isBeingRestored());
|
| + }
|
| + }
|
| +
|
| + private void cacheTabBitmap(Tab tabToCache) {
|
| + // Trigger a capture of this tab.
|
| + if (tabToCache == null) return;
|
| + mTabContentManager.cacheTabThumbnail(tabToCache);
|
| + }
|
| +
|
| + @Override
|
| + public boolean isInOverviewMode() {
|
| + return mOverviewModeBehavior != null && mOverviewModeBehavior.overviewVisible();
|
| + }
|
| +
|
| + @Override
|
| + public boolean isSessionRestoreInProgress() {
|
| + return mSessionRestoreInProgress.get();
|
| + }
|
| +
|
| + // TODO(tedchoc): Remove the need for this to be exposed.
|
| + @Override
|
| + public void notifyChanged() {
|
| + super.notifyChanged();
|
| + }
|
| +}
|
|
|