1 // Copyright 2013 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 "content/browser/dom_storage/session_storage_database.h"
7 #include "base/files/file_util.h"
8 #include "base/logging.h"
9 #include "base/metrics/histogram.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "third_party/leveldatabase/env_chromium.h"
14 #include "third_party/leveldatabase/src/include/leveldb/db.h"
15 #include "third_party/leveldatabase/src/include/leveldb/iterator.h"
16 #include "third_party/leveldatabase/src/include/leveldb/options.h"
17 #include "third_party/leveldatabase/src/include/leveldb/status.h"
18 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
24 const char session_storage_uma_name
[] = "SessionStorageDatabase.Open";
26 enum SessionStorageUMA
{
27 SESSION_STORAGE_UMA_SUCCESS
,
28 SESSION_STORAGE_UMA_RECREATED
,
29 SESSION_STORAGE_UMA_FAIL
,
30 SESSION_STORAGE_UMA_MAX
35 // Layout of the database:
37 // -----------------------------------------------------------------------
38 // | map-1- | 2 (refcount, start of map-1-* keys)|
39 // | map-1-a | b (a = b in map 1) |
41 // | namespace- | dummy (start of namespace-* keys) |
42 // | namespace-1- (1 = namespace id)| dummy (start of namespace-1-* keys)|
43 // | namespace-1-origin1 | 1 (mapid) |
44 // | namespace-1-origin2 | 2 |
45 // | namespace-2- | dummy |
46 // | namespace-2-origin1 | 1 (shallow copy) |
47 // | namespace-2-origin2 | 2 (shallow copy) |
48 // | namespace-3- | dummy |
49 // | namespace-3-origin1 | 3 (deep copy) |
50 // | namespace-3-origin2 | 2 (shallow copy) |
51 // | next-map-id | 4 |
55 // This class keeps track of ongoing operations across different threads. When
56 // DB inconsistency is detected, we need to 1) make sure no new operations start
57 // 2) wait until all current operations finish, and let the last one of them
58 // close the DB and delete the data. The DB will remain empty for the rest of
59 // the run, and will be recreated during the next run. We cannot hope to recover
60 // during this run, since the upper layer will have a different idea about what
61 // should be in the database.
62 class SessionStorageDatabase::DBOperation
{
64 DBOperation(SessionStorageDatabase
* session_storage_database
)
65 : session_storage_database_(session_storage_database
) {
66 base::AutoLock
auto_lock(session_storage_database_
->db_lock_
);
67 ++session_storage_database_
->operation_count_
;
71 base::AutoLock
auto_lock(session_storage_database_
->db_lock_
);
72 --session_storage_database_
->operation_count_
;
73 if ((session_storage_database_
->is_inconsistent_
||
74 session_storage_database_
->db_error_
) &&
75 session_storage_database_
->operation_count_
== 0 &&
76 !session_storage_database_
->invalid_db_deleted_
) {
77 // No other operations are ongoing and the data is bad -> delete it now.
78 session_storage_database_
->db_
.reset();
81 base::WideToUTF8(session_storage_database_
->file_path_
.value()),
84 leveldb::DestroyDB(session_storage_database_
->file_path_
.value(),
87 session_storage_database_
->invalid_db_deleted_
= true;
92 SessionStorageDatabase
* session_storage_database_
;
96 SessionStorageDatabase::SessionStorageDatabase(const base::FilePath
& file_path
)
97 : file_path_(file_path
),
99 is_inconsistent_(false),
100 invalid_db_deleted_(false),
101 operation_count_(0) {
104 SessionStorageDatabase::~SessionStorageDatabase() {
107 void SessionStorageDatabase::ReadAreaValues(const std::string
& namespace_id
,
109 DOMStorageValuesMap
* result
) {
110 // We don't create a database if it doesn't exist. In that case, there is
111 // nothing to be added to the result.
112 if (!LazyOpen(false))
114 DBOperation
operation(this);
116 // While ReadAreaValues is in progress, another thread can call
117 // CommitAreaChanges. CommitAreaChanges might update map ref count key while
118 // this thread is iterating over the map ref count key. To protect the reading
119 // operation, create a snapshot and read from it.
120 leveldb::ReadOptions options
;
121 options
.snapshot
= db_
->GetSnapshot();
125 if (GetMapForArea(namespace_id
, origin
.spec(), options
, &exists
, &map_id
) &&
127 ReadMap(map_id
, options
, result
, false);
128 db_
->ReleaseSnapshot(options
.snapshot
);
131 bool SessionStorageDatabase::CommitAreaChanges(
132 const std::string
& namespace_id
,
134 bool clear_all_first
,
135 const DOMStorageValuesMap
& changes
) {
136 // Even if |changes| is empty, we need to write the appropriate placeholders
137 // in the database, so that it can be later shallow-copied succssfully.
140 DBOperation
operation(this);
142 leveldb::WriteBatch batch
;
143 // Ensure that the keys "namespace-" "namespace-N" (see the schema above)
145 const bool kOkIfExists
= true;
146 if (!CreateNamespace(namespace_id
, kOkIfExists
, &batch
))
151 if (!GetMapForArea(namespace_id
, origin
.spec(), leveldb::ReadOptions(),
156 if (!GetMapRefCount(map_id
, &ref_count
))
159 if (!DeepCopyArea(namespace_id
, origin
, !clear_all_first
,
163 else if (clear_all_first
) {
164 if (!ClearMap(map_id
, &batch
))
168 // Map doesn't exist, create it now if needed.
169 if (!changes
.empty()) {
170 if (!CreateMapForArea(namespace_id
, origin
, &map_id
, &batch
))
175 WriteValuesToMap(map_id
, changes
, &batch
);
177 leveldb::Status s
= db_
->Write(leveldb::WriteOptions(), &batch
);
178 return DatabaseErrorCheck(s
.ok());
181 bool SessionStorageDatabase::CloneNamespace(
182 const std::string
& namespace_id
, const std::string
& new_namespace_id
) {
183 // Go through all origins in the namespace |namespace_id|, create placeholders
184 // for them in |new_namespace_id|, and associate them with the existing maps.
186 // Example, data before shallow copy:
187 // | map-1- | 1 (refcount) |
189 // | namespace-1- (1 = namespace id)| dummy |
190 // | namespace-1-origin1 | 1 (mapid) |
192 // Example, data after shallow copy:
193 // | map-1- | 2 (inc. refcount) |
195 // | namespace-1-(1 = namespace id) | dummy |
196 // | namespace-1-origin1 | 1 (mapid) |
197 // | namespace-2- | dummy |
198 // | namespace-2-origin1 | 1 (mapid) << references the same map
202 DBOperation
operation(this);
204 leveldb::WriteBatch batch
;
205 const bool kOkIfExists
= false;
206 if (!CreateNamespace(new_namespace_id
, kOkIfExists
, &batch
))
209 std::map
<std::string
, std::string
> areas
;
210 if (!GetAreasInNamespace(namespace_id
, &areas
))
213 for (std::map
<std::string
, std::string
>::const_iterator it
= areas
.begin();
214 it
!= areas
.end(); ++it
) {
215 const std::string
& origin
= it
->first
;
216 const std::string
& map_id
= it
->second
;
217 if (!IncreaseMapRefCount(map_id
, &batch
))
219 AddAreaToNamespace(new_namespace_id
, origin
, map_id
, &batch
);
221 leveldb::Status s
= db_
->Write(leveldb::WriteOptions(), &batch
);
222 return DatabaseErrorCheck(s
.ok());
225 bool SessionStorageDatabase::DeleteArea(const std::string
& namespace_id
,
226 const GURL
& origin
) {
227 if (!LazyOpen(false)) {
228 // No need to create the database if it doesn't exist.
231 DBOperation
operation(this);
232 leveldb::WriteBatch batch
;
233 if (!DeleteAreaHelper(namespace_id
, origin
.spec(), &batch
))
235 leveldb::Status s
= db_
->Write(leveldb::WriteOptions(), &batch
);
236 return DatabaseErrorCheck(s
.ok());
239 bool SessionStorageDatabase::DeleteNamespace(const std::string
& namespace_id
) {
241 // The caller should have called other methods to open the DB before this
242 // function. Otherwise, DB stores nothing interesting related to the
243 // specified namespace.
244 // Do nothing if the DB is not open (or we know it has failed already),
245 base::AutoLock
auto_lock(db_lock_
);
246 if (!IsOpen() || db_error_
|| is_inconsistent_
)
249 DBOperation
operation(this);
250 // Itereate through the areas in the namespace.
251 leveldb::WriteBatch batch
;
252 std::map
<std::string
, std::string
> areas
;
253 if (!GetAreasInNamespace(namespace_id
, &areas
))
255 for (std::map
<std::string
, std::string
>::const_iterator it
= areas
.begin();
256 it
!= areas
.end(); ++it
) {
257 const std::string
& origin
= it
->first
;
258 if (!DeleteAreaHelper(namespace_id
, origin
, &batch
))
261 batch
.Delete(NamespaceStartKey(namespace_id
));
262 leveldb::Status s
= db_
->Write(leveldb::WriteOptions(), &batch
);
263 return DatabaseErrorCheck(s
.ok());
266 bool SessionStorageDatabase::ReadNamespacesAndOrigins(
267 std::map
<std::string
, std::vector
<GURL
> >* namespaces_and_origins
) {
270 DBOperation
operation(this);
272 // While ReadNamespacesAndOrigins is in progress, another thread can call
273 // CommitAreaChanges. To protect the reading operation, create a snapshot and
275 leveldb::ReadOptions options
;
276 options
.snapshot
= db_
->GetSnapshot();
278 std::string namespace_prefix
= NamespacePrefix();
279 scoped_ptr
<leveldb::Iterator
> it(db_
->NewIterator(options
));
280 it
->Seek(namespace_prefix
);
281 // If the key is not found, the status of the iterator won't be IsNotFound(),
282 // but the iterator will be invalid.
284 db_
->ReleaseSnapshot(options
.snapshot
);
288 if (!DatabaseErrorCheck(it
->status().ok())) {
289 db_
->ReleaseSnapshot(options
.snapshot
);
293 // Skip the dummy entry "namespace-" and iterate the namespaces.
294 std::string current_namespace_start_key
;
295 std::string current_namespace_id
;
296 for (it
->Next(); it
->Valid(); it
->Next()) {
297 std::string key
= it
->key().ToString();
298 if (key
.find(namespace_prefix
) != 0) {
299 // Iterated past the "namespace-" keys.
302 // For each namespace, the first key is "namespace-<namespaceid>-", and the
303 // subsequent keys are "namespace-<namespaceid>-<origin>". Read the unique
304 // "<namespaceid>" parts from the keys.
305 if (current_namespace_start_key
.empty() ||
306 key
.substr(0, current_namespace_start_key
.length()) !=
307 current_namespace_start_key
) {
308 // The key is of the form "namespace-<namespaceid>-" for a new
310 current_namespace_start_key
= key
;
311 current_namespace_id
=
312 key
.substr(namespace_prefix
.length(),
313 key
.length() - namespace_prefix
.length() - 1);
314 // Ensure that we keep track of the namespace even if it doesn't contain
316 namespaces_and_origins
->insert(
317 std::make_pair(current_namespace_id
, std::vector
<GURL
>()));
319 // The key is of the form "namespace-<namespaceid>-<origin>".
320 std::string origin
= key
.substr(current_namespace_start_key
.length());
321 (*namespaces_and_origins
)[current_namespace_id
].push_back(GURL(origin
));
324 db_
->ReleaseSnapshot(options
.snapshot
);
328 bool SessionStorageDatabase::LazyOpen(bool create_if_needed
) {
329 base::AutoLock
auto_lock(db_lock_
);
330 if (db_error_
|| is_inconsistent_
) {
331 // Don't try to open a database that we know has failed already.
337 if (!create_if_needed
&&
338 (!base::PathExists(file_path_
) || base::IsDirectoryEmpty(file_path_
))) {
339 // If the directory doesn't exist already and we haven't been asked to
340 // create a file on disk, then we don't bother opening the database. This
341 // means we wait until we absolutely need to put something onto disk before
347 leveldb::Status s
= TryToOpen(&db
);
349 LOG(WARNING
) << "Failed to open leveldb in " << file_path_
.value()
350 << ", error: " << s
.ToString();
353 // Clear the directory and try again.
354 base::DeleteFile(file_path_
, true);
357 LOG(WARNING
) << "Failed to open leveldb in " << file_path_
.value()
358 << ", error: " << s
.ToString();
359 UMA_HISTOGRAM_ENUMERATION(session_storage_uma_name
,
360 SESSION_STORAGE_UMA_FAIL
,
361 SESSION_STORAGE_UMA_MAX
);
366 UMA_HISTOGRAM_ENUMERATION(session_storage_uma_name
,
367 SESSION_STORAGE_UMA_RECREATED
,
368 SESSION_STORAGE_UMA_MAX
);
370 UMA_HISTOGRAM_ENUMERATION(session_storage_uma_name
,
371 SESSION_STORAGE_UMA_SUCCESS
,
372 SESSION_STORAGE_UMA_MAX
);
378 leveldb::Status
SessionStorageDatabase::TryToOpen(leveldb::DB
** db
) {
379 leveldb::Options options
;
380 // The directory exists but a valid leveldb database might not exist inside it
381 // (e.g., a subset of the needed files might be missing). Handle this
382 // situation gracefully by creating the database now.
383 options
.max_open_files
= 0; // Use minimum.
384 options
.create_if_missing
= true;
385 options
.reuse_logs
= leveldb_env::kDefaultLogReuseOptionValue
;
387 return leveldb::DB::Open(options
, base::WideToUTF8(file_path_
.value()), db
);
388 #elif defined(OS_POSIX)
389 return leveldb::DB::Open(options
, file_path_
.value(), db
);
393 bool SessionStorageDatabase::IsOpen() const {
394 return db_
.get() != NULL
;
397 bool SessionStorageDatabase::CallerErrorCheck(bool ok
) const {
402 bool SessionStorageDatabase::ConsistencyCheck(bool ok
) {
405 base::AutoLock
auto_lock(db_lock_
);
406 // We cannot recover the database during this run, e.g., the upper layer can
407 // have a different understanding of the database state (shallow and deep
408 // copies). Make further operations fail. The next operation that finishes
409 // will delete the data, and next run will recerate the database.
410 is_inconsistent_
= true;
414 bool SessionStorageDatabase::DatabaseErrorCheck(bool ok
) {
417 base::AutoLock
auto_lock(db_lock_
);
418 // Make further operations fail. The next operation that finishes
419 // will delete the data, and next run will recerate the database.
424 bool SessionStorageDatabase::CreateNamespace(const std::string
& namespace_id
,
426 leveldb::WriteBatch
* batch
) {
427 leveldb::Slice namespace_prefix
= NamespacePrefix();
429 leveldb::Status s
= db_
->Get(leveldb::ReadOptions(), namespace_prefix
,
431 if (!DatabaseErrorCheck(s
.ok() || s
.IsNotFound()))
434 batch
->Put(namespace_prefix
, "");
436 std::string namespace_start_key
= NamespaceStartKey(namespace_id
);
437 s
= db_
->Get(leveldb::ReadOptions(), namespace_start_key
, &dummy
);
438 if (!DatabaseErrorCheck(s
.ok() || s
.IsNotFound()))
440 if (s
.IsNotFound()) {
441 batch
->Put(namespace_start_key
, "");
444 return CallerErrorCheck(ok_if_exists
);
447 bool SessionStorageDatabase::GetAreasInNamespace(
448 const std::string
& namespace_id
,
449 std::map
<std::string
, std::string
>* areas
) {
450 std::string namespace_start_key
= NamespaceStartKey(namespace_id
);
451 scoped_ptr
<leveldb::Iterator
> it(db_
->NewIterator(leveldb::ReadOptions()));
452 it
->Seek(namespace_start_key
);
453 // If the key is not found, the status of the iterator won't be IsNotFound(),
454 // but the iterator will be invalid.
456 // The namespace_start_key is not found when the namespace doesn't contain
457 // any areas. We don't need to do anything.
460 if (!DatabaseErrorCheck(it
->status().ok()))
463 // Skip the dummy entry "namespace-<namespaceid>-" and iterate the origins.
464 for (it
->Next(); it
->Valid(); it
->Next()) {
465 std::string key
= it
->key().ToString();
466 if (key
.find(namespace_start_key
) != 0) {
467 // Iterated past the origins for this namespace.
470 std::string origin
= key
.substr(namespace_start_key
.length());
471 std::string map_id
= it
->value().ToString();
472 (*areas
)[origin
] = map_id
;
477 void SessionStorageDatabase::AddAreaToNamespace(const std::string
& namespace_id
,
478 const std::string
& origin
,
479 const std::string
& map_id
,
480 leveldb::WriteBatch
* batch
) {
481 std::string namespace_key
= NamespaceKey(namespace_id
, origin
);
482 batch
->Put(namespace_key
, map_id
);
485 bool SessionStorageDatabase::DeleteAreaHelper(
486 const std::string
& namespace_id
,
487 const std::string
& origin
,
488 leveldb::WriteBatch
* batch
) {
491 if (!GetMapForArea(namespace_id
, origin
, leveldb::ReadOptions(), &exists
,
495 return true; // Nothing to delete.
496 if (!DecreaseMapRefCount(map_id
, 1, batch
))
498 std::string namespace_key
= NamespaceKey(namespace_id
, origin
);
499 batch
->Delete(namespace_key
);
501 // If this was the only area in the namespace, delete the namespace start key,
503 std::string namespace_start_key
= NamespaceStartKey(namespace_id
);
504 scoped_ptr
<leveldb::Iterator
> it(db_
->NewIterator(leveldb::ReadOptions()));
505 it
->Seek(namespace_start_key
);
506 if (!ConsistencyCheck(it
->Valid()))
508 // Advance the iterator 2 times (we still haven't really deleted
511 if (!ConsistencyCheck(it
->Valid()))
516 std::string key
= it
->key().ToString();
517 if (key
.find(namespace_start_key
) != 0)
518 batch
->Delete(namespace_start_key
);
522 bool SessionStorageDatabase::GetMapForArea(const std::string
& namespace_id
,
523 const std::string
& origin
,
524 const leveldb::ReadOptions
& options
,
525 bool* exists
, std::string
* map_id
) {
526 std::string namespace_key
= NamespaceKey(namespace_id
, origin
);
527 leveldb::Status s
= db_
->Get(options
, namespace_key
, map_id
);
528 if (s
.IsNotFound()) {
533 return DatabaseErrorCheck(s
.ok());
536 bool SessionStorageDatabase::CreateMapForArea(const std::string
& namespace_id
,
539 leveldb::WriteBatch
* batch
) {
540 leveldb::Slice next_map_id_key
= NextMapIdKey();
541 leveldb::Status s
= db_
->Get(leveldb::ReadOptions(), next_map_id_key
, map_id
);
542 if (!DatabaseErrorCheck(s
.ok() || s
.IsNotFound()))
544 int64 next_map_id
= 0;
545 if (s
.IsNotFound()) {
548 bool conversion_ok
= base::StringToInt64(*map_id
, &next_map_id
);
549 if (!ConsistencyCheck(conversion_ok
))
552 batch
->Put(next_map_id_key
, base::Int64ToString(++next_map_id
));
553 std::string namespace_key
= NamespaceKey(namespace_id
, origin
.spec());
554 batch
->Put(namespace_key
, *map_id
);
555 batch
->Put(MapRefCountKey(*map_id
), "1");
559 bool SessionStorageDatabase::ReadMap(const std::string
& map_id
,
560 const leveldb::ReadOptions
& options
,
561 DOMStorageValuesMap
* result
,
563 scoped_ptr
<leveldb::Iterator
> it(db_
->NewIterator(options
));
564 std::string map_start_key
= MapRefCountKey(map_id
);
565 it
->Seek(map_start_key
);
566 // If the key is not found, the status of the iterator won't be IsNotFound(),
567 // but the iterator will be invalid. The map needs to exist, otherwise we have
568 // a stale map_id in the database.
569 if (!ConsistencyCheck(it
->Valid()))
571 if (!DatabaseErrorCheck(it
->status().ok()))
573 // Skip the dummy entry "map-<mapid>-".
574 for (it
->Next(); it
->Valid(); it
->Next()) {
575 std::string key
= it
->key().ToString();
576 if (key
.find(map_start_key
) != 0) {
577 // Iterated past the keys in this map.
580 // Key is of the form "map-<mapid>-<key>".
581 base::string16 key16
=
582 base::UTF8ToUTF16(key
.substr(map_start_key
.length()));
584 (*result
)[key16
] = base::NullableString16();
586 // Convert the raw data stored in std::string (it->value()) to raw data
587 // stored in base::string16.
588 size_t len
= it
->value().size() / sizeof(base::char16
);
589 const base::char16
* data_ptr
=
590 reinterpret_cast<const base::char16
*>(it
->value().data());
592 base::NullableString16(base::string16(data_ptr
, len
), false);
598 void SessionStorageDatabase::WriteValuesToMap(const std::string
& map_id
,
599 const DOMStorageValuesMap
& values
,
600 leveldb::WriteBatch
* batch
) {
601 for (DOMStorageValuesMap::const_iterator it
= values
.begin();
604 base::NullableString16 value
= it
->second
;
605 std::string key
= MapKey(map_id
, base::UTF16ToUTF8(it
->first
));
606 if (value
.is_null()) {
609 // Convert the raw data stored in base::string16 to raw data stored in
611 const char* data
= reinterpret_cast<const char*>(value
.string().data());
612 size_t size
= value
.string().size() * 2;
613 batch
->Put(key
, leveldb::Slice(data
, size
));
618 bool SessionStorageDatabase::GetMapRefCount(const std::string
& map_id
,
620 std::string ref_count_string
;
621 leveldb::Status s
= db_
->Get(leveldb::ReadOptions(),
622 MapRefCountKey(map_id
), &ref_count_string
);
623 if (!ConsistencyCheck(s
.ok()))
625 bool conversion_ok
= base::StringToInt64(ref_count_string
, ref_count
);
626 return ConsistencyCheck(conversion_ok
);
629 bool SessionStorageDatabase::IncreaseMapRefCount(const std::string
& map_id
,
630 leveldb::WriteBatch
* batch
) {
631 // Increase the ref count for the map.
633 if (!GetMapRefCount(map_id
, &old_ref_count
))
635 batch
->Put(MapRefCountKey(map_id
), base::Int64ToString(++old_ref_count
));
639 bool SessionStorageDatabase::DecreaseMapRefCount(const std::string
& map_id
,
641 leveldb::WriteBatch
* batch
) {
642 // Decrease the ref count for the map.
644 if (!GetMapRefCount(map_id
, &ref_count
))
646 if (!ConsistencyCheck(decrease
<= ref_count
))
648 ref_count
-= decrease
;
650 batch
->Put(MapRefCountKey(map_id
), base::Int64ToString(ref_count
));
652 // Clear all keys in the map.
653 if (!ClearMap(map_id
, batch
))
655 batch
->Delete(MapRefCountKey(map_id
));
660 bool SessionStorageDatabase::ClearMap(const std::string
& map_id
,
661 leveldb::WriteBatch
* batch
) {
662 DOMStorageValuesMap values
;
663 if (!ReadMap(map_id
, leveldb::ReadOptions(), &values
, true))
665 for (DOMStorageValuesMap::const_iterator it
= values
.begin();
666 it
!= values
.end(); ++it
)
667 batch
->Delete(MapKey(map_id
, base::UTF16ToUTF8(it
->first
)));
671 bool SessionStorageDatabase::DeepCopyArea(
672 const std::string
& namespace_id
, const GURL
& origin
, bool copy_data
,
673 std::string
* map_id
, leveldb::WriteBatch
* batch
) {
674 // Example, data before deep copy:
675 // | namespace-1- (1 = namespace id)| dummy |
676 // | namespace-1-origin1 | 1 (mapid) |
677 // | namespace-2- | dummy |
678 // | namespace-2-origin1 | 1 (mapid) << references the same map
679 // | map-1- | 2 (refcount) |
682 // Example, data after deep copy copy:
683 // | namespace-1-(1 = namespace id) | dummy |
684 // | namespace-1-origin1 | 1 (mapid) |
685 // | namespace-2- | dummy |
686 // | namespace-2-origin1 | 2 (mapid) << references the new map
687 // | map-1- | 1 (dec. refcount) |
689 // | map-2- | 1 (refcount) |
692 // Read the values from the old map here. If we don't need to copy the data,
693 // this can stay empty.
694 DOMStorageValuesMap values
;
695 if (copy_data
&& !ReadMap(*map_id
, leveldb::ReadOptions(), &values
, false))
697 if (!DecreaseMapRefCount(*map_id
, 1, batch
))
699 // Create a new map (this will also break the association to the old map) and
700 // write the old data into it. This will write the id of the created map into
702 if (!CreateMapForArea(namespace_id
, origin
, map_id
, batch
))
704 WriteValuesToMap(*map_id
, values
, batch
);
708 std::string
SessionStorageDatabase::NamespaceStartKey(
709 const std::string
& namespace_id
) {
710 return base::StringPrintf("namespace-%s-", namespace_id
.c_str());
713 std::string
SessionStorageDatabase::NamespaceKey(
714 const std::string
& namespace_id
, const std::string
& origin
) {
715 return base::StringPrintf("namespace-%s-%s", namespace_id
.c_str(),
719 const char* SessionStorageDatabase::NamespacePrefix() {
723 std::string
SessionStorageDatabase::MapRefCountKey(const std::string
& map_id
) {
724 return base::StringPrintf("map-%s-", map_id
.c_str());
727 std::string
SessionStorageDatabase::MapKey(const std::string
& map_id
,
728 const std::string
& key
) {
729 return base::StringPrintf("map-%s-%s", map_id
.c_str(), key
.c_str());
732 const char* SessionStorageDatabase::NextMapIdKey() {
733 return "next-map-id";
736 } // namespace content