Add GCMChannelStatusSyncer to schedule requests and enable/disable GCM
[chromium-blink-merge.git] / chrome / browser / prefs / leveldb_pref_store.cc
blob7409f957a7252106997fa36e9a627deb94069043
1 // Copyright 2014 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.
5 #include "chrome/browser/prefs/leveldb_pref_store.h"
7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "base/files/file_util.h"
10 #include "base/json/json_string_value_serializer.h"
11 #include "base/location.h"
12 #include "base/metrics/sparse_histogram.h"
13 #include "base/sequenced_task_runner.h"
14 #include "base/task_runner_util.h"
15 #include "base/threading/thread_restrictions.h"
16 #include "base/time/time.h"
17 #include "base/values.h"
18 #include "third_party/leveldatabase/env_chromium.h"
19 #include "third_party/leveldatabase/src/include/leveldb/db.h"
20 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
22 namespace {
24 enum ErrorMasks {
25 OPENED = 1 << 0,
26 DESTROYED = 1 << 1,
27 REPAIRED = 1 << 2,
28 DESTROY_FAILED = 1 << 3,
29 REPAIR_FAILED = 1 << 4,
30 IO_ERROR = 1 << 5,
31 DATA_LOST = 1 << 6,
32 ITER_NOT_OK = 1 << 7,
33 FILE_NOT_SPECIFIED = 1 << 8,
36 PersistentPrefStore::PrefReadError IntToPrefReadError(int error) {
37 DCHECK(error);
38 if (error == FILE_NOT_SPECIFIED)
39 return PersistentPrefStore::PREF_READ_ERROR_FILE_NOT_SPECIFIED;
40 if (error == OPENED)
41 return PersistentPrefStore::PREF_READ_ERROR_NONE;
42 if (error & IO_ERROR)
43 return PersistentPrefStore::PREF_READ_ERROR_LEVELDB_IO;
44 if (error & OPENED)
45 return PersistentPrefStore::PREF_READ_ERROR_LEVELDB_CORRUPTION;
46 return PersistentPrefStore::PREF_READ_ERROR_LEVELDB_CORRUPTION_READ_ONLY;
49 } // namespace
51 struct LevelDBPrefStore::ReadingResults {
52 ReadingResults() : no_dir(true), error(0) {}
53 bool no_dir;
54 scoped_ptr<leveldb::DB> db;
55 scoped_ptr<PrefValueMap> value_map;
56 int error;
59 // An instance of this class is created on the UI thread but is used
60 // exclusively on the FILE thread.
61 class LevelDBPrefStore::FileThreadSerializer {
62 public:
63 explicit FileThreadSerializer(scoped_ptr<leveldb::DB> db) : db_(db.Pass()) {}
64 void WriteToDatabase(
65 std::map<std::string, std::string>* keys_to_set,
66 std::set<std::string>* keys_to_delete) {
67 DCHECK(keys_to_set->size() > 0 || keys_to_delete->size() > 0);
68 leveldb::WriteBatch batch;
69 for (std::map<std::string, std::string>::iterator iter =
70 keys_to_set->begin();
71 iter != keys_to_set->end();
72 iter++) {
73 batch.Put(iter->first, iter->second);
76 for (std::set<std::string>::iterator iter = keys_to_delete->begin();
77 iter != keys_to_delete->end();
78 iter++) {
79 batch.Delete(*iter);
82 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch);
84 // DCHECK is fine; the corresponding error is ignored in JsonPrefStore.
85 // There's also no API available to surface the error back up to the caller.
86 // TODO(dgrogan): UMA?
87 DCHECK(status.ok()) << status.ToString();
90 private:
91 scoped_ptr<leveldb::DB> db_;
92 DISALLOW_COPY_AND_ASSIGN(FileThreadSerializer);
95 bool MoveDirectoryAside(const base::FilePath& path) {
96 base::FilePath bad_path = path.AppendASCII(".bad");
97 if (!base::Move(path, bad_path)) {
98 base::DeleteFile(bad_path, true);
99 return false;
101 return true;
104 /* static */
105 void LevelDBPrefStore::OpenDB(const base::FilePath& path,
106 ReadingResults* reading_results) {
107 DCHECK_EQ(0, reading_results->error);
108 leveldb::Options options;
109 options.create_if_missing = true;
110 leveldb::DB* db;
111 while (1) {
112 leveldb::Status status =
113 leveldb::DB::Open(options, path.AsUTF8Unsafe(), &db);
114 if (status.ok()) {
115 reading_results->db.reset(db);
116 reading_results->error |= OPENED;
117 break;
119 if (leveldb_env::IsIOError(status)) {
120 reading_results->error |= IO_ERROR;
121 break;
123 if (reading_results->error & DESTROYED)
124 break;
126 DCHECK(!(reading_results->error & REPAIR_FAILED));
127 if (!(reading_results->error & REPAIRED)) {
128 status = leveldb::RepairDB(path.AsUTF8Unsafe(), options);
129 if (status.ok()) {
130 reading_results->error |= REPAIRED;
131 continue;
133 reading_results->error |= REPAIR_FAILED;
135 if (!MoveDirectoryAside(path)) {
136 status = leveldb::DestroyDB(path.AsUTF8Unsafe(), options);
137 if (!status.ok()) {
138 reading_results->error |= DESTROY_FAILED;
139 break;
142 reading_results->error |= DESTROYED;
144 DCHECK(reading_results->error);
145 DCHECK(!((reading_results->error & OPENED) &&
146 (reading_results->error & DESTROY_FAILED)));
147 DCHECK(reading_results->error != (REPAIR_FAILED | OPENED));
150 /* static */
151 scoped_ptr<LevelDBPrefStore::ReadingResults> LevelDBPrefStore::DoReading(
152 const base::FilePath& path) {
153 base::ThreadRestrictions::AssertIOAllowed();
155 scoped_ptr<ReadingResults> reading_results(new ReadingResults);
157 reading_results->no_dir = !base::PathExists(path.DirName());
158 OpenDB(path, reading_results.get());
159 if (!reading_results->db) {
160 DCHECK(!(reading_results->error & OPENED));
161 return reading_results.Pass();
164 DCHECK(reading_results->error & OPENED);
165 reading_results->value_map.reset(new PrefValueMap);
166 scoped_ptr<leveldb::Iterator> it(
167 reading_results->db->NewIterator(leveldb::ReadOptions()));
168 // TODO(dgrogan): Is it really necessary to check it->status() each iteration?
169 for (it->SeekToFirst(); it->Valid() && it->status().ok(); it->Next()) {
170 const std::string value_string = it->value().ToString();
171 JSONStringValueSerializer deserializer(value_string);
172 std::string error_message;
173 int error_code;
174 base::Value* json_value =
175 deserializer.Deserialize(&error_code, &error_message);
176 if (json_value) {
177 reading_results->value_map->SetValue(it->key().ToString(), json_value);
178 } else {
179 DLOG(ERROR) << "Invalid json for key " << it->key().ToString()
180 << ": " << error_message;
181 reading_results->error |= DATA_LOST;
185 if (!it->status().ok())
186 reading_results->error |= ITER_NOT_OK;
188 return reading_results.Pass();
191 LevelDBPrefStore::LevelDBPrefStore(
192 const base::FilePath& filename,
193 base::SequencedTaskRunner* sequenced_task_runner)
194 : path_(filename),
195 sequenced_task_runner_(sequenced_task_runner),
196 original_task_runner_(base::MessageLoopProxy::current()),
197 read_only_(false),
198 initialized_(false),
199 read_error_(PREF_READ_ERROR_NONE),
200 weak_ptr_factory_(this) {}
202 LevelDBPrefStore::~LevelDBPrefStore() {
203 CommitPendingWrite();
204 sequenced_task_runner_->DeleteSoon(FROM_HERE, serializer_.release());
207 bool LevelDBPrefStore::GetValue(const std::string& key,
208 const base::Value** result) const {
209 DCHECK(initialized_);
210 const base::Value* tmp = NULL;
211 if (!prefs_.GetValue(key, &tmp)) {
212 return false;
215 if (result)
216 *result = tmp;
217 return true;
220 // Callers of GetMutableValue have to also call ReportValueChanged.
221 bool LevelDBPrefStore::GetMutableValue(const std::string& key,
222 base::Value** result) {
223 DCHECK(initialized_);
224 return prefs_.GetValue(key, result);
227 void LevelDBPrefStore::AddObserver(PrefStore::Observer* observer) {
228 observers_.AddObserver(observer);
231 void LevelDBPrefStore::RemoveObserver(PrefStore::Observer* observer) {
232 observers_.RemoveObserver(observer);
235 bool LevelDBPrefStore::HasObservers() const {
236 return observers_.might_have_observers();
239 bool LevelDBPrefStore::IsInitializationComplete() const { return initialized_; }
241 void LevelDBPrefStore::PersistFromUIThread() {
242 if (read_only_)
243 return;
244 DCHECK(serializer_);
246 scoped_ptr<std::set<std::string> > keys_to_delete(new std::set<std::string>);
247 keys_to_delete->swap(keys_to_delete_);
249 scoped_ptr<std::map<std::string, std::string> > keys_to_set(
250 new std::map<std::string, std::string>);
251 keys_to_set->swap(keys_to_set_);
253 sequenced_task_runner_->PostTask(
254 FROM_HERE,
255 base::Bind(&LevelDBPrefStore::FileThreadSerializer::WriteToDatabase,
256 base::Unretained(serializer_.get()),
257 base::Owned(keys_to_set.release()),
258 base::Owned(keys_to_delete.release())));
261 void LevelDBPrefStore::ScheduleWrite() {
262 if (!timer_.IsRunning()) {
263 timer_.Start(FROM_HERE,
264 base::TimeDelta::FromSeconds(10),
265 this,
266 &LevelDBPrefStore::PersistFromUIThread);
270 void LevelDBPrefStore::SetValue(const std::string& key, base::Value* value) {
271 SetValueInternal(key, value, true /*notify*/);
274 void LevelDBPrefStore::SetValueSilently(const std::string& key,
275 base::Value* value) {
276 SetValueInternal(key, value, false /*notify*/);
279 static std::string Serialize(base::Value* value) {
280 std::string value_string;
281 JSONStringValueSerializer serializer(&value_string);
282 bool serialized_ok = serializer.Serialize(*value);
283 DCHECK(serialized_ok);
284 return value_string;
287 void LevelDBPrefStore::SetValueInternal(const std::string& key,
288 base::Value* value,
289 bool notify) {
290 DCHECK(initialized_);
291 DCHECK(value);
292 scoped_ptr<base::Value> new_value(value);
293 base::Value* old_value = NULL;
294 prefs_.GetValue(key, &old_value);
295 if (!old_value || !value->Equals(old_value)) {
296 std::string value_string = Serialize(value);
297 prefs_.SetValue(key, new_value.release());
298 MarkForInsertion(key, value_string);
299 if (notify)
300 NotifyObservers(key);
304 void LevelDBPrefStore::RemoveValue(const std::string& key) {
305 DCHECK(initialized_);
306 if (prefs_.RemoveValue(key)) {
307 MarkForDeletion(key);
308 NotifyObservers(key);
312 bool LevelDBPrefStore::ReadOnly() const { return read_only_; }
314 PersistentPrefStore::PrefReadError LevelDBPrefStore::GetReadError() const {
315 return read_error_;
318 PersistentPrefStore::PrefReadError LevelDBPrefStore::ReadPrefs() {
319 DCHECK(!initialized_);
320 scoped_ptr<ReadingResults> reading_results;
321 if (path_.empty()) {
322 reading_results.reset(new ReadingResults);
323 reading_results->error = FILE_NOT_SPECIFIED;
324 } else {
325 reading_results = DoReading(path_);
328 PrefReadError error = IntToPrefReadError(reading_results->error);
329 OnStorageRead(reading_results.Pass());
330 return error;
333 void LevelDBPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate) {
334 DCHECK_EQ(false, initialized_);
335 error_delegate_.reset(error_delegate);
336 if (path_.empty()) {
337 scoped_ptr<ReadingResults> reading_results(new ReadingResults);
338 reading_results->error = FILE_NOT_SPECIFIED;
339 OnStorageRead(reading_results.Pass());
340 return;
342 PostTaskAndReplyWithResult(sequenced_task_runner_.get(),
343 FROM_HERE,
344 base::Bind(&LevelDBPrefStore::DoReading, path_),
345 base::Bind(&LevelDBPrefStore::OnStorageRead,
346 weak_ptr_factory_.GetWeakPtr()));
349 void LevelDBPrefStore::CommitPendingWrite() {
350 if (timer_.IsRunning()) {
351 timer_.Stop();
352 PersistFromUIThread();
356 void LevelDBPrefStore::MarkForDeletion(const std::string& key) {
357 if (read_only_)
358 return;
359 keys_to_delete_.insert(key);
360 // Need to erase in case there's a set operation in the same batch that would
361 // clobber this delete.
362 keys_to_set_.erase(key);
363 ScheduleWrite();
366 void LevelDBPrefStore::MarkForInsertion(const std::string& key,
367 const std::string& value) {
368 if (read_only_)
369 return;
370 keys_to_set_[key] = value;
371 // Need to erase in case there's a delete operation in the same batch that
372 // would clobber this set.
373 keys_to_delete_.erase(key);
374 ScheduleWrite();
377 void LevelDBPrefStore::ReportValueChanged(const std::string& key) {
378 base::Value* new_value = NULL;
379 bool contains_value = prefs_.GetValue(key, &new_value);
380 DCHECK(contains_value);
381 std::string value_string = Serialize(new_value);
382 MarkForInsertion(key, value_string);
383 NotifyObservers(key);
386 void LevelDBPrefStore::NotifyObservers(const std::string& key) {
387 FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key));
390 void LevelDBPrefStore::OnStorageRead(
391 scoped_ptr<LevelDBPrefStore::ReadingResults> reading_results) {
392 UMA_HISTOGRAM_SPARSE_SLOWLY("LevelDBPrefStore.ReadErrors",
393 reading_results->error);
394 read_error_ = IntToPrefReadError(reading_results->error);
396 if (reading_results->no_dir) {
397 FOR_EACH_OBSERVER(
398 PrefStore::Observer, observers_, OnInitializationCompleted(false));
399 return;
402 initialized_ = true;
404 if (reading_results->db) {
405 DCHECK(reading_results->value_map);
406 serializer_.reset(new FileThreadSerializer(reading_results->db.Pass()));
407 prefs_.Swap(reading_results->value_map.get());
408 } else {
409 read_only_ = true;
412 // TODO(dgrogan): Call pref_filter_->FilterOnLoad
414 if (error_delegate_.get() && read_error_ != PREF_READ_ERROR_NONE)
415 error_delegate_->OnError(read_error_);
417 FOR_EACH_OBSERVER(
418 PrefStore::Observer, observers_, OnInitializationCompleted(true));