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