1cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
2cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// found in the LICENSE file.
4cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
5cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)package org.chromium.chrome.browser.tabmodel;
6cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
7cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)import org.chromium.chrome.browser.Tab;
8cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType;
9cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
10cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/**
11cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * This class acts as a controller for determining where tabs should be inserted
12cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * into a tab strip model. See tab_strip_model_order_controller.cc and
13cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * tab_strip_model.cc
14cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */
15cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)public class TabModelOrderController {
16cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
17cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    private static final int NO_TAB = -1;
18cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    private final TabModelSelector mTabModelSelector;
19cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
20cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    public TabModelOrderController(TabModelSelector modelSelector) {
21cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        mTabModelSelector = modelSelector;
22cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    }
23cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
24cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    /**
25cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)     * Determine the insertion index of the next tab. If it's not the result of
26cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)     * a link being pressed, the provided index will be returned.
27cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)     *
28cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)     * @param type The launch type of the new tab.
29cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)     * @param position The provided position.
30cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)     * @return Where to insert the tab.
31cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)     */
32cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    public int determineInsertionIndex(TabLaunchType type, int position, Tab newTab) {
33cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        if (linkClicked(type)) {
34cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)            position = determineInsertionIndex(type, newTab);
35cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        }
36cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
37cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        if (willOpenInForeground(type, newTab.isIncognito())) {
38cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)            // Forget any existing relationships, we don't want to make things
39cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)            // too confusing by having multiple groups active at the same time.
40cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)            forgetAllOpeners();
41cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        }
42cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
43cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        return position;
44cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    }
45cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
46cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    /**
47cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)     * Determine the insertion index of the next tab.
48cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)     *
49cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)     * @param type The launch type of the new tab.
50cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)     * @return Where to insert the tab.
51cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)     */
52cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    public int determineInsertionIndex(TabLaunchType type, Tab newTab) {
53cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        TabModel currentModel = mTabModelSelector.getCurrentModel();
54cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        Tab currentTab = TabModelUtils.getCurrentTab(currentModel);
55cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        if (currentTab == null) {
56cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)            assert (currentModel.getCount() == 0);
57cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)            return 0;
58cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        }
59cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        int currentId = currentTab.getId();
60cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        int currentIndex = TabModelUtils.getTabIndexById(currentModel, currentId);
61cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
62cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        if (sameModelType(currentModel, newTab)) {
63cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)            if (willOpenInForeground(type, newTab.isIncognito())) {
64cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                // If the tab was opened in the foreground, insert it adjacent to
65cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                // the tab that opened that link.
66cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                return currentIndex + 1;
67cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)            } else {
68cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                // If the tab was opened in the background, position at the end of
69cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                // it's 'group'.
70cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                int index = getIndexOfLastTabOpenedBy(currentId, currentIndex);
71cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                if (index != NO_TAB) {
72cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                    return index + 1;
73cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                } else {
74cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                    return currentIndex + 1;
75cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                }
76cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)            }
77cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        } else {
78cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)            // If the tab is opening in the other model type, just put it at the end.
79cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)            return mTabModelSelector.getModel(newTab.isIncognito()).getCount();
80cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        }
81cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    }
82cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
83cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    /**
84cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)     * Returns the index of the last tab in the model opened by the specified
85cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)     * opener, starting at startIndex. To clarify, the tabs are traversed in the
86cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)     * descending order of their position in the model. This means that the tab
87cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)     * furthest in the stack with the given opener id will be returned.
88cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)     *
89cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)     * @param openerId The opener of interest.
90cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)     * @param startIndex The start point of the search.
91cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)     * @return The last tab if found, NO_TAB otherwise.
92cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)     */
93cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    private int getIndexOfLastTabOpenedBy(int openerId, int startIndex) {
94cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        TabModel currentModel = mTabModelSelector.getCurrentModel();
95cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        int count = currentModel.getCount();
96cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        for (int i = count - 1; i >= startIndex; i--) {
97cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)            Tab tab = currentModel.getTabAt(i);
98cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)            if (tab.getParentId() == openerId && tab.isGroupedWithParent()) {
99cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                return i;
100cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)            }
101cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        }
102cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        return NO_TAB;
103cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    }
104cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
105cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    /**
106cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)     * Clear the opener attribute on all tabs in the model.
107cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)     */
108cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    void forgetAllOpeners() {
109cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        TabModel currentModel = mTabModelSelector.getCurrentModel();
110cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        int count = currentModel.getCount();
111cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        for (int i = 0; i < count; i++) {
112cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)            currentModel.getTabAt(i).setGroupedWithParent(false);
113cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        }
114cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    }
115cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
116cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    /**
117cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)     * Determine if a launch type is the result of linked being clicked.
118cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)     */
119cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    static boolean linkClicked(TabLaunchType type) {
120cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        return type == TabLaunchType.FROM_LINK ||
121cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                type == TabLaunchType.FROM_LONGPRESS_FOREGROUND ||
122cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                type == TabLaunchType.FROM_LONGPRESS_BACKGROUND;
123cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    }
124cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
125cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    /**
126cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)     * Determine if a launch type will result in the tab being opened in the
127cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)     * foreground.
128cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)     * @param type               The type of opening event.
129cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)     * @param isNewTabIncognito  True if the new opened tab is incognito.
130cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)     * @return                   True if the tab will be in the foreground
131cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)     */
132cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    public boolean willOpenInForeground(TabLaunchType type, boolean isNewTabIncognito) {
133cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        // Restore is handling the active index by itself.
134f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        if (type == TabLaunchType.FROM_RESTORE) return false;
135f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        return type != TabLaunchType.FROM_LONGPRESS_BACKGROUND
136cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                || (!mTabModelSelector.isIncognitoSelected() && isNewTabIncognito);
137cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    }
138cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
139cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    /**
140cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)     * @return {@code true} If both tabs have the same model type, {@code false} otherwise.
141cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)     */
142cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    static boolean sameModelType(TabModel model, Tab tab) {
143cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        return model.isIncognito() == tab.isIncognito();
144cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    }
145cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
146cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
147