blob: 00b95cca216eff8a2e226a3f5658edd59cc670c8 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/web_applications/manifest_update_manager.h"
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "base/callback_helpers.h"
#include "base/containers/contains.h"
#include "base/memory/scoped_refptr.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
#include "chrome/browser/web_applications/components/app_shortcut_manager.h"
#include "chrome/browser/web_applications/components/externally_managed_app_manager.h"
#include "chrome/browser/web_applications/components/install_finalizer.h"
#include "chrome/browser/web_applications/components/os_integration_manager.h"
#include "chrome/browser/web_applications/components/web_app_constants.h"
#include "chrome/browser/web_applications/components/web_app_provider_base.h"
#include "chrome/browser/web_applications/components/web_app_utils.h"
#include "chrome/browser/web_applications/system_web_apps/test/test_system_web_app_installation.h"
#include "chrome/browser/web_applications/test/web_app_install_observer.h"
#include "chrome/browser/web_applications/test/web_app_test.h"
#include "chrome/browser/web_applications/test/web_app_test_utils.h"
#include "chrome/browser/web_applications/web_app.h"
#include "chrome/browser/web_applications/web_app_registrar.h"
#include "chrome/common/chrome_features.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/webapps/browser/installable/installable_metrics.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/url_loader_interceptor.h"
#include "extensions/browser/extension_registry.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/manifest/manifest.h"
#include "third_party/skia/include/core/SkColor.h"
#if defined(OS_WIN) || defined(OS_MAC) || \
(defined(OS_LINUX) && !BUILDFLAG(IS_CHROMEOS_LACROS))
#include "base/command_line.h"
#include "chrome/browser/web_applications/components/url_handler_manager_impl.h"
#include "chrome/browser/web_applications/test/fake_web_app_origin_association_manager.h"
#endif
namespace web_app {
namespace {
constexpr char kUpdateHistogramName[] = "Webapp.Update.ManifestUpdateResult";
constexpr char kInstallableIconList[] = R"(
[
{
"src": "launcher-icon-4x.png",
"sizes": "192x192",
"type": "image/png"
}
]
)";
constexpr SkColor kInstallableIconTopLeftColor =
SkColorSetRGB(0x15, 0x96, 0xE0);
constexpr SkColor kBasicIconTopLeftColor = SkColorSetRGB(0x55, 0x55, 0x55);
constexpr char kAnotherInstallableIconList[] = R"(
[
{
"src": "/banners/image-512px.png",
"sizes": "512x512",
"type": "image/png"
}
]
)";
constexpr char kAnotherShortcutsItemName[] = "Timeline";
constexpr char16_t kAnotherShortcutsItemName16[] = u"Timeline";
constexpr char kAnotherShortcutsItemUrl[] = "/shortcut";
constexpr char kAnotherShortcutsItemShortName[] = "H";
constexpr char kAnotherShortcutsItemDescription[] = "Navigate home";
constexpr char kAnotherIconSrc[] = "/launcher-icon-4x.png";
constexpr int kAnotherIconSize = 192;
constexpr char kShortcutsItem[] = R"(
[
{
"name": "Home",
"short_name": "HM",
"description": "Go home",
"url": ".",
"icons": [
{
"src": "/banners/image-512px.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
]
)";
constexpr char kShortcutsItems[] = R"(
[
{
"name": "Home",
"short_name": "HM",
"description": "Go home",
"url": ".",
"icons": [
{
"src": "/banners/image-512px.png",
"sizes": "512x512",
"type": "image/png"
}
]
},
{
"name": "Settings",
"short_name": "ST",
"description": "App settings",
"url": "/settings",
"icons": [
{
"src": "launcher-icon-4x.png",
"sizes": "192x192",
"type": "image/png"
}
]
}
]
)";
constexpr SkColor kAnotherInstallableIconTopLeftColor =
SkColorSetRGB(0x5C, 0x5C, 0x5C);
ManifestUpdateManager& GetManifestUpdateManager(Browser* browser) {
return WebAppProviderBase::GetProviderBase(browser->profile())
->manifest_update_manager();
}
class UpdateCheckResultAwaiter {
public:
explicit UpdateCheckResultAwaiter(Browser* browser, const GURL& url)
: browser_(browser), url_(url) {
SetCallback();
}
void SetCallback() {
GetManifestUpdateManager(browser_).SetResultCallbackForTesting(
base::BindOnce(&UpdateCheckResultAwaiter::OnResult,
base::Unretained(this)));
}
ManifestUpdateResult AwaitNextResult() && {
run_loop_.Run();
return *result_;
}
void OnResult(const GURL& url, ManifestUpdateResult result) {
if (url != url_) {
SetCallback();
return;
}
result_ = result;
run_loop_.Quit();
}
private:
Browser* browser_ = nullptr;
const GURL& url_;
base::RunLoop run_loop_;
absl::optional<ManifestUpdateResult> result_;
};
} // namespace
class ManifestUpdateManagerBrowserTest : public InProcessBrowserTest {
public:
ManifestUpdateManagerBrowserTest() {}
ManifestUpdateManagerBrowserTest(const ManifestUpdateManagerBrowserTest&) =
delete;
ManifestUpdateManagerBrowserTest& operator=(
const ManifestUpdateManagerBrowserTest&) = delete;
~ManifestUpdateManagerBrowserTest() override = default;
void SetUp() override {
http_server_.AddDefaultHandlers(GetChromeTestDataDir());
http_server_.RegisterRequestHandler(base::BindRepeating(
&ManifestUpdateManagerBrowserTest::RequestHandlerOverride,
base::Unretained(this)));
ASSERT_TRUE(http_server_.Start());
// Suppress globally to avoid OS hooks deployed for system web app during
// WebAppProvider setup.
os_hooks_suppress_ =
OsIntegrationManager::ScopedSuppressOsHooksForTesting();
InProcessBrowserTest::SetUp();
}
void SetUpOnMainThread() override {
// Cannot construct RunLoop in constructor due to threading restrictions.
shortcut_run_loop_.emplace();
}
void OnShortcutInfoRetrieved(std::unique_ptr<ShortcutInfo> shortcut_info) {
if (shortcut_info) {
updated_shortcut_top_left_color_ =
shortcut_info->favicon.begin()->AsBitmap().getColor(0, 0);
}
shortcut_run_loop_->Quit();
}
void CheckShortcutInfoUpdated(const AppId& app_id, SkColor top_left_color) {
GetProvider().os_integration_manager().GetShortcutInfoForApp(
app_id, base::BindOnce(
&ManifestUpdateManagerBrowserTest::OnShortcutInfoRetrieved,
base::Unretained(this)));
shortcut_run_loop_->Run();
EXPECT_EQ(updated_shortcut_top_left_color_, top_left_color);
}
std::unique_ptr<net::test_server::HttpResponse> RequestHandlerOverride(
const net::test_server::HttpRequest& request) {
if (request_override_)
return request_override_.Run(request);
return nullptr;
}
void OverrideManifest(const char* manifest_template,
const std::vector<std::string>& substitutions) {
std::string content = base::ReplaceStringPlaceholders(
manifest_template, substitutions, nullptr);
request_override_ = base::BindLambdaForTesting(
[this, content = std::move(content)](
const net::test_server::HttpRequest& request)
-> std::unique_ptr<net::test_server::HttpResponse> {
if (request.GetURL() != GetManifestURL())
return nullptr;
auto http_response =
std::make_unique<net::test_server::BasicHttpResponse>();
http_response->set_code(net::HTTP_FOUND);
http_response->set_content(content);
return std::move(http_response);
});
}
GURL GetAppURL() const {
return http_server_.GetURL("/banners/manifest_test_page.html");
}
GURL GetManifestURL() const {
return http_server_.GetURL("/banners/manifest.json");
}
AppId InstallWebApp() {
GURL app_url = GetAppURL();
ui_test_utils::NavigateToURL(browser(), app_url);
AppId app_id;
base::RunLoop run_loop;
GetProvider().install_manager().InstallWebAppFromManifestWithFallback(
browser()->tab_strip_model()->GetActiveWebContents(),
/*force_shortcut_app=*/false,
webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
base::BindOnce(test::TestAcceptDialogCallback),
base::BindLambdaForTesting(
[&](const AppId& new_app_id, InstallResultCode code) {
EXPECT_EQ(code, InstallResultCode::kSuccessNewInstall);
app_id = new_app_id;
run_loop.Quit();
}));
run_loop.Run();
return app_id;
}
AppId InstallDefaultApp() {
const GURL app_url = GetAppURL();
base::RunLoop run_loop;
ExternalInstallOptions install_options(
app_url, DisplayMode::kStandalone,
ExternalInstallSource::kInternalDefault);
install_options.add_to_applications_menu = false;
install_options.add_to_desktop = false;
install_options.add_to_quick_launch_bar = false;
install_options.install_placeholder = true;
GetProvider().externally_managed_app_manager().Install(
std::move(install_options),
base::BindLambdaForTesting(
[&](const GURL& installed_app_url,
ExternallyManagedAppManager::InstallResult result) {
EXPECT_EQ(installed_app_url, app_url);
EXPECT_EQ(result.code, InstallResultCode::kSuccessNewInstall);
run_loop.Quit();
}));
run_loop.Run();
return GetProvider().registrar().LookupExternalAppId(app_url).value();
}
AppId InstallPolicyApp() {
const GURL app_url = GetAppURL();
base::RunLoop run_loop;
ExternalInstallOptions install_options(
app_url, DisplayMode::kStandalone,
ExternalInstallSource::kExternalPolicy);
install_options.add_to_applications_menu = false;
install_options.add_to_desktop = false;
install_options.add_to_quick_launch_bar = false;
install_options.install_placeholder = true;
GetProvider().externally_managed_app_manager().Install(
std::move(install_options),
base::BindLambdaForTesting(
[&](const GURL& installed_app_url,
ExternallyManagedAppManager::InstallResult result) {
EXPECT_EQ(installed_app_url, app_url);
EXPECT_EQ(result.code, InstallResultCode::kSuccessNewInstall);
run_loop.Quit();
}));
run_loop.Run();
return GetProvider().registrar().LookupExternalAppId(app_url).value();
}
void SetTimeOverride(base::Time time_override) {
GetManifestUpdateManager(browser()).set_time_override_for_testing(
time_override);
}
ManifestUpdateResult GetResultAfterPageLoad(const GURL& url,
const AppId* app_id) {
UpdateCheckResultAwaiter awaiter(browser(), url);
ui_test_utils::NavigateToURL(browser(), url);
return std::move(awaiter).AwaitNextResult();
}
WebAppProviderBase& GetProvider() {
return *WebAppProviderBase::GetProviderBase(browser()->profile());
}
protected:
net::EmbeddedTestServer::HandleRequestCallback request_override_;
base::HistogramTester histogram_tester_;
net::EmbeddedTestServer http_server_;
private:
absl::optional<base::RunLoop> shortcut_run_loop_;
absl::optional<SkColor> updated_shortcut_top_left_color_;
ScopedOsHooksSuppress os_hooks_suppress_;
};
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
CheckOutOfScopeNavigation) {
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), nullptr),
ManifestUpdateResult::kNoAppInScope);
AppId app_id = InstallWebApp();
EXPECT_EQ(GetResultAfterPageLoad(GURL("http://example.org"), nullptr),
ManifestUpdateResult::kNoAppInScope);
histogram_tester_.ExpectTotalCount(kUpdateHistogramName, 0);
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, CheckIsThrottled) {
constexpr base::TimeDelta kDelayBetweenChecks = base::TimeDelta::FromDays(1);
base::Time time_override = base::Time::UnixEpoch();
SetTimeOverride(time_override);
AppId app_id = InstallWebApp();
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpToDate);
time_override += kDelayBetweenChecks / 2;
SetTimeOverride(time_override);
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kThrottled);
time_override += kDelayBetweenChecks;
SetTimeOverride(time_override);
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpToDate);
time_override += kDelayBetweenChecks / 2;
SetTimeOverride(time_override);
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kThrottled);
time_override += kDelayBetweenChecks * 2;
SetTimeOverride(time_override);
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpToDate);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kThrottled, 2);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpToDate, 3);
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
CheckCancelledByWebContentsDestroyed) {
AppId app_id = InstallWebApp();
GetManifestUpdateManager(browser()).hang_update_checks_for_testing();
GURL url = GetAppURL();
UpdateCheckResultAwaiter awaiter(browser(), url);
ui_test_utils::NavigateToURL(browser(), url);
chrome::CloseTab(browser());
EXPECT_EQ(std::move(awaiter).AwaitNextResult(),
ManifestUpdateResult::kWebContentsDestroyed);
histogram_tester_.ExpectBucketCount(
kUpdateHistogramName, ManifestUpdateResult::kWebContentsDestroyed, 1);
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
CheckCancelledByAppUninstalled) {
AppId app_id = InstallWebApp();
GetManifestUpdateManager(browser()).hang_update_checks_for_testing();
GURL url = GetAppURL();
ui_test_utils::NavigateToURL(browser(), url);
base::RunLoop run_loop;
UpdateCheckResultAwaiter awaiter(browser(), url);
GetProvider().install_finalizer().UninstallWebApp(
app_id, webapps::WebappUninstallSource::kAppMenu,
base::BindLambdaForTesting([&](bool uninstalled) {
EXPECT_TRUE(uninstalled);
run_loop.Quit();
}));
EXPECT_EQ(std::move(awaiter).AwaitNextResult(),
ManifestUpdateResult::kAppUninstalling);
run_loop.Run();
histogram_tester_.ExpectBucketCount(
kUpdateHistogramName, ManifestUpdateResult::kAppUninstalling, 1);
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
CheckIgnoresWhitespaceDifferences) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app",
"start_url": ".",
"scope": "/",
"display": "standalone",
"icons": $1
$2
}
)";
OverrideManifest(kManifestTemplate, {kInstallableIconList, ""});
AppId app_id = InstallWebApp();
OverrideManifest(kManifestTemplate, {kInstallableIconList, "\n\n\n\n"});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpToDate);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpToDate, 1);
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
CheckIgnoresNameChange) {
constexpr char kManifestTemplate[] = R"(
{
"name": "$1",
"start_url": ".",
"scope": "/",
"display": "standalone",
"icons": $2
}
)";
OverrideManifest(kManifestTemplate, {"Test app name", kInstallableIconList});
AppId app_id = InstallWebApp();
OverrideManifest(kManifestTemplate,
{"Different app name", kInstallableIconList});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpToDate);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpToDate, 1);
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
CheckIgnoresShortNameChange) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"short_name": "$1",
"start_url": ".",
"scope": "/",
"display": "standalone",
"icons": $2
}
)";
OverrideManifest(kManifestTemplate,
{"Short test app name", kInstallableIconList});
AppId app_id = InstallWebApp();
OverrideManifest(kManifestTemplate,
{"Different short test app name", kInstallableIconList});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpToDate);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpToDate, 1);
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
CheckNameUpdatesForDefaultApps) {
constexpr char kManifestTemplate[] = R"(
{
"name": "$1",
"start_url": ".",
"scope": "/",
"display": "standalone",
"icons": $2
}
)";
OverrideManifest(kManifestTemplate, {"Test app name", kInstallableIconList});
AppId app_id = InstallDefaultApp();
OverrideManifest(kManifestTemplate,
{"Different app name", kInstallableIconList});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpdated);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
EXPECT_EQ(GetProvider().registrar().GetAppShortName(app_id),
"Different app name");
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
CheckIgnoresStartUrlChange) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": "$1",
"scope": "/",
"display": "standalone",
"icons": $2
}
)";
OverrideManifest(kManifestTemplate, {"a.html", kInstallableIconList});
AppId app_id = InstallWebApp();
OverrideManifest(kManifestTemplate, {"b.html", kInstallableIconList});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppIdMismatch);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppIdMismatch, 1);
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
CheckIgnoresNoManifestChange) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "standalone",
"icons": $1
}
)";
OverrideManifest(kManifestTemplate, {kInstallableIconList});
AppId app_id = InstallWebApp();
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpToDate);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpToDate, 1);
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
CheckIgnoresInvalidManifest) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "standalone",
"icons": $1,
$2
}
)";
OverrideManifest(kManifestTemplate, {kInstallableIconList, ""});
AppId app_id = InstallWebApp();
OverrideManifest(kManifestTemplate, {kInstallableIconList,
"invalid manifest syntax !@#$%^*&()"});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppNotEligible);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppNotEligible, 1);
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
CheckIgnoresNonLocalApps) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "standalone",
"icons": $1,
"theme_color": "$2"
}
)";
OverrideManifest(kManifestTemplate, {kInstallableIconList, "blue"});
AppId app_id = InstallWebApp();
GetProvider().registry_controller().SetAppIsLocallyInstalled(app_id, false);
EXPECT_FALSE(GetProvider().registrar().IsLocallyInstalled(app_id));
OverrideManifest(kManifestTemplate, {kInstallableIconList, "red"});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kNoAppInScope);
histogram_tester_.ExpectTotalCount(kUpdateHistogramName, 0);
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
CheckIgnoresPlaceholderApps) {
// Set up app URL to redirect to force placeholder app to install.
const GURL app_url = GetAppURL();
request_override_ = base::BindLambdaForTesting(
[&app_url](const net::test_server::HttpRequest& request)
-> std::unique_ptr<net::test_server::HttpResponse> {
if (request.GetURL() != app_url)
return nullptr;
auto http_response =
std::make_unique<net::test_server::BasicHttpResponse>();
http_response->set_code(net::HTTP_TEMPORARY_REDIRECT);
http_response->AddCustomHeader(
"Location", "http://other-origin.com/defaultresponse");
http_response->set_content("redirect page");
return std::move(http_response);
});
// Install via ExternallyManagedAppManager, the redirect to a different origin
// should cause it to install a placeholder app.
AppId app_id = InstallPolicyApp();
EXPECT_TRUE(GetProvider().registrar().IsPlaceholderApp(app_id));
// Manifest updating should ignore non-redirect loads for placeholder apps
// because the ExternallyManagedAppManager will handle these.
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "standalone",
"icons": $1
}
)";
OverrideManifest(kManifestTemplate, {kInstallableIconList});
EXPECT_EQ(GetResultAfterPageLoad(app_url, &app_id),
ManifestUpdateResult::kAppIsPlaceholder);
histogram_tester_.ExpectBucketCount(
kUpdateHistogramName, ManifestUpdateResult::kAppIsPlaceholder, 1);
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
CheckFindsThemeColorChange) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "standalone",
"icons": $1,
"theme_color": "$2"
}
)";
OverrideManifest(kManifestTemplate, {kInstallableIconList, "blue"});
AppId app_id = InstallWebApp();
EXPECT_EQ(GetProvider().registrar().GetAppThemeColor(app_id), SK_ColorBLUE);
// Check that OnWebAppInstalled and OnWebAppWillBeUninstalled are not called
// if in-place web app update happens.
WebAppInstallObserver install_observer(&GetProvider().registrar());
install_observer.SetWebAppInstalledDelegate(
base::BindLambdaForTesting([](const AppId& app_id) { NOTREACHED(); }));
install_observer.SetWebAppUninstalledDelegate(
base::BindLambdaForTesting([](const AppId& app_id) { NOTREACHED(); }));
// CSS #RRGGBBAA syntax.
OverrideManifest(kManifestTemplate, {kInstallableIconList, "#00FF00F0"});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpdated);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
CheckShortcutInfoUpdated(app_id, kInstallableIconTopLeftColor);
// Updated theme_color loses any transparency.
EXPECT_EQ(GetProvider().registrar().GetAppThemeColor(app_id),
SkColorSetARGB(0xFF, 0x00, 0xFF, 0x00));
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
CheckFindsBackgroundColorChange) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "standalone",
"icons": $1,
"background_color": "$2"
}
)";
OverrideManifest(kManifestTemplate, {kInstallableIconList, "blue"});
AppId app_id = InstallWebApp();
EXPECT_EQ(GetProvider().registrar().GetAppBackgroundColor(app_id),
SK_ColorBLUE);
// CSS #RRGGBBAA syntax.
OverrideManifest(kManifestTemplate, {kInstallableIconList, "red"});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpdated);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
EXPECT_EQ(GetProvider().registrar().GetAppBackgroundColor(app_id),
SkColorSetARGB(0xFF, 0xFF, 0x00, 0x00));
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
CheckFindsManifestUrlChange) {
// This matches the content of chrome/test/data/banners/manifest_one_icon.json
constexpr char kManifestTemplate[] = R"(
{
"name": "Manifest test app",
"icons": [
{
"src": "image-512px.png",
"sizes": "512x512",
"type": "image/png"
}
],
"start_url": "manifest_test_page.html",
"display": "standalone",
"orientation": "landscape"
}
)";
OverrideManifest(kManifestTemplate, {});
AppId app_id = InstallWebApp();
EXPECT_EQ(GetProvider().registrar().GetAppManifestUrl(app_id),
GetManifestURL());
// Load a page which contains the same manifest content but at a new manifest
// URL.
url::Replacements<char> replacements;
std::string query = "manifest=/banners/manifest_one_icon.json";
replacements.SetQuery(query.c_str(), url::Component(0, query.length()));
GURL app_url_with_new_manifest = GetAppURL().ReplaceComponents(replacements);
EXPECT_EQ(GetResultAfterPageLoad(app_url_with_new_manifest, &app_id),
ManifestUpdateResult::kAppUpdated);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
EXPECT_EQ(GetProvider().registrar().GetAppManifestUrl(app_id),
http_server_.GetURL("/banners/manifest_one_icon.json"));
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, CheckKeepsSameName) {
constexpr char kManifestTemplate[] = R"(
{
"name": "$1",
"start_url": ".",
"scope": "/",
"display": "standalone",
"icons": $2,
"theme_color": "$3"
}
)";
OverrideManifest(kManifestTemplate,
{"App name 1", kInstallableIconList, "blue"});
AppId app_id = InstallWebApp();
EXPECT_EQ(GetProvider().registrar().GetAppThemeColor(app_id), SK_ColorBLUE);
EXPECT_EQ(GetProvider().registrar().GetAppShortName(app_id), "App name 1");
OverrideManifest(kManifestTemplate,
{"App name 2", kInstallableIconList, "red"});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpdated);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
CheckShortcutInfoUpdated(app_id, kInstallableIconTopLeftColor);
EXPECT_EQ(GetProvider().registrar().GetAppThemeColor(app_id), SK_ColorRED);
// The app name must not change without user confirmation.
EXPECT_EQ(GetProvider().registrar().GetAppShortName(app_id), "App name 1");
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
CheckDoesNotFindIconUrlChange) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "standalone",
"icons": $1
}
)";
OverrideManifest(kManifestTemplate, {kInstallableIconList});
AppId app_id = InstallWebApp();
OverrideManifest(kManifestTemplate, {kAnotherInstallableIconList});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpToDate);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 0);
CheckShortcutInfoUpdated(app_id, kInstallableIconTopLeftColor);
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
CheckDoesFindIconUrlChangeForDefaultApps) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "standalone",
"icons": $1
}
)";
OverrideManifest(kManifestTemplate, {kInstallableIconList});
AppId app_id = InstallDefaultApp();
OverrideManifest(kManifestTemplate, {kAnotherInstallableIconList});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpdated);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
CheckShortcutInfoUpdated(app_id, kAnotherInstallableIconTopLeftColor);
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
CheckUpdatedPolicyAppsNotUninstallable) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "standalone",
"theme_color": "$1",
"icons": $2
}
)";
OverrideManifest(kManifestTemplate, {"blue", kInstallableIconList});
AppId app_id = InstallPolicyApp();
EXPECT_FALSE(
GetProvider().install_finalizer().CanUserUninstallWebApp(app_id));
OverrideManifest(kManifestTemplate, {"red", kInstallableIconList});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpdated);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
CheckShortcutInfoUpdated(app_id, kInstallableIconTopLeftColor);
// Policy installed apps should continue to be not uninstallable by the user
// after updating.
EXPECT_FALSE(
GetProvider().install_finalizer().CanUserUninstallWebApp(app_id));
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
CheckFindsScopeChange) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "$1",
"display": "standalone",
"icons": $2
}
)";
OverrideManifest(kManifestTemplate, {"/banners/", kInstallableIconList});
AppId app_id = InstallWebApp();
OverrideManifest(kManifestTemplate, {"/", kInstallableIconList});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpdated);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
CheckShortcutInfoUpdated(app_id, kInstallableIconTopLeftColor);
EXPECT_EQ(GetProvider().registrar().GetAppScope(app_id),
http_server_.GetURL("/"));
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
CheckDoesNotApplyIconURLChange) {
// This test changes the scope and also the icon list. The scope should update
// but the icons should not.
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "$1",
"display": "standalone",
"icons": $2
}
)";
OverrideManifest(kManifestTemplate, {"/banners/", kInstallableIconList});
AppId app_id = InstallWebApp();
OverrideManifest(kManifestTemplate, {"/", kAnotherInstallableIconList});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpdated);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
// The icon should not be updated.
CheckShortcutInfoUpdated(app_id, kInstallableIconTopLeftColor);
EXPECT_EQ(GetProvider().registrar().GetAppScope(app_id),
http_server_.GetURL("/"));
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
CheckDoesApplyIconURLChangeForDefaultApps) {
// This test changes the scope and also the icon list. The scope should update
// along with the icons.
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "$1",
"display": "standalone",
"icons": $2
}
)";
OverrideManifest(kManifestTemplate, {"/banners/", kInstallableIconList});
AppId app_id = InstallDefaultApp();
OverrideManifest(kManifestTemplate, {"/", kAnotherInstallableIconList});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpdated);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
// The icon should have updated.
CheckShortcutInfoUpdated(app_id, kAnotherInstallableIconTopLeftColor);
EXPECT_EQ(GetProvider().registrar().GetAppScope(app_id),
http_server_.GetURL("/"));
}
class ManifestUpdateManagerBrowserTest_PolicyAppsCanUpdate
: public ManifestUpdateManagerBrowserTest,
public testing::WithParamInterface<bool> {
public:
ManifestUpdateManagerBrowserTest_PolicyAppsCanUpdate() {
if (GetParam()) {
scoped_feature_list_.InitAndEnableFeature(
features::kWebAppManifestPolicyAppIdentityUpdate);
}
}
bool ExpectUpdateAllowed() { return GetParam(); }
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
IN_PROC_BROWSER_TEST_P(ManifestUpdateManagerBrowserTest_PolicyAppsCanUpdate,
CheckDoesApplyIconURLChangeForPolicyAppsWithFlag) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"display": "standalone",
"icons": $1
}
)";
OverrideManifest(kManifestTemplate, {kInstallableIconList});
AppId app_id = InstallPolicyApp();
OverrideManifest(kManifestTemplate, {kAnotherInstallableIconList});
if (ExpectUpdateAllowed()) {
// The icon should have updated (because the flag is enabled).
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpdated);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
CheckShortcutInfoUpdated(app_id, kAnotherInstallableIconTopLeftColor);
} else {
// The icon should not have updated.
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpToDate);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 0);
CheckShortcutInfoUpdated(app_id, kInstallableIconTopLeftColor);
}
}
// This test ensures app name cannot be changed for policy apps (without a flag
// allowing it).
IN_PROC_BROWSER_TEST_P(ManifestUpdateManagerBrowserTest_PolicyAppsCanUpdate,
CheckNameUpdatesForPolicyApps) {
constexpr char kManifestTemplate[] = R"(
{
"name": "$1",
"start_url": ".",
"scope": "/",
"display": "standalone",
"icons": $2
}
)";
OverrideManifest(kManifestTemplate, {"Test app name", kInstallableIconList});
AppId app_id = InstallPolicyApp();
OverrideManifest(kManifestTemplate,
{"Different app name", kInstallableIconList});
if (ExpectUpdateAllowed()) {
// Name should have updated (because the flag is enabled).
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpdated);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
EXPECT_EQ(GetProvider().registrar().GetAppShortName(app_id),
"Different app name");
} else {
// Name should not have updated (because the flag is missing).
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpToDate);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 0);
EXPECT_EQ(GetProvider().registrar().GetAppShortName(app_id),
"Test app name");
}
}
INSTANTIATE_TEST_SUITE_P(PolicyAppParameterizedTest,
ManifestUpdateManagerBrowserTest_PolicyAppsCanUpdate,
::testing::Values(true, false));
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
CheckFindsDisplayChange) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "$1",
"icons": $2
}
)";
OverrideManifest(kManifestTemplate, {"minimal-ui", kInstallableIconList});
AppId app_id = InstallWebApp();
OverrideManifest(kManifestTemplate, {"standalone", kInstallableIconList});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpdated);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
CheckShortcutInfoUpdated(app_id, kInstallableIconTopLeftColor);
EXPECT_EQ(GetProvider().registrar().GetAppDisplayMode(app_id),
DisplayMode::kStandalone);
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
CheckFindsDisplayBrowserChange) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "$1",
"icons": $2
}
)";
OverrideManifest(kManifestTemplate, {"standalone", kInstallableIconList});
AppId app_id = InstallWebApp();
GetProvider().registry_controller().SetAppUserDisplayMode(
app_id, DisplayMode::kStandalone, /*is_user_action=*/false);
OverrideManifest(kManifestTemplate, {"browser", kInstallableIconList});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpdated);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
EXPECT_EQ(GetProvider().registrar().GetAppDisplayMode(app_id),
DisplayMode::kBrowser);
// We don't touch the user's launch preference even if the app display mode
// changes. Instead the effective display mode changes.
EXPECT_EQ(GetProvider().registrar().GetAppUserDisplayMode(app_id),
DisplayMode::kStandalone);
EXPECT_EQ(GetProvider().registrar().GetAppEffectiveDisplayMode(app_id),
DisplayMode::kMinimalUi);
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
CheckFindsDisplayOverrideChange) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "standalone",
"display_override": $1,
"icons": $2
}
)";
OverrideManifest(kManifestTemplate,
{R"([ "fullscreen", "standalone" ])", kInstallableIconList});
AppId app_id = InstallWebApp();
OverrideManifest(kManifestTemplate,
{R"([ "fullscreen", "minimal-ui" ])", kInstallableIconList});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpdated);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
CheckShortcutInfoUpdated(app_id, kInstallableIconTopLeftColor);
std::vector<DisplayMode> app_display_mode_override =
GetProvider().registrar().GetAppDisplayModeOverride(app_id);
ASSERT_EQ(2u, app_display_mode_override.size());
EXPECT_EQ(DisplayMode::kFullscreen, app_display_mode_override[0]);
EXPECT_EQ(DisplayMode::kMinimalUi, app_display_mode_override[1]);
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
CheckFindsNewDisplayOverride) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "standalone",
$1
"icons": $2
}
)";
// No display_override in manifest
OverrideManifest(kManifestTemplate, {"", kInstallableIconList});
AppId app_id = InstallWebApp();
// Add display_override field
OverrideManifest(kManifestTemplate,
{R"("display_override": [ "minimal-ui", "standalone" ],)",
kInstallableIconList});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpdated);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
CheckShortcutInfoUpdated(app_id, kInstallableIconTopLeftColor);
std::vector<DisplayMode> app_display_mode_override =
GetProvider().registrar().GetAppDisplayModeOverride(app_id);
ASSERT_EQ(2u, app_display_mode_override.size());
EXPECT_EQ(DisplayMode::kMinimalUi, app_display_mode_override[0]);
EXPECT_EQ(DisplayMode::kStandalone, app_display_mode_override[1]);
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
CheckFindsDeletedDisplayOverride) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "standalone",
$1
"icons": $2
}
)";
// Ensure display_override exists in initial manifest
OverrideManifest(kManifestTemplate,
{R"("display_override": [ "fullscreen", "minimal-ui" ],)",
kInstallableIconList});
AppId app_id = InstallWebApp();
// Remove display_override from manifest
OverrideManifest(kManifestTemplate, {"", kInstallableIconList});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpdated);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
CheckShortcutInfoUpdated(app_id, kInstallableIconTopLeftColor);
std::vector<DisplayMode> app_display_mode_override =
GetProvider().registrar().GetAppDisplayModeOverride(app_id);
ASSERT_EQ(0u, app_display_mode_override.size());
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
CheckFindsInvalidDisplayOverride) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "standalone",
"display_override": $1,
"icons": $2
}
)";
OverrideManifest(kManifestTemplate,
{R"([ "browser", "fullscreen" ])", kInstallableIconList});
AppId app_id = InstallWebApp();
ASSERT_EQ(2u,
GetProvider().registrar().GetAppDisplayModeOverride(app_id).size());
// display_override contains only invalid values
OverrideManifest(kManifestTemplate,
{R"( [ "invalid", 7 ])", kInstallableIconList});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpdated);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
CheckShortcutInfoUpdated(app_id, kInstallableIconTopLeftColor);
std::vector<DisplayMode> app_display_mode_override =
GetProvider().registrar().GetAppDisplayModeOverride(app_id);
ASSERT_EQ(0u, app_display_mode_override.size());
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
CheckIgnoresDisplayOverrideInvalidChange) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "standalone",
$1
"icons": $2
}
)";
// No display_override in manifest
OverrideManifest(kManifestTemplate, {"", kInstallableIconList});
AppId app_id = InstallWebApp();
// display_override contains only invalid values
OverrideManifest(
kManifestTemplate,
{R"("display_override": [ "invalid", 7 ],)", kInstallableIconList});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpToDate);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpToDate, 1);
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
CheckIgnoresDisplayOverrideChange) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "standalone",
"display_override": $1,
"icons": $2
}
)";
OverrideManifest(kManifestTemplate,
{R"([ "standard", "fullscreen" ])", kInstallableIconList});
AppId app_id = InstallWebApp();
// display_override contains an additional invalid value
OverrideManifest(
kManifestTemplate,
{R"([ "invalid", "standard", "fullscreen" ])", kInstallableIconList});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpToDate);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpToDate, 1);
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
CheckDoesNotFindIconContentChange) {
constexpr char kManifest[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "standalone",
"icons": [
{
"src": "/web_apps/basic-192.png?ignore",
"sizes": "192x192",
"type": "image/png"
}
]
}
)";
OverrideManifest(kManifest, {});
AppId app_id = InstallWebApp();
// Replace the contents of basic-192.png with blue-192.png without changing
// the URL.
content::URLLoaderInterceptor url_interceptor(base::BindLambdaForTesting(
[this](content::URLLoaderInterceptor::RequestParams* params)
-> bool /*intercepted*/ {
if (params->url_request.url ==
http_server_.GetURL("/web_apps/basic-192.png?ignore")) {
content::URLLoaderInterceptor::WriteResponse(
"chrome/test/data/web_apps/blue-192.png", params->client.get());
return true;
}
return false;
}));
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpToDate);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 0);
CheckShortcutInfoUpdated(app_id, kBasicIconTopLeftColor);
EXPECT_EQ(ReadAppIconPixel(browser()->profile(), app_id, /*size=*/192,
/*x=*/0, /*y=*/0),
SK_ColorBLACK);
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
CheckDoesNotUpdateGeneratedIcons) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "standalone",
"icons": $1
}
)";
OverrideManifest(kManifestTemplate, {"[]"});
AppId app_id = InstallWebApp();
OverrideManifest(kManifestTemplate, {kAnotherInstallableIconList});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpToDate);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 0);
}
class ManifestUpdateManagerCaptureLinksBrowserTest
: public ManifestUpdateManagerBrowserTest {
base::test::ScopedFeatureList scoped_feature_list_{
blink::features::kWebAppEnableLinkCapturing};
};
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerCaptureLinksBrowserTest,
CheckFindsCaptureLinksChange) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "standalone",
"icons": $1,
"capture_links": "$2"
}
)";
OverrideManifest(kManifestTemplate, {kInstallableIconList, "none"});
AppId app_id = InstallWebApp();
EXPECT_EQ(GetProvider().registrar().GetAppCaptureLinks(app_id),
blink::mojom::CaptureLinks::kNone);
OverrideManifest(kManifestTemplate, {kInstallableIconList, "new-client"});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpdated);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
CheckShortcutInfoUpdated(app_id, kInstallableIconTopLeftColor);
EXPECT_EQ(GetProvider().registrar().GetAppCaptureLinks(app_id),
blink::mojom::CaptureLinks::kNewClient);
}
class ManifestUpdateManagerSystemAppBrowserTest
: public ManifestUpdateManagerBrowserTest {
public:
ManifestUpdateManagerSystemAppBrowserTest()
: system_app_(
TestSystemWebAppInstallation::SetUpStandaloneSingleWindowApp()) {}
void SetUpOnMainThread() override { system_app_->WaitForAppInstall(); }
protected:
std::unique_ptr<TestSystemWebAppInstallation> system_app_;
};
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerSystemAppBrowserTest,
CheckUpdateSkipped) {
AppId app_id = system_app_->GetAppId();
EXPECT_EQ(GetResultAfterPageLoad(system_app_->GetAppUrl(), &app_id),
ManifestUpdateResult::kAppIsSystemWebApp);
histogram_tester_.ExpectBucketCount(
kUpdateHistogramName, ManifestUpdateResult::kAppIsSystemWebApp, 1);
EXPECT_EQ(GetProvider().registrar().GetAppThemeColor(app_id), SK_ColorGREEN);
}
using ManifestUpdateManagerWebAppsBrowserTest =
ManifestUpdateManagerBrowserTest;
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerWebAppsBrowserTest,
CheckFindsAddedShareTarget) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "minimal-ui",
"icons": $1
}
)";
constexpr char kShareTargetManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "minimal-ui",
"share_target": {
"action": "/web_share_target/share.html",
"method": "GET",
"params": {
"url": "link"
}
},
"icons": $1
}
)";
OverrideManifest(kManifestTemplate, {kInstallableIconList});
AppId app_id = InstallWebApp();
OverrideManifest(kShareTargetManifestTemplate, {kInstallableIconList});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpdated);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
const WebApp* web_app =
GetProvider().registrar().AsWebAppRegistrar()->GetAppById(app_id);
EXPECT_TRUE(web_app->share_target().has_value());
EXPECT_EQ(web_app->share_target()->method, apps::ShareTarget::Method::kGet);
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerWebAppsBrowserTest,
CheckFindsShareTargetChange) {
constexpr char kShareTargetManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "minimal-ui",
"share_target": {
"action": "/web_share_target/share.html",
"method": "$1",
"params": {
"url": "link"
}
},
"icons": $2
}
)";
OverrideManifest(kShareTargetManifestTemplate, {"GET", kInstallableIconList});
AppId app_id = InstallWebApp();
OverrideManifest(kShareTargetManifestTemplate,
{"POST", kInstallableIconList});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpdated);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
const WebApp* web_app =
GetProvider().registrar().AsWebAppRegistrar()->GetAppById(app_id);
EXPECT_TRUE(web_app->share_target().has_value());
EXPECT_EQ(web_app->share_target()->method, apps::ShareTarget::Method::kPost);
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerWebAppsBrowserTest,
CheckFindsDeletedShareTarget) {
constexpr char kShareTargetManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "minimal-ui",
"share_target": {
"action": "/web_share_target/share.html",
"method": "GET",
"params": {
"url": "link"
}
},
"icons": $1
}
)";
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "minimal-ui",
"icons": $1
}
)";
OverrideManifest(kShareTargetManifestTemplate, {kInstallableIconList});
AppId app_id = InstallWebApp();
OverrideManifest(kManifestTemplate, {kInstallableIconList});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpdated);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
const WebApp* web_app =
GetProvider().registrar().AsWebAppRegistrar()->GetAppById(app_id);
EXPECT_FALSE(web_app->share_target().has_value());
}
// Functional tests. More tests for detecting file handler updates are
// available in unit tests at ManifestUpdateTaskTest.
class ManifestUpdateManagerBrowserTestWithFileHandling
: public ManifestUpdateManagerBrowserTest {
base::test::ScopedFeatureList scoped_feature_list_{
blink::features::kFileHandlingAPI};
};
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithFileHandling,
CheckFindsAddedFileHandler) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "minimal-ui",
"icons": $1
}
)";
constexpr char kFileHandlerManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "minimal-ui",
"file_handlers": [
{
"action": "/?plaintext",
"name": "Plain Text",
"accept": {
"text/plain": [".txt"]
}
}
],
"icons": $1
}
)";
OverrideManifest(kManifestTemplate, {kInstallableIconList});
AppId app_id = InstallWebApp();
OverrideManifest(kFileHandlerManifestTemplate, {kInstallableIconList});
EXPECT_EQ(ManifestUpdateResult::kAppUpdated,
GetResultAfterPageLoad(GetAppURL(), &app_id));
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
const WebApp* web_app =
GetProvider().registrar().AsWebAppRegistrar()->GetAppById(app_id);
EXPECT_FALSE(web_app->file_handlers().empty());
const auto& file_handler = web_app->file_handlers()[0];
EXPECT_EQ("plaintext", file_handler.action.query());
EXPECT_EQ(1u, file_handler.accept.size());
EXPECT_EQ("text/plain", file_handler.accept[0].mime_type);
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithFileHandling,
CheckIgnoresUnchangedFileHandler) {
constexpr char kFileHandlerManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "minimal-ui",
"file_handlers": [
{
"action": "/?plaintext",
"name": "Plain Text",
"accept": {
"text/plain": [".txt"]
}
}
],
"icons": $1
}
)";
OverrideManifest(kFileHandlerManifestTemplate, {kInstallableIconList});
AppId app_id = InstallWebApp();
OverrideManifest(kFileHandlerManifestTemplate, {kInstallableIconList});
EXPECT_EQ(ManifestUpdateResult::kAppUpToDate,
GetResultAfterPageLoad(GetAppURL(), &app_id));
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpToDate, 1);
const WebApp* web_app =
GetProvider().registrar().AsWebAppRegistrar()->GetAppById(app_id);
EXPECT_FALSE(web_app->file_handlers().empty());
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithFileHandling,
CheckFindsChangedFileExtension) {
constexpr char kFileHandlerManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "minimal-ui",
"file_handlers": [
{
"action": "/?plaintext",
"name": "Plain Text",
"accept": {
"text/plain": ["$1"]
}
}
],
"icons": $2
}
)";
OverrideManifest(kFileHandlerManifestTemplate,
{".txt", kInstallableIconList});
AppId app_id = InstallWebApp();
const WebApp* web_app =
GetProvider().registrar().AsWebAppRegistrar()->GetAppById(app_id);
const auto& old_file_handler = web_app->file_handlers()[0];
EXPECT_EQ(1u, old_file_handler.accept.size());
auto old_extensions = old_file_handler.accept[0].file_extensions;
EXPECT_EQ(1u, old_extensions.size());
EXPECT_TRUE(base::Contains(old_extensions, ".txt"));
OverrideManifest(kFileHandlerManifestTemplate, {".md", kInstallableIconList});
EXPECT_EQ(ManifestUpdateResult::kAppUpdated,
GetResultAfterPageLoad(GetAppURL(), &app_id));
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
const auto& new_file_handler = web_app->file_handlers()[0];
EXPECT_EQ(1u, new_file_handler.accept.size());
auto new_extensions = new_file_handler.accept[0].file_extensions;
EXPECT_EQ(1u, new_extensions.size());
EXPECT_TRUE(base::Contains(new_extensions, ".md"));
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithFileHandling,
FileHandlingPermissionResetsOnUpdate) {
constexpr char kFileHandlerManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "minimal-ui",
"file_handlers": [
{
"action": "/?plaintext",
"name": "Plain Text",
"accept": {
"text/plain": ["$1"]
}
}
],
"icons": [
{
"src": "launcher-icon-4x.png",
"sizes": "192x192",
"type": "image/png"
}
],
"theme_color": "$2"
}
)";
OverrideManifest(kFileHandlerManifestTemplate, {".txt", "red"});
AppId app_id = InstallWebApp();
const WebApp* web_app =
GetProvider().registrar().AsWebAppRegistrar()->GetAppById(app_id);
const auto& old_file_handler = web_app->file_handlers()[0];
auto old_extensions = old_file_handler.accept[0].file_extensions;
EXPECT_TRUE(base::Contains(old_extensions, ".txt"));
auto* map =
HostContentSettingsMapFactory::GetForProfile(browser()->profile());
const GURL url = GetAppURL();
const GURL origin = url.GetOrigin();
EXPECT_EQ(CONTENT_SETTING_ASK,
map->GetContentSetting(origin, origin,
ContentSettingsType::FILE_HANDLING));
// Set permission to ALLOW.
map->SetContentSettingDefaultScope(origin, origin,
ContentSettingsType::FILE_HANDLING,
CONTENT_SETTING_ALLOW);
EXPECT_EQ(CONTENT_SETTING_ALLOW,
map->GetContentSetting(origin, origin,
ContentSettingsType::FILE_HANDLING));
// Update manifest, adding an extension to the file handler. Permission should
// be downgraded to ASK. The time override is necessary to make sure the
// manifest update isn't skipped due to throttling.
base::Time time_override = base::Time::Now();
SetTimeOverride(time_override);
OverrideManifest(kFileHandlerManifestTemplate, {".md\", \".txt", "red"});
EXPECT_EQ(ManifestUpdateResult::kAppUpdated,
GetResultAfterPageLoad(url, &app_id));
auto new_extensions = web_app->file_handlers()[0].accept[0].file_extensions;
EXPECT_TRUE(base::Contains(new_extensions, ".md"));
EXPECT_TRUE(base::Contains(new_extensions, ".txt"));
EXPECT_EQ(CONTENT_SETTING_ASK,
map->GetContentSetting(origin, origin,
ContentSettingsType::FILE_HANDLING));
// Set permission to ALLOW.
map->SetContentSettingDefaultScope(origin, origin,
ContentSettingsType::FILE_HANDLING,
CONTENT_SETTING_ALLOW);
// Update manifest, but keep same file handlers. Permission should be left on
// ALLOW.
time_override += base::TimeDelta::FromDays(10);
SetTimeOverride(time_override);
OverrideManifest(kFileHandlerManifestTemplate, {".md\", \".txt", "blue"});
EXPECT_EQ(ManifestUpdateResult::kAppUpdated,
GetResultAfterPageLoad(url, &app_id));
new_extensions = web_app->file_handlers()[0].accept[0].file_extensions;
EXPECT_TRUE(base::Contains(new_extensions, ".md"));
EXPECT_TRUE(base::Contains(new_extensions, ".txt"));
EXPECT_EQ(CONTENT_SETTING_ALLOW,
map->GetContentSetting(origin, origin,
ContentSettingsType::FILE_HANDLING));
// Update manifest, asking for /fewer/ file types. Permission should be left
// on ALLOW.
time_override += base::TimeDelta::FromDays(10);
SetTimeOverride(time_override);
OverrideManifest(kFileHandlerManifestTemplate, {".txt", "blue"});
EXPECT_EQ(ManifestUpdateResult::kAppUpdated,
GetResultAfterPageLoad(url, &app_id));
new_extensions = web_app->file_handlers()[0].accept[0].file_extensions;
EXPECT_FALSE(base::Contains(new_extensions, ".md"));
EXPECT_TRUE(base::Contains(new_extensions, ".txt"));
EXPECT_EQ(CONTENT_SETTING_ALLOW,
map->GetContentSetting(origin, origin,
ContentSettingsType::FILE_HANDLING));
// Block the permission, update manifest, permission should still be block.
map->SetContentSettingDefaultScope(origin, origin,
ContentSettingsType::FILE_HANDLING,
CONTENT_SETTING_BLOCK);
OverrideManifest(kFileHandlerManifestTemplate, {".txt", "red"});
time_override += base::TimeDelta::FromDays(10);
SetTimeOverride(time_override);
EXPECT_EQ(ManifestUpdateResult::kAppUpdated,
GetResultAfterPageLoad(url, &app_id));
EXPECT_EQ(CONTENT_SETTING_BLOCK,
map->GetContentSetting(origin, origin,
ContentSettingsType::FILE_HANDLING));
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithFileHandling,
BlockPermissionRemoveFileHandlers) {
constexpr char kFileHandlerManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "minimal-ui",
"file_handlers": [
{
"action": "/?plaintext",
"name": "Plain Text",
"accept": {
"text/plain": ["$1"]
}
}
],
"icons": $2
}
)";
OverrideManifest(kFileHandlerManifestTemplate,
{".txt", kInstallableIconList});
AppId app_id = InstallWebApp();
const WebAppRegistrar* registrar =
GetProvider().registrar().AsWebAppRegistrar();
const WebApp* web_app = registrar->GetAppById(app_id);
EXPECT_FALSE(web_app->file_handler_permission_blocked());
ASSERT_FALSE(web_app->file_handlers().empty());
const auto& old_file_handler = web_app->file_handlers()[0];
ASSERT_FALSE(old_file_handler.accept.empty());
auto old_extensions = old_file_handler.accept[0].file_extensions;
EXPECT_TRUE(base::Contains(old_extensions, ".txt"));
auto* map =
HostContentSettingsMapFactory::GetForProfile(browser()->profile());
const GURL url = GetAppURL();
const GURL origin = url.GetOrigin();
EXPECT_EQ(CONTENT_SETTING_ASK,
map->GetContentSetting(origin, origin,
ContentSettingsType::FILE_HANDLING));
// Set permission to BLOCK.
map->SetContentSettingDefaultScope(origin, origin,
ContentSettingsType::FILE_HANDLING,
CONTENT_SETTING_BLOCK);
EXPECT_EQ(CONTENT_SETTING_BLOCK,
map->GetContentSetting(origin, origin,
ContentSettingsType::FILE_HANDLING));
// App should be updated to permission blocked by
// `WebAppInstallFinalizer::OnContentSettingChanged`.
EXPECT_TRUE(registrar->GetAppById(app_id)->file_handler_permission_blocked());
// Update manifest.
OverrideManifest(kFileHandlerManifestTemplate, {".md", kInstallableIconList});
EXPECT_EQ(ManifestUpdateResult::kAppUpdated,
GetResultAfterPageLoad(url, &app_id));
// Manifest update task should preserve the permission blocked state.
EXPECT_TRUE(registrar->GetAppById(app_id)->file_handler_permission_blocked());
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithFileHandling,
CheckFindsDeletedFileHandler) {
constexpr char kFileHandlerManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "minimal-ui",
"file_handlers": [
{
"action": "/?plaintext",
"name": "Plain Text",
"accept": {
"text/plain": [".txt"]
}
}
],
"icons": $1
}
)";
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "minimal-ui",
"icons": $1
}
)";
OverrideManifest(kFileHandlerManifestTemplate, {kInstallableIconList});
AppId app_id = InstallWebApp();
OverrideManifest(kManifestTemplate, {kInstallableIconList});
EXPECT_EQ(ManifestUpdateResult::kAppUpdated,
GetResultAfterPageLoad(GetAppURL(), &app_id));
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
const WebApp* web_app =
GetProvider().registrar().AsWebAppRegistrar()->GetAppById(app_id);
EXPECT_TRUE(web_app->file_handlers().empty());
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithFileHandling,
CheckFileExtensionList) {
constexpr char kFileHandlerManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "minimal-ui",
"file_handlers": [
{
"action": "/?plaintext",
"name": "Plain Text",
"accept": {
"text/plain": [".txt"]
}
}
],
"icons": $1
}
)";
OverrideManifest(kFileHandlerManifestTemplate, {kInstallableIconList});
InstallWebApp();
std::u16string associations_list =
GetFileTypeAssociationsHandledByWebAppsForDisplay(browser()->profile(),
GetAppURL());
#if defined(OS_LINUX)
EXPECT_EQ(u"text/plain", associations_list);
#else
EXPECT_EQ(u"TXT", associations_list);
#endif // defined(OS_LINUX)
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithFileHandling,
CheckFileExtensionsList) {
constexpr char kFileHandlerManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "minimal-ui",
"file_handlers": [
{
"action": "/?plaintext",
"name": "Plain Text",
"accept": {
"text/plain": [".txt", ".md"]
}
}
],
"icons": $1
}
)";
OverrideManifest(kFileHandlerManifestTemplate, {kInstallableIconList});
InstallWebApp();
std::u16string associations_list =
GetFileTypeAssociationsHandledByWebAppsForDisplay(browser()->profile(),
GetAppURL());
#if defined(OS_LINUX)
EXPECT_EQ(u"text/plain", associations_list);
#else
EXPECT_EQ(u"MD, TXT", associations_list);
#endif // defined(OS_LINUX)
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithFileHandling,
CheckFileExtensionsListWithTwoFileHandlers) {
constexpr char kFileHandlerManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "minimal-ui",
"file_handlers": [
{
"action": "/?plaintext",
"name": "Plain Text",
"accept": {
"text/plain": [".txt"]
}
},
{
"action": "/?longtype",
"name": "Long Custom type",
"accept": {
"long/type": [".longtype"]
}
}
],
"icons": $1
}
)";
OverrideManifest(kFileHandlerManifestTemplate, {kInstallableIconList});
InstallWebApp();
std::u16string associations_list =
GetFileTypeAssociationsHandledByWebAppsForDisplay(browser()->profile(),
GetAppURL());
#if defined(OS_LINUX)
EXPECT_EQ(u"long/type, text/plain", associations_list);
#else
EXPECT_EQ(u"LONGTYPE, TXT", associations_list);
#endif // defined(OS_LINUX)
}
class ManifestUpdateManagerBrowserTestWithShortcutsMenu
: public ManifestUpdateManagerBrowserTest {
base::test::ScopedFeatureList scoped_feature_list_{
features::kDesktopPWAsAppIconShortcutsMenu};
};
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithShortcutsMenu,
CheckFindsShortcutsMenuUpdated) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "standalone",
"icons": $1,
"shortcuts": $2
}
)";
OverrideManifest(kManifestTemplate, {kInstallableIconList, kShortcutsItem});
AppId app_id = InstallWebApp();
OverrideManifest(kManifestTemplate, {kInstallableIconList, kShortcutsItems});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpdated);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
EXPECT_EQ(
GetProvider().registrar().GetAppShortcutsMenuItemInfos(app_id).size(),
2u);
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithShortcutsMenu,
CheckFindsItemNameUpdated) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "standalone",
"icons": $1,
"shortcuts": [
{
"name": "$2",
"short_name": "HM",
"description": "Go home",
"url": ".",
"icons": [
{
"src": "/banners/image-512px.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
]
}
)";
OverrideManifest(kManifestTemplate, {kInstallableIconList, "Home"});
AppId app_id = InstallWebApp();
OverrideManifest(kManifestTemplate,
{kInstallableIconList, kAnotherShortcutsItemName});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpdated);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
EXPECT_EQ(
GetProvider().registrar().GetAppShortcutsMenuItemInfos(app_id)[0].name,
kAnotherShortcutsItemName16);
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithShortcutsMenu,
CheckIgnoresShortNameAndDescriptionChange) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "standalone",
"icons": $1,
"shortcuts": [
{
"name": "Home",
"short_name": "$2",
"description": "$3",
"url": ".",
"icons": [
{
"src": "/banners/image-512px.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
]
}
)";
OverrideManifest(kManifestTemplate, {kInstallableIconList, "HM", "Go home"});
AppId app_id = InstallWebApp();
OverrideManifest(kManifestTemplate,
{kInstallableIconList, kAnotherShortcutsItemShortName,
kAnotherShortcutsItemDescription});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpToDate);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpToDate, 1);
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithShortcutsMenu,
CheckFindsItemUrlUpdated) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "standalone",
"icons": $1,
"shortcuts": [
{
"name": "Home",
"short_name": "HM",
"description": "Go home",
"url": "$2",
"icons": [
{
"src": "/banners/image-512px.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
]
}
)";
OverrideManifest(kManifestTemplate, {kInstallableIconList, "/"});
AppId app_id = InstallWebApp();
OverrideManifest(kManifestTemplate,
{kInstallableIconList, kAnotherShortcutsItemUrl});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpdated);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
EXPECT_EQ(
GetProvider().registrar().GetAppShortcutsMenuItemInfos(app_id)[0].url,
http_server_.GetURL(kAnotherShortcutsItemUrl));
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithShortcutsMenu,
CheckFindsShortcutIconContentChange) {
constexpr char kManifest[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "standalone",
"icons": $1,
"shortcuts": [
{
"name": "Home",
"short_name": "HM",
"description": "Go home",
"url": "/",
"icons": [
{
"src": "/web_apps/basic-192.png?ignore",
"sizes": "192x192",
"type": "image/png"
}
]
}
]
}
)";
OverrideManifest(kManifest, {kInstallableIconList});
AppId app_id = InstallWebApp();
// Replace the contents of basic-192.png with blue-192.png without changing
// the URL.
content::URLLoaderInterceptor url_interceptor(base::BindLambdaForTesting(
[this](content::URLLoaderInterceptor::RequestParams* params)
-> bool /*intercepted*/ {
if (params->url_request.url ==
http_server_.GetURL("/web_apps/basic-192.png?ignore")) {
content::URLLoaderInterceptor::WriteResponse(
"chrome/test/data/web_apps/blue-192.png", params->client.get());
return true;
}
return false;
}));
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpdated);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
// Check that the installed icon is now blue.
base::RunLoop run_loop;
GetProvider().icon_manager().ReadAllShortcutsMenuIcons(
app_id,
base::BindLambdaForTesting(
[&run_loop](ShortcutsMenuIconBitmaps shortcuts_menu_icon_bitmaps) {
run_loop.Quit();
EXPECT_EQ(shortcuts_menu_icon_bitmaps[0].any.at(192).getColor(0, 0),
SK_ColorBLUE);
}));
run_loop.Run();
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithShortcutsMenu,
ShortcutIconContentChangeDoesNotApplyAppIconUpdate) {
constexpr char kManifest[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "standalone",
"icons": $1,
"shortcuts": [
{
"name": "Home",
"short_name": "HM",
"description": "Go home",
"url": "/",
"icons": [
{
"src": "/web_apps/basic-192.png?ignore",
"sizes": "192x192",
"type": "image/png"
}
]
}
]
}
)";
OverrideManifest(kManifest, {kInstallableIconList});
AppId app_id = InstallWebApp();
// Replace the contents of basic-192.png with blue-192.png without changing
// the URL.
content::URLLoaderInterceptor url_interceptor(base::BindLambdaForTesting(
[this](content::URLLoaderInterceptor::RequestParams* params)
-> bool /*intercepted*/ {
if (params->url_request.url ==
http_server_.GetURL("/web_apps/basic-192.png?ignore")) {
content::URLLoaderInterceptor::WriteResponse(
"chrome/test/data/web_apps/blue-192.png", params->client.get());
return true;
}
return false;
}));
OverrideManifest(kManifest, {kAnotherInstallableIconList});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpdated);
// The icon should not be updated.
CheckShortcutInfoUpdated(app_id, kInstallableIconTopLeftColor);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithShortcutsMenu,
CheckFindsShortcutIconSrcUpdated) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "standalone",
"icons": $1,
"shortcuts": [
{
"name": "Home",
"short_name": "HM",
"description": "Go home",
"url": ".",
"icons": [
{
"src": "$2",
"sizes": "512x512",
"type": "image/png"
}
]
}
]
}
)";
OverrideManifest(kManifestTemplate,
{kInstallableIconList, "/banners/image-512px.png"});
AppId app_id = InstallWebApp();
OverrideManifest(kManifestTemplate, {kInstallableIconList, kAnotherIconSrc});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpdated);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
EXPECT_EQ(GetProvider()
.registrar()
.GetAppShortcutsMenuItemInfos(app_id)[0]
.GetShortcutIconInfosForPurpose(IconPurpose::ANY)[0]
.url,
http_server_.GetURL(kAnotherIconSrc));
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithShortcutsMenu,
CheckFindsShortcutIconSizesUpdated) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "standalone",
"icons": $1,
"shortcuts": [
{
"name": "Home",
"short_name": "HM",
"description": "Go home",
"url": ".",
"icons": [
{
"src": "/banners/image-512px.png",
"sizes": "$2",
"type": "image/png"
}
]
}
]
}
)";
OverrideManifest(kManifestTemplate, {kInstallableIconList, "512x512"});
AppId app_id = InstallWebApp();
OverrideManifest(kManifestTemplate,
{kInstallableIconList,
gfx::Size(kAnotherIconSize, kAnotherIconSize).ToString()});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpdated);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
EXPECT_EQ(GetProvider()
.registrar()
.GetAppShortcutsMenuItemInfos(app_id)[0]
.GetShortcutIconInfosForPurpose(IconPurpose::ANY)[0]
.square_size_px,
kAnotherIconSize);
}
class ManifestUpdateManagerIconUpdatingBrowserTest
: public ManifestUpdateManagerBrowserTest {
base::test::ScopedFeatureList scoped_feature_list_{
features::kWebAppManifestIconUpdating};
};
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerIconUpdatingBrowserTest,
CheckFindsIconContentChange) {
constexpr char kManifest[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "standalone",
"icons": [
{
"src": "/web_apps/basic-192.png?ignore",
"sizes": "192x192",
"type": "image/png"
}
]
}
)";
OverrideManifest(kManifest, {});
AppId app_id = InstallWebApp();
// Replace the contents of basic-192.png with blue-192.png without changing
// the URL.
content::URLLoaderInterceptor url_interceptor(base::BindLambdaForTesting(
[this](content::URLLoaderInterceptor::RequestParams* params)
-> bool /*intercepted*/ {
if (params->url_request.url ==
http_server_.GetURL("/web_apps/basic-192.png?ignore")) {
content::URLLoaderInterceptor::WriteResponse(
"chrome/test/data/web_apps/blue-192.png", params->client.get());
return true;
}
return false;
}));
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpdated);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
// The icon should have changed, as the file has been updated (but the url is
// the same).
CheckShortcutInfoUpdated(app_id, SK_ColorBLUE);
EXPECT_EQ(ReadAppIconPixel(browser()->profile(), app_id, /*size=*/192,
/*x=*/0, /*y=*/0),
SK_ColorBLUE);
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerIconUpdatingBrowserTest,
CheckFindsIconUrlChange) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "standalone",
"icons": $1
}
)";
OverrideManifest(kManifestTemplate, {kInstallableIconList});
AppId app_id = InstallWebApp();
OverrideManifest(kManifestTemplate, {kAnotherInstallableIconList});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpdated);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
// The icon should have changed.
CheckShortcutInfoUpdated(app_id, kAnotherInstallableIconTopLeftColor);
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerIconUpdatingBrowserTest,
CheckIgnoresIconDownloadFail) {
constexpr char kManifest[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "standalone",
"icons": [
{
"src": "/web_apps/basic-48.png?ignore",
"sizes": "48x48",
"type": "image/png"
},
{
"src": "/web_apps/basic-192.png?ignore",
"sizes": "192x192",
"type": "image/png"
}
]
}
)";
OverrideManifest(kManifest, {});
AppId app_id = InstallWebApp();
// Make basic-48.png fail to download.
// Replace the contents of basic-192.png with blue-192.png without changing
// the URL.
content::URLLoaderInterceptor url_interceptor(base::BindLambdaForTesting(
[this](content::URLLoaderInterceptor::RequestParams* params)
-> bool /*intercepted*/ {
if (params->url_request.url ==
http_server_.GetURL("/web_apps/basic-48.png?ignore")) {
content::URLLoaderInterceptor::WriteResponse("malformed response", "",
params->client.get());
return true;
}
if (params->url_request.url ==
http_server_.GetURL("/web_apps/basic-192.png?ignore")) {
content::URLLoaderInterceptor::WriteResponse(
"chrome/test/data/web_apps/blue-192.png", params->client.get());
return true;
}
return false;
}));
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kIconDownloadFailed);
histogram_tester_.ExpectBucketCount(
kUpdateHistogramName, ManifestUpdateResult::kIconDownloadFailed, 1);
// Since one request failed, none of the icons should be updated. So the '192'
// size here is not updated to blue.
EXPECT_EQ(ReadAppIconPixel(browser()->profile(), app_id, /*size=*/48, /*x=*/0,
/*y=*/0),
SK_ColorBLACK);
EXPECT_EQ(ReadAppIconPixel(browser()->profile(), app_id, /*size=*/192,
/*x=*/0, /*y=*/0),
SK_ColorBLACK);
}
class ManifestUpdateManagerBrowserTest_UrlHandlers
: public ManifestUpdateManagerBrowserTest {
public:
#if defined(OS_WIN) || defined(OS_MAC) || \
(defined(OS_LINUX) && !BUILDFLAG(IS_CHROMEOS_LACROS))
void SetUpUrlHandlerManager() {
auto url_handler_manager =
std::make_unique<UrlHandlerManagerImpl>(browser()->profile());
// Set up web app origin association manager and expected data.
auto association_manager =
std::make_unique<FakeWebAppOriginAssociationManager>();
url::Origin foo_origin = url::Origin::Create(GURL("https://foo.com"));
url::Origin bar_origin = url::Origin::Create(GURL("https://bar.com"));
std::map<apps::UrlHandlerInfo, apps::UrlHandlerInfo> data = {
{apps::UrlHandlerInfo(foo_origin),
apps::UrlHandlerInfo(foo_origin, false, /*paths*/ {},
/*exclude_paths*/ {"/exclude"})},
{apps::UrlHandlerInfo(bar_origin),
apps::UrlHandlerInfo(bar_origin, false, /*paths*/ {},
/*exclude_paths*/ {"/exclude"})},
};
association_manager->SetData(std::move(data));
url_handler_manager->SetAssociationManagerForTesting(
std::move(association_manager));
url_handler_manager->SetSubsystems(&(GetProvider().registrar()));
GetProvider().os_integration_manager().set_url_handler_manager(
std::move(url_handler_manager));
}
#endif
base::test::ScopedFeatureList scoped_feature_list_{
blink::features::kWebAppEnableUrlHandlers};
};
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_UrlHandlers,
UpdateWithDifferentUrlHandlers) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "standalone",
"icons": $1,
"url_handlers": [$2]
}
)";
OverrideManifest(kManifestTemplate, {
kInstallableIconList,
R"({"origin": "https://foo.com"})",
});
AppId app_id = InstallWebApp();
ASSERT_EQ(1u, GetProvider().registrar().GetAppUrlHandlers(app_id).size());
OverrideManifest(
kManifestTemplate,
{kInstallableIconList,
R"({"origin": "https://foo.com"}, {"origin": "https://bar.com"})"});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpdated);
apps::UrlHandlers url_handlers =
GetProvider().registrar().GetAppUrlHandlers(app_id);
ASSERT_EQ(2u, url_handlers.size());
EXPECT_TRUE(base::Contains(
url_handlers,
apps::UrlHandlerInfo(url::Origin::Create(GURL("https://foo.com")))));
EXPECT_TRUE(base::Contains(
url_handlers,
apps::UrlHandlerInfo(url::Origin::Create(GURL("https://bar.com")))));
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_UrlHandlers,
UpdateFromNoUrlHandlersToHaveUrlHandlers) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "standalone",
"icons": $1
$2
}
)";
OverrideManifest(kManifestTemplate, {
kInstallableIconList,
R"()",
});
AppId app_id = InstallWebApp();
ASSERT_EQ(0u, GetProvider().registrar().GetAppUrlHandlers(app_id).size());
OverrideManifest(kManifestTemplate,
{kInstallableIconList,
R"(,"url_handlers": [{"origin": "https://foo.com"}])"});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpdated);
apps::UrlHandlers url_handlers =
GetProvider().registrar().GetAppUrlHandlers(app_id);
ASSERT_EQ(1u, url_handlers.size());
EXPECT_TRUE(base::Contains(
url_handlers,
apps::UrlHandlerInfo(url::Origin::Create(GURL("https://foo.com")))));
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_UrlHandlers,
UpdateFromUrlHandlersToNoUrlHandlers) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "standalone",
"icons": $1
$2
}
)";
OverrideManifest(kManifestTemplate,
{
kInstallableIconList,
R"(,"url_handlers": [{"origin": "https://foo.com"}])",
});
AppId app_id = InstallWebApp();
apps::UrlHandlers url_handlers =
GetProvider().registrar().GetAppUrlHandlers(app_id);
ASSERT_EQ(1u, url_handlers.size());
EXPECT_TRUE(base::Contains(
url_handlers,
apps::UrlHandlerInfo(url::Origin::Create(GURL("https://foo.com")))));
OverrideManifest(kManifestTemplate, {kInstallableIconList, R"()"});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpdated);
url_handlers = GetProvider().registrar().GetAppUrlHandlers(app_id);
ASSERT_EQ(0u, url_handlers.size());
}
#if defined(OS_WIN) || defined(OS_MAC) || \
(defined(OS_LINUX) && !BUILDFLAG(IS_CHROMEOS_LACROS))
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_UrlHandlers,
NoHandlersChangeUpdateAssociations) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "standalone",
"icons": $1,
"url_handlers": [
{
"origin": "https://foo.com"
},
{
"origin": "https://bar.com"
}
]
}
)";
OverrideManifest(kManifestTemplate, {kInstallableIconList});
AppId app_id = InstallWebApp();
apps::UrlHandlers url_handlers =
GetProvider().registrar().GetAppUrlHandlers(app_id);
ASSERT_EQ(2u, url_handlers.size());
EXPECT_TRUE(base::Contains(
url_handlers,
apps::UrlHandlerInfo(url::Origin::Create(GURL("https://foo.com")))));
EXPECT_TRUE(base::Contains(
url_handlers,
apps::UrlHandlerInfo(url::Origin::Create(GURL("https://bar.com")))));
// Prepare for association fetching at manifest update.
SetUpUrlHandlerManager();
OverrideManifest(kManifestTemplate, {kInstallableIconList});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppAssociationsUpdated);
// Verify url handlers are saved to prefs.
base::CommandLine cmd = base::CommandLine(base::CommandLine::NO_PROGRAM);
cmd.AppendArg("https://foo.com/ok");
std::vector<UrlHandlerLaunchParams> matches =
UrlHandlerManagerImpl::GetUrlHandlerMatches(cmd);
ASSERT_EQ(matches.size(), 1u);
// Check exclude paths came through.
cmd = base::CommandLine(base::CommandLine::NO_PROGRAM);
cmd.AppendArg("https://foo.com/exclude");
matches = UrlHandlerManagerImpl::GetUrlHandlerMatches(cmd);
ASSERT_EQ(matches.size(), 0u);
cmd = base::CommandLine(base::CommandLine::NO_PROGRAM);
cmd.AppendArg("https://bar.com/ok");
matches = UrlHandlerManagerImpl::GetUrlHandlerMatches(cmd);
ASSERT_EQ(matches.size(), 1u);
// Check exclude paths came through.
cmd = base::CommandLine(base::CommandLine::NO_PROGRAM);
cmd.AppendArg("https://bar.com/exclude");
matches = UrlHandlerManagerImpl::GetUrlHandlerMatches(cmd);
ASSERT_EQ(matches.size(), 0u);
}
#endif
class ManifestUpdateManagerBrowserTestWithProtocolHandling
: public ManifestUpdateManagerBrowserTest {
base::test::ScopedFeatureList scoped_feature_list_{
blink::features::kWebAppEnableProtocolHandlers};
};
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithProtocolHandling,
CheckFindsAddedProtocolHandler) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "minimal-ui",
"icons": $1
}
)";
constexpr char kProtocolHandlerManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "minimal-ui",
"protocol_handlers": [
{
"protocol": "mailto",
"url": "?mailto=%s"
}
],
"icons": $1
}
)";
OverrideManifest(kManifestTemplate, {kInstallableIconList});
AppId app_id = InstallWebApp();
OverrideManifest(kProtocolHandlerManifestTemplate, {kInstallableIconList});
EXPECT_EQ(ManifestUpdateResult::kAppUpdated,
GetResultAfterPageLoad(GetAppURL(), &app_id));
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
const WebApp* web_app =
GetProvider().registrar().AsWebAppRegistrar()->GetAppById(app_id);
EXPECT_FALSE(web_app->protocol_handlers().empty());
const auto& protocol_handler = web_app->protocol_handlers()[0];
EXPECT_EQ("mailto", protocol_handler.protocol);
EXPECT_EQ(http_server_.GetURL("/banners/manifest.json?mailto=%s"),
protocol_handler.url.spec());
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithProtocolHandling,
CheckIgnoresUnchangedProtocolHandler) {
constexpr char kProtocolHandlerManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "minimal-ui",
"protocol_handlers": [
{
"protocol": "mailto",
"url": "?mailto=%s"
}
],
"icons": $1
}
)";
OverrideManifest(kProtocolHandlerManifestTemplate, {kInstallableIconList});
AppId app_id = InstallWebApp();
OverrideManifest(kProtocolHandlerManifestTemplate, {kInstallableIconList});
EXPECT_EQ(ManifestUpdateResult::kAppUpToDate,
GetResultAfterPageLoad(GetAppURL(), &app_id));
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpToDate, 1);
const WebApp* web_app =
GetProvider().registrar().AsWebAppRegistrar()->GetAppById(app_id);
EXPECT_FALSE(web_app->protocol_handlers().empty());
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithProtocolHandling,
CheckFindsChangedProtocolHandler) {
constexpr char kProtocolHandlerManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "minimal-ui",
"protocol_handlers": [
{
"protocol": "$1",
"url": "?$2=%s"
}
],
"icons": $3
}
)";
OverrideManifest(kProtocolHandlerManifestTemplate,
{"mailto", "mailto", kInstallableIconList});
AppId app_id = InstallWebApp();
const WebApp* web_app =
GetProvider().registrar().AsWebAppRegistrar()->GetAppById(app_id);
EXPECT_EQ(1u, web_app->protocol_handlers().size());
const auto& old_protocol_handler = web_app->protocol_handlers()[0];
EXPECT_EQ("mailto", old_protocol_handler.protocol);
EXPECT_EQ(http_server_.GetURL("/banners/manifest.json?mailto=%s"),
old_protocol_handler.url.spec());
OverrideManifest(kProtocolHandlerManifestTemplate,
{"web+mailto", "web+mailto", kInstallableIconList});
EXPECT_EQ(ManifestUpdateResult::kAppUpdated,
GetResultAfterPageLoad(GetAppURL(), &app_id));
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
EXPECT_EQ(1u, web_app->protocol_handlers().size());
const auto& new_protocol_handler = web_app->protocol_handlers()[0];
EXPECT_EQ("web+mailto", new_protocol_handler.protocol);
EXPECT_EQ(http_server_.GetURL("/banners/manifest.json?web+mailto=%s"),
new_protocol_handler.url.spec());
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithProtocolHandling,
CheckFindsDeletedProtocolHandler) {
constexpr char kProtocolHandlerManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "minimal-ui",
"protocol_handlers": [
{
"protocol": "mailto",
"url": "?mailto=%s"
}
],
"icons": $1
}
)";
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "minimal-ui",
"icons": $1
}
)";
OverrideManifest(kProtocolHandlerManifestTemplate, {kInstallableIconList});
AppId app_id = InstallWebApp();
OverrideManifest(kManifestTemplate, {kInstallableIconList});
EXPECT_EQ(ManifestUpdateResult::kAppUpdated,
GetResultAfterPageLoad(GetAppURL(), &app_id));
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
const WebApp* web_app =
GetProvider().registrar().AsWebAppRegistrar()->GetAppById(app_id);
EXPECT_TRUE(web_app->protocol_handlers().empty());
}
class ManifestUpdateManagerBrowserTestWithWebAppNoteTaking
: public ManifestUpdateManagerBrowserTest {
base::test::ScopedFeatureList scoped_feature_list_{
blink::features::kWebAppNoteTaking};
};
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithWebAppNoteTaking,
CheckFindsAddedNewNoteUrl) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "minimal-ui",
"icons": $1
}
)";
constexpr char kNewNoteUrlManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "minimal-ui",
"note_taking": {
"new_note_url": "/new"
},
"icons": $1
}
)";
OverrideManifest(kManifestTemplate, {kInstallableIconList});
AppId app_id = InstallWebApp();
const WebApp* web_app =
GetProvider().registrar().AsWebAppRegistrar()->GetAppById(app_id);
EXPECT_TRUE(web_app->note_taking_new_note_url().is_empty());
OverrideManifest(kNewNoteUrlManifestTemplate, {kInstallableIconList});
EXPECT_EQ(ManifestUpdateResult::kAppUpdated,
GetResultAfterPageLoad(GetAppURL(), &app_id));
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
EXPECT_EQ(http_server_.GetURL("/new"),
web_app->note_taking_new_note_url().spec());
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithWebAppNoteTaking,
CheckIgnoresUnchangedNewNoteUrl) {
constexpr char kNewNoteUrlManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "minimal-ui",
"note_taking": {
"new_note_url": "/new"
},
"icons": $1
}
)";
OverrideManifest(kNewNoteUrlManifestTemplate, {kInstallableIconList});
AppId app_id = InstallWebApp();
const WebApp* web_app =
GetProvider().registrar().AsWebAppRegistrar()->GetAppById(app_id);
EXPECT_EQ(http_server_.GetURL("/new"),
web_app->note_taking_new_note_url().spec());
OverrideManifest(kNewNoteUrlManifestTemplate, {kInstallableIconList});
EXPECT_EQ(ManifestUpdateResult::kAppUpToDate,
GetResultAfterPageLoad(GetAppURL(), &app_id));
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpToDate, 1);
EXPECT_EQ(http_server_.GetURL("/new"),
web_app->note_taking_new_note_url().spec());
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithWebAppNoteTaking,
CheckFindsChangedNewNoteUrl) {
constexpr char kNewNoteUrlManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "minimal-ui",
"note_taking": {
"new_note_url": "$1"
},
"icons": $2
}
)";
OverrideManifest(kNewNoteUrlManifestTemplate,
{"old-relative-url", kInstallableIconList});
AppId app_id = InstallWebApp();
const WebApp* web_app =
GetProvider().registrar().AsWebAppRegistrar()->GetAppById(app_id);
// URL parsed relative to manifest URL, which is in /banners/.
EXPECT_EQ(http_server_.GetURL("/banners/old-relative-url"),
web_app->note_taking_new_note_url().spec());
OverrideManifest(kNewNoteUrlManifestTemplate,
{"/newer", kInstallableIconList});
EXPECT_EQ(ManifestUpdateResult::kAppUpdated,
GetResultAfterPageLoad(GetAppURL(), &app_id));
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
EXPECT_EQ(http_server_.GetURL("/newer"),
web_app->note_taking_new_note_url().spec());
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithWebAppNoteTaking,
CheckFindsDeletedNewNoteUrl) {
constexpr char kNewNoteUrlManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "minimal-ui",
"note_taking": {
"new_note_url": "/new"
},
"icons": $1
}
)";
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": ".",
"scope": "/",
"display": "minimal-ui",
"icons": $1
}
)";
OverrideManifest(kNewNoteUrlManifestTemplate, {kInstallableIconList});
AppId app_id = InstallWebApp();
const WebApp* web_app =
GetProvider().registrar().AsWebAppRegistrar()->GetAppById(app_id);
EXPECT_FALSE(web_app->note_taking_new_note_url().is_empty());
OverrideManifest(kManifestTemplate, {kInstallableIconList});
EXPECT_EQ(ManifestUpdateResult::kAppUpdated,
GetResultAfterPageLoad(GetAppURL(), &app_id));
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
EXPECT_TRUE(web_app->note_taking_new_note_url().is_empty());
}
class ManifestUpdateManagerBrowserTest_ManifestId
: public ManifestUpdateManagerBrowserTest {
public:
ManifestUpdateManagerBrowserTest_ManifestId() {
scoped_feature_list_.InitAndEnableFeature(
blink::features::kWebAppEnableManifestId);
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_ManifestId,
AllowStartUrlUpdate) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": "$1",
"scope": "/",
"display": "minimal-ui",
"id": "test",
"icons": $2
}
)";
OverrideManifest(kManifestTemplate, {"/startA", kInstallableIconList});
AppId app_id = InstallWebApp();
EXPECT_EQ(GetProvider().registrar().GetAppStartUrl(app_id).path(), "/startA");
OverrideManifest(kManifestTemplate, {"/startB", kInstallableIconList});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpdated);
EXPECT_EQ(GetProvider().registrar().GetAppStartUrl(app_id).path(), "/startB");
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpdated, 1);
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_ManifestId,
CheckIgnoresIdChange) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"id": "$1",
"start_url": "start",
"scope": "/",
"display": "standalone",
"icons": $2
}
)";
OverrideManifest(kManifestTemplate, {"test", kInstallableIconList});
AppId app_id = InstallWebApp();
OverrideManifest(kManifestTemplate, {"testb", kInstallableIconList});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppIdMismatch);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppIdMismatch, 1);
}
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_ManifestId,
ChecksSettingIdMatchDefault) {
constexpr char kManifestTemplate[] = R"(
{
"name": "Test app name",
"start_url": "/start",
"scope": "/",
"display": "standalone",
"icons": $1
}
)";
OverrideManifest(kManifestTemplate, {kInstallableIconList});
AppId app_id = InstallWebApp();
// manifest_id should default to start_url when it's not provided in manifest.
EXPECT_EQ(GetProvider()
.registrar()
.AsWebAppRegistrar()
->GetAppById(app_id)
->manifest_id()
.value(),
"start");
constexpr char kManifestTemplate2[] = R"(
{
"name": "Test app name",
"id": "$1",
"start_url": "/start",
"scope": "/",
"display": "standalone",
"icons": $2
}
)";
// Setting manifest id to match default value won't trigger update as the
// parsed manifest is the same.
OverrideManifest(kManifestTemplate2, {"start", kInstallableIconList});
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
ManifestUpdateResult::kAppUpToDate);
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
ManifestUpdateResult::kAppUpToDate, 1);
}
} // namespace web_app