blob: e5f2cc9e78183fa7deeb0a6d127ad6fe652d1132 [file] [log] [blame]
// Copyright 2021 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/url_handler_manager_impl.h"
#include <vector>
#include "base/command_line.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/web_applications/test/fake_web_app_origin_association_manager.h"
#include "chrome/browser/web_applications/test/fake_web_app_registry_controller.h"
#include "chrome/browser/web_applications/test/web_app_test.h"
#include "chrome/browser/web_applications/url_handler_prefs.h"
#include "chrome/browser/web_applications/web_app.h"
#include "chrome/browser/web_applications/web_app_helpers.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/base/scoped_testing_local_state.h"
#include "chrome/test/base/testing_browser_process.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/services/app_service/public/cpp/url_handler_info.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/features.h"
namespace web_app {
namespace {
constexpr char kAppUrl1[] = "https://web-app1.com/";
constexpr char kOriginUrl1[] = "https://origin-1.com/abc";
constexpr char kOriginUrl2[] = "https://origin-2.com/abc";
} // namespace
class UrlHandlerManagerImplTest : public WebAppTest {
public:
UrlHandlerManagerImplTest()
: local_state_(TestingBrowserProcess::GetGlobal()) {
features_.InitAndEnableFeature(blink::features::kWebAppEnableUrlHandlers);
}
~UrlHandlerManagerImplTest() override = default;
protected:
void SetUp() override {
WebAppTest::SetUp();
fake_registry_controller_ =
std::make_unique<FakeWebAppRegistryController>();
fake_registry_controller_->SetUp(profile());
auto url_handler_manager =
std::make_unique<UrlHandlerManagerImpl>(profile());
url_handler_manager_ = url_handler_manager.get();
url_handler_manager->SetSubsystems(&fake_registry_controller_->registrar());
auto association_manager =
std::make_unique<FakeWebAppOriginAssociationManager>();
std::map<apps::UrlHandlerInfo, apps::UrlHandlerInfo> data = {
{apps::UrlHandlerInfo(origin_1_),
apps::UrlHandlerInfo(origin_1_, false, {"/abc"}, {"/foo"})},
{apps::UrlHandlerInfo(origin_2_),
apps::UrlHandlerInfo(origin_2_, false, {"/abc"}, {"/bar"})},
};
association_manager->SetData(std::move(data));
url_handler_manager->SetAssociationManagerForTesting(
std::move(association_manager));
fake_os_integration_manager().SetUrlHandlerManager(
std::move(url_handler_manager));
fake_registry_controller_->Init();
}
void TearDown() override { WebAppTest::TearDown(); }
FakeOsIntegrationManager& fake_os_integration_manager() {
return fake_registry_controller_->os_integration_manager();
}
FakeWebAppRegistryController& controller() {
return *fake_registry_controller_;
}
UrlHandlerManagerImpl& url_handler_manager() { return *url_handler_manager_; }
std::unique_ptr<WebApp> CreateWebAppWithUrlHandlers(
const GURL& app_url,
const apps::UrlHandlers& url_handlers) {
const std::string app_id =
GenerateAppId(/*manifest_id=*/absl::nullopt, app_url);
auto web_app = std::make_unique<WebApp>(app_id);
web_app->AddSource(Source::kDefault);
web_app->SetDisplayMode(DisplayMode::kStandalone);
web_app->SetUserDisplayMode(DisplayMode::kStandalone);
web_app->SetName("Name");
web_app->SetStartUrl(app_url);
web_app->SetUrlHandlers(url_handlers);
return web_app;
}
AppId RegisterAppAndUrlHandlers() {
auto web_app = CreateWebAppWithUrlHandlers(
app_url_1_, {apps::UrlHandlerInfo(origin_1_)});
const AppId& app_id = web_app->app_id();
controller().RegisterApp(std::move(web_app));
base::RunLoop run_loop;
url_handler_manager().RegisterUrlHandlers(
app_id, base::BindLambdaForTesting([&](bool success) {
EXPECT_TRUE(success);
run_loop.Quit();
}));
run_loop.Run();
return app_id;
}
AppId UpdateAppAndUrlHandlers() {
auto web_app = CreateWebAppWithUrlHandlers(
app_url_1_, {apps::UrlHandlerInfo(origin_2_)});
const AppId& app_id = web_app->app_id();
controller().UnregisterApp(app_id);
controller().RegisterApp(std::move(web_app));
base::RunLoop run_loop;
url_handler_manager().UpdateUrlHandlers(
app_id, base::BindLambdaForTesting([&](bool success) {
EXPECT_TRUE(success);
run_loop.Quit();
}));
run_loop.Run();
return app_id;
}
const GURL app_url_1_ = GURL(kAppUrl1);
const GURL origin_url_1_ = GURL(kOriginUrl1);
const GURL origin_url_2_ = GURL(kOriginUrl2);
const url::Origin origin_1_ = url::Origin::Create(origin_url_1_);
const url::Origin origin_2_ = url::Origin::Create(origin_url_2_);
private:
base::test::ScopedFeatureList features_;
std::unique_ptr<FakeWebAppRegistryController> fake_registry_controller_;
UrlHandlerManagerImpl* url_handler_manager_;
ScopedTestingLocalState local_state_;
};
TEST_F(UrlHandlerManagerImplTest, RegisterAndUnregisterApp) {
const AppId app_id = RegisterAppAndUrlHandlers();
base::CommandLine cmd = base::CommandLine(base::CommandLine::NO_PROGRAM);
cmd.AppendArg(kOriginUrl1);
// Find URL in cmd and look for matching URL handlers.
std::vector<UrlHandlerLaunchParams> matches =
UrlHandlerManagerImpl::GetUrlHandlerMatches(cmd);
ASSERT_EQ(matches.size(), 1u);
const UrlHandlerLaunchParams& params = matches[0];
EXPECT_EQ(params.profile_path, profile()->GetPath());
EXPECT_EQ(params.app_id, app_id);
EXPECT_EQ(params.url, kOriginUrl1);
// Unregister URL handlers, remove app.
url_handler_manager().UnregisterUrlHandlers(app_id);
controller().UnregisterApp(app_id);
// Confirm there is no matching URL handler now.
matches = UrlHandlerManagerImpl::GetUrlHandlerMatches(cmd);
EXPECT_TRUE(matches.empty());
}
TEST_F(UrlHandlerManagerImplTest, RegisterAndUpdateApp) {
base::CommandLine cmd_1 = base::CommandLine(base::CommandLine::NO_PROGRAM);
cmd_1.AppendArg(kOriginUrl1);
AppId app_id;
{
app_id = RegisterAppAndUrlHandlers();
// Find URL in commandline and look for matching URL handlers.
std::vector<UrlHandlerLaunchParams> matches =
UrlHandlerManagerImpl::GetUrlHandlerMatches(cmd_1);
ASSERT_EQ(matches.size(), 1u);
const UrlHandlerLaunchParams& params = matches[0];
EXPECT_EQ(params.profile_path, profile()->GetPath());
EXPECT_EQ(params.app_id, app_id);
EXPECT_EQ(params.url, kOriginUrl1);
}
{
AppId updated_app_id = UpdateAppAndUrlHandlers();
EXPECT_EQ(app_id, updated_app_id);
// Expect that url handlers targeting origin 1 have been replaced.
std::vector<UrlHandlerLaunchParams> matches =
UrlHandlerManagerImpl::GetUrlHandlerMatches(cmd_1);
EXPECT_TRUE(matches.empty());
}
{
base::CommandLine cmd_2 = base::CommandLine(base::CommandLine::NO_PROGRAM);
cmd_2.AppendArg(kOriginUrl2);
std::vector<UrlHandlerLaunchParams> matches =
UrlHandlerManagerImpl::GetUrlHandlerMatches(cmd_2);
// Expect new url handlers that target origin 2.
ASSERT_EQ(matches.size(), 1u);
const UrlHandlerLaunchParams& params = matches[0];
EXPECT_EQ(params.profile_path, profile()->GetPath());
EXPECT_EQ(params.app_id, app_id);
EXPECT_EQ(params.url, kOriginUrl2);
// Check excluded path shouldn't match
cmd_2 = base::CommandLine(base::CommandLine::NO_PROGRAM);
cmd_2.AppendArg("https://origin-2.com/bar");
matches = UrlHandlerManagerImpl::GetUrlHandlerMatches(cmd_2);
EXPECT_TRUE(matches.empty());
}
}
TEST_F(UrlHandlerManagerImplTest, GetUrlHandlerMatches_CommandlineHasNoUrl) {
const AppId app_Id = RegisterAppAndUrlHandlers();
base::CommandLine cmd = base::CommandLine(base::CommandLine::NO_PROGRAM);
auto matches = UrlHandlerManagerImpl::GetUrlHandlerMatches(cmd);
EXPECT_TRUE(matches.empty());
}
TEST_F(UrlHandlerManagerImplTest,
GetUrlHandlerMatches_CommandlineHasAppSwitches) {
const AppId app_id = RegisterAppAndUrlHandlers();
{
base::CommandLine cmd = base::CommandLine(base::CommandLine::NO_PROGRAM);
cmd.AppendSwitchASCII(switches::kAppId, app_id);
cmd.AppendArg(kOriginUrl1);
auto matches = UrlHandlerManagerImpl::GetUrlHandlerMatches(cmd);
EXPECT_TRUE(matches.empty());
}
{
base::CommandLine cmd = base::CommandLine(base::CommandLine::NO_PROGRAM);
cmd.AppendSwitchASCII(switches::kApp, kAppUrl1);
cmd.AppendArg(kOriginUrl1);
auto matches = UrlHandlerManagerImpl::GetUrlHandlerMatches(cmd);
EXPECT_TRUE(matches.empty());
}
}
TEST_F(UrlHandlerManagerImplTest, FeatureFlagDisabled_Unregister) {
AppId app_id = RegisterAppAndUrlHandlers();
// Disable feature and try to unregister url handlers.
{
base::test::ScopedFeatureList features;
features.InitAndDisableFeature(blink::features::kWebAppEnableUrlHandlers);
EXPECT_TRUE(url_handler_manager().UnregisterUrlHandlers(app_id));
}
// Check that url handlers are no longer registered.
base::CommandLine cmd = base::CommandLine(base::CommandLine::NO_PROGRAM);
cmd.AppendArg(kOriginUrl1);
auto matches = UrlHandlerManagerImpl::GetUrlHandlerMatches(cmd);
ASSERT_EQ(matches.size(), 0u);
}
TEST_F(UrlHandlerManagerImplTest, GetUrlHandlerMatches_CommandlineNoMatch) {
const AppId app_Id = RegisterAppAndUrlHandlers();
base::CommandLine cmd = base::CommandLine(base::CommandLine::NO_PROGRAM);
cmd.AppendArg("https://origin-1.com/foo");
auto matches = UrlHandlerManagerImpl::GetUrlHandlerMatches(cmd);
EXPECT_TRUE(matches.empty());
}
} // namespace web_app