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