Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / value_store / leveldb_value_store.cc
blob043bcb3d1915f4e3a316f871d9ff06f47aa8f1cb
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.
5 #include "chrome/browser/value_store/leveldb_value_store.h"
7 #include "base/file_util.h"
8 #include "base/json/json_reader.h"
9 #include "base/json/json_writer.h"
10 #include "base/logging.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/strings/sys_string_conversions.h"
14 #include "chrome/browser/value_store/value_store_util.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "third_party/leveldatabase/src/include/leveldb/iterator.h"
17 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
19 namespace util = value_store_util;
20 using content::BrowserThread;
22 namespace {
24 const char kInvalidJson[] = "Invalid JSON";
26 // Scoped leveldb snapshot which releases the snapshot on destruction.
27 class ScopedSnapshot {
28 public:
29 explicit ScopedSnapshot(leveldb::DB* db)
30 : db_(db), snapshot_(db->GetSnapshot()) {}
32 ~ScopedSnapshot() {
33 db_->ReleaseSnapshot(snapshot_);
36 const leveldb::Snapshot* get() {
37 return snapshot_;
40 private:
41 leveldb::DB* db_;
42 const leveldb::Snapshot* snapshot_;
44 DISALLOW_COPY_AND_ASSIGN(ScopedSnapshot);
47 } // namespace
49 LeveldbValueStore::LeveldbValueStore(const base::FilePath& db_path)
50 : db_path_(db_path) {
51 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
53 scoped_ptr<Error> open_error = EnsureDbIsOpen();
54 if (open_error)
55 LOG(WARNING) << open_error->message;
58 LeveldbValueStore::~LeveldbValueStore() {
59 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
61 // Delete the database from disk if it's empty (but only if we managed to
62 // open it!). This is safe on destruction, assuming that we have exclusive
63 // access to the database.
64 if (db_ && IsEmpty())
65 DeleteDbFile();
68 size_t LeveldbValueStore::GetBytesInUse(const std::string& key) {
69 // Let SettingsStorageQuotaEnforcer implement this.
70 NOTREACHED() << "Not implemented";
71 return 0;
74 size_t LeveldbValueStore::GetBytesInUse(
75 const std::vector<std::string>& keys) {
76 // Let SettingsStorageQuotaEnforcer implement this.
77 NOTREACHED() << "Not implemented";
78 return 0;
81 size_t LeveldbValueStore::GetBytesInUse() {
82 // Let SettingsStorageQuotaEnforcer implement this.
83 NOTREACHED() << "Not implemented";
84 return 0;
87 ValueStore::ReadResult LeveldbValueStore::Get(const std::string& key) {
88 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
90 scoped_ptr<Error> open_error = EnsureDbIsOpen();
91 if (open_error)
92 return MakeReadResult(open_error.Pass());
94 scoped_ptr<base::Value> setting;
95 scoped_ptr<Error> error = ReadFromDb(leveldb::ReadOptions(), key, &setting);
96 if (error)
97 return MakeReadResult(error.Pass());
99 base::DictionaryValue* settings = new base::DictionaryValue();
100 if (setting)
101 settings->SetWithoutPathExpansion(key, setting.release());
102 return MakeReadResult(make_scoped_ptr(settings));
105 ValueStore::ReadResult LeveldbValueStore::Get(
106 const std::vector<std::string>& keys) {
107 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
109 scoped_ptr<Error> open_error = EnsureDbIsOpen();
110 if (open_error)
111 return MakeReadResult(open_error.Pass());
113 leveldb::ReadOptions options;
114 scoped_ptr<base::DictionaryValue> settings(new base::DictionaryValue());
116 // All interaction with the db is done on the same thread, so snapshotting
117 // isn't strictly necessary. This is just defensive.
118 ScopedSnapshot snapshot(db_.get());
119 options.snapshot = snapshot.get();
120 for (std::vector<std::string>::const_iterator it = keys.begin();
121 it != keys.end(); ++it) {
122 scoped_ptr<base::Value> setting;
123 scoped_ptr<Error> error = ReadFromDb(options, *it, &setting);
124 if (error)
125 return MakeReadResult(error.Pass());
126 if (setting)
127 settings->SetWithoutPathExpansion(*it, setting.release());
130 return MakeReadResult(settings.Pass());
133 ValueStore::ReadResult LeveldbValueStore::Get() {
134 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
136 scoped_ptr<Error> open_error = EnsureDbIsOpen();
137 if (open_error)
138 return MakeReadResult(open_error.Pass());
140 base::JSONReader json_reader;
141 leveldb::ReadOptions options = leveldb::ReadOptions();
142 // All interaction with the db is done on the same thread, so snapshotting
143 // isn't strictly necessary. This is just defensive.
144 scoped_ptr<base::DictionaryValue> settings(new base::DictionaryValue());
146 ScopedSnapshot snapshot(db_.get());
147 options.snapshot = snapshot.get();
148 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(options));
149 for (it->SeekToFirst(); it->Valid(); it->Next()) {
150 std::string key = it->key().ToString();
151 base::Value* value = json_reader.ReadToValue(it->value().ToString());
152 if (!value) {
153 return MakeReadResult(
154 Error::Create(CORRUPTION, kInvalidJson, util::NewKey(key)));
156 settings->SetWithoutPathExpansion(key, value);
159 if (it->status().IsNotFound()) {
160 NOTREACHED() << "IsNotFound() but iterating over all keys?!";
161 return MakeReadResult(settings.Pass());
164 if (!it->status().ok())
165 return MakeReadResult(ToValueStoreError(it->status(), util::NoKey()));
167 return MakeReadResult(settings.Pass());
170 ValueStore::WriteResult LeveldbValueStore::Set(
171 WriteOptions options, const std::string& key, const base::Value& value) {
172 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
174 scoped_ptr<Error> open_error = EnsureDbIsOpen();
175 if (open_error)
176 return MakeWriteResult(open_error.Pass());
178 leveldb::WriteBatch batch;
179 scoped_ptr<ValueStoreChangeList> changes(new ValueStoreChangeList());
180 scoped_ptr<Error> batch_error =
181 AddToBatch(options, key, value, &batch, changes.get());
182 if (batch_error)
183 return MakeWriteResult(batch_error.Pass());
185 scoped_ptr<Error> write_error = WriteToDb(&batch);
186 return write_error ? MakeWriteResult(write_error.Pass())
187 : MakeWriteResult(changes.Pass());
190 ValueStore::WriteResult LeveldbValueStore::Set(
191 WriteOptions options, const base::DictionaryValue& settings) {
192 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
194 scoped_ptr<Error> open_error = EnsureDbIsOpen();
195 if (open_error)
196 return MakeWriteResult(open_error.Pass());
198 leveldb::WriteBatch batch;
199 scoped_ptr<ValueStoreChangeList> changes(new ValueStoreChangeList());
201 for (base::DictionaryValue::Iterator it(settings);
202 !it.IsAtEnd(); it.Advance()) {
203 scoped_ptr<Error> batch_error =
204 AddToBatch(options, it.key(), it.value(), &batch, changes.get());
205 if (batch_error)
206 return MakeWriteResult(batch_error.Pass());
209 scoped_ptr<Error> write_error = WriteToDb(&batch);
210 return write_error ? MakeWriteResult(write_error.Pass())
211 : MakeWriteResult(changes.Pass());
214 ValueStore::WriteResult LeveldbValueStore::Remove(const std::string& key) {
215 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
216 return Remove(std::vector<std::string>(1, key));
219 ValueStore::WriteResult LeveldbValueStore::Remove(
220 const std::vector<std::string>& keys) {
221 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
223 scoped_ptr<Error> open_error = EnsureDbIsOpen();
224 if (open_error)
225 return MakeWriteResult(open_error.Pass());
227 leveldb::WriteBatch batch;
228 scoped_ptr<ValueStoreChangeList> changes(new ValueStoreChangeList());
230 for (std::vector<std::string>::const_iterator it = keys.begin();
231 it != keys.end(); ++it) {
232 scoped_ptr<base::Value> old_value;
233 scoped_ptr<Error> read_error =
234 ReadFromDb(leveldb::ReadOptions(), *it, &old_value);
235 if (read_error)
236 return MakeWriteResult(read_error.Pass());
238 if (old_value) {
239 changes->push_back(ValueStoreChange(*it, old_value.release(), NULL));
240 batch.Delete(*it);
244 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch);
245 if (!status.ok() && !status.IsNotFound())
246 return MakeWriteResult(ToValueStoreError(status, util::NoKey()));
247 return MakeWriteResult(changes.Pass());
250 ValueStore::WriteResult LeveldbValueStore::Clear() {
251 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
253 scoped_ptr<ValueStoreChangeList> changes(new ValueStoreChangeList());
255 ReadResult read_result = Get();
256 if (read_result->HasError())
257 return MakeWriteResult(read_result->PassError());
259 base::DictionaryValue& whole_db = read_result->settings();
260 while (!whole_db.empty()) {
261 std::string next_key = base::DictionaryValue::Iterator(whole_db).key();
262 scoped_ptr<base::Value> next_value;
263 whole_db.RemoveWithoutPathExpansion(next_key, &next_value);
264 changes->push_back(
265 ValueStoreChange(next_key, next_value.release(), NULL));
268 DeleteDbFile();
269 return MakeWriteResult(changes.Pass());
272 scoped_ptr<ValueStore::Error> LeveldbValueStore::EnsureDbIsOpen() {
273 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
275 if (db_)
276 return util::NoError();
278 leveldb::Options options;
279 options.max_open_files = 0; // Use minimum.
280 options.create_if_missing = true;
282 leveldb::DB* db = NULL;
283 leveldb::Status status =
284 leveldb::DB::Open(options, db_path_.AsUTF8Unsafe(), &db);
285 if (!status.ok())
286 return ToValueStoreError(status, util::NoKey());
288 CHECK(db);
289 db_.reset(db);
290 return util::NoError();
293 scoped_ptr<ValueStore::Error> LeveldbValueStore::ReadFromDb(
294 leveldb::ReadOptions options,
295 const std::string& key,
296 scoped_ptr<base::Value>* setting) {
297 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
298 DCHECK(setting);
300 std::string value_as_json;
301 leveldb::Status s = db_->Get(options, key, &value_as_json);
303 if (s.IsNotFound()) {
304 // Despite there being no value, it was still a success. Check this first
305 // because ok() is false on IsNotFound.
306 return util::NoError();
309 if (!s.ok())
310 return ToValueStoreError(s, util::NewKey(key));
312 base::Value* value = base::JSONReader().ReadToValue(value_as_json);
313 if (!value)
314 return Error::Create(CORRUPTION, kInvalidJson, util::NewKey(key));
316 setting->reset(value);
317 return util::NoError();
320 scoped_ptr<ValueStore::Error> LeveldbValueStore::AddToBatch(
321 ValueStore::WriteOptions options,
322 const std::string& key,
323 const base::Value& value,
324 leveldb::WriteBatch* batch,
325 ValueStoreChangeList* changes) {
326 bool write_new_value = true;
328 if (!(options & NO_GENERATE_CHANGES)) {
329 scoped_ptr<base::Value> old_value;
330 scoped_ptr<Error> read_error =
331 ReadFromDb(leveldb::ReadOptions(), key, &old_value);
332 if (read_error)
333 return read_error.Pass();
334 if (!old_value || !old_value->Equals(&value)) {
335 changes->push_back(
336 ValueStoreChange(key, old_value.release(), value.DeepCopy()));
337 } else {
338 write_new_value = false;
342 if (write_new_value) {
343 std::string value_as_json;
344 base::JSONWriter::Write(&value, &value_as_json);
345 batch->Put(key, value_as_json);
348 return util::NoError();
351 scoped_ptr<ValueStore::Error> LeveldbValueStore::WriteToDb(
352 leveldb::WriteBatch* batch) {
353 leveldb::Status status = db_->Write(leveldb::WriteOptions(), batch);
354 return status.ok() ? util::NoError()
355 : ToValueStoreError(status, util::NoKey());
358 bool LeveldbValueStore::IsEmpty() {
359 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
360 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions()));
362 it->SeekToFirst();
363 bool is_empty = !it->Valid();
364 if (!it->status().ok()) {
365 LOG(ERROR) << "Checking DB emptiness failed: " << it->status().ToString();
366 return false;
368 return is_empty;
371 void LeveldbValueStore::DeleteDbFile() {
372 db_.reset(); // release any lock on the directory
373 if (!base::DeleteFile(db_path_, true /* recursive */)) {
374 LOG(WARNING) << "Failed to delete LeveldbValueStore database at " <<
375 db_path_.value();
379 scoped_ptr<ValueStore::Error> LeveldbValueStore::ToValueStoreError(
380 const leveldb::Status& status,
381 scoped_ptr<std::string> key) {
382 CHECK(!status.ok());
383 CHECK(!status.IsNotFound()); // not an error
385 std::string message = status.ToString();
386 // The message may contain |db_path_|, which may be considered sensitive
387 // data, and those strings are passed to the extension, so strip it out.
388 ReplaceSubstringsAfterOffset(&message, 0u, db_path_.AsUTF8Unsafe(), "...");
390 return Error::Create(CORRUPTION, message, key.Pass());