blob: 80a40e518038f0237f1d3a56dfa9abc89247ed56 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/web_applications/web_app_install_finalizer.h"
#include <initializer_list>
#include <memory>
#include <optional>
#include <tuple>
#include <utility>
#include "base/feature_list.h"
#include "base/scoped_observation.h"
#include "base/test/bind.h"
#include "base/test/simple_test_clock.h"
#include "base/test/test_future.h"
#include "base/traits_bag.h"
#include "build/build_config.h"
#include "build/buildflag.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/ui/web_applications/test/isolated_web_app_test_utils.h"
#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_integrity_block_data.h"
#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_info.h"
#include "chrome/browser/web_applications/isolated_web_apps/test/isolated_web_app_builder.h"
#include "chrome/browser/web_applications/proto/web_app_install_state.pb.h"
#include "chrome/browser/web_applications/scope_extension_info.h"
#include "chrome/browser/web_applications/test/fake_os_integration_manager.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/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_utils.h"
#include "chrome/browser/web_applications/web_app_constants.h"
#include "chrome/browser/web_applications/web_app_helpers.h"
#include "chrome/browser/web_applications/web_app_install_info.h"
#include "chrome/browser/web_applications/web_app_install_manager.h"
#include "chrome/browser/web_applications/web_app_install_manager_observer.h"
#include "chrome/browser/web_applications/web_app_install_utils.h"
#include "chrome/browser/web_applications/web_app_management_type.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#include "chrome/browser/web_applications/web_app_registrar.h"
#include "chrome/common/chrome_features.h"
#include "chrome/test/base/testing_profile.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/services/app_service/public/cpp/file_handler.h"
#include "components/sync/base/time.h"
#include "components/webapps/browser/install_result_code.h"
#include "components/webapps/isolated_web_apps/types/storage_location.h"
#include "mojo/public/cpp/bindings/struct_ptr.h"
#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/mojom/manifest/manifest.mojom-forward.h"
#include "third_party/blink/public/mojom/manifest/manifest.mojom.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace web_app {
namespace {
using testing::_;
struct FinalizeInstallResult {
webapps::AppId installed_app_id;
webapps::InstallResultCode code;
};
} // namespace
class TestInstallManagerObserver : public WebAppInstallManagerObserver {
public:
explicit TestInstallManagerObserver(WebAppInstallManager* install_manager) {
install_manager_observation_.Observe(install_manager);
}
void OnWebAppManifestUpdated(const webapps::AppId& app_id) override {
web_app_manifest_updated_called_ = true;
}
bool web_app_manifest_updated_called() const {
return web_app_manifest_updated_called_;
}
private:
bool web_app_manifest_updated_called_ = false;
base::ScopedObservation<WebAppInstallManager, WebAppInstallManagerObserver>
install_manager_observation_{this};
};
class WebAppInstallFinalizerUnitTest : public WebAppTest {
public:
WebAppInstallFinalizerUnitTest() = default;
WebAppInstallFinalizerUnitTest(const WebAppInstallFinalizerUnitTest&) =
delete;
WebAppInstallFinalizerUnitTest& operator=(
const WebAppInstallFinalizerUnitTest&) = delete;
~WebAppInstallFinalizerUnitTest() override = default;
void SetUp() override {
WebAppTest::SetUp();
FakeWebAppProvider* provider = FakeWebAppProvider::Get(profile());
auto install_manager = std::make_unique<WebAppInstallManager>(profile());
install_manager_observer_ =
std::make_unique<TestInstallManagerObserver>(install_manager.get());
provider->SetInstallManager(std::move(install_manager));
provider->SetInstallFinalizer(
std::make_unique<WebAppInstallFinalizer>(profile()));
provider->SetOriginAssociationManager(
std::make_unique<FakeWebAppOriginAssociationManager>());
test::AwaitStartWebAppProviderAndSubsystems(profile());
}
void TearDown() override {
install_manager_observer_.reset();
WebAppTest::TearDown();
}
// Synchronous version of FinalizeInstall.
FinalizeInstallResult AwaitFinalizeInstall(
const WebAppInstallInfo& info,
const WebAppInstallFinalizer::FinalizeOptions& options) {
FinalizeInstallResult result{};
base::RunLoop run_loop;
finalizer().FinalizeInstall(
info, options,
base::BindLambdaForTesting([&](const webapps::AppId& installed_app_id,
webapps::InstallResultCode code) {
result.installed_app_id = installed_app_id;
result.code = code;
run_loop.Quit();
}));
run_loop.Run();
return result;
}
void AddFileHandler(
std::vector<blink::mojom::ManifestFileHandlerPtr>* file_handlers) {
auto file_handler = blink::mojom::ManifestFileHandler::New();
file_handler->action = GURL("https://example.com/action");
file_handler->name = u"Test handler";
file_handler->accept[u"application/pdf"].emplace_back(u".pdf");
file_handlers->push_back(std::move(file_handler));
}
WebAppProvider& provider() { return *WebAppProvider::GetForTest(profile()); }
WebAppInstallFinalizer& finalizer() { return provider().install_finalizer(); }
WebAppRegistrar& registrar() { return provider().registrar_unsafe(); }
WebAppInstallManager& install_manager() {
return provider().install_manager();
}
protected:
std::unique_ptr<TestInstallManagerObserver> install_manager_observer_;
private:
data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
};
TEST_F(WebAppInstallFinalizerUnitTest, BasicInstallSucceeds) {
auto info = WebAppInstallInfo::CreateWithStartUrlForTesting(
GURL("https://foo.example"));
info->title = u"Foo Title";
WebAppInstallFinalizer::FinalizeOptions options(
webapps::WebappInstallSource::INTERNAL_DEFAULT);
FinalizeInstallResult result = AwaitFinalizeInstall(*info, options);
EXPECT_EQ(webapps::InstallResultCode::kSuccessNewInstall, result.code);
EXPECT_EQ(result.installed_app_id,
GenerateAppId(/*manifest_id=*/std::nullopt, info->start_url()));
}
TEST_F(WebAppInstallFinalizerUnitTest, ConcurrentInstallSucceeds) {
auto info1 = WebAppInstallInfo::CreateWithStartUrlForTesting(
GURL("https://foo1.example"));
info1->title = u"Foo1 Title";
auto info2 = WebAppInstallInfo::CreateWithStartUrlForTesting(
GURL("https://foo2.example"));
info2->title = u"Foo2 Title";
WebAppInstallFinalizer::FinalizeOptions options(
webapps::WebappInstallSource::INTERNAL_DEFAULT);
base::RunLoop run_loop;
bool callback1_called = false;
bool callback2_called = false;
// Start install finalization for the 1st app.
{
finalizer().FinalizeInstall(
*info1, options,
base::BindLambdaForTesting([&](const webapps::AppId& installed_app_id,
webapps::InstallResultCode code) {
EXPECT_EQ(webapps::InstallResultCode::kSuccessNewInstall, code);
EXPECT_EQ(
installed_app_id,
GenerateAppId(/*manifest_id=*/std::nullopt, info1->start_url()));
callback1_called = true;
if (callback2_called)
run_loop.Quit();
}));
}
// Start install finalization for the 2nd app.
{
finalizer().FinalizeInstall(
*info2, options,
base::BindLambdaForTesting([&](const webapps::AppId& installed_app_id,
webapps::InstallResultCode code) {
EXPECT_EQ(webapps::InstallResultCode::kSuccessNewInstall, code);
EXPECT_EQ(
installed_app_id,
GenerateAppId(/*manifest_id=*/std::nullopt, info2->start_url()));
callback2_called = true;
if (callback1_called)
run_loop.Quit();
}));
}
run_loop.Run();
EXPECT_TRUE(callback1_called);
EXPECT_TRUE(callback2_called);
}
TEST_F(WebAppInstallFinalizerUnitTest, InstallStoresLatestWebAppInstallSource) {
auto info = WebAppInstallInfo::CreateWithStartUrlForTesting(
GURL("https://foo.example"));
info->title = u"Foo Title";
WebAppInstallFinalizer::FinalizeOptions options(
webapps::WebappInstallSource::INTERNAL_DEFAULT);
FinalizeInstallResult result = AwaitFinalizeInstall(*info, options);
EXPECT_EQ(webapps::WebappInstallSource::INTERNAL_DEFAULT,
*registrar().GetLatestAppInstallSource(result.installed_app_id));
}
TEST_F(WebAppInstallFinalizerUnitTest, OnWebAppManifestUpdatedTriggered) {
auto info = WebAppInstallInfo::CreateWithStartUrlForTesting(
GURL("https://foo.example"));
info->title = u"Foo Title";
WebAppInstallFinalizer::FinalizeOptions options(
webapps::WebappInstallSource::EXTERNAL_POLICY);
FinalizeInstallResult result = AwaitFinalizeInstall(*info, options);
base::test::TestFuture<const webapps::AppId&, webapps::InstallResultCode>
update_future;
finalizer().FinalizeUpdate(*info, update_future.GetCallback());
ASSERT_TRUE(update_future.Wait());
EXPECT_TRUE(install_manager_observer_->web_app_manifest_updated_called());
}
TEST_F(WebAppInstallFinalizerUnitTest, ManifestUpdateOsIntegrationDefaultApps) {
auto info = WebAppInstallInfo::CreateWithStartUrlForTesting(
GURL("https://foo.example"));
info->title = u"Foo Title";
WebAppInstallFinalizer::FinalizeOptions options(
webapps::WebappInstallSource::EXTERNAL_DEFAULT);
options.install_state = proto::InstallState::INSTALLED_WITHOUT_OS_INTEGRATION;
options.add_to_applications_menu = false;
options.add_to_quick_launch_bar = false;
options.add_to_desktop = false;
FinalizeInstallResult result = AwaitFinalizeInstall(*info, options);
EXPECT_EQ(proto::InstallState::INSTALLED_WITHOUT_OS_INTEGRATION,
registrar().GetInstallState(result.installed_app_id));
base::test::TestFuture<const webapps::AppId&, webapps::InstallResultCode>
update_future;
finalizer().FinalizeUpdate(*info, update_future.GetCallback());
ASSERT_TRUE(update_future.Wait());
EXPECT_TRUE(install_manager_observer_->web_app_manifest_updated_called());
// Post manifest update, OS integration is not triggered for default apps.
EXPECT_EQ(proto::InstallState::INSTALLED_WITHOUT_OS_INTEGRATION,
registrar().GetInstallState(result.installed_app_id));
}
TEST_F(WebAppInstallFinalizerUnitTest,
NonLocalThenLocalInstallSetsBothInstallTime) {
auto info = WebAppInstallInfo::CreateWithStartUrlForTesting(
GURL("https://foo.example"));
info->title = u"Foo Title";
WebAppInstallFinalizer::FinalizeOptions options(
webapps::WebappInstallSource::INTERNAL_DEFAULT);
options.install_state = proto::SUGGESTED_FROM_ANOTHER_DEVICE;
// OS Hooks must be disabled for non-locally installed app.
options.add_to_applications_menu = false;
options.add_to_desktop = false;
options.add_to_quick_launch_bar = false;
{
FinalizeInstallResult result = AwaitFinalizeInstall(*info, options);
ASSERT_EQ(webapps::InstallResultCode::kSuccessNewInstall, result.code);
const WebApp* installed_app =
registrar().GetAppById(result.installed_app_id);
EXPECT_EQ(proto::SUGGESTED_FROM_ANOTHER_DEVICE,
installed_app->install_state());
EXPECT_TRUE(installed_app->first_install_time().is_null());
EXPECT_TRUE(installed_app->latest_install_time().is_null());
}
options.install_state = proto::INSTALLED_WITH_OS_INTEGRATION;
{
FinalizeInstallResult result = AwaitFinalizeInstall(*info, options);
ASSERT_EQ(webapps::InstallResultCode::kSuccessNewInstall, result.code);
const WebApp* installed_app =
registrar().GetAppById(result.installed_app_id);
EXPECT_EQ(proto::INSTALLED_WITH_OS_INTEGRATION,
installed_app->install_state());
EXPECT_FALSE(installed_app->first_install_time().is_null());
EXPECT_FALSE(installed_app->latest_install_time().is_null());
EXPECT_EQ(installed_app->first_install_time(),
installed_app->latest_install_time());
}
}
TEST_F(WebAppInstallFinalizerUnitTest,
LatestInstallTimeAlwaysUpdatedIfReinstalled) {
auto info = WebAppInstallInfo::CreateWithStartUrlForTesting(
GURL("https://foo.example"));
info->title = u"Foo Title";
WebAppInstallFinalizer::FinalizeOptions options(
webapps::WebappInstallSource::INTERNAL_DEFAULT);
options.add_to_applications_menu = false;
options.add_to_desktop = false;
options.add_to_quick_launch_bar = false;
options.install_state = proto::InstallState::INSTALLED_WITH_OS_INTEGRATION;
base::Time old_first_install_time;
base::Time old_latest_install_time;
base::SimpleTestClock test_clock;
finalizer().SetClockForTesting(&test_clock);
auto toProtoResolutionTime = [](base::Time time) {
return syncer::ProtoTimeToTime(syncer::TimeToProtoTime(time));
};
test_clock.SetNow(toProtoResolutionTime(base::Time::Now()));
{
FinalizeInstallResult result = AwaitFinalizeInstall(*info, options);
ASSERT_EQ(webapps::InstallResultCode::kSuccessNewInstall, result.code);
const WebApp* installed_app =
registrar().GetAppById(result.installed_app_id);
EXPECT_EQ(installed_app->install_state(),
proto::InstallState::INSTALLED_WITH_OS_INTEGRATION);
old_first_install_time = installed_app->first_install_time();
old_latest_install_time = installed_app->latest_install_time();
EXPECT_FALSE(old_first_install_time.is_null());
EXPECT_FALSE(old_latest_install_time.is_null());
EXPECT_EQ(old_first_install_time, old_latest_install_time);
EXPECT_EQ(old_first_install_time, toProtoResolutionTime(test_clock.Now()));
}
test_clock.Advance(base::Hours(1));
// Try reinstalling the same app again, the latest install time should be
// updated but the first install time should still stay the same.
{
FinalizeInstallResult result = AwaitFinalizeInstall(*info, options);
ASSERT_EQ(webapps::InstallResultCode::kSuccessNewInstall, result.code);
const WebApp* installed_app =
registrar().GetAppById(result.installed_app_id);
EXPECT_EQ(installed_app->install_state(),
proto::InstallState::INSTALLED_WITH_OS_INTEGRATION);
EXPECT_FALSE(installed_app->first_install_time().is_null());
EXPECT_FALSE(installed_app->latest_install_time().is_null());
EXPECT_EQ(installed_app->first_install_time(), old_first_install_time);
EXPECT_NE(installed_app->latest_install_time(), old_latest_install_time);
EXPECT_EQ(installed_app->latest_install_time(),
toProtoResolutionTime(test_clock.Now()));
}
// Reset the clock to the default clock, so raw_ptr issues don't happen.
finalizer().SetClockForTesting(base::DefaultClock::GetInstance());
}
TEST_F(WebAppInstallFinalizerUnitTest, InstallNoDesktopShortcut) {
auto info = WebAppInstallInfo::CreateWithStartUrlForTesting(
GURL("https://foo.example"));
info->title = u"Foo Title";
WebAppInstallFinalizer::FinalizeOptions options(
webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON);
options.add_to_desktop = false;
FinalizeInstallResult result = AwaitFinalizeInstall(*info, options);
EXPECT_EQ(webapps::InstallResultCode::kSuccessNewInstall, result.code);
EXPECT_EQ(result.installed_app_id,
GenerateAppId(/*manifest_id=*/std::nullopt, info->start_url()));
}
TEST_F(WebAppInstallFinalizerUnitTest, InstallNoQuickLaunchBarShortcut) {
auto info = WebAppInstallInfo::CreateWithStartUrlForTesting(
GURL("https://foo.example"));
info->title = u"Foo Title";
WebAppInstallFinalizer::FinalizeOptions options(
webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON);
options.add_to_quick_launch_bar = false;
FinalizeInstallResult result = AwaitFinalizeInstall(*info, options);
EXPECT_EQ(webapps::InstallResultCode::kSuccessNewInstall, result.code);
EXPECT_EQ(result.installed_app_id,
GenerateAppId(/*manifest_id=*/std::nullopt, info->start_url()));
}
TEST_F(WebAppInstallFinalizerUnitTest,
InstallNoDesktopShortcutAndNoQuickLaunchBarShortcut) {
auto info = WebAppInstallInfo::CreateWithStartUrlForTesting(
GURL("https://foo.example"));
info->title = u"Foo Title";
WebAppInstallFinalizer::FinalizeOptions options(
webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON);
options.add_to_desktop = false;
options.add_to_quick_launch_bar = false;
FinalizeInstallResult result = AwaitFinalizeInstall(*info, options);
EXPECT_EQ(webapps::InstallResultCode::kSuccessNewInstall, result.code);
EXPECT_EQ(result.installed_app_id,
GenerateAppId(/*manifest_id=*/std::nullopt, info->start_url()));
}
TEST_F(WebAppInstallFinalizerUnitTest, InstallNoCreateOsShorcuts) {
auto info = WebAppInstallInfo::CreateWithStartUrlForTesting(
GURL("https://foo.example"));
info->title = u"Foo Title";
WebAppInstallFinalizer::FinalizeOptions options(
webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON);
options.add_to_desktop = false;
options.add_to_quick_launch_bar = false;
FinalizeInstallResult result = AwaitFinalizeInstall(*info, options);
EXPECT_EQ(webapps::InstallResultCode::kSuccessNewInstall, result.code);
EXPECT_EQ(result.installed_app_id,
GenerateAppId(/*manifest_id=*/std::nullopt, info->start_url()));
}
TEST_F(WebAppInstallFinalizerUnitTest,
InstallOsHooksEnabledForUserInstalledApps) {
auto info = WebAppInstallInfo::CreateWithStartUrlForTesting(
GURL("https://foo.example"));
info->title = u"Foo Title";
WebAppInstallFinalizer::FinalizeOptions options(
webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON);
FinalizeInstallResult result = AwaitFinalizeInstall(*info, options);
EXPECT_EQ(webapps::InstallResultCode::kSuccessNewInstall, result.code);
EXPECT_EQ(result.installed_app_id,
GenerateAppId(/*manifest_id=*/std::nullopt, info->start_url()));
}
TEST_F(WebAppInstallFinalizerUnitTest, InstallOsHooksDisabledForDefaultApps) {
auto info = WebAppInstallInfo::CreateWithStartUrlForTesting(
GURL("https://foo.example"));
info->title = u"Foo Title";
WebAppInstallFinalizer::FinalizeOptions options(
webapps::WebappInstallSource::EXTERNAL_DEFAULT);
options.install_state = proto::InstallState::INSTALLED_WITHOUT_OS_INTEGRATION;
options.add_to_applications_menu = false;
options.add_to_quick_launch_bar = false;
options.add_to_desktop = false;
FinalizeInstallResult result = AwaitFinalizeInstall(*info, options);
EXPECT_EQ(webapps::InstallResultCode::kSuccessNewInstall, result.code);
EXPECT_EQ(result.installed_app_id,
GenerateAppId(/*manifest_id=*/std::nullopt, info->start_url()));
// Update the app, adding a file handler.
std::vector<blink::mojom::ManifestFileHandlerPtr> file_handlers;
AddFileHandler(&file_handlers);
PopulateFileHandlerInfoFromManifest(file_handlers, info->start_url(),
info.get());
base::test::TestFuture<const webapps::AppId&, webapps::InstallResultCode>
update_future;
finalizer().FinalizeUpdate(*info, update_future.GetCallback());
auto [app_id, code] = update_future.Take();
EXPECT_EQ(webapps::InstallResultCode::kSuccessAlreadyInstalled, code);
}
TEST_F(WebAppInstallFinalizerUnitTest, InstallUrlSetInWebAppDB) {
auto info = WebAppInstallInfo::CreateWithStartUrlForTesting(
GURL("https://foo.example"));
info->title = u"Foo Title";
info->install_url = GURL("https://foo.example/installer");
WebAppInstallFinalizer::FinalizeOptions options(
webapps::WebappInstallSource::EXTERNAL_POLICY);
FinalizeInstallResult result = AwaitFinalizeInstall(*info, options);
EXPECT_EQ(webapps::InstallResultCode::kSuccessNewInstall, result.code);
EXPECT_EQ(result.installed_app_id,
GenerateAppId(/*manifest_id=*/std::nullopt, info->start_url()));
const WebApp* installed_app = registrar().GetAppById(result.installed_app_id);
const WebApp::ExternalConfigMap& config_map =
installed_app->management_to_external_config_map();
auto it = config_map.find(WebAppManagement::kPolicy);
EXPECT_NE(it, config_map.end());
EXPECT_EQ(1u, it->second.install_urls.size());
EXPECT_EQ(GURL("https://foo.example/installer"),
*it->second.install_urls.begin());
}
TEST_F(WebAppInstallFinalizerUnitTest, IsolationDataSetInWebAppDB) {
base::Version version("1.2.3");
auto info = WebAppInstallInfo::CreateWithStartUrlForTesting(
GURL("isolated-app://random_app"));
info->title = u"Foo Title";
info->isolated_web_app_version = version;
const IsolatedWebAppStorageLocation location(
IwaStorageUnownedBundle{base::FilePath(FILE_PATH_LITERAL("p"))});
WebAppInstallFinalizer::FinalizeOptions options(
webapps::WebappInstallSource::EXTERNAL_POLICY);
auto integrity_block_data =
IsolatedWebAppIntegrityBlockData(test::CreateSignatures());
options.iwa_options = WebAppInstallFinalizer::FinalizeOptions::IwaOptions(
location, integrity_block_data);
FinalizeInstallResult result = AwaitFinalizeInstall(*info, options);
EXPECT_EQ(webapps::InstallResultCode::kSuccessNewInstall, result.code);
EXPECT_EQ(result.installed_app_id,
GenerateAppId(/*manifest_id=*/std::nullopt, info->start_url()));
const WebApp* installed_app = registrar().GetAppById(result.installed_app_id);
EXPECT_THAT(
installed_app,
test::IwaIs(_, test::IsolationDataIs(location, version,
/*controlled_frame_partiions=*/_,
/*pending_update_info=*/std::nullopt,
integrity_block_data)));
}
TEST_F(WebAppInstallFinalizerUnitTest, PopUpContentSettingsGrantedForIwa) {
std::unique_ptr<ScopedBundledIsolatedWebApp> app =
IsolatedWebAppBuilder(ManifestBuilder().SetVersion("1.0.0"))
.BuildBundle();
app->TrustSigningKey();
const web_app::IsolatedWebAppUrlInfo url_info =
app->InstallChecked(profile());
scoped_refptr<HostContentSettingsMap> host_content_settings_map(
HostContentSettingsMapFactory::GetForProfile(profile()));
EXPECT_EQ(ContentSetting::CONTENT_SETTING_ALLOW,
host_content_settings_map->GetContentSetting(
url_info.origin().GetURL(), url_info.origin().GetURL(),
ContentSettingsType::POPUPS));
}
TEST_F(WebAppInstallFinalizerUnitTest, ValidateOriginAssociationsApproved) {
GURL start_url("https://foo.example");
auto info = WebAppInstallInfo::CreateWithStartUrlForTesting(start_url);
info->title = u"Foo Title";
WebAppInstallFinalizer::FinalizeOptions options(
webapps::WebappInstallSource::INTERNAL_DEFAULT);
auto scope_extension =
ScopeExtensionInfo::CreateForScope(start_url,
/*has_origin_wildcard=*/true);
CHECK(!scope_extension.origin.opaque());
info->scope_extensions = {scope_extension};
// Set data such that scope_extension will be returned in validated data.
std::map<ScopeExtensionInfo, ScopeExtensionInfo> data = {
{scope_extension, scope_extension}};
static_cast<FakeWebAppOriginAssociationManager&>(
provider().origin_association_manager())
.SetData(data);
FinalizeInstallResult result = AwaitFinalizeInstall(*info, options);
EXPECT_EQ(webapps::InstallResultCode::kSuccessNewInstall, result.code);
const WebApp* installed_app = registrar().GetAppById(result.installed_app_id);
EXPECT_EQ(installed_app->install_state(),
proto::InstallState::INSTALLED_WITH_OS_INTEGRATION);
EXPECT_EQ(ScopeExtensions({scope_extension}),
installed_app->validated_scope_extensions());
}
TEST_F(WebAppInstallFinalizerUnitTest, ValidateOriginAssociationsDenied) {
GURL start_url("https://foo.example");
auto info = WebAppInstallInfo::CreateWithStartUrlForTesting(start_url);
info->title = u"Foo Title";
WebAppInstallFinalizer::FinalizeOptions options(
webapps::WebappInstallSource::INTERNAL_DEFAULT);
auto scope_extension =
ScopeExtensionInfo::CreateForScope(start_url,
/*has_origin_wildcard=*/true);
info->scope_extensions = {scope_extension};
// Set data such that scope_extension will not be returned in validated data.
std::map<ScopeExtensionInfo, ScopeExtensionInfo> data;
static_cast<FakeWebAppOriginAssociationManager&>(
provider().origin_association_manager())
.SetData(data);
FinalizeInstallResult result = AwaitFinalizeInstall(*info, options);
EXPECT_EQ(webapps::InstallResultCode::kSuccessNewInstall, result.code);
const WebApp* installed_app = registrar().GetAppById(result.installed_app_id);
EXPECT_EQ(installed_app->install_state(),
proto::InstallState::INSTALLED_WITH_OS_INTEGRATION);
EXPECT_EQ(ScopeExtensions(), installed_app->validated_scope_extensions());
}
class WebAppInstallFinalizerUnitTestQueriesAndFragments
: public WebAppInstallFinalizerUnitTest,
public testing::WithParamInterface<std::tuple<std::string, std::string>> {
public:
WebAppInstallFinalizerUnitTestQueriesAndFragments() = default;
WebAppInstallFinalizerUnitTestQueriesAndFragments(
const WebAppInstallFinalizerUnitTestQueriesAndFragments&) = delete;
WebAppInstallFinalizerUnitTestQueriesAndFragments& operator=(
const WebAppInstallFinalizerUnitTestQueriesAndFragments&) = delete;
~WebAppInstallFinalizerUnitTestQueriesAndFragments() override = default;
};
TEST_P(WebAppInstallFinalizerUnitTestQueriesAndFragments,
ValidateOriginAssociationsDropQueriesAndFragments) {
std::string start_url_str, expected_sanitized_start_url_str;
std::tie(start_url_str, expected_sanitized_start_url_str) = GetParam();
GURL start_url(start_url_str);
GURL expected_sanitized_start_url(expected_sanitized_start_url_str);
auto info = WebAppInstallInfo::CreateWithStartUrlForTesting(start_url);
info->title = u"Foo Title";
WebAppInstallFinalizer::FinalizeOptions options(
webapps::WebappInstallSource::INTERNAL_DEFAULT);
auto scope_extension =
ScopeExtensionInfo::CreateForScope(start_url,
/*has_origin_wildcard=*/true);
CHECK(!scope_extension.origin.opaque());
info->scope_extensions = {scope_extension};
// Set data such that scope_extension will be returned in validated data.
std::map<ScopeExtensionInfo, ScopeExtensionInfo> data = {
{scope_extension, scope_extension}};
static_cast<FakeWebAppOriginAssociationManager&>(
provider().origin_association_manager())
.SetData(data);
FinalizeInstallResult result = AwaitFinalizeInstall(*info, options);
EXPECT_EQ(webapps::InstallResultCode::kSuccessNewInstall, result.code);
const WebApp* installed_app = registrar().GetAppById(result.installed_app_id);
EXPECT_EQ(installed_app->install_state(),
proto::InstallState::INSTALLED_WITH_OS_INTEGRATION);
EXPECT_EQ(ScopeExtensions({scope_extension}),
installed_app->validated_scope_extensions());
for (const auto& scope_ext_info :
installed_app->validated_scope_extensions()) {
ASSERT_EQ(expected_sanitized_start_url, scope_ext_info.scope);
}
}
INSTANTIATE_TEST_SUITE_P(
,
WebAppInstallFinalizerUnitTestQueriesAndFragments,
testing::Values(
std::tuple<std::string, std::string>("https://foo.example/path",
"https://foo.example/path"),
std::tuple<std::string, std::string>(
"https://foo.example/search?q=querystring",
"https://foo.example/search"),
std::tuple<std::string, std::string>("https://foo.example/#hello",
"https://foo.example"),
std::tuple<std::string, std::string>(
"https://foo.example/search?q=querystring#hello",
"https://foo.example/search")));
} // namespace web_app