// 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/ash/file_manager/empty_trash_io_task.h"
#include <algorithm>
#include <memory>
#include <utility>
#include "base/check.h"
#include "base/files/file.h"
#include "base/files/file_util.h"
#include "base/functional/callback.h"
#include "base/task/bind_post_task.h"
#include "base/task/sequenced_task_runner.h"
#include "chrome/browser/ash/file_manager/io_task_util.h"
#include "chrome/browser/ash/file_manager/trash_common_util.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
namespace file_manager::io_task {
EmptyTrashIOTask::EmptyTrashIOTask(
blink::StorageKey storage_key,
Profile* profile,
scoped_refptr<storage::FileSystemContext> file_system_context,
base::FilePath base_path,
bool show_notification)
: IOTask(show_notification),
file_system_context_(std::move(file_system_context)),
storage_key_(std::move(storage_key)),
profile_(profile),
base_path_(std::move(base_path)) {
progress_.state = State::kQueued;
progress_.type = OperationType::kEmptyTrash;
}
EmptyTrashIOTask::~EmptyTrashIOTask() {
LOG_IF(WARNING, in_flight_ > 0)
<< "An EmptyTrashIOTask is getting deleted although it still has "
<< in_flight_ << " ongoing deletion operations in progress";
}
void EmptyTrashIOTask::Execute(IOTask::ProgressCallback /*progress_callback*/,
IOTask::CompleteCallback complete_callback) {
DCHECK(!complete_callback_);
complete_callback_ = std::move(complete_callback);
// A map containing paths which are enabled for trashing.
const trash::TrashPathsMap locations =
trash::GenerateEnabledTrashLocationsForProfile(profile_, base_path_);
if (locations.empty()) {
progress_.state = State::kSuccess;
Complete();
return;
}
DCHECK_EQ(in_flight_, 0);
progress_.state = State::kInProgress;
for (const trash::TrashPathsMap::value_type& location : locations) {
base::FilePath dir =
location.first.Append(location.second.relative_folder_path);
const EntryStatus& entry = progress_.outputs.emplace_back(
file_system_context_->CreateCrackedFileSystemURL(
storage_key_, storage::FileSystemType::kFileSystemTypeLocal, dir),
std::nullopt);
++in_flight_;
VLOG(1) << "Removing " << entry.url.path();
// Double-check the path to delete.
CHECK(dir.IsAbsolute()) << " for " << dir;
CHECK(!dir.ReferencesParent()) << " for " << dir;
CHECK(dir.BaseName().value().starts_with(trash::kTrashFolderName))
<< " for " << dir;
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::MayBlock()},
base::BindOnce(&base::DeletePathRecursively, std::move(dir)),
base::BindOnce(&EmptyTrashIOTask::OnRemoved,
weak_ptr_factory_.GetWeakPtr(),
progress_.outputs.size() - 1));
}
}
void EmptyTrashIOTask::OnRemoved(const size_t i, const bool ok) {
DCHECK_LT(i, progress_.outputs.size());
if (EntryStatus& entry = progress_.outputs[i]; ok) {
VLOG(1) << "Removed " << entry.url.path();
entry.error = base::File::FILE_OK;
} else {
LOG(ERROR) << "Cannot remove " << entry.url.path();
entry.error = base::File::FILE_ERROR_FAILED;
}
DCHECK_GT(in_flight_, 0);
if (--in_flight_ > 0) {
// Still waiting for some deletion tasks to finish.
return;
}
// All the deletion tasks have finished.
if (progress_.state != State::kCancelled) {
// If there was no error, then it is a success.
progress_.state =
std::all_of(progress_.outputs.cbegin(), progress_.outputs.cend(),
[](const EntryStatus& entry) {
return entry.error == base::File::FILE_OK;
})
? State::kSuccess
: State::kError;
}
LOG_IF(ERROR, progress_.state != State::kSuccess)
<< "Cannot empty the trash bin: " << progress_.state;
Complete();
}
void EmptyTrashIOTask::Complete() {
DCHECK(complete_callback_);
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(std::move(complete_callback_), std::move(progress_)));
}
void EmptyTrashIOTask::Cancel() {
LOG_IF(WARNING, progress_.state == State::kInProgress)
<< "Cannot cancel the " << in_flight_
<< " operations that are currently emptying the trash bin";
progress_.state = State::kCancelled;
}
} // namespace file_manager::io_task