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