| // 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 "chromeos/ash/components/early_prefs/early_prefs_reader.h" |
| |
| #include <memory> |
| #include <optional> |
| #include <utility> |
| |
| #include "base/files/file_path.h" |
| #include "base/functional/bind.h" |
| #include "base/json/json_file_value_serializer.h" |
| #include "base/logging.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "base/values.h" |
| #include "chromeos/ash/components/early_prefs/early_prefs_constants.h" |
| |
| namespace ash { |
| namespace { |
| |
| constexpr int kCurrentSchemaVersion = 1; |
| |
| std::unique_ptr<base::Value> ReadFileFromDisk(const base::FilePath& data_file) { |
| std::unique_ptr<base::Value> result; |
| |
| int error_code; |
| std::string error_msg; |
| JSONFileValueDeserializer deserializer(data_file); |
| result = deserializer.Deserialize(&error_code, &error_msg); |
| if (!result) { |
| if (error_code == JSONFileValueDeserializer::JSON_NO_SUCH_FILE) { |
| // Somewhat expected situation, do not log. |
| return nullptr; |
| } |
| LOG(ERROR) << "Error while loading early prefs file: " << error_msg; |
| } |
| return result; |
| } |
| |
| } // namespace |
| |
| EarlyPrefsReader::EarlyPrefsReader( |
| const base::FilePath& data_dir, |
| scoped_refptr<base::SequencedTaskRunner> file_task_runner) |
| : file_task_runner_(std::move(file_task_runner)) { |
| data_file_ = data_dir.Append(early_prefs::kEarlyPrefsFileName); |
| } |
| |
| EarlyPrefsReader::~EarlyPrefsReader() = default; |
| |
| void EarlyPrefsReader::ReadFile(ResultCallback result_callback) { |
| CHECK(!data_); |
| file_task_runner_->PostTaskAndReplyWithResult( |
| FROM_HERE, base::BindOnce(&ReadFileFromDisk, data_file_), |
| base::BindOnce(&EarlyPrefsReader::OnFileRead, weak_factory_.GetWeakPtr(), |
| std::move(result_callback))); |
| } |
| |
| void EarlyPrefsReader::OnFileRead(ResultCallback callback, |
| std::unique_ptr<base::Value> root) { |
| if (!root) { |
| std::move(callback).Run(false); |
| return; |
| } |
| if (!ValidateData(root->GetIfDict())) { |
| std::move(callback).Run(false); |
| return; |
| } |
| root_ = std::move(root); |
| data_ = root_->GetDict().FindDict(early_prefs::kDataKey); |
| CHECK(data_); |
| std::move(callback).Run(true); |
| } |
| |
| bool EarlyPrefsReader::ValidateData(const base::Value::Dict* root) const { |
| if (!root) { |
| return false; |
| } |
| if (root->size() != 2) { |
| return false; |
| } |
| auto schema = root->FindInt(early_prefs::kSchemaKey); |
| if (!schema || schema.value() != kCurrentSchemaVersion) { |
| return false; |
| } |
| auto* data = root->FindDict(early_prefs::kDataKey); |
| if (!data) { |
| return false; |
| } |
| for (auto pref : *data) { |
| if (!ValidatePref(pref.second)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool EarlyPrefsReader::ValidatePref(const base::Value& pref) const { |
| if (!pref.is_dict()) { |
| return false; |
| } |
| const auto& dict = pref.GetDict(); |
| auto is_managed = dict.FindBool(early_prefs::kPrefIsManagedKey); |
| if (!is_managed) { |
| return false; |
| } |
| if (is_managed.value()) { |
| auto is_recommended = dict.FindBool(early_prefs::kPrefIsRecommendedKey); |
| if (!is_recommended) { |
| return false; |
| } |
| } |
| if (!dict.Find(early_prefs::kPrefValueKey)) { |
| return false; |
| } |
| return true; |
| } |
| |
| bool EarlyPrefsReader::HasPref(const std::string& key) const { |
| if (!data_) { |
| return false; |
| } |
| return data_->Find(key) != nullptr; |
| } |
| |
| bool EarlyPrefsReader::IsManaged(const std::string& key) const { |
| CHECK(HasPref(key)); |
| return data_->FindDict(key)->FindBool(early_prefs::kPrefIsManagedKey).value(); |
| } |
| |
| bool EarlyPrefsReader::IsRecommended(const std::string& key) const { |
| CHECK(IsManaged(key)); |
| return data_->FindDict(key) |
| ->FindBool(early_prefs::kPrefIsRecommendedKey) |
| .value(); |
| } |
| |
| const base::Value* EarlyPrefsReader::GetValue(const std::string& key) const { |
| CHECK(HasPref(key)); |
| return data_->FindDict(key)->Find(early_prefs::kPrefValueKey); |
| } |
| |
| } // namespace ash |