chromium/chrome/browser/ui/tabs/tab_strip_model.cc

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include "chrome/browser/ui/tabs/tab_strip_model.h"

#include <algorithm>
#include <memory>
#include <optional>
#include <set>
#include <string>
#include <unordered_map>
#include <utility>

#include "base/containers/adapters.h"
#include "base/containers/contains.h"
#include "base/containers/flat_map.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/user_metrics.h"
#include "base/not_fatal_until.h"
#include "base/observer_list.h"
#include "base/ranges/algorithm.h"
#include "base/trace_event/common/trace_event_common.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/commerce/browser_utils.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/extensions/tab_helper.h"
#include "chrome/browser/lifetime/browser_shutdown.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/reading_list/reading_list_model_factory.h"
#include "chrome/browser/resource_coordinator/tab_helper.h"
#include "chrome/browser/send_tab_to_self/send_tab_to_self_util.h"
#include "chrome/browser/ui/bookmarks/bookmark_utils.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/send_tab_to_self/send_tab_to_self_bubble.h"
#include "chrome/browser/ui/tab_ui_helper.h"
#include "chrome/browser/ui/tabs/features.h"
#include "chrome/browser/ui/tabs/organization/metrics.h"
#include "chrome/browser/ui/tabs/organization/tab_organization_service.h"
#include "chrome/browser/ui/tabs/organization/tab_organization_service_factory.h"
#include "chrome/browser/ui/tabs/organization/tab_organization_session.h"
#include "chrome/browser/ui/tabs/tab_contents_data.h"
#include "chrome/browser/ui/tabs/tab_enums.h"
#include "chrome/browser/ui/tabs/tab_group.h"
#include "chrome/browser/ui/tabs/tab_group_model.h"
#include "chrome/browser/ui/tabs/tab_model.h"
#include "chrome/browser/ui/tabs/tab_strip_collection.h"
#include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
#include "chrome/browser/ui/tabs/tab_utils.h"
#include "chrome/browser/ui/thumbnails/thumbnail_tab_helper.h"
#include "chrome/browser/ui/ui_features.h"
#include "chrome/browser/ui/views/tabs/tab_drag_controller.h"
#include "chrome/browser/ui/web_applications/web_app_dialog_utils.h"
#include "chrome/browser/ui/web_applications/web_app_launch_utils.h"
#include "chrome/browser/ui/web_applications/web_app_tabbed_utils.h"
#include "chrome/browser/web_applications/policy/web_app_policy_manager.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#include "chrome/browser/web_applications/web_app_tab_helper.h"
#include "chrome/common/webui_url_constants.h"
#include "components/commerce/core/commerce_utils.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/feature_engagement/public/feature_constants.h"
#include "components/reading_list/core/reading_list_model.h"
#include "components/tab_groups/tab_group_id.h"
#include "components/tab_groups/tab_group_visual_data.h"
#include "components/web_modal/web_contents_modal_dialog_manager.h"
#include "components/webapps/common/web_app_id.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_widget_host.h"
#include "content/public/browser/render_widget_host_observer.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/url_constants.h"
#include "media/base/media_switches.h"
#include "third_party/perfetto/include/perfetto/tracing/traced_value.h"
#include "ui/base/page_transition_types.h"
#include "ui/gfx/range/range.h"

UserMetricsAction;
WebContents;

namespace {

TabGroupModelFactory* factory_instance =;

// Works similarly to base::AutoReset but checks for access from the wrong
// thread as well as ensuring that the previous value of the re-entrancy guard
// variable was false.
class ReentrancyCheck {};

// Returns true if the specified transition is one of the types that cause the
// opener relationships for the tab in which the transition occurred to be
// forgotten. This is generally any navigation that isn't a link click (i.e.
// any navigation that can be considered to be the start of a new task distinct
// from what had previously occurred in that tab).
bool ShouldForgetOpenersForTransition(ui::PageTransition transition) {}

tabs::TabInterface::DetachReason RemoveReasonToDetachReason(
    TabStripModelChange::RemoveReason reason) {}

}  // namespace

TabGroupModelFactory::TabGroupModelFactory() {}

// static
TabGroupModelFactory* TabGroupModelFactory::GetInstance() {}

std::unique_ptr<TabGroupModel> TabGroupModelFactory::Create(
    TabGroupController* controller) {}

DetachedWebContents::DetachedWebContents(
    int index_before_any_removals,
    int index_at_time_of_removal,
    std::unique_ptr<tabs::TabModel> tab,
    content::WebContents* contents,
    TabStripModelChange::RemoveReason remove_reason,
    std::optional<SessionID> id)
    :{}
DetachedWebContents::~DetachedWebContents() = default;
DetachedWebContents::DetachedWebContents(DetachedWebContents&&) = default;

// Holds all state necessary to send notifications for detached tabs.
struct TabStripModel::DetachNotifications {};

///////////////////////////////////////////////////////////////////////////////
// TabStripModel, public:

constexpr int TabStripModel::kNoTab;

TabStripModel::TabStripModel(TabStripModelDelegate* delegate,
                             Profile* profile,
                             TabGroupModelFactory* group_model_factory)
    :{}

TabStripModel::~TabStripModel() {}

void TabStripModel::SetTabStripUI(TabStripModelObserver* observer) {}

void TabStripModel::AddObserver(TabStripModelObserver* observer) {}

void TabStripModel::RemoveObserver(TabStripModelObserver* observer) {}

int TabStripModel::count() const {}

bool TabStripModel::empty() const {}

int TabStripModel::GetIndexOfTab(tabs::TabHandle tab_handle) const {}

tabs::TabHandle TabStripModel::GetTabHandleAt(int index) const {}

bool TabStripModel::ContainsIndex(int index) const {}

void TabStripModel::AppendWebContents(std::unique_ptr<WebContents> contents,
                                      bool foreground) {}

void TabStripModel::AppendTab(std::unique_ptr<tabs::TabModel> tab,
                              bool foreground) {}

int TabStripModel::InsertWebContentsAt(
    int index,
    std::unique_ptr<WebContents> contents,
    int add_types,
    std::optional<tab_groups::TabGroupId> group) {}

int TabStripModel::InsertDetachedTabAt(
    int index,
    std::unique_ptr<tabs::TabModel> tab,
    int add_types,
    std::optional<tab_groups::TabGroupId> group) {}

std::unique_ptr<content::WebContents> TabStripModel::DiscardWebContentsAt(
    int index,
    std::unique_ptr<WebContents> new_contents) {}

std::unique_ptr<tabs::TabModel> TabStripModel::DetachTabAtForInsertion(
    int index) {}

void TabStripModel::DetachAndDeleteWebContentsAt(int index) {}

std::unique_ptr<DetachedWebContents>
TabStripModel::DetachWebContentsWithReasonAt(
    int index,
    TabStripModelChange::RemoveReason reason) {}

void TabStripModel::OnChange(const TabStripModelChange& change,
                             const TabStripSelectionChange& selection) {}

std::unique_ptr<DetachedWebContents> TabStripModel::DetachWebContentsImpl(
    int index_before_any_removals,
    int index_at_time_of_removal,
    bool create_historical_tab,
    TabStripModelChange::RemoveReason reason) {}

void TabStripModel::SendDetachWebContentsNotifications(
    DetachNotifications* notifications) {}

void TabStripModel::ActivateTabAt(int index,
                                  TabStripUserGestureDetails user_gesture) {}

int TabStripModel::MoveWebContentsAt(int index,
                                     int to_position,
                                     bool select_after_move) {}

int TabStripModel::MoveWebContentsAt(
    int index,
    int to_position,
    bool select_after_move,
    std::optional<tab_groups::TabGroupId> group) {}

void TabStripModel::MoveSelectedTabsTo(
    int index,
    std::optional<tab_groups::TabGroupId> group) {}

void TabStripModel::MoveGroupTo(const tab_groups::TabGroupId& group,
                                int to_index) {}

void TabStripModel::MoveGroupToImpl(const tab_groups::TabGroupId& group,
                                    int to_index) {}

WebContents* TabStripModel::GetActiveWebContents() const {}

tabs::TabModel* TabStripModel::GetActiveTab() const {}

WebContents* TabStripModel::GetWebContentsAt(int index) const {}

int TabStripModel::GetIndexOfWebContents(const WebContents* contents) const {}

void TabStripModel::UpdateWebContentsStateAt(int index,
                                             TabChangeType change_type) {}

void TabStripModel::SetTabNeedsAttentionAt(int index, bool attention) {}

void TabStripModel::CloseAllTabs() {}

void TabStripModel::CloseAllTabsInGroup(const tab_groups::TabGroupId& group) {}

void TabStripModel::CloseWebContentsAt(int index, uint32_t close_types) {}

bool TabStripModel::TabsAreLoading() const {}

tabs::TabModel* TabStripModel::GetOpenerOfTabAt(const int index) const {}

void TabStripModel::SetOpenerOfWebContentsAt(int index, WebContents* opener) {}

int TabStripModel::GetIndexOfLastWebContentsOpenedBy(const WebContents* opener,
                                                     int start_index) const {}

void TabStripModel::TabNavigating(WebContents* contents,
                                  ui::PageTransition transition) {}

void TabStripModel::SetTabBlocked(int index, bool blocked) {}

int TabStripModel::SetTabPinned(int index, bool pinned) {}

bool TabStripModel::IsTabPinned(int index) const {}

bool TabStripModel::IsTabCollapsed(int index) const {}

bool TabStripModel::IsGroupCollapsed(
    const tab_groups::TabGroupId& group) const {}

bool TabStripModel::IsTabBlocked(int index) const {}

bool TabStripModel::IsTabClosable(int index) const {}

bool TabStripModel::IsTabClosable(const content::WebContents* contents) const {}

std::optional<tab_groups::TabGroupId> TabStripModel::GetTabGroupForTab(
    int index) const {}

std::optional<tab_groups::TabGroupId> TabStripModel::GetSurroundingTabGroup(
    int index) const {}

int TabStripModel::IndexOfFirstNonPinnedTab() const {}

void TabStripModel::ExtendSelectionTo(int index) {}

bool TabStripModel::ToggleSelectionAt(int index) {}

void TabStripModel::AddSelectionFromAnchorTo(int index) {}

bool TabStripModel::IsTabSelected(int index) const {}

void TabStripModel::SetSelectionFromModel(ui::ListSelectionModel source) {}

const ui::ListSelectionModel& TabStripModel::selection_model() const {}

bool TabStripModel::CanShowModalUI() const {}

std::unique_ptr<ScopedTabStripModalUI> TabStripModel::ShowModalUI() {}

void TabStripModel::ForceShowingModalUIForTesting(bool showing) {}

void TabStripModel::AddWebContents(
    std::unique_ptr<WebContents> contents,
    int index,
    ui::PageTransition transition,
    int add_types,
    std::optional<tab_groups::TabGroupId> group) {}

void TabStripModel::AddTab(std::unique_ptr<tabs::TabModel> tab,
                           int index,
                           ui::PageTransition transition,
                           int add_types,
                           std::optional<tab_groups::TabGroupId> group) {}

void TabStripModel::CloseSelectedTabs() {}

void TabStripModel::SelectNextTab(TabStripUserGestureDetails detail) {}

void TabStripModel::SelectPreviousTab(TabStripUserGestureDetails detail) {}

void TabStripModel::SelectLastTab(TabStripUserGestureDetails detail) {}

void TabStripModel::MoveTabNext() {}

void TabStripModel::MoveTabPrevious() {}

tab_groups::TabGroupId TabStripModel::AddToNewGroup(
    const std::vector<int> indices,
    const tab_groups::TabGroupId group_id,
    tab_groups::TabGroupVisualData visual_data) {}

tab_groups::TabGroupId TabStripModel::AddToNewGroup(
    const std::vector<int> indices) {}

void TabStripModel::AddToExistingGroup(const std::vector<int> indices,
                                       const tab_groups::TabGroupId group,
                                       const bool add_to_end) {}

void TabStripModel::AddToGroupForRestore(const std::vector<int>& indices,
                                         const tab_groups::TabGroupId& group) {}

void TabStripModel::RemoveFromGroup(const std::vector<int>& indices) {}

bool TabStripModel::IsReadLaterSupportedForAny(
    const std::vector<int>& indices) {}

void TabStripModel::AddToReadLater(const std::vector<int>& indices) {}

void TabStripModel::CreateTabGroup(const tab_groups::TabGroupId& group) {}

void TabStripModel::OpenTabGroupEditor(const tab_groups::TabGroupId& group) {}

void TabStripModel::ChangeTabGroupContents(
    const tab_groups::TabGroupId& group) {}

void TabStripModel::ChangeTabGroupVisuals(
    const tab_groups::TabGroupId& group,
    const TabGroupChange::VisualsChange& visuals) {}

void TabStripModel::MoveTabGroup(const tab_groups::TabGroupId& group) {}

void TabStripModel::CloseTabGroup(const tab_groups::TabGroupId& group) {}

std::u16string TabStripModel::GetTitleAt(int index) const {}

int TabStripModel::GetTabCount() const {}

// Context menu functions.
bool TabStripModel::IsContextMenuCommandEnabled(
    int context_index,
    ContextMenuCommand command_id) const {}

void TabStripModel::ExecuteContextMenuCommand(int context_index,
                                              ContextMenuCommand command_id) {}

void TabStripModel::ExecuteAddToExistingGroupCommand(
    int context_index,
    const tab_groups::TabGroupId& group) {}

void TabStripModel::ExecuteAddToExistingWindowCommand(int context_index,
                                                      int browser_index) {}

std::vector<tab_groups::TabGroupId>
TabStripModel::GetGroupsDestroyedFromRemovingIndices(
    const std::vector<int>& indices) const {}

void TabStripModel::ExecuteCloseTabsByIndicesCommand(
    const std::vector<int>& indices_to_delete) {}

bool TabStripModel::WillContextMenuMuteSites(int index) {}

bool TabStripModel::WillContextMenuPin(int index) {}

bool TabStripModel::WillContextMenuGroup(int index) {}

// static
bool TabStripModel::ContextMenuCommandToBrowserCommand(int cmd_id,
                                                       int* browser_cmd) {}

int TabStripModel::GetIndexOfNextWebContentsOpenedBy(const WebContents* opener,
                                                     int start_index) const {}

std::optional<int> TabStripModel::GetNextExpandedActiveTab(
    int start_index,
    std::optional<tab_groups::TabGroupId> collapsing_group) const {}

void TabStripModel::ForgetAllOpeners() {}

void TabStripModel::ForgetOpener(WebContents* contents) {}

void TabStripModel::WriteIntoTrace(perfetto::TracedValue context) const {}

///////////////////////////////////////////////////////////////////////////////
// TabStripModel, private:

bool TabStripModel::RunUnloadListenerBeforeClosing(
    content::WebContents* contents) {}

bool TabStripModel::ShouldRunUnloadListenerBeforeClosing(
    content::WebContents* contents) {}

int TabStripModel::ConstrainInsertionIndex(int index, bool pinned_tab) const {}

int TabStripModel::ConstrainMoveIndex(int index, bool pinned_tab) const {}

std::vector<int> TabStripModel::GetIndicesForCommand(int index) const {}

std::vector<int> TabStripModel::GetIndicesClosedByCommand(
    int index,
    ContextMenuCommand id) const {}

bool TabStripModel::IsNewTabAtEndOfTabStrip(WebContents* contents) const {}

std::vector<content::WebContents*> TabStripModel::GetWebContentsesByIndices(
    const std::vector<int>& indices) const {}

int TabStripModel::InsertTabAtImpl(
    int index,
    std::unique_ptr<tabs::TabModel> tab,
    int add_types,
    std::optional<tab_groups::TabGroupId> group) {}

tabs::TabModel* TabStripModel::GetTabAtIndex(int index) const {}

tabs::TabModel* TabStripModel::GetTabForWebContents(
    const content::WebContents* contents) const {}

void TabStripModel::CloseTabs(base::span<content::WebContents* const> items,
                              uint32_t close_types) {}

bool TabStripModel::CloseWebContentses(
    base::span<content::WebContents* const> items,
    uint32_t close_types,
    DetachNotifications* notifications) {}

TabStripSelectionChange TabStripModel::SetSelection(
    ui::ListSelectionModel new_model,
    TabStripModelObserver::ChangeReason reason,
    bool triggered_by_other_operation) {}

void TabStripModel::SelectRelativeTab(TabRelativeDirection direction,
                                      TabStripUserGestureDetails detail) {}

void TabStripModel::MoveTabRelative(TabRelativeDirection direction) {}

std::pair<std::optional<int>, std::optional<int>>
TabStripModel::GetAdjacentTabsAfterSelectedMove(
    base::PassKey<TabDragController>,
    int destination_index) {}

std::vector<int> TabStripModel::GetSelectedPinnedTabs() {}

std::vector<int> TabStripModel::GetSelectedUnpinnedTabs() {}

void TabStripModel::AddToNewGroupImpl(
    const std::vector<int>& indices,
    const tab_groups::TabGroupId& new_group,
    std::optional<tab_groups::TabGroupVisualData> visual_data) {}

void TabStripModel::AddToExistingGroupImpl(const std::vector<int>& indices,
                                           const tab_groups::TabGroupId& group,
                                           const bool add_to_end) {}

void TabStripModel::MoveTabsAndSetGroupImpl(
    const std::vector<int>& indices,
    int destination_index,
    std::optional<tab_groups::TabGroupId> group) {}

void TabStripModel::AddToReadLaterImpl(const std::vector<int>& indices) {}

void TabStripModel::InsertTabAtIndexImpl(
    std::unique_ptr<tabs::TabModel> tab_model,
    int index,
    std::optional<tab_groups::TabGroupId> group,
    bool pin,
    bool active) {}

std::unique_ptr<tabs::TabModel> TabStripModel::RemoveTabFromIndexImpl(
    int index) {}

void TabStripModel::MoveTabToIndexImpl(
    int initial_index,
    int final_index,
    const std::optional<tab_groups::TabGroupId> group,
    bool pin,
    bool select_after_move) {}

void TabStripModel::MoveTabsToIndexImpl(
    const std::vector<int>& tab_indices,
    int destination_index,
    const std::optional<tab_groups::TabGroupId> group) {}

void TabStripModel::TabGroupStateChanged(
    int index,
    tabs::TabModel* tab,
    const std::optional<tab_groups::TabGroupId> initial_group,
    const std::optional<tab_groups::TabGroupId> new_group) {}

void TabStripModel::RemoveTabFromGroupModel(
    const tab_groups::TabGroupId& group) {}

void TabStripModel::AddTabToGroupModel(const tab_groups::TabGroupId& group) {}

void TabStripModel::ValidateTabStripModel() {}

void TabStripModel::SendMoveNotificationForWebContents(
    int index,
    int to_position,
    WebContents* web_contents,
    TabStripSelectionChange& selection_change) {}

TabStripSelectionChange TabStripModel::MaybeUpdateSelectionModel(
    int initial_index,
    int final_index,
    bool select_after_move) {}

std::vector<std::pair<int, int>> TabStripModel::CalculateIncrementalTabMoves(
    const std::vector<int>& tab_indices,
    int destination_index) const {}

std::vector<TabStripModel::MoveNotification>
TabStripModel::PrepareTabsToMoveToIndex(const std::vector<int>& tab_indices,
                                        int destination_index) {}

void TabStripModel::SetTabsPinned(std::vector<int> indices, bool pinned) {}

// Sets the sound content setting for each site at the |indices|.
void TabStripModel::SetSitesMuted(const std::vector<int>& indices,
                                  bool mute) const {}

void TabStripModel::FixOpeners(int index) {}

std::optional<tab_groups::TabGroupId> TabStripModel::GetGroupToAssign(
    int index,
    int to_position) {}

int TabStripModel::GetTabIndexAfterClosing(int index,
                                           int removing_index) const {}

void TabStripModel::OnActiveTabChanged(
    const TabStripSelectionChange& selection) {}

bool TabStripModel::PolicyAllowsTabClosing(
    content::WebContents* contents) const {}

int TabStripModel::DetermineInsertionIndex(ui::PageTransition transition,
                                           bool foreground) {}

void TabStripModel::GroupCloseStopped(const tab_groups::TabGroupId& group) {}

std::optional<int> TabStripModel::DetermineNewSelectedIndex(
    int removing_index) const {}

TabStripModel::ScopedTabStripModalUIImpl::ScopedTabStripModalUIImpl(
    TabStripModel* model)
    :{}

TabStripModel::ScopedTabStripModalUIImpl::~ScopedTabStripModalUIImpl() {}