blob: 615824351e7d376becb00e7df4330be58fff1fd1 [file] [log] [blame]
// 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/download/download_status_updater.h"
#include <memory>
#include <string>
#include "base/containers/contains.h"
#include "base/notreached.h"
#include "chrome/browser/download/bubble/download_bubble_prefs.h"
#include "chrome/browser/download/download_commands.h"
#include "chrome/browser/download/download_item_model.h"
#include "chrome/browser/download/download_ui_model.h"
#include "chrome/browser/profiles/keep_alive/scoped_profile_keep_alive.h"
#include "chrome/browser/profiles/profile.h"
#include "chromeos/crosapi/mojom/download_controller.mojom.h"
#include "chromeos/crosapi/mojom/download_status_updater.mojom.h"
#include "chromeos/lacros/lacros_service.h"
#include "components/download/public/common/download_item_utils.h"
#include "content/public/browser/download_item_utils.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace {
// Helpers ---------------------------------------------------------------------
crosapi::mojom::DownloadStatusUpdater* GetRemote(
absl::optional<uint32_t> min_version = absl::nullopt) {
using DownloadStatusUpdater = crosapi::mojom::DownloadStatusUpdater;
auto* service = chromeos::LacrosService::Get();
if (!service || !service->IsAvailable<DownloadStatusUpdater>()) {
return nullptr;
}
// NOTE: Use `remote.version()` rather than `service->GetInterfaceVersion()`
// as the latter does not respect versions of remotes injected for testing.
auto& remote = service->GetRemote<DownloadStatusUpdater>();
return remote.version() >= min_version.value_or(remote.version())
? remote.get()
: nullptr;
}
bool IsCommandEnabled(DownloadItemModel& model,
DownloadCommands::Command command) {
// To support other commands, we may need to update checks below to also
// inspect `BubbleUIInfo` subpage buttons.
CHECK(command == DownloadCommands::CANCEL ||
command == DownloadCommands::PAUSE ||
command == DownloadCommands::RESUME);
const bool is_download_bubble_v2_enabled =
download::IsDownloadBubbleV2Enabled(Profile::FromBrowserContext(
content::DownloadItemUtils::GetBrowserContext(
model.GetDownloadItem())));
// `BubbleUIInfo` contains at most one of either `CANCEL`, `PAUSE`, or
// `RESUME` when download bubble v2 is disabled, despite the fact that a
// download may be simultaneously cancellable and pausable/resumable. For
// this reason, do not use `BubbleUIInfo`-based determination of command
// enablement when download bubble v2 is disabled.
if (!is_download_bubble_v2_enabled) {
DownloadCommands commands(model.GetWeakPtr());
return model.IsCommandEnabled(&commands, command);
}
const DownloadUIModel::BubbleUIInfo info =
model.GetBubbleUIInfo(/*is_download_bubble_v2_enabled=*/true);
// A command is enabled if `BubbleUIInfo` contains a quick action for it. This
// is preferred over non-`BubbleUIInfo`-based determination of command
// enablement as it takes more signals into account, e.g. if the download has
// been marked dangerous.
return base::Contains(info.quick_actions, command,
&DownloadUIModel::BubbleUIInfo::QuickAction::command);
}
crosapi::mojom::DownloadStatusPtr ConvertToMojoDownloadStatus(
download::DownloadItem* download) {
DownloadItemModel model(download);
auto status = crosapi::mojom::DownloadStatus::New();
status->guid = download->GetGuid();
status->state = download::download_item_utils::ConvertToMojoDownloadState(
download->GetState());
status->received_bytes = download->GetReceivedBytes();
status->total_bytes = download->GetTotalBytes();
status->target_file_path = download->GetTargetFilePath();
status->cancellable = IsCommandEnabled(model, DownloadCommands::CANCEL);
status->pausable = IsCommandEnabled(model, DownloadCommands::PAUSE);
status->resumable = IsCommandEnabled(model, DownloadCommands::RESUME);
return status;
}
} // namespace
// DownloadStatusUpdater::Delegate ---------------------------------------------
// The delegate of the `DownloadStatusUpdater` in Lacros Chrome which serves as
// the client for the `DownloadStatusUpdater` in Ash Chrome.
class DownloadStatusUpdater::Delegate
: public crosapi::mojom::DownloadStatusUpdaterClient {
public:
Delegate() {
using crosapi::mojom::DownloadStatusUpdater;
if (auto* remote =
GetRemote(DownloadStatusUpdater::kBindClientMinVersion)) {
remote->BindClient(receiver_.BindNewPipeAndPassRemoteWithVersion());
}
}
Delegate(const Delegate&) = delete;
Delegate& operator=(const Delegate&) = delete;
~Delegate() override = default;
private:
// crosapi::mojom::DownloadStatusUpdaterClient:
void Cancel(const std::string& guid, CancelCallback callback) override {
// TODO(http://b/279794441): Implement.
NOTIMPLEMENTED();
std::move(callback).Run(/*handled=*/false);
}
void Pause(const std::string& guid, PauseCallback callback) override {
// TODO(http://b/279794441): Implement.
NOTIMPLEMENTED();
std::move(callback).Run(/*handled=*/false);
}
void Resume(const std::string& guid, ResumeCallback callback) override {
// TODO(http://b/279794441): Implement.
NOTIMPLEMENTED();
std::move(callback).Run(/*handled=*/false);
}
void ShowInBrowser(const std::string& guid,
ShowInBrowserCallback callback) override {
// TODO(http://b/279794441): Implement.
NOTIMPLEMENTED();
std::move(callback).Run(/*handled=*/false);
}
// The receiver bound to `this` for use by crosapi.
mojo::Receiver<crosapi::mojom::DownloadStatusUpdaterClient> receiver_{this};
};
// DownloadStatusUpdater -------------------------------------------------------
DownloadStatusUpdater::DownloadStatusUpdater()
: delegate_(std::make_unique<Delegate>()) {}
DownloadStatusUpdater::~DownloadStatusUpdater() = default;
void DownloadStatusUpdater::UpdateAppIconDownloadProgress(
download::DownloadItem* download) {
if (auto* remote = GetRemote()) {
remote->Update(ConvertToMojoDownloadStatus(download));
}
}