blob: f7a331f8f852872dd677bf2987c0d53e8a6a71f1 [file] [log] [blame]
// Copyright 2022 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/isolated_web_apps/install_isolated_web_app_command.h"
#include <memory>
#include <optional>
#include <ostream>
#include <string>
#include <string_view>
#include <utility>
#include "base/check.h"
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/sequence_checker.h"
#include "base/strings/to_string.h"
#include "base/types/expected.h"
#include "base/types/expected_macros.h"
#include "base/values.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/web_applications/callback_utils.h"
#include "chrome/browser/web_applications/commands/web_app_command.h"
#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_install_command_helper.h"
#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_install_source.h"
#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_storage_location.h"
#include "chrome/browser/web_applications/locks/app_lock.h"
#include "chrome/browser/web_applications/web_app.h"
#include "chrome/browser/web_applications/web_app_helpers.h"
#include "chrome/browser/web_applications/web_app_install_finalizer.h"
#include "chrome/browser/web_applications/web_app_install_info.h"
#include "chrome/browser/web_applications/web_app_install_utils.h"
#include "chrome/browser/web_applications/web_app_utils.h"
#include "chrome/browser/web_applications/web_contents/web_contents_manager.h"
#include "components/web_package/signed_web_bundles/signed_web_bundle_id.h"
#include "components/webapps/browser/install_result_code.h"
#include "components/webapps/browser/installable/installable_logging.h"
#include "components/webapps/browser/installable/installable_metrics.h"
#include "components/webapps/browser/web_contents/web_app_url_loader.h"
#include "components/webapps/common/web_app_id.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/web_contents.h"
namespace web_app {
InstallIsolatedWebAppCommandSuccess::InstallIsolatedWebAppCommandSuccess(
base::Version installed_version,
IsolatedWebAppStorageLocation location)
: installed_version(std::move(installed_version)),
location(std::move(location)) {}
InstallIsolatedWebAppCommandSuccess::~InstallIsolatedWebAppCommandSuccess() =
default;
InstallIsolatedWebAppCommandSuccess::InstallIsolatedWebAppCommandSuccess(
const InstallIsolatedWebAppCommandSuccess& other) = default;
std::ostream& operator<<(std::ostream& os,
const InstallIsolatedWebAppCommandSuccess& success) {
return os << "InstallIsolatedWebAppCommandSuccess "
<< base::Value::Dict()
.Set("installed_version",
success.installed_version.GetString())
.Set("location", base::ToString(success.location));
}
std::ostream& operator<<(std::ostream& os,
const InstallIsolatedWebAppCommandError& error) {
return os << "InstallIsolatedWebAppCommandError { message = \""
<< error.message << "\" }.";
}
InstallIsolatedWebAppCommand::InstallIsolatedWebAppCommand(
const IsolatedWebAppUrlInfo& url_info,
const IsolatedWebAppInstallSource& install_source,
const std::optional<base::Version>& expected_version,
std::unique_ptr<content::WebContents> web_contents,
std::unique_ptr<ScopedKeepAlive> optional_keep_alive,
std::unique_ptr<ScopedProfileKeepAlive> optional_profile_keep_alive,
base::OnceCallback<void(base::expected<InstallIsolatedWebAppCommandSuccess,
InstallIsolatedWebAppCommandError>)>
callback,
std::unique_ptr<IsolatedWebAppInstallCommandHelper> command_helper)
: WebAppCommand<AppLock,
base::expected<InstallIsolatedWebAppCommandSuccess,
InstallIsolatedWebAppCommandError>>(
"InstallIsolatedWebAppCommand",
AppLockDescription(url_info.app_id()),
base::BindOnce(
[](web_package::SignedWebBundleId web_bundle_id,
base::expected<InstallIsolatedWebAppCommandSuccess,
InstallIsolatedWebAppCommandError> result) {
webapps::InstallableMetrics::TrackInstallResult(
result.has_value());
DVLOG(0) << "Install result of IWA "
<< base::ToString(web_bundle_id) << ": "
<< (result.has_value()
? base::ToString(result.value())
: base::ToString(result.error()));
return result;
},
url_info.web_bundle_id())
.Then(std::move(callback)),
/*args_for_shutdown=*/
base::unexpected(InstallIsolatedWebAppCommandError{
.message = std::string("System shutting down.")})),
command_helper_(std::move(command_helper)),
url_info_(url_info),
expected_version_(expected_version),
install_surface_(install_source.install_surface()),
install_source_(install_source.source()),
web_contents_(std::move(web_contents)),
optional_keep_alive_(std::move(optional_keep_alive)),
optional_profile_keep_alive_(std::move(optional_profile_keep_alive)) {
DETACH_FROM_SEQUENCE(sequence_checker_);
CHECK(web_contents_ != nullptr);
CHECK(optional_profile_keep_alive_ == nullptr ||
&profile() == optional_profile_keep_alive_->profile());
GetMutableDebugValue().Set("app_id", url_info_.app_id());
GetMutableDebugValue().Set("origin", url_info_.origin().Serialize());
GetMutableDebugValue().Set("bundle_id", url_info_.web_bundle_id().id());
GetMutableDebugValue().Set(
"bundle_type", static_cast<int>(url_info_.web_bundle_id().type()));
GetMutableDebugValue().Set("install_surface",
base::ToString(install_surface_));
GetMutableDebugValue().Set("install_source", install_source_->ToDebugValue());
GetMutableDebugValue().Set("expected_version",
expected_version_.has_value()
? expected_version_->GetString()
: "unknown");
}
InstallIsolatedWebAppCommand::~InstallIsolatedWebAppCommand() {
if (destination_storage_location_.has_value()) {
CleanupLocationIfOwned(profile().GetPath(), *destination_storage_location_,
base::DoNothing());
}
}
void InstallIsolatedWebAppCommand::StartWithLock(
std::unique_ptr<AppLock> lock) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
lock_ = std::move(lock);
url_loader_ = lock_->web_contents_manager().CreateUrlLoader();
auto weak_ptr = weak_factory_.GetWeakPtr();
RunChainedCallbacks(
base::BindOnce(&InstallIsolatedWebAppCommand::CopyToProfileDirectory,
weak_ptr),
base::BindOnce(&InstallIsolatedWebAppCommand::CheckTrustAndSignatures,
weak_ptr),
base::BindOnce(&InstallIsolatedWebAppCommand::CreateStoragePartition,
weak_ptr),
base::BindOnce(&InstallIsolatedWebAppCommand::LoadInstallUrl, weak_ptr),
base::BindOnce(
&InstallIsolatedWebAppCommand::CheckInstallabilityAndRetrieveManifest,
weak_ptr),
base::BindOnce(
&InstallIsolatedWebAppCommand::ValidateManifestAndCreateInstallInfo,
weak_ptr),
base::BindOnce(
&InstallIsolatedWebAppCommand::RetrieveIconsAndPopulateInstallInfo,
weak_ptr),
base::BindOnce(&InstallIsolatedWebAppCommand::FinalizeInstall, weak_ptr));
}
void InstallIsolatedWebAppCommand::CopyToProfileDirectory(
base::OnceClosure next_step_callback) {
UpdateBundlePathAndCreateStorageLocation(
profile().GetPath(), *install_source_,
base::BindOnce(&InstallIsolatedWebAppCommand::OnCopiedToProfileDirectory,
weak_factory_.GetWeakPtr(),
std::move(next_step_callback)));
}
void InstallIsolatedWebAppCommand::OnCopiedToProfileDirectory(
base::OnceClosure next_step_callback,
base::expected<IsolatedWebAppStorageLocation, std::string> new_location) {
ASSIGN_OR_RETURN(destination_storage_location_, new_location,
&InstallIsolatedWebAppCommand::ReportFailure, this);
destination_source_ = IwaSourceWithMode::FromStorageLocation(
profile().GetPath(), *destination_storage_location_);
// Make sure that `install_source_`, which is now outdated, can no longer be
// accessed.
install_source_.reset();
GetMutableDebugValue().Set("destination_source",
destination_source_->ToDebugValue());
GetMutableDebugValue().Set("destination_storage_location",
destination_storage_location_->ToDebugValue());
std::move(next_step_callback).Run();
}
void InstallIsolatedWebAppCommand::CheckTrustAndSignatures(
base::OnceClosure next_step_callback) {
command_helper_->CheckTrustAndSignatures(
*destination_source_, &profile(),
base::BindOnce(&InstallIsolatedWebAppCommand::RunNextStepOnSuccess<void>,
weak_factory_.GetWeakPtr(),
std::move(next_step_callback)));
}
void InstallIsolatedWebAppCommand::CreateStoragePartition(
base::OnceClosure next_step_callback) {
command_helper_->CreateStoragePartitionIfNotPresent(profile());
std::move(next_step_callback).Run();
}
void InstallIsolatedWebAppCommand::LoadInstallUrl(
base::OnceClosure next_step_callback) {
command_helper_->LoadInstallUrl(
*destination_source_, *web_contents_.get(), *url_loader_.get(),
base::BindOnce(&InstallIsolatedWebAppCommand::RunNextStepOnSuccess<void>,
weak_factory_.GetWeakPtr(),
std::move(next_step_callback)));
}
void InstallIsolatedWebAppCommand::CheckInstallabilityAndRetrieveManifest(
base::OnceCallback<void(IsolatedWebAppInstallCommandHelper::ManifestAndUrl)>
next_step_callback) {
command_helper_->CheckInstallabilityAndRetrieveManifest(
*web_contents_.get(),
base::BindOnce(&InstallIsolatedWebAppCommand::RunNextStepOnSuccess<
IsolatedWebAppInstallCommandHelper::ManifestAndUrl>,
weak_factory_.GetWeakPtr(),
std::move(next_step_callback)));
}
void InstallIsolatedWebAppCommand::ValidateManifestAndCreateInstallInfo(
base::OnceCallback<void(WebAppInstallInfo)> next_step_callback,
IsolatedWebAppInstallCommandHelper::ManifestAndUrl manifest_and_url) {
base::expected<WebAppInstallInfo, std::string> install_info =
command_helper_->ValidateManifestAndCreateInstallInfo(expected_version_,
manifest_and_url);
RunNextStepOnSuccess(std::move(next_step_callback), std::move(install_info));
}
void InstallIsolatedWebAppCommand::RetrieveIconsAndPopulateInstallInfo(
base::OnceCallback<void(WebAppInstallInfo)> next_step_callback,
WebAppInstallInfo install_info) {
CHECK(!expected_version_ ||
*expected_version_ == install_info.isolated_web_app_version);
actual_version_ = install_info.isolated_web_app_version;
GetMutableDebugValue().Set("actual_version", actual_version_->GetString());
command_helper_->RetrieveIconsAndPopulateInstallInfo(
std::move(install_info), *web_contents_.get(),
base::BindOnce(&InstallIsolatedWebAppCommand::RunNextStepOnSuccess<
WebAppInstallInfo>,
weak_factory_.GetWeakPtr(),
std::move(next_step_callback)));
}
void InstallIsolatedWebAppCommand::FinalizeInstall(WebAppInstallInfo info) {
WebAppInstallFinalizer::FinalizeOptions options(install_surface_);
options.isolated_web_app_location = *destination_storage_location_;
lock_->install_finalizer().FinalizeInstall(
info, options,
base::BindOnce(&InstallIsolatedWebAppCommand::OnFinalizeInstall,
weak_factory_.GetWeakPtr()));
}
void InstallIsolatedWebAppCommand::OnFinalizeInstall(
const webapps::AppId& unused_app_id,
webapps::InstallResultCode install_result_code) {
if (install_result_code == webapps::InstallResultCode::kSuccessNewInstall) {
ReportSuccess();
} else {
ReportFailure("Error during finalization: " +
base::ToString(install_result_code));
}
}
void InstallIsolatedWebAppCommand::ReportFailure(std::string_view message) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
GetMutableDebugValue().Set("result", base::StrCat({"error: ", message}));
CompleteAndSelfDestruct(CommandResult::kFailure,
base::unexpected(InstallIsolatedWebAppCommandError{
.message = std::string(message)}));
}
void InstallIsolatedWebAppCommand::ReportSuccess() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
GetMutableDebugValue().Set("result", "success");
// Reset `destination_storage_location_` to prevent cleanup in the destructor.
IsolatedWebAppStorageLocation location =
std::exchange(destination_storage_location_, std::nullopt).value();
CompleteAndSelfDestruct(CommandResult::kSuccess,
InstallIsolatedWebAppCommandSuccess(
*actual_version_, std::move(location)));
}
Profile& InstallIsolatedWebAppCommand::profile() {
CHECK(web_contents_);
CHECK(web_contents_->GetBrowserContext());
return *Profile::FromBrowserContext(web_contents_->GetBrowserContext());
}
} // namespace web_app