chromium/chrome/browser/ui/webui/ash/borealis_installer/borealis_installer_page_handler.cc

// 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/ui/webui/ash/borealis_installer/borealis_installer_page_handler.h"
#include "ash/webui/settings/public/constants/routes.mojom.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "chrome/browser/ash/borealis/borealis_app_launcher.h"
#include "chrome/browser/ash/borealis/borealis_context_manager.h"
#include "chrome/browser/ash/borealis/borealis_features.h"
#include "chrome/browser/ash/borealis/borealis_installer.h"
#include "chrome/browser/ash/borealis/borealis_metrics.h"
#include "chrome/browser/ash/borealis/borealis_service.h"
#include "chrome/browser/ash/borealis/borealis_types.mojom.h"
#include "chrome/browser/ash/borealis/borealis_util.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/settings_window_manager_chromeos.h"
#include "chrome/browser/ui/views/borealis/borealis_splash_screen_view.h"
#include "chrome/grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/l10n/time_format.h"
#include "ui/strings/grit/ui_strings.h"
#include "ui/web_dialogs/web_dialog_ui.h"

namespace {

// Returns the text to be used for a predicted completion time, which is
// something like "starting up" or "3 mins remaining" depending on how
// accurately we can predict.
std::string GetInstallationPredictionText(const base::Time& start_time,
                                          double completion_proportion) {
  base::TimeDelta duration = base::Time::Now() - start_time;
  // We have no confidence in the prediction for the first second or for
  // too-small proportions.
  if (completion_proportion < 0.001 || duration < base::Seconds(1)) {
    return l10n_util::GetStringUTF8(IDS_BOREALIS_INSTALLER_ONGOING_INITIAL);
  }
  // Linear-interpolation to predict remaining time.
  base::TimeDelta remaining = (duration / completion_proportion) - duration;
  return base::UTF16ToUTF8(
      ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_REMAINING,
                             ui::TimeFormat::LENGTH_SHORT, remaining));
}

}  // namespace

namespace ash {

BorealisInstallerPageHandler::BorealisInstallerPageHandler(
    mojo::PendingReceiver<ash::borealis_installer::mojom::PageHandler>
        pending_page_handler,
    mojo::PendingRemote<ash::borealis_installer::mojom::Page> pending_page,
    base::OnceClosure on_page_closed,
    content::WebUI* web_ui)
    : receiver_{this, std::move(pending_page_handler)},
      page_{std::move(pending_page)},
      on_page_closed_{std::move(on_page_closed)},
      profile_(Profile::FromWebUI(web_ui)),
      observation_(this) {}

BorealisInstallerPageHandler::~BorealisInstallerPageHandler() = default;

void BorealisInstallerPageHandler::Install() {
  install_start_time_ = base::Time::Now();
  borealis::BorealisInstaller& installer =
      borealis::BorealisService::GetForProfile(profile_)->Installer();
  if (observation_.IsObserving()) {
    observation_.Reset();
  }
  observation_.Observe(&installer);
  installer.Start();
}

void BorealisInstallerPageHandler::ShutDown() {
  borealis::BorealisService::GetForProfile(profile_)
      ->ContextManager()
      .ShutDownBorealis(
          base::BindOnce([](borealis::BorealisShutdownResult result) {
            if (result == borealis::BorealisShutdownResult::kSuccess) {
              return;
            }
            LOG(ERROR) << "Failed to shutdown borealis after install: code="
                       << static_cast<int>(result);
          }));
}

void BorealisInstallerPageHandler::CancelInstall() {
  borealis::BorealisService::GetForProfile(profile_)->Installer().Cancel();
}

void BorealisInstallerPageHandler::Launch() {
  // Installation starts the borealis VM, so the splash screen would normally
  // not show, therefore we need to show it manually here. Since this is
  // post-install we know borealis wasn't running previously.
  borealis::ShowBorealisSplashScreenView(profile_);
  // Launch button has been clicked.
  borealis::BorealisService::GetForProfile(profile_)->AppLauncher().Launch(
      borealis::kClientAppId,
      borealis::BorealisLaunchSource::kPostInstallLaunch,
      base::BindOnce([](borealis::BorealisAppLauncher::LaunchResult result) {
        if (result == borealis::BorealisAppLauncher::LaunchResult::kSuccess) {
          return;
        }
        LOG(ERROR) << "Failed to launch borealis after install: code="
                   << static_cast<int>(result);
      }));
}

void BorealisInstallerPageHandler::OnPageClosed() {
  if (on_page_closed_) {
    std::move(on_page_closed_).Run();
  }
}

void BorealisInstallerPageHandler::OnProgressUpdated(double fraction_complete) {
  page_->OnProgressUpdate(
      fraction_complete,
      GetInstallationPredictionText(install_start_time_, fraction_complete));
}

void BorealisInstallerPageHandler::OnInstallationEnded(
    borealis::mojom::InstallResult result,
    const std::string& error_description) {
  page_->OnInstallFinished(result);
}

void BorealisInstallerPageHandler::OpenStoragePage() {
  chrome::SettingsWindowManager::GetInstance()->ShowOSSettings(
      profile_, chromeos::settings::mojom::kStorageSubpagePath);
}

void BorealisInstallerPageHandler::RequestClosePage() {
  page_->RequestClose();
}

}  // namespace ash