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