diff options
Diffstat (limited to 'chrome/browser/tabs/tab_strip_model.h')
-rw-r--r-- | chrome/browser/tabs/tab_strip_model.h | 536 |
1 files changed, 536 insertions, 0 deletions
diff --git a/chrome/browser/tabs/tab_strip_model.h b/chrome/browser/tabs/tab_strip_model.h new file mode 100644 index 0000000..aaa3edb --- /dev/null +++ b/chrome/browser/tabs/tab_strip_model.h @@ -0,0 +1,536 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_BROWSER_TABS_TAB_STRIP_MODEL_H__ +#define CHROME_BROWSER_TABS_TAB_STRIP_MODEL_H__ + +#include <vector> + +#include "base/basictypes.h" +#include "base/observer_list.h" +#include "chrome/browser/history/history.h" +#include "chrome/browser/site_instance.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/page_transition_types.h" +#include "chrome/common/pref_member.h" + +namespace gfx { +class Point; +} +class GURL; +class NavigationController; +class Profile; +class TabContents; +class TabStripModelOrderController; +class TabStripModel; + +//////////////////////////////////////////////////////////////////////////////// +// +// TabStripModelObserver +// +// Objects implement this interface when they wish to be notified of changes +// to the TabStripModel. +// +// Two major implementers are the TabStrip, which uses notifications sent +// via this interface to update the presentation of the strip, and the Browser +// object, which updates bookkeeping and shows/hides individual TabContentses. +// +// Register your TabStripModelObserver with the TabStripModel using its +// Add/RemoveObserver methods. +// +//////////////////////////////////////////////////////////////////////////////// +class TabStripModelObserver { + public: + // A new TabContents was inserted into the TabStripModel at the specified + // index. |foreground| is whether or not it was opened in the foreground + // (selected). + virtual void TabInsertedAt(TabContents* contents, + int index, + bool foreground) { } + // The specified TabContents at |index| is being closed (and eventually + // destroyed). + virtual void TabClosingAt(TabContents* contents, int index) { } + // The specified TabContents at |index| is being detached, perhaps to be + // inserted in another TabStripModel. The implementer should take whatever + // action is necessary to deal with the TabContents no longer being present. + virtual void TabDetachedAt(TabContents* contents, int index) { } + // The selected TabContents changed from |old_contents| to |new_contents| at + // |index|. |user_gesture| specifies whether or not this was done by a user + // input event (e.g. clicking on a tab, keystroke) or as a side-effect of + // some other function. + virtual void TabSelectedAt(TabContents* old_contents, + TabContents* new_contents, + int index, + bool user_gesture) { } + // The specified TabContents at |from_index| was moved to |to_index|. + virtual void TabMoved(TabContents* contents, + int from_index, + int to_index) { } + // The specified TabContents at |index| changed in some way. + virtual void TabChangedAt(TabContents* contents, int index) { } + // Loading progress representations for tabs should be validated/updated. + virtual void TabValidateAnimations() { } + // The TabStripModel now no longer has any "significant" (user created or + // user manipulated) tabs. The implementer may use this as a trigger to try + // and close the window containing the TabStripModel, for example... + virtual void TabStripEmpty() { } +}; + +/////////////////////////////////////////////////////////////////////////////// +// +// TabStripModelDelegate +// +// A delegate interface that the TabStripModel uses to perform work that it +// can't do itself, such as obtain a container HWND for creating new +// TabContents, creating new TabStripModels for detached tabs, etc. +// +// This interface is typically implemented by the controller that instantiates +// the TabStripModel (in our case the Browser object). +// +/////////////////////////////////////////////////////////////////////////////// +class TabStripModelDelegate { + public: + // Ask for a new TabStripModel to be created and the given tab contents to + // be added to it. Its presentation (e.g. a browser window) anchored at the + // specified creation point. It is left up to the delegate to decide how to + // size the window. ass an empty point (0, 0) to allow the delegate to decide + // where to position the window. + virtual void CreateNewStripWithContents(TabContents* contents, + const gfx::Point& creation_point) = 0; + + enum { + TAB_MOVE_ACTION = 1, + TAB_TEAROFF_ACTION = 2 + }; + + // Determine what drag actions are possible for the specified strip. + virtual int GetDragActions() const = 0; + + // Creates an appropriate TabContents for the given URL. This is handled by + // the delegate since the TabContents may require special circumstances to + // exist for it to be constructed (e.g. a parent HWND). + // If |defer_load| is true, the navigation controller doesn't load the url. + // If |instance| is not null, its process is used to render the tab. + virtual TabContents* CreateTabContentsForURL( + const GURL& url, + Profile* profile, + PageTransition::Type transition, + bool defer_load, + SiteInstance* instance) const = 0; + + // Show the web application context menu at the provided point. |p| is in + // screen coordinate system. + virtual void ShowApplicationMenu(const gfx::Point p) = 0; + + // Return whether some contents can be duplicated. + virtual bool CanDuplicateContentsAt(int index) = 0; + + // Duplicate the contents at the provided index and places it into its own + // window. + virtual void DuplicateContentsAt(int index) = 0; + + // Called every time the the throbber needs to be updated. We have this to + // give the browser/frame a chance to implement some loading animation. This + // is used by simple web application frames. + virtual void ValidateLoadingAnimations() = 0; + + // Called when a drag session has completed and the frame that initiated the + // the session should be closed. + virtual void CloseFrameAfterDragSession() = 0; +}; + +//////////////////////////////////////////////////////////////////////////////// +// +// TabStripModel +// +// A model & low level controller of a Browser Window tabstrip. Holds a vector +// of TabContents, 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. +// +// 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 NotificationObserver { + public: + // Construct a TabStripModel with a delegate to help it do certain things + // (See TabStripModelDelegate documentation). + 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 TabContentses/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/set the active TabStripModelOrderController associated with this + // TabStripModel + TabStripModelOrderController* order_controller() const { + return order_controller_; + } + void SetOrderController(TabStripModelOrderController* order_controller); + + // Retrieve the index of the currently selected TabContents. + int selected_index() const { return selected_index_; } + + // See documentation for |next_selected_index_| below. + int next_selected_index() const { return next_selected_index_; } + + // 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_; } + + // Basic API ///////////////////////////////////////////////////////////////// + + static const int kNoTab = -1; + + // Determines if the specified index is contained within the TabStripModel. + bool ContainsIndex(int index) const; + + // Adds the specified TabContents in the default location. Tabs opened in the + // foreground inherit the group of the previously selected tab. + void AppendTabContents(TabContents* contents, bool foreground); + + // Adds the specified TabContents in the specified location. If + // |inherit_group| is true, the new contents is linked to the current tab's + // group. + void InsertTabContentsAt(int index, + TabContents* contents, + bool foreground, + bool inherit_group); + + // Closes the TabContents at the specified index. This causes the TabContents + // to be destroyed, but it may not happen immediately (e.g. if it's a + // WebContents). + void CloseTabContentsAt(int index) { + InternalCloseTabContentsAt(index, true); + } + + // Replaces the entire state of a the tab at index by switching in a + // different NavigationController. This is used through the recently + // closed tabs list, which needs to replace a tab's current state + // and history with another set of contents and history. + // + // The old NavigationController is deallocated and this object takes + // ownership of the passed in controller. + void ReplaceNavigationControllerAt(int index, + NavigationController* controller); + + // Detaches the TabContents at the specified index from this strip. The + // TabContents is not destroyed, just removed from display. The caller is + // responsible for doing something with it (e.g. stuffing it into another + // strip). + TabContents* DetachTabContentsAt(int index); + + // Select the TabContents at the specified index. |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 selected as a by-product of some other + // action. + void SelectTabContentsAt(int index, bool user_gesture); + + // Replace the TabContents at the specified index with another TabContents. + // This is used when a navigation causes a different TabContentsType to be + // required, e.g. the transition from New Tab to a web page. + void ReplaceTabContentsAt(int index, TabContents* replacement_contents); + + // Move the TabContents at the specified index to another index. This method + // does NOT send Detached/Attached notifications, rather it moves the + // TabContents inline and sends a Moved notification instead. + void MoveTabContentsAt(int index, int to_position); + + // Returns the currently selected TabContents, or NULL if there is none. + TabContents* GetSelectedTabContents() const; + + // Returns the TabContents at the specified index, or NULL if there is none. + TabContents* GetTabContentsAt(int index) const; + + // Returns the index of the specified TabContents, or -1 if the TabContents + // is not in this TabStripModel. + int GetIndexOfTabContents(const TabContents* contents) const; + + // Returns the index of the specified NavigationController, or -1 if it is + // not in this TabStripModel. + int GetIndexOfController(const NavigationController* controller) const; + + // Notify any observers that the TabContents at the specified index has + // changed in some way. + void UpdateTabContentsStateAt(int index); + + // Notify any observers that Loading progress for TabContents should be + // validated. + // TODO(beng): (Cleanup) This should definitely be moved to the View. + void UpdateTabContentsLoadingAnimations(); + + // Make sure there is an auto-generated New Tab tab in the TabStripModel. + // If |force_create| is true, the New Tab will be created even if the + // preference is set to false (used by startup). + void EnsureNewTabVisible(bool force_create); + + // 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 TabContents that are currently loading. + bool TabsAreLoading() const; + + // Whether the tab has a beforeunload/unload listener that needs firing before + // being closed. + bool TabHasUnloadListener(int index); + + // Returns the controller controller that opened the TabContents at |index|. + NavigationController* GetOpenerOfTabContentsAt(int index); + + // Returns the index of the next TabContents in the sequence of TabContentses + // spawned by the specified NavigationController 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 GetIndexOfNextTabContentsOpenedBy(NavigationController* opener, + int start_index, + bool use_group); + + // Returns the index of the last TabContents in the model opened by the + // specified opener, starting at |start_index|. + int GetIndexOfLastTabContentsOpenedBy(NavigationController* opener, + int start_index); + + // 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 TabContents. This should be + // called when a TabContents 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). + void ForgetGroup(TabContents* contents); + + // Command level API ///////////////////////////////////////////////////////// + + // Adds a blank tab to the TabStripModel. + TabContents* AddBlankTab(bool foreground); + TabContents* AddBlankTabAt(int index, bool foreground); + + // Adds a TabContents at the best position in the TabStripModel given the + // specified insertion index, transition, etc. Ultimately, the insertion + // index of the TabContents is left up to the Order Controller associated + // with this TabStripModel, so the final insertion index may differ from + // |index|. + void AddTabContents(TabContents* contents, + int index, + PageTransition::Type transition, + bool foreground); + + // Closes the selected TabContents. + void CloseSelectedTab(); + + // Select adjacent tabs + void SelectNextTab(); + void SelectPreviousTab(); + + // Selects the last tab in the tab strip. + void SelectLastTab(); + + // View API ////////////////////////////////////////////////////////////////// + + // The specified contents should be opened in a new tabstrip. + void TearOffTabContents(TabContents* detached_contents, + const gfx::Point& drop_point); + + // Context menu functions. + enum ContextMenuCommand { + CommandFirst = 0, + CommandNewTab, + CommandReload, + CommandDuplicate, + CommandCloseTab, + CommandCloseOtherTabs, + CommandCloseTabsToRight, + CommandCloseTabsOpenedBy, + CommandLast + }; + + // Returns true if the specified command is enabled. + bool IsContextMenuCommandEnabled(int context_index, + ContextMenuCommand command_id); + + // Performs the action associated with the specified command for the given + // TabStripModel index |context_index|. + void ExecuteContextMenuCommand(int context_index, + ContextMenuCommand command_id); + + // Overridden from notificationObserver: + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + private: + // We cannot be constructed without a delegate. + TabStripModel(); + + // Closes the TabContents at the specified index. This causes the TabContents + // to be destroyed, but it may not happen immediately (e.g. if it's a + // WebContents). If the page in question has an unload event the TabContents + // will not be destroyed until after the event has completed, which will then + // call back into this method. + // + // The boolean parameter create_historical_tab controls whether to + // record this tab and its history for reopening recently closed + // tabs. + void InternalCloseTabContentsAt(int index, bool create_historical_tab); + + TabContents* GetContentsAt(int index) const; + + // The actual implementation of SelectTabContentsAt. Takes the previously + // selected contents in |old_contents|, which may actually not be in + // |contents_| anymore because it may have been removed by a call to say + // DetachTabContentsAt... + void ChangeSelectedContentsFrom( + TabContents* old_contents, int to_index, bool user_gesture); + + // Returns the number of New Tab tabs in the TabStripModel. + int GetNewTabCount() const; + + // Convenience for setting the opener pointer for the specified |contents| to + // be |opener|'s NavigationController. + void SetOpenerForContents(TabContents* contents, TabContents* opener); + + // Returns true if closing the tab should add it to TabRestoreService. This + // returns true only if the profile has a TabRestoreService and the browser + // type is TABBED_BROWSER. + bool ShouldAddToTabRestoreService(TabContents* contents); + + // 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. + struct TabContentsData; + static bool OpenerMatches(TabContentsData* data, + NavigationController* opener, + bool use_group); + + // Our delegate. + TabStripModelDelegate* delegate_; + + // A hunk of data representing a TabContents and (optionally) the + // NavigationController that spawned it. This memory only sticks around while + // the TabContents is in the current TabStripModel, unless otherwise + // specified in code. + struct TabContentsData { + TabContents* contents; + // We use NavigationControllers here since they more closely model the + // "identity" of a Tab, TabContents can change depending on the URL loaded + // in the Tab. + // The group is used to model a set of tabs spawned from a single parent + // tab. This value is preserved for a given tab as long as the tab remains + // navigated to the link it was initially opened at or some navigation from + // that page (i.e. if the user types or visits a bookmark or some other + // navigation within that tab, the group relationship is lost). This + // property can safely be used to implement features that depend on a + // logical group of related tabs. + NavigationController* group; + // The owner models the same relationship as group, except it is more + // easily discarded, e.g. when the user switches to a tab not part of the + // same group. This property is used to determine what tab to select next + // when one is closed. + NavigationController* opener; + explicit TabContentsData(TabContents* a_contents) + : contents(a_contents) { + SetGroup(NULL); + } + + // Create a relationship between this TabContents and other TabContentses. + // Used to identify which TabContents to select next after one is closed. + void SetGroup(NavigationController* a_group) { + group = a_group; + opener = a_group; + } + + // Forget the opener relationship so that when this TabContents is closed + // unpredictable re-selection does not occur. + void ForgetOpener() { + opener = NULL; + } + }; + + // The TabContents data currently hosted within this TabStripModel. + typedef std::vector<TabContentsData*> TabContentsDataVector; + TabContentsDataVector contents_data_; + + // The index of the TabContents in |contents_| that is currently selected. + int selected_index_; + + // The index of the TabContnets in |contents_| that will be selected when the + // current composite operation completes. A Tab Detach is an example of a + // composite operation - it not only removes a tab from the strip, but also + // causes the selection to shift. Some code needs to know what the next + // selected index will be. In other cases, this value is equal to + // selected_index_. + int next_selected_index_; + + // A profile associated with this TabStripModel, used when creating new Tabs. + 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. + TabStripModelOrderController* order_controller_; + + // Our observers. + typedef ObserverList<TabStripModelObserver> TabStripModelObservers; + TabStripModelObservers observers_; + + DISALLOW_EVIL_CONSTRUCTORS(TabStripModel); +}; + +#endif // CHROME_BROWSER_TABS_TAB_STRIP_MODEL_H__ |