blob: df0dc367756e56cd850abb44779ab035acc65b78 [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "extensions/browser/json_file_sanitizer.h"
#include <optional>
#include <string>
#include <utility>
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/task/sequenced_task_runner.h"
#include "base/types/expected.h"
#include "base/values.h"
#include "extensions/browser/extension_file_task_runner.h"
namespace extensions {
// static
std::unique_ptr<JsonFileSanitizer> JsonFileSanitizer::CreateAndStart(
const std::set<base::FilePath>& file_paths,
Callback callback,
const scoped_refptr<base::SequencedTaskRunner>& io_task_runner) {
// Note we can't use std::make_unique as we want to keep the constructor
// private.
std::unique_ptr<JsonFileSanitizer> sanitizer(
new JsonFileSanitizer(std::move(callback), io_task_runner));
sanitizer->Start(file_paths);
return sanitizer;
}
JsonFileSanitizer::JsonFileSanitizer(
Callback callback,
const scoped_refptr<base::SequencedTaskRunner>& io_task_runner)
: callback_(std::move(callback)), io_task_runner_(io_task_runner) {}
JsonFileSanitizer::~JsonFileSanitizer() = default;
void JsonFileSanitizer::Start(const std::set<base::FilePath>& file_paths) {
if (file_paths.empty()) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(&JsonFileSanitizer::ReportSuccess,
weak_factory_.GetWeakPtr()));
return;
}
remaining_callbacks_ = file_paths.size();
for (const base::FilePath& path : file_paths) {
io_task_runner_->PostTaskAndReplyWithResult(
FROM_HERE, base::BindOnce(&JsonFileSanitizer::ProcessFile, path),
base::BindOnce(&JsonFileSanitizer::OnProcessedFile,
weak_factory_.GetWeakPtr()));
}
}
base::expected<void, JsonFileSanitizer::Error> JsonFileSanitizer::ProcessFile(
const base::FilePath& path) {
std::string contents;
bool read_success = base::ReadFileToString(path, &contents);
bool delete_success = base::DeleteFile(path);
if (!read_success) {
return base::unexpected(Error::kFileReadError);
}
if (!delete_success) {
return base::unexpected(Error::kFileDeleteError);
}
std::optional<base::Value> result = base::JSONReader::Read(contents);
if (!result.has_value() || !result->is_dict()) {
return base::unexpected(Error::kDecodingError);
}
// Reserialize the JSON and write it back to the original file.
std::optional<std::string> json_string = base::WriteJsonWithOptions(
*result, base::JSONWriter::OPTIONS_PRETTY_PRINT);
if (!json_string) {
return base::unexpected(Error::kSerializingError);
}
if (!base::WriteFile(path, *json_string)) {
return base::unexpected(Error::kFileWriteError);
}
return base::ok();
}
void JsonFileSanitizer::OnProcessedFile(base::expected<void, Error> result) {
if (result.has_value()) {
if (--remaining_callbacks_ == 0) {
ReportSuccess();
}
} else {
ReportError(result.error());
}
}
void JsonFileSanitizer::ReportSuccess() {
std::move(callback_).Run(base::ok());
}
void JsonFileSanitizer::ReportError(Error error) {
// Prevent any other task from reporting, we want to notify only once.
weak_factory_.InvalidateWeakPtrs();
std::move(callback_).Run(base::unexpected(error));
}
} // namespace extensions