#include "chrome/browser/ash/apps/apk_web_app_installer.h"
#include <limits>
#include <utility>
#include "base/functional/bind.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/web_applications/mojom/user_display_mode.mojom.h"
#include "chrome/browser/web_applications/web_app_command_scheduler.h"
#include "chrome/browser/web_applications/web_app_helpers.h"
#include "chrome/browser/web_applications/web_app_install_params.h"
#include "chrome/browser/web_applications/web_app_install_utils.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#include "chrome/browser/web_applications/web_app_utils.h"
#include "components/webapps/browser/install_result_code.h"
#include "components/webapps/browser/installable/installable_metrics.h"
#include "content/public/browser/browser_thread.h"
#include "services/data_decoder/public/cpp/decode_image.h"
#include "third_party/blink/public/mojom/manifest/display_mode.mojom.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/gfx/image/image_skia.h"
#include "url/gurl.h"
#include "url/url_constants.h"
namespace {
constexpr int64_t kInvalidColor =
static_cast<int64_t>(std::numeric_limits<int32_t>::max()) + 1;
}
namespace ash {
void ApkWebAppInstaller::Install(Profile* profile,
const std::string& package_name,
arc::mojom::WebAppInfoPtr web_app_info,
arc::mojom::RawIconPngDataPtr icon,
InstallFinishCallback callback,
base::WeakPtr<Owner> weak_owner) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(weak_owner.get());
auto* installer =
new ApkWebAppInstaller(profile, std::move(callback), weak_owner);
installer->Start(package_name, std::move(web_app_info), std::move(icon));
}
ApkWebAppInstaller::ApkWebAppInstaller(Profile* profile,
InstallFinishCallback callback,
base::WeakPtr<Owner> weak_owner)
: profile_(profile),
is_web_only_twa_(false),
sha256_fingerprint_(std::nullopt),
callback_(std::move(callback)),
weak_owner_(weak_owner) {}
ApkWebAppInstaller::~ApkWebAppInstaller() = default;
void ApkWebAppInstaller::Start(const std::string& package_name,
arc::mojom::WebAppInfoPtr arc_web_app_info,
arc::mojom::RawIconPngDataPtr icon) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!weak_owner_.get()) {
CompleteInstallation(webapps::AppId(),
webapps::InstallResultCode::kApkWebAppInstallFailed);
return;
}
if (arc_web_app_info.is_null() || !icon || !icon->icon_png_data ||
!icon->icon_png_data.has_value() || icon->icon_png_data->empty()) {
LOG(ERROR) << "Insufficient data to install a web app";
CompleteInstallation(webapps::AppId(),
webapps::InstallResultCode::kApkWebAppInstallFailed);
return;
}
DCHECK(!web_app_install_info_);
auto start_url = GURL(arc_web_app_info->start_url);
webapps::ManifestId manifest_id =
web_app::GenerateManifestIdFromStartUrlOnly(start_url);
web_app_install_info_ =
std::make_unique<web_app::WebAppInstallInfo>(manifest_id, start_url);
web_app_install_info_->title = base::UTF8ToUTF16(arc_web_app_info->title);
web_app_install_info_->scope = GURL(arc_web_app_info->scope_url);
DCHECK(web_app_install_info_->scope.is_valid());
web_app_install_info_->additional_policy_ids.push_back(package_name);
web_app_install_info_->install_url = GURL(arc_web_app_info->start_url);
DCHECK(web_app_install_info_->install_url.is_valid());
if (arc_web_app_info->theme_color != kInvalidColor) {
web_app_install_info_->theme_color = SkColorSetA(
static_cast<SkColor>(arc_web_app_info->theme_color), SK_AlphaOPAQUE);
}
web_app_install_info_->display_mode = blink::mojom::DisplayMode::kStandalone;
web_app_install_info_->user_display_mode =
web_app::mojom::UserDisplayMode::kStandalone;
is_web_only_twa_ = arc_web_app_info->is_web_only_twa;
sha256_fingerprint_ = arc_web_app_info->certificate_sha256_fingerprint;
data_decoder::DecodeImageIsolated(
std::move(icon->icon_png_data.value()),
data_decoder::mojom::ImageCodec::kDefault,
false, data_decoder::kDefaultMaxSizeInBytes,
gfx::Size(),
base::BindOnce(&ApkWebAppInstaller::OnImageDecoded,
base::Unretained(this)));
}
void ApkWebAppInstaller::CompleteInstallation(const webapps::AppId& id,
webapps::InstallResultCode code) {
std::move(callback_).Run(id, is_web_only_twa_, sha256_fingerprint_, code);
delete this;
}
void ApkWebAppInstaller::OnWebAppCreated(const GURL& start_url,
const webapps::AppId& app_id,
webapps::InstallResultCode code) {
if (!weak_owner_.get()) {
CompleteInstallation(
webapps::AppId(),
webapps::InstallResultCode::kCancelledOnWebAppProviderShuttingDown);
return;
}
if (code != webapps::InstallResultCode::kSuccessNewInstall) {
CompleteInstallation(app_id, code);
return;
}
CompleteInstallation(app_id, code);
}
void ApkWebAppInstaller::OnImageDecoded(const SkBitmap& decoded_image) {
DCHECK(web_app_install_info_);
if (decoded_image.width() == decoded_image.height())
web_app_install_info_->icon_bitmaps.any[decoded_image.width()] =
decoded_image;
if (!weak_owner_.get()) {
CompleteInstallation(
webapps::AppId(),
webapps::InstallResultCode::kCancelledOnWebAppProviderShuttingDown);
return;
}
DoInstall();
}
void ApkWebAppInstaller::DoInstall() {
auto* provider = web_app::WebAppProvider::GetForWebApps(profile_);
DCHECK(provider);
GURL start_url = web_app_install_info_->start_url();
provider->scheduler().InstallFromInfoWithParams(
std::move(web_app_install_info_),
false,
webapps::WebappInstallSource::ARC,
base::BindOnce(&ApkWebAppInstaller::OnWebAppCreated,
base::Unretained(this), std::move(start_url)),
web_app::WebAppInstallParams());
}
}