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(); |
+ } |
+ |
+} |