chromium/chrome/browser/ui/tabs/tab_strip_model_unittest.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 <stddef.h>

#include <algorithm>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <utility>
#include <vector>

#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/memory/raw_ptr.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/tabs/features.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_user_gesture_details.h"
#include "chrome/browser/ui/tabs/tab_utils.h"
#include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h"
#include "chrome/browser/ui/tabs/test_util.h"
#include "chrome/browser/ui/ui_features.h"
#include "chrome/test/base/browser_with_test_window_test.h"
#include "chrome/test/base/testing_profile.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/tab_groups/tab_group_color.h"
#include "components/tab_groups/tab_group_id.h"
#include "components/web_modal/web_contents_modal_dialog_manager.h"
#include "components/web_modal/web_contents_modal_dialog_manager_delegate.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/navigation_simulator.h"
#include "content/public/test/test_renderer_host.h"
#include "content/public/test/web_contents_tester.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkColor.h"

WebContents;

namespace {

const char kTabStripModelTestIDUserDataKey[] =;

class TabStripModelTestIDUserData : public base::SupportsUserData::Data {};

class MockTabStripModelObserver : public TabStripModelObserver {};

const char* const MockTabStripModelObserver::State::kActionNames[]{};

}  // namespace

class TabStripModelTest : public testing::Test,
                          public ::testing::WithParamInterface<bool> {};

TEST_P(TabStripModelTest, TestBasicAPI) {}

TEST_P(TabStripModelTest, TestTabHandlesStaticTabstrip) {}

TEST_P(TabStripModelTest, TestTabHandlesMovingTabInSameTabstrip) {}

TEST_P(TabStripModelTest, TestTabHandlesTabClosed) {}

TEST_P(TabStripModelTest, TestTabHandlesOutOfBounds) {}

TEST_P(TabStripModelTest, TestTabHandlesAcrossModels) {}

TEST_P(TabStripModelTest, TestBasicOpenerAPI) {}

static int GetInsertionIndex(TabStripModel* tabstrip) {}

static void InsertWebContentses(TabStripModel* tabstrip,
                                std::unique_ptr<WebContents> contents1,
                                std::unique_ptr<WebContents> contents2,
                                std::unique_ptr<WebContents> contents3) {}

static bool IsSiteInContentSettingExceptionList(
    HostContentSettingsMap* settings,
    GURL& url,
    ContentSettingsType type) {}

// Tests opening background tabs.
TEST_P(TabStripModelTest, TestLTRInsertionOptions) {}

// This test constructs a tabstrip, and then simulates loading several tabs in
// the background from link clicks on the first tab. Then it simulates opening
// a new tab from the first tab in the foreground via a link click, verifies
// that this tab is opened adjacent to the opener, then closes it.
// Finally it tests that a tab opened for some non-link purpose opens at the
// end of the strip, not bundled to any existing context.
TEST_P(TabStripModelTest, TestInsertionIndexDetermination) {}

// Tests that non-adjacent tabs with an opener are ignored when deciding where
// to position tabs.
TEST_P(TabStripModelTest, TestInsertionIndexDeterminationAfterDragged) {}

// Tests that grandchild tabs are considered to be opened by their grandparent
// tab when deciding where to position tabs.
TEST_P(TabStripModelTest, TestInsertionIndexDeterminationNestedOpener) {}

// Tests that selection is shifted to the correct tab when a tab is closed.
// If the tab is in the background when it is closed, the selection does not
// change.
TEST_P(TabStripModelTest, CloseInactiveTabKeepsSelection) {}

// Tests that selection is shifted to the correct tab when a tab is closed.
// If the tab doesn't have an opener or a group, selection shifts to the right.
TEST_P(TabStripModelTest, CloseActiveTabShiftsSelectionRight) {}

// Tests that selection is shifted to the correct tab when a tab is closed.
// If the tab doesn't have an opener but is in a group, selection shifts to
// another tab in the same group.
TEST_P(TabStripModelTest, CloseGroupedTabShiftsSelectionWithinGroup) {}

// Tests that the active selection will change to another non collapsed tab when
// the active index is in the collapsing group.
TEST_P(TabStripModelTest, CollapseGroupShiftsSelection_SuccessNextTab) {}

// Tests that the active selection will change to another non collapsed tab when
// the active index is in the collapsing group.
TEST_P(TabStripModelTest, CollapseGroupShiftsSelection_SuccessPreviousTab) {}

// Tests that there is no valid selection to shift to when the active tab is in
// the group that will be collapsed.
TEST_P(TabStripModelTest, CollapseGroupShiftsSelection_NoAvailableTabs) {}

// Tests that selection is shifted to the correct tab when a tab is closed.
// If the tab does have an opener, selection shifts to the next tab opened by
// the same opener scanning LTR.
TEST_P(TabStripModelTest, CloseTabWithOpenerShiftsSelectionWithinOpened) {}

// Tests that selection is shifted to the correct tab when a tab is closed.
// If the tab has an opener but no "siblings" opened by the same opener,
// selection shifts to the opener itself.
TEST_P(TabStripModelTest, CloseTabWithOpenerShiftsSelectionToOpener) {}

// Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
// CommandCloseTab.
TEST_P(TabStripModelTest, CommandCloseTab) {}

// Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
// CommandCloseOtherTabs.
TEST_P(TabStripModelTest, CommandCloseOtherTabs) {}

// Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
// CommandCloseTabsToRight.
TEST_P(TabStripModelTest, CommandCloseTabsToRight) {}

// Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
// CommandTogglePinned.
TEST_P(TabStripModelTest, CommandTogglePinned) {}

// Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
// CommandToggleGrouped.
TEST_P(TabStripModelTest, CommandToggleGrouped) {}

// Tests the following context menu commands:
//  - Close Tab
//  - Close Other Tabs
//  - Close Tabs To Right
TEST_P(TabStripModelTest, TestContextMenuCloseCommands) {}

TEST_P(TabStripModelTest, GetIndicesClosedByCommand) {}

// Tests whether or not WebContentses are inserted in the correct position
// using this "smart" function with a simulated middle click action on a series
// of links on the home page.
TEST_P(TabStripModelTest, AddWebContents_MiddleClickLinksAndClose) {}

// Tests whether or not a WebContents created by a left click on a link
// that opens a new tab is inserted correctly adjacent to the tab that spawned
// it.
TEST_P(TabStripModelTest, AddWebContents_LeftClickPopup) {}

// Tests whether or not new tabs that should split context (typed pages,
// generated urls, also blank tabs) open at the end of the tabstrip instead of
// in the middle.
TEST_P(TabStripModelTest, AddWebContents_CreateNewBlankTab) {}

// Tests whether opener state is correctly forgotten when the user switches
// context.
TEST_P(TabStripModelTest, AddWebContents_ForgetOpeners) {}

// Tests whether or not a WebContents in a new tab belongs in the same tab
// group as its opener.
TEST_P(TabStripModelTest, AddWebContents_LinkOpensInSameGroupAsOpener) {}

// Tests that a inserting a new ungrouped tab between two tabs in the same group
// will add that new tab to the group.
TEST_P(TabStripModelTest, AddWebContents_UngroupedTabDoesNotBreakContinuity) {}
// Added for http://b/issue?id=958960
TEST_P(TabStripModelTest, AppendContentsReselectionTest) {}

// Added for http://b/issue?id=1027661
TEST_P(TabStripModelTest, ReselectionConsidersChildrenTest) {}

TEST_P(TabStripModelTest, NewTabAtEndOfStripInheritsOpener) {}

// A test of navigations in a tab that is part of a tree of openers from some
// parent tab. If the navigations are link clicks, the opener relationships of
// the tab. If they are of any other type, they are not preserved.
TEST_P(TabStripModelTest, NavigationForgetsOpeners) {}

// A test for the "quick look" use case where the user can open a new tab at the
// end of the tab strip, do one search, and then close the tab to get back to
// where they were.
TEST_P(TabStripModelTest, NavigationForgettingDoesntAffectNewTab) {}

namespace {

class UnloadListenerTabStripModelDelegate : public TestTabStripModelDelegate {};

}  // namespace

// Tests that fast shutdown is attempted appropriately.
TEST_P(TabStripModelTest, FastShutdown) {}

// Tests various permutations of pinning tabs.
TEST_P(TabStripModelTest, Pinning) {}

// Makes sure the TabStripModel calls the right observer methods during a
// replace.
TEST_P(TabStripModelTest, ReplaceSendsSelected) {}

// Ensure pinned tabs are not mixed with non-pinned tabs when using
// MoveWebContentsAt.
TEST_P(TabStripModelTest, MoveWebContentsAtWithPinned) {}

TEST_P(TabStripModelTest, MoveTabNext) {}

TEST_P(TabStripModelTest, MoveTabNext_Pinned) {}

TEST_P(TabStripModelTest, MoveTabNext_Group) {}

TEST_P(TabStripModelTest, MoveTabNext_GroupAtEnd) {}

TEST_P(TabStripModelTest, MoveTabNext_PinnedDoesNotGroup) {}

TEST_P(TabStripModelTest, MoveTabPrevious) {}

TEST_P(TabStripModelTest, MoveTabPrevious_Pinned) {}

TEST_P(TabStripModelTest, MoveTabPrevious_Group) {}

TEST_P(TabStripModelTest, MoveTabPrevious_GroupAtEnd) {}

TEST_P(TabStripModelTest, MoveSelectedTabsTo) {}

// Tests that moving a tab forgets all openers referencing it.
TEST_P(TabStripModelTest, MoveSelectedTabsTo_ForgetOpeners) {}

TEST_P(TabStripModelTest, CloseSelectedTabs) {}

TEST_P(TabStripModelTest, FirstTabIsActive) {}

TEST_P(TabStripModelTest, MultipleSelection) {}

// Verifies that if we change the selection from a multi selection to a single
// selection, but not in a way that changes the selected_index that
// TabSelectionChanged is invoked.
TEST_P(TabStripModelTest, MultipleToSingle) {}

namespace {

// Test Browser-like class for TabStripModelTest.TabBlockedState.
class TabBlockedStateTestBrowser
    : public TabStripModelObserver,
      public web_modal::WebContentsModalDialogManagerDelegate {};

class DummySingleWebContentsDialogManager
    : public web_modal::SingleWebContentsDialogManager {};

}  // namespace

// Verifies a newly inserted tab retains its previous blocked state.
// http://crbug.com/276334
TEST_P(TabStripModelTest, TabBlockedState) {}

// Verifies ordering of tabs opened via a link from a pinned tab with a
// subsequent pinned tab.
TEST_P(TabStripModelTest, LinkClicksWithPinnedTabOrdering) {}

// This test covers a bug in TabStripModel::MoveWebContentsAt(). Specifically
// if |select_after_move| was true it checked if the index
// select_after_move (as an int) was selected rather than |to_position|.
TEST_P(TabStripModelTest, MoveWebContentsAt) {}

TEST_P(TabStripModelTest, NewTabsUngrouped) {}

TEST_P(TabStripModelTest, AddTabToNewGroup) {}

TEST_P(TabStripModelTest, AddTabToNewGroupUpdatesObservers) {}

TEST_P(TabStripModelTest, ReplacingTabGroupUpdatesObservers) {}

TEST_P(TabStripModelTest, AddTabToNewGroupMiddleOfExistingGroup) {}

TEST_P(TabStripModelTest, AddTabToNewGroupMiddleOfExistingGroupTwoGroups) {}

TEST_P(TabStripModelTest, AddTabToNewGroupReorders) {}

TEST_P(TabStripModelTest, AddTabToNewGroupUnpins) {}

TEST_P(TabStripModelTest, AddTabToNewGroupUnpinsAndReorders) {}

TEST_P(TabStripModelTest, AddTabToNewGroupMovesPinnedAndUnpinnedTabs) {}

TEST_P(TabStripModelTest, AddTabToExistingGroupIdempotent) {}

TEST_P(TabStripModelTest, AddTabToExistingGroup) {}

TEST_P(TabStripModelTest, AddTabToExistingGroupUpdatesObservers) {}

TEST_P(TabStripModelTest, AddTabToLeftOfExistingGroupReorders) {}

TEST_P(TabStripModelTest, AddTabToRighOfExistingGroupReorders) {}

TEST_P(TabStripModelTest, AddTabToExistingGroupReorders) {}

TEST_P(TabStripModelTest, AddTabToExistingGroupUnpins) {}

TEST_P(TabStripModelTest, PinTabInGroupUngroups) {}

TEST_P(TabStripModelTest, RemoveTabFromGroupNoopForUngroupedTab) {}

TEST_P(TabStripModelTest, RemoveTabFromGroup) {}

TEST_P(TabStripModelTest, RemoveTabFromGroupUpdatesObservers) {}

TEST_P(TabStripModelTest, RemoveTabFromGroupMaintainsOrder) {}

TEST_P(TabStripModelTest, RemoveTabFromGroupDoesntReorderIfNoGroup) {}

TEST_P(TabStripModelTest,
       RemoveTabFromGroupMaintainsRelativeOrderOfSelectedTabs) {}

TEST_P(TabStripModelTest, RemoveTabFromGroupMixtureOfGroups) {}

TEST_P(TabStripModelTest, RemoveTabFromGroupDeletesGroup) {}

TEST_P(TabStripModelTest, AddToNewGroupDeletesGroup) {}

TEST_P(TabStripModelTest, MoveGroupToTest) {}

TEST_P(TabStripModelTest, AddToExistingGroupDeletesGroup) {}

TEST_P(TabStripModelTest, CloseTabDeletesGroup) {}

TEST_P(TabStripModelTest, CloseTabNotifiesObserversOfGroupChange) {}

TEST_P(TabStripModelTest, InsertWebContentsAtWithGroupNotifiesObservers) {}

// When inserting a WebContents, if a group is not specified, the new tab
// should be left ungrouped.
TEST_P(TabStripModelTest, InsertWebContentsAtDoesNotGroupByDefault) {}

// When inserting a WebContents, if a group is specified, the new tab should be
// added to that group.
TEST_P(TabStripModelTest, InsertWebContentsAtWithGroupGroups) {}

TEST_P(TabStripModelTest, NewTabWithGroup) {}

TEST_P(TabStripModelTest, NewTabWithGroupDeletedCorrectly) {}

TEST_P(TabStripModelTest, NewTabWithoutIndexInsertsAtEndOfGroup) {}

TEST_P(TabStripModelTest, DiscontinuousNewTabIndexTooHigh) {}

TEST_P(TabStripModelTest, DiscontinuousNewTabIndexTooLow) {}

TEST_P(TabStripModelTest, CreateGroupSetsVisualData) {}

TEST_P(TabStripModelTest, SetVisualDataForGroup) {}

TEST_P(TabStripModelTest, VisualDataChangeNotifiesObservers) {}

TEST_P(TabStripModelTest, ObserverCanBeDestroyedEarly) {}

TEST_P(TabStripModelTest, MovingTabToStartOfGroupDoesNotChangeGroup) {}

TEST_P(TabStripModelTest, MovingTabToMiddleOfGroupDoesNotChangeGroup) {}

TEST_P(TabStripModelTest, MovingTabToEndOfGroupDoesNotChangeGroup) {}

TEST_P(TabStripModelTest, MovingTabOutsideOfGroupToStartOfTabstripClearsGroup) {}

TEST_P(TabStripModelTest, MovingTabOutsideOfGroupToEndOfTabstripClearsGroup) {}

TEST_P(TabStripModelTest, MovingTabBetweenUngroupedTabsClearsGroup) {}

TEST_P(TabStripModelTest, MovingUngroupedTabBetweenGroupsDoesNotAssignGroup) {}

TEST_P(TabStripModelTest,
       MovingUngroupedTabBetweenGroupAndUngroupedDoesNotAssignGroup) {}

TEST_P(TabStripModelTest,
       MovingUngroupedTabBetweenUngroupedAndGroupDoesNotAssignGroup) {}

TEST_P(TabStripModelTest,
       MovingGroupMemberBetweenTwoDifferentGroupsClearsGroup) {}

TEST_P(TabStripModelTest,
       MovingSingleTabGroupBetweenTwoGroupsDoesNotClearGroup) {}

TEST_P(TabStripModelTest, MovingUngroupedTabIntoGroupSetsGroup) {}

TEST_P(TabStripModelTest, MovingGroupedTabIntoGroupChangesGroup) {}

TEST_P(TabStripModelTest, MoveWebContentsAtCorrectlyRemovesGroupEntries) {}

TEST_P(TabStripModelTest, MoveWebContentsAtCorrectlySendsGroupChangedEvent) {}

TEST_P(TabStripModelTest, MoveWebContentsAtCorrectlySendsGroupClearedEvent) {}

// Ensure that the opener for a tab never refers to a dangling WebContents.
// Regression test for crbug.com/1092308.
TEST_P(TabStripModelTest, DanglingOpener) {}

class TabToWindowTestTabStripModelDelegate : public TestTabStripModelDelegate {};

// Sanity check to ensure that the "Move Tabs to Window" command talks to
// the delegate correctly.
TEST_P(TabStripModelTest, MoveTabsToNewWindow) {}

TEST_P(TabStripModelTest, SurroundingGroupAtIndex) {}

TEST_P(TabStripModelTest, ActivateRecordsStartTime) {}

TEST_P(TabStripModelTest, ToggleSiteMuted) {}

TEST_P(TabStripModelTest, ToggleSiteMutedWithLessSpecificRule) {}

TEST_P(TabStripModelTest, ToggleSiteMutedWithOtherDisjointRule) {}

TEST_P(TabStripModelTest, ToggleSiteMutedWithDifferentDefault) {}

TEST_P(TabStripModelTest, ToggleMuteUnmuteMultipleSites) {}

TEST_P(TabStripModelTest, AppendTab) {}

INSTANTIATE_TEST_SUITE_P();