blob: e2f0e25fab002cbffbcddafb7009fa20b46fb9a2 [file] [log] [blame]
// Copyright 2020 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/components/os_integration_manager.h"
#include <memory>
#include "base/bind.h"
#include "base/files/file_path.h"
#include "base/run_loop.h"
#include "base/strings/string_piece_forward.h"
#include "base/test/bind.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "chrome/browser/web_applications/components/web_app_constants.h"
#include "chrome/browser/web_applications/web_app.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_features.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/features.h"
namespace web_app {
namespace {
class MockOsIntegrationManager : public OsIntegrationManager {
public:
MockOsIntegrationManager()
: OsIntegrationManager(nullptr, nullptr, nullptr, nullptr, nullptr) {}
~MockOsIntegrationManager() override = default;
// Installation:
MOCK_METHOD(void,
CreateShortcuts,
(const AppId& app_id,
bool add_to_desktop,
CreateShortcutsCallback callback),
(override));
MOCK_METHOD(void,
RegisterFileHandlers,
(const AppId& app_id,
base::OnceCallback<void(bool success)> callback),
(override));
MOCK_METHOD(void,
RegisterProtocolHandlers,
(const AppId& app_id,
base::OnceCallback<void(bool success)> callback),
(override));
MOCK_METHOD(void,
RegisterUrlHandlers,
(const AppId& app_id,
base::OnceCallback<void(bool success)> callback),
(override));
MOCK_METHOD(void,
RegisterShortcutsMenu,
(const AppId& app_id,
const std::vector<WebApplicationShortcutsMenuItemInfo>&
shortcuts_menu_item_infos,
const ShortcutsMenuIconBitmaps& shortcuts_menu_icon_bitmaps,
base::OnceCallback<void(bool success)> callback),
(override));
MOCK_METHOD(void,
ReadAllShortcutsMenuIconsAndRegisterShortcutsMenu,
(const AppId& app_id,
base::OnceCallback<void(bool success)> callback),
(override));
MOCK_METHOD(void,
RegisterRunOnOsLogin,
(const AppId& app_id, RegisterRunOnOsLoginCallback callback),
(override));
MOCK_METHOD(void,
MacAppShimOnAppInstalledForProfile,
(const AppId& app_id),
(override));
MOCK_METHOD(void, AddAppToQuickLaunchBar, (const AppId& app_id), (override));
MOCK_METHOD(void,
RegisterWebAppOsUninstallation,
(const AppId& app_id, const std::string& name),
(override));
// Uninstallation:
MOCK_METHOD(bool, UnregisterShortcutsMenu, (const AppId& app_id), (override));
MOCK_METHOD(void,
UnregisterRunOnOsLogin,
(const AppId& app_id,
const base::FilePath& profile_path,
const std::u16string& shortcut_title,
UnregisterRunOnOsLoginCallback callback),
(override));
MOCK_METHOD(void,
DeleteShortcuts,
(const AppId& app_id,
const base::FilePath& shortcuts_data_dir,
std::unique_ptr<ShortcutInfo> shortcut_info,
DeleteShortcutsCallback callback),
(override));
MOCK_METHOD(void,
UnregisterFileHandlers,
(const AppId& app_id,
std::unique_ptr<ShortcutInfo> info,
base::OnceCallback<void(bool)> callback),
(override));
MOCK_METHOD(void,
UnregisterProtocolHandlers,
(const AppId& app_id, base::OnceCallback<void(bool)> callback),
(override));
MOCK_METHOD(void, UnregisterUrlHandlers, (const AppId& app_id), (override));
MOCK_METHOD(void,
UnregisterWebAppOsUninstallation,
(const AppId& app_id),
(override));
// Update:
MOCK_METHOD(void,
UpdateFileHandlersWithShortcutInfo,
(const AppId& app_id,
FileHandlerUpdateAction file_handlers_need_os_update,
std::unique_ptr<ShortcutInfo> info),
(override));
MOCK_METHOD(void,
UpdateShortcuts,
(const AppId& app_id, base::StringPiece old_name),
(override));
MOCK_METHOD(void,
UpdateShortcutsMenu,
(const AppId& app_id, const WebApplicationInfo& web_app_info),
(override));
MOCK_METHOD(void,
UpdateUrlHandlers,
(const AppId& app_id,
base::OnceCallback<void(bool success)> callback),
(override));
MOCK_METHOD(void, UpdateProtocolHandlers, (const AppId& app_id), (override));
// Utility methods:
MOCK_METHOD(std::unique_ptr<ShortcutInfo>,
BuildShortcutInfo,
(const AppId& app_id),
(override));
};
#if defined(OS_WIN)
const base::FilePath::CharType kFakeProfilePath[] =
FILE_PATH_LITERAL("\\profile\\path");
#else
const base::FilePath::CharType kFakeProfilePath[] =
FILE_PATH_LITERAL("/profile/path");
#endif // defined(OS_WIN)
const char kFakeAppUrl[] = "https://fake.com";
const std::u16string kFakeAppTitle(u"fake title");
std::unique_ptr<ShortcutInfo> CreateTestShorcutInfo(const AppId& app_id) {
auto shortcut_info = std::make_unique<ShortcutInfo>();
shortcut_info->profile_path = base::FilePath(kFakeProfilePath);
shortcut_info->extension_id = app_id;
shortcut_info->url = GURL(kFakeAppUrl);
shortcut_info->title = kFakeAppTitle;
return shortcut_info;
}
class OsIntegrationManagerTest : public testing::Test {
public:
OsIntegrationManagerTest() {
features_.InitWithFeatures({blink::features::kWebAppEnableUrlHandlers,
::features::kDesktopPWAsRunOnOsLogin},
{});
}
~OsIntegrationManagerTest() override = default;
private:
content::BrowserTaskEnvironment task_environment_;
base::test::ScopedFeatureList features_;
};
TEST_F(OsIntegrationManagerTest, InstallOsHooksOnlyShortcuts) {
base::RunLoop run_loop;
OsHooksResults install_results;
InstallOsHooksCallback callback =
base::BindLambdaForTesting([&](OsHooksResults results) {
install_results = results;
run_loop.Quit();
});
const AppId app_id = "test";
testing::StrictMock<MockOsIntegrationManager> manager;
EXPECT_CALL(manager, MacAppShimOnAppInstalledForProfile(app_id)).Times(1);
EXPECT_CALL(manager, CreateShortcuts(app_id, false, testing::_))
.WillOnce(base::test::RunOnceCallback<2>(true));
InstallOsHooksOptions options;
options.os_hooks = OsHooksResults{false};
options.os_hooks[OsHookType::kShortcuts] = true;
manager.InstallOsHooks(app_id, std::move(callback), nullptr, options);
run_loop.Run();
EXPECT_TRUE(install_results[OsHookType::kShortcuts]);
}
TEST_F(OsIntegrationManagerTest, InstallOsHooksEverything) {
base::RunLoop run_loop;
OsHooksResults install_results;
InstallOsHooksCallback callback =
base::BindLambdaForTesting([&](OsHooksResults results) {
install_results = results;
run_loop.Quit();
});
const AppId app_id = "test";
// Note - when features are enabled by default, more calls will needed to be
// added here.
testing::StrictMock<MockOsIntegrationManager> manager;
EXPECT_CALL(manager, MacAppShimOnAppInstalledForProfile(app_id)).Times(1);
EXPECT_CALL(manager, CreateShortcuts(app_id, true, testing::_))
.WillOnce(base::test::RunOnceCallback<2>(true));
EXPECT_CALL(manager, RegisterFileHandlers(app_id, testing::_)).Times(1);
EXPECT_CALL(manager, RegisterProtocolHandlers(app_id, testing::_)).Times(1);
EXPECT_CALL(manager, RegisterUrlHandlers(app_id, testing::_)).Times(1);
EXPECT_CALL(manager, AddAppToQuickLaunchBar(app_id)).Times(1);
EXPECT_CALL(manager, ReadAllShortcutsMenuIconsAndRegisterShortcutsMenu(
app_id, testing::_))
.WillOnce(base::test::RunOnceCallback<1>(true));
EXPECT_CALL(manager, RegisterRunOnOsLogin(app_id, testing::_)).Times(1);
InstallOsHooksOptions options;
options.add_to_desktop = true;
options.add_to_quick_launch_bar = true;
// Set all hooks to true.
options.os_hooks.set();
manager.InstallOsHooks(app_id, std::move(callback), nullptr, options);
run_loop.Run();
EXPECT_TRUE(install_results[OsHookType::kShortcuts]);
EXPECT_TRUE(install_results[OsHookType::kFileHandlers]);
EXPECT_TRUE(install_results[OsHookType::kProtocolHandlers]);
EXPECT_TRUE(install_results[OsHookType::kUrlHandlers]);
EXPECT_TRUE(install_results[OsHookType::kRunOnOsLogin]);
// Note: We asked for these to be installed, but their methods were not
// called. This is because the features are turned off. We only set these
// results to false if there is an unexpected error, so they remain true.
EXPECT_TRUE(install_results[OsHookType::kShortcutsMenu]);
EXPECT_TRUE(install_results[OsHookType::kUninstallationViaOsSettings]);
}
TEST_F(OsIntegrationManagerTest, UninstallOsHooksEverything) {
base::RunLoop run_loop;
OsHooksResults uninstall_results;
UninstallOsHooksCallback callback =
base::BindLambdaForTesting([&](OsHooksResults results) {
uninstall_results = results;
run_loop.Quit();
});
const AppId app_id = "test";
const base::FilePath kExpectedShortcutPath =
base::FilePath(kFakeProfilePath)
.Append(chrome::kWebAppDirname)
.AppendASCII("_crx_test");
testing::StrictMock<MockOsIntegrationManager> manager;
EXPECT_CALL(manager, BuildShortcutInfo(app_id))
.WillOnce(
testing::Return(testing::ByMove(CreateTestShorcutInfo(app_id))));
EXPECT_CALL(manager, DeleteShortcuts(app_id, kExpectedShortcutPath,
testing::_, testing::_))
.WillOnce(base::test::RunOnceCallback<3>(true));
EXPECT_CALL(manager, UnregisterFileHandlers(app_id, testing::_, testing::_))
.Times(1);
EXPECT_CALL(manager, UnregisterProtocolHandlers(app_id, testing::_)).Times(1);
EXPECT_CALL(manager, UnregisterUrlHandlers(app_id)).Times(1);
EXPECT_CALL(manager, UnregisterWebAppOsUninstallation(app_id)).Times(1);
EXPECT_CALL(manager, UnregisterShortcutsMenu(app_id))
.WillOnce(testing::Return(true));
EXPECT_CALL(manager,
UnregisterRunOnOsLogin(app_id, base::FilePath(kFakeProfilePath),
kFakeAppTitle, testing::_))
.Times(1);
manager.UninstallAllOsHooks(app_id, std::move(callback));
run_loop.Run();
EXPECT_TRUE(uninstall_results[OsHookType::kShortcuts]);
EXPECT_TRUE(uninstall_results[OsHookType::kFileHandlers]);
EXPECT_TRUE(uninstall_results[OsHookType::kProtocolHandlers]);
EXPECT_TRUE(uninstall_results[OsHookType::kUrlHandlers]);
EXPECT_TRUE(uninstall_results[OsHookType::kRunOnOsLogin]);
EXPECT_TRUE(uninstall_results[OsHookType::kShortcutsMenu]);
EXPECT_TRUE(uninstall_results[OsHookType::kUninstallationViaOsSettings]);
}
TEST_F(OsIntegrationManagerTest, UpdateOsHooksEverything) {
const AppId app_id = "test";
testing::StrictMock<MockOsIntegrationManager> manager;
WebApplicationInfo web_app_info;
base::StringPiece old_name = "test-name";
EXPECT_CALL(manager,
UpdateFileHandlersWithShortcutInfo(
app_id, FileHandlerUpdateAction::kUpdate, testing::_))
.Times(1);
EXPECT_CALL(manager, UpdateShortcuts(app_id, old_name)).Times(1);
EXPECT_CALL(manager, UpdateShortcutsMenu(app_id, testing::_)).Times(1);
EXPECT_CALL(manager, UpdateUrlHandlers(app_id, testing::_)).Times(1);
EXPECT_CALL(manager, UpdateProtocolHandlers(app_id)).Times(1);
manager.UpdateOsHooks(app_id, old_name, nullptr,
FileHandlerUpdateAction::kUpdate, web_app_info);
}
} // namespace
} // namespace web_app