// Copyright 2014 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.os.SystemClock; import org.chromium.base.annotations.CalledByNative; import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tabmodel.TabCreatorManager.TabCreator; import org.chromium.content_public.browser.LoadUrlParams; import org.chromium.content_public.browser.WebContents; /** * Bridges between the C++ and Java {@link TabModel} interfaces. */ public abstract class TabModelJniBridge implements TabModel { private final boolean mIsIncognito; // 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; /** Native TabModelJniBridge pointer, which will be set by {@link #initializeNative()}. */ private long mNativeTabModelJniBridge; public TabModelJniBridge(boolean isIncognito) { mIsIncognito = isIncognito; } /** Initializes the native-side counterpart to this class. */ protected void initializeNative() { assert mNativeTabModelJniBridge == 0; mNativeTabModelJniBridge = nativeInit(mIsIncognito); } /** @return Whether the native-side pointer has been initialized. */ public boolean isNativeInitialized() { return mNativeTabModelJniBridge != 0; } @Override public void destroy() { if (isNativeInitialized()) { // This will invalidate all other native references to this object in child classes. nativeDestroy(mNativeTabModelJniBridge); mNativeTabModelJniBridge = 0; } } @Override public boolean isIncognito() { return mIsIncognito; } @Override public Profile getProfile() { return nativeGetProfileAndroid(mNativeTabModelJniBridge); } /** Broadcast a native-side notification that all tabs are now loaded from storage. */ public void broadcastSessionRestoreComplete() { assert isNativeInitialized(); nativeBroadcastSessionRestoreComplete(mNativeTabModelJniBridge); } /** * Called by subclasses when a Tab is added to the TabModel. * @param tab Tab being added to the model. */ protected void tabAddedToModel(Tab tab) { if (isNativeInitialized()) nativeTabAddedToModel(mNativeTabModelJniBridge, tab); } /** * Sets the TabModel's index. * @param index Index of the Tab to select. */ @CalledByNative private void setIndex(int index) { TabModelUtils.setIndex(this, index); } @Override @CalledByNative public abstract Tab getTabAt(int index); /** * Closes the Tab at a particular index. * @param index Index of the tab to close. * @return Whether the was successfully closed. */ @CalledByNative protected abstract boolean closeTabAt(int index); /** * Returns a tab creator for this tab model. * @param incognito Whether to return an incognito TabCreator. */ protected abstract TabCreator getTabCreator(boolean incognito); /** * Creates a Tab with the given WebContents. * @param parent The parent tab that creates the new tab. * @param incognito Whether or not the tab is incognito. * @param webContents A {@link WebContents} object. * @param parentId ID of the parent. * @return Whether or not the Tab was successfully created. */ @CalledByNative protected abstract boolean createTabWithWebContents(Tab parent, boolean incognito, WebContents webContents, int parentId); /** * Creates a Tab with the given WebContents for DevTools. * @param url URL to show. */ @CalledByNative protected Tab createNewTabForDevTools(String url) { return getTabCreator(false).createNewTab(new LoadUrlParams(url), TabModel.TabLaunchType.FROM_CHROME_UI, null); } @Override @CalledByNative public abstract int getCount(); @Override @CalledByNative public abstract int index(); /** @return Whether or not a sync session is currently being restored. */ @CalledByNative protected abstract boolean 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 Tab} 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 Profile nativeGetProfileAndroid(long nativeTabModelJniBridge); private native void nativeBroadcastSessionRestoreComplete(long nativeTabModelJniBridge); private native void nativeDestroy(long nativeTabModelJniBridge); private native void nativeTabAddedToModel(long nativeTabModelJniBridge, 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); }