blob: 5e0531606d01758663c4e35edf2370c55ffe2ae1 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/download/model/auto_deletion/scheduler.h"
#import "base/json/values_util.h"
#import "base/time/time.h"
#import "base/values.h"
#import "components/prefs/pref_service.h"
#import "components/prefs/scoped_user_pref_update.h"
#import "ios/chrome/browser/download/model/auto_deletion/scheduled_file.h"
#import "ios/chrome/browser/shared/model/prefs/pref_names.h"
#import "ios/chrome/browser/shared/public/features/features.h"
namespace {
// Threshold for deleting files.
constexpr base::TimeDelta kFileDeletionThreshold = base::Days(30);
// Creates a ScheduledFile from the given base::Value.
std::optional<auto_deletion::ScheduledFile> ScheduledFileFromValue(
const base::Value& value) {
if (!value.is_dict()) {
return std::nullopt;
}
const base::Value::Dict& dict = value.GetDict();
const base::Value* time_value = dict.Find("time");
if (!time_value) {
return std::nullopt;
}
const std::optional<base::Time> maybe_time = base::ValueToTime(*time_value);
if (!maybe_time) {
return std::nullopt;
}
const std::string* maybe_hash = dict.FindString("hash");
if (!maybe_hash) {
return std::nullopt;
}
const std::string* maybe_path = dict.FindString("path");
if (!maybe_path) {
return std::nullopt;
}
return auto_deletion::ScheduledFile(base::FilePath(*maybe_path), *maybe_hash,
*maybe_time);
}
} // namespace
namespace auto_deletion {
Scheduler::Scheduler(PrefService* local_state) : local_state_(local_state) {
DCHECK(local_state_);
}
Scheduler::~Scheduler() = default;
std::vector<ScheduledFile> Scheduler::IdentifyExpiredFiles(base::Time instant) {
std::vector<ScheduledFile> expired_files;
const base::Value::List& files =
local_state_->GetList(prefs::kDownloadAutoDeletionScheduledFiles);
for (const auto& value : files) {
std::optional<ScheduledFile> maybe_file = ScheduledFileFromValue(value);
if (!maybe_file) {
continue;
}
if (!IsFileReadyForDeletion(instant, *maybe_file)) {
break;
}
expired_files.push_back(std::move(*maybe_file));
}
return expired_files;
}
void Scheduler::RemoveExpiredFiles(base::Time instant) {
ScopedListPrefUpdate update(local_state_,
prefs::kDownloadAutoDeletionScheduledFiles);
unsigned int values_to_erase_count = 0;
for (const auto& value : update.Get()) {
std::optional<ScheduledFile> maybe_file = ScheduledFileFromValue(value);
if (!maybe_file) {
++values_to_erase_count;
continue;
}
if (!IsFileReadyForDeletion(instant, *maybe_file)) {
break;
}
++values_to_erase_count;
}
if (values_to_erase_count > 0) {
base::Value::List& value = update.Get();
value.erase(value.begin(), value.begin() + values_to_erase_count);
}
}
void Scheduler::ScheduleFile(ScheduledFile file) {
ScopedListPrefUpdate update(local_state_,
prefs::kDownloadAutoDeletionScheduledFiles);
update->Append(base::Value::Dict()
.Set("path", file.filepath().AsUTF8Unsafe())
.Set("hash", file.hash())
.Set("time", base::TimeToValue(file.download_time())));
}
void Scheduler::Clear() {
local_state_->ClearPref(prefs::kDownloadAutoDeletionScheduledFiles);
}
bool Scheduler::IsFileReadyForDeletion(base::Time instant,
const ScheduledFile& file) {
if (isDownloadAutoDeletionTestingFeatureEnabled()) {
return true;
}
const base::Time download_date = file.download_time();
const base::TimeDelta download_age = instant - download_date;
return download_age > kFileDeletionThreshold;
}
} // namespace auto_deletion