blob: b382bd43180fbc1d46943d9937d5fe5264d8eeb2 [file] [log] [blame]
// Copyright 2022 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/web_applications/web_app_controller_browsertest.h"
#include "chrome/browser/web_applications/policy/web_app_policy_manager.h"
#include "base/json/json_reader.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "build/build_config.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/web_applications/externally_installed_web_app_prefs.h"
#include "chrome/browser/web_applications/policy/web_app_policy_constants.h"
#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
#include "chrome/browser/web_applications/test/web_app_test_observers.h"
#include "chrome/browser/web_applications/test/web_app_test_utils.h"
#include "chrome/browser/web_applications/web_app_command_scheduler.h"
#include "chrome/browser/web_applications/web_app_constants.h"
#include "chrome/browser/web_applications/web_app_helpers.h"
#include "chrome/browser/web_applications/web_app_id.h"
#include "chrome/browser/web_applications/web_app_install_utils.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/prefs/pref_service.h"
#include "components/webapps/browser/installable/installable_metrics.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/test/browser_test.h"
#include "third_party/blink/public/common/manifest/manifest.h"
#include "third_party/blink/public/mojom/manifest/manifest.mojom.h"
namespace web_app {
namespace {
const char kDefaultAppName[] = "app name";
const char kDefaultAppIconUrl1[] = "https://example.com/icon1.png";
const char kDefaultAppIconUrl2[] = "https://example.com/icon2.png";
const char kDefaultCustomName[] = "custom name";
const char kDefaultCustomIconUrl[] = "https://foo.example.com/custom_icon.png";
const char kDefaultCustomIconHash[] = "abcdef";
constexpr char kInstallUrl[] = "https://example.com/install";
constexpr char kStartUrl[] = "https://example.com/start/?u=1";
constexpr char kManifestUrl[] = "https://example.com/install/manifest.json";
base::Value GetForceInstalledAppItem() {
base::Value item(base::Value::Type::DICT);
item.SetKey(kUrlKey, base::Value(kInstallUrl));
item.SetKey(kDefaultLaunchContainerKey,
base::Value(kDefaultLaunchContainerWindowValue));
return item;
}
base::Value GetCustomAppNameItem() {
base::Value item = GetForceInstalledAppItem();
item.SetKey(kCustomNameKey, base::Value(kDefaultCustomName));
return item;
}
base::Value GetCustomAppIconItem() {
base::Value item = GetForceInstalledAppItem();
base::Value sub_item(base::Value::Type::DICT);
sub_item.SetKey(kCustomIconURLKey, base::Value(kDefaultCustomIconUrl));
sub_item.SetKey(kCustomIconHashKey, base::Value(kDefaultCustomIconHash));
item.SetKey(kCustomIconKey, std::move(sub_item));
return item;
}
base::Value GetCustomAppIconAndNameItem() {
base::Value item = GetCustomAppIconItem();
item.SetKey(kCustomNameKey, base::Value(kDefaultCustomName));
return item;
}
} // namespace
class WebAppPolicyManagerBrowserTest
: public WebAppControllerBrowserTest,
public testing::WithParamInterface<test::ExternalPrefMigrationTestCases> {
public:
WebAppPolicyManagerBrowserTest() {
std::vector<base::test::FeatureRef> enabled_features;
std::vector<base::test::FeatureRef> disabled_features;
switch (GetParam()) {
case test::ExternalPrefMigrationTestCases::kDisableMigrationReadPref:
disabled_features.push_back(features::kMigrateExternalPrefsToWebAppDB);
disabled_features.push_back(
features::kUseWebAppDBInsteadOfExternalPrefs);
break;
case test::ExternalPrefMigrationTestCases::kDisableMigrationReadDB:
disabled_features.push_back(features::kMigrateExternalPrefsToWebAppDB);
enabled_features.push_back(
features::kUseWebAppDBInsteadOfExternalPrefs);
break;
case test::ExternalPrefMigrationTestCases::kEnableMigrationReadPref:
enabled_features.push_back(features::kMigrateExternalPrefsToWebAppDB);
disabled_features.push_back(
features::kUseWebAppDBInsteadOfExternalPrefs);
break;
case test::ExternalPrefMigrationTestCases::kEnableMigrationReadDB:
enabled_features.push_back(features::kMigrateExternalPrefsToWebAppDB);
enabled_features.push_back(
features::kUseWebAppDBInsteadOfExternalPrefs);
break;
}
scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features);
}
void SetUpOnMainThread() override {
WebAppControllerBrowserTest::SetUpOnMainThread();
externally_installed_app_prefs_ =
std::make_unique<ExternallyInstalledWebAppPrefs>(profile()->GetPrefs());
}
void TearDown() override {
externally_installed_app_prefs_.reset();
WebAppControllerBrowserTest::TearDown();
}
Profile* profile() { return browser()->profile(); }
content::WebContents* web_contents() const {
return browser()->tab_strip_model()->GetActiveWebContents();
}
content::RenderFrameHost* RenderFrameHost() const {
return web_contents()->GetPrimaryMainFrame();
}
ExternallyInstalledWebAppPrefs& externally_installed_app_prefs() {
return *externally_installed_app_prefs_;
}
void SetPolicyPrefs(base::StringPiece json,
std::vector<std::string> replacements = {}) {
profile()->GetPrefs()->Set(
prefs::kWebAppInstallForceList,
base::JSONReader::Read(
base::ReplaceStringPlaceholders(json, replacements, nullptr))
.value());
}
private:
std::unique_ptr<ExternallyInstalledWebAppPrefs>
externally_installed_app_prefs_;
base::test::ScopedFeatureList scoped_feature_list_;
};
IN_PROC_BROWSER_TEST_P(WebAppPolicyManagerBrowserTest,
OverrideManifestWithCustomName) {
WebAppPolicyManager& policy_manager =
WebAppProvider::GetForTest(profile())->policy_manager();
base::Value::List list;
list.Append(GetCustomAppNameItem());
profile()->GetPrefs()->SetList(prefs::kWebAppInstallForceList,
std::move(list));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL(kInstallUrl)));
blink::mojom::ManifestPtr manifest = blink::mojom::Manifest::New();
policy_manager.MaybeOverrideManifest(RenderFrameHost(), manifest);
EXPECT_EQ(base::UTF8ToUTF16(std::string(kDefaultCustomName)),
manifest->name.value_or(std::u16string()));
}
IN_PROC_BROWSER_TEST_P(WebAppPolicyManagerBrowserTest,
OverrideManifestWithCustomIcon) {
WebAppPolicyManager& policy_manager =
WebAppProvider::GetForTest(profile())->policy_manager();
base::Value::List list;
list.Append(GetCustomAppIconItem());
profile()->GetPrefs()->SetList(prefs::kWebAppInstallForceList,
std::move(list));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL(kInstallUrl)));
blink::mojom::ManifestPtr manifest = blink::mojom::Manifest::New();
policy_manager.MaybeOverrideManifest(RenderFrameHost(), manifest);
EXPECT_EQ(1u, manifest->icons.size());
EXPECT_EQ(GURL(kDefaultCustomIconUrl), manifest->icons[0].src);
}
IN_PROC_BROWSER_TEST_P(WebAppPolicyManagerBrowserTest, DontOverrideManifest) {
WebAppPolicyManager& policy_manager =
WebAppProvider::GetForTest(profile())->policy_manager();
base::Value::List list;
list.Append(GetCustomAppIconAndNameItem());
profile()->GetPrefs()->SetList(prefs::kWebAppInstallForceList,
std::move(list));
// Policy is for kInstallUrl, but we pretend to get a manifest
// from kStartUrl.
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL(kStartUrl)));
blink::mojom::ManifestPtr manifest = blink::mojom::Manifest::New();
policy_manager.MaybeOverrideManifest(RenderFrameHost(), manifest);
EXPECT_EQ(std::u16string(), manifest->name.value_or(std::u16string()));
EXPECT_EQ(0u, manifest->icons.size());
}
// Scenario: App with install_url kInstallUrl has a start_url kStartUrl
// specified in manifest. Next time we navigate to kStartUrl, but we still
// need to override the manifest even though the policy key is kInstallUrl.
// This is done by matching the AppId.
IN_PROC_BROWSER_TEST_P(WebAppPolicyManagerBrowserTest,
MismatchedInstallAndStartUrl) {
WebAppPolicyManager& policy_manager =
WebAppProvider::GetForTest(profile())->policy_manager();
// Set policy:
base::Value::List list;
list.Append(GetCustomAppIconAndNameItem());
profile()->GetPrefs()->SetList(prefs::kWebAppInstallForceList,
std::move(list));
// Create manifest:
blink::mojom::ManifestPtr manifest = blink::mojom::Manifest::New();
manifest->name = base::UTF8ToUTF16(std::string(kDefaultAppName));
manifest->start_url = GURL(kStartUrl);
// Populate manifest with 2 icons:
blink::Manifest::ImageResource icon;
icon.src = GURL(kDefaultAppIconUrl1);
icon.sizes.emplace_back(0, 0); // Represents size "any".
icon.purpose.push_back(blink::mojom::ManifestImageResource::Purpose::ANY);
manifest->icons.emplace_back(icon);
icon.src = GURL(kDefaultAppIconUrl2);
manifest->icons.emplace_back(icon);
// Install the web app, and add it in the externally_installed_app_prefs:
auto install_source = ExternalInstallSource::kExternalPolicy;
std::unique_ptr<WebAppInstallInfo> install_info =
std::make_unique<WebAppInstallInfo>();
install_info->install_url = GURL(kInstallUrl);
UpdateWebAppInfoFromManifest(*manifest, GURL(kManifestUrl),
install_info.get());
auto* provider = WebAppProvider::GetForTest(profile());
provider->scheduler().InstallFromInfo(
std::move(install_info),
/*overwrite_existing_manifest_fields=*/true,
webapps::WebappInstallSource::EXTERNAL_POLICY, base::DoNothing());
externally_installed_app_prefs().Insert(
GURL(kInstallUrl), GenerateAppId(absl::nullopt, GURL(kStartUrl)),
install_source);
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL(kStartUrl)));
policy_manager.MaybeOverrideManifest(RenderFrameHost(), manifest);
EXPECT_EQ(base::UTF8ToUTF16(std::string(kDefaultCustomName)),
manifest->name.value_or(std::u16string()));
EXPECT_EQ(1u, manifest->icons.size());
EXPECT_EQ(GURL(kDefaultCustomIconUrl), manifest->icons[0].src);
}
#if BUILDFLAG(IS_CHROMEOS_ASH)
// Scenario: A policy installed web app is replacing an existing app causing it
// to be uninstalled after the policy app is installed.
// This test does not yet work in Lacros because
// AppServiceProxyLacros::UninstallSilently() has not yet been implemented.
IN_PROC_BROWSER_TEST_P(WebAppPolicyManagerBrowserTest, MigratingPolicyApp) {
// Install old app to replace.
auto install_info = std::make_unique<WebAppInstallInfo>();
install_info->start_url = GURL("https://some.app.com");
install_info->title = u"some app";
AppId old_app_id = test::InstallWebApp(profile(), std::move(install_info));
WebAppTestUninstallObserver uninstall_observer(profile());
uninstall_observer.BeginListening({old_app_id});
// Update policy app to replace old app.
SetPolicyPrefs(R"([{
"url": "https://example.com/install",
"uninstall_and_replace": ["$1"]
}])",
{old_app_id});
// Old app should get uninstalled by policy app install.
uninstall_observer.Wait();
}
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
INSTANTIATE_TEST_SUITE_P(
All,
WebAppPolicyManagerBrowserTest,
::testing::Values(
test::ExternalPrefMigrationTestCases::kDisableMigrationReadPref,
test::ExternalPrefMigrationTestCases::kDisableMigrationReadDB,
test::ExternalPrefMigrationTestCases::kEnableMigrationReadPref,
test::ExternalPrefMigrationTestCases::kEnableMigrationReadDB),
test::GetExternalPrefMigrationTestName);
} // namespace web_app