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 "base/prefs/leveldb_pref_store.h" |
| 6 |
| 7 #include <algorithm> |
| 8 #include <set> |
| 9 |
| 10 #include "base/bind.h" |
| 11 #include "base/callback.h" |
| 12 #include "base/file_util.h" |
| 13 #include "base/json/json_file_value_serializer.h" |
| 14 #include "base/json/json_string_value_serializer.h" |
| 15 #include "base/memory/ref_counted.h" |
| 16 #include "base/message_loop/message_loop_proxy.h" |
| 17 #include "base/prefs/pref_filter.h" |
| 18 #include "base/sequenced_task_runner.h" |
| 19 #include "base/threading/sequenced_worker_pool.h" |
| 20 #include "base/values.h" |
| 21 #include "third_party/leveldatabase/src/include/leveldb/db.h" |
| 22 |
| 23 namespace { |
| 24 |
| 25 // Some extensions we'll tack on to copies of the Preferences files. |
| 26 //const base::FilePath::CharType* kBadExtension = FILE_PATH_LITERAL("bad"); |
| 27 |
| 28 } |
| 29 |
| 30 LevelDBPrefStore::LevelDBPrefStore( |
| 31 const base::FilePath& filename, |
| 32 base::SequencedTaskRunner* sequenced_task_runner, |
| 33 scoped_ptr<PrefFilter> pref_filter) |
| 34 : path_(filename), |
| 35 sequenced_task_runner_(sequenced_task_runner), |
| 36 original_task_runner_(base::MessageLoopProxy::current()), |
| 37 read_only_(false), |
| 38 pref_filter_(pref_filter.Pass()), |
| 39 initialized_(false), |
| 40 read_error_(PREF_READ_ERROR_OTHER) {} |
| 41 |
| 42 bool LevelDBPrefStore::GetValue(const std::string& key, |
| 43 const base::Value** result) const { |
| 44 LOG(ERROR) << "Answering call to GetValue(" << key << ")"; |
| 45 const base::Value* tmp = NULL; |
| 46 if (!prefs_.GetValue(key, &tmp)) { |
| 47 return false; |
| 48 } |
| 49 |
| 50 if (result) |
| 51 *result = tmp; |
| 52 return true; |
| 53 } |
| 54 |
| 55 // Callers of GetMutableValue have to also call ReportValueChanged. |
| 56 bool LevelDBPrefStore::GetMutableValue(const std::string& key, |
| 57 base::Value** result) { |
| 58 return prefs_.GetValue(key, result); |
| 59 } |
| 60 |
| 61 void LevelDBPrefStore::AddObserver(PrefStore::Observer* observer) { |
| 62 observers_.AddObserver(observer); |
| 63 } |
| 64 |
| 65 void LevelDBPrefStore::RemoveObserver(PrefStore::Observer* observer) { |
| 66 observers_.RemoveObserver(observer); |
| 67 } |
| 68 |
| 69 bool LevelDBPrefStore::HasObservers() const { |
| 70 return observers_.might_have_observers(); |
| 71 } |
| 72 |
| 73 bool LevelDBPrefStore::IsInitializationComplete() const { return initialized_; } |
| 74 |
| 75 void LevelDBPrefStore::PersistOnFileThread(const std::string& key, |
| 76 const std::string& value) { |
| 77 DCHECK(sequenced_task_runner_->RunsTasksOnCurrentThread()); |
| 78 |
| 79 leveldb::Status status = db_->Put(leveldb::WriteOptions(), key, value); |
| 80 // DCHECK is fine; the corresponding error is ignored in json_pref_store. |
| 81 // But change to DLOG_IF or something, not DCHECK. |
| 82 DCHECK(status.ok()); |
| 83 } |
| 84 |
| 85 void LevelDBPrefStore::RemoveOnFileThread(const std::string& key) { |
| 86 DCHECK(sequenced_task_runner_->RunsTasksOnCurrentThread()); |
| 87 |
| 88 leveldb::Status status = db_->Delete(leveldb::WriteOptions(), key); |
| 89 // DCHECK is fine; the corresponding error is ignored in json_pref_store. |
| 90 // But change to DLOG_IF or something, not DCHECK. |
| 91 DCHECK(status.ok()); |
| 92 } |
| 93 |
| 94 void LevelDBPrefStore::RemoveFromUIThread(const std::string& key) { |
| 95 if (read_only_) |
| 96 return; |
| 97 sequenced_task_runner_->PostTask( |
| 98 FROM_HERE, |
| 99 base::Bind( |
| 100 &LevelDBPrefStore::RemoveOnFileThread, this, key)); |
| 101 } |
| 102 |
| 103 void LevelDBPrefStore::PersistFromUIThread(const std::string& key, |
| 104 base::Value* value) { |
| 105 if (read_only_) |
| 106 return; |
| 107 std::string value_string; |
| 108 JSONStringValueSerializer serializer(&value_string); |
| 109 DCHECK(serializer.Serialize(*value)); |
| 110 |
| 111 sequenced_task_runner_->PostTask( |
| 112 FROM_HERE, |
| 113 base::Bind( |
| 114 &LevelDBPrefStore::PersistOnFileThread, this, key, value_string)); |
| 115 } |
| 116 |
| 117 void LevelDBPrefStore::SetValue(const std::string& key, base::Value* value) { |
| 118 DCHECK(value); |
| 119 scoped_ptr<base::Value> new_value(value); |
| 120 base::Value* old_value = NULL; |
| 121 bool found = prefs_.GetValue(key, &old_value); |
| 122 if (!found || !value->Equals(old_value)) { |
| 123 PersistFromUIThread(key, new_value.get()); |
| 124 prefs_.SetValue(key, new_value.release()); |
| 125 NotifyObservers(key); |
| 126 } |
| 127 } |
| 128 |
| 129 void LevelDBPrefStore::SetValueSilently(const std::string& key, |
| 130 base::Value* value) { |
| 131 DCHECK(value); |
| 132 scoped_ptr<base::Value> new_value(value); |
| 133 base::Value* old_value = NULL; |
| 134 prefs_.GetValue(key, &old_value); |
| 135 if (!old_value || !value->Equals(old_value)) { |
| 136 PersistFromUIThread(key, new_value.get()); |
| 137 prefs_.SetValue(key, new_value.release()); |
| 138 } |
| 139 } |
| 140 |
| 141 void LevelDBPrefStore::RemoveValue(const std::string& key) { |
| 142 if (prefs_.RemoveValue(key)) { |
| 143 RemoveFromUIThread(key); |
| 144 NotifyObservers(key); |
| 145 } |
| 146 } |
| 147 |
| 148 bool LevelDBPrefStore::ReadOnly() const { return read_only_; } |
| 149 |
| 150 PersistentPrefStore::PrefReadError LevelDBPrefStore::GetReadError() const { |
| 151 return read_error_; |
| 152 } |
| 153 |
| 154 PersistentPrefStore::PrefReadError LevelDBPrefStore::ReadPrefs() { |
| 155 DCHECK(!db_); |
| 156 if (path_.empty()) { |
| 157 OnStorageRead(PREF_READ_ERROR_FILE_NOT_SPECIFIED, false); |
| 158 return PREF_READ_ERROR_FILE_NOT_SPECIFIED; |
| 159 } |
| 160 |
| 161 bool no_dir = !base::PathExists(path_.DirName()); |
| 162 |
| 163 leveldb::DB* db; |
| 164 leveldb::Options options; |
| 165 options.create_if_missing = true; |
| 166 leveldb::Status status = |
| 167 leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db); |
| 168 if (!status.ok()) { |
| 169 // TODO(dgrogan): Add error histogram and call RepairDB. |
| 170 OnStorageRead(PREF_READ_ERROR_FILE_OTHER, no_dir); |
| 171 return PREF_READ_ERROR_FILE_OTHER; |
| 172 } |
| 173 db_.reset(db); |
| 174 |
| 175 scoped_ptr<leveldb::Iterator> it(db->NewIterator(leveldb::ReadOptions())); |
| 176 for (it->SeekToFirst(); it->Valid(); it->Next()) { |
| 177 const std::string value_string = it->value().ToString(); |
| 178 LOG(ERROR) << "Reading: got " << it->key().ToString() << " -> " |
| 179 << it->value().ToString(); |
| 180 JSONStringValueSerializer deserializer(value_string); |
| 181 std::string error_message; |
| 182 int error_code; |
| 183 base::Value* json_value = |
| 184 deserializer.Deserialize(&error_code, &error_message); |
| 185 DCHECK(json_value) << error_message; |
| 186 // TODO(dgrogan): Histogram number of corrupt values? |
| 187 prefs_.SetValue(it->key().ToString(), json_value); |
| 188 } |
| 189 |
| 190 PersistentPrefStore::PrefReadError error = PREF_READ_ERROR_NONE; |
| 191 if (!it->status().ok()) { |
| 192 // TODO(dgrogan): Add error histogram. |
| 193 error = PREF_READ_ERROR_FILE_OTHER; |
| 194 } |
| 195 OnStorageRead(error, no_dir); |
| 196 return error; |
| 197 } |
| 198 |
| 199 void LevelDBPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate) { |
| 200 DCHECK_EQ(false, initialized_); |
| 201 error_delegate_.reset(error_delegate); |
| 202 if (path_.empty()) { |
| 203 OnStorageRead(PREF_READ_ERROR_FILE_NOT_SPECIFIED, false); |
| 204 return; |
| 205 } |
| 206 |
| 207 sequenced_task_runner_->PostTask(FROM_HERE, base::Bind( |
| 208 base::IgnoreResult(&LevelDBPrefStore::ReadPrefs), this)); |
| 209 } |
| 210 |
| 211 void LevelDBPrefStore::CommitPendingWrite() { |
| 212 // No-op, key-value pairs are committed as they are set. |
| 213 // TODO(dgrogan): This won't perform well because the extensions prefs are |
| 214 // changed frequently. We'll have to track a set of keys that have changed |
| 215 // and write them to disk every 10 seconds. |
| 216 } |
| 217 |
| 218 void LevelDBPrefStore::ReportValueChanged(const std::string& key) { |
| 219 base::Value* new_value = NULL; |
| 220 DCHECK(prefs_.GetValue(key, &new_value)); |
| 221 if (pref_filter_) |
| 222 pref_filter_->FilterUpdate(key); |
| 223 PersistFromUIThread(key, new_value); |
| 224 NotifyObservers(key); |
| 225 } |
| 226 |
| 227 void LevelDBPrefStore::NotifyObservers(const std::string& key) { |
| 228 FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key)); |
| 229 } |
| 230 |
| 231 void LevelDBPrefStore::OnStorageRead(PersistentPrefStore::PrefReadError error, |
| 232 bool no_dir) { |
| 233 if (!original_task_runner_->RunsTasksOnCurrentThread()) { |
| 234 // When db is opened async, this function needs to be called on original |
| 235 // thread. |
| 236 original_task_runner_->PostTask(FROM_HERE, |
| 237 base::Bind(&LevelDBPrefStore::OnStorageRead, this, error, no_dir)); |
| 238 } |
| 239 read_error_ = error; |
| 240 |
| 241 if (no_dir) { |
| 242 FOR_EACH_OBSERVER( |
| 243 PrefStore::Observer, observers_, OnInitializationCompleted(false)); |
| 244 return; |
| 245 } |
| 246 |
| 247 initialized_ = true; |
| 248 |
| 249 switch (error) { |
| 250 case PREF_READ_ERROR_ACCESS_DENIED: |
| 251 case PREF_READ_ERROR_FILE_OTHER: |
| 252 case PREF_READ_ERROR_FILE_LOCKED: |
| 253 case PREF_READ_ERROR_JSON_TYPE: |
| 254 case PREF_READ_ERROR_FILE_NOT_SPECIFIED: |
| 255 read_only_ = true; |
| 256 break; |
| 257 case PREF_READ_ERROR_NONE: |
| 258 break; |
| 259 case PREF_READ_ERROR_NO_FILE: |
| 260 // If the file just doesn't exist, maybe this is first run. In any case |
| 261 // there's no harm in writing out default prefs in this case. |
| 262 break; |
| 263 case PREF_READ_ERROR_JSON_PARSE: |
| 264 case PREF_READ_ERROR_JSON_REPEAT: |
| 265 break; |
| 266 default: |
| 267 NOTREACHED() << "Unknown error: " << error; |
| 268 } |
| 269 |
| 270 // TODO(dgrogan): Call pref_filter_->FilterOnLoad |
| 271 |
| 272 if (error_delegate_.get() && error != PREF_READ_ERROR_NONE) |
| 273 error_delegate_->OnError(error); |
| 274 |
| 275 FOR_EACH_OBSERVER( |
| 276 PrefStore::Observer, observers_, OnInitializationCompleted(true)); |
| 277 } |
| 278 |
| 279 LevelDBPrefStore::~LevelDBPrefStore() {} |
OLD | NEW |