blob: 9b3a06e55b722f950e65c7e2764ef6b18e09a3da [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/test/test_future.h"
#include "chrome/browser/web_applications/commands/manifest_silent_update_command.h"
#include "chrome/browser/web_applications/test/fake_web_app_origin_association_manager.h"
#include "chrome/browser/web_applications/test/fake_web_app_provider.h"
#include "chrome/browser/web_applications/test/fake_web_contents_manager.h"
#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
#include "chrome/browser/web_applications/test/web_app_test.h"
#include "chrome/browser/web_applications/test/web_app_test_observers.h"
#include "chrome/browser/web_applications/web_app_command_scheduler.h"
#include "chrome/browser/web_applications/web_app_origin_association_manager.h"
#include "chrome/browser/web_applications/web_app_registrar.h"
#include "chrome/browser/web_applications/web_app_scope.h"
#include "components/webapps/common/web_app_id.h"
#include "content/public/browser/web_contents.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/mojom/manifest/manifest.mojom.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace web_app {
namespace {
class WebAppScopeExtensionsTest : public WebAppTest {
public:
WebAppScopeExtensionsTest() = default;
~WebAppScopeExtensionsTest() override = default;
void SetUp() override {
WebAppTest::SetUp();
auto fake_association_manager =
std::make_unique<FakeWebAppOriginAssociationManager>();
fake_association_manager->set_pass_through(true);
fake_provider().SetOriginAssociationManager(
std::move(fake_association_manager));
test::AwaitStartWebAppProviderAndSubsystems(profile());
}
protected:
webapps::AppId InstallWebAppWithScope(const GURL& scope) {
std::unique_ptr<WebAppInstallInfo> web_app_info =
WebAppInstallInfo::CreateForTesting(scope);
web_app_info->title = u"Basic app name";
return test::InstallWebApp(profile(), std::move(web_app_info));
}
webapps::AppId InstallWebAppWithScopeExtensions(
const GURL& scope,
const std::vector<ScopeExtensionInfo>& scope_extensions) {
std::unique_ptr<WebAppInstallInfo> web_app_info =
WebAppInstallInfo::CreateForTesting(scope);
web_app_info->title = u"Basic app name";
web_app_info->scope_extensions = base::flat_set<ScopeExtensionInfo>(
scope_extensions.begin(), scope_extensions.end());
return test::InstallWebApp(profile(), std::move(web_app_info));
}
FakeWebContentsManager& web_contents_manager() {
return static_cast<FakeWebContentsManager&>(
fake_provider().web_contents_manager());
}
void SetupPageNoScopeExtensions(const GURL& url) {
web_contents_manager().SetUrlLoaded(web_contents(), url);
web_contents_manager().CreateBasicInstallPageState(
/*install_url=*/url,
/*manifest_url=*/GURL("https://www.example.com/manifest.json"),
/*start_url=*/url);
}
void SetupPageWithScopeExtensions(
const GURL& url,
std::vector<blink::mojom::ManifestScopeExtensionPtr> scope_extensions) {
SetupPageNoScopeExtensions(url);
auto& page_state = web_contents_manager().GetOrCreatePageState(url);
page_state.manifest_before_default_processing->scope_extensions =
std::move(scope_extensions);
}
WebAppRegistrar& registrar() { return fake_provider().registrar_unsafe(); }
};
TEST_F(WebAppScopeExtensionsTest, TestScopeNotifiedOnReinstall) {
const GURL scope("https://example.com/");
webapps::AppId app_id = InstallWebAppWithScope(scope);
// Re-install the app with extended scope, and verify that the scope observer
// is notified.
WebAppTestRegistryObserverAdapter adapter(
&fake_provider().registrar_unsafe());
base::test::TestFuture<const webapps::AppId&, const WebAppScope&> future;
adapter.SetWebAppEffectiveScopeChangedDelegate(future.GetRepeatingCallback());
const std::vector<ScopeExtensionInfo> scope_extensions = {
ScopeExtensionInfo::CreateForOrigin(
url::Origin::Create(GURL("https://example.org"))),
ScopeExtensionInfo::CreateForOrigin(
url::Origin::Create(GURL("https://example.net")),
/*has_origin_wildcard=*/true)};
InstallWebAppWithScopeExtensions(scope, scope_extensions);
ASSERT_TRUE(future.Wait());
EXPECT_EQ(app_id, future.Get<webapps::AppId>());
// Now re-install without the scope extensions, and verify that the scope
// observer is notified.
future.Clear();
InstallWebAppWithScope(scope);
ASSERT_TRUE(future.Wait());
EXPECT_EQ(app_id, future.Get<webapps::AppId>());
}
TEST_F(WebAppScopeExtensionsTest, TestScopeNotifiedOnAddedUpdate) {
const GURL scope("https://example.com/");
webapps::AppId app_id = InstallWebAppWithScope(scope);
// Update the app with extended scope, and verify that the scope observer
// is notified.
WebAppTestRegistryObserverAdapter adapter(
&fake_provider().registrar_unsafe());
base::test::TestFuture<const webapps::AppId&, const WebAppScope&> future;
adapter.SetWebAppEffectiveScopeChangedDelegate(future.GetRepeatingCallback());
// Do the update.
std::vector<blink::mojom::ManifestScopeExtensionPtr> scope_extensions;
scope_extensions.push_back(blink::mojom::ManifestScopeExtension::New(
url::Origin::Create(GURL("https://example.org")),
/*has_origin_wildcard=*/false));
scope_extensions.push_back(blink::mojom::ManifestScopeExtension::New(
url::Origin::Create(GURL("https://example.net")),
/*has_origin_wildcard=*/true));
SetupPageWithScopeExtensions(scope, std::move(scope_extensions));
base::test::TestFuture<ManifestSilentUpdateCheckResult>
manifest_silent_update_future;
fake_provider().scheduler().ScheduleManifestSilentUpdate(
*web_contents(), manifest_silent_update_future.GetCallback());
ASSERT_TRUE(manifest_silent_update_future.Wait());
EXPECT_EQ(
manifest_silent_update_future.Get<ManifestSilentUpdateCheckResult>(),
ManifestSilentUpdateCheckResult::kAppSilentlyUpdated);
// Wait for the scope change to be observed.
ASSERT_TRUE(future.Wait());
EXPECT_EQ(app_id, future.Get<webapps::AppId>());
// Double check the scope extensions are there.
WebAppScope effective_scope = future.Get<WebAppScope>();
EXPECT_TRUE(effective_scope.IsInScope(GURL("https://example.org")));
EXPECT_TRUE(effective_scope.IsInScope(GURL("https://example.net")));
EXPECT_TRUE(effective_scope.IsInScope(GURL("https://sub.example.net")));
}
TEST_F(WebAppScopeExtensionsTest, TestScopeNotifiedOnRemovedUpdate) {
const GURL scope("https://example.com/");
const std::vector<ScopeExtensionInfo> scope_extensions = {
ScopeExtensionInfo::CreateForOrigin(
url::Origin::Create(GURL("https://example.org"))),
ScopeExtensionInfo::CreateForOrigin(
url::Origin::Create(GURL("https://example.net")),
/*has_origin_wildcard=*/true)};
const webapps::AppId app_id =
InstallWebAppWithScopeExtensions(scope, scope_extensions);
// Update the app with no extensions, and verify that the scope observer
// is notified.
WebAppTestRegistryObserverAdapter adapter(
&fake_provider().registrar_unsafe());
base::test::TestFuture<const webapps::AppId&, const WebAppScope&> future;
adapter.SetWebAppEffectiveScopeChangedDelegate(future.GetRepeatingCallback());
// Do the update.
SetupPageNoScopeExtensions(scope);
base::test::TestFuture<ManifestSilentUpdateCheckResult>
manifest_silent_update_future;
fake_provider().scheduler().ScheduleManifestSilentUpdate(
*web_contents(), manifest_silent_update_future.GetCallback());
ASSERT_TRUE(manifest_silent_update_future.Wait());
EXPECT_EQ(
manifest_silent_update_future.Get<ManifestSilentUpdateCheckResult>(),
ManifestSilentUpdateCheckResult::kAppSilentlyUpdated);
// Wait for the scope change to be observed.
ASSERT_TRUE(future.Wait());
EXPECT_EQ(app_id, future.Get<webapps::AppId>());
// Double check the scope extensions are gone.
WebAppScope effective_scope = future.Get<WebAppScope>();
EXPECT_FALSE(effective_scope.IsInScope(GURL("https://example.org")));
EXPECT_FALSE(effective_scope.IsInScope(GURL("https://example.net")));
EXPECT_FALSE(effective_scope.IsInScope(GURL("https://sub.example.net")));
}
} // namespace
} // namespace web_app