blob: 3fba05c8ed4d6d719aa884666bec5996c674c692 [file] [log] [blame]
// Copyright 2022 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/web_applications/commands/sub_app_install_command.h"
#include "chrome/browser/web_applications/web_app_helpers.h"
static blink::mojom::SubAppsServiceAddResultCode InstallResultCodeToMojo(
webapps::InstallResultCode install_result_code) {
blink::mojom::SubAppsServiceAddResultCode mojom_install_result_code;
switch (install_result_code) {
case webapps::InstallResultCode::kSuccessNewInstall:
mojom_install_result_code =
blink::mojom::SubAppsServiceAddResultCode::kSuccessNewInstall;
break;
case webapps::InstallResultCode::kSuccessAlreadyInstalled:
mojom_install_result_code =
blink::mojom::SubAppsServiceAddResultCode::kSuccessAlreadyInstalled;
break;
case webapps::InstallResultCode::kUserInstallDeclined:
mojom_install_result_code =
blink::mojom::SubAppsServiceAddResultCode::kUserInstallDeclined;
break;
case webapps::InstallResultCode::kExpectedAppIdCheckFailed:
mojom_install_result_code =
blink::mojom::SubAppsServiceAddResultCode::kExpectedAppIdCheckFailed;
break;
default:
mojom_install_result_code =
blink::mojom::SubAppsServiceAddResultCode::kFailure;
break;
}
return mojom_install_result_code;
}
void SubAppInstallCommand::OnSyncSourceRemoved() {}
void SubAppInstallCommand::OnShutdown() {}
base::Value SubAppInstallCommand::ToDebugValue() const {
return base::Value("SubAppInstallCommand");
}
SubAppInstallCommand::SubAppInstallCommand(
web_app::WebAppInstallManager* install_manager,
web_app::WebAppRegistrar* registrar,
web_app::AppId& parent_app_id,
std::vector<std::pair<web_app::UnhashedAppId, GURL>> sub_apps,
base::flat_set<web_app::AppId> app_ids_for_lock,
base::OnceCallback<void(AppInstallResults)> callback)
: WebAppCommand{web_app::WebAppCommandLock::CreateForAppLock(
app_ids_for_lock)},
install_manager_{install_manager},
registrar_{registrar},
requested_installs_{std::move(sub_apps)},
parent_app_id_{parent_app_id},
install_callback_{std::move(callback)} {}
SubAppInstallCommand::~SubAppInstallCommand() = default;
void SubAppInstallCommand::Start() {
DCHECK(state_ == State::kNotStarted);
// Check if parent app is installed.
if (!registrar_->IsInstalled(parent_app_id_)) {
// Add failure reason to each app
base::ranges::transform(
requested_installs_, std::inserter(results_, results_.begin()),
[](auto const& pair) {
return std::pair{
pair.first,
blink::mojom::SubAppsServiceAddResultCode::kParentAppUninstalled};
});
SignalCompletionAndSelfDestruct(
web_app::CommandResult::kFailure,
base::BindOnce(std::move(install_callback_), results_));
return;
}
if (requested_installs_.empty()) {
SignalCompletionAndSelfDestruct(
web_app::CommandResult::kSuccess,
base::BindOnce(std::move(install_callback_), results_));
return;
}
// Populate pending_installs_ from requested_installs_
base::ranges::transform(
requested_installs_,
std::inserter(pending_installs_, pending_installs_.begin()),
[](auto const& pair) { return pair.first; });
num_pending_dialog_callbacks_ = pending_installs_.size();
state_ = State::kPendingDialogCallbacks;
StartNextInstall();
}
void SubAppInstallCommand::StartNextInstall() {
DCHECK(!requested_installs_.empty());
std::pair<web_app::UnhashedAppId, GURL> install_info =
std::move(requested_installs_.back());
const web_app::UnhashedAppId& unhashed_app_id = install_info.first;
GURL install_url = install_info.second;
requested_installs_.pop_back();
// TODO(https://crbug.com/1327963): Update to use WebAppCommand version of
// WebAppInstallManager::InstallSubApp once implemented.
install_manager_->InstallSubApp(
parent_app_id_, install_url,
web_app::GenerateAppIdFromUnhashed(unhashed_app_id),
base::BindOnce(&SubAppInstallCommand::OnDialogRequested,
weak_ptr_factory_.GetWeakPtr(), unhashed_app_id),
base::BindOnce(&SubAppInstallCommand::OnInstalled,
weak_ptr_factory_.GetWeakPtr(), unhashed_app_id));
}
void SubAppInstallCommand::OnDialogRequested(
const web_app::UnhashedAppId& unhashed_app_id,
content::WebContents* initiator_web_contents,
std::unique_ptr<WebAppInstallInfo> web_app_info,
web_app::WebAppInstallationAcceptanceCallback acceptance_callback) {
acceptance_callbacks_.emplace_back(unhashed_app_id, std::move(web_app_info),
std::move(acceptance_callback));
num_pending_dialog_callbacks_--;
DCHECK_GE(num_pending_dialog_callbacks_, 0u);
MaybeShowDialog();
}
void SubAppInstallCommand::MaybeShowDialog() {
if (num_pending_dialog_callbacks_ > 0) {
DCHECK(!requested_installs_.empty());
StartNextInstall();
return;
}
if (acceptance_callbacks_.empty()) {
SignalCompletionAndSelfDestruct(
web_app::CommandResult::kFailure,
base::BindOnce(std::move(install_callback_), results_));
return;
}
state_ = State::kPendingInstallComplete;
// TODO(https://crbug.com/1313109): Replace the placeholder blanket user
// acceptance below with a permissions dialog shown to the user.
for (auto& [unhashed_app_id, web_app_info, acceptance_callback] :
acceptance_callbacks_) {
std::move(acceptance_callback).Run(true, std::move(web_app_info));
}
acceptance_callbacks_.clear();
}
void SubAppInstallCommand::OnInstalled(
const web_app::UnhashedAppId& unhashed_app_id,
const web_app::AppId& app_id,
webapps::InstallResultCode result) {
AddResultAndRemoveFromPendingInstalls(unhashed_app_id, result);
// In case an installation returns with a failure before running the dialog
// callback.
if (state_ == State::kPendingDialogCallbacks) {
num_pending_dialog_callbacks_--;
MaybeShowDialog();
return;
}
DCHECK_GE(pending_installs_.size(), 0u);
MaybeFinishCommand();
}
void SubAppInstallCommand::MaybeFinishCommand() {
if (pending_installs_.size() > 0) {
return;
}
SignalCompletionAndSelfDestruct(
web_app::CommandResult::kSuccess,
base::BindOnce(std::move(install_callback_), results_));
}
void SubAppInstallCommand::AddResultAndRemoveFromPendingInstalls(
const web_app::UnhashedAppId& unhashed_app_id,
webapps::InstallResultCode result) {
std::pair result_pair(unhashed_app_id, InstallResultCodeToMojo(result));
results_.emplace_back(result_pair);
pending_installs_.erase(unhashed_app_id);
}