tab_strip_model.h revision 3345a6884c488ff3a535c2c9acdd33d74b37e311
1// Copyright (c) 2010 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_TABS_TAB_STRIP_MODEL_H_
6#define CHROME_BROWSER_TABS_TAB_STRIP_MODEL_H_
7#pragma once
8
9#include <vector>
10
11#include "base/observer_list.h"
12#include "chrome/browser/tabs/tab_strip_model_observer.h"
13#include "chrome/common/notification_observer.h"
14#include "chrome/common/notification_registrar.h"
15#include "chrome/common/page_transition_types.h"
16
17class NavigationController;
18class Profile;
19class TabContents;
20class TabStripModelDelegate;
21class TabStripModelOrderController;
22
23////////////////////////////////////////////////////////////////////////////////
24//
25// TabStripModel
26//
27//  A model & low level controller of a Browser Window tabstrip. Holds a vector
28//  of TabContents, and provides an API for adding, removing and shuffling
29//  them, as well as a higher level API for doing specific Browser-related
30//  tasks like adding new Tabs from just a URL, etc.
31//
32// Each tab may be any one of the following states:
33// . Mini-tab. Mini tabs are locked to the left side of the tab strip and
34//   rendered differently (small tabs with only a favicon). The model makes
35//   sure all mini-tabs are at the beginning of the tab strip. For example,
36//   if a non-mini tab is added it is forced to be with non-mini tabs. Requests
37//   to move tabs outside the range of the tab type are ignored. For example,
38//   a request to move a mini-tab after non-mini-tabs is ignored.
39//   You'll notice there is no explcit api for making a tab a mini-tab, rather
40//   there are two tab types that are implicitly mini-tabs:
41//   . App. Corresponds to an extension that wants an app tab. App tabs are
42//     identified by TabContents::is_app(). App tabs are always pinneded (you
43//     can't unpin them).
44//   . Pinned. Any tab can be pinned. A pinned tab is made phantom when closed.
45//     Non-app tabs whose pinned state is changed are moved to be with other
46//     mini-tabs or non-mini tabs.
47// . Phantom. Only pinned tabs may be made phantom. When a tab that can be made
48//   phantom is closed the renderer is shutdown, a new
49//   TabContents/NavigationController is created that has not yet loaded the
50//   renderer and observers are notified via the TabReplacedAt method. When a
51//   phantom tab is selected the renderer is loaded and the tab is no longer
52//   phantom.
53//   Phantom tabs do not prevent the tabstrip from closing, for example if the
54//   tabstrip has one phantom and one non-phantom tab and the non-phantom tab is
55//   closed, then the tabstrip/browser are closed.
56//
57//  A TabStripModel has one delegate that it relies on to perform certain tasks
58//  like creating new TabStripModels (probably hosted in Browser windows) when
59//  required. See TabStripDelegate above for more information.
60//
61//  A TabStripModel also has N observers (see TabStripModelObserver above),
62//  which can be registered via Add/RemoveObserver. An Observer is notified of
63//  tab creations, removals, moves, and other interesting events. The
64//  TabStrip implements this interface to know when to create new tabs in
65//  the View, and the Browser object likewise implements to be able to update
66//  its bookkeeping when such events happen.
67//
68////////////////////////////////////////////////////////////////////////////////
69class TabStripModel : public NotificationObserver {
70 public:
71  // Policy for how new tabs are inserted.
72  enum InsertionPolicy {
73    // Newly created tabs are created after the selection. This is the default.
74    INSERT_AFTER,
75
76    // Newly created tabs are inserted before the selection.
77    INSERT_BEFORE,
78  };
79
80  // Used to specify what should happen when the tab is closed.
81  enum CloseTypes {
82    CLOSE_NONE                     = 0,
83
84    // Indicates the tab was closed by the user. If true,
85    // TabContents::set_closed_by_user_gesture(true) is invoked.
86    CLOSE_USER_GESTURE             = 1 << 0,
87
88    // If true the history is recorded so that the tab can be reopened later.
89    // You almost always want to set this.
90    CLOSE_CREATE_HISTORICAL_TAB    = 1 << 1,
91  };
92
93  // Constants used when adding tabs.
94  enum AddTabTypes {
95    // Used to indicate nothing special should happen to the newly inserted
96    // tab.
97    ADD_NONE          = 0,
98
99    // The tab should be selected.
100    ADD_SELECTED      = 1 << 0,
101
102    // The tab should be pinned.
103    ADD_PINNED        = 1 << 1,
104
105    // If not set the insertion index of the TabContents is left up to the Order
106    // Controller associated, so the final insertion index may differ from the
107    // specified index. Otherwise the index supplied is used.
108    ADD_FORCE_INDEX   = 1 << 2,
109
110    // If set the newly inserted tab inherits the group of the currently
111    // selected tab. If not set the tab may still inherit the group under
112    // certain situations.
113    ADD_INHERIT_GROUP = 1 << 3,
114
115    // If set the newly inserted tab's opener is set to the currently selected
116    // tab. If not set the tab may still inherit the group/opener under certain
117    // situations.
118    // NOTE: this is ignored if ADD_INHERIT_GROUP is set.
119    ADD_INHERIT_OPENER = 1 << 4,
120  };
121
122  static const int kNoTab = -1;
123
124  // Construct a TabStripModel with a delegate to help it do certain things
125  // (See 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 TabContentses/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  // Returns true if there are any non-phantom tabs. When there are no
141  // non-phantom tabs the delegate is notified by way of TabStripEmpty and the
142  // browser closes.
143  bool HasNonPhantomTabs() const;
144
145  // Retrieve the Profile associated with this TabStripModel.
146  Profile* profile() const { return profile_; }
147
148  // Retrieve the index of the currently selected TabContents.
149  int selected_index() const { return selected_index_; }
150
151  // Returns true if the tabstrip is currently closing all open tabs (via a
152  // call to CloseAllTabs). As tabs close, the selection in the tabstrip
153  // changes which notifies observers, which can use this as an optimization to
154  // avoid doing meaningless or unhelpful work.
155  bool closing_all() const { return closing_all_; }
156
157  // Access the order controller. Exposed only for unit tests.
158  TabStripModelOrderController* order_controller() const {
159    return order_controller_;
160  }
161
162  // Sets the insertion policy. Default is INSERT_AFTER.
163  void SetInsertionPolicy(InsertionPolicy policy);
164  InsertionPolicy insertion_policy() const;
165
166  // Returns true if |observer| is in the list of observers. This is intended
167  // for debugging.
168  bool HasObserver(TabStripModelObserver* observer);
169
170  // Basic API /////////////////////////////////////////////////////////////////
171
172  // Determines if the specified index is contained within the TabStripModel.
173  bool ContainsIndex(int index) const;
174
175  // Adds the specified TabContents in the default location. Tabs opened in the
176  // foreground inherit the group of the previously selected tab.
177  void AppendTabContents(TabContents* contents, bool foreground);
178
179  // Adds the specified TabContents at the specified location. |add_types| is a
180  // bitmask of AddTypes; see it for details.
181  //
182  // All append/insert methods end up in this method.
183  //
184  // NOTE: adding a tab using this method does NOT query the order controller,
185  // as such the ADD_FORCE_INDEX AddType is meaningless here.  The only time the
186  // |index| is changed is if using the index would result in breaking the
187  // constraint that all mini-tabs occur before non-mini-tabs.
188  // See also AddTabContents.
189  void InsertTabContentsAt(int index,
190                           TabContents* contents,
191                           int add_types);
192
193  // Closes the TabContents at the specified index. This causes the TabContents
194  // to be destroyed, but it may not happen immediately (e.g. if it's a
195  // TabContents). |close_types| is a bitmask of CloseTypes.
196  // Returns true if the TabContents was closed immediately, false if it was not
197  // closed (we may be waiting for a response from an onunload handler, or
198  // waiting for the user to confirm closure).
199  bool CloseTabContentsAt(int index, uint32 close_types);
200
201  // Replaces the entire state of a the tab at index by switching in a
202  // different NavigationController. This is used through the recently
203  // closed tabs list, which needs to replace a tab's current state
204  // and history with another set of contents and history.
205  //
206  // The old NavigationController is deallocated and this object takes
207  // ownership of the passed in controller.
208  void ReplaceNavigationControllerAt(int index,
209                                     NavigationController* controller);
210
211  // Replaces the tab contents at |index| with |new_contents|. |type| is passed
212  // to the observer. This deletes the TabContents currently at |index|.
213  void ReplaceTabContentsAt(int index,
214                            TabContents* new_contents,
215                            TabStripModelObserver::TabReplaceType type);
216
217  // Detaches the TabContents at the specified index from this strip. The
218  // TabContents is not destroyed, just removed from display. The caller is
219  // responsible for doing something with it (e.g. stuffing it into another
220  // strip).
221  TabContents* DetachTabContentsAt(int index);
222
223  // Select the TabContents at the specified index. |user_gesture| is true if
224  // the user actually clicked on the tab or navigated to it using a keyboard
225  // command, false if the tab was selected as a by-product of some other
226  // action.
227  void SelectTabContentsAt(int index, bool user_gesture);
228
229  // Move the TabContents at the specified index to another index. This method
230  // does NOT send Detached/Attached notifications, rather it moves the
231  // TabContents inline and sends a Moved notification instead.
232  // If |select_after_move| is false, whatever tab was selected before the move
233  // will still be selected, but it's index may have incremented or decremented
234  // one slot.
235  // NOTE: this does nothing if the move would result in app tabs and non-app
236  // tabs mixing.
237  void MoveTabContentsAt(int index, int to_position, bool select_after_move);
238
239  // Returns the currently selected TabContents, or NULL if there is none.
240  TabContents* GetSelectedTabContents() const;
241
242  // Returns the TabContents at the specified index, or NULL if there is none.
243  TabContents* GetTabContentsAt(int index) const;
244
245  // Returns the index of the specified TabContents, or TabContents::kNoTab if
246  // the TabContents is not in this TabStripModel.
247  int GetIndexOfTabContents(const TabContents* contents) const;
248
249  // Returns the index of the specified NavigationController, or -1 if it is
250  // not in this TabStripModel.
251  int GetIndexOfController(const NavigationController* controller) const;
252
253  // Notify any observers that the TabContents at the specified index has
254  // changed in some way. See TabChangeType for details of |change_type|.
255  void UpdateTabContentsStateAt(
256      int index,
257      TabStripModelObserver::TabChangeType change_type);
258
259  // Make sure there is an auto-generated New Tab tab in the TabStripModel.
260  // If |force_create| is true, the New Tab will be created even if the
261  // preference is set to false (used by startup).
262  void EnsureNewTabVisible(bool force_create);
263
264  // Close all tabs at once. Code can use closing_all() above to defer
265  // operations that might otherwise by invoked by the flurry of detach/select
266  // notifications this method causes.
267  void CloseAllTabs();
268
269  // Returns true if there are any TabContents that are currently loading.
270  bool TabsAreLoading() const;
271
272  // Returns the controller controller that opened the TabContents at |index|.
273  NavigationController* GetOpenerOfTabContentsAt(int index);
274
275  // Returns the index of the next TabContents in the sequence of TabContentses
276  // spawned by the specified NavigationController after |start_index|.
277  // If |use_group| is true, the group property of the tab is used instead of
278  // the opener to find the next tab. Under some circumstances the group
279  // relationship may exist but the opener may not.
280  // NOTE: this skips phantom tabs.
281  int GetIndexOfNextTabContentsOpenedBy(const NavigationController* opener,
282                                        int start_index,
283                                        bool use_group) const;
284
285  // Returns the index of the first TabContents in the model opened by the
286  // specified opener.
287  // NOTE: this skips phantom tabs.
288  int GetIndexOfFirstTabContentsOpenedBy(const NavigationController* opener,
289                                         int start_index) const;
290
291  // Returns the index of the last TabContents in the model opened by the
292  // specified opener, starting at |start_index|.
293  // NOTE: this skips phantom tabs.
294  int GetIndexOfLastTabContentsOpenedBy(const NavigationController* opener,
295                                        int start_index) const;
296
297  // Called by the Browser when a navigation is about to occur in the specified
298  // TabContents. Depending on the tab, and the transition type of the
299  // navigation, the TabStripModel may adjust its selection and grouping
300  // behavior.
301  void TabNavigating(TabContents* contents, PageTransition::Type transition);
302
303  // Forget all Opener relationships that are stored (but _not_ group
304  // relationships!) This is to reduce unpredictable tab switching behavior
305  // in complex session states. The exact circumstances under which this method
306  // is called are left up to the implementation of the selected
307  // TabStripModelOrderController.
308  void ForgetAllOpeners();
309
310  // Forgets the group affiliation of the specified TabContents. This should be
311  // called when a TabContents that is part of a logical group of tabs is
312  // moved to a new logical context by the user (e.g. by typing a new URL or
313  // selecting a bookmark). This also forgets the opener, which is considered
314  // a weaker relationship than group.
315  void ForgetGroup(TabContents* contents);
316
317  // Returns true if the group/opener relationships present for |contents|
318  // should be reset when _any_ selection change occurs in the model.
319  bool ShouldResetGroupOnSelect(TabContents* contents) const;
320
321  // Changes the blocked state of the tab at |index|.
322  void SetTabBlocked(int index, bool blocked);
323
324  // Changes the pinned state of the tab at |index|. See description above
325  // class for details on this.
326  void SetTabPinned(int index, bool pinned);
327
328  // Returns true if the tab at |index| is pinned.
329  // See description above class for details on pinned tabs.
330  bool IsTabPinned(int index) const;
331
332  // Is the tab a mini-tab?
333  // See description above class for details on this.
334  bool IsMiniTab(int index) const;
335
336  // Is the tab at |index| an app?
337  // See description above class for details on app tabs.
338  bool IsAppTab(int index) const;
339
340  // Returns true if the tab is a phantom tab. A phantom tab is one where the
341  // renderer has not been loaded.
342  // See description above class for details on phantom tabs.
343  bool IsPhantomTab(int index) const;
344
345  // Returns true if the tab at |index| is blocked by a tab modal dialog.
346  bool IsTabBlocked(int index) const;
347
348  // Returns the index of the first tab that is not a mini-tab. This returns
349  // |count()| if all of the tabs are mini-tabs, and 0 if none of the tabs are
350  // mini-tabs.
351  int IndexOfFirstNonMiniTab() const;
352
353  // Returns a valid index for inserting a new tab into this model. |index| is
354  // the proposed index and |mini_tab| is true if inserting a tab will become
355  // mini (pinned or app). If |mini_tab| is true, the returned index is between
356  // 0 and IndexOfFirstNonMiniTab. If |mini_tab| is false, the returned index
357  // is between IndexOfFirstNonMiniTab and count().
358  int ConstrainInsertionIndex(int index, bool mini_tab);
359
360  // Returns the index of the first tab that is not a phantom tab. This returns
361  // kNoTab if all of the tabs are phantom tabs.
362  int IndexOfFirstNonPhantomTab() const;
363
364  // Returns the number of non phantom tabs in the TabStripModel.
365  int GetNonPhantomTabCount() const;
366
367
368  // Command level API /////////////////////////////////////////////////////////
369
370  // Adds a TabContents at the best position in the TabStripModel given the
371  // specified insertion index, transition, etc. |add_types| is a bitmask of
372  // AddTypes; see it for details. This method ends up calling into
373  // InsertTabContentsAt to do the actual inertion.
374  void AddTabContents(TabContents* contents,
375                      int index,
376                      PageTransition::Type transition,
377                      int add_types);
378
379  // Closes the selected TabContents.
380  void CloseSelectedTab();
381
382  // Select adjacent tabs
383  void SelectNextTab();
384  void SelectPreviousTab();
385
386  // Selects the last tab in the tab strip.
387  void SelectLastTab();
388
389  // Swap adjacent tabs.
390  void MoveTabNext();
391  void MoveTabPrevious();
392
393  // View API //////////////////////////////////////////////////////////////////
394
395  // Context menu functions.
396  enum ContextMenuCommand {
397    CommandFirst = 0,
398    CommandNewTab,
399    CommandReload,
400    CommandDuplicate,
401    CommandCloseTab,
402    CommandCloseOtherTabs,
403    CommandCloseTabsToRight,
404    CommandRestoreTab,
405    CommandTogglePinned,
406    CommandBookmarkAllTabs,
407    CommandUseVerticalTabs,
408    CommandLast
409  };
410
411  // Returns true if the specified command is enabled.
412  bool IsContextMenuCommandEnabled(int context_index,
413                                   ContextMenuCommand command_id) const;
414
415  // Returns true if the specified command is checked.
416  bool IsContextMenuCommandChecked(int context_index,
417                                   ContextMenuCommand command_id) const;
418
419  // Performs the action associated with the specified command for the given
420  // TabStripModel index |context_index|.
421  void ExecuteContextMenuCommand(int context_index,
422                                 ContextMenuCommand command_id);
423
424  // Returns a vector of indices of the tabs that will close when executing the
425  // command |id| for the tab at |index|. The returned indices are sorted in
426  // descending order.
427  std::vector<int> GetIndicesClosedByCommand(int index,
428                                             ContextMenuCommand id) const;
429
430  // Overridden from notificationObserver:
431  virtual void Observe(NotificationType type,
432                       const NotificationSource& source,
433                       const NotificationDetails& details);
434
435 private:
436  // We cannot be constructed without a delegate.
437  TabStripModel();
438
439  // Returns true if the specified TabContents is a New Tab at the end of the
440  // TabStrip. We check for this because opener relationships are _not_
441  // forgotten for the New Tab page opened as a result of a New Tab gesture
442  // (e.g. Ctrl+T, etc) since the user may open a tab transiently to look up
443  // something related to their current activity.
444  bool IsNewTabAtEndOfTabStrip(TabContents* contents) const;
445
446  // Closes the TabContents at the specified indices. This causes the
447  // TabContents to be destroyed, but it may not happen immediately.  If the
448  // page in question has an unload event the TabContents will not be destroyed
449  // until after the event has completed, which will then call back into this
450  // method.
451  //
452  // Returns true if the TabContents were closed immediately, false if we are
453  // waiting for the result of an onunload handler.
454  bool InternalCloseTabs(const std::vector<int>& indices, uint32 close_types);
455
456  // Invoked from InternalCloseTabs and when an extension is removed for an app
457  // tab. Notifies observers of TabClosingAt and deletes |contents|. If
458  // |create_historical_tabs| is true, CreateHistoricalTab is invoked on the
459  // delegate.
460  //
461  // The boolean parameter create_historical_tab controls whether to
462  // record these tabs and their history for reopening recently closed
463  // tabs.
464  void InternalCloseTab(TabContents* contents,
465                        int index,
466                        bool create_historical_tabs);
467
468  TabContents* GetContentsAt(int index) const;
469
470  // The actual implementation of SelectTabContentsAt. Takes the previously
471  // selected contents in |old_contents|, which may actually not be in
472  // |contents_| anymore because it may have been removed by a call to say
473  // DetachTabContentsAt...
474  void ChangeSelectedContentsFrom(
475      TabContents* old_contents, int to_index, bool user_gesture);
476
477  // Returns the number of New Tab tabs in the TabStripModel.
478  int GetNewTabCount() const;
479
480  // Selects either the next tab (|foward| is true), or the previous tab
481  // (|forward| is false).
482  void SelectRelativeTab(bool forward);
483
484  // Returns the first non-phantom tab starting at |index|, skipping the tab at
485  // |ignore_index|.
486  int IndexOfNextNonPhantomTab(int index, int ignore_index);
487
488  // Returns true if the tab at the specified index should be made phantom when
489  // the tab is closing.
490  bool ShouldMakePhantomOnClose(int index);
491
492  // Makes the tab a phantom tab.
493  void MakePhantom(int index);
494
495  // Does the work of MoveTabContentsAt. This has no checks to make sure the
496  // position is valid, those are done in MoveTabContentsAt.
497  void MoveTabContentsAtImpl(int index,
498                             int to_position,
499                             bool select_after_move);
500
501  // Returns true if the tab represented by the specified data has an opener
502  // that matches the specified one. If |use_group| is true, then this will
503  // fall back to check the group relationship as well.
504  struct TabContentsData;
505  static bool OpenerMatches(const TabContentsData* data,
506                            const NavigationController* opener,
507                            bool use_group);
508
509  // Does the work for ReplaceTabContentsAt returning the old TabContents.
510  // The caller owns the returned TabContents.
511  TabContents* ReplaceTabContentsAtImpl(
512      int index,
513      TabContents* new_contents,
514      TabStripModelObserver::TabReplaceType type);
515
516  // Our delegate.
517  TabStripModelDelegate* delegate_;
518
519  // A hunk of data representing a TabContents and (optionally) the
520  // NavigationController that spawned it. This memory only sticks around while
521  // the TabContents is in the current TabStripModel, unless otherwise
522  // specified in code.
523  struct TabContentsData {
524    explicit TabContentsData(TabContents* a_contents)
525        : contents(a_contents),
526          reset_group_on_select(false),
527          pinned(false),
528          blocked(false) {
529      SetGroup(NULL);
530    }
531
532    // Create a relationship between this TabContents and other TabContentses.
533    // Used to identify which TabContents to select next after one is closed.
534    void SetGroup(NavigationController* a_group) {
535      group = a_group;
536      opener = a_group;
537    }
538
539    // Forget the opener relationship so that when this TabContents is closed
540    // unpredictable re-selection does not occur.
541    void ForgetOpener() {
542      opener = NULL;
543    }
544
545    TabContents* contents;
546    // We use NavigationControllers here since they more closely model the
547    // "identity" of a Tab, TabContents can change depending on the URL loaded
548    // in the Tab.
549    // The group is used to model a set of tabs spawned from a single parent
550    // tab. This value is preserved for a given tab as long as the tab remains
551    // navigated to the link it was initially opened at or some navigation from
552    // that page (i.e. if the user types or visits a bookmark or some other
553    // navigation within that tab, the group relationship is lost). This
554    // property can safely be used to implement features that depend on a
555    // logical group of related tabs.
556    NavigationController* group;
557    // The owner models the same relationship as group, except it is more
558    // easily discarded, e.g. when the user switches to a tab not part of the
559    // same group. This property is used to determine what tab to select next
560    // when one is closed.
561    NavigationController* opener;
562    // True if our group should be reset the moment selection moves away from
563    // this Tab. This is the case for tabs opened in the foreground at the end
564    // of the TabStrip while viewing another Tab. If these tabs are closed
565    // before selection moves elsewhere, their opener is selected. But if
566    // selection shifts to _any_ tab (including their opener), the group
567    // relationship is reset to avoid confusing close sequencing.
568    bool reset_group_on_select;
569
570    // Is the tab pinned?
571    bool pinned;
572
573    // Is the tab interaction blocked by a modal dialog?
574    bool blocked;
575  };
576
577  // The TabContents data currently hosted within this TabStripModel.
578  typedef std::vector<TabContentsData*> TabContentsDataVector;
579  TabContentsDataVector contents_data_;
580
581  // The index of the TabContents in |contents_| that is currently selected.
582  int selected_index_;
583
584  // A profile associated with this TabStripModel, used when creating new Tabs.
585  Profile* profile_;
586
587  // True if all tabs are currently being closed via CloseAllTabs.
588  bool closing_all_;
589
590  // An object that determines where new Tabs should be inserted and where
591  // selection should move when a Tab is closed.
592  TabStripModelOrderController* order_controller_;
593
594  // Our observers.
595  typedef ObserverList<TabStripModelObserver> TabStripModelObservers;
596  TabStripModelObservers observers_;
597
598  // A scoped container for notification registries.
599  NotificationRegistrar registrar_;
600
601  DISALLOW_COPY_AND_ASSIGN(TabStripModel);
602};
603
604#endif  // CHROME_BROWSER_TABS_TAB_STRIP_MODEL_H_
605