Home | History | Annotate | Download | only in common
      1 // Copyright (c) 2011 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 "chrome/common/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/memory/ref_counted.h"
     13 #include "base/values.h"
     14 #include "content/browser/browser_thread.h"
     15 #include "content/common/json_value_serializer.h"
     16 
     17 namespace {
     18 
     19 // Some extensions we'll tack on to copies of the Preferences files.
     20 const FilePath::CharType* kBadExtension = FILE_PATH_LITERAL("bad");
     21 
     22 // Differentiates file loading between UI and FILE threads.
     23 class FileThreadDeserializer
     24     : public base::RefCountedThreadSafe<FileThreadDeserializer> {
     25  public:
     26   explicit FileThreadDeserializer(JsonPrefStore* delegate)
     27       : delegate_(delegate) {
     28   }
     29 
     30   void Start(const FilePath& path) {
     31     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     32     BrowserThread::PostTask(
     33         BrowserThread::FILE,
     34         FROM_HERE,
     35         NewRunnableMethod(this,
     36                           &FileThreadDeserializer::ReadFileAndReport,
     37                           path));
     38   }
     39 
     40   // Deserializes JSON on the FILE thread.
     41   void ReadFileAndReport(const FilePath& path) {
     42     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
     43 
     44     int error_code;
     45     std::string error_msg;
     46     JSONFileValueSerializer serializer(path);
     47     value_.reset(serializer.Deserialize(&error_code, &error_msg));
     48 
     49     HandleErrors(value_.get(), path, error_code, error_msg, &error_);
     50 
     51     no_dir_ = !file_util::PathExists(path.DirName());
     52 
     53     BrowserThread::PostTask(
     54         BrowserThread::UI,
     55         FROM_HERE,
     56         NewRunnableMethod(this, &FileThreadDeserializer::ReportOnUIThread));
     57   }
     58 
     59   // Reports deserialization result on the UI thread.
     60   void ReportOnUIThread() {
     61     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     62     delegate_->OnFileRead(value_.release(), error_, no_dir_);
     63   }
     64 
     65   static void HandleErrors(const Value* value,
     66                            const FilePath& path,
     67                            int error_code,
     68                            const std::string& error_msg,
     69                            PersistentPrefStore::PrefReadError* error);
     70 
     71  private:
     72   friend class base::RefCountedThreadSafe<FileThreadDeserializer>;
     73 
     74   bool no_dir_;
     75   PersistentPrefStore::PrefReadError error_;
     76   scoped_ptr<Value> value_;
     77   scoped_refptr<JsonPrefStore> delegate_;
     78 };
     79 
     80 // static
     81 void FileThreadDeserializer::HandleErrors(
     82     const Value* value,
     83     const FilePath& path,
     84     int error_code,
     85     const std::string& error_msg,
     86     PersistentPrefStore::PrefReadError* error) {
     87   *error = PersistentPrefStore::PREF_READ_ERROR_NONE;
     88   if (!value) {
     89     DLOG(ERROR) << "Error while loading JSON file: " << error_msg;
     90     switch (error_code) {
     91       case JSONFileValueSerializer::JSON_ACCESS_DENIED:
     92         *error = PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED;
     93         break;
     94       case JSONFileValueSerializer::JSON_CANNOT_READ_FILE:
     95         *error = PersistentPrefStore::PREF_READ_ERROR_FILE_OTHER;
     96         break;
     97       case JSONFileValueSerializer::JSON_FILE_LOCKED:
     98         *error = PersistentPrefStore::PREF_READ_ERROR_FILE_LOCKED;
     99         break;
    100       case JSONFileValueSerializer::JSON_NO_SUCH_FILE:
    101         *error = PersistentPrefStore::PREF_READ_ERROR_NO_FILE;
    102         break;
    103       default:
    104         *error = PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE;
    105         // JSON errors indicate file corruption of some sort.
    106         // Since the file is corrupt, move it to the side and continue with
    107         // empty preferences.  This will result in them losing their settings.
    108         // We keep the old file for possible support and debugging assistance
    109         // as well as to detect if they're seeing these errors repeatedly.
    110         // TODO(erikkay) Instead, use the last known good file.
    111         FilePath bad = path.ReplaceExtension(kBadExtension);
    112 
    113         // If they've ever had a parse error before, put them in another bucket.
    114         // TODO(erikkay) if we keep this error checking for very long, we may
    115         // want to differentiate between recent and long ago errors.
    116         if (file_util::PathExists(bad))
    117           *error = PersistentPrefStore::PREF_READ_ERROR_JSON_REPEAT;
    118         file_util::Move(path, bad);
    119         break;
    120     }
    121   } else if (!value->IsType(Value::TYPE_DICTIONARY)) {
    122     *error = PersistentPrefStore::PREF_READ_ERROR_JSON_TYPE;
    123   }
    124 }
    125 
    126 }  // namespace
    127 
    128 JsonPrefStore::JsonPrefStore(const FilePath& filename,
    129                              base::MessageLoopProxy* file_message_loop_proxy)
    130     : path_(filename),
    131       prefs_(new DictionaryValue()),
    132       read_only_(false),
    133       writer_(filename, file_message_loop_proxy) {
    134 }
    135 
    136 JsonPrefStore::~JsonPrefStore() {
    137   CommitPendingWrite();
    138 }
    139 
    140 PrefStore::ReadResult JsonPrefStore::GetValue(const std::string& key,
    141                                               const Value** result) const {
    142   Value* tmp = NULL;
    143   if (prefs_->Get(key, &tmp)) {
    144     *result = tmp;
    145     return READ_OK;
    146   }
    147   return READ_NO_VALUE;
    148 }
    149 
    150 void JsonPrefStore::AddObserver(PrefStore::Observer* observer) {
    151   observers_.AddObserver(observer);
    152 }
    153 
    154 void JsonPrefStore::RemoveObserver(PrefStore::Observer* observer) {
    155   observers_.RemoveObserver(observer);
    156 }
    157 
    158 PrefStore::ReadResult JsonPrefStore::GetMutableValue(const std::string& key,
    159                                                      Value** result) {
    160   return prefs_->Get(key, result) ? READ_OK : READ_NO_VALUE;
    161 }
    162 
    163 void JsonPrefStore::SetValue(const std::string& key, Value* value) {
    164   DCHECK(value);
    165   scoped_ptr<Value> new_value(value);
    166   Value* old_value = NULL;
    167   prefs_->Get(key, &old_value);
    168   if (!old_value || !value->Equals(old_value)) {
    169     prefs_->Set(key, new_value.release());
    170     FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key));
    171   }
    172 }
    173 
    174 void JsonPrefStore::SetValueSilently(const std::string& key, Value* value) {
    175   DCHECK(value);
    176   scoped_ptr<Value> new_value(value);
    177   Value* old_value = NULL;
    178   prefs_->Get(key, &old_value);
    179   if (!old_value || !value->Equals(old_value))
    180     prefs_->Set(key, new_value.release());
    181 }
    182 
    183 void JsonPrefStore::RemoveValue(const std::string& key) {
    184   if (prefs_->Remove(key, NULL)) {
    185     FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key));
    186   }
    187 }
    188 
    189 bool JsonPrefStore::ReadOnly() const {
    190   return read_only_;
    191 }
    192 
    193 void JsonPrefStore::OnFileRead(Value* value_owned,
    194                                PersistentPrefStore::PrefReadError error,
    195                                bool no_dir) {
    196   scoped_ptr<Value> value(value_owned);
    197   switch (error) {
    198     case PREF_READ_ERROR_ACCESS_DENIED:
    199     case PREF_READ_ERROR_FILE_OTHER:
    200     case PREF_READ_ERROR_FILE_LOCKED:
    201     case PREF_READ_ERROR_JSON_TYPE:
    202       read_only_ = true;
    203       break;
    204     case PREF_READ_ERROR_NONE:
    205       DCHECK(value.get());
    206       prefs_.reset(static_cast<DictionaryValue*>(value.release()));
    207       break;
    208     case PREF_READ_ERROR_NO_FILE:
    209       // If the file just doesn't exist, maybe this is first run.  In any case
    210       // there's no harm in writing out default prefs in this case.
    211       break;
    212     case PREF_READ_ERROR_JSON_PARSE:
    213     case PREF_READ_ERROR_JSON_REPEAT:
    214       break;
    215     default:
    216       NOTREACHED() << "Unknown error: " << error;
    217   }
    218 
    219   if (delegate_)
    220     delegate_->OnPrefsRead(error, no_dir);
    221 }
    222 
    223 void JsonPrefStore::ReadPrefs(Delegate* delegate) {
    224   DCHECK(delegate);
    225   delegate_ = delegate;
    226 
    227   if (path_.empty()) {
    228     read_only_ = true;
    229     delegate_->OnPrefsRead(PREF_READ_ERROR_FILE_NOT_SPECIFIED, false);
    230     return;
    231   }
    232 
    233   // This guarantees that class will not be deleted while JSON is readed.
    234   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    235 
    236   // Start async reading of the preferences file. It will delete itself
    237   // in the end.
    238   scoped_refptr<FileThreadDeserializer> deserializer(
    239       new FileThreadDeserializer(this));
    240   deserializer->Start(path_);
    241 }
    242 
    243 PersistentPrefStore::PrefReadError JsonPrefStore::ReadPrefs() {
    244   delegate_ = NULL;
    245 
    246   if (path_.empty()) {
    247     read_only_ = true;
    248     return PREF_READ_ERROR_FILE_NOT_SPECIFIED;
    249   }
    250 
    251   int error_code = 0;
    252   std::string error_msg;
    253 
    254   JSONFileValueSerializer serializer(path_);
    255   scoped_ptr<Value> value(serializer.Deserialize(&error_code, &error_msg));
    256 
    257   PersistentPrefStore::PrefReadError error;
    258   FileThreadDeserializer::HandleErrors(value.get(),
    259                                        path_,
    260                                        error_code,
    261                                        error_msg,
    262                                        &error);
    263 
    264   OnFileRead(value.release(), error, false);
    265 
    266   return error;
    267 }
    268 
    269 bool JsonPrefStore::WritePrefs() {
    270   std::string data;
    271   if (!SerializeData(&data))
    272     return false;
    273 
    274   // Lie about our ability to save.
    275   if (read_only_)
    276     return true;
    277 
    278   writer_.WriteNow(data);
    279   return true;
    280 }
    281 
    282 void JsonPrefStore::ScheduleWritePrefs() {
    283   if (read_only_)
    284     return;
    285 
    286   writer_.ScheduleWrite(this);
    287 }
    288 
    289 void JsonPrefStore::CommitPendingWrite() {
    290   if (writer_.HasPendingWrite() && !read_only_)
    291     writer_.DoScheduledWrite();
    292 }
    293 
    294 void JsonPrefStore::ReportValueChanged(const std::string& key) {
    295   FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key));
    296 }
    297 
    298 bool JsonPrefStore::SerializeData(std::string* output) {
    299   // TODO(tc): Do we want to prune webkit preferences that match the default
    300   // value?
    301   JSONStringValueSerializer serializer(output);
    302   serializer.set_pretty_print(true);
    303   scoped_ptr<DictionaryValue> copy(prefs_->DeepCopyWithoutEmptyChildren());
    304   return serializer.Serialize(*(copy.get()));
    305 }
    306