Add ICU message format support
[chromium-blink-merge.git] / content / browser / dom_storage / session_storage_database.cc
blobd0ab442238b5817b48761a8267e5858002ba420d
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 <vector>
9 #include "base/files/file_util.h"
10 #include "base/logging.h"
11 #include "base/metrics/histogram.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "third_party/leveldatabase/env_chromium.h"
16 #include "third_party/leveldatabase/src/include/leveldb/db.h"
17 #include "third_party/leveldatabase/src/include/leveldb/iterator.h"
18 #include "third_party/leveldatabase/src/include/leveldb/options.h"
19 #include "third_party/leveldatabase/src/include/leveldb/status.h"
20 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
21 #include "url/gurl.h"
24 namespace {
26 const char session_storage_uma_name[] = "SessionStorageDatabase.Open";
28 enum SessionStorageUMA {
29 SESSION_STORAGE_UMA_SUCCESS,
30 SESSION_STORAGE_UMA_RECREATED,
31 SESSION_STORAGE_UMA_FAIL,
32 SESSION_STORAGE_UMA_MAX
35 } // namespace
37 // Layout of the database:
38 // | key | value |
39 // -----------------------------------------------------------------------
40 // | map-1- | 2 (refcount, start of map-1-* keys)|
41 // | map-1-a | b (a = b in map 1) |
42 // | ... | |
43 // | namespace- | dummy (start of namespace-* keys) |
44 // | namespace-1- (1 = namespace id)| dummy (start of namespace-1-* keys)|
45 // | namespace-1-origin1 | 1 (mapid) |
46 // | namespace-1-origin2 | 2 |
47 // | namespace-2- | dummy |
48 // | namespace-2-origin1 | 1 (shallow copy) |
49 // | namespace-2-origin2 | 2 (shallow copy) |
50 // | namespace-3- | dummy |
51 // | namespace-3-origin1 | 3 (deep copy) |
52 // | namespace-3-origin2 | 2 (shallow copy) |
53 // | next-map-id | 4 |
55 namespace content {
57 // This class keeps track of ongoing operations across different threads. When
58 // DB inconsistency is detected, we need to 1) make sure no new operations start
59 // 2) wait until all current operations finish, and let the last one of them
60 // close the DB and delete the data. The DB will remain empty for the rest of
61 // the run, and will be recreated during the next run. We cannot hope to recover
62 // during this run, since the upper layer will have a different idea about what
63 // should be in the database.
64 class SessionStorageDatabase::DBOperation {
65 public:
66 explicit DBOperation(SessionStorageDatabase* session_storage_database)
67 : session_storage_database_(session_storage_database) {
68 base::AutoLock auto_lock(session_storage_database_->db_lock_);
69 ++session_storage_database_->operation_count_;
72 ~DBOperation() {
73 base::AutoLock auto_lock(session_storage_database_->db_lock_);
74 --session_storage_database_->operation_count_;
75 if ((session_storage_database_->is_inconsistent_ ||
76 session_storage_database_->db_error_) &&
77 session_storage_database_->operation_count_ == 0 &&
78 !session_storage_database_->invalid_db_deleted_) {
79 // No other operations are ongoing and the data is bad -> delete it now.
80 session_storage_database_->db_.reset();
81 leveldb::DestroyDB(session_storage_database_->file_path_.AsUTF8Unsafe(),
82 leveldb::Options());
83 session_storage_database_->invalid_db_deleted_ = true;
87 private:
88 SessionStorageDatabase* session_storage_database_;
92 SessionStorageDatabase::SessionStorageDatabase(const base::FilePath& file_path)
93 : file_path_(file_path),
94 db_error_(false),
95 is_inconsistent_(false),
96 invalid_db_deleted_(false),
97 operation_count_(0) {
100 SessionStorageDatabase::~SessionStorageDatabase() {
103 void SessionStorageDatabase::ReadAreaValues(const std::string& namespace_id,
104 const GURL& origin,
105 DOMStorageValuesMap* result) {
106 // We don't create a database if it doesn't exist. In that case, there is
107 // nothing to be added to the result.
108 if (!LazyOpen(false))
109 return;
110 DBOperation operation(this);
112 // While ReadAreaValues is in progress, another thread can call
113 // CommitAreaChanges. CommitAreaChanges might update map ref count key while
114 // this thread is iterating over the map ref count key. To protect the reading
115 // operation, create a snapshot and read from it.
116 leveldb::ReadOptions options;
117 options.snapshot = db_->GetSnapshot();
119 std::string map_id;
120 bool exists;
121 if (GetMapForArea(namespace_id, origin.spec(), options, &exists, &map_id) &&
122 exists)
123 ReadMap(map_id, options, result, false);
124 db_->ReleaseSnapshot(options.snapshot);
127 bool SessionStorageDatabase::CommitAreaChanges(
128 const std::string& namespace_id,
129 const GURL& origin,
130 bool clear_all_first,
131 const DOMStorageValuesMap& changes) {
132 // Even if |changes| is empty, we need to write the appropriate placeholders
133 // in the database, so that it can be later shallow-copied successfully.
134 if (!LazyOpen(true))
135 return false;
136 DBOperation operation(this);
138 leveldb::WriteBatch batch;
139 // Ensure that the keys "namespace-" "namespace-N" (see the schema above)
140 // exist.
141 const bool kOkIfExists = true;
142 if (!CreateNamespace(namespace_id, kOkIfExists, &batch))
143 return false;
145 std::string map_id;
146 bool exists;
147 if (!GetMapForArea(namespace_id, origin.spec(), leveldb::ReadOptions(),
148 &exists, &map_id))
149 return false;
150 if (exists) {
151 int64 ref_count;
152 if (!GetMapRefCount(map_id, &ref_count))
153 return false;
154 if (ref_count > 1) {
155 if (!DeepCopyArea(namespace_id, origin, !clear_all_first,
156 &map_id, &batch))
157 return false;
158 } else if (clear_all_first) {
159 if (!ClearMap(map_id, &batch))
160 return false;
162 } else {
163 // Map doesn't exist, create it now if needed.
164 if (!changes.empty()) {
165 if (!CreateMapForArea(namespace_id, origin, &map_id, &batch))
166 return false;
170 WriteValuesToMap(map_id, changes, &batch);
172 leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch);
173 UMA_HISTOGRAM_ENUMERATION("SessionStorageDatabase.Commit",
174 leveldb_env::GetLevelDBStatusUMAValue(s),
175 leveldb_env::LEVELDB_STATUS_MAX);
176 return DatabaseErrorCheck(s.ok());
179 bool SessionStorageDatabase::CloneNamespace(
180 const std::string& namespace_id, const std::string& new_namespace_id) {
181 // Go through all origins in the namespace |namespace_id|, create placeholders
182 // for them in |new_namespace_id|, and associate them with the existing maps.
184 // Example, data before shallow copy:
185 // | map-1- | 1 (refcount) |
186 // | map-1-a | b |
187 // | namespace-1- (1 = namespace id)| dummy |
188 // | namespace-1-origin1 | 1 (mapid) |
190 // Example, data after shallow copy:
191 // | map-1- | 2 (inc. refcount) |
192 // | map-1-a | b |
193 // | namespace-1-(1 = namespace id) | dummy |
194 // | namespace-1-origin1 | 1 (mapid) |
195 // | namespace-2- | dummy |
196 // | namespace-2-origin1 | 1 (mapid) << references the same map
198 if (!LazyOpen(true))
199 return false;
200 DBOperation operation(this);
202 leveldb::WriteBatch batch;
203 const bool kOkIfExists = false;
204 if (!CreateNamespace(new_namespace_id, kOkIfExists, &batch))
205 return false;
207 std::map<std::string, std::string> areas;
208 if (!GetAreasInNamespace(namespace_id, &areas))
209 return false;
211 for (std::map<std::string, std::string>::const_iterator it = areas.begin();
212 it != areas.end(); ++it) {
213 const std::string& origin = it->first;
214 const std::string& map_id = it->second;
215 if (!IncreaseMapRefCount(map_id, &batch))
216 return false;
217 AddAreaToNamespace(new_namespace_id, origin, map_id, &batch);
219 leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch);
220 return DatabaseErrorCheck(s.ok());
223 bool SessionStorageDatabase::DeleteArea(const std::string& namespace_id,
224 const GURL& origin) {
225 if (!LazyOpen(false)) {
226 // No need to create the database if it doesn't exist.
227 return true;
229 DBOperation operation(this);
230 leveldb::WriteBatch batch;
231 if (!DeleteAreaHelper(namespace_id, origin.spec(), &batch))
232 return false;
233 leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch);
234 return DatabaseErrorCheck(s.ok());
237 bool SessionStorageDatabase::DeleteNamespace(const std::string& namespace_id) {
239 // The caller should have called other methods to open the DB before this
240 // function. Otherwise, DB stores nothing interesting related to the
241 // specified namespace.
242 // Do nothing if the DB is not open (or we know it has failed already),
243 base::AutoLock auto_lock(db_lock_);
244 if (!IsOpen() || db_error_ || is_inconsistent_)
245 return false;
247 DBOperation operation(this);
248 // Itereate through the areas in the namespace.
249 leveldb::WriteBatch batch;
250 std::map<std::string, std::string> areas;
251 if (!GetAreasInNamespace(namespace_id, &areas))
252 return false;
253 for (std::map<std::string, std::string>::const_iterator it = areas.begin();
254 it != areas.end(); ++it) {
255 const std::string& origin = it->first;
256 if (!DeleteAreaHelper(namespace_id, origin, &batch))
257 return false;
259 batch.Delete(NamespaceStartKey(namespace_id));
260 leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch);
261 return DatabaseErrorCheck(s.ok());
264 bool SessionStorageDatabase::ReadNamespacesAndOrigins(
265 std::map<std::string, std::vector<GURL> >* namespaces_and_origins) {
266 if (!LazyOpen(true))
267 return false;
268 DBOperation operation(this);
270 // While ReadNamespacesAndOrigins is in progress, another thread can call
271 // CommitAreaChanges. To protect the reading operation, create a snapshot and
272 // read from it.
273 leveldb::ReadOptions options;
274 options.snapshot = db_->GetSnapshot();
276 std::string namespace_prefix = NamespacePrefix();
277 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(options));
278 it->Seek(namespace_prefix);
279 // If the key is not found, the status of the iterator won't be IsNotFound(),
280 // but the iterator will be invalid.
281 if (!it->Valid()) {
282 db_->ReleaseSnapshot(options.snapshot);
283 return true;
286 if (!DatabaseErrorCheck(it->status().ok())) {
287 db_->ReleaseSnapshot(options.snapshot);
288 return false;
291 // Skip the dummy entry "namespace-" and iterate the namespaces.
292 std::string current_namespace_start_key;
293 std::string current_namespace_id;
294 for (it->Next(); it->Valid(); it->Next()) {
295 std::string key = it->key().ToString();
296 if (key.find(namespace_prefix) != 0) {
297 // Iterated past the "namespace-" keys.
298 break;
300 // For each namespace, the first key is "namespace-<namespaceid>-", and the
301 // subsequent keys are "namespace-<namespaceid>-<origin>". Read the unique
302 // "<namespaceid>" parts from the keys.
303 if (current_namespace_start_key.empty() ||
304 key.substr(0, current_namespace_start_key.length()) !=
305 current_namespace_start_key) {
306 // The key is of the form "namespace-<namespaceid>-" for a new
307 // <namespaceid>.
308 current_namespace_start_key = key;
309 current_namespace_id =
310 key.substr(namespace_prefix.length(),
311 key.length() - namespace_prefix.length() - 1);
312 // Ensure that we keep track of the namespace even if it doesn't contain
313 // any origins.
314 namespaces_and_origins->insert(
315 std::make_pair(current_namespace_id, std::vector<GURL>()));
316 } else {
317 // The key is of the form "namespace-<namespaceid>-<origin>".
318 std::string origin = key.substr(current_namespace_start_key.length());
319 (*namespaces_and_origins)[current_namespace_id].push_back(GURL(origin));
322 db_->ReleaseSnapshot(options.snapshot);
323 return true;
326 bool SessionStorageDatabase::LazyOpen(bool create_if_needed) {
327 base::AutoLock auto_lock(db_lock_);
328 if (db_error_ || is_inconsistent_) {
329 // Don't try to open a database that we know has failed already.
330 return false;
332 if (IsOpen())
333 return true;
335 if (!create_if_needed &&
336 (!base::PathExists(file_path_) || base::IsDirectoryEmpty(file_path_))) {
337 // If the directory doesn't exist already and we haven't been asked to
338 // create a file on disk, then we don't bother opening the database. This
339 // means we wait until we absolutely need to put something onto disk before
340 // we do so.
341 return false;
344 leveldb::DB* db;
345 leveldb::Status s = TryToOpen(&db);
346 if (!s.ok()) {
347 LOG(WARNING) << "Failed to open leveldb in " << file_path_.value()
348 << ", error: " << s.ToString();
349 DCHECK(db == NULL);
351 // Clear the directory and try again.
352 base::DeleteFile(file_path_, true);
353 s = TryToOpen(&db);
354 if (!s.ok()) {
355 LOG(WARNING) << "Failed to open leveldb in " << file_path_.value()
356 << ", error: " << s.ToString();
357 UMA_HISTOGRAM_ENUMERATION(session_storage_uma_name,
358 SESSION_STORAGE_UMA_FAIL,
359 SESSION_STORAGE_UMA_MAX);
360 DCHECK(db == NULL);
361 db_error_ = true;
362 return false;
364 UMA_HISTOGRAM_ENUMERATION(session_storage_uma_name,
365 SESSION_STORAGE_UMA_RECREATED,
366 SESSION_STORAGE_UMA_MAX);
367 } else {
368 UMA_HISTOGRAM_ENUMERATION(session_storage_uma_name,
369 SESSION_STORAGE_UMA_SUCCESS,
370 SESSION_STORAGE_UMA_MAX);
372 db_.reset(db);
373 return true;
376 leveldb::Status SessionStorageDatabase::TryToOpen(leveldb::DB** db) {
377 leveldb::Options options;
378 // The directory exists but a valid leveldb database might not exist inside it
379 // (e.g., a subset of the needed files might be missing). Handle this
380 // situation gracefully by creating the database now.
381 options.max_open_files = 0; // Use minimum.
382 options.create_if_missing = true;
383 options.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue;
384 return leveldb::DB::Open(options, file_path_.AsUTF8Unsafe(), db);
387 bool SessionStorageDatabase::IsOpen() const {
388 return db_.get() != NULL;
391 bool SessionStorageDatabase::CallerErrorCheck(bool ok) const {
392 DCHECK(ok);
393 return ok;
396 bool SessionStorageDatabase::ConsistencyCheck(bool ok) {
397 if (ok)
398 return true;
399 base::AutoLock auto_lock(db_lock_);
400 // We cannot recover the database during this run, e.g., the upper layer can
401 // have a different understanding of the database state (shallow and deep
402 // copies). Make further operations fail. The next operation that finishes
403 // will delete the data, and next run will recerate the database.
404 is_inconsistent_ = true;
405 return false;
408 bool SessionStorageDatabase::DatabaseErrorCheck(bool ok) {
409 if (ok)
410 return true;
411 base::AutoLock auto_lock(db_lock_);
412 // Make further operations fail. The next operation that finishes
413 // will delete the data, and next run will recerate the database.
414 db_error_ = true;
415 return false;
418 bool SessionStorageDatabase::CreateNamespace(const std::string& namespace_id,
419 bool ok_if_exists,
420 leveldb::WriteBatch* batch) {
421 leveldb::Slice namespace_prefix = NamespacePrefix();
422 std::string dummy;
423 leveldb::Status s = db_->Get(leveldb::ReadOptions(), namespace_prefix,
424 &dummy);
425 if (!DatabaseErrorCheck(s.ok() || s.IsNotFound()))
426 return false;
427 if (s.IsNotFound())
428 batch->Put(namespace_prefix, "");
430 std::string namespace_start_key = NamespaceStartKey(namespace_id);
431 s = db_->Get(leveldb::ReadOptions(), namespace_start_key, &dummy);
432 if (!DatabaseErrorCheck(s.ok() || s.IsNotFound()))
433 return false;
434 if (s.IsNotFound()) {
435 batch->Put(namespace_start_key, "");
436 return true;
438 return CallerErrorCheck(ok_if_exists);
441 bool SessionStorageDatabase::GetAreasInNamespace(
442 const std::string& namespace_id,
443 std::map<std::string, std::string>* areas) {
444 std::string namespace_start_key = NamespaceStartKey(namespace_id);
445 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions()));
446 it->Seek(namespace_start_key);
447 // If the key is not found, the status of the iterator won't be IsNotFound(),
448 // but the iterator will be invalid.
449 if (!it->Valid()) {
450 // The namespace_start_key is not found when the namespace doesn't contain
451 // any areas. We don't need to do anything.
452 return true;
454 if (!DatabaseErrorCheck(it->status().ok()))
455 return false;
457 // Skip the dummy entry "namespace-<namespaceid>-" and iterate the origins.
458 for (it->Next(); it->Valid(); it->Next()) {
459 std::string key = it->key().ToString();
460 if (key.find(namespace_start_key) != 0) {
461 // Iterated past the origins for this namespace.
462 break;
464 std::string origin = key.substr(namespace_start_key.length());
465 std::string map_id = it->value().ToString();
466 (*areas)[origin] = map_id;
468 return true;
471 void SessionStorageDatabase::AddAreaToNamespace(const std::string& namespace_id,
472 const std::string& origin,
473 const std::string& map_id,
474 leveldb::WriteBatch* batch) {
475 std::string namespace_key = NamespaceKey(namespace_id, origin);
476 batch->Put(namespace_key, map_id);
479 bool SessionStorageDatabase::DeleteAreaHelper(
480 const std::string& namespace_id,
481 const std::string& origin,
482 leveldb::WriteBatch* batch) {
483 std::string map_id;
484 bool exists;
485 if (!GetMapForArea(namespace_id, origin, leveldb::ReadOptions(), &exists,
486 &map_id))
487 return false;
488 if (!exists)
489 return true; // Nothing to delete.
490 if (!DecreaseMapRefCount(map_id, 1, batch))
491 return false;
492 std::string namespace_key = NamespaceKey(namespace_id, origin);
493 batch->Delete(namespace_key);
495 // If this was the only area in the namespace, delete the namespace start key,
496 // too.
497 std::string namespace_start_key = NamespaceStartKey(namespace_id);
498 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions()));
499 it->Seek(namespace_start_key);
500 if (!ConsistencyCheck(it->Valid()))
501 return false;
502 // Advance the iterator 2 times (we still haven't really deleted
503 // namespace_key).
504 it->Next();
505 if (!ConsistencyCheck(it->Valid()))
506 return false;
507 it->Next();
508 if (!it->Valid())
509 return true;
510 std::string key = it->key().ToString();
511 if (key.find(namespace_start_key) != 0)
512 batch->Delete(namespace_start_key);
513 return true;
516 bool SessionStorageDatabase::GetMapForArea(const std::string& namespace_id,
517 const std::string& origin,
518 const leveldb::ReadOptions& options,
519 bool* exists, std::string* map_id) {
520 std::string namespace_key = NamespaceKey(namespace_id, origin);
521 leveldb::Status s = db_->Get(options, namespace_key, map_id);
522 if (s.IsNotFound()) {
523 *exists = false;
524 return true;
526 *exists = true;
527 return DatabaseErrorCheck(s.ok());
530 bool SessionStorageDatabase::CreateMapForArea(const std::string& namespace_id,
531 const GURL& origin,
532 std::string* map_id,
533 leveldb::WriteBatch* batch) {
534 leveldb::Slice next_map_id_key = NextMapIdKey();
535 leveldb::Status s = db_->Get(leveldb::ReadOptions(), next_map_id_key, map_id);
536 if (!DatabaseErrorCheck(s.ok() || s.IsNotFound()))
537 return false;
538 int64 next_map_id = 0;
539 if (s.IsNotFound()) {
540 *map_id = "0";
541 } else {
542 bool conversion_ok = base::StringToInt64(*map_id, &next_map_id);
543 if (!ConsistencyCheck(conversion_ok))
544 return false;
546 batch->Put(next_map_id_key, base::Int64ToString(++next_map_id));
547 std::string namespace_key = NamespaceKey(namespace_id, origin.spec());
548 batch->Put(namespace_key, *map_id);
549 batch->Put(MapRefCountKey(*map_id), "1");
550 return true;
553 bool SessionStorageDatabase::ReadMap(const std::string& map_id,
554 const leveldb::ReadOptions& options,
555 DOMStorageValuesMap* result,
556 bool only_keys) {
557 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(options));
558 std::string map_start_key = MapRefCountKey(map_id);
559 it->Seek(map_start_key);
560 // If the key is not found, the status of the iterator won't be IsNotFound(),
561 // but the iterator will be invalid. The map needs to exist, otherwise we have
562 // a stale map_id in the database.
563 if (!ConsistencyCheck(it->Valid()))
564 return false;
565 if (!DatabaseErrorCheck(it->status().ok()))
566 return false;
567 // Skip the dummy entry "map-<mapid>-".
568 for (it->Next(); it->Valid(); it->Next()) {
569 std::string key = it->key().ToString();
570 if (key.find(map_start_key) != 0) {
571 // Iterated past the keys in this map.
572 break;
574 // Key is of the form "map-<mapid>-<key>".
575 base::string16 key16 =
576 base::UTF8ToUTF16(key.substr(map_start_key.length()));
577 if (only_keys) {
578 (*result)[key16] = base::NullableString16();
579 } else {
580 // Convert the raw data stored in std::string (it->value()) to raw data
581 // stored in base::string16.
582 size_t len = it->value().size() / sizeof(base::char16);
583 const base::char16* data_ptr =
584 reinterpret_cast<const base::char16*>(it->value().data());
585 (*result)[key16] =
586 base::NullableString16(base::string16(data_ptr, len), false);
589 return true;
592 void SessionStorageDatabase::WriteValuesToMap(const std::string& map_id,
593 const DOMStorageValuesMap& values,
594 leveldb::WriteBatch* batch) {
595 for (DOMStorageValuesMap::const_iterator it = values.begin();
596 it != values.end();
597 ++it) {
598 base::NullableString16 value = it->second;
599 std::string key = MapKey(map_id, base::UTF16ToUTF8(it->first));
600 if (value.is_null()) {
601 batch->Delete(key);
602 } else {
603 // Convert the raw data stored in base::string16 to raw data stored in
604 // std::string.
605 const char* data = reinterpret_cast<const char*>(value.string().data());
606 size_t size = value.string().size() * 2;
607 batch->Put(key, leveldb::Slice(data, size));
612 bool SessionStorageDatabase::GetMapRefCount(const std::string& map_id,
613 int64* ref_count) {
614 std::string ref_count_string;
615 leveldb::Status s = db_->Get(leveldb::ReadOptions(),
616 MapRefCountKey(map_id), &ref_count_string);
617 if (!ConsistencyCheck(s.ok()))
618 return false;
619 bool conversion_ok = base::StringToInt64(ref_count_string, ref_count);
620 return ConsistencyCheck(conversion_ok);
623 bool SessionStorageDatabase::IncreaseMapRefCount(const std::string& map_id,
624 leveldb::WriteBatch* batch) {
625 // Increase the ref count for the map.
626 int64 old_ref_count;
627 if (!GetMapRefCount(map_id, &old_ref_count))
628 return false;
629 batch->Put(MapRefCountKey(map_id), base::Int64ToString(++old_ref_count));
630 return true;
633 bool SessionStorageDatabase::DecreaseMapRefCount(const std::string& map_id,
634 int decrease,
635 leveldb::WriteBatch* batch) {
636 // Decrease the ref count for the map.
637 int64 ref_count;
638 if (!GetMapRefCount(map_id, &ref_count))
639 return false;
640 if (!ConsistencyCheck(decrease <= ref_count))
641 return false;
642 ref_count -= decrease;
643 if (ref_count > 0) {
644 batch->Put(MapRefCountKey(map_id), base::Int64ToString(ref_count));
645 } else {
646 // Clear all keys in the map.
647 if (!ClearMap(map_id, batch))
648 return false;
649 batch->Delete(MapRefCountKey(map_id));
651 return true;
654 bool SessionStorageDatabase::ClearMap(const std::string& map_id,
655 leveldb::WriteBatch* batch) {
656 DOMStorageValuesMap values;
657 if (!ReadMap(map_id, leveldb::ReadOptions(), &values, true))
658 return false;
659 for (DOMStorageValuesMap::const_iterator it = values.begin();
660 it != values.end(); ++it)
661 batch->Delete(MapKey(map_id, base::UTF16ToUTF8(it->first)));
662 return true;
665 bool SessionStorageDatabase::DeepCopyArea(
666 const std::string& namespace_id, const GURL& origin, bool copy_data,
667 std::string* map_id, leveldb::WriteBatch* batch) {
668 // Example, data before deep copy:
669 // | namespace-1- (1 = namespace id)| dummy |
670 // | namespace-1-origin1 | 1 (mapid) |
671 // | namespace-2- | dummy |
672 // | namespace-2-origin1 | 1 (mapid) << references the same map
673 // | map-1- | 2 (refcount) |
674 // | map-1-a | b |
676 // Example, data after deep copy copy:
677 // | namespace-1-(1 = namespace id) | dummy |
678 // | namespace-1-origin1 | 1 (mapid) |
679 // | namespace-2- | dummy |
680 // | namespace-2-origin1 | 2 (mapid) << references the new map
681 // | map-1- | 1 (dec. refcount) |
682 // | map-1-a | b |
683 // | map-2- | 1 (refcount) |
684 // | map-2-a | b |
686 // Read the values from the old map here. If we don't need to copy the data,
687 // this can stay empty.
688 DOMStorageValuesMap values;
689 if (copy_data && !ReadMap(*map_id, leveldb::ReadOptions(), &values, false))
690 return false;
691 if (!DecreaseMapRefCount(*map_id, 1, batch))
692 return false;
693 // Create a new map (this will also break the association to the old map) and
694 // write the old data into it. This will write the id of the created map into
695 // |map_id|.
696 if (!CreateMapForArea(namespace_id, origin, map_id, batch))
697 return false;
698 WriteValuesToMap(*map_id, values, batch);
699 return true;
702 std::string SessionStorageDatabase::NamespaceStartKey(
703 const std::string& namespace_id) {
704 return base::StringPrintf("namespace-%s-", namespace_id.c_str());
707 std::string SessionStorageDatabase::NamespaceKey(
708 const std::string& namespace_id, const std::string& origin) {
709 return base::StringPrintf("namespace-%s-%s", namespace_id.c_str(),
710 origin.c_str());
713 const char* SessionStorageDatabase::NamespacePrefix() {
714 return "namespace-";
717 std::string SessionStorageDatabase::MapRefCountKey(const std::string& map_id) {
718 return base::StringPrintf("map-%s-", map_id.c_str());
721 std::string SessionStorageDatabase::MapKey(const std::string& map_id,
722 const std::string& key) {
723 return base::StringPrintf("map-%s-%s", map_id.c_str(), key.c_str());
726 const char* SessionStorageDatabase::NextMapIdKey() {
727 return "next-map-id";
730 } // namespace content