Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / browser / dom_storage / session_storage_database.cc
blob62d643031dee2947ff3a1edf1390f0dc784c91b9
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"
19 #include "url/gurl.h"
22 namespace {
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
33 } // namespace
35 // Layout of the database:
36 // | key | value |
37 // -----------------------------------------------------------------------
38 // | map-1- | 2 (refcount, start of map-1-* keys)|
39 // | map-1-a | b (a = b in map 1) |
40 // | ... | |
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 |
53 namespace content {
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 {
63 public:
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_;
70 ~DBOperation() {
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();
79 #if defined(OS_WIN)
80 leveldb::DestroyDB(
81 base::WideToUTF8(session_storage_database_->file_path_.value()),
82 leveldb::Options());
83 #else
84 leveldb::DestroyDB(session_storage_database_->file_path_.value(),
85 leveldb::Options());
86 #endif
87 session_storage_database_->invalid_db_deleted_ = true;
91 private:
92 SessionStorageDatabase* session_storage_database_;
96 SessionStorageDatabase::SessionStorageDatabase(const base::FilePath& file_path)
97 : file_path_(file_path),
98 db_error_(false),
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,
108 const GURL& origin,
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))
113 return;
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();
123 std::string map_id;
124 bool exists;
125 if (GetMapForArea(namespace_id, origin.spec(), options, &exists, &map_id) &&
126 exists)
127 ReadMap(map_id, options, result, false);
128 db_->ReleaseSnapshot(options.snapshot);
131 bool SessionStorageDatabase::CommitAreaChanges(
132 const std::string& namespace_id,
133 const GURL& origin,
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.
138 if (!LazyOpen(true))
139 return false;
140 DBOperation operation(this);
142 leveldb::WriteBatch batch;
143 // Ensure that the keys "namespace-" "namespace-N" (see the schema above)
144 // exist.
145 const bool kOkIfExists = true;
146 if (!CreateNamespace(namespace_id, kOkIfExists, &batch))
147 return false;
149 std::string map_id;
150 bool exists;
151 if (!GetMapForArea(namespace_id, origin.spec(), leveldb::ReadOptions(),
152 &exists, &map_id))
153 return false;
154 if (exists) {
155 int64 ref_count;
156 if (!GetMapRefCount(map_id, &ref_count))
157 return false;
158 if (ref_count > 1) {
159 if (!DeepCopyArea(namespace_id, origin, !clear_all_first,
160 &map_id, &batch))
161 return false;
163 else if (clear_all_first) {
164 if (!ClearMap(map_id, &batch))
165 return false;
167 } else {
168 // Map doesn't exist, create it now if needed.
169 if (!changes.empty()) {
170 if (!CreateMapForArea(namespace_id, origin, &map_id, &batch))
171 return false;
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) |
188 // | map-1-a | b |
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) |
194 // | map-1-a | b |
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
200 if (!LazyOpen(true))
201 return false;
202 DBOperation operation(this);
204 leveldb::WriteBatch batch;
205 const bool kOkIfExists = false;
206 if (!CreateNamespace(new_namespace_id, kOkIfExists, &batch))
207 return false;
209 std::map<std::string, std::string> areas;
210 if (!GetAreasInNamespace(namespace_id, &areas))
211 return false;
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))
218 return false;
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.
229 return true;
231 DBOperation operation(this);
232 leveldb::WriteBatch batch;
233 if (!DeleteAreaHelper(namespace_id, origin.spec(), &batch))
234 return false;
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_)
247 return false;
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))
254 return false;
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))
259 return false;
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) {
268 if (!LazyOpen(true))
269 return false;
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
274 // read from it.
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.
283 if (!it->Valid()) {
284 db_->ReleaseSnapshot(options.snapshot);
285 return true;
288 if (!DatabaseErrorCheck(it->status().ok())) {
289 db_->ReleaseSnapshot(options.snapshot);
290 return false;
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.
300 break;
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
309 // <namespaceid>.
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
315 // any origins.
316 namespaces_and_origins->insert(
317 std::make_pair(current_namespace_id, std::vector<GURL>()));
318 } else {
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);
325 return true;
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.
332 return false;
334 if (IsOpen())
335 return true;
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
342 // we do so.
343 return false;
346 leveldb::DB* db;
347 leveldb::Status s = TryToOpen(&db);
348 if (!s.ok()) {
349 LOG(WARNING) << "Failed to open leveldb in " << file_path_.value()
350 << ", error: " << s.ToString();
351 DCHECK(db == NULL);
353 // Clear the directory and try again.
354 base::DeleteFile(file_path_, true);
355 s = TryToOpen(&db);
356 if (!s.ok()) {
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);
362 DCHECK(db == NULL);
363 db_error_ = true;
364 return false;
366 UMA_HISTOGRAM_ENUMERATION(session_storage_uma_name,
367 SESSION_STORAGE_UMA_RECREATED,
368 SESSION_STORAGE_UMA_MAX);
369 } else {
370 UMA_HISTOGRAM_ENUMERATION(session_storage_uma_name,
371 SESSION_STORAGE_UMA_SUCCESS,
372 SESSION_STORAGE_UMA_MAX);
374 db_.reset(db);
375 return true;
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;
386 #if defined(OS_WIN)
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);
390 #endif
393 bool SessionStorageDatabase::IsOpen() const {
394 return db_.get() != NULL;
397 bool SessionStorageDatabase::CallerErrorCheck(bool ok) const {
398 DCHECK(ok);
399 return ok;
402 bool SessionStorageDatabase::ConsistencyCheck(bool ok) {
403 if (ok)
404 return true;
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;
411 return false;
414 bool SessionStorageDatabase::DatabaseErrorCheck(bool ok) {
415 if (ok)
416 return true;
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.
420 db_error_ = true;
421 return false;
424 bool SessionStorageDatabase::CreateNamespace(const std::string& namespace_id,
425 bool ok_if_exists,
426 leveldb::WriteBatch* batch) {
427 leveldb::Slice namespace_prefix = NamespacePrefix();
428 std::string dummy;
429 leveldb::Status s = db_->Get(leveldb::ReadOptions(), namespace_prefix,
430 &dummy);
431 if (!DatabaseErrorCheck(s.ok() || s.IsNotFound()))
432 return false;
433 if (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()))
439 return false;
440 if (s.IsNotFound()) {
441 batch->Put(namespace_start_key, "");
442 return true;
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.
455 if (!it->Valid()) {
456 // The namespace_start_key is not found when the namespace doesn't contain
457 // any areas. We don't need to do anything.
458 return true;
460 if (!DatabaseErrorCheck(it->status().ok()))
461 return false;
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.
468 break;
470 std::string origin = key.substr(namespace_start_key.length());
471 std::string map_id = it->value().ToString();
472 (*areas)[origin] = map_id;
474 return true;
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) {
489 std::string map_id;
490 bool exists;
491 if (!GetMapForArea(namespace_id, origin, leveldb::ReadOptions(), &exists,
492 &map_id))
493 return false;
494 if (!exists)
495 return true; // Nothing to delete.
496 if (!DecreaseMapRefCount(map_id, 1, batch))
497 return false;
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,
502 // too.
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()))
507 return false;
508 // Advance the iterator 2 times (we still haven't really deleted
509 // namespace_key).
510 it->Next();
511 if (!ConsistencyCheck(it->Valid()))
512 return false;
513 it->Next();
514 if (!it->Valid())
515 return true;
516 std::string key = it->key().ToString();
517 if (key.find(namespace_start_key) != 0)
518 batch->Delete(namespace_start_key);
519 return true;
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()) {
529 *exists = false;
530 return true;
532 *exists = true;
533 return DatabaseErrorCheck(s.ok());
536 bool SessionStorageDatabase::CreateMapForArea(const std::string& namespace_id,
537 const GURL& origin,
538 std::string* map_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()))
543 return false;
544 int64 next_map_id = 0;
545 if (s.IsNotFound()) {
546 *map_id = "0";
547 } else {
548 bool conversion_ok = base::StringToInt64(*map_id, &next_map_id);
549 if (!ConsistencyCheck(conversion_ok))
550 return false;
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");
556 return true;
559 bool SessionStorageDatabase::ReadMap(const std::string& map_id,
560 const leveldb::ReadOptions& options,
561 DOMStorageValuesMap* result,
562 bool only_keys) {
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()))
570 return false;
571 if (!DatabaseErrorCheck(it->status().ok()))
572 return false;
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.
578 break;
580 // Key is of the form "map-<mapid>-<key>".
581 base::string16 key16 =
582 base::UTF8ToUTF16(key.substr(map_start_key.length()));
583 if (only_keys) {
584 (*result)[key16] = base::NullableString16();
585 } else {
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());
591 (*result)[key16] =
592 base::NullableString16(base::string16(data_ptr, len), false);
595 return true;
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();
602 it != values.end();
603 ++it) {
604 base::NullableString16 value = it->second;
605 std::string key = MapKey(map_id, base::UTF16ToUTF8(it->first));
606 if (value.is_null()) {
607 batch->Delete(key);
608 } else {
609 // Convert the raw data stored in base::string16 to raw data stored in
610 // std::string.
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,
619 int64* ref_count) {
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()))
624 return false;
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.
632 int64 old_ref_count;
633 if (!GetMapRefCount(map_id, &old_ref_count))
634 return false;
635 batch->Put(MapRefCountKey(map_id), base::Int64ToString(++old_ref_count));
636 return true;
639 bool SessionStorageDatabase::DecreaseMapRefCount(const std::string& map_id,
640 int decrease,
641 leveldb::WriteBatch* batch) {
642 // Decrease the ref count for the map.
643 int64 ref_count;
644 if (!GetMapRefCount(map_id, &ref_count))
645 return false;
646 if (!ConsistencyCheck(decrease <= ref_count))
647 return false;
648 ref_count -= decrease;
649 if (ref_count > 0) {
650 batch->Put(MapRefCountKey(map_id), base::Int64ToString(ref_count));
651 } else {
652 // Clear all keys in the map.
653 if (!ClearMap(map_id, batch))
654 return false;
655 batch->Delete(MapRefCountKey(map_id));
657 return true;
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))
664 return false;
665 for (DOMStorageValuesMap::const_iterator it = values.begin();
666 it != values.end(); ++it)
667 batch->Delete(MapKey(map_id, base::UTF16ToUTF8(it->first)));
668 return true;
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) |
680 // | map-1-a | b |
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) |
688 // | map-1-a | b |
689 // | map-2- | 1 (refcount) |
690 // | map-2-a | b |
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))
696 return false;
697 if (!DecreaseMapRefCount(*map_id, 1, batch))
698 return false;
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
701 // |map_id|.
702 if (!CreateMapForArea(namespace_id, origin, map_id, batch))
703 return false;
704 WriteValuesToMap(*map_id, values, batch);
705 return true;
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(),
716 origin.c_str());
719 const char* SessionStorageDatabase::NamespacePrefix() {
720 return "namespace-";
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