tab_strip_model.h revision 0f1bc08d4cfcc34181b0b5cbf065c40f687bf740
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.
4
5#ifndef CHROME_BROWSER_UI_TABS_TAB_STRIP_MODEL_H_
6#define CHROME_BROWSER_UI_TABS_TAB_STRIP_MODEL_H_
7
8#include <vector>
9
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 "content/public/common/page_transition_types.h"
14#include "ui/base/models/list_selection_model.h"
15
16class Profile;
17class TabStripModelDelegate;
18class TabStripModelOrderController;
19
20namespace content {
21class WebContents;
22}
23
24////////////////////////////////////////////////////////////////////////////////
25//
26// TabStripModel
27//
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.
32//
33// Each tab may be any one of the following states:
34// . Mini-tab. Mini tabs are locked to the left side of the tab strip and
35//   rendered differently (small tabs with only a favicon). The model makes
36//   sure all mini-tabs are at the beginning of the tab strip. For example,
37//   if a non-mini tab is added it is forced to be with non-mini tabs. Requests
38//   to move tabs outside the range of the tab type are ignored. For example,
39//   a request to move a mini-tab after non-mini-tabs is ignored.
40//   You'll notice there is no explicit api for making a tab a mini-tab, rather
41//   there are two tab types that are implicitly mini-tabs:
42//   . App. Corresponds to an extension that wants an app tab. App tabs are
43//     identified by extensions::TabHelper::is_app().
44//     App tabs are always pinned (you can't unpin them).
45//   . Pinned. Any tab can be pinned. Non-app tabs whose pinned state is changed
46//     are moved to be with other mini-tabs or non-mini tabs.
47//
48//  A TabStripModel has one delegate that it relies on to perform certain tasks
49//  like creating new TabStripModels (probably hosted in Browser windows) when
50//  required. See TabStripDelegate above for more information.
51//
52//  A TabStripModel also has N observers (see TabStripModelObserver above),
53//  which can be registered via Add/RemoveObserver. An Observer is notified of
54//  tab creations, removals, moves, and other interesting events. The
55//  TabStrip implements this interface to know when to create new tabs in
56//  the View, and the Browser object likewise implements to be able to update
57//  its bookkeeping when such events happen.
58//
59////////////////////////////////////////////////////////////////////////////////
60class TabStripModel {
61 public:
62  // Used to specify what should happen when the tab is closed.
63  enum CloseTypes {
64    CLOSE_NONE                     = 0,
65
66    // Indicates the tab was closed by the user. If true,
67    // WebContents::SetClosedByUserGesture(true) is invoked.
68    CLOSE_USER_GESTURE             = 1 << 0,
69
70    // If true the history is recorded so that the tab can be reopened later.
71    // You almost always want to set this.
72    CLOSE_CREATE_HISTORICAL_TAB    = 1 << 1,
73  };
74
75  // Constants used when adding tabs.
76  enum AddTabTypes {
77    // Used to indicate nothing special should happen to the newly inserted
78    // tab.
79    ADD_NONE          = 0,
80
81    // The tab should be active.
82    ADD_ACTIVE        = 1 << 0,
83
84    // The tab should be pinned.
85    ADD_PINNED        = 1 << 1,
86
87    // If not set the insertion index of the WebContents is left up to
88    // the Order Controller associated, so the final insertion index may differ
89    // from the specified index. Otherwise the index supplied is used.
90    ADD_FORCE_INDEX   = 1 << 2,
91
92    // If set the newly inserted tab inherits the group of the currently
93    // selected tab. If not set the tab may still inherit the group under
94    // certain situations.
95    ADD_INHERIT_GROUP = 1 << 3,
96
97    // If set the newly inserted tab's opener is set to the active tab. If not
98    // set the tab may still inherit the group/opener under certain situations.
99    // NOTE: this is ignored if ADD_INHERIT_GROUP is set.
100    ADD_INHERIT_OPENER = 1 << 4,
101  };
102
103  // Enumerates different ways to open a new tab. Does not apply to opening
104  // existing links or searches in a new tab, only to brand new empty tabs.
105  enum NewTab {
106    // New tab was opened using the new tab button on the tab strip.
107    NEW_TAB_BUTTON,
108
109    // New tab was opened using the menu command - either through the keyboard
110    // shortcut, or by opening the menu and selecting the command. Applies to
111    // both Wrench menu and the menu bar's File menu (on platforms that have
112    // one).
113    NEW_TAB_COMMAND,
114
115    // New tab was opened through the context menu on the tab strip.
116    NEW_TAB_CONTEXT_MENU,
117
118    // Number of enum entries, used for UMA histogram reporting macros.
119    NEW_TAB_ENUM_COUNT,
120  };
121
122  static const int kNoTab = -1;
123
124  // Construct a TabStripModel with a delegate to help it do certain things
125  // (see the TabStripModelDelegate documentation). |delegate| cannot be NULL.
126  TabStripModel(TabStripModelDelegate* delegate, Profile* profile);
127  virtual ~TabStripModel();
128
129  // Retrieves the TabStripModelDelegate associated with this TabStripModel.
130  TabStripModelDelegate* delegate() const { return delegate_; }
131
132  // Add and remove observers to changes within this TabStripModel.
133  void AddObserver(TabStripModelObserver* observer);
134  void RemoveObserver(TabStripModelObserver* observer);
135
136  // Retrieve the number of WebContentses/emptiness of the TabStripModel.
137  int count() const { return static_cast<int>(contents_data_.size()); }
138  bool empty() const { return contents_data_.empty(); }
139
140  // Retrieve the Profile associated with this TabStripModel.
141  Profile* profile() const { return profile_; }
142
143  // Retrieve the index of the currently active WebContents.
144  int active_index() const { return selection_model_.active(); }
145
146  // Returns true if the tabstrip is currently closing all open tabs (via a
147  // call to CloseAllTabs). As tabs close, the selection in the tabstrip
148  // changes which notifies observers, which can use this as an optimization to
149  // avoid doing meaningless or unhelpful work.
150  bool closing_all() const { return closing_all_; }
151
152  // Access the order controller. Exposed only for unit tests.
153  TabStripModelOrderController* order_controller() const {
154    return order_controller_.get();
155  }
156
157  // Basic API /////////////////////////////////////////////////////////////////
158
159  // Determines if the specified index is contained within the TabStripModel.
160  bool ContainsIndex(int index) const;
161
162  // Adds the specified WebContents in the default location. Tabs opened
163  // in the foreground inherit the group of the previously active tab.
164  void AppendWebContents(content::WebContents* contents, bool foreground);
165
166  // Adds the specified WebContents at the specified location.
167  // |add_types| is a bitmask of AddTabTypes; see it for details.
168  //
169  // All append/insert methods end up in this method.
170  //
171  // NOTE: adding a tab using this method does NOT query the order controller,
172  // as such the ADD_FORCE_INDEX AddTabTypes is meaningless here.  The only time
173  // the |index| is changed is if using the index would result in breaking the
174  // constraint that all mini-tabs occur before non-mini-tabs.
175  // See also AddWebContents.
176  void InsertWebContentsAt(int index,
177                           content::WebContents* contents,
178                           int add_types);
179
180  // Closes the WebContents at the specified index. This causes the
181  // WebContents to be destroyed, but it may not happen immediately.
182  // |close_types| is a bitmask of CloseTypes. Returns true if the
183  // WebContents was closed immediately, false if it was not closed (we
184  // may be waiting for a response from an onunload handler, or waiting for the
185  // user to confirm closure).
186  bool CloseWebContentsAt(int index, uint32 close_types);
187
188  // Replaces the WebContents at |index| with |new_contents|. The
189  // WebContents that was at |index| is returned and its ownership returns
190  // to the caller.
191  content::WebContents* ReplaceWebContentsAt(
192      int index,
193      content::WebContents* new_contents);
194
195  // Destroys the WebContents at the specified index, but keeps the tab
196  // visible in the tab strip. Used to free memory in low-memory conditions,
197  // especially on Chrome OS. The tab reloads if the user clicks on it.
198  // Returns the new empty WebContents, used only for testing.
199  content::WebContents* DiscardWebContentsAt(int index);
200
201  // Detaches the WebContents at the specified index from this strip. The
202  // WebContents is not destroyed, just removed from display. The caller
203  // is responsible for doing something with it (e.g. stuffing it into another
204  // strip). Returns the detached WebContents.
205  content::WebContents* DetachWebContentsAt(int index);
206
207  // Makes the tab at the specified index the active tab. |user_gesture| is true
208  // if the user actually clicked on the tab or navigated to it using a keyboard
209  // command, false if the tab was activated as a by-product of some other
210  // action.
211  void ActivateTabAt(int index, bool user_gesture);
212
213  // Adds tab at |index| to the currently selected tabs, without changing the
214  // active tab index.
215  void AddTabAtToSelection(int index);
216
217  // Move the WebContents at the specified index to another index. This
218  // method does NOT send Detached/Attached notifications, rather it moves the
219  // WebContents inline and sends a Moved notification instead.
220  // If |select_after_move| is false, whatever tab was selected before the move
221  // will still be selected, but its index may have incremented or decremented
222  // one slot.
223  // NOTE: This respects basic ordering constraints and thus does nothing if the
224  // move would result in app tabs and non-app tabs mixing.
225  void MoveWebContentsAt(int index, int to_position, bool select_after_move);
226
227  // Moves the selected tabs to |index|. |index| is treated as if the tab strip
228  // did not contain any of the selected tabs. For example, if the tabstrip
229  // contains [A b c D E f] (upper case selected) and this is invoked with 1 the
230  // result is [b A D E c f].
231  // This method maintains that all mini-tabs occur before non-mini-tabs.  When
232  // mini-tabs are selected the move is processed in two chunks: first mini-tabs
233  // are moved, then non-mini-tabs are moved. If the index is after
234  // (mini-tab-count - selected-mini-tab-count), then the index the non-mini
235  // selected tabs are moved to is (index + selected-mini-tab-count). For
236  // example, if the model consists of [A b c D E f] (A b c are mini) and this
237  // is invoked with 2, the result is [b c A D E f]. In this example nothing
238  // special happened because the target index was <= (mini-tab-count -
239  // selected-mini-tab-count). If the target index were 3, then the result would
240  // be [b c A f D F]. A, being mini, can move no further than index 2. The
241  // non-mini-tabs are moved to the target index + selected-mini-tab-count (3 +
242  // 1)
243  void MoveSelectedTabsTo(int index);
244
245  // Returns the currently active WebContents, or NULL if there is none.
246  content::WebContents* GetActiveWebContents() const;
247
248  // Returns the WebContents at the specified index, or NULL if there is
249  // none.
250  content::WebContents* GetWebContentsAt(int index) const;
251
252  // Returns the index of the specified WebContents, or TabStripModel::kNoTab
253  // if the WebContents is not in this TabStripModel.
254  int GetIndexOfWebContents(const content::WebContents* contents) const;
255
256  // Notify any observers that the WebContents at the specified index has
257  // changed in some way. See TabChangeType for details of |change_type|.
258  void UpdateWebContentsStateAt(
259      int index,
260      TabStripModelObserver::TabChangeType change_type);
261
262  // Close all tabs at once. Code can use closing_all() above to defer
263  // operations that might otherwise by invoked by the flurry of detach/select
264  // notifications this method causes.
265  void CloseAllTabs();
266
267  // Returns true if there are any WebContentses that are currently loading.
268  bool TabsAreLoading() const;
269
270  // Returns the WebContents that opened the WebContents at |index|, or NULL if
271  // there is no opener on record.
272  content::WebContents* GetOpenerOfWebContentsAt(int index);
273
274  // Changes the |opener| of the WebContents at |index|.
275  // Note: |opener| must be in this tab strip.
276  void SetOpenerOfWebContentsAt(int index, content::WebContents* opener);
277
278  // Returns the index of the next WebContents in the sequence of WebContentses
279  // spawned by the specified WebContents after |start_index|. If |use_group| is
280  // true, the group property of the tab is used instead of the opener to find
281  // the next tab. Under some circumstances the group relationship may exist but
282  // the opener may not.
283  int GetIndexOfNextWebContentsOpenedBy(const content::WebContents* opener,
284                                        int start_index,
285                                        bool use_group) const;
286
287  // Returns the index of the last WebContents in the model opened by the
288  // specified opener, starting at |start_index|.
289  int GetIndexOfLastWebContentsOpenedBy(const content::WebContents* opener,
290                                        int start_index) const;
291
292  // To be called when a navigation is about to occur in the specified
293  // WebContents. Depending on the tab, and the transition type of the
294  // navigation, the TabStripModel may adjust its selection and grouping
295  // behavior.
296  void TabNavigating(content::WebContents* contents,
297                     content::PageTransition transition);
298
299  // Forget all Opener relationships that are stored (but _not_ group
300  // relationships!) This is to reduce unpredictable tab switching behavior
301  // in complex session states. The exact circumstances under which this method
302  // is called are left up to the implementation of the selected
303  // TabStripModelOrderController.
304  void ForgetAllOpeners();
305
306  // Forgets the group affiliation of the specified WebContents. This
307  // should be called when a WebContents that is part of a logical group
308  // of tabs is moved to a new logical context by the user (e.g. by typing a new
309  // URL or selecting a bookmark). This also forgets the opener, which is
310  // considered a weaker relationship than group.
311  void ForgetGroup(content::WebContents* contents);
312
313  // Returns true if the group/opener relationships present for |contents|
314  // should be reset when _any_ selection change occurs in the model.
315  bool ShouldResetGroupOnSelect(content::WebContents* contents) const;
316
317  // Changes the blocked state of the tab at |index|.
318  void SetTabBlocked(int index, bool blocked);
319
320  // Changes the pinned state of the tab at |index|. See description above
321  // class for details on this.
322  void SetTabPinned(int index, bool pinned);
323
324  // Returns true if the tab at |index| is pinned.
325  // See description above class for details on pinned tabs.
326  bool IsTabPinned(int index) const;
327
328  // Is the tab a mini-tab?
329  // See description above class for details on this.
330  bool IsMiniTab(int index) const;
331
332  // Is the tab at |index| an app?
333  // See description above class for details on app tabs.
334  bool IsAppTab(int index) const;
335
336  // Returns true if the tab at |index| is blocked by a tab modal dialog.
337  bool IsTabBlocked(int index) const;
338
339  // Returns true if the WebContents at |index| has been discarded to
340  // save memory.  See DiscardWebContentsAt() for details.
341  bool IsTabDiscarded(int index) const;
342
343  // Returns the index of the first tab that is not a mini-tab. This returns
344  // |count()| if all of the tabs are mini-tabs, and 0 if none of the tabs are
345  // mini-tabs.
346  int IndexOfFirstNonMiniTab() const;
347
348  // Returns a valid index for inserting a new tab into this model. |index| is
349  // the proposed index and |mini_tab| is true if inserting a tab will become
350  // mini (pinned or app). If |mini_tab| is true, the returned index is between
351  // 0 and IndexOfFirstNonMiniTab. If |mini_tab| is false, the returned index
352  // is between IndexOfFirstNonMiniTab and count().
353  int ConstrainInsertionIndex(int index, bool mini_tab);
354
355  // Extends the selection from the anchor to |index|.
356  void ExtendSelectionTo(int index);
357
358  // Toggles the selection at |index|. This does nothing if |index| is selected
359  // and there are no other selected tabs.
360  void ToggleSelectionAt(int index);
361
362  // Makes sure the tabs from the anchor to |index| are selected. This only
363  // adds to the selection.
364  void AddSelectionFromAnchorTo(int index);
365
366  // Returns true if the tab at |index| is selected.
367  bool IsTabSelected(int index) const;
368
369  // Sets the selection to match that of |source|.
370  void SetSelectionFromModel(const ui::ListSelectionModel& source);
371
372  const ui::ListSelectionModel& selection_model() const {
373    return selection_model_;
374  }
375
376  // Command level API /////////////////////////////////////////////////////////
377
378  // Adds a WebContents at the best position in the TabStripModel given
379  // the specified insertion index, transition, etc. |add_types| is a bitmask of
380  // AddTabTypes; see it for details. This method ends up calling into
381  // InsertWebContentsAt to do the actual insertion. Pass kNoTab for |index| to
382  // append the contents to the end of the tab strip.
383  void AddWebContents(content::WebContents* contents,
384                      int index,
385                      content::PageTransition transition,
386                      int add_types);
387
388  // Closes the selected tabs.
389  void CloseSelectedTabs();
390
391  // Select adjacent tabs
392  void SelectNextTab();
393  void SelectPreviousTab();
394
395  // Selects the last tab in the tab strip.
396  void SelectLastTab();
397
398  // Swap adjacent tabs.
399  void MoveTabNext();
400  void MoveTabPrevious();
401
402  // View API //////////////////////////////////////////////////////////////////
403
404  // Context menu functions.
405  enum ContextMenuCommand {
406    CommandFirst = 0,
407    CommandNewTab,
408    CommandReload,
409    CommandDuplicate,
410    CommandCloseTab,
411    CommandCloseOtherTabs,
412    CommandCloseTabsToRight,
413    CommandRestoreTab,
414    CommandTogglePinned,
415    CommandBookmarkAllTabs,
416    CommandSelectByDomain,
417    CommandSelectByOpener,
418    CommandLast
419  };
420
421  // Returns true if the specified command is enabled. If |context_index| is
422  // selected the response applies to all selected tabs.
423  bool IsContextMenuCommandEnabled(int context_index,
424                                   ContextMenuCommand command_id) const;
425
426  // Performs the action associated with the specified command for the given
427  // TabStripModel index |context_index|.  If |context_index| is selected the
428  // command applies to all selected tabs.
429  void ExecuteContextMenuCommand(int context_index,
430                                 ContextMenuCommand command_id);
431
432  // Returns a vector of indices of the tabs that will close when executing the
433  // command |id| for the tab at |index|. The returned indices are sorted in
434  // descending order.
435  std::vector<int> GetIndicesClosedByCommand(int index,
436                                             ContextMenuCommand id) const;
437
438  // Returns true if 'CommandTogglePinned' will pin. |index| is the index
439  // supplied to |ExecuteContextMenuCommand|.
440  bool WillContextMenuPin(int index);
441
442  // Convert a ContextMenuCommand into a browser command. Returns true if a
443  // corresponding browser command exists, false otherwise.
444  static bool ContextMenuCommandToBrowserCommand(int cmd_id, int* browser_cmd);
445
446 private:
447  class WebContentsData;
448
449  // Used when making selection notifications.
450  enum NotifyTypes {
451    NOTIFY_DEFAULT,
452
453    // The selection is changing from a user gesture.
454    NOTIFY_USER_GESTURE,
455  };
456
457  // Convenience for converting a vector of indices into a vector of
458  // WebContents.
459  std::vector<content::WebContents*> GetWebContentsFromIndices(
460      const std::vector<int>& indices) const;
461
462  // Gets the set of tab indices whose domain matches the tab at |index|.
463  void GetIndicesWithSameDomain(int index, std::vector<int>* indices);
464
465  // Gets the set of tab indices that have the same opener as the tab at
466  // |index|.
467  void GetIndicesWithSameOpener(int index, std::vector<int>* indices);
468
469  // If |index| is selected all the selected indices are returned, otherwise a
470  // vector with |index| is returned. This is used when executing commands to
471  // determine which indices the command applies to.
472  std::vector<int> GetIndicesForCommand(int index) const;
473
474  // Returns true if the specified WebContents is a New Tab at the end of
475  // the tabstrip. We check for this because opener relationships are _not_
476  // forgotten for the New Tab page opened as a result of a New Tab gesture
477  // (e.g. Ctrl+T, etc) since the user may open a tab transiently to look up
478  // something related to their current activity.
479  bool IsNewTabAtEndOfTabStrip(content::WebContents* contents) const;
480
481  // Closes the WebContentses at the specified indices. This causes the
482  // WebContentses to be destroyed, but it may not happen immediately. If
483  // the page in question has an unload event the WebContents will not be
484  // destroyed until after the event has completed, which will then call back
485  // into this method.
486  //
487  // Returns true if the WebContentses were closed immediately, false if we
488  // are waiting for the result of an onunload handler.
489  bool InternalCloseTabs(const std::vector<int>& indices,
490                         uint32 close_types);
491
492  // Invoked from InternalCloseTabs and when an extension is removed for an app
493  // tab. Notifies observers of TabClosingAt and deletes |contents|. If
494  // |create_historical_tabs| is true, CreateHistoricalTab is invoked on the
495  // delegate.
496  //
497  // The boolean parameter create_historical_tab controls whether to
498  // record these tabs and their history for reopening recently closed
499  // tabs.
500  void InternalCloseTab(content::WebContents* contents,
501                        int index,
502                        bool create_historical_tabs);
503
504  // Gets the WebContents at an index. Does no bounds checking.
505  content::WebContents* GetWebContentsAtImpl(int index) const;
506
507  // Notifies the observers if the active tab is being deactivated.
508  void NotifyIfTabDeactivated(content::WebContents* contents);
509
510  // Notifies the observers if the active tab has changed.
511  void NotifyIfActiveTabChanged(content::WebContents* old_contents,
512                                NotifyTypes notify_types);
513
514  // Notifies the observers if the active tab or the tab selection has changed.
515  // |old_model| is a snapshot of |selection_model_| before the change.
516  // Note: This function might end up sending 0 to 2 notifications in the
517  // following order: ActiveTabChanged, TabSelectionChanged.
518  void NotifyIfActiveOrSelectionChanged(
519      content::WebContents* old_contents,
520      NotifyTypes notify_types,
521      const ui::ListSelectionModel& old_model);
522
523  // Sets the selection to |new_model| and notifies any observers.
524  // Note: This function might end up sending 0 to 3 notifications in the
525  // following order: TabDeactivated, ActiveTabChanged, TabSelectionChanged.
526  void SetSelection(const ui::ListSelectionModel& new_model,
527                    NotifyTypes notify_types);
528
529  // Selects either the next tab (|forward| is true), or the previous tab
530  // (|forward| is false).
531  void SelectRelativeTab(bool forward);
532
533  // Does the work of MoveWebContentsAt. This has no checks to make sure the
534  // position is valid, those are done in MoveWebContentsAt.
535  void MoveWebContentsAtImpl(int index,
536                             int to_position,
537                             bool select_after_move);
538
539  // Implementation of MoveSelectedTabsTo. Moves |length| of the selected tabs
540  // starting at |start| to |index|. See MoveSelectedTabsTo for more details.
541  void MoveSelectedTabsToImpl(int index, size_t start, size_t length);
542
543  // Returns true if the tab represented by the specified data has an opener
544  // that matches the specified one. If |use_group| is true, then this will
545  // fall back to check the group relationship as well.
546  static bool OpenerMatches(const WebContentsData* data,
547                            const content::WebContents* opener,
548                            bool use_group);
549
550  // Sets the group/opener of any tabs that reference |tab| to NULL.
551  void ForgetOpenersAndGroupsReferencing(const content::WebContents* tab);
552
553  // Our delegate.
554  TabStripModelDelegate* delegate_;
555
556  // The WebContents data currently hosted within this TabStripModel.
557  typedef std::vector<WebContentsData*> WebContentsDataVector;
558  WebContentsDataVector contents_data_;
559
560  // A profile associated with this TabStripModel.
561  Profile* profile_;
562
563  // True if all tabs are currently being closed via CloseAllTabs.
564  bool closing_all_;
565
566  // An object that determines where new Tabs should be inserted and where
567  // selection should move when a Tab is closed.
568  scoped_ptr<TabStripModelOrderController> order_controller_;
569
570  // Our observers.
571  typedef ObserverList<TabStripModelObserver> TabStripModelObservers;
572  TabStripModelObservers observers_;
573
574  ui::ListSelectionModel selection_model_;
575
576  // TODO(sky): remove this; used for debugging 291265.
577  bool in_notify_;
578
579  DISALLOW_IMPLICIT_CONSTRUCTORS(TabStripModel);
580};
581
582#endif  // CHROME_BROWSER_UI_TABS_TAB_STRIP_MODEL_H_
583