| Index: chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelOrderController.java
|
| diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelOrderController.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelOrderController.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..d5ac68f725739164758d70992da39a45f12318f7
|
| --- /dev/null
|
| +++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelOrderController.java
|
| @@ -0,0 +1,146 @@
|
| +// 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 org.chromium.chrome.browser.Tab;
|
| +import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType;
|
| +
|
| +/**
|
| + * This class acts as a controller for determining where tabs should be inserted
|
| + * into a tab strip model. See tab_strip_model_order_controller.cc and
|
| + * tab_strip_model.cc
|
| + */
|
| +public class TabModelOrderController {
|
| +
|
| + private static final int NO_TAB = -1;
|
| + private final TabModelSelector mTabModelSelector;
|
| +
|
| + public TabModelOrderController(TabModelSelector modelSelector) {
|
| + mTabModelSelector = modelSelector;
|
| + }
|
| +
|
| + /**
|
| + * Determine the insertion index of the next tab. If it's not the result of
|
| + * a link being pressed, the provided index will be returned.
|
| + *
|
| + * @param type The launch type of the new tab.
|
| + * @param position The provided position.
|
| + * @return Where to insert the tab.
|
| + */
|
| + public int determineInsertionIndex(TabLaunchType type, int position, Tab newTab) {
|
| + if (linkClicked(type)) {
|
| + position = determineInsertionIndex(type, newTab);
|
| + }
|
| +
|
| + if (willOpenInForeground(type, newTab.isIncognito())) {
|
| + // Forget any existing relationships, we don't want to make things
|
| + // too confusing by having multiple groups active at the same time.
|
| + forgetAllOpeners();
|
| + }
|
| +
|
| + return position;
|
| + }
|
| +
|
| + /**
|
| + * Determine the insertion index of the next tab.
|
| + *
|
| + * @param type The launch type of the new tab.
|
| + * @return Where to insert the tab.
|
| + */
|
| + public int determineInsertionIndex(TabLaunchType type, Tab newTab) {
|
| + TabModel currentModel = mTabModelSelector.getCurrentModel();
|
| + Tab currentTab = TabModelUtils.getCurrentTab(currentModel);
|
| + if (currentTab == null) {
|
| + assert (currentModel.getCount() == 0);
|
| + return 0;
|
| + }
|
| + int currentId = currentTab.getId();
|
| + int currentIndex = TabModelUtils.getTabIndexById(currentModel, currentId);
|
| +
|
| + if (sameModelType(currentModel, newTab)) {
|
| + if (willOpenInForeground(type, newTab.isIncognito())) {
|
| + // If the tab was opened in the foreground, insert it adjacent to
|
| + // the tab that opened that link.
|
| + return currentIndex + 1;
|
| + } else {
|
| + // If the tab was opened in the background, position at the end of
|
| + // it's 'group'.
|
| + int index = getIndexOfLastTabOpenedBy(currentId, currentIndex);
|
| + if (index != NO_TAB) {
|
| + return index + 1;
|
| + } else {
|
| + return currentIndex + 1;
|
| + }
|
| + }
|
| + } else {
|
| + // If the tab is opening in the other model type, just put it at the end.
|
| + return mTabModelSelector.getModel(newTab.isIncognito()).getCount();
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Returns the index of the last tab in the model opened by the specified
|
| + * opener, starting at startIndex. To clarify, the tabs are traversed in the
|
| + * descending order of their position in the model. This means that the tab
|
| + * furthest in the stack with the given opener id will be returned.
|
| + *
|
| + * @param openerId The opener of interest.
|
| + * @param startIndex The start point of the search.
|
| + * @return The last tab if found, NO_TAB otherwise.
|
| + */
|
| + private int getIndexOfLastTabOpenedBy(int openerId, int startIndex) {
|
| + TabModel currentModel = mTabModelSelector.getCurrentModel();
|
| + int count = currentModel.getCount();
|
| + for (int i = count - 1; i >= startIndex; i--) {
|
| + Tab tab = currentModel.getTabAt(i);
|
| + if (tab.getParentId() == openerId && tab.isGroupedWithParent()) {
|
| + return i;
|
| + }
|
| + }
|
| + return NO_TAB;
|
| + }
|
| +
|
| + /**
|
| + * Clear the opener attribute on all tabs in the model.
|
| + */
|
| + void forgetAllOpeners() {
|
| + TabModel currentModel = mTabModelSelector.getCurrentModel();
|
| + int count = currentModel.getCount();
|
| + for (int i = 0; i < count; i++) {
|
| + currentModel.getTabAt(i).setGroupedWithParent(false);
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Determine if a launch type is the result of linked being clicked.
|
| + */
|
| + static boolean linkClicked(TabLaunchType type) {
|
| + return type == TabLaunchType.FROM_LINK ||
|
| + type == TabLaunchType.FROM_LONGPRESS_FOREGROUND ||
|
| + type == TabLaunchType.FROM_LONGPRESS_BACKGROUND;
|
| + }
|
| +
|
| + /**
|
| + * Determine if a launch type will result in the tab being opened in the
|
| + * foreground.
|
| + * @param type The type of opening event.
|
| + * @param isNewTabIncognito True if the new opened tab is incognito.
|
| + * @return True if the tab will be in the foreground
|
| + */
|
| + public boolean willOpenInForeground(TabLaunchType type, boolean isNewTabIncognito) {
|
| + // Restore is handling the active index by itself.
|
| + return (type != TabLaunchType.FROM_LONGPRESS_BACKGROUND &&
|
| + type != TabLaunchType.FROM_RESTORE)
|
| + || (!mTabModelSelector.isIncognitoSelected() && isNewTabIncognito);
|
| + }
|
| +
|
| + /**
|
| + * @return {@code true} If both tabs have the same model type, {@code false} otherwise.
|
| + */
|
| + static boolean sameModelType(TabModel model, Tab tab) {
|
| + return model.isIncognito() == tab.isIncognito();
|
| + }
|
| +
|
| +}
|
|
|