Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / prefs / leveldb_pref_store.cc
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.
4
5 #include "chrome/browser/prefs/leveldb_pref_store.h"
6
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"
21
22 namespace {
23
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,
34 };
35
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;
47 }
48
49 } // namespace
50
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;
57 };
58
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);
74     }
75
76     for (std::set<std::string>::iterator iter = keys_to_delete->begin();
77          iter != keys_to_delete->end();
78          iter++) {
79       batch.Delete(*iter);
80     }
81
82     leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch);
83
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();
88   }
89
90  private:
91   scoped_ptr<leveldb::DB> db_;
92   DISALLOW_COPY_AND_ASSIGN(FileThreadSerializer);
93 };
94
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;
100   }
101   return true;
102 }
103
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;
118     }
119     if (leveldb_env::IsIOError(status)) {
120       reading_results->error |= IO_ERROR;
121       break;
122     }
123     if (reading_results->error & DESTROYED)
124       break;
125
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;
132       }
133       reading_results->error |= REPAIR_FAILED;
134     }
135     if (!MoveDirectoryAside(path)) {
136       status = leveldb::DestroyDB(path.AsUTF8Unsafe(), options);
137       if (!status.ok()) {
138         reading_results->error |= DESTROY_FAILED;
139         break;
140       }
141     }
142     reading_results->error |= DESTROYED;
143   }
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));
148 }
149
150 /* static */
151 scoped_ptr<LevelDBPrefStore::ReadingResults> LevelDBPrefStore::DoReading(
152     const base::FilePath& path) {
153   base::ThreadRestrictions::AssertIOAllowed();
154
155   scoped_ptr<ReadingResults> reading_results(new ReadingResults);
156
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();
162   }
163
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;
182     }
183   }
184
185   if (!it->status().ok())
186     reading_results->error |= ITER_NOT_OK;
187
188   return reading_results.Pass();
189 }
190
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) {}
201
202 LevelDBPrefStore::~LevelDBPrefStore() {
203   CommitPendingWrite();
204   sequenced_task_runner_->DeleteSoon(FROM_HERE, serializer_.release());
205 }
206
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;
213   }
214
215   if (result)
216     *result = tmp;
217   return true;
218 }
219
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);
225 }
226
227 void LevelDBPrefStore::AddObserver(PrefStore::Observer* observer) {
228   observers_.AddObserver(observer);
229 }
230
231 void LevelDBPrefStore::RemoveObserver(PrefStore::Observer* observer) {
232   observers_.RemoveObserver(observer);
233 }
234
235 bool LevelDBPrefStore::HasObservers() const {
236   return observers_.might_have_observers();
237 }
238
239 bool LevelDBPrefStore::IsInitializationComplete() const { return initialized_; }
240
241 void LevelDBPrefStore::PersistFromUIThread() {
242   if (read_only_)
243     return;
244   DCHECK(serializer_);
245
246   scoped_ptr<std::set<std::string> > keys_to_delete(new std::set<std::string>);
247   keys_to_delete->swap(keys_to_delete_);
248
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_);
252
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())));
259 }
260
261 void LevelDBPrefStore::ScheduleWrite() {
262   if (!timer_.IsRunning()) {
263     timer_.Start(FROM_HERE,
264                  base::TimeDelta::FromSeconds(10),
265                  this,
266                  &LevelDBPrefStore::PersistFromUIThread);
267   }
268 }
269
270 void LevelDBPrefStore::SetValue(const std::string& key, base::Value* value) {
271   SetValueInternal(key, value, true /*notify*/);
272 }
273
274 void LevelDBPrefStore::SetValueSilently(const std::string& key,
275                                         base::Value* value) {
276   SetValueInternal(key, value, false /*notify*/);
277 }
278
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;
285 }
286
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);
301   }
302 }
303
304 void LevelDBPrefStore::RemoveValue(const std::string& key) {
305   DCHECK(initialized_);
306   if (prefs_.RemoveValue(key)) {
307     MarkForDeletion(key);
308     NotifyObservers(key);
309   }
310 }
311
312 bool LevelDBPrefStore::ReadOnly() const { return read_only_; }
313
314 PersistentPrefStore::PrefReadError LevelDBPrefStore::GetReadError() const {
315   return read_error_;
316 }
317
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_);
326   }
327
328   PrefReadError error = IntToPrefReadError(reading_results->error);
329   OnStorageRead(reading_results.Pass());
330   return error;
331 }
332
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;
341   }
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()));
347 }
348
349 void LevelDBPrefStore::CommitPendingWrite() {
350   if (timer_.IsRunning()) {
351     timer_.Stop();
352     PersistFromUIThread();
353   }
354 }
355
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();
364 }
365
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();
375 }
376
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);
384 }
385
386 void LevelDBPrefStore::NotifyObservers(const std::string& key) {
387   FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key));
388 }
389
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);
395
396   if (reading_results->no_dir) {
397     FOR_EACH_OBSERVER(
398         PrefStore::Observer, observers_, OnInitializationCompleted(false));
399     return;
400   }
401
402   initialized_ = true;
403
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;
410   }
411
412   // TODO(dgrogan): Call pref_filter_->FilterOnLoad
413
414   if (error_delegate_.get() && read_error_ != PREF_READ_ERROR_NONE)
415     error_delegate_->OnError(read_error_);
416
417   FOR_EACH_OBSERVER(
418       PrefStore::Observer, observers_, OnInitializationCompleted(true));
419 }