// Copyright 2015 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 android.app.Activity;
import android.content.Intent;
import android.text.TextUtils;
import androidx.annotation.Nullable;
import org.chromium.base.IntentUtils;
import org.chromium.base.SysUtils;
import org.chromium.base.TraceEvent;
import org.chromium.base.metrics.TimingMetric;
import org.chromium.base.supplier.OneshotSupplier;
import org.chromium.base.supplier.Supplier;
import org.chromium.chrome.browser.IntentHandler;
import org.chromium.chrome.browser.ServiceTabLauncher;
import org.chromium.chrome.browser.WarmupManager;
import org.chromium.chrome.browser.app.tab_activity_glue.ReparentingDelegateFactory;
import org.chromium.chrome.browser.app.tab_activity_glue.ReparentingTask;
import org.chromium.chrome.browser.compositor.CompositorViewHolder;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.new_tab_url.DseNewTabUrlManager;
import org.chromium.chrome.browser.prefetch.settings.PreloadPagesSettingsBridge;
import org.chromium.chrome.browser.prefetch.settings.PreloadPagesState;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.profiles.ProfileProvider;
import org.chromium.chrome.browser.tab.RedirectHandlerTabHelper;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabAssociatedApp;
import org.chromium.chrome.browser.tab.TabBuilder;
import org.chromium.chrome.browser.tab.TabCreationState;
import org.chromium.chrome.browser.tab.TabDelegateFactory;
import org.chromium.chrome.browser.tab.TabLaunchType;
import org.chromium.chrome.browser.tab.TabParentIntent;
import org.chromium.chrome.browser.tab.TabResolver;
import org.chromium.chrome.browser.tab.TabState;
import org.chromium.components.embedder_support.util.UrlConstants;
import org.chromium.components.url_formatter.UrlFormatter;
import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.content_public.browser.Visibility;
import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.base.PageTransition;
import org.chromium.ui.base.WindowAndroid;
import org.chromium.url.GURL;
/** This class creates various kinds of new tabs and adds them to the right {@link TabModel}. */
public class ChromeTabCreator extends TabCreator {
private final Activity mActivity;
private final OneshotSupplier<ProfileProvider> mProfileProviderSupplier;
private final boolean mIncognito;
private WindowAndroid mNativeWindow;
private TabModel mTabModel;
private TabModelOrderController mOrderController;
private Supplier<TabDelegateFactory> mTabDelegateFactorySupplier;
private final AsyncTabParamsManager mAsyncTabParamsManager;
private final Supplier<TabModelSelector> mTabModelSelectorSupplier;
private final Supplier<CompositorViewHolder> mCompositorViewHolderSupplier;
@Nullable private final DseNewTabUrlManager mDseNewTabUrlManager;
public ChromeTabCreator(
Activity activity,
WindowAndroid nativeWindow,
Supplier<TabDelegateFactory> tabDelegateFactory,
OneshotSupplier<ProfileProvider> profileProviderSupplier,
boolean incognito,
AsyncTabParamsManager asyncTabParamsManager,
Supplier<TabModelSelector> tabModelSelectorSupplier,
Supplier<CompositorViewHolder> compositorViewHolderSupplier,
@Nullable DseNewTabUrlManager dseNewTabUrlManager) {
mActivity = activity;
mNativeWindow = nativeWindow;
mTabDelegateFactorySupplier = tabDelegateFactory;
mProfileProviderSupplier = profileProviderSupplier;
mIncognito = incognito;
mAsyncTabParamsManager = asyncTabParamsManager;
mTabModelSelectorSupplier = tabModelSelectorSupplier;
mCompositorViewHolderSupplier = compositorViewHolderSupplier;
mDseNewTabUrlManager = dseNewTabUrlManager;
}
/**
* Converts a tabLaunchType to a histogram TabLaunchType key used in
* Android.Tab.CreateNewTabDuration.{TabLaunchType} histogram. These must be kept in sync.
*/
private static String tabLaunchTypeToHistogramKey(@TabLaunchType Integer tabLaunchType) {
switch (tabLaunchType) {
case TabLaunchType.FROM_LINK:
return "Link";
case TabLaunchType.FROM_EXTERNAL_APP:
return "ExternalApp";
case TabLaunchType.FROM_CHROME_UI:
return "ChromeUI";
case TabLaunchType.FROM_RESTORE:
return "Restore";
case TabLaunchType.FROM_LONGPRESS_FOREGROUND:
return "LongressForeground";
case TabLaunchType.FROM_LONGPRESS_BACKGROUND:
return "LongpressBackground";
case TabLaunchType.FROM_REPARENTING:
return "Reparenting";
case TabLaunchType.FROM_LAUNCHER_SHORTCUT:
return "LauncherShortcut";
case TabLaunchType.FROM_SPECULATIVE_BACKGROUND_CREATION:
return "SpeculativeBackgroundCreation";
case TabLaunchType.FROM_BROWSER_ACTIONS:
return "BrowserActions";
case TabLaunchType.FROM_LAUNCH_NEW_INCOGNITO_TAB:
return "NewIncognitoTab";
case TabLaunchType.FROM_STARTUP:
return "Startup";
case TabLaunchType.FROM_TAB_GROUP_UI:
return "TabGroupUI";
case TabLaunchType.FROM_LONGPRESS_BACKGROUND_IN_GROUP:
return "LongpressBackgroundInGroup";
case TabLaunchType.FROM_APP_WIDGET:
return "AppWidget";
case TabLaunchType.FROM_LONGPRESS_INCOGNITO:
return "LongpressIncognito";
case TabLaunchType.FROM_RECENT_TABS:
return "RecentTabs";
case TabLaunchType.FROM_READING_LIST:
return "ReadingList";
case TabLaunchType.FROM_TAB_SWITCHER_UI:
return "TabSwitcherUI";
case TabLaunchType.FROM_RESTORE_TABS_UI:
return "RestoreTabsUI";
case TabLaunchType.FROM_OMNIBOX:
return "Omnibox";
case TabLaunchType.UNSET:
return "Unset";
case TabLaunchType.FROM_SYNC_BACKGROUND:
return "SyncBackground";
case TabLaunchType.FROM_RECENT_TABS_FOREGROUND:
return "RecentTabsForeground";
default:
assert false : "Unexpected serialization of tabLaunchType: " + tabLaunchType;
return "TypeUnknown";
}
}
private Profile getProfile() {
return ProfileProvider.getOrCreateProfile(mProfileProviderSupplier.get(), mIncognito);
}
/**
* Preconnect to the URL and its subresources as the tab is being created.
* @param url URL to be preconnected to.
*/
private void maybePreconnectUrlAndSubResources(GURL url) {
// This is an experimental performance optimization behind a flag that can speed up
// navigation by starting the connection earlier.
if (!ChromeFeatureList.isEnabled(ChromeFeatureList.PRECONNECT_ON_TAB_CREATION)) return;
// We don't want to trigger preconnect for low end devices with low resources.
if (SysUtils.isLowEndDevice()) return;
// Skip preconnecting an empty URL.
if (url.isEmpty()) return;
Profile profile = getProfile();
// Only preconnect if we are allowed to trigger preloading.
if (PreloadPagesSettingsBridge.getState(profile) == PreloadPagesState.NO_PRELOADING) return;
WarmupManager.getInstance().maybePreconnectUrlAndSubResources(profile, url.getScheme());
}
/**
* Creates a new tab and posts to UI.
*
* @param loadUrlParams parameters of the url load.
* @param type Information about how the tab was launched.
* @param parent the parent tab, if present.
* @return The new tab.
*/
@Override
public Tab createNewTab(LoadUrlParams loadUrlParams, @TabLaunchType int type, Tab parent) {
return createNewTab(loadUrlParams, type, parent, null);
}
/**
* Creates a new tab and posts to UI.
*
* @param loadUrlParams parameters of the url load.
* @param type Information about how the tab was launched.
* @param parent the parent tab, if present.
* @param position the requested position (index in the tab model)
* @return The new tab.
*/
@Override
public Tab createNewTab(
LoadUrlParams loadUrlParams, @TabLaunchType int type, Tab parent, int position) {
return createNewTab(loadUrlParams, null, type, parent, position, null);
}
/**
* Creates a new tab and posts to UI.
*
* @param loadUrlParams parameters of the url load.
* @param title The title of the tab if lazily loaded.
* @param type Information about how the tab was launched.
* @param parent the parent tab, if present.
* @param position the requested position (index in the tab model)
* @return The new tab.
*/
@Override
public Tab createNewTab(
LoadUrlParams loadUrlParams,
String title,
@TabLaunchType int type,
Tab parent,
int position) {
return createNewTab(loadUrlParams, title, type, parent, position, null);
}
/**
* Creates a new tab and posts to UI.
*
* @param loadUrlParams parameters of the url load.
* @param type Information about how the tab was launched.
* @param parent the parent tab, if present.
* @param intent the source of the url if it isn't null.
* @return The new tab.
*/
public Tab createNewTab(
LoadUrlParams loadUrlParams, @TabLaunchType int type, Tab parent, Intent intent) {
int position =
(intent == null || !IntentUtils.isTrustedIntentFromSelf(intent))
? TabModel.INVALID_TAB_INDEX
: intent.getIntExtra(
IntentHandler.EXTRA_TAB_INDEX, TabModel.INVALID_TAB_INDEX);
// If parent is in the same tab model, place the new tab next to it.
if (position == TabModel.INVALID_TAB_INDEX) {
int index = mTabModel.indexOf(parent);
if (index != TabModel.INVALID_TAB_INDEX) position = index + 1;
}
return createNewTab(loadUrlParams, null, type, parent, position, intent);
}
/**
* Creates a new tab and posts to UI.
*
* @param loadUrlParams parameters of the url load.
* @param title the title to use for a lazily loaded tab.
* @param type Information about how the tab was launched.
* @param parent the parent tab, if present.
* @param position the requested position (index in the tab model)
* @param intent the source of the url if it isn't null.
* @return The new tab.
*/
private Tab createNewTab(
LoadUrlParams loadUrlParams,
String title,
@TabLaunchType int type,
Tab parent,
int position,
Intent intent) {
// Measure tab creation duration for different launch types to understand tab creation
// performance.
try (TraceEvent te = TraceEvent.scoped("ChromeTabCreator.createNewTab");
TimingMetric unused =
TimingMetric.mediumUptime(
"Android.Tab.CreateNewTabDuration."
+ tabLaunchTypeToHistogramKey(type))) {
int parentId = parent != null ? parent.getId() : Tab.INVALID_TAB_ID;
GURL url = UrlFormatter.fixupUrl(loadUrlParams.getUrl());
if (mDseNewTabUrlManager != null) {
url = mDseNewTabUrlManager.maybeGetOverrideUrl(url);
}
// Sanitize the url.
loadUrlParams.setUrl(url.getValidSpecOrEmpty());
loadUrlParams.setTransitionType(
getTransitionType(type, intent, loadUrlParams.getTransitionType()));
// Preconnect to the URL and its subresources as the tab is being created.
maybePreconnectUrlAndSubResources(url);
// Check if the tab is being created asynchronously.
int assignedTabId = IntentHandler.getTabId(intent);
AsyncTabParams asyncParams = mAsyncTabParamsManager.remove(assignedTabId);
boolean openInForeground = mOrderController.willOpenInForeground(type, mIncognito);
TabDelegateFactory delegateFactory =
parent == null ? createDefaultTabDelegateFactory() : null;
Tab tab;
@TabCreationState int creationState = TabCreationState.LIVE_IN_FOREGROUND;
if (asyncParams != null && asyncParams.getTabToReparent() != null) {
type = TabLaunchType.FROM_REPARENTING;
TabReparentingParams params = (TabReparentingParams) asyncParams;
tab = params.getTabToReparent();
ReparentingTask.from(tab)
.finish(
ReparentingDelegateFactory.createReparentingTaskDelegate(
mCompositorViewHolderSupplier.get(),
mNativeWindow,
createDefaultTabDelegateFactory()),
params.getFinalizeCallback());
} else if (asyncParams != null && asyncParams.getWebContents() != null) {
openInForeground = true;
WebContents webContents = asyncParams.getWebContents();
// A WebContents was passed through the Intent. Create a new Tab to hold it.
Intent parentIntent =
IntentUtils.safeGetParcelableExtra(
intent, IntentHandler.EXTRA_PARENT_INTENT);
parentId =
IntentUtils.safeGetIntExtra(
intent, IntentHandler.EXTRA_PARENT_TAB_ID, parentId);
TabModelSelector selector = mTabModelSelectorSupplier.get();
parent = selector != null ? selector.getTabById(parentId) : null;
assert TabModelUtils.getTabIndexById(mTabModel, assignedTabId)
== TabModel.INVALID_TAB_INDEX;
tab =
TabBuilder.createLiveTab(getProfile(), !openInForeground)
.setId(assignedTabId)
.setParent(parent)
.setWindow(mNativeWindow)
.setLaunchType(type)
.setWebContents(webContents)
.setDelegateFactory(delegateFactory)
.setInitiallyHidden(!openInForeground)
.build();
TabParentIntent.from(tab).set(parentIntent).setCurrentTab(selector::getCurrentTab);
webContents.resumeLoadingCreatedWebContents();
} else if ((!openInForeground && SysUtils.isLowEndDevice())
|| type == TabLaunchType.FROM_SYNC_BACKGROUND) {
// For tab group sync we don't want to trigger a navigation until the user opens the
// tab so use the lazy load mechanism for this.
// On low memory devices the tabs opened in background are not loaded automatically
// to preserve resources (cpu, memory, strong renderer binding) for the foreground
// tab.
tab =
TabBuilder.createForLazyLoad(getProfile(), loadUrlParams, title)
.setParent(parent)
.setWindow(mNativeWindow)
.setLaunchType(type)
.setDelegateFactory(delegateFactory)
.setInitiallyHidden(!openInForeground)
.build();
creationState = TabCreationState.FROZEN_FOR_LAZY_LOAD;
} else if (WarmupManager.getInstance().hasSpareTab(getProfile())) {
// Load URL using spare tab if available. This occurs only if a spare tab has been
// created beforehand. The creation of a spare tab is a costly operation that should
// not be performed without testing. Spare tab is only used for navigations in the
// foreground and for high-end devices.
TraceEvent.end("ChromeTabCreator.loadUrlWithSpareTab");
tab = WarmupManager.getInstance().takeSpareTab(getProfile(), type);
assert tab != null;
// Reparent the tab to its parent, updating the DelegateFactory and NativeWindow.
tab.reparentTab(parent);
ReparentingTask.from(tab)
.finish(
ReparentingDelegateFactory.createReparentingTaskDelegate(
mCompositorViewHolderSupplier.get(),
mNativeWindow,
createDefaultTabDelegateFactory()),
null);
// Set tab to visible before loading the url. This will ensure metrics are recorded
// correctly with spare tab.
if (openInForeground) {
tab.getWebContents().updateWebContentsVisibility(Visibility.VISIBLE);
}
tab.loadUrl(loadUrlParams);
TraceEvent.end("ChromeTabCreator.loadUrlWithSpareTab");
} else {
TraceEvent.begin("ChromeTabCreator.loadUrl");
tab =
TabBuilder.createLiveTab(getProfile(), !openInForeground)
.setParent(parent)
.setWindow(mNativeWindow)
.setLaunchType(type)
.setDelegateFactory(delegateFactory)
.setInitiallyHidden(!openInForeground)
.build();
tab.loadUrl(loadUrlParams);
TraceEvent.end("ChromeTabCreator.loadUrl");
}
// When tab reparenting the |intent| is the reparenting intent, not the intent that
// created the tab.
if (type != TabLaunchType.FROM_REPARENTING) {
RedirectHandlerTabHelper.updateIntentInTab(tab, intent);
}
if (intent != null && intent.hasExtra(ServiceTabLauncher.LAUNCH_REQUEST_ID_EXTRA)) {
ServiceTabLauncher.onWebContentsForRequestAvailable(
intent.getIntExtra(ServiceTabLauncher.LAUNCH_REQUEST_ID_EXTRA, 0),
tab.getWebContents());
}
if (creationState == TabCreationState.LIVE_IN_FOREGROUND && !openInForeground) {
creationState = TabCreationState.LIVE_IN_BACKGROUND;
}
mTabModel.addTab(tab, position, type, creationState);
return tab;
}
}
@Override
public boolean createTabWithWebContents(
@Nullable Tab parent, WebContents webContents, @TabLaunchType int type, GURL url) {
assert webContents != null;
// The parent tab was already closed. Do not open child tabs.
int parentId = parent != null ? parent.getId() : Tab.INVALID_TAB_ID;
if (mTabModel.isClosurePending(parentId)) return false;
// Measure tab creation duration for different launch types to understand tab creation
// performance using an existing WebContents.
try (TraceEvent te = TraceEvent.scoped("ChromeTabCreator.createTabWithWebContents");
TimingMetric unused =
TimingMetric.mediumUptime(
"Android.Tab.CreateNewTabDuration."
+ tabLaunchTypeToHistogramKey(type)
+ ".WithExistingWebContents")) {
// If parent is in the same tab model, place the new tab next to it.
int position = TabModel.INVALID_TAB_INDEX;
int index = TabModelUtils.getTabIndexById(mTabModel, parentId);
if (index != TabModel.INVALID_TAB_INDEX) position = index + 1;
boolean openInForeground = mOrderController.willOpenInForeground(type, mIncognito);
TabDelegateFactory delegateFactory =
parent == null ? createDefaultTabDelegateFactory() : null;
Tab tab;
@TabCreationState int creationState = 0;
if (webContents.getMainFrame() == null
|| !webContents.getMainFrame().isRenderFrameLive()) {
// The webContents may not have a renderer. Treat it as FROZEN_FOR_LAZY_LOAD
// so that the TabStateAttribute forces an immediate write.
tab =
TabBuilder.createLazyTabWithWebContents(getProfile())
.setParent(parent)
.setWindow(mNativeWindow)
.setLaunchType(type)
.setWebContents(webContents)
.setDelegateFactory(delegateFactory)
.setInitiallyHidden(!openInForeground)
.build();
creationState = TabCreationState.FROZEN_FOR_LAZY_LOAD;
} else {
tab =
TabBuilder.createLiveTab(getProfile(), !openInForeground)
.setParent(parent)
.setWindow(mNativeWindow)
.setLaunchType(type)
.setWebContents(webContents)
.setDelegateFactory(delegateFactory)
.setInitiallyHidden(!openInForeground)
.build();
creationState =
openInForeground
? TabCreationState.LIVE_IN_FOREGROUND
: TabCreationState.LIVE_IN_BACKGROUND;
}
mTabModel.addTab(tab, position, type, creationState);
return true;
}
}
@Override
public Tab launchUrl(String url, @TabLaunchType int type) {
return launchUrl(url, type, null, 0);
}
/**
* Creates a new tab and loads the specified URL in it. This is a convenience method for
* {@link #createNewTab} with the default {@link LoadUrlParams} and no parent tab.
*
* @param url the URL to open.
* @param type the type of action that triggered that launch. Determines how the tab is opened
* (for example, in the foreground or background).
* @param intent the source of url if it isn't null.
* @param intentTimestamp the time the intent was received.
* @return the created tab.
*/
public Tab launchUrl(String url, @TabLaunchType int type, Intent intent, long intentTimestamp) {
LoadUrlParams loadUrlParams = new LoadUrlParams(url);
loadUrlParams.setIntentReceivedTimestamp(intentTimestamp);
return createNewTab(loadUrlParams, type, null, intent);
}
/**
* Opens the specified URL into a tab, potentially reusing a tab. Typically if a user opens
* several link from the same application, we reuse the same tab so as to not open too many
* tabs.
*
* @param url the URL to open
* @param appId the ID of the application that triggered that URL navigation.
* @param forceNewTab whether the URL should be opened in a new tab. If false, an existing tab
* already opened by the same app will be reused.
* @param intent the source of url if it isn't null.
* @return the tab the URL was opened in, could be a new tab or a reused one.
*/
// TODO(crbug.com/40691614): Clean up the launches from SearchActivity/Chrome.
public Tab launchUrlFromExternalApp(
LoadUrlParams loadUrlParams, String appId, boolean forceNewTab, Intent intent) {
assert !mIncognito;
// Don't re-use tabs for intents from Chrome. Note that this can be spoofed so shouldn't be
// relied on for anything security sensitive.
boolean isLaunchedFromChrome = TextUtils.equals(appId, mActivity.getPackageName());
if (forceNewTab || isLaunchedFromChrome) {
// We don't associate the tab with that app ID, as it is assumed that if the
// application wanted to open this tab as a new tab, it probably does not want it
// reused either.
// Using FROM_LINK ensures the tab is parented to the current tab, which allows
// the back button to close these tabs and restore selection to the previous
// tab.
@TabLaunchType
int launchType =
isLaunchedFromChrome
? TabLaunchType.FROM_LINK
: TabLaunchType.FROM_EXTERNAL_APP;
return createNewTab(loadUrlParams, launchType, null, intent);
}
if (appId == null) {
// If we have no application ID, we use a made-up one so that these tabs can be
// reused.
appId = TabModelImpl.UNKNOWN_APP_ID;
}
// Let's try to find an existing tab that was started by that app.
for (int i = 0; i < mTabModel.getCount(); i++) {
Tab tab = mTabModel.getTabAt(i);
if (appId.equals(TabAssociatedApp.getAppId(tab))) {
// We don't reuse the tab, we create a new one at the same index instead.
// Reusing a tab would require clearing the navigation history and clearing the
// contents (we would not want the previous content to show).
Tab newTab =
createNewTab(
loadUrlParams,
null,
TabLaunchType.FROM_EXTERNAL_APP,
null,
i,
intent);
TabAssociatedApp.from(newTab).setAppId(appId);
mTabModel.closeTabs(TabClosureParams.closeTab(tab).allowUndo(false).build());
return newTab;
}
}
// No tab for that app, we'll have to create a new one.
Tab tab = createNewTab(loadUrlParams, TabLaunchType.FROM_EXTERNAL_APP, null, intent);
TabAssociatedApp.from(tab).setAppId(appId);
return tab;
}
@Override
public Tab createFrozenTab(TabState state, int id, int index) {
TabModelSelector selector = mTabModelSelectorSupplier.get();
TabResolver resolver =
(tabId) -> {
return selector != null ? selector.getTabById(tabId) : null;
};
boolean selectTab =
mOrderController.willOpenInForeground(TabLaunchType.FROM_RESTORE, mIncognito);
AsyncTabParams asyncParams = mAsyncTabParamsManager.remove(id);
Tab tab = null;
@TabLaunchType int launchType = TabLaunchType.FROM_RESTORE;
@TabCreationState int creationState = TabCreationState.FROZEN_ON_RESTORE;
if (asyncParams != null && asyncParams.getTabToReparent() != null) {
creationState = TabCreationState.LIVE_IN_BACKGROUND;
TabReparentingParams params = (TabReparentingParams) asyncParams;
tab = params.getTabToReparent();
if (tab.isIncognito() != mIncognito) {
throw new IllegalStateException(
"Incognito state mismatch. TabState: "
+ mIncognito
+ ". Tab: "
+ tab.isIncognito());
}
ReparentingTask.from(tab)
.finish(
ReparentingDelegateFactory.createReparentingTaskDelegate(
mCompositorViewHolderSupplier.get(),
mNativeWindow,
createDefaultTabDelegateFactory()),
params.getFinalizeCallback());
// TODO(crbug.com/40141359): Photos/videos viewed in custom tabs aren't displayed
// properly after reparenting. This is a temporary fix for RBS issue crbug.com/1105810,
// investigate and fix the root cause.
if (tab.getUrl().getScheme().equals(UrlConstants.FILE_SCHEME)) {
tab.reloadIgnoringCache();
} else if (tab.needsReload()) {
tab.reload();
}
}
if (tab == null) {
tab =
TabBuilder.createFromFrozenState(getProfile())
.setId(id)
.setTabResolver(resolver)
.setWindow(mNativeWindow)
.setDelegateFactory(createDefaultTabDelegateFactory())
.setInitiallyHidden(!selectTab)
.setTabState(state)
.build();
}
mTabModel.addTab(tab, index, launchType, creationState);
return tab;
}
/**
* @param tabLaunchType Type of the tab launch.
* @param intent The intent causing the tab launch.
* @param originalTransitionType The original transition type.
* @return The page transition type constant.
*/
private int getTransitionType(
@TabLaunchType int tabLaunchType,
Intent intent,
@PageTransition int originalTransitionType) {
int transition = PageTransition.LINK;
switch (tabLaunchType) {
case TabLaunchType.FROM_OMNIBOX:
transition = originalTransitionType;
break;
case TabLaunchType.FROM_RESTORE:
case TabLaunchType.FROM_LINK:
case TabLaunchType.FROM_EXTERNAL_APP:
case TabLaunchType.FROM_BROWSER_ACTIONS:
// FROM_API ensures intent handling isn't used.
transition = PageTransition.LINK | PageTransition.FROM_API;
break;
case TabLaunchType.FROM_CHROME_UI:
case TabLaunchType.FROM_TAB_SWITCHER_UI:
case TabLaunchType.FROM_RESTORE_TABS_UI:
case TabLaunchType.FROM_TAB_GROUP_UI:
case TabLaunchType.FROM_STARTUP:
case TabLaunchType.FROM_LAUNCHER_SHORTCUT:
case TabLaunchType.FROM_LAUNCH_NEW_INCOGNITO_TAB:
case TabLaunchType.FROM_APP_WIDGET:
case TabLaunchType.FROM_READING_LIST:
case TabLaunchType.FROM_SYNC_BACKGROUND:
transition = PageTransition.AUTO_TOPLEVEL;
break;
case TabLaunchType.FROM_LONGPRESS_FOREGROUND:
case TabLaunchType.FROM_LONGPRESS_INCOGNITO:
transition = PageTransition.LINK;
break;
case TabLaunchType.FROM_LONGPRESS_BACKGROUND:
case TabLaunchType.FROM_LONGPRESS_BACKGROUND_IN_GROUP:
case TabLaunchType.FROM_RECENT_TABS:
case TabLaunchType.FROM_RECENT_TABS_FOREGROUND:
// On low end devices tabs are backgrounded in a frozen state, so we set the
// transition type to RELOAD to avoid handling intents when the tab is foregrounded.
// (https://crbug.com/758027)
transition =
SysUtils.isLowEndDevice() ? PageTransition.RELOAD : PageTransition.LINK;
break;
default:
assert false;
break;
}
return IntentHandler.getTransitionTypeFromIntent(intent, transition);
}
/**
* Sets the tab model and tab content manager to use.
* @param model The new {@link TabModel} to use.
* @param orderController The controller for determining the order of tabs.
*/
public void setTabModel(TabModel model, TabModelOrderController orderController) {
mTabModel = model;
mOrderController = orderController;
}
/**
* @return The default tab delegate factory to be used if creating new tabs w/o parents.
*/
public TabDelegateFactory createDefaultTabDelegateFactory() {
return mTabDelegateFactorySupplier != null ? mTabDelegateFactorySupplier.get() : null;
}
}