| 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
 | 
| 
 |