blob: ad50b97f0aa5d174be0e746f0934cdee915454c9 [file] [log] [blame]
// Copyright 2022 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/web_applications/commands/update_file_handler_command.h"
#include <memory>
#include <utility>
#include "base/barrier_callback.h"
#include "base/functional/callback.h"
#include "base/memory/ptr_util.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/web_applications/locks/app_lock.h"
#include "chrome/browser/web_applications/os_integration/os_integration_manager.h"
#include "chrome/browser/web_applications/web_app_constants.h"
#include "chrome/browser/web_applications/web_app_id.h"
#include "chrome/browser/web_applications/web_app_registrar.h"
#include "chrome/browser/web_applications/web_app_sync_bridge.h"
namespace web_app {
namespace {
base::RepeatingCallback<void(Result)> CreateBarrierForSynchronizeWithResult(
base::OnceCallback<void(Result)> final_callback) {
return base::BarrierCallback<Result>(
/*num_callbacks=*/2, base::BindOnce(
[](base::OnceCallback<void(Result)> callback,
std::vector<Result> combined_results) {
DCHECK_EQ(2u, combined_results.size());
Result final_result = Result::kOk;
if (combined_results[0] == Result::kError ||
combined_results[1] == Result::kError) {
final_result = Result::kError;
}
std::move(callback).Run(final_result);
},
std::move(final_callback)));
}
} // namespace
// static
std::unique_ptr<UpdateFileHandlerCommand>
UpdateFileHandlerCommand::CreateForPersistUserChoice(
const AppId& app_id,
bool allowed,
base::OnceClosure callback) {
return base::WrapUnique(
new UpdateFileHandlerCommand(app_id, allowed, std::move(callback)));
}
// static
std::unique_ptr<UpdateFileHandlerCommand>
UpdateFileHandlerCommand::CreateForUpdate(const AppId& app_id,
base::OnceClosure callback) {
return base::WrapUnique(new UpdateFileHandlerCommand(
app_id, /*user_choice_to_remember=*/absl::nullopt, std::move(callback)));
}
UpdateFileHandlerCommand::UpdateFileHandlerCommand(
const AppId& app_id,
absl::optional<bool> user_choice_to_remember,
base::OnceClosure callback)
: WebAppCommandTemplate<AppLock>("UpdateFileHandlerCommand"),
lock_description_(
std::make_unique<AppLockDescription, base::flat_set<AppId>>(
{app_id})),
app_id_(app_id),
user_choice_to_remember_(std::move(user_choice_to_remember)),
callback_(std::move(callback)) {
debug_info_.Set("app_id", app_id_);
if (user_choice_to_remember_)
debug_info_.Set("user_choice_to_remember",
user_choice_to_remember_.value() ? "allow" : "disallow");
}
UpdateFileHandlerCommand::~UpdateFileHandlerCommand() = default;
void UpdateFileHandlerCommand::StartWithLock(std::unique_ptr<AppLock> lock) {
debug_info_.Set("was_update_required", false);
lock_ = std::move(lock);
if (!lock_->registrar().IsLocallyInstalled(app_id_)) {
ReportResultAndDestroy(CommandResult::kFailure);
return;
}
if (user_choice_to_remember_) {
lock_->sync_bridge().SetAppFileHandlerApprovalState(
app_id_, user_choice_to_remember_.value()
? ApiApprovalState::kAllowed
: ApiApprovalState::kDisallowed);
}
// File handling could have been disabled via origin trial as well as user
// choice, so check both here.
bool file_handling_enabled =
lock_->os_integration_manager().IsFileHandlingAPIAvailable(app_id_) &&
!lock_->registrar().IsAppFileHandlerPermissionBlocked(app_id_);
// This checks whether the current enabled state matches what we expect
// to be registered with the OS. If so, no need to do any update.
if (file_handling_enabled ==
lock_->registrar().ExpectThatFileHandlersAreRegisteredWithOs(app_id_)) {
ReportResultAndDestroy(CommandResult::kSuccess);
return;
}
FileHandlerUpdateAction action = file_handling_enabled
? FileHandlerUpdateAction::kUpdate
: FileHandlerUpdateAction::kRemove;
debug_info_.Set("was_update_required", true);
auto callback_for_synchronize = CreateBarrierForSynchronizeWithResult(
base::BindOnce(&UpdateFileHandlerCommand::OnFileHandlerUpdated,
weak_factory_.GetWeakPtr(), file_handling_enabled));
#if BUILDFLAG(IS_MAC)
// On Mac, the file handlers are encoded in the app shortcut. First
// unregister the file handlers (verifying that it finishes
// synchronously), then update the shortcut.
Result unregister_file_handlers_result = Result::kError;
lock_->os_integration_manager().UpdateFileHandlers(
app_id_, action,
base::BindOnce([](Result* result_out,
Result actual_result) { *result_out = actual_result; },
&unregister_file_handlers_result));
DCHECK_EQ(Result::kOk, unregister_file_handlers_result);
lock_->os_integration_manager().Synchronize(
app_id_, base::BindOnce(callback_for_synchronize, Result::kOk));
// If we're enabling file handling, yet this app does not have any file
// handlers there is no need to update the shortcut, as doing so would be a
// no-op anyway.
const apps::FileHandlers* handlers =
lock_->registrar().GetAppFileHandlers(app_id_);
if (file_handling_enabled && (!handlers || handlers->empty())) {
callback_for_synchronize.Run(Result::kOk);
} else {
// TODO(crbug.com/1401125): Remove UpdateFileHandlers() and
// UpdateShortcuts() once OS integration sub managers have been implemented.
lock_->os_integration_manager().UpdateShortcuts(app_id_, /*old_name=*/{},
callback_for_synchronize);
}
#else
lock_->os_integration_manager().UpdateFileHandlers(app_id_, action,
callback_for_synchronize);
lock_->os_integration_manager().Synchronize(
app_id_, base::BindOnce(callback_for_synchronize, Result::kOk));
#endif
}
LockDescription& UpdateFileHandlerCommand::lock_description() const {
return *lock_description_;
}
base::Value UpdateFileHandlerCommand::ToDebugValue() const {
return base::Value(debug_info_.Clone());
}
void UpdateFileHandlerCommand::OnFileHandlerUpdated(bool file_handling_enabled,
Result result) {
DCHECK_EQ(
file_handling_enabled,
lock_->registrar().ExpectThatFileHandlersAreRegisteredWithOs(app_id_));
ReportResultAndDestroy(result == Result::kOk ? CommandResult::kSuccess
: CommandResult::kFailure);
}
void UpdateFileHandlerCommand::ReportResultAndDestroy(CommandResult result) {
DCHECK(!callback_.is_null());
SignalCompletionAndSelfDestruct(result, base::BindOnce(std::move(callback_)));
}
void UpdateFileHandlerCommand::OnShutdown() {
ReportResultAndDestroy(CommandResult::kShutdown);
}
} // namespace web_app