Chromium Code Reviews| 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 |