Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(332)

Unified Diff: components/filesystem/public/cpp/prefs/filesystem_json_pref_store.cc

Issue 1624683002: mash: Add a simple, temporary preferences store. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: sky comments Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: components/filesystem/public/cpp/prefs/filesystem_json_pref_store.cc
diff --git a/components/filesystem/public/cpp/prefs/filesystem_json_pref_store.cc b/components/filesystem/public/cpp/prefs/filesystem_json_pref_store.cc
new file mode 100644
index 0000000000000000000000000000000000000000..ffb93b70079ea76be084d6d5bc83ce720fcdc3cf
--- /dev/null
+++ b/components/filesystem/public/cpp/prefs/filesystem_json_pref_store.cc
@@ -0,0 +1,452 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/filesystem/public/cpp/prefs/filesystem_json_pref_store.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/json/json_string_value_serializer.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/prefs/pref_filter.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/task_runner_util.h"
+#include "base/time/default_clock.h"
+#include "base/values.h"
+#include "mojo/common/common_type_converters.h"
+
+namespace filesystem {
+
+// Result returned from internal read tasks.
+struct FilesystemJsonPrefStore::ReadResult {
+ public:
+ ReadResult();
+ ~ReadResult();
+
+ scoped_ptr<base::Value> value;
+ PrefReadError error;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ReadResult);
+};
+
+FilesystemJsonPrefStore::ReadResult::ReadResult()
+ : error(PersistentPrefStore::PREF_READ_ERROR_NONE) {}
+
+FilesystemJsonPrefStore::ReadResult::~ReadResult() {}
+
+namespace {
+
+PersistentPrefStore::PrefReadError HandleReadErrors(const base::Value* value) {
+ if (!value->IsType(base::Value::TYPE_DICTIONARY))
+ return PersistentPrefStore::PREF_READ_ERROR_JSON_TYPE;
+ return PersistentPrefStore::PREF_READ_ERROR_NONE;
+}
+
+} // namespace
+
+FilesystemJsonPrefStore::FilesystemJsonPrefStore(
+ const std::string& pref_filename,
+ filesystem::FileSystemPtr filesystem,
+ scoped_ptr<PrefFilter> pref_filter)
+ : path_(pref_filename),
+ binding_(this),
+ filesystem_(std::move(filesystem)),
+ prefs_(new base::DictionaryValue()),
+ read_only_(false),
+ pref_filter_(std::move(pref_filter)),
+ initialized_(false),
+ filtering_in_progress_(false),
+ pending_lossy_write_(false),
+ read_error_(PREF_READ_ERROR_NONE) {
+ DCHECK(!path_.empty());
+}
+
+bool FilesystemJsonPrefStore::GetValue(const std::string& key,
+ const base::Value** result) const {
+ DCHECK(CalledOnValidThread());
+
+ base::Value* tmp = nullptr;
+ if (!prefs_->Get(key, &tmp))
+ return false;
+
+ if (result)
+ *result = tmp;
+ return true;
+}
+
+void FilesystemJsonPrefStore::AddObserver(PrefStore::Observer* observer) {
+ DCHECK(CalledOnValidThread());
+
+ observers_.AddObserver(observer);
+}
+
+void FilesystemJsonPrefStore::RemoveObserver(PrefStore::Observer* observer) {
+ DCHECK(CalledOnValidThread());
+
+ observers_.RemoveObserver(observer);
+}
+
+bool FilesystemJsonPrefStore::HasObservers() const {
+ DCHECK(CalledOnValidThread());
+
+ return observers_.might_have_observers();
+}
+
+bool FilesystemJsonPrefStore::IsInitializationComplete() const {
+ DCHECK(CalledOnValidThread());
+
+ return initialized_;
+}
+
+bool FilesystemJsonPrefStore::GetMutableValue(const std::string& key,
+ base::Value** result) {
+ DCHECK(CalledOnValidThread());
+
+ return prefs_->Get(key, result);
+}
+
+void FilesystemJsonPrefStore::SetValue(const std::string& key,
+ scoped_ptr<base::Value> value,
+ uint32_t flags) {
+ DCHECK(CalledOnValidThread());
+
+ DCHECK(value);
+ base::Value* old_value = nullptr;
+ prefs_->Get(key, &old_value);
+ if (!old_value || !value->Equals(old_value)) {
+ prefs_->Set(key, std::move(value));
+ ReportValueChanged(key, flags);
+ }
+}
+
+void FilesystemJsonPrefStore::SetValueSilently(const std::string& key,
+ scoped_ptr<base::Value> value,
+ uint32_t flags) {
+ DCHECK(CalledOnValidThread());
+
+ DCHECK(value);
+ base::Value* old_value = nullptr;
+ prefs_->Get(key, &old_value);
+ if (!old_value || !value->Equals(old_value)) {
+ prefs_->Set(key, std::move(value));
+ ScheduleWrite(flags);
+ }
+}
+
+void FilesystemJsonPrefStore::RemoveValue(const std::string& key,
+ uint32_t flags) {
+ DCHECK(CalledOnValidThread());
+
+ if (prefs_->RemovePath(key, nullptr))
+ ReportValueChanged(key, flags);
+}
+
+void FilesystemJsonPrefStore::RemoveValueSilently(const std::string& key,
+ uint32_t flags) {
+ DCHECK(CalledOnValidThread());
+
+ prefs_->RemovePath(key, nullptr);
+ ScheduleWrite(flags);
+}
+
+bool FilesystemJsonPrefStore::ReadOnly() const {
+ DCHECK(CalledOnValidThread());
+
+ return read_only_;
+}
+
+PersistentPrefStore::PrefReadError FilesystemJsonPrefStore::GetReadError()
+ const {
+ DCHECK(CalledOnValidThread());
+
+ return read_error_;
+}
+
+PersistentPrefStore::PrefReadError FilesystemJsonPrefStore::ReadPrefs() {
+ NOTREACHED();
+ // TODO(erg): Synchronously reading files makes no sense in a mojo world and
+ // should be removed from the API.
+ return PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE;
+}
+
+void FilesystemJsonPrefStore::ReadPrefsAsync(
+ ReadErrorDelegate* error_delegate) {
+ DCHECK(CalledOnValidThread());
+
+ initialized_ = false;
+ error_delegate_.reset(error_delegate);
+
+ if (!directory_) {
+ OpenFilesystem(
+ Bind(&FilesystemJsonPrefStore::OnPreferencesReadStart, AsWeakPtr()));
+ } else {
+ OnPreferencesReadStart();
+ }
+}
+
+void FilesystemJsonPrefStore::CommitPendingWrite() {
+ DCHECK(CalledOnValidThread());
+
+ // TODO(erg): This is another one of those cases where we have problems
+ // because of mismatch between the models used in the pref service versus
+ // here. Most of the time, CommitPendingWrite() is called from
+ // PrefService. However, in JSONPrefStore, we also call this method on
+ // shutdown and thus need to synchronously write. But in mojo:filesystem,
+ // everything is done asynchronously. So we're sort of stuck until we can
+ // change the interface, which we'll do in the longer term.
+
+ SchedulePendingLossyWrites();
+}
+
+void FilesystemJsonPrefStore::SchedulePendingLossyWrites() {
+ // This method is misnamed for the sake of the interface. Given that writing
+ // is already asynchronous in this new world, "sheduleing" a pending write
+ // just starts the asynchronous process.
+ if (pending_lossy_write_)
+ PerformWrite();
+}
+
+void FilesystemJsonPrefStore::ReportValueChanged(const std::string& key,
+ uint32_t flags) {
+ DCHECK(CalledOnValidThread());
+
+ if (pref_filter_)
+ pref_filter_->FilterUpdate(key);
+
+ FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key));
+
+ ScheduleWrite(flags);
+}
+
+void FilesystemJsonPrefStore::OnFileSystemShutdown() {}
+
+void FilesystemJsonPrefStore::OnFileRead(scoped_ptr<ReadResult> read_result) {
+ DCHECK(CalledOnValidThread());
+
+ DCHECK(read_result);
+
+ scoped_ptr<base::DictionaryValue> unfiltered_prefs(new base::DictionaryValue);
+
+ read_error_ = read_result->error;
+
+ switch (read_error_) {
+ case PREF_READ_ERROR_ACCESS_DENIED:
+ case PREF_READ_ERROR_FILE_OTHER:
+ case PREF_READ_ERROR_FILE_LOCKED:
+ case PREF_READ_ERROR_JSON_TYPE:
+ case PREF_READ_ERROR_FILE_NOT_SPECIFIED:
+ read_only_ = true;
+ break;
+ case PREF_READ_ERROR_NONE:
+ DCHECK(read_result->value.get());
+ unfiltered_prefs.reset(
+ static_cast<base::DictionaryValue*>(read_result->value.release()));
+ break;
+ case PREF_READ_ERROR_NO_FILE:
+ // If the file just doesn't exist, maybe this is first run. In any case
+ // there's no harm in writing out default prefs in this case.
+ case PREF_READ_ERROR_JSON_PARSE:
+ case PREF_READ_ERROR_JSON_REPEAT:
+ break;
+ case PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE:
+ // This is a special error code to be returned by ReadPrefs when it
+ // can't complete synchronously, it should never be returned by the read
+ // operation itself.
+ case PREF_READ_ERROR_MAX_ENUM:
+ NOTREACHED();
+ break;
+ }
+
+ if (pref_filter_) {
+ filtering_in_progress_ = true;
+ const PrefFilter::PostFilterOnLoadCallback post_filter_on_load_callback(
+ base::Bind(&FilesystemJsonPrefStore::FinalizeFileRead, AsWeakPtr()));
+ pref_filter_->FilterOnLoad(post_filter_on_load_callback,
+ std::move(unfiltered_prefs));
+ } else {
+ FinalizeFileRead(std::move(unfiltered_prefs), false);
+ }
+}
+
+FilesystemJsonPrefStore::~FilesystemJsonPrefStore() {
+ // TODO(erg): Now that writing is asynchronous, we can't really async write
+ // prefs at shutdown. See comment in CommitPendingWrite().
+}
+
+void FilesystemJsonPrefStore::FinalizeFileRead(
+ scoped_ptr<base::DictionaryValue> prefs,
+ bool schedule_write) {
+ DCHECK(CalledOnValidThread());
+
+ filtering_in_progress_ = false;
+
+ prefs_ = std::move(prefs);
+
+ initialized_ = true;
+
+ if (schedule_write)
+ ScheduleWrite(DEFAULT_PREF_WRITE_FLAGS);
+
+ if (error_delegate_ && read_error_ != PREF_READ_ERROR_NONE)
+ error_delegate_->OnError(read_error_);
+
+ FOR_EACH_OBSERVER(PrefStore::Observer, observers_,
+ OnInitializationCompleted(true));
+
+ return;
+}
+
+void FilesystemJsonPrefStore::ScheduleWrite(uint32_t flags) {
+ if (read_only_)
+ return;
+
+ if (flags & LOSSY_PREF_WRITE_FLAG)
+ pending_lossy_write_ = true;
+ else
+ PerformWrite();
+}
+
+void FilesystemJsonPrefStore::PerformWrite() {
+ if (!directory_) {
+ OpenFilesystem(
+ Bind(&FilesystemJsonPrefStore::OnTempFileWriteStart, AsWeakPtr()));
+ } else {
+ OnTempFileWriteStart();
+ }
+}
+
+void FilesystemJsonPrefStore::OpenFilesystem(base::Closure callback) {
+ filesystem::FileSystemClientPtr client;
+ binding_.Bind(GetProxy(&client));
+
+ filesystem_->OpenFileSystem(
+ "origin", GetProxy(&directory_), std::move(client),
+ base::Bind(&FilesystemJsonPrefStore::OnOpenFilesystem, AsWeakPtr(),
+ callback));
+}
+
+void FilesystemJsonPrefStore::OnOpenFilesystem(base::Closure callback,
+ FileError err) {
+ if (err != FileError::OK) {
+ // Do real error checking.
+ NOTIMPLEMENTED();
+ return;
+ }
+
+ callback.Run();
+}
+
+void FilesystemJsonPrefStore::OnTempFileWriteStart() {
+ // Open up a temporary file and truncate it.
+ directory_->OpenFile(
+ "tmp", GetProxy(&temporary_file_), kFlagWrite | kFlagCreate,
+ Bind(&FilesystemJsonPrefStore::OnTempFileOpened, AsWeakPtr()));
+}
+
+void FilesystemJsonPrefStore::OnTempFileOpened(FileError err) {
+ // TODO(erg): Error handling. The JsonPrefStore code assumes that writing the
+ // file can never fail.
+ CHECK_EQ(FileError::OK, err);
+
+ // Calculate what we want to write, and then write to the temporary file.
+ pending_lossy_write_ = false;
+
+ if (pref_filter_)
+ pref_filter_->FilterSerializeData(prefs_.get());
+
+ std::string output;
+ JSONStringValueSerializer serializer(&output);
+ serializer.set_pretty_print(false);
+ serializer.Serialize(*prefs_);
+
+ temporary_file_->Write(
+ mojo::Array<uint8_t>::From(output), 0, Whence::FROM_CURRENT,
+ Bind(&FilesystemJsonPrefStore::OnTempFileWrite, AsWeakPtr()));
+}
+
+void FilesystemJsonPrefStore::OnTempFileWrite(FileError err,
+ uint32_t num_bytes_written) {
+ // TODO(erg): Error handling. The JsonPrefStore code assumes that writing the
+ // file can never fail.
+ CHECK_EQ(FileError::OK, err);
+
+ // Now that we've written the file, close it.
+ temporary_file_->Close(
+ Bind(&FilesystemJsonPrefStore::OnTempFileClosed, AsWeakPtr()));
+}
+
+void FilesystemJsonPrefStore::OnTempFileClosed(FileError err) {
+ // TODO(erg): Error handling. The JsonPrefStore code assumes that writing the
+ // file can never fail.
+ CHECK_EQ(FileError::OK, err);
+
+ temporary_file_.reset();
+ directory_->Rename(
+ "tmp", path_,
+ Bind(&FilesystemJsonPrefStore::OnTempFileRenamed, AsWeakPtr()));
+}
+
+void FilesystemJsonPrefStore::OnTempFileRenamed(FileError err) {}
+
+void FilesystemJsonPrefStore::OnPreferencesReadStart() {
+ // TODO(erg): implement me.
+ directory_->OpenFile(
+ path_, GetProxy(&preferences_file_), kFlagRead | kFlagOpen,
+ Bind(&FilesystemJsonPrefStore::OnPreferencesFileOpened, AsWeakPtr()));
+}
+
+void FilesystemJsonPrefStore::OnPreferencesFileOpened(FileError err) {
+ // TODO(erg): Error handling.
+ if (err == FileError::OK) {
+ preferences_file_->ReadEntireFile(
+ Bind(&FilesystemJsonPrefStore::OnPreferencesFileRead, AsWeakPtr()));
+ } else {
+ OnPreferencesFileRead(err, mojo::Array<uint8_t>());
+ }
+}
+
+void FilesystemJsonPrefStore::OnPreferencesFileRead(
+ FileError err,
+ mojo::Array<uint8_t> contents) {
+ scoped_ptr<FilesystemJsonPrefStore::ReadResult> read_result(
+ new FilesystemJsonPrefStore::ReadResult);
+ // TODO(erg): Needs even better error handling.
+ switch (err) {
+ case FileError::IN_USE:
+ case FileError::ACCESS_DENIED: {
+ read_only_ = true;
+ break;
+ }
+ case FileError::NOT_FOUND: {
+ // If the file just doesn't exist, maybe this is the first run. Just
+ // don't pass a value.
+ read_result->error = PREF_READ_ERROR_NO_FILE;
+ break;
+ }
+ default: {
+ int error_code;
+ std::string error_msg;
+ JSONStringValueDeserializer deserializer(base::StringPiece(
+ reinterpret_cast<char*>(&contents.front()), contents.size()));
+ read_result->value = deserializer.Deserialize(&error_code, &error_msg);
+ read_result->error = HandleReadErrors(read_result->value.get());
+ }
+ }
+
+ preferences_file_.reset();
+
+ OnFileRead(std::move(read_result));
+}
+
+} // namespace filesystem

Powered by Google App Engine
This is Rietveld 408576698