6e47a1fdf1c6bfbfbc786f1371bf5fa141345bcc
[platform/framework/web/crosswalk.git] / src / content / browser / dom_storage / session_storage_database.cc
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.
4
5 #include "content/browser/dom_storage/session_storage_database.h"
6
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"
19
20
21 namespace {
22
23 const char session_storage_uma_name[] = "SessionStorageDatabase.Open";
24
25 enum SessionStorageUMA {
26   SESSION_STORAGE_UMA_SUCCESS,
27   SESSION_STORAGE_UMA_RECREATED,
28   SESSION_STORAGE_UMA_FAIL,
29   SESSION_STORAGE_UMA_MAX
30 };
31
32 }  // namespace
33
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                                  |
51
52 namespace content {
53
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_;
67   }
68
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;
87     }
88   }
89
90  private:
91   SessionStorageDatabase* session_storage_database_;
92 };
93
94
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) {
101 }
102
103 SessionStorageDatabase::~SessionStorageDatabase() {
104 }
105
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);
114
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();
121
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);
128 }
129
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);
140
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;
147
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;
161     }
162     else if (clear_all_first) {
163       if (!ClearMap(map_id, &batch))
164         return false;
165     }
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;
171     }
172   }
173
174   WriteValuesToMap(map_id, changes, &batch);
175
176   leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch);
177   return DatabaseErrorCheck(s.ok());
178 }
179
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.
184
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)           |
190
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
198
199   if (!LazyOpen(true))
200     return false;
201   DBOperation operation(this);
202
203   leveldb::WriteBatch batch;
204   const bool kOkIfExists = false;
205   if (!CreateNamespace(new_namespace_id, kOkIfExists, &batch))
206     return false;
207
208   std::map<std::string, std::string> areas;
209   if (!GetAreasInNamespace(namespace_id, &areas))
210     return false;
211
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);
219   }
220   leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch);
221   return DatabaseErrorCheck(s.ok());
222 }
223
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;
229   }
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());
236 }
237
238 bool SessionStorageDatabase::DeleteNamespace(const std::string& namespace_id) {
239   if (!LazyOpen(false)) {
240     // No need to create the database if it doesn't exist.
241     return true;
242   }
243   DBOperation operation(this);
244   // Itereate through the areas in the namespace.
245   leveldb::WriteBatch batch;
246   std::map<std::string, std::string> areas;
247   if (!GetAreasInNamespace(namespace_id, &areas))
248     return false;
249   for (std::map<std::string, std::string>::const_iterator it = areas.begin();
250        it != areas.end(); ++it) {
251     const std::string& origin = it->first;
252     if (!DeleteAreaHelper(namespace_id, origin, &batch))
253       return false;
254   }
255   batch.Delete(NamespaceStartKey(namespace_id));
256   leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch);
257   return DatabaseErrorCheck(s.ok());
258 }
259
260 bool SessionStorageDatabase::ReadNamespacesAndOrigins(
261     std::map<std::string, std::vector<GURL> >* namespaces_and_origins) {
262   if (!LazyOpen(true))
263     return false;
264   DBOperation operation(this);
265
266   // While ReadNamespacesAndOrigins is in progress, another thread can call
267   // CommitAreaChanges. To protect the reading operation, create a snapshot and
268   // read from it.
269   leveldb::ReadOptions options;
270   options.snapshot = db_->GetSnapshot();
271
272   std::string namespace_prefix = NamespacePrefix();
273   scoped_ptr<leveldb::Iterator> it(db_->NewIterator(options));
274   it->Seek(namespace_prefix);
275   // If the key is not found, the status of the iterator won't be IsNotFound(),
276   // but the iterator will be invalid.
277   if (!it->Valid()) {
278     db_->ReleaseSnapshot(options.snapshot);
279     return true;
280   }
281
282   if (!DatabaseErrorCheck(it->status().ok())) {
283     db_->ReleaseSnapshot(options.snapshot);
284     return false;
285   }
286
287   // Skip the dummy entry "namespace-" and iterate the namespaces.
288   std::string current_namespace_start_key;
289   std::string current_namespace_id;
290   for (it->Next(); it->Valid(); it->Next()) {
291     std::string key = it->key().ToString();
292     if (key.find(namespace_prefix) != 0) {
293       // Iterated past the "namespace-" keys.
294       break;
295     }
296     // For each namespace, the first key is "namespace-<namespaceid>-", and the
297     // subsequent keys are "namespace-<namespaceid>-<origin>". Read the unique
298     // "<namespaceid>" parts from the keys.
299     if (current_namespace_start_key.empty() ||
300         key.substr(0, current_namespace_start_key.length()) !=
301         current_namespace_start_key) {
302       // The key is of the form "namespace-<namespaceid>-" for a new
303       // <namespaceid>.
304       current_namespace_start_key = key;
305       current_namespace_id =
306           key.substr(namespace_prefix.length(),
307                      key.length() - namespace_prefix.length() - 1);
308       // Ensure that we keep track of the namespace even if it doesn't contain
309       // any origins.
310       namespaces_and_origins->insert(
311           std::make_pair(current_namespace_id, std::vector<GURL>()));
312     } else {
313       // The key is of the form "namespace-<namespaceid>-<origin>".
314       std::string origin = key.substr(current_namespace_start_key.length());
315       (*namespaces_and_origins)[current_namespace_id].push_back(GURL(origin));
316     }
317   }
318   db_->ReleaseSnapshot(options.snapshot);
319   return true;
320 }
321
322 bool SessionStorageDatabase::LazyOpen(bool create_if_needed) {
323   base::AutoLock auto_lock(db_lock_);
324   if (db_error_ || is_inconsistent_) {
325     // Don't try to open a database that we know has failed already.
326     return false;
327   }
328   if (IsOpen())
329     return true;
330
331   if (!create_if_needed &&
332       (!base::PathExists(file_path_) || base::IsDirectoryEmpty(file_path_))) {
333     // If the directory doesn't exist already and we haven't been asked to
334     // create a file on disk, then we don't bother opening the database. This
335     // means we wait until we absolutely need to put something onto disk before
336     // we do so.
337     return false;
338   }
339
340   leveldb::DB* db;
341   leveldb::Status s = TryToOpen(&db);
342   if (!s.ok()) {
343     LOG(WARNING) << "Failed to open leveldb in " << file_path_.value()
344                  << ", error: " << s.ToString();
345     DCHECK(db == NULL);
346
347     // Clear the directory and try again.
348     base::DeleteFile(file_path_, true);
349     s = TryToOpen(&db);
350     if (!s.ok()) {
351       LOG(WARNING) << "Failed to open leveldb in " << file_path_.value()
352                    << ", error: " << s.ToString();
353       UMA_HISTOGRAM_ENUMERATION(session_storage_uma_name,
354                                 SESSION_STORAGE_UMA_FAIL,
355                                 SESSION_STORAGE_UMA_MAX);
356       DCHECK(db == NULL);
357       db_error_ = true;
358       return false;
359     }
360     UMA_HISTOGRAM_ENUMERATION(session_storage_uma_name,
361                               SESSION_STORAGE_UMA_RECREATED,
362                               SESSION_STORAGE_UMA_MAX);
363   } else {
364     UMA_HISTOGRAM_ENUMERATION(session_storage_uma_name,
365                               SESSION_STORAGE_UMA_SUCCESS,
366                               SESSION_STORAGE_UMA_MAX);
367   }
368   db_.reset(db);
369   return true;
370 }
371
372 leveldb::Status SessionStorageDatabase::TryToOpen(leveldb::DB** db) {
373   leveldb::Options options;
374   // The directory exists but a valid leveldb database might not exist inside it
375   // (e.g., a subset of the needed files might be missing). Handle this
376   // situation gracefully by creating the database now.
377   options.max_open_files = 0;  // Use minimum.
378   options.create_if_missing = true;
379 #if defined(OS_WIN)
380   return leveldb::DB::Open(options, base::WideToUTF8(file_path_.value()), db);
381 #elif defined(OS_POSIX)
382   return leveldb::DB::Open(options, file_path_.value(), db);
383 #endif
384 }
385
386 bool SessionStorageDatabase::IsOpen() const {
387   return db_.get() != NULL;
388 }
389
390 bool SessionStorageDatabase::CallerErrorCheck(bool ok) const {
391   DCHECK(ok);
392   return ok;
393 }
394
395 bool SessionStorageDatabase::ConsistencyCheck(bool ok) {
396   if (ok)
397     return true;
398   base::AutoLock auto_lock(db_lock_);
399   // We cannot recover the database during this run, e.g., the upper layer can
400   // have a different understanding of the database state (shallow and deep
401   // copies). Make further operations fail. The next operation that finishes
402   // will delete the data, and next run will recerate the database.
403   is_inconsistent_ = true;
404   return false;
405 }
406
407 bool SessionStorageDatabase::DatabaseErrorCheck(bool ok) {
408   if (ok)
409     return true;
410   base::AutoLock auto_lock(db_lock_);
411   // Make further operations fail. The next operation that finishes
412   // will delete the data, and next run will recerate the database.
413   db_error_ = true;
414   return false;
415 }
416
417 bool SessionStorageDatabase::CreateNamespace(const std::string& namespace_id,
418                                              bool ok_if_exists,
419                                              leveldb::WriteBatch* batch) {
420   leveldb::Slice namespace_prefix = NamespacePrefix();
421   std::string dummy;
422   leveldb::Status s = db_->Get(leveldb::ReadOptions(), namespace_prefix,
423                                &dummy);
424   if (!DatabaseErrorCheck(s.ok() || s.IsNotFound()))
425     return false;
426   if (s.IsNotFound())
427     batch->Put(namespace_prefix, "");
428
429   std::string namespace_start_key = NamespaceStartKey(namespace_id);
430   s = db_->Get(leveldb::ReadOptions(), namespace_start_key, &dummy);
431   if (!DatabaseErrorCheck(s.ok() || s.IsNotFound()))
432     return false;
433   if (s.IsNotFound()) {
434     batch->Put(namespace_start_key, "");
435     return true;
436   }
437   return CallerErrorCheck(ok_if_exists);
438 }
439
440 bool SessionStorageDatabase::GetAreasInNamespace(
441     const std::string& namespace_id,
442     std::map<std::string, std::string>* areas) {
443   std::string namespace_start_key = NamespaceStartKey(namespace_id);
444   scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions()));
445   it->Seek(namespace_start_key);
446   // If the key is not found, the status of the iterator won't be IsNotFound(),
447   // but the iterator will be invalid.
448   if (!it->Valid()) {
449     // The namespace_start_key is not found when the namespace doesn't contain
450     // any areas. We don't need to do anything.
451     return true;
452   }
453   if (!DatabaseErrorCheck(it->status().ok()))
454     return false;
455
456   // Skip the dummy entry "namespace-<namespaceid>-" and iterate the origins.
457   for (it->Next(); it->Valid(); it->Next()) {
458     std::string key = it->key().ToString();
459     if (key.find(namespace_start_key) != 0) {
460       // Iterated past the origins for this namespace.
461       break;
462     }
463     std::string origin = key.substr(namespace_start_key.length());
464     std::string map_id = it->value().ToString();
465     (*areas)[origin] = map_id;
466   }
467   return true;
468 }
469
470 void SessionStorageDatabase::AddAreaToNamespace(const std::string& namespace_id,
471                                                 const std::string& origin,
472                                                 const std::string& map_id,
473                                                 leveldb::WriteBatch* batch) {
474   std::string namespace_key = NamespaceKey(namespace_id, origin);
475   batch->Put(namespace_key, map_id);
476 }
477
478 bool SessionStorageDatabase::DeleteAreaHelper(
479     const std::string& namespace_id,
480     const std::string& origin,
481     leveldb::WriteBatch* batch) {
482   std::string map_id;
483   bool exists;
484   if (!GetMapForArea(namespace_id, origin, leveldb::ReadOptions(), &exists,
485                      &map_id))
486     return false;
487   if (!exists)
488     return true;  // Nothing to delete.
489   if (!DecreaseMapRefCount(map_id, 1, batch))
490     return false;
491   std::string namespace_key = NamespaceKey(namespace_id, origin);
492   batch->Delete(namespace_key);
493
494   // If this was the only area in the namespace, delete the namespace start key,
495   // too.
496   std::string namespace_start_key = NamespaceStartKey(namespace_id);
497   scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions()));
498   it->Seek(namespace_start_key);
499   if (!ConsistencyCheck(it->Valid()))
500     return false;
501   // Advance the iterator 2 times (we still haven't really deleted
502   // namespace_key).
503   it->Next();
504   if (!ConsistencyCheck(it->Valid()))
505     return false;
506   it->Next();
507   if (!it->Valid())
508     return true;
509   std::string key = it->key().ToString();
510   if (key.find(namespace_start_key) != 0)
511     batch->Delete(namespace_start_key);
512   return true;
513 }
514
515 bool SessionStorageDatabase::GetMapForArea(const std::string& namespace_id,
516                                            const std::string& origin,
517                                            const leveldb::ReadOptions& options,
518                                            bool* exists, std::string* map_id) {
519   std::string namespace_key = NamespaceKey(namespace_id, origin);
520   leveldb::Status s = db_->Get(options, namespace_key, map_id);
521   if (s.IsNotFound()) {
522     *exists = false;
523     return true;
524   }
525   *exists = true;
526   return DatabaseErrorCheck(s.ok());
527 }
528
529 bool SessionStorageDatabase::CreateMapForArea(const std::string& namespace_id,
530                                               const GURL& origin,
531                                               std::string* map_id,
532                                               leveldb::WriteBatch* batch) {
533   leveldb::Slice next_map_id_key = NextMapIdKey();
534   leveldb::Status s = db_->Get(leveldb::ReadOptions(), next_map_id_key, map_id);
535   if (!DatabaseErrorCheck(s.ok() || s.IsNotFound()))
536     return false;
537   int64 next_map_id = 0;
538   if (s.IsNotFound()) {
539     *map_id = "0";
540   } else {
541     bool conversion_ok = base::StringToInt64(*map_id, &next_map_id);
542     if (!ConsistencyCheck(conversion_ok))
543       return false;
544   }
545   batch->Put(next_map_id_key, base::Int64ToString(++next_map_id));
546   std::string namespace_key = NamespaceKey(namespace_id, origin.spec());
547   batch->Put(namespace_key, *map_id);
548   batch->Put(MapRefCountKey(*map_id), "1");
549   return true;
550 }
551
552 bool SessionStorageDatabase::ReadMap(const std::string& map_id,
553                                      const leveldb::ReadOptions& options,
554                                      DOMStorageValuesMap* result,
555                                      bool only_keys) {
556   scoped_ptr<leveldb::Iterator> it(db_->NewIterator(options));
557   std::string map_start_key = MapRefCountKey(map_id);
558   it->Seek(map_start_key);
559   // If the key is not found, the status of the iterator won't be IsNotFound(),
560   // but the iterator will be invalid. The map needs to exist, otherwise we have
561   // a stale map_id in the database.
562   if (!ConsistencyCheck(it->Valid()))
563     return false;
564   if (!DatabaseErrorCheck(it->status().ok()))
565     return false;
566   // Skip the dummy entry "map-<mapid>-".
567   for (it->Next(); it->Valid(); it->Next()) {
568     std::string key = it->key().ToString();
569     if (key.find(map_start_key) != 0) {
570       // Iterated past the keys in this map.
571       break;
572     }
573     // Key is of the form "map-<mapid>-<key>".
574     base::string16 key16 =
575         base::UTF8ToUTF16(key.substr(map_start_key.length()));
576     if (only_keys) {
577       (*result)[key16] = base::NullableString16();
578     } else {
579       // Convert the raw data stored in std::string (it->value()) to raw data
580       // stored in base::string16.
581       size_t len = it->value().size() / sizeof(base::char16);
582       const base::char16* data_ptr =
583           reinterpret_cast<const base::char16*>(it->value().data());
584       (*result)[key16] =
585           base::NullableString16(base::string16(data_ptr, len), false);
586     }
587   }
588   return true;
589 }
590
591 void SessionStorageDatabase::WriteValuesToMap(const std::string& map_id,
592                                               const DOMStorageValuesMap& values,
593                                               leveldb::WriteBatch* batch) {
594   for (DOMStorageValuesMap::const_iterator it = values.begin();
595        it != values.end();
596        ++it) {
597     base::NullableString16 value = it->second;
598     std::string key = MapKey(map_id, base::UTF16ToUTF8(it->first));
599     if (value.is_null()) {
600       batch->Delete(key);
601     } else {
602       // Convert the raw data stored in base::string16 to raw data stored in
603       // std::string.
604       const char* data = reinterpret_cast<const char*>(value.string().data());
605       size_t size = value.string().size() * 2;
606       batch->Put(key, leveldb::Slice(data, size));
607     }
608   }
609 }
610
611 bool SessionStorageDatabase::GetMapRefCount(const std::string& map_id,
612                                             int64* ref_count) {
613   std::string ref_count_string;
614   leveldb::Status s = db_->Get(leveldb::ReadOptions(),
615                                MapRefCountKey(map_id), &ref_count_string);
616   if (!ConsistencyCheck(s.ok()))
617     return false;
618   bool conversion_ok = base::StringToInt64(ref_count_string, ref_count);
619   return ConsistencyCheck(conversion_ok);
620 }
621
622 bool SessionStorageDatabase::IncreaseMapRefCount(const std::string& map_id,
623                                                  leveldb::WriteBatch* batch) {
624   // Increase the ref count for the map.
625   int64 old_ref_count;
626   if (!GetMapRefCount(map_id, &old_ref_count))
627     return false;
628   batch->Put(MapRefCountKey(map_id), base::Int64ToString(++old_ref_count));
629   return true;
630 }
631
632 bool SessionStorageDatabase::DecreaseMapRefCount(const std::string& map_id,
633                                                  int decrease,
634                                                  leveldb::WriteBatch* batch) {
635   // Decrease the ref count for the map.
636   int64 ref_count;
637   if (!GetMapRefCount(map_id, &ref_count))
638     return false;
639   if (!ConsistencyCheck(decrease <= ref_count))
640     return false;
641   ref_count -= decrease;
642   if (ref_count > 0) {
643     batch->Put(MapRefCountKey(map_id), base::Int64ToString(ref_count));
644   } else {
645     // Clear all keys in the map.
646     if (!ClearMap(map_id, batch))
647       return false;
648     batch->Delete(MapRefCountKey(map_id));
649   }
650   return true;
651 }
652
653 bool SessionStorageDatabase::ClearMap(const std::string& map_id,
654                                       leveldb::WriteBatch* batch) {
655   DOMStorageValuesMap values;
656   if (!ReadMap(map_id, leveldb::ReadOptions(), &values, true))
657     return false;
658   for (DOMStorageValuesMap::const_iterator it = values.begin();
659        it != values.end(); ++it)
660     batch->Delete(MapKey(map_id, base::UTF16ToUTF8(it->first)));
661   return true;
662 }
663
664 bool SessionStorageDatabase::DeepCopyArea(
665     const std::string& namespace_id, const GURL& origin, bool copy_data,
666     std::string* map_id, leveldb::WriteBatch* batch) {
667   // Example, data before deep copy:
668   // | namespace-1- (1 = namespace id)| dummy               |
669   // | namespace-1-origin1            | 1 (mapid)           |
670   // | namespace-2-                   | dummy               |
671   // | namespace-2-origin1            | 1 (mapid) << references the same map
672   // | map-1-                         | 2 (refcount)        |
673   // | map-1-a                        | b                   |
674
675   // Example, data after deep copy copy:
676   // | namespace-1-(1 = namespace id) | dummy               |
677   // | namespace-1-origin1            | 1 (mapid)           |
678   // | namespace-2-                   | dummy               |
679   // | namespace-2-origin1            | 2 (mapid) << references the new map
680   // | map-1-                         | 1 (dec. refcount)   |
681   // | map-1-a                        | b                   |
682   // | map-2-                         | 1 (refcount)        |
683   // | map-2-a                        | b                   |
684
685   // Read the values from the old map here. If we don't need to copy the data,
686   // this can stay empty.
687   DOMStorageValuesMap values;
688   if (copy_data && !ReadMap(*map_id, leveldb::ReadOptions(), &values, false))
689     return false;
690   if (!DecreaseMapRefCount(*map_id, 1, batch))
691     return false;
692   // Create a new map (this will also break the association to the old map) and
693   // write the old data into it. This will write the id of the created map into
694   // |map_id|.
695   if (!CreateMapForArea(namespace_id, origin, map_id, batch))
696     return false;
697   WriteValuesToMap(*map_id, values, batch);
698   return true;
699 }
700
701 std::string SessionStorageDatabase::NamespaceStartKey(
702     const std::string& namespace_id) {
703   return base::StringPrintf("namespace-%s-", namespace_id.c_str());
704 }
705
706 std::string SessionStorageDatabase::NamespaceKey(
707     const std::string& namespace_id, const std::string& origin) {
708   return base::StringPrintf("namespace-%s-%s", namespace_id.c_str(),
709                             origin.c_str());
710 }
711
712 const char* SessionStorageDatabase::NamespacePrefix() {
713   return "namespace-";
714 }
715
716 std::string SessionStorageDatabase::MapRefCountKey(const std::string& map_id) {
717   return base::StringPrintf("map-%s-", map_id.c_str());
718 }
719
720 std::string SessionStorageDatabase::MapKey(const std::string& map_id,
721                                            const std::string& key) {
722   return base::StringPrintf("map-%s-%s", map_id.c_str(), key.c_str());
723 }
724
725 const char* SessionStorageDatabase::NextMapIdKey() {
726   return "next-map-id";
727 }
728
729 }  // namespace content