- add sources.
[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/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 SessionStorageDatabase::SessionStorageDatabase(const base::FilePath& file_path)
55     : file_path_(file_path),
56       db_error_(false),
57       is_inconsistent_(false) {
58 }
59
60 SessionStorageDatabase::~SessionStorageDatabase() {
61 }
62
63 void SessionStorageDatabase::ReadAreaValues(const std::string& namespace_id,
64                                             const GURL& origin,
65                                             DOMStorageValuesMap* result) {
66   // We don't create a database if it doesn't exist. In that case, there is
67   // nothing to be added to the result.
68   if (!LazyOpen(false))
69     return;
70
71   // While ReadAreaValues is in progress, another thread can call
72   // CommitAreaChanges. CommitAreaChanges might update map ref count key while
73   // this thread is iterating over the map ref count key. To protect the reading
74   // operation, create a snapshot and read from it.
75   leveldb::ReadOptions options;
76   options.snapshot = db_->GetSnapshot();
77
78   std::string map_id;
79   bool exists;
80   if (GetMapForArea(namespace_id, origin.spec(), options, &exists, &map_id) &&
81       exists)
82     ReadMap(map_id, options, result, false);
83   db_->ReleaseSnapshot(options.snapshot);
84 }
85
86 bool SessionStorageDatabase::CommitAreaChanges(
87     const std::string& namespace_id,
88     const GURL& origin,
89     bool clear_all_first,
90     const DOMStorageValuesMap& changes) {
91   // Even if |changes| is empty, we need to write the appropriate placeholders
92   // in the database, so that it can be later shallow-copied succssfully.
93   if (!LazyOpen(true))
94     return false;
95
96   leveldb::WriteBatch batch;
97   // Ensure that the keys "namespace-" "namespace-N" (see the schema above)
98   // exist.
99   const bool kOkIfExists = true;
100   if (!CreateNamespace(namespace_id, kOkIfExists, &batch))
101     return false;
102
103   std::string map_id;
104   bool exists;
105   if (!GetMapForArea(namespace_id, origin.spec(), leveldb::ReadOptions(),
106                      &exists, &map_id))
107     return false;
108   if (exists) {
109     int64 ref_count;
110     if (!GetMapRefCount(map_id, &ref_count))
111       return false;
112     if (ref_count > 1) {
113       if (!DeepCopyArea(namespace_id, origin, !clear_all_first,
114                         &map_id, &batch))
115         return false;
116     }
117     else if (clear_all_first) {
118       if (!ClearMap(map_id, &batch))
119         return false;
120     }
121   } else {
122     // Map doesn't exist, create it now if needed.
123     if (!changes.empty()) {
124       if (!CreateMapForArea(namespace_id, origin, &map_id, &batch))
125         return false;
126     }
127   }
128
129   WriteValuesToMap(map_id, changes, &batch);
130
131   leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch);
132   return DatabaseErrorCheck(s.ok());
133 }
134
135 bool SessionStorageDatabase::CloneNamespace(
136     const std::string& namespace_id, const std::string& new_namespace_id) {
137   // Go through all origins in the namespace |namespace_id|, create placeholders
138   // for them in |new_namespace_id|, and associate them with the existing maps.
139
140   // Example, data before shallow copy:
141   // | map-1-                         | 1 (refcount)        |
142   // | map-1-a                        | b                   |
143   // | namespace-1- (1 = namespace id)| dummy               |
144   // | namespace-1-origin1            | 1 (mapid)           |
145
146   // Example, data after shallow copy:
147   // | map-1-                         | 2 (inc. refcount)   |
148   // | map-1-a                        | b                   |
149   // | namespace-1-(1 = namespace id) | dummy               |
150   // | namespace-1-origin1            | 1 (mapid)           |
151   // | namespace-2-                   | dummy               |
152   // | namespace-2-origin1            | 1 (mapid) << references the same map
153
154   if (!LazyOpen(true))
155     return false;
156
157   leveldb::WriteBatch batch;
158   const bool kOkIfExists = false;
159   if (!CreateNamespace(new_namespace_id, kOkIfExists, &batch))
160     return false;
161
162   std::map<std::string, std::string> areas;
163   if (!GetAreasInNamespace(namespace_id, &areas))
164     return false;
165
166   for (std::map<std::string, std::string>::const_iterator it = areas.begin();
167        it != areas.end(); ++it) {
168     const std::string& origin = it->first;
169     const std::string& map_id = it->second;
170     if (!IncreaseMapRefCount(map_id, &batch))
171       return false;
172     AddAreaToNamespace(new_namespace_id, origin, map_id, &batch);
173   }
174   leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch);
175   return DatabaseErrorCheck(s.ok());
176 }
177
178 bool SessionStorageDatabase::DeleteArea(const std::string& namespace_id,
179                                         const GURL& origin) {
180   if (!LazyOpen(false)) {
181     // No need to create the database if it doesn't exist.
182     return true;
183   }
184   leveldb::WriteBatch batch;
185   if (!DeleteAreaHelper(namespace_id, origin.spec(), &batch))
186     return false;
187   leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch);
188   return DatabaseErrorCheck(s.ok());
189 }
190
191 bool SessionStorageDatabase::DeleteNamespace(const std::string& namespace_id) {
192   if (!LazyOpen(false)) {
193     // No need to create the database if it doesn't exist.
194     return true;
195   }
196   // Itereate through the areas in the namespace.
197   leveldb::WriteBatch batch;
198   std::map<std::string, std::string> areas;
199   if (!GetAreasInNamespace(namespace_id, &areas))
200     return false;
201   for (std::map<std::string, std::string>::const_iterator it = areas.begin();
202        it != areas.end(); ++it) {
203     const std::string& origin = it->first;
204     if (!DeleteAreaHelper(namespace_id, origin, &batch))
205       return false;
206   }
207   batch.Delete(NamespaceStartKey(namespace_id));
208   leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch);
209   return DatabaseErrorCheck(s.ok());
210 }
211
212 bool SessionStorageDatabase::ReadNamespacesAndOrigins(
213     std::map<std::string, std::vector<GURL> >* namespaces_and_origins) {
214   if (!LazyOpen(true))
215     return false;
216
217   // While ReadNamespacesAndOrigins is in progress, another thread can call
218   // CommitAreaChanges. To protect the reading operation, create a snapshot and
219   // read from it.
220   leveldb::ReadOptions options;
221   options.snapshot = db_->GetSnapshot();
222
223   std::string namespace_prefix = NamespacePrefix();
224   scoped_ptr<leveldb::Iterator> it(db_->NewIterator(options));
225   it->Seek(namespace_prefix);
226   // If the key is not found, the status of the iterator won't be IsNotFound(),
227   // but the iterator will be invalid.
228   if (!it->Valid()) {
229     db_->ReleaseSnapshot(options.snapshot);
230     return true;
231   }
232
233   if (!DatabaseErrorCheck(it->status().ok())) {
234     db_->ReleaseSnapshot(options.snapshot);
235     return false;
236   }
237
238   // Skip the dummy entry "namespace-" and iterate the namespaces.
239   std::string current_namespace_start_key;
240   std::string current_namespace_id;
241   for (it->Next(); it->Valid(); it->Next()) {
242     std::string key = it->key().ToString();
243     if (key.find(namespace_prefix) != 0) {
244       // Iterated past the "namespace-" keys.
245       break;
246     }
247     // For each namespace, the first key is "namespace-<namespaceid>-", and the
248     // subsequent keys are "namespace-<namespaceid>-<origin>". Read the unique
249     // "<namespaceid>" parts from the keys.
250     if (current_namespace_start_key.empty() ||
251         key.substr(0, current_namespace_start_key.length()) !=
252         current_namespace_start_key) {
253       // The key is of the form "namespace-<namespaceid>-" for a new
254       // <namespaceid>.
255       current_namespace_start_key = key;
256       current_namespace_id =
257           key.substr(namespace_prefix.length(),
258                      key.length() - namespace_prefix.length() - 1);
259       // Ensure that we keep track of the namespace even if it doesn't contain
260       // any origins.
261       namespaces_and_origins->insert(
262           std::make_pair(current_namespace_id, std::vector<GURL>()));
263     } else {
264       // The key is of the form "namespace-<namespaceid>-<origin>".
265       std::string origin = key.substr(current_namespace_start_key.length());
266       (*namespaces_and_origins)[current_namespace_id].push_back(GURL(origin));
267     }
268   }
269   db_->ReleaseSnapshot(options.snapshot);
270   return true;
271 }
272
273 bool SessionStorageDatabase::LazyOpen(bool create_if_needed) {
274   base::AutoLock auto_lock(db_lock_);
275   if (db_error_ || is_inconsistent_) {
276     // Don't try to open a database that we know has failed already.
277     return false;
278   }
279   if (IsOpen())
280     return true;
281
282   if (!create_if_needed &&
283       (!base::PathExists(file_path_) ||
284        file_util::IsDirectoryEmpty(file_path_))) {
285     // If the directory doesn't exist already and we haven't been asked to
286     // create a file on disk, then we don't bother opening the database. This
287     // means we wait until we absolutely need to put something onto disk before
288     // we do so.
289     return false;
290   }
291
292   leveldb::DB* db;
293   leveldb::Status s = TryToOpen(&db);
294   if (!s.ok()) {
295     LOG(WARNING) << "Failed to open leveldb in " << file_path_.value()
296                  << ", error: " << s.ToString();
297     DCHECK(db == NULL);
298
299     // Clear the directory and try again.
300     base::DeleteFile(file_path_, true);
301     s = TryToOpen(&db);
302     if (!s.ok()) {
303       LOG(WARNING) << "Failed to open leveldb in " << file_path_.value()
304                    << ", error: " << s.ToString();
305       UMA_HISTOGRAM_ENUMERATION(session_storage_uma_name,
306                                 SESSION_STORAGE_UMA_FAIL,
307                                 SESSION_STORAGE_UMA_MAX);
308       DCHECK(db == NULL);
309       db_error_ = true;
310       return false;
311     }
312     UMA_HISTOGRAM_ENUMERATION(session_storage_uma_name,
313                               SESSION_STORAGE_UMA_RECREATED,
314                               SESSION_STORAGE_UMA_MAX);
315   } else {
316     UMA_HISTOGRAM_ENUMERATION(session_storage_uma_name,
317                               SESSION_STORAGE_UMA_SUCCESS,
318                               SESSION_STORAGE_UMA_MAX);
319   }
320   db_.reset(db);
321   return true;
322 }
323
324 leveldb::Status SessionStorageDatabase::TryToOpen(leveldb::DB** db) {
325   leveldb::Options options;
326   // The directory exists but a valid leveldb database might not exist inside it
327   // (e.g., a subset of the needed files might be missing). Handle this
328   // situation gracefully by creating the database now.
329   options.max_open_files = 0;  // Use minimum.
330   options.create_if_missing = true;
331 #if defined(OS_WIN)
332   return leveldb::DB::Open(options, WideToUTF8(file_path_.value()), db);
333 #elif defined(OS_POSIX)
334   return leveldb::DB::Open(options, file_path_.value(), db);
335 #endif
336 }
337
338 bool SessionStorageDatabase::IsOpen() const {
339   return db_.get() != NULL;
340 }
341
342 bool SessionStorageDatabase::CallerErrorCheck(bool ok) const {
343   DCHECK(ok);
344   return ok;
345 }
346
347 bool SessionStorageDatabase::ConsistencyCheck(bool ok) {
348   if (ok)
349     return true;
350   base::AutoLock auto_lock(db_lock_);
351   DCHECK(false);
352   is_inconsistent_ = true;
353   // We cannot recover the database during this run, e.g., the upper layer can
354   // have a different understanding of the database state (shallow and deep
355   // copies).
356   // TODO(marja): Error handling.
357   return false;
358 }
359
360 bool SessionStorageDatabase::DatabaseErrorCheck(bool ok) {
361   if (ok)
362     return true;
363   base::AutoLock auto_lock(db_lock_);
364   db_error_ = true;
365   // TODO(marja): Error handling.
366   return false;
367 }
368
369 bool SessionStorageDatabase::CreateNamespace(const std::string& namespace_id,
370                                              bool ok_if_exists,
371                                              leveldb::WriteBatch* batch) {
372   leveldb::Slice namespace_prefix = NamespacePrefix();
373   std::string dummy;
374   leveldb::Status s = db_->Get(leveldb::ReadOptions(), namespace_prefix,
375                                &dummy);
376   if (!DatabaseErrorCheck(s.ok() || s.IsNotFound()))
377     return false;
378   if (s.IsNotFound())
379     batch->Put(namespace_prefix, "");
380
381   std::string namespace_start_key = NamespaceStartKey(namespace_id);
382   s = db_->Get(leveldb::ReadOptions(), namespace_start_key, &dummy);
383   if (!DatabaseErrorCheck(s.ok() || s.IsNotFound()))
384     return false;
385   if (s.IsNotFound()) {
386     batch->Put(namespace_start_key, "");
387     return true;
388   }
389   return CallerErrorCheck(ok_if_exists);
390 }
391
392 bool SessionStorageDatabase::GetAreasInNamespace(
393     const std::string& namespace_id,
394     std::map<std::string, std::string>* areas) {
395   std::string namespace_start_key = NamespaceStartKey(namespace_id);
396   scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions()));
397   it->Seek(namespace_start_key);
398   // If the key is not found, the status of the iterator won't be IsNotFound(),
399   // but the iterator will be invalid.
400   if (!it->Valid()) {
401     // The namespace_start_key is not found when the namespace doesn't contain
402     // any areas. We don't need to do anything.
403     return true;
404   }
405   if (!DatabaseErrorCheck(it->status().ok()))
406     return false;
407
408   // Skip the dummy entry "namespace-<namespaceid>-" and iterate the origins.
409   for (it->Next(); it->Valid(); it->Next()) {
410     std::string key = it->key().ToString();
411     if (key.find(namespace_start_key) != 0) {
412       // Iterated past the origins for this namespace.
413       break;
414     }
415     std::string origin = key.substr(namespace_start_key.length());
416     std::string map_id = it->value().ToString();
417     (*areas)[origin] = map_id;
418   }
419   return true;
420 }
421
422 void SessionStorageDatabase::AddAreaToNamespace(const std::string& namespace_id,
423                                                 const std::string& origin,
424                                                 const std::string& map_id,
425                                                 leveldb::WriteBatch* batch) {
426   std::string namespace_key = NamespaceKey(namespace_id, origin);
427   batch->Put(namespace_key, map_id);
428 }
429
430 bool SessionStorageDatabase::DeleteAreaHelper(
431     const std::string& namespace_id,
432     const std::string& origin,
433     leveldb::WriteBatch* batch) {
434   std::string map_id;
435   bool exists;
436   if (!GetMapForArea(namespace_id, origin, leveldb::ReadOptions(), &exists,
437                      &map_id))
438     return false;
439   if (!exists)
440     return true;  // Nothing to delete.
441   if (!DecreaseMapRefCount(map_id, 1, batch))
442     return false;
443   std::string namespace_key = NamespaceKey(namespace_id, origin);
444   batch->Delete(namespace_key);
445
446   // If this was the only area in the namespace, delete the namespace start key,
447   // too.
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 (!ConsistencyCheck(it->Valid()))
452     return false;
453   // Advance the iterator 2 times (we still haven't really deleted
454   // namespace_key).
455   it->Next();
456   if (!ConsistencyCheck(it->Valid()))
457     return false;
458   it->Next();
459   if (!it->Valid())
460     return true;
461   std::string key = it->key().ToString();
462   if (key.find(namespace_start_key) != 0)
463     batch->Delete(namespace_start_key);
464   return true;
465 }
466
467 bool SessionStorageDatabase::GetMapForArea(const std::string& namespace_id,
468                                            const std::string& origin,
469                                            const leveldb::ReadOptions& options,
470                                            bool* exists, std::string* map_id) {
471   std::string namespace_key = NamespaceKey(namespace_id, origin);
472   leveldb::Status s = db_->Get(options, namespace_key, map_id);
473   if (s.IsNotFound()) {
474     *exists = false;
475     return true;
476   }
477   *exists = true;
478   return DatabaseErrorCheck(s.ok());
479 }
480
481 bool SessionStorageDatabase::CreateMapForArea(const std::string& namespace_id,
482                                               const GURL& origin,
483                                               std::string* map_id,
484                                               leveldb::WriteBatch* batch) {
485   leveldb::Slice next_map_id_key = NextMapIdKey();
486   leveldb::Status s = db_->Get(leveldb::ReadOptions(), next_map_id_key, map_id);
487   if (!DatabaseErrorCheck(s.ok() || s.IsNotFound()))
488     return false;
489   int64 next_map_id = 0;
490   if (s.IsNotFound()) {
491     *map_id = "0";
492   } else {
493     bool conversion_ok = base::StringToInt64(*map_id, &next_map_id);
494     if (!ConsistencyCheck(conversion_ok))
495       return false;
496   }
497   batch->Put(next_map_id_key, base::Int64ToString(++next_map_id));
498   std::string namespace_key = NamespaceKey(namespace_id, origin.spec());
499   batch->Put(namespace_key, *map_id);
500   batch->Put(MapRefCountKey(*map_id), "1");
501   return true;
502 }
503
504 bool SessionStorageDatabase::ReadMap(const std::string& map_id,
505                                      const leveldb::ReadOptions& options,
506                                      DOMStorageValuesMap* result,
507                                      bool only_keys) {
508   scoped_ptr<leveldb::Iterator> it(db_->NewIterator(options));
509   std::string map_start_key = MapRefCountKey(map_id);
510   it->Seek(map_start_key);
511   // If the key is not found, the status of the iterator won't be IsNotFound(),
512   // but the iterator will be invalid. The map needs to exist, otherwise we have
513   // a stale map_id in the database.
514   if (!ConsistencyCheck(it->Valid()))
515     return false;
516   if (!DatabaseErrorCheck(it->status().ok()))
517     return false;
518   // Skip the dummy entry "map-<mapid>-".
519   for (it->Next(); it->Valid(); it->Next()) {
520     std::string key = it->key().ToString();
521     if (key.find(map_start_key) != 0) {
522       // Iterated past the keys in this map.
523       break;
524     }
525     // Key is of the form "map-<mapid>-<key>".
526     base::string16 key16 = UTF8ToUTF16(key.substr(map_start_key.length()));
527     if (only_keys) {
528       (*result)[key16] = base::NullableString16();
529     } else {
530       // Convert the raw data stored in std::string (it->value()) to raw data
531       // stored in base::string16.
532       size_t len = it->value().size() / sizeof(char16);
533       const char16* data_ptr =
534           reinterpret_cast<const char16*>(it->value().data());
535       (*result)[key16] =
536           base::NullableString16(base::string16(data_ptr, len), false);
537     }
538   }
539   return true;
540 }
541
542 void SessionStorageDatabase::WriteValuesToMap(const std::string& map_id,
543                                               const DOMStorageValuesMap& values,
544                                               leveldb::WriteBatch* batch) {
545   for (DOMStorageValuesMap::const_iterator it = values.begin();
546        it != values.end();
547        ++it) {
548     base::NullableString16 value = it->second;
549     std::string key = MapKey(map_id, UTF16ToUTF8(it->first));
550     if (value.is_null()) {
551       batch->Delete(key);
552     } else {
553       // Convert the raw data stored in base::string16 to raw data stored in
554       // std::string.
555       const char* data = reinterpret_cast<const char*>(value.string().data());
556       size_t size = value.string().size() * 2;
557       batch->Put(key, leveldb::Slice(data, size));
558     }
559   }
560 }
561
562 bool SessionStorageDatabase::GetMapRefCount(const std::string& map_id,
563                                             int64* ref_count) {
564   std::string ref_count_string;
565   leveldb::Status s = db_->Get(leveldb::ReadOptions(),
566                                MapRefCountKey(map_id), &ref_count_string);
567   if (!ConsistencyCheck(s.ok()))
568     return false;
569   bool conversion_ok = base::StringToInt64(ref_count_string, ref_count);
570   return ConsistencyCheck(conversion_ok);
571 }
572
573 bool SessionStorageDatabase::IncreaseMapRefCount(const std::string& map_id,
574                                                  leveldb::WriteBatch* batch) {
575   // Increase the ref count for the map.
576   int64 old_ref_count;
577   if (!GetMapRefCount(map_id, &old_ref_count))
578     return false;
579   batch->Put(MapRefCountKey(map_id), base::Int64ToString(++old_ref_count));
580   return true;
581 }
582
583 bool SessionStorageDatabase::DecreaseMapRefCount(const std::string& map_id,
584                                                  int decrease,
585                                                  leveldb::WriteBatch* batch) {
586   // Decrease the ref count for the map.
587   int64 ref_count;
588   if (!GetMapRefCount(map_id, &ref_count))
589     return false;
590   if (!ConsistencyCheck(decrease <= ref_count))
591     return false;
592   ref_count -= decrease;
593   if (ref_count > 0) {
594     batch->Put(MapRefCountKey(map_id), base::Int64ToString(ref_count));
595   } else {
596     // Clear all keys in the map.
597     if (!ClearMap(map_id, batch))
598       return false;
599     batch->Delete(MapRefCountKey(map_id));
600   }
601   return true;
602 }
603
604 bool SessionStorageDatabase::ClearMap(const std::string& map_id,
605                                       leveldb::WriteBatch* batch) {
606   DOMStorageValuesMap values;
607   if (!ReadMap(map_id, leveldb::ReadOptions(), &values, true))
608     return false;
609   for (DOMStorageValuesMap::const_iterator it = values.begin();
610        it != values.end(); ++it)
611     batch->Delete(MapKey(map_id, UTF16ToUTF8(it->first)));
612   return true;
613 }
614
615 bool SessionStorageDatabase::DeepCopyArea(
616     const std::string& namespace_id, const GURL& origin, bool copy_data,
617     std::string* map_id, leveldb::WriteBatch* batch) {
618   // Example, data before deep copy:
619   // | namespace-1- (1 = namespace id)| dummy               |
620   // | namespace-1-origin1            | 1 (mapid)           |
621   // | namespace-2-                   | dummy               |
622   // | namespace-2-origin1            | 1 (mapid) << references the same map
623   // | map-1-                         | 2 (refcount)        |
624   // | map-1-a                        | b                   |
625
626   // Example, data after deep copy copy:
627   // | namespace-1-(1 = namespace id) | dummy               |
628   // | namespace-1-origin1            | 1 (mapid)           |
629   // | namespace-2-                   | dummy               |
630   // | namespace-2-origin1            | 2 (mapid) << references the new map
631   // | map-1-                         | 1 (dec. refcount)   |
632   // | map-1-a                        | b                   |
633   // | map-2-                         | 1 (refcount)        |
634   // | map-2-a                        | b                   |
635
636   // Read the values from the old map here. If we don't need to copy the data,
637   // this can stay empty.
638   DOMStorageValuesMap values;
639   if (copy_data && !ReadMap(*map_id, leveldb::ReadOptions(), &values, false))
640     return false;
641   if (!DecreaseMapRefCount(*map_id, 1, batch))
642     return false;
643   // Create a new map (this will also break the association to the old map) and
644   // write the old data into it. This will write the id of the created map into
645   // |map_id|.
646   if (!CreateMapForArea(namespace_id, origin, map_id, batch))
647     return false;
648   WriteValuesToMap(*map_id, values, batch);
649   return true;
650 }
651
652 std::string SessionStorageDatabase::NamespaceStartKey(
653     const std::string& namespace_id) {
654   return base::StringPrintf("namespace-%s-", namespace_id.c_str());
655 }
656
657 std::string SessionStorageDatabase::NamespaceKey(
658     const std::string& namespace_id, const std::string& origin) {
659   return base::StringPrintf("namespace-%s-%s", namespace_id.c_str(),
660                             origin.c_str());
661 }
662
663 const char* SessionStorageDatabase::NamespacePrefix() {
664   return "namespace-";
665 }
666
667 std::string SessionStorageDatabase::MapRefCountKey(const std::string& map_id) {
668   return base::StringPrintf("map-%s-", map_id.c_str());
669 }
670
671 std::string SessionStorageDatabase::MapKey(const std::string& map_id,
672                                            const std::string& key) {
673   return base::StringPrintf("map-%s-%s", map_id.c_str(), key.c_str());
674 }
675
676 const char* SessionStorageDatabase::NextMapIdKey() {
677   return "next-map-id";
678 }
679
680 }  // namespace content