ozone: evdev: Sync caps lock LED state to evdev
[chromium-blink-merge.git] / content / browser / dom_storage / session_storage_database.cc
blob3afc66923d44b1b27f8411287889a55aa13f7231
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/src/include/leveldb/db.h"
14 #include "third_party/leveldatabase/src/include/leveldb/iterator.h"
15 #include "third_party/leveldatabase/src/include/leveldb/options.h"
16 #include "third_party/leveldatabase/src/include/leveldb/status.h"
17 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
18 #include "url/gurl.h"
21 namespace {
23 const char session_storage_uma_name[] = "SessionStorageDatabase.Open";
25 enum SessionStorageUMA {
26 SESSION_STORAGE_UMA_SUCCESS,
27 SESSION_STORAGE_UMA_RECREATED,
28 SESSION_STORAGE_UMA_FAIL,
29 SESSION_STORAGE_UMA_MAX
32 } // namespace
34 // Layout of the database:
35 // | key | value |
36 // -----------------------------------------------------------------------
37 // | map-1- | 2 (refcount, start of map-1-* keys)|
38 // | map-1-a | b (a = b in map 1) |
39 // | ... | |
40 // | namespace- | dummy (start of namespace-* keys) |
41 // | namespace-1- (1 = namespace id)| dummy (start of namespace-1-* keys)|
42 // | namespace-1-origin1 | 1 (mapid) |
43 // | namespace-1-origin2 | 2 |
44 // | namespace-2- | dummy |
45 // | namespace-2-origin1 | 1 (shallow copy) |
46 // | namespace-2-origin2 | 2 (shallow copy) |
47 // | namespace-3- | dummy |
48 // | namespace-3-origin1 | 3 (deep copy) |
49 // | namespace-3-origin2 | 2 (shallow copy) |
50 // | next-map-id | 4 |
52 namespace content {
54 // This class keeps track of ongoing operations across different threads. When
55 // DB inconsistency is detected, we need to 1) make sure no new operations start
56 // 2) wait until all current operations finish, and let the last one of them
57 // close the DB and delete the data. The DB will remain empty for the rest of
58 // the run, and will be recreated during the next run. We cannot hope to recover
59 // during this run, since the upper layer will have a different idea about what
60 // should be in the database.
61 class SessionStorageDatabase::DBOperation {
62 public:
63 DBOperation(SessionStorageDatabase* session_storage_database)
64 : session_storage_database_(session_storage_database) {
65 base::AutoLock auto_lock(session_storage_database_->db_lock_);
66 ++session_storage_database_->operation_count_;
69 ~DBOperation() {
70 base::AutoLock auto_lock(session_storage_database_->db_lock_);
71 --session_storage_database_->operation_count_;
72 if ((session_storage_database_->is_inconsistent_ ||
73 session_storage_database_->db_error_) &&
74 session_storage_database_->operation_count_ == 0 &&
75 !session_storage_database_->invalid_db_deleted_) {
76 // No other operations are ongoing and the data is bad -> delete it now.
77 session_storage_database_->db_.reset();
78 #if defined(OS_WIN)
79 leveldb::DestroyDB(
80 base::WideToUTF8(session_storage_database_->file_path_.value()),
81 leveldb::Options());
82 #else
83 leveldb::DestroyDB(session_storage_database_->file_path_.value(),
84 leveldb::Options());
85 #endif
86 session_storage_database_->invalid_db_deleted_ = true;
90 private:
91 SessionStorageDatabase* session_storage_database_;
95 SessionStorageDatabase::SessionStorageDatabase(const base::FilePath& file_path)
96 : file_path_(file_path),
97 db_error_(false),
98 is_inconsistent_(false),
99 invalid_db_deleted_(false),
100 operation_count_(0) {
103 SessionStorageDatabase::~SessionStorageDatabase() {
106 void SessionStorageDatabase::ReadAreaValues(const std::string& namespace_id,
107 const GURL& origin,
108 DOMStorageValuesMap* result) {
109 // We don't create a database if it doesn't exist. In that case, there is
110 // nothing to be added to the result.
111 if (!LazyOpen(false))
112 return;
113 DBOperation operation(this);
115 // While ReadAreaValues is in progress, another thread can call
116 // CommitAreaChanges. CommitAreaChanges might update map ref count key while
117 // this thread is iterating over the map ref count key. To protect the reading
118 // operation, create a snapshot and read from it.
119 leveldb::ReadOptions options;
120 options.snapshot = db_->GetSnapshot();
122 std::string map_id;
123 bool exists;
124 if (GetMapForArea(namespace_id, origin.spec(), options, &exists, &map_id) &&
125 exists)
126 ReadMap(map_id, options, result, false);
127 db_->ReleaseSnapshot(options.snapshot);
130 bool SessionStorageDatabase::CommitAreaChanges(
131 const std::string& namespace_id,
132 const GURL& origin,
133 bool clear_all_first,
134 const DOMStorageValuesMap& changes) {
135 // Even if |changes| is empty, we need to write the appropriate placeholders
136 // in the database, so that it can be later shallow-copied succssfully.
137 if (!LazyOpen(true))
138 return false;
139 DBOperation operation(this);
141 leveldb::WriteBatch batch;
142 // Ensure that the keys "namespace-" "namespace-N" (see the schema above)
143 // exist.
144 const bool kOkIfExists = true;
145 if (!CreateNamespace(namespace_id, kOkIfExists, &batch))
146 return false;
148 std::string map_id;
149 bool exists;
150 if (!GetMapForArea(namespace_id, origin.spec(), leveldb::ReadOptions(),
151 &exists, &map_id))
152 return false;
153 if (exists) {
154 int64 ref_count;
155 if (!GetMapRefCount(map_id, &ref_count))
156 return false;
157 if (ref_count > 1) {
158 if (!DeepCopyArea(namespace_id, origin, !clear_all_first,
159 &map_id, &batch))
160 return false;
162 else if (clear_all_first) {
163 if (!ClearMap(map_id, &batch))
164 return false;
166 } else {
167 // Map doesn't exist, create it now if needed.
168 if (!changes.empty()) {
169 if (!CreateMapForArea(namespace_id, origin, &map_id, &batch))
170 return false;
174 WriteValuesToMap(map_id, changes, &batch);
176 leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch);
177 return DatabaseErrorCheck(s.ok());
180 bool SessionStorageDatabase::CloneNamespace(
181 const std::string& namespace_id, const std::string& new_namespace_id) {
182 // Go through all origins in the namespace |namespace_id|, create placeholders
183 // for them in |new_namespace_id|, and associate them with the existing maps.
185 // Example, data before shallow copy:
186 // | map-1- | 1 (refcount) |
187 // | map-1-a | b |
188 // | namespace-1- (1 = namespace id)| dummy |
189 // | namespace-1-origin1 | 1 (mapid) |
191 // Example, data after shallow copy:
192 // | map-1- | 2 (inc. refcount) |
193 // | map-1-a | b |
194 // | namespace-1-(1 = namespace id) | dummy |
195 // | namespace-1-origin1 | 1 (mapid) |
196 // | namespace-2- | dummy |
197 // | namespace-2-origin1 | 1 (mapid) << references the same map
199 if (!LazyOpen(true))
200 return false;
201 DBOperation operation(this);
203 leveldb::WriteBatch batch;
204 const bool kOkIfExists = false;
205 if (!CreateNamespace(new_namespace_id, kOkIfExists, &batch))
206 return false;
208 std::map<std::string, std::string> areas;
209 if (!GetAreasInNamespace(namespace_id, &areas))
210 return false;
212 for (std::map<std::string, std::string>::const_iterator it = areas.begin();
213 it != areas.end(); ++it) {
214 const std::string& origin = it->first;
215 const std::string& map_id = it->second;
216 if (!IncreaseMapRefCount(map_id, &batch))
217 return false;
218 AddAreaToNamespace(new_namespace_id, origin, map_id, &batch);
220 leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch);
221 return DatabaseErrorCheck(s.ok());
224 bool SessionStorageDatabase::DeleteArea(const std::string& namespace_id,
225 const GURL& origin) {
226 if (!LazyOpen(false)) {
227 // No need to create the database if it doesn't exist.
228 return true;
230 DBOperation operation(this);
231 leveldb::WriteBatch batch;
232 if (!DeleteAreaHelper(namespace_id, origin.spec(), &batch))
233 return false;
234 leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch);
235 return DatabaseErrorCheck(s.ok());
238 bool SessionStorageDatabase::DeleteNamespace(const std::string& namespace_id) {
240 // The caller should have called other methods to open the DB before this
241 // function. Otherwise, DB stores nothing interesting related to the
242 // specified namespace.
243 // Do nothing if the DB is not open (or we know it has failed already),
244 base::AutoLock auto_lock(db_lock_);
245 if (!IsOpen() || db_error_ || is_inconsistent_)
246 return false;
248 DBOperation operation(this);
249 // Itereate through the areas in the namespace.
250 leveldb::WriteBatch batch;
251 std::map<std::string, std::string> areas;
252 if (!GetAreasInNamespace(namespace_id, &areas))
253 return false;
254 for (std::map<std::string, std::string>::const_iterator it = areas.begin();
255 it != areas.end(); ++it) {
256 const std::string& origin = it->first;
257 if (!DeleteAreaHelper(namespace_id, origin, &batch))
258 return false;
260 batch.Delete(NamespaceStartKey(namespace_id));
261 leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch);
262 return DatabaseErrorCheck(s.ok());
265 bool SessionStorageDatabase::ReadNamespacesAndOrigins(
266 std::map<std::string, std::vector<GURL> >* namespaces_and_origins) {
267 if (!LazyOpen(true))
268 return false;
269 DBOperation operation(this);
271 // While ReadNamespacesAndOrigins is in progress, another thread can call
272 // CommitAreaChanges. To protect the reading operation, create a snapshot and
273 // read from it.
274 leveldb::ReadOptions options;
275 options.snapshot = db_->GetSnapshot();
277 std::string namespace_prefix = NamespacePrefix();
278 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(options));
279 it->Seek(namespace_prefix);
280 // If the key is not found, the status of the iterator won't be IsNotFound(),
281 // but the iterator will be invalid.
282 if (!it->Valid()) {
283 db_->ReleaseSnapshot(options.snapshot);
284 return true;
287 if (!DatabaseErrorCheck(it->status().ok())) {
288 db_->ReleaseSnapshot(options.snapshot);
289 return false;
292 // Skip the dummy entry "namespace-" and iterate the namespaces.
293 std::string current_namespace_start_key;
294 std::string current_namespace_id;
295 for (it->Next(); it->Valid(); it->Next()) {
296 std::string key = it->key().ToString();
297 if (key.find(namespace_prefix) != 0) {
298 // Iterated past the "namespace-" keys.
299 break;
301 // For each namespace, the first key is "namespace-<namespaceid>-", and the
302 // subsequent keys are "namespace-<namespaceid>-<origin>". Read the unique
303 // "<namespaceid>" parts from the keys.
304 if (current_namespace_start_key.empty() ||
305 key.substr(0, current_namespace_start_key.length()) !=
306 current_namespace_start_key) {
307 // The key is of the form "namespace-<namespaceid>-" for a new
308 // <namespaceid>.
309 current_namespace_start_key = key;
310 current_namespace_id =
311 key.substr(namespace_prefix.length(),
312 key.length() - namespace_prefix.length() - 1);
313 // Ensure that we keep track of the namespace even if it doesn't contain
314 // any origins.
315 namespaces_and_origins->insert(
316 std::make_pair(current_namespace_id, std::vector<GURL>()));
317 } else {
318 // The key is of the form "namespace-<namespaceid>-<origin>".
319 std::string origin = key.substr(current_namespace_start_key.length());
320 (*namespaces_and_origins)[current_namespace_id].push_back(GURL(origin));
323 db_->ReleaseSnapshot(options.snapshot);
324 return true;
327 bool SessionStorageDatabase::LazyOpen(bool create_if_needed) {
328 base::AutoLock auto_lock(db_lock_);
329 if (db_error_ || is_inconsistent_) {
330 // Don't try to open a database that we know has failed already.
331 return false;
333 if (IsOpen())
334 return true;
336 if (!create_if_needed &&
337 (!base::PathExists(file_path_) || base::IsDirectoryEmpty(file_path_))) {
338 // If the directory doesn't exist already and we haven't been asked to
339 // create a file on disk, then we don't bother opening the database. This
340 // means we wait until we absolutely need to put something onto disk before
341 // we do so.
342 return false;
345 leveldb::DB* db;
346 leveldb::Status s = TryToOpen(&db);
347 if (!s.ok()) {
348 LOG(WARNING) << "Failed to open leveldb in " << file_path_.value()
349 << ", error: " << s.ToString();
350 DCHECK(db == NULL);
352 // Clear the directory and try again.
353 base::DeleteFile(file_path_, true);
354 s = TryToOpen(&db);
355 if (!s.ok()) {
356 LOG(WARNING) << "Failed to open leveldb in " << file_path_.value()
357 << ", error: " << s.ToString();
358 UMA_HISTOGRAM_ENUMERATION(session_storage_uma_name,
359 SESSION_STORAGE_UMA_FAIL,
360 SESSION_STORAGE_UMA_MAX);
361 DCHECK(db == NULL);
362 db_error_ = true;
363 return false;
365 UMA_HISTOGRAM_ENUMERATION(session_storage_uma_name,
366 SESSION_STORAGE_UMA_RECREATED,
367 SESSION_STORAGE_UMA_MAX);
368 } else {
369 UMA_HISTOGRAM_ENUMERATION(session_storage_uma_name,
370 SESSION_STORAGE_UMA_SUCCESS,
371 SESSION_STORAGE_UMA_MAX);
373 db_.reset(db);
374 return true;
377 leveldb::Status SessionStorageDatabase::TryToOpen(leveldb::DB** db) {
378 leveldb::Options options;
379 // The directory exists but a valid leveldb database might not exist inside it
380 // (e.g., a subset of the needed files might be missing). Handle this
381 // situation gracefully by creating the database now.
382 options.max_open_files = 0; // Use minimum.
383 options.create_if_missing = true;
384 #if defined(OS_WIN)
385 return leveldb::DB::Open(options, base::WideToUTF8(file_path_.value()), db);
386 #elif defined(OS_POSIX)
387 return leveldb::DB::Open(options, file_path_.value(), db);
388 #endif
391 bool SessionStorageDatabase::IsOpen() const {
392 return db_.get() != NULL;
395 bool SessionStorageDatabase::CallerErrorCheck(bool ok) const {
396 DCHECK(ok);
397 return ok;
400 bool SessionStorageDatabase::ConsistencyCheck(bool ok) {
401 if (ok)
402 return true;
403 base::AutoLock auto_lock(db_lock_);
404 // We cannot recover the database during this run, e.g., the upper layer can
405 // have a different understanding of the database state (shallow and deep
406 // copies). Make further operations fail. The next operation that finishes
407 // will delete the data, and next run will recerate the database.
408 is_inconsistent_ = true;
409 return false;
412 bool SessionStorageDatabase::DatabaseErrorCheck(bool ok) {
413 if (ok)
414 return true;
415 base::AutoLock auto_lock(db_lock_);
416 // Make further operations fail. The next operation that finishes
417 // will delete the data, and next run will recerate the database.
418 db_error_ = true;
419 return false;
422 bool SessionStorageDatabase::CreateNamespace(const std::string& namespace_id,
423 bool ok_if_exists,
424 leveldb::WriteBatch* batch) {
425 leveldb::Slice namespace_prefix = NamespacePrefix();
426 std::string dummy;
427 leveldb::Status s = db_->Get(leveldb::ReadOptions(), namespace_prefix,
428 &dummy);
429 if (!DatabaseErrorCheck(s.ok() || s.IsNotFound()))
430 return false;
431 if (s.IsNotFound())
432 batch->Put(namespace_prefix, "");
434 std::string namespace_start_key = NamespaceStartKey(namespace_id);
435 s = db_->Get(leveldb::ReadOptions(), namespace_start_key, &dummy);
436 if (!DatabaseErrorCheck(s.ok() || s.IsNotFound()))
437 return false;
438 if (s.IsNotFound()) {
439 batch->Put(namespace_start_key, "");
440 return true;
442 return CallerErrorCheck(ok_if_exists);
445 bool SessionStorageDatabase::GetAreasInNamespace(
446 const std::string& namespace_id,
447 std::map<std::string, std::string>* areas) {
448 std::string namespace_start_key = NamespaceStartKey(namespace_id);
449 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions()));
450 it->Seek(namespace_start_key);
451 // If the key is not found, the status of the iterator won't be IsNotFound(),
452 // but the iterator will be invalid.
453 if (!it->Valid()) {
454 // The namespace_start_key is not found when the namespace doesn't contain
455 // any areas. We don't need to do anything.
456 return true;
458 if (!DatabaseErrorCheck(it->status().ok()))
459 return false;
461 // Skip the dummy entry "namespace-<namespaceid>-" and iterate the origins.
462 for (it->Next(); it->Valid(); it->Next()) {
463 std::string key = it->key().ToString();
464 if (key.find(namespace_start_key) != 0) {
465 // Iterated past the origins for this namespace.
466 break;
468 std::string origin = key.substr(namespace_start_key.length());
469 std::string map_id = it->value().ToString();
470 (*areas)[origin] = map_id;
472 return true;
475 void SessionStorageDatabase::AddAreaToNamespace(const std::string& namespace_id,
476 const std::string& origin,
477 const std::string& map_id,
478 leveldb::WriteBatch* batch) {
479 std::string namespace_key = NamespaceKey(namespace_id, origin);
480 batch->Put(namespace_key, map_id);
483 bool SessionStorageDatabase::DeleteAreaHelper(
484 const std::string& namespace_id,
485 const std::string& origin,
486 leveldb::WriteBatch* batch) {
487 std::string map_id;
488 bool exists;
489 if (!GetMapForArea(namespace_id, origin, leveldb::ReadOptions(), &exists,
490 &map_id))
491 return false;
492 if (!exists)
493 return true; // Nothing to delete.
494 if (!DecreaseMapRefCount(map_id, 1, batch))
495 return false;
496 std::string namespace_key = NamespaceKey(namespace_id, origin);
497 batch->Delete(namespace_key);
499 // If this was the only area in the namespace, delete the namespace start key,
500 // too.
501 std::string namespace_start_key = NamespaceStartKey(namespace_id);
502 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions()));
503 it->Seek(namespace_start_key);
504 if (!ConsistencyCheck(it->Valid()))
505 return false;
506 // Advance the iterator 2 times (we still haven't really deleted
507 // namespace_key).
508 it->Next();
509 if (!ConsistencyCheck(it->Valid()))
510 return false;
511 it->Next();
512 if (!it->Valid())
513 return true;
514 std::string key = it->key().ToString();
515 if (key.find(namespace_start_key) != 0)
516 batch->Delete(namespace_start_key);
517 return true;
520 bool SessionStorageDatabase::GetMapForArea(const std::string& namespace_id,
521 const std::string& origin,
522 const leveldb::ReadOptions& options,
523 bool* exists, std::string* map_id) {
524 std::string namespace_key = NamespaceKey(namespace_id, origin);
525 leveldb::Status s = db_->Get(options, namespace_key, map_id);
526 if (s.IsNotFound()) {
527 *exists = false;
528 return true;
530 *exists = true;
531 return DatabaseErrorCheck(s.ok());
534 bool SessionStorageDatabase::CreateMapForArea(const std::string& namespace_id,
535 const GURL& origin,
536 std::string* map_id,
537 leveldb::WriteBatch* batch) {
538 leveldb::Slice next_map_id_key = NextMapIdKey();
539 leveldb::Status s = db_->Get(leveldb::ReadOptions(), next_map_id_key, map_id);
540 if (!DatabaseErrorCheck(s.ok() || s.IsNotFound()))
541 return false;
542 int64 next_map_id = 0;
543 if (s.IsNotFound()) {
544 *map_id = "0";
545 } else {
546 bool conversion_ok = base::StringToInt64(*map_id, &next_map_id);
547 if (!ConsistencyCheck(conversion_ok))
548 return false;
550 batch->Put(next_map_id_key, base::Int64ToString(++next_map_id));
551 std::string namespace_key = NamespaceKey(namespace_id, origin.spec());
552 batch->Put(namespace_key, *map_id);
553 batch->Put(MapRefCountKey(*map_id), "1");
554 return true;
557 bool SessionStorageDatabase::ReadMap(const std::string& map_id,
558 const leveldb::ReadOptions& options,
559 DOMStorageValuesMap* result,
560 bool only_keys) {
561 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(options));
562 std::string map_start_key = MapRefCountKey(map_id);
563 it->Seek(map_start_key);
564 // If the key is not found, the status of the iterator won't be IsNotFound(),
565 // but the iterator will be invalid. The map needs to exist, otherwise we have
566 // a stale map_id in the database.
567 if (!ConsistencyCheck(it->Valid()))
568 return false;
569 if (!DatabaseErrorCheck(it->status().ok()))
570 return false;
571 // Skip the dummy entry "map-<mapid>-".
572 for (it->Next(); it->Valid(); it->Next()) {
573 std::string key = it->key().ToString();
574 if (key.find(map_start_key) != 0) {
575 // Iterated past the keys in this map.
576 break;
578 // Key is of the form "map-<mapid>-<key>".
579 base::string16 key16 =
580 base::UTF8ToUTF16(key.substr(map_start_key.length()));
581 if (only_keys) {
582 (*result)[key16] = base::NullableString16();
583 } else {
584 // Convert the raw data stored in std::string (it->value()) to raw data
585 // stored in base::string16.
586 size_t len = it->value().size() / sizeof(base::char16);
587 const base::char16* data_ptr =
588 reinterpret_cast<const base::char16*>(it->value().data());
589 (*result)[key16] =
590 base::NullableString16(base::string16(data_ptr, len), false);
593 return true;
596 void SessionStorageDatabase::WriteValuesToMap(const std::string& map_id,
597 const DOMStorageValuesMap& values,
598 leveldb::WriteBatch* batch) {
599 for (DOMStorageValuesMap::const_iterator it = values.begin();
600 it != values.end();
601 ++it) {
602 base::NullableString16 value = it->second;
603 std::string key = MapKey(map_id, base::UTF16ToUTF8(it->first));
604 if (value.is_null()) {
605 batch->Delete(key);
606 } else {
607 // Convert the raw data stored in base::string16 to raw data stored in
608 // std::string.
609 const char* data = reinterpret_cast<const char*>(value.string().data());
610 size_t size = value.string().size() * 2;
611 batch->Put(key, leveldb::Slice(data, size));
616 bool SessionStorageDatabase::GetMapRefCount(const std::string& map_id,
617 int64* ref_count) {
618 std::string ref_count_string;
619 leveldb::Status s = db_->Get(leveldb::ReadOptions(),
620 MapRefCountKey(map_id), &ref_count_string);
621 if (!ConsistencyCheck(s.ok()))
622 return false;
623 bool conversion_ok = base::StringToInt64(ref_count_string, ref_count);
624 return ConsistencyCheck(conversion_ok);
627 bool SessionStorageDatabase::IncreaseMapRefCount(const std::string& map_id,
628 leveldb::WriteBatch* batch) {
629 // Increase the ref count for the map.
630 int64 old_ref_count;
631 if (!GetMapRefCount(map_id, &old_ref_count))
632 return false;
633 batch->Put(MapRefCountKey(map_id), base::Int64ToString(++old_ref_count));
634 return true;
637 bool SessionStorageDatabase::DecreaseMapRefCount(const std::string& map_id,
638 int decrease,
639 leveldb::WriteBatch* batch) {
640 // Decrease the ref count for the map.
641 int64 ref_count;
642 if (!GetMapRefCount(map_id, &ref_count))
643 return false;
644 if (!ConsistencyCheck(decrease <= ref_count))
645 return false;
646 ref_count -= decrease;
647 if (ref_count > 0) {
648 batch->Put(MapRefCountKey(map_id), base::Int64ToString(ref_count));
649 } else {
650 // Clear all keys in the map.
651 if (!ClearMap(map_id, batch))
652 return false;
653 batch->Delete(MapRefCountKey(map_id));
655 return true;
658 bool SessionStorageDatabase::ClearMap(const std::string& map_id,
659 leveldb::WriteBatch* batch) {
660 DOMStorageValuesMap values;
661 if (!ReadMap(map_id, leveldb::ReadOptions(), &values, true))
662 return false;
663 for (DOMStorageValuesMap::const_iterator it = values.begin();
664 it != values.end(); ++it)
665 batch->Delete(MapKey(map_id, base::UTF16ToUTF8(it->first)));
666 return true;
669 bool SessionStorageDatabase::DeepCopyArea(
670 const std::string& namespace_id, const GURL& origin, bool copy_data,
671 std::string* map_id, leveldb::WriteBatch* batch) {
672 // Example, data before deep copy:
673 // | namespace-1- (1 = namespace id)| dummy |
674 // | namespace-1-origin1 | 1 (mapid) |
675 // | namespace-2- | dummy |
676 // | namespace-2-origin1 | 1 (mapid) << references the same map
677 // | map-1- | 2 (refcount) |
678 // | map-1-a | b |
680 // Example, data after deep copy copy:
681 // | namespace-1-(1 = namespace id) | dummy |
682 // | namespace-1-origin1 | 1 (mapid) |
683 // | namespace-2- | dummy |
684 // | namespace-2-origin1 | 2 (mapid) << references the new map
685 // | map-1- | 1 (dec. refcount) |
686 // | map-1-a | b |
687 // | map-2- | 1 (refcount) |
688 // | map-2-a | b |
690 // Read the values from the old map here. If we don't need to copy the data,
691 // this can stay empty.
692 DOMStorageValuesMap values;
693 if (copy_data && !ReadMap(*map_id, leveldb::ReadOptions(), &values, false))
694 return false;
695 if (!DecreaseMapRefCount(*map_id, 1, batch))
696 return false;
697 // Create a new map (this will also break the association to the old map) and
698 // write the old data into it. This will write the id of the created map into
699 // |map_id|.
700 if (!CreateMapForArea(namespace_id, origin, map_id, batch))
701 return false;
702 WriteValuesToMap(*map_id, values, batch);
703 return true;
706 std::string SessionStorageDatabase::NamespaceStartKey(
707 const std::string& namespace_id) {
708 return base::StringPrintf("namespace-%s-", namespace_id.c_str());
711 std::string SessionStorageDatabase::NamespaceKey(
712 const std::string& namespace_id, const std::string& origin) {
713 return base::StringPrintf("namespace-%s-%s", namespace_id.c_str(),
714 origin.c_str());
717 const char* SessionStorageDatabase::NamespacePrefix() {
718 return "namespace-";
721 std::string SessionStorageDatabase::MapRefCountKey(const std::string& map_id) {
722 return base::StringPrintf("map-%s-", map_id.c_str());
725 std::string SessionStorageDatabase::MapKey(const std::string& map_id,
726 const std::string& key) {
727 return base::StringPrintf("map-%s-%s", map_id.c_str(), key.c_str());
730 const char* SessionStorageDatabase::NextMapIdKey() {
731 return "next-map-id";
734 } // namespace content