blob: e603afe253dfedc7001bd34da6acfa1331ac4b95 [file] [log] [blame]
// Copyright 2023 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/jobs/install_placeholder_job.h"
#include <memory>
#include "base/containers/flat_set.h"
#include "base/functional/callback_helpers.h"
#include "base/test/bind.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/test_future.h"
#include "chrome/browser/web_applications/commands/web_app_command.h"
#include "chrome/browser/web_applications/external_install_options.h"
#include "chrome/browser/web_applications/locks/shared_web_contents_with_app_lock.h"
#include "chrome/browser/web_applications/mojom/user_display_mode.mojom.h"
#include "chrome/browser/web_applications/os_integration/os_integration_manager.h"
#include "chrome/browser/web_applications/proto/web_app_os_integration_state.pb.h"
#include "chrome/browser/web_applications/test/fake_os_integration_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/mock_data_retriever.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/web_app_command_manager.h"
#include "chrome/browser/web_applications/web_app_command_scheduler.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_management_type.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#include "chrome/browser/web_applications/web_app_registrar.h"
#include "components/webapps/browser/install_result_code.h"
#include "components/webapps/browser/web_contents/web_app_url_loader.h"
#include "components/webapps/common/web_app_id.h"
#include "net/http/http_status_code.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace web_app {
namespace {
class InstallPlaceholderJobWrapperCommand
: public WebAppCommand<SharedWebContentsWithAppLock,
webapps::InstallResultCode,
webapps::AppId> {
public:
InstallPlaceholderJobWrapperCommand(
Profile* profile,
const ExternalInstallOptions& install_options,
InstallPlaceholderJob::InstallAndReplaceCallback callback,
std::unique_ptr<WebAppDataRetriever> data_retriever = nullptr)
: WebAppCommand<SharedWebContentsWithAppLock,
webapps::InstallResultCode,
webapps::AppId>(
"InstallPlaceholderJobWrapperCommand",
SharedWebContentsWithAppLockDescription(
base::flat_set<webapps::AppId>{
GenerateAppId(/*manifest_id_path=*/std::nullopt,
install_options.install_url)}),
std::move(callback),
/*args_for_shutdown=*/
std::make_tuple(webapps::InstallResultCode::
kCancelledOnWebAppProviderShuttingDown,
webapps::AppId())),
profile_(*profile),
install_options_(install_options),
data_retriever_(std::move(data_retriever)) {}
~InstallPlaceholderJobWrapperCommand() override = default;
void StartWithLock(
std::unique_ptr<SharedWebContentsWithAppLock> lock) override {
lock_ = std::move(lock);
install_placeholder_job_ = std::make_unique<InstallPlaceholderJob>(
&*profile_, *GetMutableDebugValue().EnsureDict("job"), install_options_,
base::BindOnce(
&InstallPlaceholderJobWrapperCommand::OnPlaceholderInstalled,
weak_factory_.GetWeakPtr()),
*lock_);
if (data_retriever_) {
install_placeholder_job_->SetDataRetrieverForTesting(
std::move(data_retriever_));
}
install_placeholder_job_->Start();
}
void OnPlaceholderInstalled(webapps::InstallResultCode code,
webapps::AppId app_id) {
CompleteAndSelfDestruct(webapps::IsSuccess(code) ? CommandResult::kSuccess
: CommandResult::kFailure,
code, app_id);
}
private:
raw_ref<Profile> profile_;
ExternalInstallOptions install_options_;
std::unique_ptr<WebAppDataRetriever> data_retriever_;
std::unique_ptr<SharedWebContentsWithAppLock> lock_;
std::unique_ptr<InstallPlaceholderJob> install_placeholder_job_;
base::WeakPtrFactory<InstallPlaceholderJobWrapperCommand> weak_factory_{this};
};
class InstallPlaceholderJobTest : public WebAppTest {
public:
static constexpr int kIconSize = 96;
const GURL kInstallUrl = GURL("https://example.com");
void SetUp() override {
WebAppTest::SetUp();
test::AwaitStartWebAppProviderAndSubsystems(profile());
}
void TearDown() override {
WebAppTest::TearDown();
}
WebAppProvider* provider() { return WebAppProvider::GetForTest(profile()); }
FakeOsIntegrationManager& fake_os_integration_manager() {
return static_cast<FakeOsIntegrationManager&>(
provider()->os_integration_manager());
}
};
TEST_F(InstallPlaceholderJobTest, InstallPlaceholder) {
ExternalInstallOptions options(kInstallUrl, mojom::UserDisplayMode::kBrowser,
ExternalInstallSource::kExternalPolicy);
base::test::TestFuture<webapps::InstallResultCode, webapps::AppId> future;
provider()->command_manager().ScheduleCommand(
std::make_unique<InstallPlaceholderJobWrapperCommand>(
profile(), options, future.GetCallback()));
EXPECT_EQ(future.Get<0>(), webapps::InstallResultCode::kSuccessNewInstall);
const webapps::AppId app_id = future.Get<1>();
EXPECT_TRUE(provider()->registrar_unsafe().IsPlaceholderApp(
app_id, WebAppManagement::kPolicy));
std::optional<proto::os_state::WebAppOsIntegration> os_state =
provider()->registrar_unsafe().GetAppCurrentOsIntegrationState(app_id);
ASSERT_TRUE(os_state.has_value());
EXPECT_TRUE(os_state->has_shortcut());
EXPECT_EQ(os_state->run_on_os_login().run_on_os_login_mode(),
proto::os_state::RunOnOsLogin::MODE_NOT_RUN);
}
TEST_F(InstallPlaceholderJobTest, InstallPlaceholderWithOverrideIconUrl) {
ExternalInstallOptions options(kInstallUrl, mojom::UserDisplayMode::kBrowser,
ExternalInstallSource::kExternalPolicy);
const GURL icon_url("https://example.com/test.png");
options.override_icon_url = icon_url;
base::test::TestFuture<webapps::InstallResultCode, webapps::AppId> future;
auto data_retriever =
std::make_unique<testing::StrictMock<MockDataRetriever>>();
bool skip_page_favicons = true;
bool fail_all_if_any_fail = false;
SkBitmap bitmap;
std::vector<gfx::Size> icon_sizes(1, gfx::Size(kIconSize, kIconSize));
bitmap.allocN32Pixels(kIconSize, kIconSize);
bitmap.eraseColor(SK_ColorRED);
IconsMap icons = {{icon_url, {bitmap}}};
const IconUrlWithSize icon_metadata =
IconUrlWithSize::CreateForUnspecifiedSize(icon_url);
DownloadedIconsHttpResults http_result = {
{icon_metadata, net::HttpStatusCode::HTTP_OK}};
EXPECT_CALL(*data_retriever,
GetIcons(testing::_, testing::ElementsAre(icon_metadata),
skip_page_favicons, fail_all_if_any_fail,
base::test::IsNotNullCallback()))
.WillOnce(base::test::RunOnceCallback<4>(
IconsDownloadedResult::kCompleted, std::move(icons), http_result));
auto command = std::make_unique<InstallPlaceholderJobWrapperCommand>(
profile(), options, future.GetCallback(), std::move(data_retriever));
provider()->command_manager().ScheduleCommand(std::move(command));
EXPECT_EQ(future.Get<0>(), webapps::InstallResultCode::kSuccessNewInstall);
const webapps::AppId app_id = future.Get<1>();
EXPECT_TRUE(provider()->registrar_unsafe().IsPlaceholderApp(
app_id, WebAppManagement::kPolicy));
std::optional<proto::os_state::WebAppOsIntegration> os_state =
provider()->registrar_unsafe().GetAppCurrentOsIntegrationState(app_id);
ASSERT_TRUE(os_state.has_value());
EXPECT_TRUE(os_state->has_shortcut());
}
} // namespace
} // namespace web_app