| // Copyright (c) 2012 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. |
| |
| #ifndef CHROME_BROWSER_UI_TABS_TAB_STRIP_MODEL_H_ |
| #define CHROME_BROWSER_UI_TABS_TAB_STRIP_MODEL_H_ |
| |
| #include <vector> |
| |
| #include "base/memory/scoped_ptr.h" |
| #include "base/observer_list.h" |
| #include "chrome/browser/ui/tabs/tab_strip_model_observer.h" |
| #include "content/public/common/page_transition_types.h" |
| #include "ui/base/models/list_selection_model.h" |
| |
| class Profile; |
| class TabStripModelDelegate; |
| class TabStripModelOrderController; |
| |
| namespace content { |
| class WebContents; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // TabStripModel |
| // |
| // A model & low level controller of a Browser Window tabstrip. Holds a vector |
| // of WebContentses, and provides an API for adding, removing and |
| // shuffling them, as well as a higher level API for doing specific Browser- |
| // related tasks like adding new Tabs from just a URL, etc. |
| // |
| // Each tab may be any one of the following states: |
| // . Mini-tab. Mini tabs are locked to the left side of the tab strip and |
| // rendered differently (small tabs with only a favicon). The model makes |
| // sure all mini-tabs are at the beginning of the tab strip. For example, |
| // if a non-mini tab is added it is forced to be with non-mini tabs. Requests |
| // to move tabs outside the range of the tab type are ignored. For example, |
| // a request to move a mini-tab after non-mini-tabs is ignored. |
| // You'll notice there is no explicit api for making a tab a mini-tab, rather |
| // there are two tab types that are implicitly mini-tabs: |
| // . App. Corresponds to an extension that wants an app tab. App tabs are |
| // identified by extensions::TabHelper::is_app(). |
| // App tabs are always pinned (you can't unpin them). |
| // . Pinned. Any tab can be pinned. Non-app tabs whose pinned state is changed |
| // are moved to be with other mini-tabs or non-mini tabs. |
| // |
| // A TabStripModel has one delegate that it relies on to perform certain tasks |
| // like creating new TabStripModels (probably hosted in Browser windows) when |
| // required. See TabStripDelegate above for more information. |
| // |
| // A TabStripModel also has N observers (see TabStripModelObserver above), |
| // which can be registered via Add/RemoveObserver. An Observer is notified of |
| // tab creations, removals, moves, and other interesting events. The |
| // TabStrip implements this interface to know when to create new tabs in |
| // the View, and the Browser object likewise implements to be able to update |
| // its bookkeeping when such events happen. |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| class TabStripModel { |
| public: |
| // Used to specify what should happen when the tab is closed. |
| enum CloseTypes { |
| CLOSE_NONE = 0, |
| |
| // Indicates the tab was closed by the user. If true, |
| // WebContents::SetClosedByUserGesture(true) is invoked. |
| CLOSE_USER_GESTURE = 1 << 0, |
| |
| // If true the history is recorded so that the tab can be reopened later. |
| // You almost always want to set this. |
| CLOSE_CREATE_HISTORICAL_TAB = 1 << 1, |
| }; |
| |
| // Constants used when adding tabs. |
| enum AddTabTypes { |
| // Used to indicate nothing special should happen to the newly inserted |
| // tab. |
| ADD_NONE = 0, |
| |
| // The tab should be active. |
| ADD_ACTIVE = 1 << 0, |
| |
| // The tab should be pinned. |
| ADD_PINNED = 1 << 1, |
| |
| // If not set the insertion index of the WebContents is left up to |
| // the Order Controller associated, so the final insertion index may differ |
| // from the specified index. Otherwise the index supplied is used. |
| ADD_FORCE_INDEX = 1 << 2, |
| |
| // If set the newly inserted tab inherits the group of the currently |
| // selected tab. If not set the tab may still inherit the group under |
| // certain situations. |
| ADD_INHERIT_GROUP = 1 << 3, |
| |
| // If set the newly inserted tab's opener is set to the active tab. If not |
| // set the tab may still inherit the group/opener under certain situations. |
| // NOTE: this is ignored if ADD_INHERIT_GROUP is set. |
| ADD_INHERIT_OPENER = 1 << 4, |
| }; |
| |
| // Enumerates different ways to open a new tab. Does not apply to opening |
| // existing links or searches in a new tab, only to brand new empty tabs. |
| enum NewTab { |
| // New tab was opened using the new tab button on the tab strip. |
| NEW_TAB_BUTTON, |
| |
| // New tab was opened using the menu command - either through the keyboard |
| // shortcut, or by opening the menu and selecting the command. Applies to |
| // both Wrench menu and the menu bar's File menu (on platforms that have |
| // one). |
| NEW_TAB_COMMAND, |
| |
| // New tab was opened through the context menu on the tab strip. |
| NEW_TAB_CONTEXT_MENU, |
| |
| // Number of enum entries, used for UMA histogram reporting macros. |
| NEW_TAB_ENUM_COUNT, |
| }; |
| |
| static const int kNoTab = -1; |
| |
| // Construct a TabStripModel with a delegate to help it do certain things |
| // (see the TabStripModelDelegate documentation). |delegate| cannot be NULL. |
| TabStripModel(TabStripModelDelegate* delegate, Profile* profile); |
| virtual ~TabStripModel(); |
| |
| // Retrieves the TabStripModelDelegate associated with this TabStripModel. |
| TabStripModelDelegate* delegate() const { return delegate_; } |
| |
| // Add and remove observers to changes within this TabStripModel. |
| void AddObserver(TabStripModelObserver* observer); |
| void RemoveObserver(TabStripModelObserver* observer); |
| |
| // Retrieve the number of WebContentses/emptiness of the TabStripModel. |
| int count() const { return static_cast<int>(contents_data_.size()); } |
| bool empty() const { return contents_data_.empty(); } |
| |
| // Retrieve the Profile associated with this TabStripModel. |
| Profile* profile() const { return profile_; } |
| |
| // Retrieve the index of the currently active WebContents. |
| int active_index() const { return selection_model_.active(); } |
| |
| // Returns true if the tabstrip is currently closing all open tabs (via a |
| // call to CloseAllTabs). As tabs close, the selection in the tabstrip |
| // changes which notifies observers, which can use this as an optimization to |
| // avoid doing meaningless or unhelpful work. |
| bool closing_all() const { return closing_all_; } |
| |
| // Access the order controller. Exposed only for unit tests. |
| TabStripModelOrderController* order_controller() const { |
| return order_controller_.get(); |
| } |
| |
| // Basic API ///////////////////////////////////////////////////////////////// |
| |
| // Determines if the specified index is contained within the TabStripModel. |
| bool ContainsIndex(int index) const; |
| |
| // Adds the specified WebContents in the default location. Tabs opened |
| // in the foreground inherit the group of the previously active tab. |
| void AppendWebContents(content::WebContents* contents, bool foreground); |
| |
| // Adds the specified WebContents at the specified location. |
| // |add_types| is a bitmask of AddTabTypes; see it for details. |
| // |
| // All append/insert methods end up in this method. |
| // |
| // NOTE: adding a tab using this method does NOT query the order controller, |
| // as such the ADD_FORCE_INDEX AddTabTypes is meaningless here. The only time |
| // the |index| is changed is if using the index would result in breaking the |
| // constraint that all mini-tabs occur before non-mini-tabs. |
| // See also AddWebContents. |
| void InsertWebContentsAt(int index, |
| content::WebContents* contents, |
| int add_types); |
| |
| // Closes the WebContents at the specified index. This causes the |
| // WebContents to be destroyed, but it may not happen immediately. |
| // |close_types| is a bitmask of CloseTypes. Returns true if the |
| // WebContents was closed immediately, false if it was not closed (we |
| // may be waiting for a response from an onunload handler, or waiting for the |
| // user to confirm closure). |
| bool CloseWebContentsAt(int index, uint32 close_types); |
| |
| // Replaces the WebContents at |index| with |new_contents|. The |
| // WebContents that was at |index| is returned and its ownership returns |
| // to the caller. |
| content::WebContents* ReplaceWebContentsAt( |
| int index, |
| content::WebContents* new_contents); |
| |
| // Destroys the WebContents at the specified index, but keeps the tab |
| // visible in the tab strip. Used to free memory in low-memory conditions, |
| // especially on Chrome OS. The tab reloads if the user clicks on it. |
| // Returns the new empty WebContents, used only for testing. |
| content::WebContents* DiscardWebContentsAt(int index); |
| |
| // Detaches the WebContents at the specified index from this strip. The |
| // WebContents is not destroyed, just removed from display. The caller |
| // is responsible for doing something with it (e.g. stuffing it into another |
| // strip). Returns the detached WebContents. |
| content::WebContents* DetachWebContentsAt(int index); |
| |
| // Makes the tab at the specified index the active tab. |user_gesture| is true |
| // if the user actually clicked on the tab or navigated to it using a keyboard |
| // command, false if the tab was activated as a by-product of some other |
| // action. |
| void ActivateTabAt(int index, bool user_gesture); |
| |
| // Adds tab at |index| to the currently selected tabs, without changing the |
| // active tab index. |
| void AddTabAtToSelection(int index); |
| |
| // Move the WebContents at the specified index to another index. This |
| // method does NOT send Detached/Attached notifications, rather it moves the |
| // WebContents inline and sends a Moved notification instead. |
| // If |select_after_move| is false, whatever tab was selected before the move |
| // will still be selected, but its index may have incremented or decremented |
| // one slot. |
| // NOTE: This respects basic ordering constraints and thus does nothing if the |
| // move would result in app tabs and non-app tabs mixing. |
| void MoveWebContentsAt(int index, int to_position, bool select_after_move); |
| |
| // Moves the selected tabs to |index|. |index| is treated as if the tab strip |
| // did not contain any of the selected tabs. For example, if the tabstrip |
| // contains [A b c D E f] (upper case selected) and this is invoked with 1 the |
| // result is [b A D E c f]. |
| // This method maintains that all mini-tabs occur before non-mini-tabs. When |
| // mini-tabs are selected the move is processed in two chunks: first mini-tabs |
| // are moved, then non-mini-tabs are moved. If the index is after |
| // (mini-tab-count - selected-mini-tab-count), then the index the non-mini |
| // selected tabs are moved to is (index + selected-mini-tab-count). For |
| // example, if the model consists of [A b c D E f] (A b c are mini) and this |
| // is invoked with 2, the result is [b c A D E f]. In this example nothing |
| // special happened because the target index was <= (mini-tab-count - |
| // selected-mini-tab-count). If the target index were 3, then the result would |
| // be [b c A f D F]. A, being mini, can move no further than index 2. The |
| // non-mini-tabs are moved to the target index + selected-mini-tab-count (3 + |
| // 1) |
| void MoveSelectedTabsTo(int index); |
| |
| // Returns the currently active WebContents, or NULL if there is none. |
| content::WebContents* GetActiveWebContents() const; |
| |
| // Returns the WebContents at the specified index, or NULL if there is |
| // none. |
| content::WebContents* GetWebContentsAt(int index) const; |
| |
| // Returns the index of the specified WebContents, or TabStripModel::kNoTab |
| // if the WebContents is not in this TabStripModel. |
| int GetIndexOfWebContents(const content::WebContents* contents) const; |
| |
| // Notify any observers that the WebContents at the specified index has |
| // changed in some way. See TabChangeType for details of |change_type|. |
| void UpdateWebContentsStateAt( |
| int index, |
| TabStripModelObserver::TabChangeType change_type); |
| |
| // Close all tabs at once. Code can use closing_all() above to defer |
| // operations that might otherwise by invoked by the flurry of detach/select |
| // notifications this method causes. |
| void CloseAllTabs(); |
| |
| // Returns true if there are any WebContentses that are currently loading. |
| bool TabsAreLoading() const; |
| |
| // Returns the WebContents that opened the WebContents at |index|, or NULL if |
| // there is no opener on record. |
| content::WebContents* GetOpenerOfWebContentsAt(int index); |
| |
| // Changes the |opener| of the WebContents at |index|. |
| // Note: |opener| must be in this tab strip. |
| void SetOpenerOfWebContentsAt(int index, content::WebContents* opener); |
| |
| // Returns the index of the next WebContents in the sequence of WebContentses |
| // spawned by the specified WebContents after |start_index|. If |use_group| is |
| // true, the group property of the tab is used instead of the opener to find |
| // the next tab. Under some circumstances the group relationship may exist but |
| // the opener may not. |
| int GetIndexOfNextWebContentsOpenedBy(const content::WebContents* opener, |
| int start_index, |
| bool use_group) const; |
| |
| // Returns the index of the last WebContents in the model opened by the |
| // specified opener, starting at |start_index|. |
| int GetIndexOfLastWebContentsOpenedBy(const content::WebContents* opener, |
| int start_index) const; |
| |
| // To be called when a navigation is about to occur in the specified |
| // WebContents. Depending on the tab, and the transition type of the |
| // navigation, the TabStripModel may adjust its selection and grouping |
| // behavior. |
| void TabNavigating(content::WebContents* contents, |
| content::PageTransition transition); |
| |
| // Forget all Opener relationships that are stored (but _not_ group |
| // relationships!) This is to reduce unpredictable tab switching behavior |
| // in complex session states. The exact circumstances under which this method |
| // is called are left up to the implementation of the selected |
| // TabStripModelOrderController. |
| void ForgetAllOpeners(); |
| |
| // Forgets the group affiliation of the specified WebContents. This |
| // should be called when a WebContents that is part of a logical group |
| // of tabs is moved to a new logical context by the user (e.g. by typing a new |
| // URL or selecting a bookmark). This also forgets the opener, which is |
| // considered a weaker relationship than group. |
| void ForgetGroup(content::WebContents* contents); |
| |
| // Returns true if the group/opener relationships present for |contents| |
| // should be reset when _any_ selection change occurs in the model. |
| bool ShouldResetGroupOnSelect(content::WebContents* contents) const; |
| |
| // Changes the blocked state of the tab at |index|. |
| void SetTabBlocked(int index, bool blocked); |
| |
| // Changes the pinned state of the tab at |index|. See description above |
| // class for details on this. |
| void SetTabPinned(int index, bool pinned); |
| |
| // Returns true if the tab at |index| is pinned. |
| // See description above class for details on pinned tabs. |
| bool IsTabPinned(int index) const; |
| |
| // Is the tab a mini-tab? |
| // See description above class for details on this. |
| bool IsMiniTab(int index) const; |
| |
| // Is the tab at |index| an app? |
| // See description above class for details on app tabs. |
| bool IsAppTab(int index) const; |
| |
| // Returns true if the tab at |index| is blocked by a tab modal dialog. |
| bool IsTabBlocked(int index) const; |
| |
| // Returns true if the WebContents at |index| has been discarded to |
| // save memory. See DiscardWebContentsAt() for details. |
| bool IsTabDiscarded(int index) const; |
| |
| // Returns the index of the first tab that is not a mini-tab. This returns |
| // |count()| if all of the tabs are mini-tabs, and 0 if none of the tabs are |
| // mini-tabs. |
| int IndexOfFirstNonMiniTab() const; |
| |
| // Returns a valid index for inserting a new tab into this model. |index| is |
| // the proposed index and |mini_tab| is true if inserting a tab will become |
| // mini (pinned or app). If |mini_tab| is true, the returned index is between |
| // 0 and IndexOfFirstNonMiniTab. If |mini_tab| is false, the returned index |
| // is between IndexOfFirstNonMiniTab and count(). |
| int ConstrainInsertionIndex(int index, bool mini_tab); |
| |
| // Extends the selection from the anchor to |index|. |
| void ExtendSelectionTo(int index); |
| |
| // Toggles the selection at |index|. This does nothing if |index| is selected |
| // and there are no other selected tabs. |
| void ToggleSelectionAt(int index); |
| |
| // Makes sure the tabs from the anchor to |index| are selected. This only |
| // adds to the selection. |
| void AddSelectionFromAnchorTo(int index); |
| |
| // Returns true if the tab at |index| is selected. |
| bool IsTabSelected(int index) const; |
| |
| // Sets the selection to match that of |source|. |
| void SetSelectionFromModel(const ui::ListSelectionModel& source); |
| |
| const ui::ListSelectionModel& selection_model() const { |
| return selection_model_; |
| } |
| |
| // Command level API ///////////////////////////////////////////////////////// |
| |
| // Adds a WebContents at the best position in the TabStripModel given |
| // the specified insertion index, transition, etc. |add_types| is a bitmask of |
| // AddTabTypes; see it for details. This method ends up calling into |
| // InsertWebContentsAt to do the actual insertion. Pass kNoTab for |index| to |
| // append the contents to the end of the tab strip. |
| void AddWebContents(content::WebContents* contents, |
| int index, |
| content::PageTransition transition, |
| int add_types); |
| |
| // Closes the selected tabs. |
| void CloseSelectedTabs(); |
| |
| // Select adjacent tabs |
| void SelectNextTab(); |
| void SelectPreviousTab(); |
| |
| // Selects the last tab in the tab strip. |
| void SelectLastTab(); |
| |
| // Swap adjacent tabs. |
| void MoveTabNext(); |
| void MoveTabPrevious(); |
| |
| // View API ////////////////////////////////////////////////////////////////// |
| |
| // Context menu functions. |
| enum ContextMenuCommand { |
| CommandFirst = 0, |
| CommandNewTab, |
| CommandReload, |
| CommandDuplicate, |
| CommandCloseTab, |
| CommandCloseOtherTabs, |
| CommandCloseTabsToRight, |
| CommandRestoreTab, |
| CommandTogglePinned, |
| CommandBookmarkAllTabs, |
| CommandSelectByDomain, |
| CommandSelectByOpener, |
| CommandLast |
| }; |
| |
| // Returns true if the specified command is enabled. If |context_index| is |
| // selected the response applies to all selected tabs. |
| bool IsContextMenuCommandEnabled(int context_index, |
| ContextMenuCommand command_id) const; |
| |
| // Performs the action associated with the specified command for the given |
| // TabStripModel index |context_index|. If |context_index| is selected the |
| // command applies to all selected tabs. |
| void ExecuteContextMenuCommand(int context_index, |
| ContextMenuCommand command_id); |
| |
| // Returns a vector of indices of the tabs that will close when executing the |
| // command |id| for the tab at |index|. The returned indices are sorted in |
| // descending order. |
| std::vector<int> GetIndicesClosedByCommand(int index, |
| ContextMenuCommand id) const; |
| |
| // Returns true if 'CommandTogglePinned' will pin. |index| is the index |
| // supplied to |ExecuteContextMenuCommand|. |
| bool WillContextMenuPin(int index); |
| |
| // Convert a ContextMenuCommand into a browser command. Returns true if a |
| // corresponding browser command exists, false otherwise. |
| static bool ContextMenuCommandToBrowserCommand(int cmd_id, int* browser_cmd); |
| |
| private: |
| class WebContentsData; |
| |
| // Used when making selection notifications. |
| enum NotifyTypes { |
| NOTIFY_DEFAULT, |
| |
| // The selection is changing from a user gesture. |
| NOTIFY_USER_GESTURE, |
| }; |
| |
| // Convenience for converting a vector of indices into a vector of |
| // WebContents. |
| std::vector<content::WebContents*> GetWebContentsFromIndices( |
| const std::vector<int>& indices) const; |
| |
| // Gets the set of tab indices whose domain matches the tab at |index|. |
| void GetIndicesWithSameDomain(int index, std::vector<int>* indices); |
| |
| // Gets the set of tab indices that have the same opener as the tab at |
| // |index|. |
| void GetIndicesWithSameOpener(int index, std::vector<int>* indices); |
| |
| // If |index| is selected all the selected indices are returned, otherwise a |
| // vector with |index| is returned. This is used when executing commands to |
| // determine which indices the command applies to. |
| std::vector<int> GetIndicesForCommand(int index) const; |
| |
| // Returns true if the specified WebContents is a New Tab at the end of |
| // the tabstrip. We check for this because opener relationships are _not_ |
| // forgotten for the New Tab page opened as a result of a New Tab gesture |
| // (e.g. Ctrl+T, etc) since the user may open a tab transiently to look up |
| // something related to their current activity. |
| bool IsNewTabAtEndOfTabStrip(content::WebContents* contents) const; |
| |
| // Closes the WebContentses at the specified indices. This causes the |
| // WebContentses to be destroyed, but it may not happen immediately. If |
| // the page in question has an unload event the WebContents will not be |
| // destroyed until after the event has completed, which will then call back |
| // into this method. |
| // |
| // Returns true if the WebContentses were closed immediately, false if we |
| // are waiting for the result of an onunload handler. |
| bool InternalCloseTabs(const std::vector<int>& indices, |
| uint32 close_types); |
| |
| // Invoked from InternalCloseTabs and when an extension is removed for an app |
| // tab. Notifies observers of TabClosingAt and deletes |contents|. If |
| // |create_historical_tabs| is true, CreateHistoricalTab is invoked on the |
| // delegate. |
| // |
| // The boolean parameter create_historical_tab controls whether to |
| // record these tabs and their history for reopening recently closed |
| // tabs. |
| void InternalCloseTab(content::WebContents* contents, |
| int index, |
| bool create_historical_tabs); |
| |
| // Gets the WebContents at an index. Does no bounds checking. |
| content::WebContents* GetWebContentsAtImpl(int index) const; |
| |
| // Notifies the observers if the active tab is being deactivated. |
| void NotifyIfTabDeactivated(content::WebContents* contents); |
| |
| // Notifies the observers if the active tab has changed. |
| void NotifyIfActiveTabChanged(content::WebContents* old_contents, |
| NotifyTypes notify_types); |
| |
| // Notifies the observers if the active tab or the tab selection has changed. |
| // |old_model| is a snapshot of |selection_model_| before the change. |
| // Note: This function might end up sending 0 to 2 notifications in the |
| // following order: ActiveTabChanged, TabSelectionChanged. |
| void NotifyIfActiveOrSelectionChanged( |
| content::WebContents* old_contents, |
| NotifyTypes notify_types, |
| const ui::ListSelectionModel& old_model); |
| |
| // Sets the selection to |new_model| and notifies any observers. |
| // Note: This function might end up sending 0 to 3 notifications in the |
| // following order: TabDeactivated, ActiveTabChanged, TabSelectionChanged. |
| void SetSelection(const ui::ListSelectionModel& new_model, |
| NotifyTypes notify_types); |
| |
| // Selects either the next tab (|forward| is true), or the previous tab |
| // (|forward| is false). |
| void SelectRelativeTab(bool forward); |
| |
| // Does the work of MoveWebContentsAt. This has no checks to make sure the |
| // position is valid, those are done in MoveWebContentsAt. |
| void MoveWebContentsAtImpl(int index, |
| int to_position, |
| bool select_after_move); |
| |
| // Implementation of MoveSelectedTabsTo. Moves |length| of the selected tabs |
| // starting at |start| to |index|. See MoveSelectedTabsTo for more details. |
| void MoveSelectedTabsToImpl(int index, size_t start, size_t length); |
| |
| // Returns true if the tab represented by the specified data has an opener |
| // that matches the specified one. If |use_group| is true, then this will |
| // fall back to check the group relationship as well. |
| static bool OpenerMatches(const WebContentsData* data, |
| const content::WebContents* opener, |
| bool use_group); |
| |
| // Sets the group/opener of any tabs that reference |tab| to NULL. |
| void ForgetOpenersAndGroupsReferencing(const content::WebContents* tab); |
| |
| // Our delegate. |
| TabStripModelDelegate* delegate_; |
| |
| // The WebContents data currently hosted within this TabStripModel. |
| typedef std::vector<WebContentsData*> WebContentsDataVector; |
| WebContentsDataVector contents_data_; |
| |
| // A profile associated with this TabStripModel. |
| Profile* profile_; |
| |
| // True if all tabs are currently being closed via CloseAllTabs. |
| bool closing_all_; |
| |
| // An object that determines where new Tabs should be inserted and where |
| // selection should move when a Tab is closed. |
| scoped_ptr<TabStripModelOrderController> order_controller_; |
| |
| // Our observers. |
| typedef ObserverList<TabStripModelObserver> TabStripModelObservers; |
| TabStripModelObservers observers_; |
| |
| ui::ListSelectionModel selection_model_; |
| |
| // TODO(sky): remove this; used for debugging 291265. |
| bool in_notify_; |
| |
| DISALLOW_IMPLICIT_CONSTRUCTORS(TabStripModel); |
| }; |
| |
| #endif // CHROME_BROWSER_UI_TABS_TAB_STRIP_MODEL_H_ |