blob: bac3df7a67baa7cee9b7dfc7c9a5c48459ef74ea [file] [log] [blame]
Ben Murdochbb1529c2013-08-08 10:24:53 +01001// Copyright 2013 The Chromium Authors. All rights reserved.
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +01002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Ben Murdochbb1529c2013-08-08 10:24:53 +01005#include "content/browser/dom_storage/session_storage_database.h"
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +01006
7#include "base/file_util.h"
8#include "base/logging.h"
9#include "base/metrics/histogram.h"
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010010#include "base/strings/string_number_conversions.h"
Torne (Richard Coles)5e3f23d2013-06-11 16:24:11 +010011#include "base/strings/stringprintf.h"
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010012#include "base/strings/utf_string_conversions.h"
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010013#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"
Ben Murdocheb525c52013-07-10 11:40:50 +010018#include "url/gurl.h"
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010019
20
21namespace {
22
23const char session_storage_uma_name[] = "SessionStorageDatabase.Open";
24
25enum 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
Ben Murdochbb1529c2013-08-08 10:24:53 +010052namespace content {
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010053
54SessionStorageDatabase::SessionStorageDatabase(const base::FilePath& file_path)
55 : file_path_(file_path),
56 db_error_(false),
57 is_inconsistent_(false) {
58}
59
60SessionStorageDatabase::~SessionStorageDatabase() {
61}
62
63void SessionStorageDatabase::ReadAreaValues(const std::string& namespace_id,
64 const GURL& origin,
Ben Murdochbb1529c2013-08-08 10:24:53 +010065 DOMStorageValuesMap* result) {
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010066 // 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
Ben Murdochbb1529c2013-08-08 10:24:53 +010086bool SessionStorageDatabase::CommitAreaChanges(
87 const std::string& namespace_id,
88 const GURL& origin,
89 bool clear_all_first,
90 const DOMStorageValuesMap& changes) {
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010091 // 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
135bool 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
178bool 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
191bool 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
212bool 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
273bool 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 &&
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100283 (!base::PathExists(file_path_) ||
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100284 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.
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100300 base::DeleteFile(file_path_, true);
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100301 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
324leveldb::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.
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100329 options.max_open_files = 0; // Use minimum.
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100330 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
338bool SessionStorageDatabase::IsOpen() const {
339 return db_.get() != NULL;
340}
341
342bool SessionStorageDatabase::CallerErrorCheck(bool ok) const {
343 DCHECK(ok);
344 return ok;
345}
346
347bool 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
360bool 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
369bool 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
392bool 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
422void 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
430bool 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
467bool 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
481bool 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
504bool SessionStorageDatabase::ReadMap(const std::string& map_id,
505 const leveldb::ReadOptions& options,
Ben Murdochbb1529c2013-08-08 10:24:53 +0100506 DOMStorageValuesMap* result,
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100507 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) {
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100528 (*result)[key16] = base::NullableString16();
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100529 } 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());
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100535 (*result)[key16] =
536 base::NullableString16(base::string16(data_ptr, len), false);
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100537 }
538 }
539 return true;
540}
541
542void SessionStorageDatabase::WriteValuesToMap(const std::string& map_id,
Ben Murdochbb1529c2013-08-08 10:24:53 +0100543 const DOMStorageValuesMap& values,
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100544 leveldb::WriteBatch* batch) {
Ben Murdochbb1529c2013-08-08 10:24:53 +0100545 for (DOMStorageValuesMap::const_iterator it = values.begin();
546 it != values.end();
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100547 ++it) {
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100548 base::NullableString16 value = it->second;
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100549 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
562bool 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
573bool 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
583bool 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
604bool SessionStorageDatabase::ClearMap(const std::string& map_id,
605 leveldb::WriteBatch* batch) {
Ben Murdochbb1529c2013-08-08 10:24:53 +0100606 DOMStorageValuesMap values;
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100607 if (!ReadMap(map_id, leveldb::ReadOptions(), &values, true))
608 return false;
Ben Murdochbb1529c2013-08-08 10:24:53 +0100609 for (DOMStorageValuesMap::const_iterator it = values.begin();
610 it != values.end(); ++it)
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100611 batch->Delete(MapKey(map_id, UTF16ToUTF8(it->first)));
612 return true;
613}
614
615bool 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.
Ben Murdochbb1529c2013-08-08 10:24:53 +0100638 DOMStorageValuesMap values;
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100639 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
652std::string SessionStorageDatabase::NamespaceStartKey(
653 const std::string& namespace_id) {
654 return base::StringPrintf("namespace-%s-", namespace_id.c_str());
655}
656
657std::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
663const char* SessionStorageDatabase::NamespacePrefix() {
664 return "namespace-";
665}
666
667std::string SessionStorageDatabase::MapRefCountKey(const std::string& map_id) {
668 return base::StringPrintf("map-%s-", map_id.c_str());
669}
670
671std::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
676const char* SessionStorageDatabase::NextMapIdKey() {
677 return "next-map-id";
678}
679
Ben Murdochbb1529c2013-08-08 10:24:53 +0100680} // namespace content