Home | History | Annotate | Download | only in prefs
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/prefs/json_pref_store.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/bind.h"
     10 #include "base/callback.h"
     11 #include "base/file_util.h"
     12 #include "base/json/json_file_value_serializer.h"
     13 #include "base/json/json_string_value_serializer.h"
     14 #include "base/memory/ref_counted.h"
     15 #include "base/message_loop/message_loop_proxy.h"
     16 #include "base/prefs/pref_filter.h"
     17 #include "base/sequenced_task_runner.h"
     18 #include "base/threading/sequenced_worker_pool.h"
     19 #include "base/values.h"
     20 
     21 namespace {
     22 
     23 // Some extensions we'll tack on to copies of the Preferences files.
     24 const base::FilePath::CharType* kBadExtension = FILE_PATH_LITERAL("bad");
     25 
     26 // Differentiates file loading between origin thread and passed
     27 // (aka file) thread.
     28 class FileThreadDeserializer
     29     : public base::RefCountedThreadSafe<FileThreadDeserializer> {
     30  public:
     31   FileThreadDeserializer(JsonPrefStore* delegate,
     32                          base::SequencedTaskRunner* sequenced_task_runner)
     33       : no_dir_(false),
     34         error_(PersistentPrefStore::PREF_READ_ERROR_NONE),
     35         delegate_(delegate),
     36         sequenced_task_runner_(sequenced_task_runner),
     37         origin_loop_proxy_(base::MessageLoopProxy::current()) {
     38   }
     39 
     40   void Start(const base::FilePath& path,
     41              const base::FilePath& alternate_path) {
     42     DCHECK(origin_loop_proxy_->BelongsToCurrentThread());
     43     // TODO(gab): This should use PostTaskAndReplyWithResult instead of using
     44     // the |error_| member to pass data across tasks.
     45     sequenced_task_runner_->PostTask(
     46         FROM_HERE,
     47         base::Bind(&FileThreadDeserializer::ReadFileAndReport,
     48                    this, path, alternate_path));
     49   }
     50 
     51   // Deserializes JSON on the sequenced task runner.
     52   void ReadFileAndReport(const base::FilePath& path,
     53                          const base::FilePath& alternate_path) {
     54     DCHECK(sequenced_task_runner_->RunsTasksOnCurrentThread());
     55 
     56     value_.reset(DoReading(path, alternate_path, &error_, &no_dir_));
     57 
     58     origin_loop_proxy_->PostTask(
     59         FROM_HERE,
     60         base::Bind(&FileThreadDeserializer::ReportOnOriginThread, this));
     61   }
     62 
     63   // Reports deserialization result on the origin thread.
     64   void ReportOnOriginThread() {
     65     DCHECK(origin_loop_proxy_->BelongsToCurrentThread());
     66     delegate_->OnFileRead(value_.Pass(), error_, no_dir_);
     67   }
     68 
     69   static base::Value* DoReading(const base::FilePath& path,
     70                                 const base::FilePath& alternate_path,
     71                                 PersistentPrefStore::PrefReadError* error,
     72                                 bool* no_dir) {
     73     if (!base::PathExists(path) && !alternate_path.empty() &&
     74         base::PathExists(alternate_path)) {
     75       base::Move(alternate_path, path);
     76     }
     77 
     78     int error_code;
     79     std::string error_msg;
     80     JSONFileValueSerializer serializer(path);
     81     base::Value* value = serializer.Deserialize(&error_code, &error_msg);
     82     HandleErrors(value, path, error_code, error_msg, error);
     83     *no_dir = !base::PathExists(path.DirName());
     84     return value;
     85   }
     86 
     87   static void HandleErrors(const base::Value* value,
     88                            const base::FilePath& path,
     89                            int error_code,
     90                            const std::string& error_msg,
     91                            PersistentPrefStore::PrefReadError* error);
     92 
     93  private:
     94   friend class base::RefCountedThreadSafe<FileThreadDeserializer>;
     95   ~FileThreadDeserializer() {}
     96 
     97   bool no_dir_;
     98   PersistentPrefStore::PrefReadError error_;
     99   scoped_ptr<base::Value> value_;
    100   const scoped_refptr<JsonPrefStore> delegate_;
    101   const scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_;
    102   const scoped_refptr<base::MessageLoopProxy> origin_loop_proxy_;
    103 };
    104 
    105 // static
    106 void FileThreadDeserializer::HandleErrors(
    107     const base::Value* value,
    108     const base::FilePath& path,
    109     int error_code,
    110     const std::string& error_msg,
    111     PersistentPrefStore::PrefReadError* error) {
    112   *error = PersistentPrefStore::PREF_READ_ERROR_NONE;
    113   if (!value) {
    114     DVLOG(1) << "Error while loading JSON file: " << error_msg
    115              << ", file: " << path.value();
    116     switch (error_code) {
    117       case JSONFileValueSerializer::JSON_ACCESS_DENIED:
    118         *error = PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED;
    119         break;
    120       case JSONFileValueSerializer::JSON_CANNOT_READ_FILE:
    121         *error = PersistentPrefStore::PREF_READ_ERROR_FILE_OTHER;
    122         break;
    123       case JSONFileValueSerializer::JSON_FILE_LOCKED:
    124         *error = PersistentPrefStore::PREF_READ_ERROR_FILE_LOCKED;
    125         break;
    126       case JSONFileValueSerializer::JSON_NO_SUCH_FILE:
    127         *error = PersistentPrefStore::PREF_READ_ERROR_NO_FILE;
    128         break;
    129       default:
    130         *error = PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE;
    131         // JSON errors indicate file corruption of some sort.
    132         // Since the file is corrupt, move it to the side and continue with
    133         // empty preferences.  This will result in them losing their settings.
    134         // We keep the old file for possible support and debugging assistance
    135         // as well as to detect if they're seeing these errors repeatedly.
    136         // TODO(erikkay) Instead, use the last known good file.
    137         base::FilePath bad = path.ReplaceExtension(kBadExtension);
    138 
    139         // If they've ever had a parse error before, put them in another bucket.
    140         // TODO(erikkay) if we keep this error checking for very long, we may
    141         // want to differentiate between recent and long ago errors.
    142         if (base::PathExists(bad))
    143           *error = PersistentPrefStore::PREF_READ_ERROR_JSON_REPEAT;
    144         base::Move(path, bad);
    145         break;
    146     }
    147   } else if (!value->IsType(base::Value::TYPE_DICTIONARY)) {
    148     *error = PersistentPrefStore::PREF_READ_ERROR_JSON_TYPE;
    149   }
    150 }
    151 
    152 }  // namespace
    153 
    154 scoped_refptr<base::SequencedTaskRunner> JsonPrefStore::GetTaskRunnerForFile(
    155     const base::FilePath& filename,
    156     base::SequencedWorkerPool* worker_pool) {
    157   std::string token("json_pref_store-");
    158   token.append(filename.AsUTF8Unsafe());
    159   return worker_pool->GetSequencedTaskRunnerWithShutdownBehavior(
    160       worker_pool->GetNamedSequenceToken(token),
    161       base::SequencedWorkerPool::BLOCK_SHUTDOWN);
    162 }
    163 
    164 JsonPrefStore::JsonPrefStore(const base::FilePath& filename,
    165                              base::SequencedTaskRunner* sequenced_task_runner,
    166                              scoped_ptr<PrefFilter> pref_filter)
    167     : path_(filename),
    168       sequenced_task_runner_(sequenced_task_runner),
    169       prefs_(new base::DictionaryValue()),
    170       read_only_(false),
    171       writer_(filename, sequenced_task_runner),
    172       pref_filter_(pref_filter.Pass()),
    173       initialized_(false),
    174       filtering_in_progress_(false),
    175       read_error_(PREF_READ_ERROR_NONE) {
    176 }
    177 
    178 JsonPrefStore::JsonPrefStore(const base::FilePath& filename,
    179                              const base::FilePath& alternate_filename,
    180                              base::SequencedTaskRunner* sequenced_task_runner,
    181                              scoped_ptr<PrefFilter> pref_filter)
    182     : path_(filename),
    183       alternate_path_(alternate_filename),
    184       sequenced_task_runner_(sequenced_task_runner),
    185       prefs_(new base::DictionaryValue()),
    186       read_only_(false),
    187       writer_(filename, sequenced_task_runner),
    188       pref_filter_(pref_filter.Pass()),
    189       initialized_(false),
    190       filtering_in_progress_(false),
    191       read_error_(PREF_READ_ERROR_NONE) {
    192 }
    193 
    194 bool JsonPrefStore::GetValue(const std::string& key,
    195                              const base::Value** result) const {
    196   base::Value* tmp = NULL;
    197   if (!prefs_->Get(key, &tmp))
    198     return false;
    199 
    200   if (result)
    201     *result = tmp;
    202   return true;
    203 }
    204 
    205 void JsonPrefStore::AddObserver(PrefStore::Observer* observer) {
    206   observers_.AddObserver(observer);
    207 }
    208 
    209 void JsonPrefStore::RemoveObserver(PrefStore::Observer* observer) {
    210   observers_.RemoveObserver(observer);
    211 }
    212 
    213 bool JsonPrefStore::HasObservers() const {
    214   return observers_.might_have_observers();
    215 }
    216 
    217 bool JsonPrefStore::IsInitializationComplete() const {
    218   return initialized_;
    219 }
    220 
    221 bool JsonPrefStore::GetMutableValue(const std::string& key,
    222                                     base::Value** result) {
    223   return prefs_->Get(key, result);
    224 }
    225 
    226 void JsonPrefStore::SetValue(const std::string& key, base::Value* value) {
    227   DCHECK(value);
    228   scoped_ptr<base::Value> new_value(value);
    229   base::Value* old_value = NULL;
    230   prefs_->Get(key, &old_value);
    231   if (!old_value || !value->Equals(old_value)) {
    232     prefs_->Set(key, new_value.release());
    233     ReportValueChanged(key);
    234   }
    235 }
    236 
    237 void JsonPrefStore::SetValueSilently(const std::string& key,
    238                                      base::Value* value) {
    239   DCHECK(value);
    240   scoped_ptr<base::Value> new_value(value);
    241   base::Value* old_value = NULL;
    242   prefs_->Get(key, &old_value);
    243   if (!old_value || !value->Equals(old_value)) {
    244     prefs_->Set(key, new_value.release());
    245     if (!read_only_)
    246       writer_.ScheduleWrite(this);
    247   }
    248 }
    249 
    250 void JsonPrefStore::RemoveValue(const std::string& key) {
    251   if (prefs_->RemovePath(key, NULL))
    252     ReportValueChanged(key);
    253 }
    254 
    255 void JsonPrefStore::RemoveValueSilently(const std::string& key) {
    256   prefs_->RemovePath(key, NULL);
    257   if (!read_only_)
    258     writer_.ScheduleWrite(this);
    259 }
    260 
    261 bool JsonPrefStore::ReadOnly() const {
    262   return read_only_;
    263 }
    264 
    265 PersistentPrefStore::PrefReadError JsonPrefStore::GetReadError() const {
    266   return read_error_;
    267 }
    268 
    269 PersistentPrefStore::PrefReadError JsonPrefStore::ReadPrefs() {
    270   if (path_.empty()) {
    271     OnFileRead(
    272         scoped_ptr<base::Value>(), PREF_READ_ERROR_FILE_NOT_SPECIFIED, false);
    273     return PREF_READ_ERROR_FILE_NOT_SPECIFIED;
    274   }
    275 
    276   PrefReadError error;
    277   bool no_dir;
    278   scoped_ptr<base::Value> value(
    279       FileThreadDeserializer::DoReading(path_, alternate_path_, &error,
    280                                         &no_dir));
    281   OnFileRead(value.Pass(), error, no_dir);
    282   return filtering_in_progress_ ? PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE :
    283                                   error;
    284 }
    285 
    286 void JsonPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate) {
    287   initialized_ = false;
    288   error_delegate_.reset(error_delegate);
    289   if (path_.empty()) {
    290     OnFileRead(
    291         scoped_ptr<base::Value>(), PREF_READ_ERROR_FILE_NOT_SPECIFIED, false);
    292     return;
    293   }
    294 
    295   // Start async reading of the preferences file. It will delete itself
    296   // in the end.
    297   scoped_refptr<FileThreadDeserializer> deserializer(
    298       new FileThreadDeserializer(this, sequenced_task_runner_.get()));
    299   deserializer->Start(path_, alternate_path_);
    300 }
    301 
    302 void JsonPrefStore::CommitPendingWrite() {
    303   if (writer_.HasPendingWrite() && !read_only_)
    304     writer_.DoScheduledWrite();
    305 }
    306 
    307 void JsonPrefStore::ReportValueChanged(const std::string& key) {
    308   if (pref_filter_)
    309     pref_filter_->FilterUpdate(key);
    310 
    311   FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key));
    312 
    313   if (!read_only_)
    314     writer_.ScheduleWrite(this);
    315 }
    316 
    317 void JsonPrefStore::RegisterOnNextSuccessfulWriteCallback(
    318     const base::Closure& on_next_successful_write) {
    319   writer_.RegisterOnNextSuccessfulWriteCallback(on_next_successful_write);
    320 }
    321 
    322 void JsonPrefStore::OnFileRead(scoped_ptr<base::Value> value,
    323                                PersistentPrefStore::PrefReadError error,
    324                                bool no_dir) {
    325   scoped_ptr<base::DictionaryValue> unfiltered_prefs(new base::DictionaryValue);
    326 
    327   read_error_ = error;
    328 
    329   bool initialization_successful = !no_dir;
    330 
    331   if (initialization_successful) {
    332     switch (read_error_) {
    333       case PREF_READ_ERROR_ACCESS_DENIED:
    334       case PREF_READ_ERROR_FILE_OTHER:
    335       case PREF_READ_ERROR_FILE_LOCKED:
    336       case PREF_READ_ERROR_JSON_TYPE:
    337       case PREF_READ_ERROR_FILE_NOT_SPECIFIED:
    338         read_only_ = true;
    339         break;
    340       case PREF_READ_ERROR_NONE:
    341         DCHECK(value.get());
    342         unfiltered_prefs.reset(
    343             static_cast<base::DictionaryValue*>(value.release()));
    344         break;
    345       case PREF_READ_ERROR_NO_FILE:
    346         // If the file just doesn't exist, maybe this is first run.  In any case
    347         // there's no harm in writing out default prefs in this case.
    348         break;
    349       case PREF_READ_ERROR_JSON_PARSE:
    350       case PREF_READ_ERROR_JSON_REPEAT:
    351         break;
    352       case PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE:
    353         // This is a special error code to be returned by ReadPrefs when it
    354         // can't complete synchronously, it should never be returned by the read
    355         // operation itself.
    356         NOTREACHED();
    357         break;
    358       case PREF_READ_ERROR_LEVELDB_IO:
    359       case PREF_READ_ERROR_LEVELDB_CORRUPTION_READ_ONLY:
    360       case PREF_READ_ERROR_LEVELDB_CORRUPTION:
    361         // These are specific to LevelDBPrefStore.
    362         NOTREACHED();
    363       case PREF_READ_ERROR_MAX_ENUM:
    364         NOTREACHED();
    365         break;
    366     }
    367   }
    368 
    369   if (pref_filter_) {
    370     filtering_in_progress_ = true;
    371     const PrefFilter::PostFilterOnLoadCallback post_filter_on_load_callback(
    372         base::Bind(
    373             &JsonPrefStore::FinalizeFileRead, this, initialization_successful));
    374     pref_filter_->FilterOnLoad(post_filter_on_load_callback,
    375                                unfiltered_prefs.Pass());
    376   } else {
    377     FinalizeFileRead(initialization_successful, unfiltered_prefs.Pass(), false);
    378   }
    379 }
    380 
    381 JsonPrefStore::~JsonPrefStore() {
    382   CommitPendingWrite();
    383 }
    384 
    385 bool JsonPrefStore::SerializeData(std::string* output) {
    386   if (pref_filter_)
    387     pref_filter_->FilterSerializeData(prefs_.get());
    388 
    389   JSONStringValueSerializer serializer(output);
    390   serializer.set_pretty_print(true);
    391   return serializer.Serialize(*prefs_);
    392 }
    393 
    394 void JsonPrefStore::FinalizeFileRead(bool initialization_successful,
    395                                      scoped_ptr<base::DictionaryValue> prefs,
    396                                      bool schedule_write) {
    397   filtering_in_progress_ = false;
    398 
    399   if (!initialization_successful) {
    400     FOR_EACH_OBSERVER(PrefStore::Observer,
    401                       observers_,
    402                       OnInitializationCompleted(false));
    403     return;
    404   }
    405 
    406   prefs_ = prefs.Pass();
    407 
    408   initialized_ = true;
    409 
    410   if (schedule_write && !read_only_)
    411     writer_.ScheduleWrite(this);
    412 
    413   if (error_delegate_ && read_error_ != PREF_READ_ERROR_NONE)
    414     error_delegate_->OnError(read_error_);
    415 
    416   FOR_EACH_OBSERVER(PrefStore::Observer,
    417                     observers_,
    418                     OnInitializationCompleted(true));
    419 
    420   return;
    421 }
    422