package org.chromium.chrome.browser.tabmodel;
-import android.os.SystemClock;
-
-import org.chromium.base.CalledByNative;
import org.chromium.base.ObserverList;
import org.chromium.base.TraceEvent;
import org.chromium.chrome.browser.Tab;
-import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.util.MathUtils;
import org.chromium.content_public.browser.WebContents;
/**
* This is the default implementation of the {@link TabModel} interface.
*/
-public abstract class TabModelBase implements TabModel {
+public abstract class TabModelBase extends TabModelJniBridge {
private static final String TAG = "TabModelBase";
- // TODO(dtrainor, simonb): Make these non-static so we don't break if we have multiple instances
- // of chrome running. Also investigate how this affects document mode.
- private static long sTabSwitchStartTime;
- private static TabSelectionType sTabSelectionType;
- private static boolean sTabSwitchLatencyMetricRequired;
- private static boolean sPerceivedTabSwitchLatencyMetricLogged;
-
/**
* The main list of tabs. Note that when this changes, all pending closures must be committed
* via {@link #commitAllTabClosures()} as the indices are no longer valid. Also
*/
private final List<Tab> mTabs = new ArrayList<Tab>();
- private final boolean mIsIncognito;
-
private final TabModelOrderController mOrderController;
protected final TabModelDelegate mModelDelegate;
*/
private int mIndex = INVALID_TAB_INDEX;
- /** Native Tab pointer which will be set by nativeInit(). */
- private long mNativeTabModelImpl = 0;
-
public TabModelBase(boolean incognito, TabModelOrderController orderController,
TabModelDelegate modelDelegate) {
- mIsIncognito = incognito;
- mNativeTabModelImpl = nativeInit(incognito);
+ super(incognito);
+ initializeNative();
mOrderController = orderController;
mModelDelegate = modelDelegate;
mObservers = new ObserverList<TabModelObserver>();
}
@Override
- public Profile getProfile() {
- return nativeGetProfileAndroid(mNativeTabModelImpl);
- }
-
- @Override
- public boolean isIncognito() {
- return mIsIncognito;
- }
-
- @Override
public void destroy() {
for (Tab tab : mTabs) {
if (tab.isInitialized()) tab.destroy();
}
mRewoundList.destroy();
-
- if (mNativeTabModelImpl != 0) {
- nativeDestroy(mNativeTabModelImpl);
- mNativeTabModelImpl = 0;
- }
-
mTabs.clear();
mObservers.clear();
+
+ super.destroy();
}
@Override
for (TabModelObserver obs : mObservers) obs.willAddTab(tab, type);
- boolean selectTab = mOrderController.willOpenInForeground(type, mIsIncognito);
+ boolean selectTab = mOrderController.willOpenInForeground(type, isIncognito());
index = mOrderController.determineInsertionIndex(type, index, tab);
assert index <= mTabs.size();
- assert tab.isIncognito() == mIsIncognito;
+ assert tab.isIncognito() == isIncognito();
// TODO(dtrainor): Update the list of undoable tabs instead of committing it.
commitAllTabClosures();
mModelDelegate.didChange();
mModelDelegate.didCreateNewTab(tab);
- if (mNativeTabModelImpl != 0) nativeTabAddedToModel(mNativeTabModelImpl, tab);
+ tabAddedToModel(tab);
for (TabModelObserver obs : mObservers) obs.didAddTab(tab, type);
if (selectTab) {
- mModelDelegate.selectModel(mIsIncognito);
+ mModelDelegate.selectModel(isIncognito());
setIndex(newIndex, TabModel.TabSelectionType.FROM_NEW);
}
}
@Override
- @CalledByNative
public boolean closeTab(Tab tab) {
return closeTab(tab, true, false, false);
}
private Tab findTabInAllTabModels(int tabId) {
- Tab tab = TabModelUtils.getTabById(mModelDelegate.getModel(mIsIncognito), tabId);
+ Tab tab = TabModelUtils.getTabById(mModelDelegate.getModel(isIncognito()), tabId);
if (tab != null) return tab;
- return TabModelUtils.getTabById(mModelDelegate.getModel(!mIsIncognito), tabId);
+ return TabModelUtils.getTabById(mModelDelegate.getModel(!isIncognito()), tabId);
}
@Override
nextTab = parentTab;
} else if (adjacentTab != null) {
nextTab = adjacentTab;
- } else if (mIsIncognito) {
+ } else if (isIncognito()) {
nextTab = TabModelUtils.getCurrentTab(mModelDelegate.getModel(false));
}
@Override
public boolean supportsPendingClosures() {
- return !mIsIncognito;
+ return !isIncognito();
}
@Override
// If we're the active model call setIndex to actually select this tab, otherwise just set
// mIndex but don't kick off everything that happens when calling setIndex().
if (activeModel) {
- setIndex(insertIndex);
+ TabModelUtils.setIndex(this, insertIndex);
} else {
mIndex = insertIndex;
}
@Override
public boolean closeTab(Tab tabToClose, boolean animate, boolean uponExit, boolean canUndo) {
+ return closeTab(tabToClose, animate, uponExit, canUndo, canUndo);
+ }
+
+ /**
+ * See TabModel.java documentation for description of other parameters.
+ * @param notify Whether or not to notify observers about the pending closure. If this is
+ * {@code true}, {@link #supportsPendingClosures()} is {@code true},
+ * and canUndo is {@code true}, observers will be notified of the pending
+ * closure. Observers will still be notified of a committed/cancelled closure
+ * even if they are not notified of a pending closure to start with.
+ */
+ private boolean closeTab(Tab tabToClose, boolean animate, boolean uponExit,
+ boolean canUndo, boolean notify) {
if (tabToClose == null) {
assert false : "Tab is null!";
return false;
canUndo &= supportsPendingClosures();
- if (canUndo) {
+ if (notify && canUndo) {
for (TabModelObserver obs : mObservers) obs.tabPendingClosure(tabToClose);
}
startTabClosure(tabToClose, animate, uponExit, canUndo);
@Override
public void closeAllTabs() {
+ closeAllTabs(true, false);
+ }
+
+ @Override
+ public void closeAllTabs(boolean allowDelegation, boolean uponExit) {
commitAllTabClosures();
while (getCount() > 0) {
}
}
+ /**
+ * Close all tabs on this model without notifying observers about pending tab closures.
+ *
+ * @param animate true iff the closing animation should be displayed
+ * @param uponExit true iff the tabs are being closed upon application exit (after user presses
+ * the system back button)
+ * @param canUndo Whether or not this action can be undone. If this is {@code true} and
+ * {@link #supportsPendingClosures()} is {@code true}, these {@link Tab}s
+ * will not actually be closed until {@link #commitTabClosure(int)} or
+ * {@link #commitAllTabClosures()} is called, but they will be effectively
+ * removed from this list.
+ * @return a list containing the ids of tabs that have been closed
+ */
+ public ArrayList<Integer> closeAllTabs(boolean animate, boolean uponExit, boolean canUndo) {
+ ArrayList<Integer> closedTabs = new ArrayList<Integer>();
+ while (getCount() > 0) {
+ Tab tab = getTabAt(0);
+ closedTabs.add(tab.getId());
+ closeTab(tab, animate, uponExit, canUndo, false);
+ }
+ return closedTabs;
+ }
+
@Override
- @CalledByNative
public Tab getTabAt(int index) {
// This will catch INVALID_TAB_INDEX and return null
if (index < 0 || index >= mTabs.size()) return null;
}
/**
- * @param incognito
- * @param nativeWebContents
- * @param parentId
- * @return
- */
- @CalledByNative
- protected abstract Tab createTabWithNativeContents(boolean incognito, long nativeWebContents,
- int parentId);
-
- /**
* Performs the necessary actions to remove this {@link Tab} from this {@link TabModel}.
* This does not actually destroy the {@link Tab} (see
* {@link #finalizeTabClosure(Tab)}.
* rewindable closes were undone). If there are no possible rewindable closes this list
* should match {@link #mTabs}.
*/
- private List<Tab> mRewoundTabs = new ArrayList<Tab>();
+ private final List<Tab> mRewoundTabs = new ArrayList<Tab>();
@Override
public boolean isIncognito() {
}
}
- /**
- * Broadcast a notification (in native code) that all tabs are now loaded from storage.
- */
- public void broadcastSessionRestoreComplete() {
- nativeBroadcastSessionRestoreComplete(mNativeTabModelImpl);
+ @Override
+ protected boolean closeTabAt(int index) {
+ return closeTab(getTabAt(index));
}
- // JNI related methods -------------------------------------------------------------------------
-
@Override
- @CalledByNative
public int getCount() {
return mTabs.size();
}
@Override
- @CalledByNative
public int index() {
return mIndex;
}
- @SuppressWarnings("unused")
- @CalledByNative
- private void setIndex(int index) {
- TabModelUtils.setIndex(this, index);
- }
-
- /**
- * Used by Developer Tools to create a new tab with a given URL.
- *
- * @param url The URL to open.
- * @return The new tab.
- */
- @CalledByNative
- protected abstract Tab createNewTabForDevTools(String url);
-
- @CalledByNative
- private boolean isSessionRestoreInProgress() {
+ @Override
+ protected boolean isSessionRestoreInProgress() {
return mModelDelegate.isSessionRestoreInProgress();
}
-
- /**
- * Register the start of tab switch latency timing. Called when setIndex() indicates a tab
- * switch event.
- * @param type The type of action that triggered the tab selection.
- */
- public static void startTabSwitchLatencyTiming(final TabSelectionType type) {
- sTabSwitchStartTime = SystemClock.uptimeMillis();
- sTabSelectionType = type;
- sTabSwitchLatencyMetricRequired = false;
- sPerceivedTabSwitchLatencyMetricLogged = false;
- }
-
- /**
- * Should be called a visible {@link ChromeTab} gets a frame to render in the browser process.
- * If we don't get this call, we ignore requests to
- * {@link #flushActualTabSwitchLatencyMetric()}.
- */
- public static void setActualTabSwitchLatencyMetricRequired() {
- if (sTabSwitchStartTime <= 0) return;
- sTabSwitchLatencyMetricRequired = true;
- }
-
- /**
- * Logs the perceived tab switching latency metric. This will automatically be logged if
- * the actual metric is set and flushed.
- */
- public static void logPerceivedTabSwitchLatencyMetric() {
- if (sTabSwitchStartTime <= 0 || sPerceivedTabSwitchLatencyMetricLogged) return;
-
- flushTabSwitchLatencyMetric(true);
- sPerceivedTabSwitchLatencyMetricLogged = true;
- }
-
- /**
- * Flush the latency metric if called after the indication that a frame is ready.
- */
- public static void flushActualTabSwitchLatencyMetric() {
- if (sTabSwitchStartTime <= 0 || !sTabSwitchLatencyMetricRequired) return;
- logPerceivedTabSwitchLatencyMetric();
- flushTabSwitchLatencyMetric(false);
-
- sTabSwitchStartTime = 0;
- sTabSwitchLatencyMetricRequired = false;
- }
-
- private static void flushTabSwitchLatencyMetric(boolean perceived) {
- if (sTabSwitchStartTime <= 0) return;
- final long ms = SystemClock.uptimeMillis() - sTabSwitchStartTime;
- switch (sTabSelectionType) {
- case FROM_CLOSE:
- nativeLogFromCloseMetric(ms, perceived);
- break;
- case FROM_EXIT:
- nativeLogFromExitMetric(ms, perceived);
- break;
- case FROM_NEW:
- nativeLogFromNewMetric(ms, perceived);
- break;
- case FROM_USER:
- nativeLogFromUserMetric(ms, perceived);
- break;
- }
- }
-
- private native long nativeInit(boolean isIncognito);
- private native void nativeDestroy(long nativeTabModelBase);
- private native void nativeBroadcastSessionRestoreComplete(long nativeTabModelBase);
- private native Profile nativeGetProfileAndroid(long nativeTabModelBase);
- private native void nativeTabAddedToModel(long nativeTabModelBase, Tab tab);
- // Native methods for tab switch latency metrics.
- private static native void nativeLogFromCloseMetric(long ms, boolean perceived);
- private static native void nativeLogFromExitMetric(long ms, boolean perceived);
- private static native void nativeLogFromNewMetric(long ms, boolean perceived);
- private static native void nativeLogFromUserMetric(long ms, boolean perceived);
}