blob: 658e00f75c295af87242f1a651f8618428af3090 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "base/json/json_reader.h"
#include "base/scoped_observation.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/values.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/policy/policy_test_utils.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.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_strip_model_observer.h"
#include "chrome/browser/ui/ui_features.h"
#include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
#include "chrome/browser/web_applications/test/prevent_close_test_base.h"
#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
#include "chrome/browser/web_applications/web_app_id_constants.h"
#include "chrome/browser/web_applications/web_app_install_info.h"
#include "chrome/common/chrome_features.h"
#include "components/policy/core/browser/browser_policy_connector.h"
#include "components/policy/core/browser/browser_policy_connector_base.h"
#include "components/policy/core/common/mock_configuration_policy_provider.h"
#include "components/policy/core/common/policy_map.h"
#include "components/policy/policy_constants.h"
#include "components/webapps/common/web_app_id.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test.h"
#include "ui/base/ui_base_features.h"
#include "ui/base/window_open_disposition.h"
#include "url/gurl.h"
#if BUILDFLAG(IS_CHROMEOS)
#include "chromeos/constants/chromeos_features.h"
#endif
using testing::_;
namespace {
constexpr char kCalculatorAppUrl[] = "https://calculator.apps.chrome/";
constexpr char kPreventCloseEnabledForCalculator[] = R"([
{
"manifest_id": "https://calculator.apps.chrome/",
"run_on_os_login": "run_windowed",
"prevent_close_after_run_on_os_login": true
}
])";
constexpr char kCalculatorForceInstalled[] = R"([
{
"url": "https://calculator.apps.chrome/",
"default_launch_container": "window"
}
])";
#if BUILDFLAG(IS_CHROMEOS)
constexpr bool kShouldPreventClose = true;
#else
constexpr bool kShouldPreventClose = false;
#endif // BUILDFLAG(IS_CHROMEOS)
} // namespace
class TabStripModelPreventCloseTest : public PreventCloseTestBase,
public BrowserListObserver,
public TabStripModelObserver {
public:
TabStripModelPreventCloseTest() { BrowserList::AddObserver(this); }
explicit TabStripModelPreventCloseTest(const PreventCloseTestBase&) = delete;
TabStripModelPreventCloseTest& operator=(
const TabStripModelPreventCloseTest&) = delete;
~TabStripModelPreventCloseTest() override {
BrowserList::RemoveObserver(this);
}
// BrowserListObserver:
void OnBrowserRemoved(Browser* browser) override { observer_.Reset(); }
// TabStripModelObserver:
MOCK_METHOD(void,
TabCloseCancelled,
(const content::WebContents* contents),
(override));
protected:
base::ScopedObservation<TabStripModel, TabStripModelPreventCloseTest>
observer_{this};
};
IN_PROC_BROWSER_TEST_F(TabStripModelPreventCloseTest,
PreventCloseEnforedByPolicy) {
InstallPWA(GURL(kCalculatorAppUrl), web_app::kCalculatorAppId);
SetPoliciesAndWaitUntilInstalled(web_app::kCalculatorAppId,
kPreventCloseEnabledForCalculator,
kCalculatorForceInstalled);
Browser* const browser =
LaunchPWA(web_app::kCalculatorAppId, /*launch_in_window=*/true);
ASSERT_TRUE(browser);
observer_.Observe(browser->tab_strip_model());
TabStripModel* const tab_strip_model = browser->tab_strip_model();
EXPECT_EQ(1, tab_strip_model->count());
EXPECT_EQ(!kShouldPreventClose, tab_strip_model->IsTabClosable(
tab_strip_model->GetActiveWebContents()));
EXPECT_CALL(*this, TabCloseCancelled(_)).Times(kShouldPreventClose ? 1 : 0);
tab_strip_model->CloseAllTabs();
EXPECT_EQ(kShouldPreventClose ? 1 : 0, tab_strip_model->count());
if (kShouldPreventClose) {
ClearWebAppSettings();
EXPECT_TRUE(tab_strip_model->IsTabClosable(
tab_strip_model->GetActiveWebContents()));
tab_strip_model->CloseAllTabs();
EXPECT_EQ(0, tab_strip_model->count());
}
}
// TODO(b/321593065): enable this flaky test.
#if BUILDFLAG(IS_CHROMEOS_ASH)
#define MAYBE_PreventCloseEnforcedByPolicyTabbedAppShallBeClosable \
DISABLED_PreventCloseEnforcedByPolicyTabbedAppShallBeClosable
#else
#define MAYBE_PreventCloseEnforcedByPolicyTabbedAppShallBeClosable \
PreventCloseEnforcedByPolicyTabbedAppShallBeClosable
#endif
IN_PROC_BROWSER_TEST_F(
TabStripModelPreventCloseTest,
MAYBE_PreventCloseEnforcedByPolicyTabbedAppShallBeClosable) {
#if BUILDFLAG(IS_CHROMEOS)
if (chromeos::features::IsCrosShortstandEnabled()) {
GTEST_SKIP()
<< "Cannot launch web apps in a tab when Shortstand is enabled.";
}
#endif
InstallPWA(GURL(kCalculatorAppUrl), web_app::kCalculatorAppId);
SetPoliciesAndWaitUntilInstalled(web_app::kCalculatorAppId,
kPreventCloseEnabledForCalculator,
kCalculatorForceInstalled);
Browser* const browser =
LaunchPWA(web_app::kCalculatorAppId, /*launch_in_window=*/false);
ASSERT_TRUE(browser);
observer_.Observe(browser->tab_strip_model());
TabStripModel* const tab_strip_model = browser->tab_strip_model();
EXPECT_NE(0, tab_strip_model->count());
EXPECT_TRUE(
tab_strip_model->IsTabClosable(tab_strip_model->GetActiveWebContents()));
EXPECT_CALL(*this, TabCloseCancelled(_)).Times(0);
tab_strip_model->CloseAllTabs();
EXPECT_EQ(0, tab_strip_model->count());
}
class TabStripModelBrowserTest : public InProcessBrowserTest {
public:
TabStripModelBrowserTest() {
feature_list_.InitWithFeatures(
{features::kTabOrganization, features::kChromeRefresh2023}, {});
}
base::test::ScopedFeatureList feature_list_;
};
IN_PROC_BROWSER_TEST_F(TabStripModelBrowserTest, CommandOrganizeTabs) {
base::HistogramTester histogram_tester;
TabStripModel* const tab_strip_model = browser()->tab_strip_model();
EXPECT_EQ(1, tab_strip_model->count());
EXPECT_TRUE(tab_strip_model->IsContextMenuCommandEnabled(
0, TabStripModel::CommandOrganizeTabs));
// Execute CommandOrganizeTabs once. Expect a request to have been started.
tab_strip_model->ExecuteContextMenuCommand(
0, TabStripModel::CommandOrganizeTabs);
TabOrganizationService* const service =
TabOrganizationServiceFactory::GetForProfile(browser()->profile());
const TabOrganizationSession* const session =
service->GetSessionForBrowser(browser());
EXPECT_NE(session, nullptr);
EXPECT_EQ(session->request()->state(),
TabOrganizationRequest::State::NOT_STARTED);
histogram_tester.ExpectUniqueSample("Tab.Organization.AllEntrypoints.Clicked",
true, 1);
histogram_tester.ExpectUniqueSample("Tab.Organization.TabContextMenu.Clicked",
true, 1);
}