910e62b5创建于 1月15日历史提交
// Copyright 2018 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/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 {

// static
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());

  // If |weak_owner| is invalidated, installation will be stopped.
  // ApkWebAppInstaller owns itself and deletes itself when finished in
  // CompleteInstallation().
  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;
  }

  // We can't install without |arc_web_app_info| or |icon_png_data|. They
  // may be null if there was an error generating the data.
  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);
  // TODO(b:340994232): ARC-installed web apps should pass through a manifest ID
  // and use it here instead of assuming it is not set and generating it from
  // the 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);

  // The install_url and the start_url seem to be same in this case.
  // This is because inside OnWebAppCreated(), the start_url is
  // passed to the external prefs to be stored as the install_url.
  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;

  // Decode the image in a sandboxed process off the main thread.
  // base::Unretained is safe because this object owns itself.
  data_decoder::DecodeImageIsolated(
      std::move(icon->icon_png_data.value()),
      data_decoder::mojom::ImageCodec::kDefault,
      /*shrink_to_fit=*/false, data_decoder::kDefaultMaxSizeInBytes,
      /*desired_image_frame_size=*/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) {
  // It is assumed that if |weak_owner_| is gone, |profile_| is gone too. The
  // web app will be automatically cleaned up by provider.
  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()) {
    // Assume |profile_| is no longer valid - destroy this object and
    // terminate.
    CompleteInstallation(
        webapps::AppId(),
        webapps::InstallResultCode::kCancelledOnWebAppProviderShuttingDown);
    return;
  }
  DoInstall();
}

void ApkWebAppInstaller::DoInstall() {
  auto* provider = web_app::WebAppProvider::GetForWebApps(profile_);
  DCHECK(provider);
  // Doesn't overwrite already existing web app with manifest fields from the
  // apk.
  GURL start_url = web_app_install_info_->start_url();
  provider->scheduler().InstallFromInfoWithParams(
      std::move(web_app_install_info_),
      /*overwrite_existing_manifest_fields=*/false,
      webapps::WebappInstallSource::ARC,
      base::BindOnce(&ApkWebAppInstaller::OnWebAppCreated,
                     base::Unretained(this), std::move(start_url)),
      web_app::WebAppInstallParams());
}

}  // namespace ash