Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(36)

Side by Side Diff: webkit/dom_storage/session_storage_database.cc

Issue 10176005: Add dom_storage::SessionStorageDatabase. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: . Created 8 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 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 "webkit/dom_storage/session_storage_database.h"
6
7 #include "base/file_util.h"
8 #include "base/logging.h"
9 #include "base/stringprintf.h"
10 #include "base/string_number_conversions.h"
11 #include "base/utf_string_conversions.h"
12 #include "googleurl/src/gurl.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/status.h"
16 #include "third_party/leveldatabase/src/include/leveldb/options.h"
17 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
18
19 // Layout of the database:
20 // | key | value |
21 // -----------------------------------------------------------------------
22 // | map-1 | 2 (refcount, start of map-1-* keys)|
23 // | map-1-a | b (a = b in map 1) |
24 // | ... | |
25 // | namespace- | dummy (start of namespace-* keys) |
26 // | namespace-1 (1 = namespace id) | dummy (start of namespace-1-* keys)|
27 // | namespace-1-origin1 | 1 (mapid) |
28 // | namespace-1-origin2 | 2 |
29 // | namespace-2 | dummy |
30 // | namespace-2-origin1 | 1 (shallow copy) |
31 // | namespace-2-origin2 | 2 (shallow copy) |
32 // | namespace-3 | dummy |
33 // | namespace-3-origin1 | 3 (deep copy) |
34 // | namespace-3-origin2 | 2 (shallow copy) |
35 // | next-namespace-id | 4 |
36 // | next-map-id | 4 |
37
38 namespace dom_storage {
39
40 SessionStorageDatabase::SessionStorageDatabase(const FilePath& file_path)
michaeln 2012/04/25 08:32:12 Might be helpful to distinguish between namespace_
marja 2012/04/25 15:44:27 I tried to separate the db logic better from the h
41 : file_path_(file_path),
42 db_error_(false),
43 is_inconsistent_(false),
44 namespace_offset_(0) { }
45
46 SessionStorageDatabase::~SessionStorageDatabase() {
47 }
48
49 void SessionStorageDatabase::ReadAllValues(int64 namespace_id,
50 const GURL& origin,
51 ValuesMap* result) {
52 // We don't create a database if it doesn't exist. In that case, there is
53 // nothing to be added to the result.
54 if (!LazyOpen(false))
55 return;
56 // Check if there is map for |namespace_id| and |origin|.
57 std::string namespace_key =
58 NamespaceKey(namespace_id, namespace_offset_, origin);
59 std::string map_id;
60 leveldb::Status s = db_->Get(leveldb::ReadOptions(), namespace_key, &map_id);
61 if (s.IsNotFound())
62 return;
63 if (!DatabaseErrorCheck(s.ok()))
64 return;
65 ReadMap(map_id, result, false);
michaeln 2012/04/25 08:32:12 pseudo code noodling... if (!LazyOpen(false)) r
marja 2012/04/25 15:44:27 Done.
66 }
67
68 bool SessionStorageDatabase::CommitChanges(int64 namespace_id,
69 const GURL& origin,
70 bool clear_all_first,
71 const ValuesMap& changes) {
michaeln 2012/04/25 08:32:12 pseudo code noodling... if (!LazyOpen(true)) re
marja 2012/04/25 15:44:27 This doesn't work, we cannot go and read the ref c
72 // Even if |changes| is empty, we need to write the appropriate placeholders
73 // in the database, so that it can be later shallow-copied succssfully.
74 if (!LazyOpen(true))
75 return false;
76
77 leveldb::WriteBatch batch;
78 // Ensure that the keys "namespace-" "namespace-N" (see the schema above)
79 // exist.
80 batch.Put(NamespacePrefix(), "");
81 batch.Put(NamespaceStartKey(namespace_id, namespace_offset_), "");
michaeln 2012/04/25 08:32:12 would be nice to not overwrite existing entries fo
marja 2012/04/25 15:44:27 Done.
82
83 // Ensure that the next namespace id is up to date.
84 if (!UpdateNextNamespaceId(namespace_id, &batch))
85 return false;
86
87 // Write the data into the map.
88 std::string namespace_key =
89 NamespaceKey(namespace_id, namespace_offset_, origin);
90 std::string map_id;
91 leveldb::Status s;
92 s = db_->Get(leveldb::ReadOptions(), namespace_key, &map_id);
93 if (!DatabaseErrorCheck(s.ok() || s.IsNotFound()))
94 return false;
95
96 if (s.ok()) {
97 // We shouldn't write data to a shallow copy.
michaeln 2012/04/25 08:32:12 should we dcheck this condition instead?
marja 2012/04/25 15:44:27 ConsistencyCheck DCHECKs. ConsistencyCheck is the
98 int64 ref_count;
99 if (!GetRefCount(MapRefCountKey(map_id), &ref_count))
100 return false;
101 if (!ConsistencyCheck(ref_count == 1))
102 return false;
103
104 if (clear_all_first) {
105 if (!ClearMap(map_id, &batch))
106 return false;
107 }
108 } else {
109 // Map doesn't exist, create it now if needed.
110 if (!changes.empty()) {
111 if (!CreateNewMap(namespace_key, &batch, &map_id))
112 return false;
113 }
114 }
115
116 // WriteValuesToMap won't do anything if changes is empty.
117 WriteValuesToMap(map_id, changes, &batch);
118
119 s = db_->Write(leveldb::WriteOptions(), &batch);
120 return DatabaseErrorCheck(s.ok());
121 }
122
123 bool SessionStorageDatabase::ShallowCopyNamespace(int64 namespace_id,
124 int64 new_namespace_id) {
michaeln 2012/04/25 08:32:12 pseudo code noodling... WriteBatch batch; // set
marja 2012/04/25 15:44:27 Done.
125 // Go through all origins in the namespace |namespace_id|, create placeholders
126 // for them in |new_namespace_id|, and associate them with the existing maps.
127
128 // Example, data before shallow copy:
129 // | map-1 | 1 (refcount) |
130 // | map-1-a | b |
131 // | namespace-1 (1 = namespace id) | dummy |
132 // | namespace-1-origin1 | 1 (mapid) |
133
134 // Example, data after shallow copy:
135 // | map-1 | 2 (inc. refcount) |
136 // | map-1-a | b |
137 // | namespace-1 (1 = namespace id) | dummy |
138 // | namespace-1-origin1 | 1 (mapid) |
139 // | namespace-2 | dummy |
140 // | namespace-2-origin1 | 1 (mapid) << references the same map
141
142 if (!LazyOpen(true))
143 return false;
144
145 leveldb::WriteBatch batch;
146 batch.Put(NamespaceStartKey(new_namespace_id, namespace_offset_), "");
147
148 // Ensure that the next namespace id is up to date.
149 if (!UpdateNextNamespaceId(new_namespace_id, &batch))
150 return false;
151
152 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions()));
153 std::string namespace_start_key =
154 NamespaceStartKey(namespace_id, namespace_offset_);
155 it->Seek(namespace_start_key);
156 if (it->status().IsNotFound()) {
157 // It's possible that the namespace doesn't contain any data. (This happens
158 // when the namespace doesn't contain any areas.) We don't need to do
159 // anything.
160 return true;
161 }
162 if (!DatabaseErrorCheck(it->status().ok()))
163 return false;
164
165 // Skip the dummy entry "namespace-<namespaceid>" and iterate the origins.
166 for (it->Next(); it->Valid(); it->Next()) {
167 std::string key = it->key().ToString();
168 if (key.find(namespace_start_key) != 0) {
169 // Iterated past the origins for this namespace.
170 break;
171 }
172 std::string map_id = it->value().ToString();
173 // Increase the ref count for the map.
174 std::string map_key = MapRefCountKey(map_id);
175 int64 old_ref_count = 0;
176 if (!GetRefCount(map_key, &old_ref_count))
177 return false;
178 batch.Put(map_key, base::Int64ToString(++old_ref_count));
179 // Associate the map with the new namespace.
180 size_t second_dash = key.find('-', namespace_start_key.length());
181 std::string origin = key.substr(second_dash + 1);
182 std::string new_namespace_key = NamespaceKey(
183 base::Int64ToString(new_namespace_id + namespace_offset_), origin);
184 batch.Put(new_namespace_key, map_id);
185 }
186 leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch);
187 return DatabaseErrorCheck(s.ok());
188 }
189
190 bool SessionStorageDatabase::DeepCopy(int64 namespace_id, const GURL& origin) {
michaeln 2012/04/25 08:32:12 pseudo code noodling... if (!GetMapForArea(namesp
marja 2012/04/25 15:44:27 Done.
191 // Example, data before deep copy:
192 // | namespace-1 (1 = namespace id) | dummy |
193 // | namespace-1-origin1 | 1 (mapid) |
194 // | namespace-2 | dummy |
195 // | namespace-2-origin1 | 1 (mapid) << references the same map
196 // | map-1 | 2 (refcount) |
197 // | map-1-a | b |
198
199 // Example, data after deep copy copy:
200 // | namespace-1 (1 = namespace id) | dummy |
201 // | namespace-1-origin1 | 1 (mapid) |
202 // | namespace-2 | dummy |
203 // | namespace-2-origin1 | 2 (mapid) << references the new map
204 // | map-1 | 1 (dec. refcount) |
205 // | map-1-a | b |
206 // | map-2 | 1 (refcount) |
207 // | map-2-a | b |
208
209 if (!LazyOpen(true))
210 return false;
211
212 leveldb::Status s;
213 std::string namespace_key =
214 NamespaceKey(namespace_id, namespace_offset_, origin);
215 leveldb::WriteBatch batch;
216 std::string old_map_id;
217 s = db_->Get(leveldb::ReadOptions(), namespace_key, &old_map_id);
218 if (!ConsistencyCheck(!s.IsNotFound()))
219 return false;
220 if (!DatabaseErrorCheck(s.ok()))
221 return false;
222
223 // If this copy is the the only "shallow" copy of the map, no deep copying is
michaeln 2012/04/25 08:32:12 Is there any case where the higher level code woul
marja 2012/04/25 15:44:27 Atm this can happen if we do a shallow copy, write
224 // needed.
225 int64 ref_count = 0;
226 if (!GetRefCount(MapRefCountKey(old_map_id), &ref_count))
227 return false;
228 if (ref_count == 1)
229 return true;
230
231 std::string new_map_id;
232 if (!CreateNewMap(namespace_key, &batch, &new_map_id))
233 return false;
234
235 // Copy the values in the map.
236 ValuesMap values;
237 if (!ReadMap(old_map_id, &values, false))
238 return false;
239 WriteValuesToMap(new_map_id, values, &batch);
240
241 if (!DecreaseRefCount(old_map_id, 1, &batch))
242 return false;
243
244 s = db_->Write(leveldb::WriteOptions(), &batch);
245 return DatabaseErrorCheck(s.ok());
246 }
247
248 bool SessionStorageDatabase::DisassociateMap(int64 namespace_id,
249 const GURL& origin) {
michaeln 2012/04/25 08:32:12 ResetArea(id, origin) pseudo code noodling... in
marja 2012/04/25 15:44:27 This turned out to be the same than DeleteOrigin -
250 if (!LazyOpen(true))
251 return false;
252
253 leveldb::Status s;
254 std::string namespace_key =
255 NamespaceKey(namespace_id, namespace_offset_, origin);
256 std::string old_map_id;
257 s = db_->Get(leveldb::ReadOptions(), namespace_key, &old_map_id);
258 if (!DatabaseErrorCheck(s.ok() || s.IsNotFound()))
259 return false;
260 if (s.IsNotFound())
261 return true;
262
263 leveldb::WriteBatch batch;
264 if (!DecreaseRefCount(old_map_id, 1, &batch))
265 return false;
266 batch.Delete(namespace_key);
267 s = db_->Write(leveldb::WriteOptions(), &batch);
268 return DatabaseErrorCheck(s.ok());
269 }
270
271 bool SessionStorageDatabase::DeleteOrigin(int64 namespace_id,
272 const GURL& origin) {
273 if (!LazyOpen(false)) {
274 // No need to create the database if it doesn't exist.
275 return true;
276 }
277 leveldb::WriteBatch batch;
278 if (!DeleteOrigin(base::Int64ToString(namespace_id), origin.spec(), &batch))
279 return false;
280 leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch);
281 return DatabaseErrorCheck(s.ok());
282 }
283
284 bool SessionStorageDatabase::DeleteNamespace(int64 namespace_id) {
michaeln 2012/04/25 08:32:12 pseudo code noodling... std::vector<std::pair<GUR
marja 2012/04/25 15:44:27 Done.
285 if (!LazyOpen(false)) {
286 // No need to create the database if it doesn't exist.
287 return true;
288 }
289 std::string namespace_id_str = base::Int64ToString(namespace_id);
290 std::string namespace_start_key = NamespaceStartKey(namespace_id_str);
291 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions()));
292 it->Seek(namespace_start_key);
293 if (it->status().IsNotFound())
294 return true;
295 if (!DatabaseErrorCheck(it->status().ok()))
296 return false;
297 leveldb::WriteBatch batch;
298 // Skip the dummy entry "namespace-<namespaceid>"; then iterate the origins in
299 // the namespace.
300 for (it->Next(); it->Valid(); it->Next()) {
301 // Key is of the form "namespace-<namespaceid>-<origin>".
302 std::string key = it->key().ToString();
303 std::string namespace_prefix = NamespacePrefix();
304 if (key.find(namespace_prefix) != 0) {
305 // Iterated beyond the keys for this namespace.
306 break;
307 }
308 int prefix_length = namespace_prefix.length();
309 size_t second_dash = key.find('-', prefix_length);
310 if (second_dash == std::string::npos ||
311 key.substr(prefix_length,
312 second_dash - prefix_length) != namespace_id_str) {
313 // Iterated beyond the keys for this namespace.
314 break;
315 }
316 std::string origin = key.substr(second_dash + 1);
317 if (!DeleteOrigin(namespace_id_str, origin, &batch))
318 return false;
319 }
320 batch.Delete(namespace_start_key);
321 leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch);
322 return DatabaseErrorCheck(s.ok());
323 }
324
325 bool SessionStorageDatabase::DeleteLeftoverData() {
326 if (!LazyOpen(false)) {
327 // No need to create the database if it doesn't exist.
328 return true;
329 }
330 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions()));
331 std::string namespace_prefix = NamespacePrefix();
332 it->Seek(namespace_prefix);
333 if (it->status().IsNotFound()) {
334 // No data to delete.
335 return true;
336 }
337 if (!DatabaseErrorCheck(it->status().ok()))
338 return false;
339
340 // Find out how much we need to decrease the ref counts for each map referred
341 // to by one of the namespaces. Multiple namespaces can refer to the same map.
342 std::map<std::string, int> referred_maps;
343 leveldb::WriteBatch batch;
344
345 for (it->Next(); it->Valid(); it->Next()) {
346 std::string key = it->key().ToString();
347 if (key.find(namespace_prefix) != 0) {
348 // Iterated beyond the "namespace-" keys.
349 break;
350 }
351 size_t second_dash = key.find('-', namespace_prefix.length());
352 if (second_dash != std::string::npos) {
353 // The key is of the form "namespace-<namespaceid>-<origin>", and the
354 // value is a map id. Decrease the ref count for that map.
355 ++referred_maps[it->value().ToString()];
356 }
357 batch.Delete(key);
358 }
359 // Decrease the ref counts of the maps.
360 for (std::map<std::string, int>::const_iterator it = referred_maps.begin();
361 it != referred_maps.end(); ++it) {
362 if (!DecreaseRefCount(it->first, it->second, &batch))
363 return false;
364 }
365
366 leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch);
michaeln 2012/04/25 08:32:12 I think this results in an empty session storage d
marja 2012/04/25 15:44:27 Deleted it for now.
367 return DatabaseErrorCheck(s.ok());
368 }
369
370 bool SessionStorageDatabase::LazyOpen(bool create_if_needed) {
371 if (db_error_ || is_inconsistent_) {
372 // Don't try to open a database that we know has failed already.
373 return false;
374 }
375 if (IsOpen())
376 return true;
377
378 if (!create_if_needed &&
379 (!file_util::PathExists(file_path_) ||
380 file_util::IsDirectoryEmpty(file_path_))) {
381 // If the directory doesn't exist already and we haven't been asked to
382 // create a file on disk, then we don't bother opening the database. This
383 // means we wait until we absolutely need to put something onto disk before
384 // we do so.
385 return false;
386 }
387
388 leveldb::DB* db;
389 leveldb::Status s = TryToOpen(file_path_, &db);
390 if (!s.ok()) {
391 LOG(WARNING) << "Failed to open leveldb in " << file_path_.value()
392 << ", error: " << s.ToString();
393 DCHECK(db == NULL);
394
395 // Clear the directory and try again.
396 file_util::Delete(file_path_, true);
397 s = TryToOpen(file_path_, &db);
398 if (!s.ok()) {
399 LOG(WARNING) << "Failed to open leveldb in " << file_path_.value()
400 << ", error: " << s.ToString();
401 DCHECK(db == NULL);
402 db_error_ = true;
403 return false;
404 }
405 }
406 db_.reset(db);
407
408 return GetNextNamespaceId(&namespace_offset_);
409 }
410
411 leveldb::Status SessionStorageDatabase::TryToOpen(const FilePath& file_path,
412 leveldb::DB** db) {
413 leveldb::Options options;
414 // The directory exists but a valid leveldb database might not exist inside it
415 // (e.g., a subset of the needed files might be missing). Handle this
416 // situation gracefully by creating the database now.
417 options.create_if_missing = true;
418 #if defined(OS_WIN)
419 return leveldb::DB::Open(options, WideToUTF8(file_path.value()), db);
420 #elif defined(OS_POSIX)
421 return leveldb::DB::Open(options, file_path.value(), db);
422 #endif
423 }
424
425 bool SessionStorageDatabase::IsOpen() const {
426 return db_.get() != NULL;
427 }
428
429 bool SessionStorageDatabase::ConsistencyCheck(bool ok) {
430 DCHECK(ok);
431 if (!ok) {
432 // We cannot recover the database during this run, e.g., the upper layer can
433 // have a different understanding of the database state (shallow and deep
434 // copies). Don't do any database operations during this run, and delete the
435 // data. During next run, the database is recreated.
436 is_inconsistent_ = true;
437 file_util::Delete(file_path_, true);
438 }
439 return ok;
440 }
441
442 bool SessionStorageDatabase::DatabaseErrorCheck(bool ok) {
443 if (!ok) {
444 // We cannot recover the database during this run, e.g., the upper layer can
445 // have a different understanding of the database state (shallow and deep
446 // copies). Don't do any database operations during this run, and delete the
447 // data. During the next run, the database is recreated.
448 db_error_ = true;
449 file_util::Delete(file_path_, true);
michaeln 2012/04/25 08:32:12 This doesn't look like a viable error handling str
marja 2012/04/25 15:44:27 Ahh, true, we should at least close the db first.
450 }
451 return ok;
452 }
453
454 bool SessionStorageDatabase::ReadMap(const std::string& map_id,
455 ValuesMap* result,
456 bool only_keys) {
457 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions()));
458 std::string map_start_key = MapRefCountKey(map_id);
459 it->Seek(map_start_key);
460 // The map needs to exist, otherwise we have a stale map_id in the database.
461 if (!ConsistencyCheck(!it->status().IsNotFound()))
462 return false;
463 if (!DatabaseErrorCheck(it->status().ok()))
464 return false;
465 // Skip the dummy entry "map-<mapid>".
466 for (it->Next(); it->Valid(); it->Next()) {
467 // Key is of the form "map-<mapid>-<key>".
468 std::string key = it->key().ToString();
469 int prefix_length = MapPrefix().length();
470 size_t second_dash = key.find('-', prefix_length);
471 if (second_dash == std::string::npos ||
472 key.substr(prefix_length, second_dash - prefix_length) != map_id) {
473 // Iterated beyond the keys in this map.
474 break;
475 }
476 string16 key16 = UTF8ToUTF16(key.substr(second_dash + 1));
477 if (only_keys) {
478 (*result)[key16] = NullableString16(true);
479 } else {
480 // Convert the raw data stored in std::string (it->value()) to raw data
481 // stored in string16.
482 string16 value;
483 size_t len = it->value().size() / sizeof(char16);
484 value.resize(len);
485 value.assign(reinterpret_cast<const char16*>(it->value().data()), len);
486 (*result)[key16] = NullableString16(value, false);
michaeln 2012/04/25 08:32:12 is the call to resize needed? i'm not sure if this
marja 2012/04/25 15:44:27 Done.
487 }
488 }
489 return true;
490 }
491
492 bool SessionStorageDatabase::CreateNewMap(const std::string& namespace_key,
493 leveldb::WriteBatch* batch,
494 std::string* map_id) {
495 // Create a new map.
496 std::string next_map_id_key = NextMapIdKey();
497 leveldb::Status s = db_->Get(leveldb::ReadOptions(), next_map_id_key, map_id);
498 if (!DatabaseErrorCheck(s.ok() || s.IsNotFound()))
499 return false;
500 int64 next_map_id = 0;
501 if (s.IsNotFound()) {
502 *map_id = "0";
503 } else {
504 bool conversion_ok = base::StringToInt64(*map_id, &next_map_id);
505 if (!ConsistencyCheck(conversion_ok))
506 return false;
507 }
508 batch->Put(next_map_id_key, base::Int64ToString(++next_map_id));
509 batch->Put(namespace_key, *map_id);
510 batch->Put(MapRefCountKey(*map_id), base::Int64ToString(1));
511 return true;
512 }
513
514 void SessionStorageDatabase::WriteValuesToMap(const std::string& map_id,
515 const ValuesMap& values,
516 leveldb::WriteBatch* batch) {
517 for (ValuesMap::const_iterator it = values.begin(); it != values.end();
518 ++it) {
519 NullableString16 value = it->second;
520 std::string key = MapKey(map_id, UTF16ToUTF8(it->first));
521 if (value.is_null()) {
522 batch->Delete(key);
michaeln 2012/04/25 08:32:12 can batch->Delete or batch->Put fail... ah... i se
523 } else {
524 // Convert the raw data stored in string16 to raw data stored in
525 // std::string.
526 const char* data = reinterpret_cast<const char*>(value.string().data());
527 size_t size = value.string().size() * 2;
528 batch->Put(key, std::string(data, size));
michaeln 2012/04/25 08:32:12 i think you could use the Slice(const char* d, siz
marja 2012/04/25 15:44:27 Done.
529 }
530 }
531 }
532
533 bool SessionStorageDatabase::GetNextNamespaceId(int64* next_namespace_id) {
534 std::string next_namespace_id_string;
535 leveldb::Status s = db_->Get(leveldb::ReadOptions(), NextNamespaceIdKey(),
536 &next_namespace_id_string);
537 if (!DatabaseErrorCheck(s.ok() || s.IsNotFound()))
538 return false;
539 if (s.IsNotFound()) {
540 *next_namespace_id = 0;
541 return true;
542 }
543 bool conversion_ok =
544 base::StringToInt64(next_namespace_id_string, next_namespace_id);
545 return ConsistencyCheck(conversion_ok);
546 }
547
548 bool SessionStorageDatabase::UpdateNextNamespaceId(int64 namespace_id,
549 leveldb::WriteBatch* batch) {
550 int64 next_namespace_id;
551 if (!GetNextNamespaceId(&next_namespace_id))
552 return false;
553 if (next_namespace_id < namespace_id + namespace_offset_ + 1) {
554 next_namespace_id = namespace_id + namespace_offset_ + 1;
555 batch->Put(NextNamespaceIdKey(), base::Int64ToString(next_namespace_id));
556 }
557 return true;
558 }
559
560 bool SessionStorageDatabase::GetRefCount(const std::string& map_key,
561 int64* ref_count) {
562 std::string ref_count_string;
563 leveldb::Status s =
564 db_->Get(leveldb::ReadOptions(), map_key, &ref_count_string);
565 if (!ConsistencyCheck(s.ok()))
566 return false;
567 bool conversion_ok = base::StringToInt64(ref_count_string, ref_count);
568 return ConsistencyCheck(conversion_ok);
569 }
570
571 bool SessionStorageDatabase::DecreaseRefCount(const std::string& map_id,
572 int decrease,
573 leveldb::WriteBatch* batch) {
574 // Decrease the ref count for the map.
575 std::string map_key = MapRefCountKey(map_id);
576 int64 ref_count = 0;
577 if (!GetRefCount(map_key, &ref_count))
578 return false;
579 if (!ConsistencyCheck(decrease <= ref_count))
580 return false;
581 ref_count -= decrease;
582 if (ref_count > 0) {
583 batch->Put(map_key, base::Int64ToString(ref_count));
584 } else {
585 // Clear all keys in the map.
586 if (!ClearMap(map_id, batch))
587 return false;
588 batch->Delete(map_key);
589 }
590 return true;
591 }
592
593 bool SessionStorageDatabase::ClearMap(const std::string& map_id,
594 leveldb::WriteBatch* batch) {
595 ValuesMap values;
596 if (!ReadMap(map_id, &values, true))
597 return false;
598 for (ValuesMap::const_iterator it = values.begin(); it != values.end(); ++it)
599 batch->Delete(MapKey(map_id, UTF16ToUTF8(it->first)));
600 return true;
601 }
602
603 bool SessionStorageDatabase::DeleteOrigin(const std::string& namespace_id,
604 const std::string& origin,
605 leveldb::WriteBatch* batch) {
606 std::string namespace_key = NamespaceKey(namespace_id, origin);
607 std::string map_id;
608 leveldb::Status s = db_->Get(leveldb::ReadOptions(), namespace_key, &map_id);
609 if (s.IsNotFound())
610 return true; // Nothing to delete.
611 if (!DatabaseErrorCheck(s.ok()))
612 return false;
613 if (!DecreaseRefCount(map_id, 1, batch))
614 return false;
615 batch->Delete(namespace_key);
616 return true;
617 }
618
619 std::string SessionStorageDatabase::NamespaceStartKey(
620 const std::string& namespace_id) {
621 return base::StringPrintf("namespace-%s", namespace_id.c_str());
622 }
623
624 std::string SessionStorageDatabase::NamespaceStartKey(int64 namespace_id,
625 int64 namespace_offset) {
626 return NamespaceStartKey(
627 base::Int64ToString(namespace_id + namespace_offset));
628 }
629
630 std::string SessionStorageDatabase::NamespaceKey(
631 const std::string& namespace_id, const std::string& origin) {
632 return base::StringPrintf("namespace-%s-%s", namespace_id.c_str(),
633 origin.c_str());
634 }
635
636 std::string SessionStorageDatabase::NamespaceKey(
637 int64 namespace_id, int64 namespace_offset, const GURL& origin) {
638 return NamespaceKey(base::Int64ToString(namespace_id + namespace_offset),
639 origin.spec());
640 }
641
642 std::string SessionStorageDatabase::NamespacePrefix() {
643 return std::string("namespace-");
644 }
645
646 std::string SessionStorageDatabase::MapRefCountKey(const std::string& map_id) {
647 return base::StringPrintf("map-%s", map_id.c_str());
648 }
649
650 std::string SessionStorageDatabase::MapKey(const std::string& map_id,
651 const std::string& key) {
652 return base::StringPrintf("map-%s-%s", map_id.c_str(), key.c_str());
653 }
654
655 std::string SessionStorageDatabase::MapPrefix() {
656 return std::string("map-");
657 }
658
659 std::string SessionStorageDatabase::NextNamespaceIdKey() {
660 return "next-namespace-id";
661 }
662
663 std::string SessionStorageDatabase::NextMapIdKey() {
664 return "next-map-id";
665 }
666
667 } // namespace dom_storage
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698