#include "chrome/browser/web_applications/preinstalled_web_app_manager.h"
#include <algorithm>
#include <memory>
#include <set>
#include <string_view>
#include <vector>
#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_command_line.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_path_override.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/extensions/extension_management_test_util.h"
#include "chrome/browser/profiles/profile_test_util.h"
#include "chrome/browser/web_applications/mojom/user_display_mode.mojom.h"
#include "chrome/browser/web_applications/preinstalled_app_install_features.h"
#include "chrome/browser/web_applications/preinstalled_web_app_config_utils.h"
#include "chrome/browser/web_applications/preinstalled_web_apps/preinstalled_web_apps.h"
#include "chrome/browser/web_applications/test/fake_web_app_provider.h"
#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
#include "chrome/browser/web_applications/web_app_constants.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/base/testing_profile.h"
#include "components/account_id/account_id.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
#if BUILDFLAG(IS_CHROMEOS)
#include "chrome/browser/policy/profile_policy_connector.h"
#endif
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_switches.h"
#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
#include "chromeos/ash/components/standalone_browser/feature_refs.h"
#include "chromeos/ash/components/system/fake_statistics_provider.h"
#include "chromeos/ash/components/system/statistics_provider.h"
#include "components/user_manager/scoped_user_manager.h"
#include "components/user_manager/user_names.h"
#endif
#if BUILDFLAG(IS_CHROMEOS_LACROS)
#include "chrome/common/chrome_paths_lacros.h"
#include "chromeos/crosapi/mojom/crosapi.mojom.h"
#endif
namespace web_app {
namespace {
constexpr char kUserTypesTestDir[] = …;
#if BUILDFLAG(IS_CHROMEOS)
constexpr char kGoodJsonTestDir[] = "good_json";
constexpr char kAppAllUrl[] = "https://www.google.com/all";
constexpr char kAppGuestUrl[] = "https://www.google.com/guest";
constexpr char kAppManagedUrl[] = "https://www.google.com/managed";
constexpr char kAppUnmanagedUrl[] = "https://www.google.com/unmanaged";
constexpr char kAppChildUrl[] = "https://www.google.com/child";
#endif
}
class PreinstalledWebAppManagerTest : public testing::Test { … };
TEST_F(PreinstalledWebAppManagerTest, ReplacementExtensionBlockedByPolicy) { … }
#if BUILDFLAG(IS_CHROMEOS)
TEST_F(PreinstalledWebAppManagerTest, GoodJson) {
set_profile(CreateProfileAndLogin());
const auto install_options_list = LoadApps(kGoodJsonTestDir);
std::vector<ExternalInstallOptions> test_install_options_list;
{
ExternalInstallOptions install_options(
GURL("https://www.chromestatus.com/features"),
mojom::UserDisplayMode::kBrowser,
ExternalInstallSource::kExternalDefault);
install_options.user_type_allowlist = {"unmanaged"};
install_options.add_to_applications_menu = true;
install_options.add_to_search = true;
install_options.add_to_management = true;
install_options.add_to_desktop = true;
install_options.add_to_quick_launch_bar = false;
install_options.require_manifest = true;
install_options.disable_if_touchscreen_with_stylus_not_supported = false;
test_install_options_list.push_back(std::move(install_options));
}
{
ExternalInstallOptions install_options(
GURL("https://events.google.com/io2016/?utm_source=web_app_manifest"),
mojom::UserDisplayMode::kStandalone,
ExternalInstallSource::kExternalDefault);
install_options.user_type_allowlist = {"unmanaged"};
install_options.add_to_applications_menu = true;
install_options.add_to_search = true;
install_options.add_to_management = true;
install_options.add_to_desktop = false;
install_options.add_to_quick_launch_bar = false;
install_options.require_manifest = true;
install_options.disable_if_touchscreen_with_stylus_not_supported = false;
install_options.uninstall_and_replace.push_back("migrationsourceappid");
test_install_options_list.push_back(std::move(install_options));
}
EXPECT_EQ(test_install_options_list.size(), install_options_list.size());
for (const auto& install_option : test_install_options_list) {
EXPECT_TRUE(base::Contains(install_options_list, install_option));
}
ExpectHistograms(2, 0, 0);
}
TEST_F(PreinstalledWebAppManagerTest, BadJson) {
set_profile(CreateProfileAndLogin());
const auto app_infos = LoadApps("bad_json");
EXPECT_EQ(0u, app_infos.size());
ExpectHistograms(0, 0, 1);
}
TEST_F(PreinstalledWebAppManagerTest, TxtButNoJson) {
set_profile(CreateProfileAndLogin());
const auto app_infos = LoadApps("txt_but_no_json");
EXPECT_EQ(0u, app_infos.size());
ExpectHistograms(0, 0, 0);
}
TEST_F(PreinstalledWebAppManagerTest, MixedJson) {
set_profile(CreateProfileAndLogin());
const auto app_infos = LoadApps("mixed_json");
EXPECT_EQ(1u, app_infos.size());
if (app_infos.size() == 1) {
EXPECT_EQ(app_infos[0].install_url.spec(),
std::string("https://polytimer.rocks/?homescreen=1"));
}
ExpectHistograms(1, 0, 2);
}
TEST_F(PreinstalledWebAppManagerTest, MissingAppUrl) {
set_profile(CreateProfileAndLogin());
const auto app_infos = LoadApps("missing_app_url");
EXPECT_EQ(0u, app_infos.size());
ExpectHistograms(0, 0, 1);
}
TEST_F(PreinstalledWebAppManagerTest, EmptyAppUrl) {
set_profile(CreateProfileAndLogin());
const auto app_infos = LoadApps("empty_app_url");
EXPECT_EQ(0u, app_infos.size());
ExpectHistograms(0, 0, 1);
}
TEST_F(PreinstalledWebAppManagerTest, InvalidAppUrl) {
set_profile(CreateProfileAndLogin());
const auto app_infos = LoadApps("invalid_app_url");
EXPECT_EQ(0u, app_infos.size());
ExpectHistograms(0, 0, 1);
}
TEST_F(PreinstalledWebAppManagerTest, TrueHideFromUser) {
set_profile(CreateProfileAndLogin());
const auto app_infos = LoadApps("true_hide_from_user");
EXPECT_EQ(1u, app_infos.size());
const auto& app = app_infos[0];
EXPECT_FALSE(app.add_to_applications_menu);
EXPECT_FALSE(app.add_to_search);
EXPECT_FALSE(app.add_to_management);
ExpectHistograms(1, 0, 0);
}
TEST_F(PreinstalledWebAppManagerTest, InvalidHideFromUser) {
set_profile(CreateProfileAndLogin());
const auto app_infos = LoadApps("invalid_hide_from_user");
EXPECT_EQ(0u, app_infos.size());
ExpectHistograms(0, 0, 1);
}
TEST_F(PreinstalledWebAppManagerTest, InvalidCreateShortcuts) {
set_profile(CreateProfileAndLogin());
const auto app_infos = LoadApps("invalid_create_shortcuts");
EXPECT_EQ(0u, app_infos.size());
ExpectHistograms(0, 0, 1);
}
TEST_F(PreinstalledWebAppManagerTest, MissingLaunchContainer) {
set_profile(CreateProfileAndLogin());
const auto app_infos = LoadApps("missing_launch_container");
EXPECT_EQ(0u, app_infos.size());
ExpectHistograms(0, 0, 1);
}
TEST_F(PreinstalledWebAppManagerTest, InvalidLaunchContainer) {
set_profile(CreateProfileAndLogin());
const auto app_infos = LoadApps("invalid_launch_container");
EXPECT_EQ(0u, app_infos.size());
ExpectHistograms(0, 0, 1);
}
TEST_F(PreinstalledWebAppManagerTest, InvalidUninstallAndReplace) {
set_profile(CreateProfileAndLogin());
const auto app_infos = LoadApps("invalid_uninstall_and_replace");
EXPECT_EQ(0u, app_infos.size());
ExpectHistograms(0, 0, 2);
}
TEST_F(PreinstalledWebAppManagerTest, PreinstalledWebAppInstallDisabled) {
set_profile(CreateProfileAndLogin());
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(
features::kPreinstalledWebAppInstallation);
const auto app_infos = LoadApps(kGoodJsonTestDir);
EXPECT_EQ(0u, app_infos.size());
histograms_.ExpectTotalCount(
PreinstalledWebAppManager::kHistogramConfigErrorCount, 0);
histograms_.ExpectTotalCount(
PreinstalledWebAppManager::kHistogramEnabledCount, 0);
histograms_.ExpectTotalCount(
PreinstalledWebAppManager::kHistogramDisabledCount, 0);
}
TEST_F(PreinstalledWebAppManagerTest, EnabledByFinch) {
set_profile(CreateProfileAndLogin());
base::AutoReset<bool> testing_scope =
SetPreinstalledAppInstallFeatureAlwaysEnabledForTesting();
const auto app_infos = LoadApps("enabled_by_finch");
EXPECT_EQ(2u, app_infos.size());
ExpectHistograms(2, 0, 0);
}
TEST_F(PreinstalledWebAppManagerTest, NotEnabledByFinch) {
set_profile(CreateProfileAndLogin());
const auto app_infos = LoadApps("enabled_by_finch");
EXPECT_EQ(0u, app_infos.size());
ExpectHistograms(0, 2, 0);
}
TEST_F(PreinstalledWebAppManagerTest, GuestUser) {
#if BUILDFLAG(IS_CHROMEOS_ASH)
set_profile(CreateGuestProfileAndLogin());
UseOtrProfile();
VerifySetOfApps({GURL(kAppAllUrl), GURL(kAppGuestUrl)});
#else
set_profile(CreateGuestProfileAndLogin());
VerifySetOfApps({GURL(kAppAllUrl), GURL(kAppGuestUrl)});
#endif
}
TEST_F(PreinstalledWebAppManagerTest, UnmanagedUser) {
set_profile(CreateProfileAndLogin());
VerifySetOfApps({GURL(kAppAllUrl), GURL(kAppUnmanagedUrl)});
}
TEST_F(PreinstalledWebAppManagerTest, ManagedUser) {
auto profile = CreateProfileAndLogin();
profile->GetProfilePolicyConnector()->OverrideIsManagedForTesting(true);
set_profile(std::move(profile));
VerifySetOfApps({GURL(kAppAllUrl), GURL(kAppManagedUrl)});
}
TEST_F(PreinstalledWebAppManagerTest, ManagedGuestUser) {
profiles::testing::ScopedTestManagedGuestSession test_managed_guest_session;
auto profile = CreateProfileAndLogin();
profile->GetProfilePolicyConnector()->OverrideIsManagedForTesting(true);
set_profile(std::move(profile));
VerifySetOfApps({});
}
TEST_F(PreinstalledWebAppManagerTest, ChildUser) {
auto profile = CreateProfileAndLogin();
profile->SetIsSupervisedProfile();
EXPECT_TRUE(profile->IsChild());
set_profile(std::move(profile));
VerifySetOfApps({GURL(kAppAllUrl), GURL(kAppChildUrl)});
}
#if BUILDFLAG(IS_CHROMEOS_ASH)
TEST_F(PreinstalledWebAppManagerTest, NonPrimaryProfile) {
set_profile(CreateProfile());
VerifySetOfApps({GURL(kAppAllUrl), GURL(kAppUnmanagedUrl)});
}
#endif
TEST_F(PreinstalledWebAppManagerTest, ExtraWebApps) {
set_profile(CreateProfileAndLogin());
SetExtraWebAppsDir("extra_web_apps", "model1");
const auto app_infos = LoadApps("extra_web_apps");
EXPECT_EQ(1u, app_infos.size());
ExpectHistograms(1, 0, 0);
}
TEST_F(PreinstalledWebAppManagerTest, ExtraWebAppsNoMatchingDirectory) {
set_profile(CreateProfileAndLogin());
SetExtraWebAppsDir("extra_web_apps", "model3");
const auto app_infos = LoadApps("extra_web_apps");
EXPECT_EQ(0u, app_infos.size());
ExpectHistograms(0, 0, 0);
}
#else
TEST_F(PreinstalledWebAppManagerTest, NoApp) { … }
#endif
#if BUILDFLAG(IS_CHROMEOS)
class DisabledPreinstalledWebAppManagerTest
: public PreinstalledWebAppManagerTest {
public:
DisabledPreinstalledWebAppManagerTest() {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableDefaultApps);
}
};
TEST_F(DisabledPreinstalledWebAppManagerTest, LoadConfigsWhileDisabled) {
set_profile(CreateProfileAndLogin());
EXPECT_EQ(LoadApps(kGoodJsonTestDir,
true)
.size(),
0u);
}
#endif
}