json_pref_store.cc revision 21d179b334e59e9a3bfcaed4c4430bef1bc5759d
1// Copyright (c) 2010 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/file_util.h" 10#include "base/values.h" 11#include "chrome/common/json_value_serializer.h" 12 13namespace { 14 15// Some extensions we'll tack on to copies of the Preferences files. 16const FilePath::CharType* kBadExtension = FILE_PATH_LITERAL("bad"); 17 18} // namespace 19 20JsonPrefStore::JsonPrefStore(const FilePath& filename, 21 base::MessageLoopProxy* file_message_loop_proxy) 22 : path_(filename), 23 prefs_(new DictionaryValue()), 24 read_only_(false), 25 writer_(filename, file_message_loop_proxy) { 26} 27 28JsonPrefStore::~JsonPrefStore() { 29 if (writer_.HasPendingWrite() && !read_only_) 30 writer_.DoScheduledWrite(); 31} 32 33PrefStore::ReadResult JsonPrefStore::GetValue(const std::string& key, 34 Value** result) const { 35 return prefs_->Get(key, result) ? READ_OK : READ_NO_VALUE; 36} 37 38void JsonPrefStore::AddObserver(PrefStore::Observer* observer) { 39 observers_.AddObserver(observer); 40} 41 42void JsonPrefStore::RemoveObserver(PrefStore::Observer* observer) { 43 observers_.RemoveObserver(observer); 44} 45 46void JsonPrefStore::SetValue(const std::string& key, Value* value) { 47 DCHECK(value); 48 scoped_ptr<Value> new_value(value); 49 Value* old_value = NULL; 50 prefs_->Get(key, &old_value); 51 if (!old_value || !value->Equals(old_value)) { 52 prefs_->Set(key, new_value.release()); 53 FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key)); 54 } 55} 56 57void JsonPrefStore::SetValueSilently(const std::string& key, Value* value) { 58 DCHECK(value); 59 scoped_ptr<Value> new_value(value); 60 Value* old_value = NULL; 61 prefs_->Get(key, &old_value); 62 if (!old_value || !value->Equals(old_value)) 63 prefs_->Set(key, new_value.release()); 64} 65 66void JsonPrefStore::RemoveValue(const std::string& key) { 67 if (prefs_->Remove(key, NULL)) { 68 FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key)); 69 } 70} 71 72bool JsonPrefStore::ReadOnly() const { 73 return read_only_; 74} 75 76PersistentPrefStore::PrefReadError JsonPrefStore::ReadPrefs() { 77 if (path_.empty()) { 78 read_only_ = true; 79 return PREF_READ_ERROR_FILE_NOT_SPECIFIED; 80 } 81 JSONFileValueSerializer serializer(path_); 82 83 int error_code = 0; 84 std::string error_msg; 85 scoped_ptr<Value> value(serializer.Deserialize(&error_code, &error_msg)); 86 if (!value.get()) { 87#if defined(GOOGLE_CHROME_BUILD) 88 // This log could be used for more detailed client-side error diagnosis, 89 // but since this triggers often with unit tests, we need to disable it 90 // in non-official builds. 91 PLOG(ERROR) << "Error reading Preferences: " << error_msg << " " << 92 path_.value(); 93#endif 94 PrefReadError error; 95 switch (error_code) { 96 case JSONFileValueSerializer::JSON_ACCESS_DENIED: 97 // If the file exists but is simply unreadable, put the file into a 98 // state where we don't try to save changes. Otherwise, we could 99 // clobber the existing prefs. 100 error = PREF_READ_ERROR_ACCESS_DENIED; 101 read_only_ = true; 102 break; 103 case JSONFileValueSerializer::JSON_CANNOT_READ_FILE: 104 error = PREF_READ_ERROR_FILE_OTHER; 105 read_only_ = true; 106 break; 107 case JSONFileValueSerializer::JSON_FILE_LOCKED: 108 error = PREF_READ_ERROR_FILE_LOCKED; 109 read_only_ = true; 110 break; 111 case JSONFileValueSerializer::JSON_NO_SUCH_FILE: 112 // If the file just doesn't exist, maybe this is first run. In any case 113 // there's no harm in writing out default prefs in this case. 114 error = PREF_READ_ERROR_NO_FILE; 115 break; 116 default: 117 error = PREF_READ_ERROR_JSON_PARSE; 118 // JSON errors indicate file corruption of some sort. 119 // Since the file is corrupt, move it to the side and continue with 120 // empty preferences. This will result in them losing their settings. 121 // We keep the old file for possible support and debugging assistance 122 // as well as to detect if they're seeing these errors repeatedly. 123 // TODO(erikkay) Instead, use the last known good file. 124 FilePath bad = path_.ReplaceExtension(kBadExtension); 125 126 // If they've ever had a parse error before, put them in another bucket. 127 // TODO(erikkay) if we keep this error checking for very long, we may 128 // want to differentiate between recent and long ago errors. 129 if (file_util::PathExists(bad)) 130 error = PREF_READ_ERROR_JSON_REPEAT; 131 file_util::Move(path_, bad); 132 break; 133 } 134 return error; 135 } 136 137 // Preferences should always have a dictionary root. 138 if (!value->IsType(Value::TYPE_DICTIONARY)) { 139 // See comment for the default case above. 140 read_only_ = true; 141 return PREF_READ_ERROR_JSON_TYPE; 142 } 143 144 prefs_.reset(static_cast<DictionaryValue*>(value.release())); 145 146 return PREF_READ_ERROR_NONE; 147} 148 149bool JsonPrefStore::WritePrefs() { 150 std::string data; 151 if (!SerializeData(&data)) 152 return false; 153 154 // Lie about our ability to save. 155 if (read_only_) 156 return true; 157 158 writer_.WriteNow(data); 159 return true; 160} 161 162void JsonPrefStore::ScheduleWritePrefs() { 163 if (read_only_) 164 return; 165 166 writer_.ScheduleWrite(this); 167} 168 169bool JsonPrefStore::SerializeData(std::string* output) { 170 // TODO(tc): Do we want to prune webkit preferences that match the default 171 // value? 172 JSONStringValueSerializer serializer(output); 173 serializer.set_pretty_print(true); 174 scoped_ptr<DictionaryValue> copy(prefs_->DeepCopyWithoutEmptyChildren()); 175 return serializer.Serialize(*(copy.get())); 176} 177