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