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