blob: 0b269670fdcab2acbca2579d13b8e803fca24755 [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/commands/install_placeholder_command.h"
#include <memory>
#include <utility>
#include "base/strings/to_string.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/single_thread_task_runner.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/web_applications/external_install_options.h"
#include "chrome/browser/web_applications/install_bounce_metric.h"
#include "chrome/browser/web_applications/locks/app_lock.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_utils.h"
#include "chrome/browser/web_applications/web_app_uninstall_and_replace_job.h"
#include "chrome/browser/web_applications/web_contents/web_app_data_retriever.h"
#include "components/webapps/browser/install_result_code.h"
#include "content/public/browser/web_contents.h"
namespace web_app {
namespace {
// How often we retry to download a custom icon, not counting the first attempt.
const int MAX_ICON_DOWNLOAD_RETRIES = 1;
const base::TimeDelta ICON_DOWNLOAD_RETRY_DELAY = base::Seconds(5);
} // namespace
InstallPlaceholderCommand::InstallPlaceholderCommand(
Profile* profile,
const ExternalInstallOptions& install_options,
InstallAndReplaceCallback callback,
base::WeakPtr<content::WebContents> web_contents,
std::unique_ptr<WebAppDataRetriever> data_retriever)
: WebAppCommandTemplate<AppLock>("InstallPlaceholderCommand"),
profile_(profile),
app_id_(GenerateAppId(/*manifest_id=*/absl::nullopt,
install_options.install_url)),
lock_description_(std::make_unique<AppLockDescription>(app_id_)),
install_options_(install_options),
callback_(std::move(callback)),
web_contents_(web_contents),
data_retriever_(std::move(data_retriever)) {
debug_value_.Set("external_install_options", install_options.AsDebugValue());
debug_value_.Set("app_id", app_id_);
}
InstallPlaceholderCommand::~InstallPlaceholderCommand() = default;
void InstallPlaceholderCommand::StartWithLock(std::unique_ptr<AppLock> lock) {
lock_ = std::move(lock);
if (install_options_.override_icon_url) {
FetchCustomIcon(install_options_.override_icon_url.value(),
MAX_ICON_DOWNLOAD_RETRIES);
return;
}
FinalizeInstall(/*bitmaps=*/absl::nullopt);
}
const LockDescription& InstallPlaceholderCommand::lock_description() const {
return *lock_description_;
}
base::Value InstallPlaceholderCommand::ToDebugValue() const {
return base::Value(debug_value_.Clone());
}
void InstallPlaceholderCommand::OnSyncSourceRemoved() {}
void InstallPlaceholderCommand::OnShutdown() {
Abort(webapps::InstallResultCode::kCancelledOnWebAppProviderShuttingDown);
}
void InstallPlaceholderCommand::Abort(webapps::InstallResultCode code) {
if (!callback_) {
return;
}
debug_value_.Set("result_code", base::ToString(code));
webapps::InstallableMetrics::TrackInstallResult(false);
SignalCompletionAndSelfDestruct(
CommandResult::kFailure,
base::BindOnce(std::move(callback_), app_id_, code,
/*did_uninstall_and_replace=*/false));
}
void InstallPlaceholderCommand::FetchCustomIcon(const GURL& url,
int retries_left) {
if (!web_contents_ || web_contents_->IsBeingDestroyed()) {
Abort(webapps::InstallResultCode::kWebContentsDestroyed);
return;
}
data_retriever_->GetIcons(
web_contents_.get(), {url}, /*skip_page_favicons=*/true,
base::BindOnce(&InstallPlaceholderCommand::OnCustomIconFetched,
weak_factory_.GetWeakPtr(), url, retries_left));
}
void InstallPlaceholderCommand::OnCustomIconFetched(
const GURL& image_url,
int retries_left,
IconsDownloadedResult result,
IconsMap icons_map,
DownloadedIconsHttpResults icons_http_results) {
auto bitmaps_it = icons_map.find(image_url);
if (bitmaps_it != icons_map.end() && !bitmaps_it->second.empty()) {
// Download succeeded.
debug_value_.Set("custom_icon_download_success", true);
FinalizeInstall(bitmaps_it->second);
return;
}
if (retries_left <= 0) {
// Download failed.
debug_value_.Set("custom_icon_download_success", false);
FinalizeInstall(absl::nullopt);
return;
}
// Retry download.
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&InstallPlaceholderCommand::FetchCustomIcon,
weak_factory_.GetWeakPtr(), image_url, retries_left - 1),
ICON_DOWNLOAD_RETRY_DELAY);
}
void InstallPlaceholderCommand::FinalizeInstall(
absl::optional<std::reference_wrapper<const std::vector<SkBitmap>>>
bitmaps) {
WebAppInstallInfo web_app_info;
web_app_info.title =
install_options_.override_name
? base::UTF8ToUTF16(install_options_.override_name.value())
: install_options_.fallback_app_name
? base::UTF8ToUTF16(install_options_.fallback_app_name.value())
: base::UTF8ToUTF16(install_options_.install_url.spec());
if (bitmaps) {
IconsMap icons_map;
icons_map.emplace(GURL(install_options_.override_icon_url.value()),
bitmaps.value());
PopulateProductIcons(&web_app_info, &icons_map);
}
web_app_info.start_url = install_options_.install_url;
web_app_info.install_url = install_options_.install_url;
web_app_info.user_display_mode = install_options_.user_display_mode;
WebAppInstallFinalizer::FinalizeOptions options(
ConvertExternalInstallSourceToInstallSource(
install_options_.install_source));
// Overwrite fields if we are doing a forced reinstall, because some
// values (custom name or icon) might have changed.
options.overwrite_existing_manifest_fields = install_options_.force_reinstall;
options.add_to_applications_menu = install_options_.add_to_applications_menu;
options.add_to_desktop = install_options_.add_to_desktop;
options.add_to_quick_launch_bar = install_options_.add_to_quick_launch_bar;
web_app_info.is_placeholder = true;
lock_->install_finalizer().FinalizeInstall(
web_app_info, options,
base::BindOnce(&InstallPlaceholderCommand::OnInstallFinalized,
weak_factory_.GetWeakPtr()));
}
void InstallPlaceholderCommand::OnInstallFinalized(
const AppId& app_id,
webapps::InstallResultCode code,
OsHooksErrors os_hooks_errors) {
debug_value_.Set("result_code", base::ToString(code));
if (!web_contents_ || web_contents_->IsBeingDestroyed()) {
Abort(webapps::InstallResultCode::kWebContentsDestroyed);
return;
}
if (code != webapps::InstallResultCode::kSuccessNewInstall) {
Abort(code);
return;
}
RecordWebAppInstallationTimestamp(
Profile::FromBrowserContext(web_contents_->GetBrowserContext())
->GetPrefs(),
app_id,
ConvertExternalInstallSourceToInstallSource(
install_options_.install_source));
RecordAppBanner(web_contents_.get(), install_options_.install_url);
webapps::InstallableMetrics::TrackInstallResult(webapps::IsSuccess(code));
DCHECK(lock_);
uninstall_and_replace_job_.emplace(
profile_, lock_->AsWeakPtr(), install_options_.uninstall_and_replace,
app_id,
base::BindOnce(&InstallPlaceholderCommand::OnUninstallAndReplaced,
weak_factory_.GetWeakPtr(), app_id, std::move(code)));
uninstall_and_replace_job_->Start();
}
void InstallPlaceholderCommand::OnUninstallAndReplaced(
const AppId& app_id,
webapps::InstallResultCode code,
bool did_uninstall_and_replace) {
debug_value_.Set("did_uninstall_and_replace", did_uninstall_and_replace);
SignalCompletionAndSelfDestruct(
CommandResult::kSuccess,
base::BindOnce(std::move(callback_), app_id, std::move(code),
did_uninstall_and_replace));
}
} // namespace web_app