1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #ifndef CHROME_BROWSER_UI_TABS_TAB_STRIP_MODEL_H_
6 #define CHROME_BROWSER_UI_TABS_TAB_STRIP_MODEL_H_
10 #include "base/memory/scoped_ptr.h"
11 #include "base/observer_list.h"
12 #include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
13 #include "ui/base/models/list_selection_model.h"
14 #include "ui/base/page_transition_types.h"
17 class TabStripModelDelegate
;
18 class TabStripModelOrderController
;
24 ////////////////////////////////////////////////////////////////////////////////
28 // A model & low level controller of a Browser Window tabstrip. Holds a vector
29 // of WebContentses, and provides an API for adding, removing and
30 // shuffling them, as well as a higher level API for doing specific Browser-
31 // related tasks like adding new Tabs from just a URL, etc.
33 // Each tab may be pinned. Pinned tabs are locked to the left side of the tab
34 // strip and rendered differently (small tabs with only a favicon). The model
35 // makes sure all pinned tabs are at the beginning of the tab strip. For
36 // example, if a non-pinned tab is added it is forced to be with non-pinned
37 // tabs. Requests to move tabs outside the range of the tab type are ignored.
38 // For example, a request to move a pinned tab after non-pinned tabs is ignored.
40 // A TabStripModel has one delegate that it relies on to perform certain tasks
41 // like creating new TabStripModels (probably hosted in Browser windows) when
42 // required. See TabStripDelegate above for more information.
44 // A TabStripModel also has N observers (see TabStripModelObserver above),
45 // which can be registered via Add/RemoveObserver. An Observer is notified of
46 // tab creations, removals, moves, and other interesting events. The
47 // TabStrip implements this interface to know when to create new tabs in
48 // the View, and the Browser object likewise implements to be able to update
49 // its bookkeeping when such events happen.
51 ////////////////////////////////////////////////////////////////////////////////
54 // Used to specify what should happen when the tab is closed.
58 // Indicates the tab was closed by the user. If true,
59 // WebContents::SetClosedByUserGesture(true) is invoked.
60 CLOSE_USER_GESTURE
= 1 << 0,
62 // If true the history is recorded so that the tab can be reopened later.
63 // You almost always want to set this.
64 CLOSE_CREATE_HISTORICAL_TAB
= 1 << 1,
67 // Constants used when adding tabs.
69 // Used to indicate nothing special should happen to the newly inserted
73 // The tab should be active.
76 // The tab should be pinned.
79 // If not set the insertion index of the WebContents is left up to
80 // the Order Controller associated, so the final insertion index may differ
81 // from the specified index. Otherwise the index supplied is used.
82 ADD_FORCE_INDEX
= 1 << 2,
84 // If set the newly inserted tab inherits the group of the currently
85 // selected tab. If not set the tab may still inherit the group under
86 // certain situations.
87 ADD_INHERIT_GROUP
= 1 << 3,
89 // If set the newly inserted tab's opener is set to the active tab. If not
90 // set the tab may still inherit the group/opener under certain situations.
91 // NOTE: this is ignored if ADD_INHERIT_GROUP is set.
92 ADD_INHERIT_OPENER
= 1 << 4,
95 // Enumerates different ways to open a new tab. Does not apply to opening
96 // existing links or searches in a new tab, only to brand new empty tabs.
98 // New tab was opened using the new tab button on the tab strip.
101 // New tab was opened using the menu command - either through the keyboard
102 // shortcut, or by opening the menu and selecting the command. Applies to
103 // both Wrench menu and the menu bar's File menu (on platforms that have
107 // New tab was opened through the context menu on the tab strip.
108 NEW_TAB_CONTEXT_MENU
,
110 // Number of enum entries, used for UMA histogram reporting macros.
114 static const int kNoTab
= -1;
116 // Construct a TabStripModel with a delegate to help it do certain things
117 // (see the TabStripModelDelegate documentation). |delegate| cannot be NULL.
118 TabStripModel(TabStripModelDelegate
* delegate
, Profile
* profile
);
119 virtual ~TabStripModel();
121 // Retrieves the TabStripModelDelegate associated with this TabStripModel.
122 TabStripModelDelegate
* delegate() const { return delegate_
; }
124 // Add and remove observers to changes within this TabStripModel.
125 void AddObserver(TabStripModelObserver
* observer
);
126 void RemoveObserver(TabStripModelObserver
* observer
);
128 // Retrieve the number of WebContentses/emptiness of the TabStripModel.
129 int count() const { return static_cast<int>(contents_data_
.size()); }
130 bool empty() const { return contents_data_
.empty(); }
132 // Retrieve the Profile associated with this TabStripModel.
133 Profile
* profile() const { return profile_
; }
135 // Retrieve the index of the currently active WebContents.
136 int active_index() const { return selection_model_
.active(); }
138 // Returns true if the tabstrip is currently closing all open tabs (via a
139 // call to CloseAllTabs). As tabs close, the selection in the tabstrip
140 // changes which notifies observers, which can use this as an optimization to
141 // avoid doing meaningless or unhelpful work.
142 bool closing_all() const { return closing_all_
; }
144 // Access the order controller. Exposed only for unit tests.
145 TabStripModelOrderController
* order_controller() const {
146 return order_controller_
.get();
149 // Basic API /////////////////////////////////////////////////////////////////
151 // Determines if the specified index is contained within the TabStripModel.
152 bool ContainsIndex(int index
) const;
154 // Adds the specified WebContents in the default location. Tabs opened
155 // in the foreground inherit the group of the previously active tab.
156 void AppendWebContents(content::WebContents
* contents
, bool foreground
);
158 // Adds the specified WebContents at the specified location.
159 // |add_types| is a bitmask of AddTabTypes; see it for details.
161 // All append/insert methods end up in this method.
163 // NOTE: adding a tab using this method does NOT query the order controller,
164 // as such the ADD_FORCE_INDEX AddTabTypes is meaningless here. The only time
165 // the |index| is changed is if using the index would result in breaking the
166 // constraint that all pinned tabs occur before non-pinned tabs.
167 // See also AddWebContents.
168 void InsertWebContentsAt(int index
,
169 content::WebContents
* contents
,
172 // Closes the WebContents at the specified index. This causes the
173 // WebContents to be destroyed, but it may not happen immediately.
174 // |close_types| is a bitmask of CloseTypes. Returns true if the
175 // WebContents was closed immediately, false if it was not closed (we
176 // may be waiting for a response from an onunload handler, or waiting for the
177 // user to confirm closure).
178 bool CloseWebContentsAt(int index
, uint32 close_types
);
180 // Replaces the WebContents at |index| with |new_contents|. The
181 // WebContents that was at |index| is returned and its ownership returns
183 content::WebContents
* ReplaceWebContentsAt(
185 content::WebContents
* new_contents
);
187 // Destroys the WebContents at the specified index, but keeps the tab
188 // visible in the tab strip. Used to free memory in low-memory conditions,
189 // especially on Chrome OS. The tab reloads if the user clicks on it.
190 // Returns the new empty WebContents, used only for testing.
191 content::WebContents
* DiscardWebContentsAt(int index
);
193 // Detaches the WebContents at the specified index from this strip. The
194 // WebContents is not destroyed, just removed from display. The caller
195 // is responsible for doing something with it (e.g. stuffing it into another
196 // strip). Returns the detached WebContents.
197 content::WebContents
* DetachWebContentsAt(int index
);
199 // Makes the tab at the specified index the active tab. |user_gesture| is true
200 // if the user actually clicked on the tab or navigated to it using a keyboard
201 // command, false if the tab was activated as a by-product of some other
203 void ActivateTabAt(int index
, bool user_gesture
);
205 // Adds tab at |index| to the currently selected tabs, without changing the
207 void AddTabAtToSelection(int index
);
209 // Move the WebContents at the specified index to another index. This
210 // method does NOT send Detached/Attached notifications, rather it moves the
211 // WebContents inline and sends a Moved notification instead.
212 // If |select_after_move| is false, whatever tab was selected before the move
213 // will still be selected, but its index may have incremented or decremented
215 void MoveWebContentsAt(int index
, int to_position
, bool select_after_move
);
217 // Moves the selected tabs to |index|. |index| is treated as if the tab strip
218 // did not contain any of the selected tabs. For example, if the tabstrip
219 // contains [A b c D E f] (upper case selected) and this is invoked with 1 the
220 // result is [b A D E c f].
221 // This method maintains that all pinned tabs occur before non-pinned tabs.
222 // When pinned tabs are selected the move is processed in two chunks: first
223 // pinned tabs are moved, then non-pinned tabs are moved. If the index is
224 // after (pinned-tab-count - selected-pinned-tab-count), then the index the
225 // non-pinned selected tabs are moved to is (index +
226 // selected-pinned-tab-count). For example, if the model consists of
227 // [A b c D E f] (A b c are pinned) and this is invoked with 2, the result is
228 // [b c A D E f]. In this example nothing special happened because the target
229 // index was <= (pinned-tab-count - selected-pinned-tab-count). If the target
230 // index were 3, then the result would be [b c A f D F]. A, being pinned, can
231 // move no further than index 2. The non-pinned tabs are moved to the target
232 // index + selected-pinned tab-count (3 + 1).
233 void MoveSelectedTabsTo(int index
);
235 // Returns the currently active WebContents, or NULL if there is none.
236 content::WebContents
* GetActiveWebContents() const;
238 // Returns the WebContents at the specified index, or NULL if there is
240 content::WebContents
* GetWebContentsAt(int index
) const;
242 // Returns the index of the specified WebContents, or TabStripModel::kNoTab
243 // if the WebContents is not in this TabStripModel.
244 int GetIndexOfWebContents(const content::WebContents
* contents
) const;
246 // Notify any observers that the WebContents at the specified index has
247 // changed in some way. See TabChangeType for details of |change_type|.
248 void UpdateWebContentsStateAt(
250 TabStripModelObserver::TabChangeType change_type
);
252 // Close all tabs at once. Code can use closing_all() above to defer
253 // operations that might otherwise by invoked by the flurry of detach/select
254 // notifications this method causes.
257 // Returns true if there are any WebContentses that are currently loading.
258 bool TabsAreLoading() const;
260 // Returns the WebContents that opened the WebContents at |index|, or NULL if
261 // there is no opener on record.
262 content::WebContents
* GetOpenerOfWebContentsAt(int index
);
264 // Changes the |opener| of the WebContents at |index|.
265 // Note: |opener| must be in this tab strip.
266 void SetOpenerOfWebContentsAt(int index
, content::WebContents
* opener
);
268 // Returns the index of the next WebContents in the sequence of WebContentses
269 // spawned by the specified WebContents after |start_index|. If |use_group| is
270 // true, the group property of the tab is used instead of the opener to find
271 // the next tab. Under some circumstances the group relationship may exist but
272 // the opener may not.
273 int GetIndexOfNextWebContentsOpenedBy(const content::WebContents
* opener
,
275 bool use_group
) const;
277 // Returns the index of the last WebContents in the model opened by the
278 // specified opener, starting at |start_index|.
279 int GetIndexOfLastWebContentsOpenedBy(const content::WebContents
* opener
,
280 int start_index
) const;
282 // To be called when a navigation is about to occur in the specified
283 // WebContents. Depending on the tab, and the transition type of the
284 // navigation, the TabStripModel may adjust its selection and grouping
286 void TabNavigating(content::WebContents
* contents
,
287 ui::PageTransition transition
);
289 // Forget all Opener relationships that are stored (but _not_ group
290 // relationships!) This is to reduce unpredictable tab switching behavior
291 // in complex session states. The exact circumstances under which this method
292 // is called are left up to the implementation of the selected
293 // TabStripModelOrderController.
294 void ForgetAllOpeners();
296 // Forgets the group affiliation of the specified WebContents. This
297 // should be called when a WebContents that is part of a logical group
298 // of tabs is moved to a new logical context by the user (e.g. by typing a new
299 // URL or selecting a bookmark). This also forgets the opener, which is
300 // considered a weaker relationship than group.
301 void ForgetGroup(content::WebContents
* contents
);
303 // Returns true if the group/opener relationships present for |contents|
304 // should be reset when _any_ selection change occurs in the model.
305 bool ShouldResetGroupOnSelect(content::WebContents
* contents
) const;
307 // Changes the blocked state of the tab at |index|.
308 void SetTabBlocked(int index
, bool blocked
);
310 // Changes the pinned state of the tab at |index|. See description above
311 // class for details on this.
312 void SetTabPinned(int index
, bool pinned
);
314 // Returns true if the tab at |index| is pinned.
315 // See description above class for details on pinned tabs.
316 bool IsTabPinned(int index
) const;
318 // Returns true if the tab at |index| is blocked by a tab modal dialog.
319 bool IsTabBlocked(int index
) const;
321 // Returns true if the WebContents at |index| has been discarded to
322 // save memory. See DiscardWebContentsAt() for details.
323 bool IsTabDiscarded(int index
) const;
325 // Returns the index of the first tab that is not a pinned tab. This returns
326 // |count()| if all of the tabs are pinned tabs, and 0 if none of the tabs are
328 int IndexOfFirstNonPinnedTab() const;
330 // Returns a valid index for inserting a new tab into this model. |index| is
331 // the proposed index and |pinned_tab| is true if inserting a tab will become
332 // pinned (pinned). If |pinned_tab| is true, the returned index is between 0
333 // and IndexOfFirstNonPinnedTab. If |pinned_tab| is false, the returned index
334 // is between IndexOfFirstNonPinnedTab and count().
335 int ConstrainInsertionIndex(int index
, bool pinned_tab
);
337 // Extends the selection from the anchor to |index|.
338 void ExtendSelectionTo(int index
);
340 // Toggles the selection at |index|. This does nothing if |index| is selected
341 // and there are no other selected tabs.
342 void ToggleSelectionAt(int index
);
344 // Makes sure the tabs from the anchor to |index| are selected. This only
345 // adds to the selection.
346 void AddSelectionFromAnchorTo(int index
);
348 // Returns true if the tab at |index| is selected.
349 bool IsTabSelected(int index
) const;
351 // Sets the selection to match that of |source|.
352 void SetSelectionFromModel(const ui::ListSelectionModel
& source
);
354 const ui::ListSelectionModel
& selection_model() const {
355 return selection_model_
;
358 // Command level API /////////////////////////////////////////////////////////
360 // Adds a WebContents at the best position in the TabStripModel given
361 // the specified insertion index, transition, etc. |add_types| is a bitmask of
362 // AddTabTypes; see it for details. This method ends up calling into
363 // InsertWebContentsAt to do the actual insertion. Pass kNoTab for |index| to
364 // append the contents to the end of the tab strip.
365 void AddWebContents(content::WebContents
* contents
,
367 ui::PageTransition transition
,
370 // Closes the selected tabs.
371 void CloseSelectedTabs();
373 // Select adjacent tabs
374 void SelectNextTab();
375 void SelectPreviousTab();
377 // Selects the last tab in the tab strip.
378 void SelectLastTab();
380 // Swap adjacent tabs.
382 void MoveTabPrevious();
384 // View API //////////////////////////////////////////////////////////////////
386 // Context menu functions.
387 enum ContextMenuCommand
{
393 CommandCloseOtherTabs
,
394 CommandCloseTabsToRight
,
397 CommandToggleTabAudioMuted
,
398 CommandBookmarkAllTabs
,
399 CommandSelectByDomain
,
400 CommandSelectByOpener
,
404 // Returns true if the specified command is enabled. If |context_index| is
405 // selected the response applies to all selected tabs.
406 bool IsContextMenuCommandEnabled(int context_index
,
407 ContextMenuCommand command_id
) const;
409 // Performs the action associated with the specified command for the given
410 // TabStripModel index |context_index|. If |context_index| is selected the
411 // command applies to all selected tabs.
412 void ExecuteContextMenuCommand(int context_index
,
413 ContextMenuCommand command_id
);
415 // Returns a vector of indices of the tabs that will close when executing the
416 // command |id| for the tab at |index|. The returned indices are sorted in
418 std::vector
<int> GetIndicesClosedByCommand(int index
,
419 ContextMenuCommand id
) const;
421 // Returns true if 'CommandTogglePinned' will pin. |index| is the index
422 // supplied to |ExecuteContextMenuCommand|.
423 bool WillContextMenuPin(int index
);
425 // Convert a ContextMenuCommand into a browser command. Returns true if a
426 // corresponding browser command exists, false otherwise.
427 static bool ContextMenuCommandToBrowserCommand(int cmd_id
, int* browser_cmd
);
430 class WebContentsData
;
432 // Used when making selection notifications.
436 // The selection is changing from a user gesture.
440 // Convenience for converting a vector of indices into a vector of
442 std::vector
<content::WebContents
*> GetWebContentsFromIndices(
443 const std::vector
<int>& indices
) const;
445 // Gets the set of tab indices whose domain matches the tab at |index|.
446 void GetIndicesWithSameDomain(int index
, std::vector
<int>* indices
);
448 // Gets the set of tab indices that have the same opener as the tab at
450 void GetIndicesWithSameOpener(int index
, std::vector
<int>* indices
);
452 // If |index| is selected all the selected indices are returned, otherwise a
453 // vector with |index| is returned. This is used when executing commands to
454 // determine which indices the command applies to.
455 std::vector
<int> GetIndicesForCommand(int index
) const;
457 // Returns true if the specified WebContents is a New Tab at the end of
458 // the tabstrip. We check for this because opener relationships are _not_
459 // forgotten for the New Tab page opened as a result of a New Tab gesture
460 // (e.g. Ctrl+T, etc) since the user may open a tab transiently to look up
461 // something related to their current activity.
462 bool IsNewTabAtEndOfTabStrip(content::WebContents
* contents
) const;
464 // Closes the WebContentses at the specified indices. This causes the
465 // WebContentses to be destroyed, but it may not happen immediately. If
466 // the page in question has an unload event the WebContents will not be
467 // destroyed until after the event has completed, which will then call back
470 // Returns true if the WebContentses were closed immediately, false if we
471 // are waiting for the result of an onunload handler.
472 bool InternalCloseTabs(const std::vector
<int>& indices
,
475 // Invoked from InternalCloseTabs and when an extension is removed for an app
476 // tab. Notifies observers of TabClosingAt and deletes |contents|. If
477 // |create_historical_tabs| is true, CreateHistoricalTab is invoked on the
480 // The boolean parameter create_historical_tab controls whether to
481 // record these tabs and their history for reopening recently closed
483 void InternalCloseTab(content::WebContents
* contents
,
485 bool create_historical_tabs
);
487 // Gets the WebContents at an index. Does no bounds checking.
488 content::WebContents
* GetWebContentsAtImpl(int index
) const;
490 // Notifies the observers if the active tab is being deactivated.
491 void NotifyIfTabDeactivated(content::WebContents
* contents
);
493 // Notifies the observers if the active tab has changed.
494 void NotifyIfActiveTabChanged(content::WebContents
* old_contents
,
495 NotifyTypes notify_types
);
497 // Notifies the observers if the active tab or the tab selection has changed.
498 // |old_model| is a snapshot of |selection_model_| before the change.
499 // Note: This function might end up sending 0 to 2 notifications in the
500 // following order: ActiveTabChanged, TabSelectionChanged.
501 void NotifyIfActiveOrSelectionChanged(
502 content::WebContents
* old_contents
,
503 NotifyTypes notify_types
,
504 const ui::ListSelectionModel
& old_model
);
506 // Sets the selection to |new_model| and notifies any observers.
507 // Note: This function might end up sending 0 to 3 notifications in the
508 // following order: TabDeactivated, ActiveTabChanged, TabSelectionChanged.
509 void SetSelection(const ui::ListSelectionModel
& new_model
,
510 NotifyTypes notify_types
);
512 // Selects either the next tab (|forward| is true), or the previous tab
513 // (|forward| is false).
514 void SelectRelativeTab(bool forward
);
516 // Does the work of MoveWebContentsAt. This has no checks to make sure the
517 // position is valid, those are done in MoveWebContentsAt.
518 void MoveWebContentsAtImpl(int index
,
520 bool select_after_move
);
522 // Implementation of MoveSelectedTabsTo. Moves |length| of the selected tabs
523 // starting at |start| to |index|. See MoveSelectedTabsTo for more details.
524 void MoveSelectedTabsToImpl(int index
, size_t start
, size_t length
);
526 // Returns true if the tab represented by the specified data has an opener
527 // that matches the specified one. If |use_group| is true, then this will
528 // fall back to check the group relationship as well.
529 static bool OpenerMatches(const WebContentsData
* data
,
530 const content::WebContents
* opener
,
533 // Sets the group/opener of any tabs that reference the tab at |index| to that
534 // tab's group/opener respectively.
535 void FixOpenersAndGroupsReferencing(int index
);
538 TabStripModelDelegate
* delegate_
;
540 // The WebContents data currently hosted within this TabStripModel.
541 typedef std::vector
<WebContentsData
*> WebContentsDataVector
;
542 WebContentsDataVector contents_data_
;
544 // A profile associated with this TabStripModel.
547 // True if all tabs are currently being closed via CloseAllTabs.
550 // An object that determines where new Tabs should be inserted and where
551 // selection should move when a Tab is closed.
552 scoped_ptr
<TabStripModelOrderController
> order_controller_
;
555 typedef base::ObserverList
<TabStripModelObserver
> TabStripModelObservers
;
556 TabStripModelObservers observers_
;
558 ui::ListSelectionModel selection_model_
;
560 // TODO(sky): remove this; used for debugging 291265.
563 base::WeakPtrFactory
<TabStripModel
> weak_factory_
;
565 DISALLOW_IMPLICIT_CONSTRUCTORS(TabStripModel
);
568 #endif // CHROME_BROWSER_UI_TABS_TAB_STRIP_MODEL_H_