| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "webkit/dom_storage/session_storage_database.h" | 5 #include "webkit/dom_storage/session_storage_database.h" |
| 6 | 6 |
| 7 #include "base/file_util.h" | 7 #include "base/file_util.h" |
| 8 #include "base/logging.h" | 8 #include "base/logging.h" |
| 9 #include "base/stringprintf.h" | 9 #include "base/stringprintf.h" |
| 10 #include "base/string_number_conversions.h" | 10 #include "base/string_number_conversions.h" |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 45 SessionStorageDatabase::~SessionStorageDatabase() { | 45 SessionStorageDatabase::~SessionStorageDatabase() { |
| 46 } | 46 } |
| 47 | 47 |
| 48 void SessionStorageDatabase::ReadAreaValues(const std::string& namespace_id, | 48 void SessionStorageDatabase::ReadAreaValues(const std::string& namespace_id, |
| 49 const GURL& origin, | 49 const GURL& origin, |
| 50 ValuesMap* result) { | 50 ValuesMap* result) { |
| 51 // We don't create a database if it doesn't exist. In that case, there is | 51 // We don't create a database if it doesn't exist. In that case, there is |
| 52 // nothing to be added to the result. | 52 // nothing to be added to the result. |
| 53 if (!LazyOpen(false)) | 53 if (!LazyOpen(false)) |
| 54 return; | 54 return; |
| 55 |
| 56 // While ReadAreaValues is in progress, another thread can call |
| 57 // CommitAreaChanges. CommitAreaChanges might update map ref count key while |
| 58 // this thread is iterating over the map ref count key. To protect the reading |
| 59 // operation, create a snapshot and read from it. |
| 60 leveldb::ReadOptions options; |
| 61 options.snapshot = db_->GetSnapshot(); |
| 62 |
| 55 std::string map_id; | 63 std::string map_id; |
| 56 bool exists; | 64 bool exists; |
| 57 if (!GetMapForArea(namespace_id, origin.spec(), &exists, &map_id)) | 65 if (GetMapForArea(namespace_id, origin.spec(), options, &exists, &map_id) && |
| 58 return; | 66 exists) |
| 59 if (exists) | 67 ReadMap(map_id, options, result, false); |
| 60 ReadMap(map_id, result, false); | 68 db_->ReleaseSnapshot(options.snapshot); |
| 61 } | 69 } |
| 62 | 70 |
| 63 bool SessionStorageDatabase::CommitAreaChanges(const std::string& namespace_id, | 71 bool SessionStorageDatabase::CommitAreaChanges(const std::string& namespace_id, |
| 64 const GURL& origin, | 72 const GURL& origin, |
| 65 bool clear_all_first, | 73 bool clear_all_first, |
| 66 const ValuesMap& changes) { | 74 const ValuesMap& changes) { |
| 67 // Even if |changes| is empty, we need to write the appropriate placeholders | 75 // Even if |changes| is empty, we need to write the appropriate placeholders |
| 68 // in the database, so that it can be later shallow-copied succssfully. | 76 // in the database, so that it can be later shallow-copied succssfully. |
| 69 if (!LazyOpen(true)) | 77 if (!LazyOpen(true)) |
| 70 return false; | 78 return false; |
| 71 | 79 |
| 72 leveldb::WriteBatch batch; | 80 leveldb::WriteBatch batch; |
| 73 // Ensure that the keys "namespace-" "namespace-N" (see the schema above) | 81 // Ensure that the keys "namespace-" "namespace-N" (see the schema above) |
| 74 // exist. | 82 // exist. |
| 75 const bool kOkIfExists = true; | 83 const bool kOkIfExists = true; |
| 76 if (!CreateNamespace(namespace_id, kOkIfExists, &batch)) | 84 if (!CreateNamespace(namespace_id, kOkIfExists, &batch)) |
| 77 return false; | 85 return false; |
| 78 | 86 |
| 79 std::string map_id; | 87 std::string map_id; |
| 80 bool exists; | 88 bool exists; |
| 81 if (!GetMapForArea(namespace_id, origin.spec(), &exists, &map_id)) | 89 if (!GetMapForArea(namespace_id, origin.spec(), leveldb::ReadOptions(), |
| 90 &exists, &map_id)) |
| 82 return false; | 91 return false; |
| 83 if (exists) { | 92 if (exists) { |
| 84 int64 ref_count; | 93 int64 ref_count; |
| 85 if (!GetMapRefCount(map_id, &ref_count)) | 94 if (!GetMapRefCount(map_id, &ref_count)) |
| 86 return false; | 95 return false; |
| 87 if (ref_count > 1) { | 96 if (ref_count > 1) { |
| 88 if (!DeepCopyArea(namespace_id, origin, !clear_all_first, | 97 if (!DeepCopyArea(namespace_id, origin, !clear_all_first, |
| 89 &map_id, &batch)) | 98 &map_id, &batch)) |
| 90 return false; | 99 return false; |
| 91 } | 100 } |
| (...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 185 } | 194 } |
| 186 | 195 |
| 187 bool SessionStorageDatabase::ReadNamespaceIds( | 196 bool SessionStorageDatabase::ReadNamespaceIds( |
| 188 std::vector<std::string>* namespace_ids) { | 197 std::vector<std::string>* namespace_ids) { |
| 189 if (!LazyOpen(true)) | 198 if (!LazyOpen(true)) |
| 190 return false; | 199 return false; |
| 191 | 200 |
| 192 std::string namespace_prefix = NamespacePrefix(); | 201 std::string namespace_prefix = NamespacePrefix(); |
| 193 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions())); | 202 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions())); |
| 194 it->Seek(namespace_prefix); | 203 it->Seek(namespace_prefix); |
| 195 if (it->status().IsNotFound()) | 204 // Apparently it's possible for a leveldb iterator to have status "OK" and be |
| 205 // invalid. Interpret invalid iterator as "key not found". |
| 206 if (it->status().IsNotFound() || !it->Valid()) |
| 196 return true; | 207 return true; |
| 197 | 208 |
| 198 if (!DatabaseErrorCheck(it->status().ok())) | 209 if (!DatabaseErrorCheck(it->status().ok())) |
| 199 return false; | 210 return false; |
| 200 | 211 |
| 201 // Skip the dummy entry "namespace-" and iterate the namespaces. | 212 // Skip the dummy entry "namespace-" and iterate the namespaces. |
| 202 std::string current_namespace_start_key; | 213 std::string current_namespace_start_key; |
| 203 for (it->Next(); it->Valid(); it->Next()) { | 214 for (it->Next(); it->Valid(); it->Next()) { |
| 204 std::string key = it->key().ToString(); | 215 std::string key = it->key().ToString(); |
| 205 if (key.find(namespace_prefix) != 0) { | 216 if (key.find(namespace_prefix) != 0) { |
| (...skipping 10 matching lines...) Expand all Loading... |
| 216 // <namespaceid>. | 227 // <namespaceid>. |
| 217 current_namespace_start_key = key; | 228 current_namespace_start_key = key; |
| 218 namespace_ids->push_back( | 229 namespace_ids->push_back( |
| 219 key.substr(namespace_prefix.length(), | 230 key.substr(namespace_prefix.length(), |
| 220 key.length() - namespace_prefix.length() - 1)); | 231 key.length() - namespace_prefix.length() - 1)); |
| 221 } | 232 } |
| 222 } | 233 } |
| 223 return true; | 234 return true; |
| 224 } | 235 } |
| 225 | 236 |
| 237 bool SessionStorageDatabase::ReadOriginsInNamespace( |
| 238 const std::string& namespace_id, std::vector<GURL>* origins) { |
| 239 std::map<std::string, std::string> areas; |
| 240 if (!GetAreasInNamespace(namespace_id, &areas)) |
| 241 return false; |
| 242 for (std::map<std::string, std::string>::const_iterator it = areas.begin(); |
| 243 it != areas.end(); ++it) |
| 244 origins->push_back(GURL(it->first)); |
| 245 return true; |
| 246 } |
| 247 |
| 226 bool SessionStorageDatabase::LazyOpen(bool create_if_needed) { | 248 bool SessionStorageDatabase::LazyOpen(bool create_if_needed) { |
| 227 base::AutoLock auto_lock(db_lock_); | 249 base::AutoLock auto_lock(db_lock_); |
| 228 if (db_error_ || is_inconsistent_) { | 250 if (db_error_ || is_inconsistent_) { |
| 229 // Don't try to open a database that we know has failed already. | 251 // Don't try to open a database that we know has failed already. |
| 230 return false; | 252 return false; |
| 231 } | 253 } |
| 232 if (IsOpen()) | 254 if (IsOpen()) |
| 233 return true; | 255 return true; |
| 234 | 256 |
| 235 if (!create_if_needed && | 257 if (!create_if_needed && |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 330 } | 352 } |
| 331 return CallerErrorCheck(ok_if_exists); | 353 return CallerErrorCheck(ok_if_exists); |
| 332 } | 354 } |
| 333 | 355 |
| 334 bool SessionStorageDatabase::GetAreasInNamespace( | 356 bool SessionStorageDatabase::GetAreasInNamespace( |
| 335 const std::string& namespace_id, | 357 const std::string& namespace_id, |
| 336 std::map<std::string, std::string>* areas) { | 358 std::map<std::string, std::string>* areas) { |
| 337 std::string namespace_start_key = NamespaceStartKey(namespace_id); | 359 std::string namespace_start_key = NamespaceStartKey(namespace_id); |
| 338 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions())); | 360 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions())); |
| 339 it->Seek(namespace_start_key); | 361 it->Seek(namespace_start_key); |
| 340 if (it->status().IsNotFound()) { | 362 // Apparently it's possible for a leveldb iterator to have status "OK" and be |
| 363 // invalid. Interpret invalid iterator as "key not found". |
| 364 if (it->status().IsNotFound() || !it->Valid()) { |
| 341 // The namespace_start_key is not found when the namespace doesn't contain | 365 // The namespace_start_key is not found when the namespace doesn't contain |
| 342 // any areas. We don't need to do anything. | 366 // any areas. We don't need to do anything. |
| 343 return true; | 367 return true; |
| 344 } | 368 } |
| 345 if (!DatabaseErrorCheck(it->status().ok())) | 369 if (!DatabaseErrorCheck(it->status().ok())) |
| 346 return false; | 370 return false; |
| 347 | 371 |
| 348 // Skip the dummy entry "namespace-<namespaceid>-" and iterate the origins. | 372 // Skip the dummy entry "namespace-<namespaceid>-" and iterate the origins. |
| 349 for (it->Next(); it->Valid(); it->Next()) { | 373 for (it->Next(); it->Valid(); it->Next()) { |
| 350 std::string key = it->key().ToString(); | 374 std::string key = it->key().ToString(); |
| (...skipping 15 matching lines...) Expand all Loading... |
| 366 std::string namespace_key = NamespaceKey(namespace_id, origin); | 390 std::string namespace_key = NamespaceKey(namespace_id, origin); |
| 367 batch->Put(namespace_key, map_id); | 391 batch->Put(namespace_key, map_id); |
| 368 } | 392 } |
| 369 | 393 |
| 370 bool SessionStorageDatabase::DeleteAreaHelper( | 394 bool SessionStorageDatabase::DeleteAreaHelper( |
| 371 const std::string& namespace_id, | 395 const std::string& namespace_id, |
| 372 const std::string& origin, | 396 const std::string& origin, |
| 373 leveldb::WriteBatch* batch) { | 397 leveldb::WriteBatch* batch) { |
| 374 std::string map_id; | 398 std::string map_id; |
| 375 bool exists; | 399 bool exists; |
| 376 if (!GetMapForArea(namespace_id, origin, &exists, &map_id)) | 400 if (!GetMapForArea(namespace_id, origin, leveldb::ReadOptions(), &exists, |
| 401 &map_id)) |
| 377 return false; | 402 return false; |
| 378 if (!exists) | 403 if (!exists) |
| 379 return true; // Nothing to delete. | 404 return true; // Nothing to delete. |
| 380 if (!DecreaseMapRefCount(map_id, 1, batch)) | 405 if (!DecreaseMapRefCount(map_id, 1, batch)) |
| 381 return false; | 406 return false; |
| 382 std::string namespace_key = NamespaceKey(namespace_id, origin); | 407 std::string namespace_key = NamespaceKey(namespace_id, origin); |
| 383 batch->Delete(namespace_key); | 408 batch->Delete(namespace_key); |
| 384 return true; | 409 return true; |
| 385 } | 410 } |
| 386 | 411 |
| 387 bool SessionStorageDatabase::GetMapForArea(const std::string& namespace_id, | 412 bool SessionStorageDatabase::GetMapForArea(const std::string& namespace_id, |
| 388 const std::string& origin, | 413 const std::string& origin, |
| 414 const leveldb::ReadOptions& options, |
| 389 bool* exists, std::string* map_id) { | 415 bool* exists, std::string* map_id) { |
| 390 std::string namespace_key = NamespaceKey(namespace_id, origin); | 416 std::string namespace_key = NamespaceKey(namespace_id, origin); |
| 391 leveldb::Status s = db_->Get(leveldb::ReadOptions(), namespace_key, map_id); | 417 leveldb::Status s = db_->Get(options, namespace_key, map_id); |
| 392 if (s.IsNotFound()) { | 418 if (s.IsNotFound()) { |
| 393 *exists = false; | 419 *exists = false; |
| 394 return true; | 420 return true; |
| 395 } | 421 } |
| 396 *exists = true; | 422 *exists = true; |
| 397 return DatabaseErrorCheck(s.ok()); | 423 return DatabaseErrorCheck(s.ok()); |
| 398 } | 424 } |
| 399 | 425 |
| 400 bool SessionStorageDatabase::CreateMapForArea(const std::string& namespace_id, | 426 bool SessionStorageDatabase::CreateMapForArea(const std::string& namespace_id, |
| 401 const GURL& origin, | 427 const GURL& origin, |
| (...skipping 12 matching lines...) Expand all Loading... |
| 414 return false; | 440 return false; |
| 415 } | 441 } |
| 416 batch->Put(next_map_id_key, base::Int64ToString(++next_map_id)); | 442 batch->Put(next_map_id_key, base::Int64ToString(++next_map_id)); |
| 417 std::string namespace_key = NamespaceKey(namespace_id, origin.spec()); | 443 std::string namespace_key = NamespaceKey(namespace_id, origin.spec()); |
| 418 batch->Put(namespace_key, *map_id); | 444 batch->Put(namespace_key, *map_id); |
| 419 batch->Put(MapRefCountKey(*map_id), "1"); | 445 batch->Put(MapRefCountKey(*map_id), "1"); |
| 420 return true; | 446 return true; |
| 421 } | 447 } |
| 422 | 448 |
| 423 bool SessionStorageDatabase::ReadMap(const std::string& map_id, | 449 bool SessionStorageDatabase::ReadMap(const std::string& map_id, |
| 450 const leveldb::ReadOptions& options, |
| 424 ValuesMap* result, | 451 ValuesMap* result, |
| 425 bool only_keys) { | 452 bool only_keys) { |
| 426 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions())); | 453 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(options)); |
| 427 std::string map_start_key = MapRefCountKey(map_id); | 454 std::string map_start_key = MapRefCountKey(map_id); |
| 428 it->Seek(map_start_key); | 455 it->Seek(map_start_key); |
| 429 // The map needs to exist, otherwise we have a stale map_id in the database. | 456 // The map needs to exist, otherwise we have a stale map_id in the database. |
| 430 if (!ConsistencyCheck(!it->status().IsNotFound())) | 457 if (!ConsistencyCheck(!it->status().IsNotFound() && it->Valid())) |
| 431 return false; | 458 return false; |
| 432 if (!DatabaseErrorCheck(it->status().ok())) | 459 if (!DatabaseErrorCheck(it->status().ok())) |
| 433 return false; | 460 return false; |
| 434 // Skip the dummy entry "map-<mapid>-". | 461 // Skip the dummy entry "map-<mapid>-". |
| 435 for (it->Next(); it->Valid(); it->Next()) { | 462 for (it->Next(); it->Valid(); it->Next()) { |
| 436 std::string key = it->key().ToString(); | 463 std::string key = it->key().ToString(); |
| 437 if (key.find(map_start_key) != 0) { | 464 if (key.find(map_start_key) != 0) { |
| 438 // Iterated past the keys in this map. | 465 // Iterated past the keys in this map. |
| 439 break; | 466 break; |
| 440 } | 467 } |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 511 if (!ClearMap(map_id, batch)) | 538 if (!ClearMap(map_id, batch)) |
| 512 return false; | 539 return false; |
| 513 batch->Delete(MapRefCountKey(map_id)); | 540 batch->Delete(MapRefCountKey(map_id)); |
| 514 } | 541 } |
| 515 return true; | 542 return true; |
| 516 } | 543 } |
| 517 | 544 |
| 518 bool SessionStorageDatabase::ClearMap(const std::string& map_id, | 545 bool SessionStorageDatabase::ClearMap(const std::string& map_id, |
| 519 leveldb::WriteBatch* batch) { | 546 leveldb::WriteBatch* batch) { |
| 520 ValuesMap values; | 547 ValuesMap values; |
| 521 if (!ReadMap(map_id, &values, true)) | 548 if (!ReadMap(map_id, leveldb::ReadOptions(), &values, true)) |
| 522 return false; | 549 return false; |
| 523 for (ValuesMap::const_iterator it = values.begin(); it != values.end(); ++it) | 550 for (ValuesMap::const_iterator it = values.begin(); it != values.end(); ++it) |
| 524 batch->Delete(MapKey(map_id, UTF16ToUTF8(it->first))); | 551 batch->Delete(MapKey(map_id, UTF16ToUTF8(it->first))); |
| 525 return true; | 552 return true; |
| 526 } | 553 } |
| 527 | 554 |
| 528 bool SessionStorageDatabase::DeepCopyArea( | 555 bool SessionStorageDatabase::DeepCopyArea( |
| 529 const std::string& namespace_id, const GURL& origin, bool copy_data, | 556 const std::string& namespace_id, const GURL& origin, bool copy_data, |
| 530 std::string* map_id, leveldb::WriteBatch* batch) { | 557 std::string* map_id, leveldb::WriteBatch* batch) { |
| 531 // Example, data before deep copy: | 558 // Example, data before deep copy: |
| (...skipping 10 matching lines...) Expand all Loading... |
| 542 // | namespace-2- | dummy | | 569 // | namespace-2- | dummy | |
| 543 // | namespace-2-origin1 | 2 (mapid) << references the new map | 570 // | namespace-2-origin1 | 2 (mapid) << references the new map |
| 544 // | map-1- | 1 (dec. refcount) | | 571 // | map-1- | 1 (dec. refcount) | |
| 545 // | map-1-a | b | | 572 // | map-1-a | b | |
| 546 // | map-2- | 1 (refcount) | | 573 // | map-2- | 1 (refcount) | |
| 547 // | map-2-a | b | | 574 // | map-2-a | b | |
| 548 | 575 |
| 549 // Read the values from the old map here. If we don't need to copy the data, | 576 // Read the values from the old map here. If we don't need to copy the data, |
| 550 // this can stay empty. | 577 // this can stay empty. |
| 551 ValuesMap values; | 578 ValuesMap values; |
| 552 if (copy_data && !ReadMap(*map_id, &values, false)) | 579 if (copy_data && !ReadMap(*map_id, leveldb::ReadOptions(), &values, false)) |
| 553 return false; | 580 return false; |
| 554 if (!DecreaseMapRefCount(*map_id, 1, batch)) | 581 if (!DecreaseMapRefCount(*map_id, 1, batch)) |
| 555 return false; | 582 return false; |
| 556 // Create a new map (this will also break the association to the old map) and | 583 // Create a new map (this will also break the association to the old map) and |
| 557 // write the old data into it. This will write the id of the created map into | 584 // write the old data into it. This will write the id of the created map into |
| 558 // |map_id|. | 585 // |map_id|. |
| 559 if (!CreateMapForArea(namespace_id, origin, map_id, batch)) | 586 if (!CreateMapForArea(namespace_id, origin, map_id, batch)) |
| 560 return false; | 587 return false; |
| 561 WriteValuesToMap(*map_id, values, batch); | 588 WriteValuesToMap(*map_id, values, batch); |
| 562 return true; | 589 return true; |
| (...skipping 21 matching lines...) Expand all Loading... |
| 584 std::string SessionStorageDatabase::MapKey(const std::string& map_id, | 611 std::string SessionStorageDatabase::MapKey(const std::string& map_id, |
| 585 const std::string& key) { | 612 const std::string& key) { |
| 586 return base::StringPrintf("map-%s-%s", map_id.c_str(), key.c_str()); | 613 return base::StringPrintf("map-%s-%s", map_id.c_str(), key.c_str()); |
| 587 } | 614 } |
| 588 | 615 |
| 589 const char* SessionStorageDatabase::NextMapIdKey() { | 616 const char* SessionStorageDatabase::NextMapIdKey() { |
| 590 return "next-map-id"; | 617 return "next-map-id"; |
| 591 } | 618 } |
| 592 | 619 |
| 593 } // namespace dom_storage | 620 } // namespace dom_storage |
| OLD | NEW |