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