Skip to content

Commit

Permalink
Implement Lacros Data Migration error page.
Browse files Browse the repository at this point in the history
If the migration failed with out of disk, show the special page,
to let users know they need to free up their disk space.
It also has "go to files" button, which is to open files.app
in the following session. (This is not yet implemented in this CL,
but TODO is added).

Otherwise, generic error page is shown, to let users give it another
try later.

BUG=1296174
TEST=Ran manually on DUT. Ran browser_tests.

Change-Id: Ie313b4b5334708ee6a498033a382469eb255897a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3465954
Reviewed-by: Denis Kuznetsov <antrim@chromium.org>
Commit-Queue: Hidehiko Abe <hidehiko@chromium.org>
Cr-Commit-Position: refs/heads/main@{#984764}
  • Loading branch information
Hidehiko Abe authored and Chromium LUCI CQ committed Mar 24, 2022
1 parent 19dae26 commit 515a55f
Show file tree
Hide file tree
Showing 13 changed files with 289 additions and 13 deletions.
15 changes: 15 additions & 0 deletions chrome/app/chromeos_strings.grdp
Expand Up @@ -4273,6 +4273,21 @@
<message name="IDS_LACROS_DATA_MIGRATION_SCREEN_SKIP_SUGGESTION" desc="Message shown when skip button is visible.">
This is taking longer than expected, you can skip or wait until it's done.
</message>
<message name="IDS_LACROS_DATA_MIGRATION_SCREEN_ERROR_TITLE" desc="Shown if the lacros migration fails.">
Chrome browser could not be updated
</message>
<message name="IDS_LACROS_DATA_MIGRATION_SCREEN_ERROR_LOW_DISK_SPACE" desc="Shown if the lacros migration failed with low disk space error.">
There isn't enough space on this device to complete this update. Clean up <ph name="NECESSARY_SPACE">$1<ex>10 MB</ex></ph> on your device and try again from your Chrome browser.
</message>
<message name="IDS_LACROS_DATA_MIGRATION_SCREEN_ERROR_SUBTITLE" desc="Shown if the lacros migration failed other than low disk space error.">
You can try again later in Chrome.
</message>
<message name="IDS_LACROS_DATA_MIGRATION_SCREEN_ERROR_CANCEL_BUTTON" desc="Text button shown on the lacros data migration failure to cancel everything">
Cancel
</message>
<message name="IDS_LACROS_DATA_MIGRATION_SCREEN_ERROR_GOTO_FILES_BUTTON" desc="Text button shown on the lacros data migration failure to open files.app in the following user session">
Go to files
</message>

<!-- Lacros data migration triggering UI. -->
<message name="IDS_LACROS_DATA_MIGRATION_RELAUNCH" desc="Message shown as a menu to relaunch Chrome in order to trigger data migration.">
Expand Down
@@ -0,0 +1 @@
6c6cadecf63cfcde457d87a9e1d9bb1e77bfde93
@@ -0,0 +1 @@
6c6cadecf63cfcde457d87a9e1d9bb1e77bfde93
@@ -0,0 +1 @@
6c6cadecf63cfcde457d87a9e1d9bb1e77bfde93
@@ -0,0 +1 @@
ecff12674ec971dbedf9a43d9368296f77c94c9c
@@ -0,0 +1 @@
6c6cadecf63cfcde457d87a9e1d9bb1e77bfde93
48 changes: 40 additions & 8 deletions chrome/browser/ash/login/screens/lacros_data_migration_screen.cc
Expand Up @@ -23,6 +23,8 @@
namespace ash {
namespace {
constexpr char kUserActionSkip[] = "skip";
constexpr char kUserActionCancel[] = "cancel";
constexpr char kUserActionGotoFiles[] = "gotoFiles";
constexpr base::TimeDelta kShowSkipButtonDuration = base::Seconds(20);

// If the battery percent is lower than this ratio, and the charger is not
Expand All @@ -34,7 +36,8 @@ LacrosDataMigrationScreen::LacrosDataMigrationScreen(
LacrosDataMigrationScreenView* view)
: BaseScreen(LacrosDataMigrationScreenView::kScreenId,
OobeScreenPriority::SCREEN_DEVICE_DEVELOPER_MODIFICATION),
view_(view) {
view_(view),
attempt_restart_(base::BindRepeating(&chrome::AttemptRestart)) {
DCHECK(view_);
if (view_)
view_->Bind(this);
Expand Down Expand Up @@ -69,15 +72,16 @@ void LacrosDataMigrationScreen::ShowImpl() {
<< switches::kBrowserDataMigrationForUser
<< ". Aborting migration.";

chrome::AttemptRestart();
attempt_restart_.Run();
return;
}
DCHECK(!user_id_hash.empty()) << "user_id_hash should not be empty.";

base::FilePath user_data_dir;
if (!base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir)) {
LOG(ERROR) << "Could not get the original user data dir path. Aborting "
"migration.";
chrome::AttemptRestart();
attempt_restart_.Run();
return;
}

Expand All @@ -93,13 +97,11 @@ void LacrosDataMigrationScreen::ShowImpl() {
migrator_ = std::make_unique<BrowserDataMigratorImpl>(
profile_data_dir, user_id_hash, progress_callback,
g_browser_process->local_state());

migrator_->Migrate(base::BindOnce([](BrowserDataMigrator::Result result) {
// TODO(crbug.com/1296174): support page transition on failure.
chrome::AttemptRestart();
}));
}

migrator_->Migrate(base::BindOnce(&LacrosDataMigrationScreen::OnMigrated,
weak_factory_.GetWeakPtr()));

// Show the screen.
view_->Show();

Expand Down Expand Up @@ -136,11 +138,35 @@ void LacrosDataMigrationScreen::OnUserAction(const std::string& action_id) {
// which triggers Chrome to restart.
migrator_->Cancel();
}
} else if (action_id == kUserActionCancel) {
attempt_restart_.Run();
} else if (action_id == kUserActionGotoFiles) {
// TODO(crbug.com/1296174): Record a bit here so that Files.app will be
// opened in the following user session.
attempt_restart_.Run();
} else {
BaseScreen::OnUserAction(action_id);
}
}

void LacrosDataMigrationScreen::OnMigrated(BrowserDataMigrator::Result result) {
switch (result.kind) {
case BrowserDataMigrator::ResultKind::kSkipped:
case BrowserDataMigrator::ResultKind::kSucceeded:
case BrowserDataMigrator::ResultKind::kCancelled:
attempt_restart_.Run();
return;
case BrowserDataMigrator::ResultKind::kFailed:
if (view_) {
// Goto Files button should be displayed on migration failure caused by
// out of disk space.
const bool show_goto_files = result.required_size.has_value();
view_->SetFailureStatus(result.required_size, show_goto_files);
}
break;
}
}

void LacrosDataMigrationScreen::HideImpl() {
GetWakeLock()->CancelWakeLock();
power_manager_subscription_.Reset();
Expand Down Expand Up @@ -191,4 +217,10 @@ void LacrosDataMigrationScreen::SetMigratorForTesting(
migrator_ = std::move(migrator);
}

void LacrosDataMigrationScreen::SetAttemptRestartForTesting(
const base::RepeatingClosure& attempt_restart) {
DCHECK(!attempt_restart.is_null());
attempt_restart_ = attempt_restart;
}

} // namespace ash
Expand Up @@ -50,6 +50,11 @@ class LacrosDataMigrationScreen : public BaseScreen,
// PowerManagerClient::Observer:
void PowerChanged(const power_manager::PowerSupplyProperties& proto) override;

// Sets |attempt_restart_| for testing. This helps testing as it can block
// to restart.
void SetAttemptRestartForTesting(
const base::RepeatingClosure& attempt_restart);

private:
// BaseScreen:
void ShowImpl() override;
Expand All @@ -59,13 +64,17 @@ class LacrosDataMigrationScreen : public BaseScreen,
// Updates the low battery message.
void UpdateLowBatteryStatus();

// Called when migration is completed.
void OnMigrated(BrowserDataMigrator::Result result);

device::mojom::WakeLock* GetWakeLock();

mojo::Remote<device::mojom::WakeLock> wake_lock_;

LacrosDataMigrationScreenView* view_;
std::unique_ptr<BrowserDataMigrator> migrator_;
bool skip_post_show_button_for_testing_ = false;
base::RepeatingClosure attempt_restart_;

// PowerManagerClient::Observer is used only when screen is shown.
base::ScopedObservation<PowerManagerClient, PowerManagerClient::Observer>
Expand Down
Expand Up @@ -29,17 +29,33 @@ constexpr char kLacrosDataMigrationId[] = "lacros-data-migration";
const test::UIPath kSkipButton = {kLacrosDataMigrationId, "skipButton"};
const test::UIPath kUpdating = {kLacrosDataMigrationId, "updating"};
const test::UIPath kLowBattery = {kLacrosDataMigrationId, "lowBattery"};
const test::UIPath kProgressDialog = {kLacrosDataMigrationId, "progressDialog"};
const test::UIPath kErrorDialog = {kLacrosDataMigrationId, "errorDialog"};
const test::UIPath kLowDiskSpace = {kLacrosDataMigrationId,
"lowDiskSpaceError"};
const test::UIPath kGenericError = {kLacrosDataMigrationId, "genericError"};
const test::UIPath kCancelButton = {kLacrosDataMigrationId, "cancelButton"};
const test::UIPath kGotoFilesButton = {kLacrosDataMigrationId,
"gotoFilesButton"};

class FakeMigrator : public BrowserDataMigrator {
public:
// BrowserDataMigrator overrides.
void Migrate(MigrateCallback callback) override {}
void Migrate(MigrateCallback callback) override {
callback_ = std::move(callback);
}
void Cancel() override { cancel_called_ = true; }

bool IsCancelCalled() { return cancel_called_; }

void MaybeRunCallback(const BrowserDataMigrator::Result& result) {
if (!callback_.is_null())
std::move(callback_).Run(result);
}

private:
bool cancel_called_ = false;
MigrateCallback callback_;
};

class LacrosDataMigrationScreenTest : public OobeBaseTest {
Expand Down Expand Up @@ -70,22 +86,30 @@ class LacrosDataMigrationScreenTest : public OobeBaseTest {
fake_migrator_ = new FakeMigrator();
lacros_data_migration_screen->SetMigratorForTesting(
base::WrapUnique(fake_migrator_));
lacros_data_migration_screen->SetAttemptRestartForTesting(
base::BindRepeating(
&LacrosDataMigrationScreenTest::OnAttemptRestartCalled,
base::Unretained(this)));
lacros_data_migration_screen->SetSkipPostShowButtonForTesting(true);
OobeBaseTest::SetUpOnMainThread();
}

bool is_attempt_restart_called() const { return is_attempt_restart_called_; }

protected:
FakeMigrator* fake_migrator() { return fake_migrator_; }
FakePowerManagerClient* power_manager_client() {
return static_cast<FakePowerManagerClient*>(PowerManagerClient::Get());
}
void OnAttemptRestartCalled() { is_attempt_restart_called_ = true; }

private:
// This is owned by `LacrosDataMigrationScreen`.
FakeMigrator* fake_migrator_;
DeviceStateMixin device_state_{
&mixin_host_, DeviceStateMixin::State::OOBE_COMPLETED_CONSUMER_OWNED};
LoginManagerMixin login_mixin_{&mixin_host_};
bool is_attempt_restart_called_ = false;
};

IN_PROC_BROWSER_TEST_F(LacrosDataMigrationScreenTest, SkipButton) {
Expand Down Expand Up @@ -175,5 +199,82 @@ IN_PROC_BROWSER_TEST_F(LacrosDataMigrationScreenTest, LowBattery) {
test::OobeJS().CreateVisibilityWaiter(false, kLowBattery)->Wait();
}

IN_PROC_BROWSER_TEST_F(LacrosDataMigrationScreenTest, OutOfDiskError) {
OobeScreenWaiter waiter(LacrosDataMigrationScreenView::kScreenId);
WizardController::default_controller()->AdvanceToScreen(
LacrosDataMigrationScreenView::kScreenId);
waiter.Wait();

test::OobeJS().ExpectVisiblePath(kProgressDialog);
test::OobeJS().ExpectHiddenPath(kErrorDialog);

fake_migrator()->MaybeRunCallback(
{BrowserDataMigrator::ResultKind::kFailed, 12345});

test::OobeJS().CreateVisibilityWaiter(false, kProgressDialog)->Wait();
test::OobeJS().CreateVisibilityWaiter(true, kErrorDialog)->Wait();
test::OobeJS().ExpectVisiblePath(kLowDiskSpace);
test::OobeJS().ExpectHiddenPath(kGenericError);
test::OobeJS().ExpectVisiblePath(kCancelButton);
test::OobeJS().ExpectVisiblePath(kGotoFilesButton);
}

IN_PROC_BROWSER_TEST_F(LacrosDataMigrationScreenTest, GenericError) {
OobeScreenWaiter waiter(LacrosDataMigrationScreenView::kScreenId);
WizardController::default_controller()->AdvanceToScreen(
LacrosDataMigrationScreenView::kScreenId);
waiter.Wait();

test::OobeJS().ExpectVisiblePath(kProgressDialog);
test::OobeJS().ExpectHiddenPath(kErrorDialog);

fake_migrator()->MaybeRunCallback({BrowserDataMigrator::ResultKind::kFailed});

test::OobeJS().CreateVisibilityWaiter(false, kProgressDialog)->Wait();
test::OobeJS().CreateVisibilityWaiter(true, kErrorDialog)->Wait();
test::OobeJS().ExpectHiddenPath(kLowDiskSpace);
test::OobeJS().ExpectVisiblePath(kGenericError);
test::OobeJS().ExpectVisiblePath(kCancelButton);
test::OobeJS().ExpectHiddenPath(kGotoFilesButton);
}

IN_PROC_BROWSER_TEST_F(LacrosDataMigrationScreenTest, OnCancel) {
OobeScreenWaiter waiter(LacrosDataMigrationScreenView::kScreenId);
WizardController::default_controller()->AdvanceToScreen(
LacrosDataMigrationScreenView::kScreenId);
waiter.Wait();

fake_migrator()->MaybeRunCallback(
{BrowserDataMigrator::ResultKind::kFailed, 12345});
test::OobeJS().CreateVisibilityWaiter(true, kErrorDialog)->Wait();

EXPECT_FALSE(is_attempt_restart_called());
test::OobeJS().TapOnPath(kCancelButton);
test::TestPredicateWaiter(
base::BindRepeating(
&LacrosDataMigrationScreenTest::is_attempt_restart_called,
base::Unretained(this)))
.Wait();
}

IN_PROC_BROWSER_TEST_F(LacrosDataMigrationScreenTest, OnGotoFiles) {
OobeScreenWaiter waiter(LacrosDataMigrationScreenView::kScreenId);
WizardController::default_controller()->AdvanceToScreen(
LacrosDataMigrationScreenView::kScreenId);
waiter.Wait();

fake_migrator()->MaybeRunCallback(
{BrowserDataMigrator::ResultKind::kFailed, 12345});
test::OobeJS().CreateVisibilityWaiter(true, kErrorDialog)->Wait();

EXPECT_FALSE(is_attempt_restart_called());
test::OobeJS().TapOnPath(kGotoFilesButton);
test::TestPredicateWaiter(
base::BindRepeating(
&LacrosDataMigrationScreenTest::is_attempt_restart_called,
base::Unretained(this)))
.Wait();
}

} // namespace
} // namespace ash
Expand Up @@ -9,6 +9,7 @@
<link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">

<link rel="import" href="../../components/behaviors/login_screen_behavior.html">
<link rel="import" href="../../components/behaviors/multi_step_behavior.html">
<link rel="import" href="../../components/behaviors/oobe_dialog_host_behavior.html">
<link rel="import" href="../../components/behaviors/oobe_i18n_behavior.html">
<link rel="import" href="../../components/common_styles/oobe_dialog_host_styles.html">
Expand All @@ -34,7 +35,8 @@
width: 380px;
}
</style>
<oobe-adaptive-dialog id="dialog" role="dialog">
<oobe-adaptive-dialog id="progressDialog" for-step="progress" id="dialog"
role="dialog">
<iron-icon slot="icon" icon="oobe-32:googleg"></iron-icon>
<h1 slot="title">[[i18nDynamic(locale, 'lacrosDataMigrationTitle')]]</h1>
<paper-progress slot="progress" min="0" max="100"
Expand Down Expand Up @@ -75,6 +77,44 @@ <h1 slot="title">[[i18nDynamic(locale, 'lacrosDataMigrationTitle')]]</h1>
</oobe-text-button>
</div>
</oobe-adaptive-dialog>

<oobe-adaptive-dialog id="errorDialog" for-step="error" role="dialog"
footer-shrinkable>
<iron-icon slot="icon" icon="oobe-32:warning"></iron-icon>
<h1 slot="title">
[[i18nDynamic(locale, 'lacrosDataMigrationErrorTitle')]]
</h1>
<div slot="subtitle" id="lowDiskSpaceError"
hidden="[[!requiredSizeStr_]]">
<p>
[[i18nDynamic(locale, 'lacrosDataMigrationErrorLowDiskSpace',
requiredSizeStr_)]]
</p>
</div>
<div slot="subtitle" id="genericError" hidden="[[requiredSizeStr_]]">
<p>
[[i18nDynamic(locale, 'lacrosDataMigrationErrorSubtitle')]]
</p>
</div>
<div slot="content" class="flex layout vertical center center-justified">
<img src="/images/error.svg"
class="oobe-illustration" aria-hidden="true">
</div>

<!-- Cancel button -->
<div slot="bottom-buttons" class="layout horizontal end-justified">
<oobe-text-button id="cancelButton"
on-click="onCancelButtonClicked_"
text-key="lacrosDataMigrationErrorCancelButton">
</oobe-text-button>
<oobe-text-button id="gotoFilesButton"
on-click="onGotoFilesButtonClicked_"
text-key="lacrosDataMigrationErrorGotoFilesButton"
hidden="[[!showGotoFiles_]]"
inverse>
</oobe-text-button>
</div>
</oobe-adaptive-dialog>
</template>
<script src="lacros_data_migration.js"></script>
</dom-module>

0 comments on commit 515a55f

Please sign in to comment.