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 <stddef.h> 9 #include <stdint.h> 10 11 #include <map> 12 #include <memory> 13 #include <vector> 14 15 #include "base/containers/span.h" 16 #include "base/gtest_prod_util.h" 17 #include "base/macros.h" 18 #include "base/memory/weak_ptr.h" 19 #include "base/observer_list.h" 20 #include "base/optional.h" 21 #include "base/scoped_observer.h" 22 #include "base/strings/string16.h" 23 #include "base/time/time.h" 24 #include "base/timer/timer.h" 25 #include "build/build_config.h" 26 #include "chrome/browser/ui/tabs/tab_group_controller.h" 27 #include "chrome/browser/ui/tabs/tab_strip_model_order_controller.h" 28 #include "chrome/browser/ui/tabs/tab_switch_event_latency_recorder.h" 29 #include "components/tab_groups/tab_group_id.h" 30 #include "components/tab_groups/tab_group_visual_data.h" 31 #include "ui/base/models/list_selection_model.h" 32 #include "ui/base/page_transition_types.h" 33 34 #if defined(OS_ANDROID) 35 #error This file should only be included on desktop. 36 #endif 37 38 class Profile; 39 class TabGroupModel; 40 class TabStripModelDelegate; 41 class TabStripModelObserver; 42 43 namespace content { 44 class WebContents; 45 } 46 47 //////////////////////////////////////////////////////////////////////////////// 48 // 49 // TabStripModel 50 // 51 // A model & low level controller of a Browser Window tabstrip. Holds a vector 52 // of WebContentses, and provides an API for adding, removing and 53 // shuffling them, as well as a higher level API for doing specific Browser- 54 // related tasks like adding new Tabs from just a URL, etc. 55 // 56 // Each tab may be pinned. Pinned tabs are locked to the left side of the tab 57 // strip and rendered differently (small tabs with only a favicon). The model 58 // makes sure all pinned tabs are at the beginning of the tab strip. For 59 // example, if a non-pinned tab is added it is forced to be with non-pinned 60 // tabs. Requests to move tabs outside the range of the tab type are ignored. 61 // For example, a request to move a pinned tab after non-pinned tabs is ignored. 62 // 63 // A TabStripModel has one delegate that it relies on to perform certain tasks 64 // like creating new TabStripModels (probably hosted in Browser windows) when 65 // required. See TabStripDelegate above for more information. 66 // 67 // A TabStripModel also has N observers (see TabStripModelObserver above), 68 // which can be registered via Add/RemoveObserver. An Observer is notified of 69 // tab creations, removals, moves, and other interesting events. The 70 // TabStrip implements this interface to know when to create new tabs in 71 // the View, and the Browser object likewise implements to be able to update 72 // its bookkeeping when such events happen. 73 // 74 // This implementation of TabStripModel is not thread-safe and should only be 75 // accessed on the UI thread. 76 // 77 //////////////////////////////////////////////////////////////////////////////// 78 class TabStripModel : public TabGroupController { 79 public: 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 // WebContents::SetClosedByUserGesture(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 tab. 96 ADD_NONE = 0, 97 98 // The tab should be active. 99 ADD_ACTIVE = 1 << 0, 100 101 // The tab should be pinned. 102 ADD_PINNED = 1 << 1, 103 104 // If not set the insertion index of the WebContents is left up to the Order 105 // Controller associated, so the final insertion index may differ from the 106 // specified index. Otherwise the index supplied is used. 107 ADD_FORCE_INDEX = 1 << 2, 108 109 // If set the newly inserted tab's opener is set to the active tab. If not 110 // set the tab may still inherit the opener under certain situations. 111 ADD_INHERIT_OPENER = 1 << 3, 112 }; 113 114 // Enumerates different ways to open a new tab. Does not apply to opening 115 // existing links or searches in a new tab, only to brand new empty tabs. 116 // KEEP IN SYNC WITH THE NewTabType ENUM IN enums.xml. 117 // NEW VALUES MUST BE APPENDED AND AVOID CHANGING ANY PRE-EXISTING VALUES. 118 enum NewTab { 119 // New tab was opened using the new tab button on the tab strip. 120 NEW_TAB_BUTTON = 0, 121 122 // New tab was opened using the menu command - either through the keyboard 123 // shortcut, or by opening the menu and selecting the command. Applies to 124 // both app menu and the menu bar's File menu (on platforms that have one). 125 NEW_TAB_COMMAND = 1, 126 127 // New tab was opened through the context menu on the tab strip. 128 NEW_TAB_CONTEXT_MENU = 2, 129 130 // New tab was opened through the new tab button in the toolbar for the 131 // WebUI touch-optimized tab strip. 132 NEW_TAB_BUTTON_IN_TOOLBAR_FOR_TOUCH = 3, 133 134 // New tab was opened through the new tab button inside of the WebUI tab 135 // strip. 136 NEW_TAB_BUTTON_IN_WEBUI_TAB_STRIP = 4, 137 138 // Number of enum entries, used for UMA histogram reporting macros. 139 NEW_TAB_ENUM_COUNT = 5, 140 }; 141 142 static constexpr int kNoTab = -1; 143 144 // Construct a TabStripModel with a delegate to help it do certain things 145 // (see the TabStripModelDelegate documentation). |delegate| cannot be NULL. 146 explicit TabStripModel(TabStripModelDelegate* delegate, Profile* profile); 147 ~TabStripModel() override; 148 149 // Retrieves the TabStripModelDelegate associated with this TabStripModel. delegate()150 TabStripModelDelegate* delegate() const { return delegate_; } 151 152 // Sets the TabStripModelObserver used by the UI showing the tabs. As other 153 // observers may query the UI for state, the UI's observer must be first. 154 void SetTabStripUI(TabStripModelObserver* observer); 155 156 // Add and remove observers to changes within this TabStripModel. 157 void AddObserver(TabStripModelObserver* observer); 158 void RemoveObserver(TabStripModelObserver* observer); 159 160 // Retrieve the number of WebContentses/emptiness of the TabStripModel. count()161 int count() const { return static_cast<int>(contents_data_.size()); } empty()162 bool empty() const { return contents_data_.empty(); } 163 164 // Retrieve the Profile associated with this TabStripModel. profile()165 Profile* profile() const { return profile_; } 166 167 // Retrieve the index of the currently active WebContents. This will be 168 // ui::ListSelectionModel::kUnselectedIndex if no tab is currently selected 169 // (this happens while the tab strip is being initialized or is empty). active_index()170 int active_index() const { return selection_model_.active(); } 171 172 // Returns true if the tabstrip is currently closing all open tabs (via a 173 // call to CloseAllTabs). As tabs close, the selection in the tabstrip 174 // changes which notifies observers, which can use this as an optimization to 175 // avoid doing meaningless or unhelpful work. closing_all()176 bool closing_all() const { return closing_all_; } 177 178 // Basic API ///////////////////////////////////////////////////////////////// 179 180 // Determines if the specified index is contained within the TabStripModel. 181 bool ContainsIndex(int index) const; 182 183 // Adds the specified WebContents in the default location. Tabs opened 184 // in the foreground inherit the opener of the previously active tab. 185 void AppendWebContents(std::unique_ptr<content::WebContents> contents, 186 bool foreground); 187 188 // Adds the specified WebContents at the specified location. 189 // |add_types| is a bitmask of AddTabTypes; see it for details. 190 // 191 // All append/insert methods end up in this method. 192 // 193 // NOTE: adding a tab using this method does NOT query the order controller, 194 // as such the ADD_FORCE_INDEX AddTabTypes is meaningless here. The only time 195 // the |index| is changed is if using the index would result in breaking the 196 // constraint that all pinned tabs occur before non-pinned tabs. It returns 197 // the index the web contents is actually inserted to. See also 198 // AddWebContents. 199 int InsertWebContentsAt( 200 int index, 201 std::unique_ptr<content::WebContents> contents, 202 int add_types, 203 base::Optional<tab_groups::TabGroupId> group = base::nullopt); 204 // Closes the WebContents at the specified index. This causes the 205 // WebContents to be destroyed, but it may not happen immediately. 206 // |close_types| is a bitmask of CloseTypes. Returns true if the 207 // WebContents was closed immediately, false if it was not closed (we 208 // may be waiting for a response from an onunload handler, or waiting for the 209 // user to confirm closure). 210 bool CloseWebContentsAt(int index, uint32_t close_types); 211 212 // Replaces the WebContents at |index| with |new_contents|. The 213 // WebContents that was at |index| is returned and its ownership returns 214 // to the caller. 215 std::unique_ptr<content::WebContents> ReplaceWebContentsAt( 216 int index, 217 std::unique_ptr<content::WebContents> new_contents); 218 219 // Detaches the WebContents at the specified index from this strip. The 220 // WebContents is not destroyed, just removed from display. The caller 221 // is responsible for doing something with it (e.g. stuffing it into another 222 // strip). Returns the detached WebContents. 223 std::unique_ptr<content::WebContents> DetachWebContentsAt(int index); 224 225 // User gesture type that triggers ActivateTabAt. kNone indicates that it was 226 // not triggered by a user gesture, but by a by-product of some other action. 227 enum class GestureType { 228 kMouse, 229 kTouch, 230 kWheel, 231 kKeyboard, 232 kOther, 233 kTabMenu, 234 kNone 235 }; 236 237 // Encapsulates user gesture information for tab activation 238 struct UserGestureDetails { 239 UserGestureDetails(GestureType type, 240 base::TimeTicks time_stamp = base::TimeTicks::Now()) typeUserGestureDetails241 : type(type), time_stamp(time_stamp) {} 242 243 GestureType type; 244 base::TimeTicks time_stamp; 245 }; 246 247 // Makes the tab at the specified index the active tab. |gesture_detail.type| 248 // contains the gesture type that triggers the tab activation. 249 // |gesture_detail.time_stamp| contains the timestamp of the user gesture, if 250 // any. 251 void ActivateTabAt(int index, 252 UserGestureDetails gesture_detail = 253 UserGestureDetails(GestureType::kNone)); 254 255 // Report histogram metrics for the number of tabs 'scrubbed' within a given 256 // interval of time. Scrubbing is considered to be a tab activated for <= 1.5 257 // seconds for this metric. 258 void RecordTabScrubbingMetrics(); 259 260 // Move the WebContents at the specified index to another index. This 261 // method does NOT send Detached/Attached notifications, rather it moves the 262 // WebContents inline and sends a Moved notification instead. 263 // EnsureGroupContiguity() is called after the move, so this will never result 264 // in non-contiguous group (though the moved tab's group may change). 265 // If |select_after_move| is false, whatever tab was selected before the move 266 // will still be selected, but its index may have incremented or decremented 267 // one slot. It returns the index the web contents is actually moved to. 268 int MoveWebContentsAt(int index, int to_position, bool select_after_move); 269 270 // Moves the selected tabs to |index|. |index| is treated as if the tab strip 271 // did not contain any of the selected tabs. For example, if the tabstrip 272 // contains [A b c D E f] (upper case selected) and this is invoked with 1 the 273 // result is [b A D E c f]. 274 // This method maintains that all pinned tabs occur before non-pinned tabs. 275 // When pinned tabs are selected the move is processed in two chunks: first 276 // pinned tabs are moved, then non-pinned tabs are moved. If the index is 277 // after (pinned-tab-count - selected-pinned-tab-count), then the index the 278 // non-pinned selected tabs are moved to is (index + 279 // selected-pinned-tab-count). For example, if the model consists of 280 // [A b c D E f] (A b c are pinned) and this is invoked with 2, the result is 281 // [b c A D E f]. In this example nothing special happened because the target 282 // index was <= (pinned-tab-count - selected-pinned-tab-count). If the target 283 // index were 3, then the result would be [b c A f D F]. A, being pinned, can 284 // move no further than index 2. The non-pinned tabs are moved to the target 285 // index + selected-pinned tab-count (3 + 1). 286 void MoveSelectedTabsTo(int index); 287 288 // Moves all tabs in |group| to |to_index|. This has no checks to make sure 289 // the position is valid for a group to move to. 290 void MoveGroupTo(const tab_groups::TabGroupId& group, int to_index); 291 292 // Returns the currently active WebContents, or NULL if there is none. 293 content::WebContents* GetActiveWebContents() const; 294 295 // Returns the WebContents at the specified index, or NULL if there is 296 // none. 297 content::WebContents* GetWebContentsAt(int index) const override; 298 299 // Returns the index of the specified WebContents, or TabStripModel::kNoTab 300 // if the WebContents is not in this TabStripModel. 301 int GetIndexOfWebContents(const content::WebContents* contents) const; 302 303 // Notify any observers that the WebContents at the specified index has 304 // changed in some way. See TabChangeType for details of |change_type|. 305 void UpdateWebContentsStateAt(int index, TabChangeType change_type); 306 307 // Cause a tab to display a UI indication to the user that it needs their 308 // attention. 309 void SetTabNeedsAttentionAt(int index, bool attention); 310 311 // Close all tabs at once. Code can use closing_all() above to defer 312 // operations that might otherwise by invoked by the flurry of detach/select 313 // notifications this method causes. 314 void CloseAllTabs(); 315 316 // Returns true if there are any WebContentses that are currently loading. 317 bool TabsAreLoading() const; 318 319 // Returns the WebContents that opened the WebContents at |index|, or NULL if 320 // there is no opener on record. 321 content::WebContents* GetOpenerOfWebContentsAt(int index); 322 323 // Changes the |opener| of the WebContents at |index|. 324 // Note: |opener| must be in this tab strip. Also a tab must not be its own 325 // opener. 326 void SetOpenerOfWebContentsAt(int index, content::WebContents* opener); 327 328 // Returns the index of the last WebContents in the model opened by the 329 // specified opener, starting at |start_index|. 330 int GetIndexOfLastWebContentsOpenedBy(const content::WebContents* opener, 331 int start_index) const; 332 333 // To be called when a navigation is about to occur in the specified 334 // WebContents. Depending on the tab, and the transition type of the 335 // navigation, the TabStripModel may adjust its selection behavior and opener 336 // inheritance. 337 void TabNavigating(content::WebContents* contents, 338 ui::PageTransition transition); 339 340 // Changes the blocked state of the tab at |index|. 341 void SetTabBlocked(int index, bool blocked); 342 343 // Changes the pinned state of the tab at |index|. See description above 344 // class for details on this. 345 void SetTabPinned(int index, bool pinned); 346 347 // Returns true if the tab at |index| is pinned. 348 // See description above class for details on pinned tabs. 349 bool IsTabPinned(int index) const; 350 351 bool IsTabCollapsed(int index) const; 352 353 bool IsGroupCollapsed(const tab_groups::TabGroupId& group) const; 354 355 // Returns true if the tab at |index| is blocked by a tab modal dialog. 356 bool IsTabBlocked(int index) const; 357 358 // Returns the group that contains the tab at |index|, or nullopt if the tab 359 // index is invalid or not grouped. 360 base::Optional<tab_groups::TabGroupId> GetTabGroupForTab( 361 int index) const override; 362 363 // If a tab inserted at |index| would be within a tab group, return that 364 // group's ID. Otherwise, return nullopt. If |index| points to the first tab 365 // in a group, it will return nullopt since a new tab would be either between 366 // two different groups or just after a non-grouped tab. 367 base::Optional<tab_groups::TabGroupId> GetSurroundingTabGroup( 368 int index) const; 369 370 // Returns the index of the first tab that is not a pinned tab. This returns 371 // |count()| if all of the tabs are pinned tabs, and 0 if none of the tabs are 372 // pinned tabs. 373 int IndexOfFirstNonPinnedTab() const; 374 375 // Extends the selection from the anchor to |index|. 376 void ExtendSelectionTo(int index); 377 378 // Toggles the selection at |index|. This does nothing if |index| is selected 379 // and there are no other selected tabs. 380 void ToggleSelectionAt(int index); 381 382 // Makes sure the tabs from the anchor to |index| are selected. This only 383 // adds to the selection. 384 void AddSelectionFromAnchorTo(int index); 385 386 // Returns true if the tab at |index| is selected. 387 bool IsTabSelected(int index) const; 388 389 // Sets the selection to match that of |source|. 390 void SetSelectionFromModel(ui::ListSelectionModel source); 391 392 const ui::ListSelectionModel& selection_model() const; 393 394 // Command level API ///////////////////////////////////////////////////////// 395 396 // Adds a WebContents at the best position in the TabStripModel given 397 // the specified insertion index, transition, etc. |add_types| is a bitmask of 398 // AddTabTypes; see it for details. This method ends up calling into 399 // InsertWebContentsAt to do the actual insertion. Pass kNoTab for |index| to 400 // append the contents to the end of the tab strip. 401 void AddWebContents( 402 std::unique_ptr<content::WebContents> contents, 403 int index, 404 ui::PageTransition transition, 405 int add_types, 406 base::Optional<tab_groups::TabGroupId> group = base::nullopt); 407 408 // Closes the selected tabs. 409 void CloseSelectedTabs(); 410 411 // Select adjacent tabs 412 void SelectNextTab( 413 UserGestureDetails detail = UserGestureDetails(GestureType::kOther)); 414 void SelectPreviousTab( 415 UserGestureDetails detail = UserGestureDetails(GestureType::kOther)); 416 417 // Selects the last tab in the tab strip. 418 void SelectLastTab( 419 UserGestureDetails detail = UserGestureDetails(GestureType::kOther)); 420 421 // Moves the active in the specified direction. Respects group boundaries. 422 void MoveTabNext(); 423 void MoveTabPrevious(); 424 425 // Create a new tab group and add the set of tabs pointed to be |indices| to 426 // it. Pins all of the tabs if any of them were pinned, and reorders the tabs 427 // so they are contiguous and do not split an existing group in half. Returns 428 // the new group. |indices| must be sorted in ascending order. 429 tab_groups::TabGroupId AddToNewGroup(const std::vector<int>& indices); 430 431 // Add the set of tabs pointed to by |indices| to the given tab group |group|. 432 // The tabs take on the pinnedness of the tabs already in the group, and are 433 // moved to immediately follow the tabs already in the group. |indices| must 434 // be sorted in ascending order. 435 void AddToExistingGroup(const std::vector<int>& indices, 436 const tab_groups::TabGroupId& group); 437 438 // Moves the set of tabs indicated by |indices| to precede the tab at index 439 // |destination_index|, maintaining their order and the order of tabs not 440 // being moved, and adds them to the tab group |group|. 441 void MoveTabsAndSetGroup(const std::vector<int>& indices, 442 int destination_index, 443 base::Optional<tab_groups::TabGroupId> group); 444 445 // Similar to AddToExistingGroup(), but creates a group with id |group| if it 446 // doesn't exist. This is only intended to be called from session restore 447 // code. 448 void AddToGroupForRestore(const std::vector<int>& indices, 449 const tab_groups::TabGroupId& group); 450 451 // Updates the tab group of the tab at |index|. If |group| is nullopt, the tab 452 // will be removed from the current group. If |group| does not exist, it will 453 // create the group then add the tab to the group. 454 void UpdateGroupForDragRevert( 455 int index, 456 base::Optional<tab_groups::TabGroupId> group_id, 457 base::Optional<tab_groups::TabGroupVisualData> group_data); 458 459 // Removes the set of tabs pointed to by |indices| from the the groups they 460 // are in, if any. The tabs are moved out of the group if necessary. |indices| 461 // must be sorted in ascending order. 462 void RemoveFromGroup(const std::vector<int>& indices); 463 group_model()464 TabGroupModel* group_model() const { return group_model_.get(); } 465 466 // Returns true if one or more of the tabs pointed to by |indices| are 467 // supported by read later. 468 bool IsReadLaterSupportedForAny(const std::vector<int> indices); 469 470 // Saves tabs with url supported by Read Later. 471 void AddToReadLater(const std::vector<int>& indices); 472 473 // TabGroupController: 474 void CreateTabGroup(const tab_groups::TabGroupId& group) override; 475 void OpenTabGroupEditor(const tab_groups::TabGroupId& group) override; 476 void ChangeTabGroupContents(const tab_groups::TabGroupId& group) override; 477 void ChangeTabGroupVisuals(const tab_groups::TabGroupId& group) override; 478 void MoveTabGroup(const tab_groups::TabGroupId& group) override; 479 void CloseTabGroup(const tab_groups::TabGroupId& group) override; 480 // The same as count(), but overridden for TabGroup to access. 481 int GetTabCount() const override; 482 483 // View API ////////////////////////////////////////////////////////////////// 484 485 // Context menu functions. Tab groups uses command ids following CommandLast 486 // for entries in the 'Add to existing group' submenu. 487 enum ContextMenuCommand { 488 CommandFirst, 489 CommandNewTabToRight, 490 CommandReload, 491 CommandDuplicate, 492 CommandCloseTab, 493 CommandCloseOtherTabs, 494 CommandCloseTabsToRight, 495 CommandTogglePinned, 496 CommandToggleGrouped, 497 CommandFocusMode, 498 CommandToggleSiteMuted, 499 CommandSendTabToSelf, 500 CommandSendTabToSelfSingleTarget, 501 CommandAddToReadLater, 502 CommandAddToNewGroup, 503 CommandAddToExistingGroup, 504 CommandRemoveFromGroup, 505 CommandMoveToExistingWindow, 506 CommandMoveTabsToNewWindow, 507 CommandLast 508 }; 509 510 // Returns true if the specified command is enabled. If |context_index| is 511 // selected the response applies to all selected tabs. 512 bool IsContextMenuCommandEnabled(int context_index, 513 ContextMenuCommand command_id) const; 514 515 // Performs the action associated with the specified command for the given 516 // TabStripModel index |context_index|. If |context_index| is selected the 517 // command applies to all selected tabs. 518 void ExecuteContextMenuCommand(int context_index, 519 ContextMenuCommand command_id); 520 521 // Adds the tab at |context_index| to the given tab group |group|. If 522 // |context_index| is selected the command applies to all selected tabs. 523 void ExecuteAddToExistingGroupCommand(int context_index, 524 const tab_groups::TabGroupId& group); 525 526 // Adds the tab at |context_index| to the browser window at |browser_index|. 527 // If |context_index| is selected the command applies to all selected tabs. 528 void ExecuteAddToExistingWindowCommand(int context_index, int browser_index); 529 530 // Get the list of existing windows that tabs can be moved to. 531 std::vector<base::string16> GetExistingWindowsForMoveMenu(); 532 533 // Returns true if 'CommandToggleSiteMuted' will mute. |index| is the 534 // index supplied to |ExecuteContextMenuCommand|. 535 bool WillContextMenuMuteSites(int index); 536 537 // Returns true if 'CommandTogglePinned' will pin. |index| is the index 538 // supplied to |ExecuteContextMenuCommand|. 539 bool WillContextMenuPin(int index); 540 541 // Returns true if 'CommandToggleGrouped' will group. |index| is the index 542 // supplied to |ExecuteContextMenuCommand|. 543 bool WillContextMenuGroup(int index); 544 545 // Convert a ContextMenuCommand into a browser command. Returns true if a 546 // corresponding browser command exists, false otherwise. 547 static bool ContextMenuCommandToBrowserCommand(int cmd_id, int* browser_cmd); 548 549 // Access the order controller. Exposed only for unit tests. order_controller()550 TabStripModelOrderController* order_controller() const { 551 return order_controller_.get(); 552 } 553 554 // Returns the index of the next WebContents in the sequence of WebContentses 555 // spawned by the specified WebContents after |start_index|. 556 int GetIndexOfNextWebContentsOpenedBy(const content::WebContents* opener, 557 int start_index) const; 558 559 // Finds the next available tab to switch to as the active tab starting at 560 // |index|. This method will check the indices to the right of |index| before 561 // checking the indices to the left of |index|. |index| cannot be returned. 562 // |collapsing_group| is optional and used in cases where the group is 563 // collapsing but not yet reflected in the model. Returns base::nullopt if 564 // there are no valid tabs. 565 base::Optional<int> GetNextExpandedActiveTab( 566 int index, 567 base::Optional<tab_groups::TabGroupId> collapsing_group) const; 568 569 // Forget all opener relationships, to reduce unpredictable tab switching 570 // behavior in complex session states. The exact circumstances under which 571 // this method is called are left up to TabStripModelOrderController. 572 void ForgetAllOpeners(); 573 574 // Forgets the opener relationship of the specified WebContents. 575 void ForgetOpener(content::WebContents* contents); 576 577 // Returns true if the opener relationships present for |contents| should be 578 // reset when _any_ active tab change occurs (rather than just one outside the 579 // current tree of openers). 580 bool ShouldResetOpenerOnActiveTabChange(content::WebContents* contents) const; 581 582 private: 583 FRIEND_TEST_ALL_PREFIXES(TabStripModelTest, GetIndicesClosedByCommand); 584 585 class WebContentsData; 586 struct DetachedWebContents; 587 struct DetachNotifications; 588 589 // Performs all the work to detach a WebContents instance but avoids sending 590 // most notifications. TabClosingAt() and TabDetachedAt() are sent because 591 // observers are reliant on the selection model being accurate at the time 592 // that TabDetachedAt() is called. 593 std::unique_ptr<content::WebContents> DetachWebContentsImpl( 594 int index, 595 bool create_historical_tab, 596 bool will_delete); 597 598 // We batch send notifications. This has two benefits: 599 // 1) This allows us to send the minimal number of necessary notifications. 600 // This is important because some notifications cause the main thread to 601 // synchronously communicate with the GPU process and cause jank. 602 // https://crbug.com/826287. 603 // 2) This allows us to avoid some problems caused by re-entrancy [e.g. 604 // using destroyed WebContents instances]. Ideally, this second check 605 // wouldn't be necessary because we would enforce that there is no 606 // re-entrancy in the TabStripModel, but that condition is currently 607 // violated in tests [and possibly in the wild as well]. 608 void SendDetachWebContentsNotifications(DetachNotifications* notifications); 609 610 bool RunUnloadListenerBeforeClosing(content::WebContents* contents); 611 bool ShouldRunUnloadListenerBeforeClosing(content::WebContents* contents); 612 613 int ConstrainInsertionIndex(int index, bool pinned_tab) const; 614 615 int ConstrainMoveIndex(int index, bool pinned_tab) const; 616 617 // If |index| is selected all the selected indices are returned, otherwise a 618 // vector with |index| is returned. This is used when executing commands to 619 // determine which indices the command applies to. Indices are sorted in 620 // increasing order. 621 std::vector<int> GetIndicesForCommand(int index) const; 622 623 // Returns a vector of indices of the tabs that will close when executing the 624 // command |id| for the tab at |index|. The returned indices are sorted in 625 // descending order. 626 std::vector<int> GetIndicesClosedByCommand(int index, 627 ContextMenuCommand id) const; 628 629 // Returns true if the specified WebContents is a New Tab at the end of 630 // the tabstrip. We check for this because opener relationships are _not_ 631 // forgotten for the New Tab page opened as a result of a New Tab gesture 632 // (e.g. Ctrl+T, etc) since the user may open a tab transiently to look up 633 // something related to their current activity. 634 bool IsNewTabAtEndOfTabStrip(content::WebContents* contents) const; 635 636 // Adds the specified WebContents at the specified location. 637 // |add_types| is a bitmask of AddTabTypes; see it for details. 638 // 639 // All append/insert methods end up in this method. 640 // 641 // NOTE: adding a tab using this method does NOT query the order controller, 642 // as such the ADD_FORCE_INDEX AddTabTypes is meaningless here. The only time 643 // the |index| is changed is if using the index would result in breaking the 644 // constraint that all pinned tabs occur before non-pinned tabs. It returns 645 // the index the web contents is actually inserted to. See also 646 // AddWebContents. 647 int InsertWebContentsAtImpl(int index, 648 std::unique_ptr<content::WebContents> contents, 649 int add_types, 650 base::Optional<tab_groups::TabGroupId> group); 651 652 // Closes the WebContentses at the specified indices. This causes the 653 // WebContentses to be destroyed, but it may not happen immediately. If 654 // the page in question has an unload event the WebContents will not be 655 // destroyed until after the event has completed, which will then call back 656 // into this method. 657 // 658 // Returns true if the WebContentses were closed immediately, false if we 659 // are waiting for the result of an onunload handler. 660 bool InternalCloseTabs(base::span<content::WebContents* const> items, 661 uint32_t close_types); 662 663 // |close_types| is a bitmask of the types in CloseTypes. 664 // Returns true if all the tabs have been deleted. A return value of false 665 // means some portion (potentially none) of the WebContents were deleted. 666 // WebContents not deleted by this function are processing unload handlers 667 // which may eventually be deleted based on the results of the unload handler. 668 // Additionally processing the unload handlers may result in needing to show 669 // UI for the WebContents. See UnloadController for details on how unload 670 // handlers are processed. 671 bool CloseWebContentses(base::span<content::WebContents* const> items, 672 uint32_t close_types); 673 674 // Gets the WebContents at an index. Does no bounds checking. 675 content::WebContents* GetWebContentsAtImpl(int index) const; 676 677 // Returns the WebContentses at the specified indices. This does no checking 678 // of the indices, it is assumed they are valid. 679 std::vector<content::WebContents*> GetWebContentsesByIndices( 680 const std::vector<int>& indices); 681 682 // Sets the selection to |new_model| and notifies any observers. 683 // Note: This function might end up sending 0 to 3 notifications in the 684 // following order: TabDeactivated, ActiveTabChanged, TabSelectionChanged. 685 // |selection| will be filled with information corresponding to 3 notification 686 // above. When it's |triggered_by_other_operation|, This won't notify 687 // observers that selection was changed. Callers should notify it by 688 // themselves. 689 TabStripSelectionChange SetSelection( 690 ui::ListSelectionModel new_model, 691 TabStripModelObserver::ChangeReason reason, 692 bool triggered_by_other_operation); 693 694 // Selects either the next tab (|forward| is true), or the previous tab 695 // (|forward| is false). 696 void SelectRelativeTab(bool forward, UserGestureDetails detail); 697 698 // Moves the active tabs into the next slot (|forward| is true), or the 699 // previous slot (|forward| is false). Respects group boundaries and creates 700 // movement slots into and out of groups. 701 void MoveTabRelative(bool forward); 702 703 // Does the work of MoveWebContentsAt. This has no checks to make sure the 704 // position is valid, those are done in MoveWebContentsAt. 705 void MoveWebContentsAtImpl(int index, 706 int to_position, 707 bool select_after_move); 708 709 // Implementation of MoveSelectedTabsTo. Moves |length| of the selected tabs 710 // starting at |start| to |index|. See MoveSelectedTabsTo for more details. 711 void MoveSelectedTabsToImpl(int index, size_t start, size_t length); 712 713 // Adds tabs to newly-allocated group id |new_group|. This group must be new 714 // and have no tabs in it. 715 void AddToNewGroupImpl(const std::vector<int>& indices, 716 const tab_groups::TabGroupId& new_group); 717 718 // Adds tabs to existing group |group|. This group must have been initialized 719 // by a previous call to |AddToNewGroupImpl()|. 720 void AddToExistingGroupImpl(const std::vector<int>& indices, 721 const tab_groups::TabGroupId& group); 722 723 // Implementation of MoveTabsAndSetGroupImpl. Moves the set of tabs in 724 // |indices| to the |destination_index| and updates the tabs to the 725 // appropriate |group|. 726 void MoveTabsAndSetGroupImpl(const std::vector<int>& indices, 727 int destination_index, 728 base::Optional<tab_groups::TabGroupId> group); 729 730 // Moves the tab at |index| to |new_index| and sets its group to |new_group|. 731 // Notifies any observers that group affiliation has changed for the tab. 732 void MoveAndSetGroup(int index, 733 int new_index, 734 base::Optional<tab_groups::TabGroupId> new_group); 735 736 void AddToReadLaterImpl(const std::vector<int>& indices); 737 738 // Helper function for MoveAndSetGroup. Removes the tab at |index| from the 739 // group that contains it, if any. Also deletes that group, if it now contains 740 // no tabs. Returns that group. 741 base::Optional<tab_groups::TabGroupId> UngroupTab(int index); 742 743 // Helper function for MoveAndSetGroup. Adds the tab at |index| to |group|. 744 void GroupTab(int index, const tab_groups::TabGroupId& group); 745 746 // Changes the pinned state of the tab at |index|. 747 void SetTabPinnedImpl(int index, bool pinned); 748 749 // Ensures all tabs indicated by |indices| are pinned, moving them in the 750 // process if necessary. Returns the new locations of all of those tabs. 751 std::vector<int> SetTabsPinned(const std::vector<int>& indices, bool pinned); 752 753 // Sets the sound content setting for each site at the |indices|. 754 void SetSitesMuted(const std::vector<int>& indices, bool mute) const; 755 756 // Sets the opener of any tabs that reference the tab at |index| to that tab's 757 // opener or null if there's a cycle. 758 void FixOpeners(int index); 759 760 // Makes sure the tab at |index| is not causing a group contiguity error. Will 761 // make the minimum change to ensure that the tab's group is not non- 762 // contiguous as well as ensuring that it is not breaking up a non-contiguous 763 // group, possibly by setting or clearing its group. 764 void EnsureGroupContiguity(int index); 765 766 // The WebContents data currently hosted within this TabStripModel. This must 767 // be kept in sync with |selection_model_|. 768 std::vector<std::unique_ptr<WebContentsData>> contents_data_; 769 770 // The model for tab groups hosted within this TabStripModel. 771 std::unique_ptr<TabGroupModel> group_model_; 772 773 TabStripModelDelegate* delegate_; 774 775 bool tab_strip_ui_was_set_ = false; 776 777 base::ObserverList<TabStripModelObserver>::Unchecked observers_; 778 779 // A profile associated with this TabStripModel. 780 Profile* profile_; 781 782 // True if all tabs are currently being closed via CloseAllTabs. 783 bool closing_all_ = false; 784 785 // An object that determines where new Tabs should be inserted and where 786 // selection should move when a Tab is closed. 787 std::unique_ptr<TabStripModelOrderController> order_controller_; 788 789 // This must be kept in sync with |contents_data_|. 790 ui::ListSelectionModel selection_model_; 791 792 // TabStripModel is not re-entrancy safe. This member is used to guard public 793 // methods that mutate state of |selection_model_| or |contents_data_|. 794 bool reentrancy_guard_ = false; 795 796 // A recorder for recording tab switching input latency to UMA 797 TabSwitchEventLatencyRecorder tab_switch_event_latency_recorder_; 798 799 // Timer used to mark intervals for metric collection on how many tabs are 800 // scrubbed over a certain interval of time. 801 base::RepeatingTimer tab_scrubbing_interval_timer_; 802 // Timestamp marking the last time a tab was activated by mouse press. This is 803 // used in determining how long a tab was active for metrics. 804 base::TimeTicks last_tab_switch_timestamp_ = base::TimeTicks(); 805 // Counter used to keep track of tab scrubs during intervals set by 806 // |tab_scrubbing_interval_timer_|. 807 size_t tabs_scrubbed_by_mouse_press_count_ = 0; 808 // Counter used to keep track of tab scrubs during intervals set by 809 // |tab_scrubbing_interval_timer_|. 810 size_t tabs_scrubbed_by_key_press_count_ = 0; 811 812 base::WeakPtrFactory<TabStripModel> weak_factory_{this}; 813 814 DISALLOW_IMPLICIT_CONSTRUCTORS(TabStripModel); 815 }; 816 817 // Forbid construction of ScopedObserver with TabStripModel: 818 // TabStripModelObserver already implements ScopedObserver's functionality 819 // natively. 820 template <> 821 class ScopedObserver<TabStripModel, TabStripModelObserver> { 822 public: 823 // Deleting the constructor gives a clear error message traceable back to here. 824 explicit ScopedObserver(TabStripModelObserver* observer) = delete; 825 }; 826 827 #endif // CHROME_BROWSER_UI_TABS_TAB_STRIP_MODEL_H_ 828