Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(522)

Unified Diff: webkit/dom_storage/session_storage_database.cc

Issue 10176005: Add dom_storage::SessionStorageDatabase. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: . Created 8 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: webkit/dom_storage/session_storage_database.cc
diff --git a/webkit/dom_storage/session_storage_database.cc b/webkit/dom_storage/session_storage_database.cc
new file mode 100644
index 0000000000000000000000000000000000000000..7b54566f671481794d79d7d9dafb6bdb61cfd14b
--- /dev/null
+++ b/webkit/dom_storage/session_storage_database.cc
@@ -0,0 +1,667 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/dom_storage/session_storage_database.h"
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/stringprintf.h"
+#include "base/string_number_conversions.h"
+#include "base/utf_string_conversions.h"
+#include "googleurl/src/gurl.h"
+#include "third_party/leveldatabase/src/include/leveldb/db.h"
+#include "third_party/leveldatabase/src/include/leveldb/iterator.h"
+#include "third_party/leveldatabase/src/include/leveldb/status.h"
+#include "third_party/leveldatabase/src/include/leveldb/options.h"
+#include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
+
+// Layout of the database:
+// | key | value |
+// -----------------------------------------------------------------------
+// | map-1 | 2 (refcount, start of map-1-* keys)|
+// | map-1-a | b (a = b in map 1) |
+// | ... | |
+// | namespace- | dummy (start of namespace-* keys) |
+// | namespace-1 (1 = namespace id) | dummy (start of namespace-1-* keys)|
+// | namespace-1-origin1 | 1 (mapid) |
+// | namespace-1-origin2 | 2 |
+// | namespace-2 | dummy |
+// | namespace-2-origin1 | 1 (shallow copy) |
+// | namespace-2-origin2 | 2 (shallow copy) |
+// | namespace-3 | dummy |
+// | namespace-3-origin1 | 3 (deep copy) |
+// | namespace-3-origin2 | 2 (shallow copy) |
+// | next-namespace-id | 4 |
+// | next-map-id | 4 |
+
+namespace dom_storage {
+
+SessionStorageDatabase::SessionStorageDatabase(const FilePath& file_path)
michaeln 2012/04/25 08:32:12 Might be helpful to distinguish between namespace_
marja 2012/04/25 15:44:27 I tried to separate the db logic better from the h
+ : file_path_(file_path),
+ db_error_(false),
+ is_inconsistent_(false),
+ namespace_offset_(0) { }
+
+SessionStorageDatabase::~SessionStorageDatabase() {
+}
+
+void SessionStorageDatabase::ReadAllValues(int64 namespace_id,
+ const GURL& origin,
+ ValuesMap* result) {
+ // We don't create a database if it doesn't exist. In that case, there is
+ // nothing to be added to the result.
+ if (!LazyOpen(false))
+ return;
+ // Check if there is map for |namespace_id| and |origin|.
+ std::string namespace_key =
+ NamespaceKey(namespace_id, namespace_offset_, origin);
+ std::string map_id;
+ leveldb::Status s = db_->Get(leveldb::ReadOptions(), namespace_key, &map_id);
+ if (s.IsNotFound())
+ return;
+ if (!DatabaseErrorCheck(s.ok()))
+ return;
+ ReadMap(map_id, result, false);
michaeln 2012/04/25 08:32:12 pseudo code noodling... if (!LazyOpen(false)) r
marja 2012/04/25 15:44:27 Done.
+}
+
+bool SessionStorageDatabase::CommitChanges(int64 namespace_id,
+ const GURL& origin,
+ bool clear_all_first,
+ const ValuesMap& changes) {
michaeln 2012/04/25 08:32:12 pseudo code noodling... if (!LazyOpen(true)) re
marja 2012/04/25 15:44:27 This doesn't work, we cannot go and read the ref c
+ // Even if |changes| is empty, we need to write the appropriate placeholders
+ // in the database, so that it can be later shallow-copied succssfully.
+ if (!LazyOpen(true))
+ return false;
+
+ leveldb::WriteBatch batch;
+ // Ensure that the keys "namespace-" "namespace-N" (see the schema above)
+ // exist.
+ batch.Put(NamespacePrefix(), "");
+ batch.Put(NamespaceStartKey(namespace_id, namespace_offset_), "");
michaeln 2012/04/25 08:32:12 would be nice to not overwrite existing entries fo
marja 2012/04/25 15:44:27 Done.
+
+ // Ensure that the next namespace id is up to date.
+ if (!UpdateNextNamespaceId(namespace_id, &batch))
+ return false;
+
+ // Write the data into the map.
+ std::string namespace_key =
+ NamespaceKey(namespace_id, namespace_offset_, origin);
+ std::string map_id;
+ leveldb::Status s;
+ s = db_->Get(leveldb::ReadOptions(), namespace_key, &map_id);
+ if (!DatabaseErrorCheck(s.ok() || s.IsNotFound()))
+ return false;
+
+ if (s.ok()) {
+ // We shouldn't write data to a shallow copy.
michaeln 2012/04/25 08:32:12 should we dcheck this condition instead?
marja 2012/04/25 15:44:27 ConsistencyCheck DCHECKs. ConsistencyCheck is the
+ int64 ref_count;
+ if (!GetRefCount(MapRefCountKey(map_id), &ref_count))
+ return false;
+ if (!ConsistencyCheck(ref_count == 1))
+ return false;
+
+ if (clear_all_first) {
+ if (!ClearMap(map_id, &batch))
+ return false;
+ }
+ } else {
+ // Map doesn't exist, create it now if needed.
+ if (!changes.empty()) {
+ if (!CreateNewMap(namespace_key, &batch, &map_id))
+ return false;
+ }
+ }
+
+ // WriteValuesToMap won't do anything if changes is empty.
+ WriteValuesToMap(map_id, changes, &batch);
+
+ s = db_->Write(leveldb::WriteOptions(), &batch);
+ return DatabaseErrorCheck(s.ok());
+}
+
+bool SessionStorageDatabase::ShallowCopyNamespace(int64 namespace_id,
+ int64 new_namespace_id) {
michaeln 2012/04/25 08:32:12 pseudo code noodling... WriteBatch batch; // set
marja 2012/04/25 15:44:27 Done.
+ // Go through all origins in the namespace |namespace_id|, create placeholders
+ // for them in |new_namespace_id|, and associate them with the existing maps.
+
+ // Example, data before shallow copy:
+ // | map-1 | 1 (refcount) |
+ // | map-1-a | b |
+ // | namespace-1 (1 = namespace id) | dummy |
+ // | namespace-1-origin1 | 1 (mapid) |
+
+ // Example, data after shallow copy:
+ // | map-1 | 2 (inc. refcount) |
+ // | map-1-a | b |
+ // | namespace-1 (1 = namespace id) | dummy |
+ // | namespace-1-origin1 | 1 (mapid) |
+ // | namespace-2 | dummy |
+ // | namespace-2-origin1 | 1 (mapid) << references the same map
+
+ if (!LazyOpen(true))
+ return false;
+
+ leveldb::WriteBatch batch;
+ batch.Put(NamespaceStartKey(new_namespace_id, namespace_offset_), "");
+
+ // Ensure that the next namespace id is up to date.
+ if (!UpdateNextNamespaceId(new_namespace_id, &batch))
+ return false;
+
+ scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions()));
+ std::string namespace_start_key =
+ NamespaceStartKey(namespace_id, namespace_offset_);
+ it->Seek(namespace_start_key);
+ if (it->status().IsNotFound()) {
+ // It's possible that the namespace doesn't contain any data. (This happens
+ // when the namespace doesn't contain any areas.) We don't need to do
+ // anything.
+ return true;
+ }
+ if (!DatabaseErrorCheck(it->status().ok()))
+ return false;
+
+ // Skip the dummy entry "namespace-<namespaceid>" and iterate the origins.
+ for (it->Next(); it->Valid(); it->Next()) {
+ std::string key = it->key().ToString();
+ if (key.find(namespace_start_key) != 0) {
+ // Iterated past the origins for this namespace.
+ break;
+ }
+ std::string map_id = it->value().ToString();
+ // Increase the ref count for the map.
+ std::string map_key = MapRefCountKey(map_id);
+ int64 old_ref_count = 0;
+ if (!GetRefCount(map_key, &old_ref_count))
+ return false;
+ batch.Put(map_key, base::Int64ToString(++old_ref_count));
+ // Associate the map with the new namespace.
+ size_t second_dash = key.find('-', namespace_start_key.length());
+ std::string origin = key.substr(second_dash + 1);
+ std::string new_namespace_key = NamespaceKey(
+ base::Int64ToString(new_namespace_id + namespace_offset_), origin);
+ batch.Put(new_namespace_key, map_id);
+ }
+ leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch);
+ return DatabaseErrorCheck(s.ok());
+}
+
+bool SessionStorageDatabase::DeepCopy(int64 namespace_id, const GURL& origin) {
michaeln 2012/04/25 08:32:12 pseudo code noodling... if (!GetMapForArea(namesp
marja 2012/04/25 15:44:27 Done.
+ // Example, data before deep copy:
+ // | namespace-1 (1 = namespace id) | dummy |
+ // | namespace-1-origin1 | 1 (mapid) |
+ // | namespace-2 | dummy |
+ // | namespace-2-origin1 | 1 (mapid) << references the same map
+ // | map-1 | 2 (refcount) |
+ // | map-1-a | b |
+
+ // Example, data after deep copy copy:
+ // | namespace-1 (1 = namespace id) | dummy |
+ // | namespace-1-origin1 | 1 (mapid) |
+ // | namespace-2 | dummy |
+ // | namespace-2-origin1 | 2 (mapid) << references the new map
+ // | map-1 | 1 (dec. refcount) |
+ // | map-1-a | b |
+ // | map-2 | 1 (refcount) |
+ // | map-2-a | b |
+
+ if (!LazyOpen(true))
+ return false;
+
+ leveldb::Status s;
+ std::string namespace_key =
+ NamespaceKey(namespace_id, namespace_offset_, origin);
+ leveldb::WriteBatch batch;
+ std::string old_map_id;
+ s = db_->Get(leveldb::ReadOptions(), namespace_key, &old_map_id);
+ if (!ConsistencyCheck(!s.IsNotFound()))
+ return false;
+ if (!DatabaseErrorCheck(s.ok()))
+ return false;
+
+ // If this copy is the the only "shallow" copy of the map, no deep copying is
michaeln 2012/04/25 08:32:12 Is there any case where the higher level code woul
marja 2012/04/25 15:44:27 Atm this can happen if we do a shallow copy, write
+ // needed.
+ int64 ref_count = 0;
+ if (!GetRefCount(MapRefCountKey(old_map_id), &ref_count))
+ return false;
+ if (ref_count == 1)
+ return true;
+
+ std::string new_map_id;
+ if (!CreateNewMap(namespace_key, &batch, &new_map_id))
+ return false;
+
+ // Copy the values in the map.
+ ValuesMap values;
+ if (!ReadMap(old_map_id, &values, false))
+ return false;
+ WriteValuesToMap(new_map_id, values, &batch);
+
+ if (!DecreaseRefCount(old_map_id, 1, &batch))
+ return false;
+
+ s = db_->Write(leveldb::WriteOptions(), &batch);
+ return DatabaseErrorCheck(s.ok());
+}
+
+bool SessionStorageDatabase::DisassociateMap(int64 namespace_id,
+ const GURL& origin) {
michaeln 2012/04/25 08:32:12 ResetArea(id, origin) pseudo code noodling... in
marja 2012/04/25 15:44:27 This turned out to be the same than DeleteOrigin -
+ if (!LazyOpen(true))
+ return false;
+
+ leveldb::Status s;
+ std::string namespace_key =
+ NamespaceKey(namespace_id, namespace_offset_, origin);
+ std::string old_map_id;
+ s = db_->Get(leveldb::ReadOptions(), namespace_key, &old_map_id);
+ if (!DatabaseErrorCheck(s.ok() || s.IsNotFound()))
+ return false;
+ if (s.IsNotFound())
+ return true;
+
+ leveldb::WriteBatch batch;
+ if (!DecreaseRefCount(old_map_id, 1, &batch))
+ return false;
+ batch.Delete(namespace_key);
+ s = db_->Write(leveldb::WriteOptions(), &batch);
+ return DatabaseErrorCheck(s.ok());
+}
+
+bool SessionStorageDatabase::DeleteOrigin(int64 namespace_id,
+ const GURL& origin) {
+ if (!LazyOpen(false)) {
+ // No need to create the database if it doesn't exist.
+ return true;
+ }
+ leveldb::WriteBatch batch;
+ if (!DeleteOrigin(base::Int64ToString(namespace_id), origin.spec(), &batch))
+ return false;
+ leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch);
+ return DatabaseErrorCheck(s.ok());
+}
+
+bool SessionStorageDatabase::DeleteNamespace(int64 namespace_id) {
michaeln 2012/04/25 08:32:12 pseudo code noodling... std::vector<std::pair<GUR
marja 2012/04/25 15:44:27 Done.
+ if (!LazyOpen(false)) {
+ // No need to create the database if it doesn't exist.
+ return true;
+ }
+ std::string namespace_id_str = base::Int64ToString(namespace_id);
+ std::string namespace_start_key = NamespaceStartKey(namespace_id_str);
+ scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions()));
+ it->Seek(namespace_start_key);
+ if (it->status().IsNotFound())
+ return true;
+ if (!DatabaseErrorCheck(it->status().ok()))
+ return false;
+ leveldb::WriteBatch batch;
+ // Skip the dummy entry "namespace-<namespaceid>"; then iterate the origins in
+ // the namespace.
+ for (it->Next(); it->Valid(); it->Next()) {
+ // Key is of the form "namespace-<namespaceid>-<origin>".
+ std::string key = it->key().ToString();
+ std::string namespace_prefix = NamespacePrefix();
+ if (key.find(namespace_prefix) != 0) {
+ // Iterated beyond the keys for this namespace.
+ break;
+ }
+ int prefix_length = namespace_prefix.length();
+ size_t second_dash = key.find('-', prefix_length);
+ if (second_dash == std::string::npos ||
+ key.substr(prefix_length,
+ second_dash - prefix_length) != namespace_id_str) {
+ // Iterated beyond the keys for this namespace.
+ break;
+ }
+ std::string origin = key.substr(second_dash + 1);
+ if (!DeleteOrigin(namespace_id_str, origin, &batch))
+ return false;
+ }
+ batch.Delete(namespace_start_key);
+ leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch);
+ return DatabaseErrorCheck(s.ok());
+}
+
+bool SessionStorageDatabase::DeleteLeftoverData() {
+ if (!LazyOpen(false)) {
+ // No need to create the database if it doesn't exist.
+ return true;
+ }
+ scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions()));
+ std::string namespace_prefix = NamespacePrefix();
+ it->Seek(namespace_prefix);
+ if (it->status().IsNotFound()) {
+ // No data to delete.
+ return true;
+ }
+ if (!DatabaseErrorCheck(it->status().ok()))
+ return false;
+
+ // Find out how much we need to decrease the ref counts for each map referred
+ // to by one of the namespaces. Multiple namespaces can refer to the same map.
+ std::map<std::string, int> referred_maps;
+ leveldb::WriteBatch batch;
+
+ for (it->Next(); it->Valid(); it->Next()) {
+ std::string key = it->key().ToString();
+ if (key.find(namespace_prefix) != 0) {
+ // Iterated beyond the "namespace-" keys.
+ break;
+ }
+ size_t second_dash = key.find('-', namespace_prefix.length());
+ if (second_dash != std::string::npos) {
+ // The key is of the form "namespace-<namespaceid>-<origin>", and the
+ // value is a map id. Decrease the ref count for that map.
+ ++referred_maps[it->value().ToString()];
+ }
+ batch.Delete(key);
+ }
+ // Decrease the ref counts of the maps.
+ for (std::map<std::string, int>::const_iterator it = referred_maps.begin();
+ it != referred_maps.end(); ++it) {
+ if (!DecreaseRefCount(it->first, it->second, &batch))
+ return false;
+ }
+
+ leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch);
michaeln 2012/04/25 08:32:12 I think this results in an empty session storage d
marja 2012/04/25 15:44:27 Deleted it for now.
+ return DatabaseErrorCheck(s.ok());
+}
+
+bool SessionStorageDatabase::LazyOpen(bool create_if_needed) {
+ if (db_error_ || is_inconsistent_) {
+ // Don't try to open a database that we know has failed already.
+ return false;
+ }
+ if (IsOpen())
+ return true;
+
+ if (!create_if_needed &&
+ (!file_util::PathExists(file_path_) ||
+ file_util::IsDirectoryEmpty(file_path_))) {
+ // If the directory doesn't exist already and we haven't been asked to
+ // create a file on disk, then we don't bother opening the database. This
+ // means we wait until we absolutely need to put something onto disk before
+ // we do so.
+ return false;
+ }
+
+ leveldb::DB* db;
+ leveldb::Status s = TryToOpen(file_path_, &db);
+ if (!s.ok()) {
+ LOG(WARNING) << "Failed to open leveldb in " << file_path_.value()
+ << ", error: " << s.ToString();
+ DCHECK(db == NULL);
+
+ // Clear the directory and try again.
+ file_util::Delete(file_path_, true);
+ s = TryToOpen(file_path_, &db);
+ if (!s.ok()) {
+ LOG(WARNING) << "Failed to open leveldb in " << file_path_.value()
+ << ", error: " << s.ToString();
+ DCHECK(db == NULL);
+ db_error_ = true;
+ return false;
+ }
+ }
+ db_.reset(db);
+
+ return GetNextNamespaceId(&namespace_offset_);
+}
+
+leveldb::Status SessionStorageDatabase::TryToOpen(const FilePath& file_path,
+ leveldb::DB** db) {
+ leveldb::Options options;
+ // The directory exists but a valid leveldb database might not exist inside it
+ // (e.g., a subset of the needed files might be missing). Handle this
+ // situation gracefully by creating the database now.
+ options.create_if_missing = true;
+#if defined(OS_WIN)
+ return leveldb::DB::Open(options, WideToUTF8(file_path.value()), db);
+#elif defined(OS_POSIX)
+ return leveldb::DB::Open(options, file_path.value(), db);
+#endif
+}
+
+bool SessionStorageDatabase::IsOpen() const {
+ return db_.get() != NULL;
+}
+
+bool SessionStorageDatabase::ConsistencyCheck(bool ok) {
+ DCHECK(ok);
+ if (!ok) {
+ // We cannot recover the database during this run, e.g., the upper layer can
+ // have a different understanding of the database state (shallow and deep
+ // copies). Don't do any database operations during this run, and delete the
+ // data. During next run, the database is recreated.
+ is_inconsistent_ = true;
+ file_util::Delete(file_path_, true);
+ }
+ return ok;
+}
+
+bool SessionStorageDatabase::DatabaseErrorCheck(bool ok) {
+ if (!ok) {
+ // We cannot recover the database during this run, e.g., the upper layer can
+ // have a different understanding of the database state (shallow and deep
+ // copies). Don't do any database operations during this run, and delete the
+ // data. During the next run, the database is recreated.
+ db_error_ = true;
+ file_util::Delete(file_path_, true);
michaeln 2012/04/25 08:32:12 This doesn't look like a viable error handling str
marja 2012/04/25 15:44:27 Ahh, true, we should at least close the db first.
+ }
+ return ok;
+}
+
+bool SessionStorageDatabase::ReadMap(const std::string& map_id,
+ ValuesMap* result,
+ bool only_keys) {
+ scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions()));
+ std::string map_start_key = MapRefCountKey(map_id);
+ it->Seek(map_start_key);
+ // The map needs to exist, otherwise we have a stale map_id in the database.
+ if (!ConsistencyCheck(!it->status().IsNotFound()))
+ return false;
+ if (!DatabaseErrorCheck(it->status().ok()))
+ return false;
+ // Skip the dummy entry "map-<mapid>".
+ for (it->Next(); it->Valid(); it->Next()) {
+ // Key is of the form "map-<mapid>-<key>".
+ std::string key = it->key().ToString();
+ int prefix_length = MapPrefix().length();
+ size_t second_dash = key.find('-', prefix_length);
+ if (second_dash == std::string::npos ||
+ key.substr(prefix_length, second_dash - prefix_length) != map_id) {
+ // Iterated beyond the keys in this map.
+ break;
+ }
+ string16 key16 = UTF8ToUTF16(key.substr(second_dash + 1));
+ if (only_keys) {
+ (*result)[key16] = NullableString16(true);
+ } else {
+ // Convert the raw data stored in std::string (it->value()) to raw data
+ // stored in string16.
+ string16 value;
+ size_t len = it->value().size() / sizeof(char16);
+ value.resize(len);
+ value.assign(reinterpret_cast<const char16*>(it->value().data()), len);
+ (*result)[key16] = NullableString16(value, false);
michaeln 2012/04/25 08:32:12 is the call to resize needed? i'm not sure if this
marja 2012/04/25 15:44:27 Done.
+ }
+ }
+ return true;
+}
+
+bool SessionStorageDatabase::CreateNewMap(const std::string& namespace_key,
+ leveldb::WriteBatch* batch,
+ std::string* map_id) {
+ // Create a new map.
+ std::string next_map_id_key = NextMapIdKey();
+ leveldb::Status s = db_->Get(leveldb::ReadOptions(), next_map_id_key, map_id);
+ if (!DatabaseErrorCheck(s.ok() || s.IsNotFound()))
+ return false;
+ int64 next_map_id = 0;
+ if (s.IsNotFound()) {
+ *map_id = "0";
+ } else {
+ bool conversion_ok = base::StringToInt64(*map_id, &next_map_id);
+ if (!ConsistencyCheck(conversion_ok))
+ return false;
+ }
+ batch->Put(next_map_id_key, base::Int64ToString(++next_map_id));
+ batch->Put(namespace_key, *map_id);
+ batch->Put(MapRefCountKey(*map_id), base::Int64ToString(1));
+ return true;
+}
+
+void SessionStorageDatabase::WriteValuesToMap(const std::string& map_id,
+ const ValuesMap& values,
+ leveldb::WriteBatch* batch) {
+ for (ValuesMap::const_iterator it = values.begin(); it != values.end();
+ ++it) {
+ NullableString16 value = it->second;
+ std::string key = MapKey(map_id, UTF16ToUTF8(it->first));
+ if (value.is_null()) {
+ batch->Delete(key);
michaeln 2012/04/25 08:32:12 can batch->Delete or batch->Put fail... ah... i se
+ } else {
+ // Convert the raw data stored in string16 to raw data stored in
+ // std::string.
+ const char* data = reinterpret_cast<const char*>(value.string().data());
+ size_t size = value.string().size() * 2;
+ batch->Put(key, std::string(data, size));
michaeln 2012/04/25 08:32:12 i think you could use the Slice(const char* d, siz
marja 2012/04/25 15:44:27 Done.
+ }
+ }
+}
+
+bool SessionStorageDatabase::GetNextNamespaceId(int64* next_namespace_id) {
+ std::string next_namespace_id_string;
+ leveldb::Status s = db_->Get(leveldb::ReadOptions(), NextNamespaceIdKey(),
+ &next_namespace_id_string);
+ if (!DatabaseErrorCheck(s.ok() || s.IsNotFound()))
+ return false;
+ if (s.IsNotFound()) {
+ *next_namespace_id = 0;
+ return true;
+ }
+ bool conversion_ok =
+ base::StringToInt64(next_namespace_id_string, next_namespace_id);
+ return ConsistencyCheck(conversion_ok);
+}
+
+bool SessionStorageDatabase::UpdateNextNamespaceId(int64 namespace_id,
+ leveldb::WriteBatch* batch) {
+ int64 next_namespace_id;
+ if (!GetNextNamespaceId(&next_namespace_id))
+ return false;
+ if (next_namespace_id < namespace_id + namespace_offset_ + 1) {
+ next_namespace_id = namespace_id + namespace_offset_ + 1;
+ batch->Put(NextNamespaceIdKey(), base::Int64ToString(next_namespace_id));
+ }
+ return true;
+}
+
+bool SessionStorageDatabase::GetRefCount(const std::string& map_key,
+ int64* ref_count) {
+ std::string ref_count_string;
+ leveldb::Status s =
+ db_->Get(leveldb::ReadOptions(), map_key, &ref_count_string);
+ if (!ConsistencyCheck(s.ok()))
+ return false;
+ bool conversion_ok = base::StringToInt64(ref_count_string, ref_count);
+ return ConsistencyCheck(conversion_ok);
+}
+
+bool SessionStorageDatabase::DecreaseRefCount(const std::string& map_id,
+ int decrease,
+ leveldb::WriteBatch* batch) {
+ // Decrease the ref count for the map.
+ std::string map_key = MapRefCountKey(map_id);
+ int64 ref_count = 0;
+ if (!GetRefCount(map_key, &ref_count))
+ return false;
+ if (!ConsistencyCheck(decrease <= ref_count))
+ return false;
+ ref_count -= decrease;
+ if (ref_count > 0) {
+ batch->Put(map_key, base::Int64ToString(ref_count));
+ } else {
+ // Clear all keys in the map.
+ if (!ClearMap(map_id, batch))
+ return false;
+ batch->Delete(map_key);
+ }
+ return true;
+}
+
+bool SessionStorageDatabase::ClearMap(const std::string& map_id,
+ leveldb::WriteBatch* batch) {
+ ValuesMap values;
+ if (!ReadMap(map_id, &values, true))
+ return false;
+ for (ValuesMap::const_iterator it = values.begin(); it != values.end(); ++it)
+ batch->Delete(MapKey(map_id, UTF16ToUTF8(it->first)));
+ return true;
+}
+
+bool SessionStorageDatabase::DeleteOrigin(const std::string& namespace_id,
+ const std::string& origin,
+ leveldb::WriteBatch* batch) {
+ std::string namespace_key = NamespaceKey(namespace_id, origin);
+ std::string map_id;
+ leveldb::Status s = db_->Get(leveldb::ReadOptions(), namespace_key, &map_id);
+ if (s.IsNotFound())
+ return true; // Nothing to delete.
+ if (!DatabaseErrorCheck(s.ok()))
+ return false;
+ if (!DecreaseRefCount(map_id, 1, batch))
+ return false;
+ batch->Delete(namespace_key);
+ return true;
+}
+
+std::string SessionStorageDatabase::NamespaceStartKey(
+ const std::string& namespace_id) {
+ return base::StringPrintf("namespace-%s", namespace_id.c_str());
+}
+
+std::string SessionStorageDatabase::NamespaceStartKey(int64 namespace_id,
+ int64 namespace_offset) {
+ return NamespaceStartKey(
+ base::Int64ToString(namespace_id + namespace_offset));
+}
+
+std::string SessionStorageDatabase::NamespaceKey(
+ const std::string& namespace_id, const std::string& origin) {
+ return base::StringPrintf("namespace-%s-%s", namespace_id.c_str(),
+ origin.c_str());
+}
+
+std::string SessionStorageDatabase::NamespaceKey(
+ int64 namespace_id, int64 namespace_offset, const GURL& origin) {
+ return NamespaceKey(base::Int64ToString(namespace_id + namespace_offset),
+ origin.spec());
+}
+
+std::string SessionStorageDatabase::NamespacePrefix() {
+ return std::string("namespace-");
+}
+
+std::string SessionStorageDatabase::MapRefCountKey(const std::string& map_id) {
+ return base::StringPrintf("map-%s", map_id.c_str());
+}
+
+std::string SessionStorageDatabase::MapKey(const std::string& map_id,
+ const std::string& key) {
+ return base::StringPrintf("map-%s-%s", map_id.c_str(), key.c_str());
+}
+
+std::string SessionStorageDatabase::MapPrefix() {
+ return std::string("map-");
+}
+
+std::string SessionStorageDatabase::NextNamespaceIdKey() {
+ return "next-namespace-id";
+}
+
+std::string SessionStorageDatabase::NextMapIdKey() {
+ return "next-map-id";
+}
+
+} // namespace dom_storage

Powered by Google App Engine
This is Rietveld 408576698