blob: ccdbe0e7e17c2f2b8112d88a27ec032ef0d98ffa [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/extensions/api/terminal/crostini_startup_status.h"
#include <algorithm>
#include <vector>
#include "base/containers/flat_map.h"
#include "base/no_destructor.h"
#include "base/strings/strcat.h"
#include "base/strings/stringprintf.h"
#include "base/system/sys_info.h"
#include "base/time/time.h"
#include "chrome/grit/generated_resources.h"
#include "chromeos/dbus/util/version_loader.h"
#include "components/version_info/version_info.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "ui/base/l10n/l10n_util.h"
using crostini::mojom::InstallerState;
namespace extensions {
namespace {
const char kCursorHide[] = "\x1b[?25l";
const char kCursorShow[] = "\x1b[?25h";
const char kColor0Normal[] = "\x1b[0m"; // Default.
const char kColor1RedBright[] = "\x1b[1;31m";
const char kColor2GreenBright[] = "\x1b[1;32m";
const char kColor3Yellow[] = "\x1b[33m";
const char kColor5Purple[] = "\x1b[35m";
const char kEraseInLine[] = "\x1b[K";
const char kSpinner[] = "|/-\\";
const int kMaxStage = 9;
std::string MoveForward(int i) {
return base::StringPrintf("\x1b[%dC", i);
}
} // namespace
CrostiniStartupStatus::CrostiniStartupStatus(
base::RepeatingCallback<void(const std::string&)> print,
bool verbose)
: print_(std::move(print)), verbose_(verbose) {
}
CrostiniStartupStatus::~CrostiniStartupStatus() = default;
void CrostiniStartupStatus::OnCrostiniRestarted(
crostini::CrostiniResult result) {
if (result != crostini::CrostiniResult::SUCCESS) {
PrintAfterStage(
kColor1RedBright,
base::StringPrintf("Error starting penguin container: %d\r\n", result));
} else {
if (verbose_) {
stage_index_ = kMaxStage + 1; // done.
PrintStage(kColor2GreenBright,
base::StrCat({l10n_util::GetStringUTF8(
IDS_CROSTINI_TERMINAL_STATUS_READY),
"\r\n"}));
}
}
Print(
base::StringPrintf("\r%s%s%s", kEraseInLine, kColor0Normal, kCursorShow));
}
void CrostiniStartupStatus::ShowProgressAtInterval() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// Wait one interval before showing progress.
if (spinner_index_ > 0) {
PrintProgress();
}
++spinner_index_;
content::GetUIThreadTaskRunner({})->PostDelayedTask(
FROM_HERE,
base::BindOnce(&CrostiniStartupStatus::ShowProgressAtInterval,
weak_factory_.GetWeakPtr()),
base::TimeDelta::FromMilliseconds(300));
}
void CrostiniStartupStatus::OnStageStarted(InstallerState stage) {
stage_ = stage;
if (stage_index_ < kMaxStage) {
++stage_index_;
}
if (!verbose_) {
return;
}
static base::NoDestructor<base::flat_map<InstallerState, std::string>>
kStartStrings({
{InstallerState::kStart,
l10n_util::GetStringUTF8(IDS_CROSTINI_TERMINAL_STATUS_START)},
{InstallerState::kInstallImageLoader,
l10n_util::GetStringUTF8(
IDS_CROSTINI_TERMINAL_STATUS_INSTALL_IMAGE_LOADER)},
{InstallerState::kCreateDiskImage,
l10n_util::GetStringUTF8(
IDS_CROSTINI_TERMINAL_STATUS_CREATE_DISK_IMAGE)},
{InstallerState::kStartTerminaVm,
l10n_util::GetStringUTF8(
IDS_CROSTINI_TERMINAL_STATUS_START_TERMINA_VM)},
{InstallerState::kCreateContainer,
l10n_util::GetStringUTF8(
IDS_CROSTINI_TERMINAL_STATUS_CREATE_CONTAINER)},
{InstallerState::kSetupContainer,
l10n_util::GetStringUTF8(
IDS_CROSTINI_TERMINAL_STATUS_SETUP_CONTAINER)},
{InstallerState::kStartContainer,
l10n_util::GetStringUTF8(
IDS_CROSTINI_TERMINAL_STATUS_START_CONTAINER)},
{InstallerState::kFetchSshKeys,
l10n_util::GetStringUTF8(
IDS_CROSTINI_TERMINAL_STATUS_FETCH_SSH_KEYS)},
{InstallerState::kMountContainer,
l10n_util::GetStringUTF8(
IDS_CROSTINI_TERMINAL_STATUS_MOUNT_CONTAINER)},
});
const std::string& stage_string = (*kStartStrings)[stage];
PrintStage(kColor3Yellow, stage_string);
}
void CrostiniStartupStatus::OnContainerDownloading(int32_t download_percent) {
if (download_percent % 8 == 0) {
PrintAfterStage(kColor3Yellow, ".");
}
}
void CrostiniStartupStatus::Print(const std::string& output) {
print_.Run(output);
}
void CrostiniStartupStatus::InitializeProgress() {
if (progress_initialized_) {
return;
}
progress_initialized_ = true;
Print(base::StringPrintf("%s%s[%s] ", kCursorHide, kColor5Purple,
std::string(kMaxStage, ' ').c_str()));
}
void CrostiniStartupStatus::PrintProgress() {
InitializeProgress();
Print(base::StringPrintf("\r%s%s%c", MoveForward(stage_index_).c_str(),
kColor5Purple, kSpinner[spinner_index_ & 0x3]));
}
void CrostiniStartupStatus::PrintStage(const char* color,
const std::string& output) {
DCHECK_GE(stage_index_, 1);
InitializeProgress();
std::string progress(stage_index_ - 1, '=');
Print(base::StringPrintf("\r%s[%s%s%s%s%s ", kColor5Purple, progress.c_str(),
MoveForward(3 + (kMaxStage - stage_index_)).c_str(),
kEraseInLine, color, output.c_str()));
end_of_line_index_ = 4 + kMaxStage + output.size();
}
void CrostiniStartupStatus::PrintAfterStage(const char* color,
const std::string& output) {
InitializeProgress();
Print(base::StringPrintf("\r%s%s%s", MoveForward(end_of_line_index_).c_str(),
color, output.c_str()));
end_of_line_index_ += output.size();
}
} // namespace extensions