Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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 "content/browser/service_worker/service_worker_database.h" | 5 #include "content/browser/service_worker/service_worker_database.h" |
| 6 | 6 |
| 7 #include <string> | 7 #include <string> |
| 8 | 8 |
| 9 #include "base/file_util.h" | 9 #include "base/file_util.h" |
| 10 #include "base/location.h" | 10 #include "base/location.h" |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 35 // | 35 // |
| 36 // key: "INITDATA_NEXT_RESOURCE_ID" | 36 // key: "INITDATA_NEXT_RESOURCE_ID" |
| 37 // value: <int64 'next_available_resource_id'> | 37 // value: <int64 'next_available_resource_id'> |
| 38 // | 38 // |
| 39 // key: "INITDATA_NEXT_VERSION_ID" | 39 // key: "INITDATA_NEXT_VERSION_ID" |
| 40 // value: <int64 'next_available_version_id'> | 40 // value: <int64 'next_available_version_id'> |
| 41 // | 41 // |
| 42 // key: "INITDATA_UNIQUE_ORIGIN:" + <GURL 'origin'> | 42 // key: "INITDATA_UNIQUE_ORIGIN:" + <GURL 'origin'> |
| 43 // value: <empty> | 43 // value: <empty> |
| 44 // | 44 // |
| 45 // key: "PRES:" + <int64 'purgeable_resource_id'> | |
| 46 // value: <empty> | |
| 47 // | |
| 45 // key: "REG:" + <GURL 'origin'> + '\x00' + <int64 'registration_id'> | 48 // key: "REG:" + <GURL 'origin'> + '\x00' + <int64 'registration_id'> |
| 46 // (ex. "REG:http://example.com\x00123456") | 49 // (ex. "REG:http://example.com\x00123456") |
| 47 // value: <ServiceWorkerRegistrationData serialized as a string> | 50 // value: <ServiceWorkerRegistrationData serialized as a string> |
| 51 // | |
| 52 // key: "URES:" + <int64 'uncommitted_resource_id'> | |
| 53 // value: <empty> | |
| 48 | 54 |
| 49 namespace content { | 55 namespace content { |
| 50 | 56 |
| 51 namespace { | 57 namespace { |
| 52 | 58 |
| 53 const char kDatabaseVersionKey[] = "INITDATA_DB_VERSION"; | 59 const char kDatabaseVersionKey[] = "INITDATA_DB_VERSION"; |
| 54 const char kNextRegIdKey[] = "INITDATA_NEXT_REGISTRATION_ID"; | 60 const char kNextRegIdKey[] = "INITDATA_NEXT_REGISTRATION_ID"; |
| 55 const char kNextResIdKey[] = "INITDATA_NEXT_RESOURCE_ID"; | 61 const char kNextResIdKey[] = "INITDATA_NEXT_RESOURCE_ID"; |
| 56 const char kNextVerIdKey[] = "INITDATA_NEXT_VERSION_ID"; | 62 const char kNextVerIdKey[] = "INITDATA_NEXT_VERSION_ID"; |
| 57 const char kUniqueOriginKey[] = "INITDATA_UNIQUE_ORIGIN:"; | 63 const char kUniqueOriginKey[] = "INITDATA_UNIQUE_ORIGIN:"; |
| 58 | 64 |
| 59 const char kRegKeyPrefix[] = "REG:"; | 65 const char kRegKeyPrefix[] = "REG:"; |
| 60 const char kKeySeparator = '\x00'; | 66 const char kKeySeparator = '\x00'; |
| 61 | 67 |
| 68 const char kUncommittedResIdKeyPrefix[] = "URES:"; | |
| 69 const char kPurgeableResIdKeyPrefix[] = "PRES:"; | |
| 70 | |
| 62 const int64 kCurrentSchemaVersion = 1; | 71 const int64 kCurrentSchemaVersion = 1; |
| 63 | 72 |
| 64 bool RemovePrefix(const std::string& str, | 73 bool RemovePrefix(const std::string& str, |
| 65 const std::string& prefix, | 74 const std::string& prefix, |
| 66 std::string* out) { | 75 std::string* out) { |
| 67 if (!StartsWithASCII(str, prefix, true)) | 76 if (!StartsWithASCII(str, prefix, true)) |
| 68 return false; | 77 return false; |
| 69 if (out) | 78 if (out) |
| 70 *out = str.substr(prefix.size()); | 79 *out = str.substr(prefix.size()); |
| 71 return true; | 80 return true; |
| 72 } | 81 } |
| 73 | 82 |
| 74 std::string CreateRegistrationKey(int64 registration_id, | 83 std::string CreateRegistrationKey(int64 registration_id, |
| 75 const GURL& origin) { | 84 const GURL& origin) { |
| 76 return base::StringPrintf("%s%s%c%s", | 85 return base::StringPrintf("%s%s%c%s", |
| 77 kRegKeyPrefix, | 86 kRegKeyPrefix, |
| 78 origin.spec().c_str(), | 87 origin.spec().c_str(), |
| 79 kKeySeparator, | 88 kKeySeparator, |
| 80 base::Int64ToString(registration_id).c_str()); | 89 base::Int64ToString(registration_id).c_str()); |
| 81 } | 90 } |
| 82 | 91 |
| 83 std::string CreateUniqueOriginKey(const GURL& origin) { | 92 std::string CreateUniqueOriginKey(const GURL& origin) { |
| 84 return base::StringPrintf("%s%s", kUniqueOriginKey, origin.spec().c_str()); | 93 return base::StringPrintf("%s%s", kUniqueOriginKey, origin.spec().c_str()); |
| 85 } | 94 } |
| 86 | 95 |
| 96 std::string CreateResourceIdKey(const char* key_prefix, int64 resource_id) { | |
| 97 return base::StringPrintf( | |
| 98 "%s%s", key_prefix, base::Int64ToString(resource_id).c_str()); | |
| 99 } | |
| 100 | |
| 87 void PutRegistrationDataToBatch( | 101 void PutRegistrationDataToBatch( |
| 88 const ServiceWorkerDatabase::RegistrationData& input, | 102 const ServiceWorkerDatabase::RegistrationData& input, |
| 89 leveldb::WriteBatch* batch) { | 103 leveldb::WriteBatch* batch) { |
| 90 DCHECK(batch); | 104 DCHECK(batch); |
| 91 | 105 |
| 92 // Convert RegistrationData to ServiceWorkerRegistrationData. | 106 // Convert RegistrationData to ServiceWorkerRegistrationData. |
| 93 ServiceWorkerRegistrationData data; | 107 ServiceWorkerRegistrationData data; |
| 94 data.set_registration_id(input.registration_id); | 108 data.set_registration_id(input.registration_id); |
| 95 data.set_scope_url(input.scope.spec()); | 109 data.set_scope_url(input.scope.spec()); |
| 96 data.set_script_url(input.script.spec()); | 110 data.set_script_url(input.script.spec()); |
| (...skipping 246 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 343 registrations[0].registration_id == registration_id) { | 357 registrations[0].registration_id == registration_id) { |
| 344 batch.Delete(CreateUniqueOriginKey(origin)); | 358 batch.Delete(CreateUniqueOriginKey(origin)); |
| 345 } | 359 } |
| 346 | 360 |
| 347 batch.Delete(CreateRegistrationKey(registration_id, origin)); | 361 batch.Delete(CreateRegistrationKey(registration_id, origin)); |
| 348 | 362 |
| 349 // TODO(nhiroki): Delete ResourceRecords tied with this registration. | 363 // TODO(nhiroki): Delete ResourceRecords tied with this registration. |
| 350 return WriteBatch(&batch); | 364 return WriteBatch(&batch); |
| 351 } | 365 } |
| 352 | 366 |
| 367 bool ServiceWorkerDatabase::GetUncommittedResourceIds(std::set<int64>* ids) { | |
| 368 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | |
| 369 if (!LazyOpen(false) || is_disabled_) | |
| 370 return false; | |
| 371 return ReadResourceIds(kUncommittedResIdKeyPrefix, ids); | |
| 372 } | |
| 373 | |
| 374 bool ServiceWorkerDatabase::WriteUncommittedResourceIds( | |
| 375 const std::set<int64>& ids) { | |
| 376 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | |
| 377 if (!LazyOpen(true) || is_disabled_) | |
| 378 return false; | |
| 379 return WriteResourceIds(kUncommittedResIdKeyPrefix, ids); | |
| 380 } | |
| 381 | |
| 382 bool ServiceWorkerDatabase::ClearUncommittedResourceIds( | |
| 383 const std::set<int64>& ids) { | |
| 384 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | |
| 385 if (!LazyOpen(true) || is_disabled_) | |
| 386 return false; | |
| 387 return DeleteResourceIds(kUncommittedResIdKeyPrefix, ids); | |
| 388 } | |
| 389 | |
| 390 bool ServiceWorkerDatabase::GetPurgeableResourceIds(std::set<int64>* ids) { | |
| 391 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | |
| 392 if (!LazyOpen(false) || is_disabled_) | |
| 393 return false; | |
| 394 return ReadResourceIds(kPurgeableResIdKeyPrefix, ids); | |
| 395 } | |
| 396 | |
| 397 bool ServiceWorkerDatabase::WritePurgeableResourceIds( | |
| 398 const std::set<int64>& ids) { | |
| 399 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | |
| 400 if (!LazyOpen(true) || is_disabled_) | |
| 401 return false; | |
| 402 return WriteResourceIds(kPurgeableResIdKeyPrefix, ids); | |
| 403 } | |
| 404 | |
| 405 bool ServiceWorkerDatabase::ClearPurgeableResourceIds( | |
| 406 const std::set<int64>& ids) { | |
| 407 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | |
| 408 if (!LazyOpen(true) || is_disabled_) | |
|
michaeln
2014/04/30 03:23:57
These tests and the dcheck could also be in the co
nhiroki
2014/04/30 04:04:06
That's just my preference. Basically I prefer such
| |
| 409 return false; | |
| 410 return DeleteResourceIds(kPurgeableResIdKeyPrefix, ids); | |
| 411 } | |
| 412 | |
| 353 bool ServiceWorkerDatabase::LazyOpen(bool create_if_needed) { | 413 bool ServiceWorkerDatabase::LazyOpen(bool create_if_needed) { |
| 354 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | 414 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); |
| 355 if (IsOpen()) | 415 if (IsOpen()) |
| 356 return true; | 416 return true; |
| 357 | 417 |
| 358 // Do not try to open a database if we tried and failed once. | 418 // Do not try to open a database if we tried and failed once. |
| 359 if (is_disabled_) | 419 if (is_disabled_) |
| 360 return false; | 420 return false; |
| 361 | 421 |
| 362 // When |path_| is empty, open a database in-memory. | 422 // When |path_| is empty, open a database in-memory. |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 444 RegistrationData parsed; | 504 RegistrationData parsed; |
| 445 if (!ParseRegistrationData(value, &parsed)) { | 505 if (!ParseRegistrationData(value, &parsed)) { |
| 446 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); | 506 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); |
| 447 return false; | 507 return false; |
| 448 } | 508 } |
| 449 | 509 |
| 450 *registration = parsed; | 510 *registration = parsed; |
| 451 return true; | 511 return true; |
| 452 } | 512 } |
| 453 | 513 |
| 514 bool ServiceWorkerDatabase::ReadResourceIds(const char* id_key_prefix, | |
| 515 std::set<int64>* ids) { | |
| 516 DCHECK(id_key_prefix); | |
| 517 DCHECK(ids); | |
| 518 | |
| 519 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); | |
| 520 for (itr->Seek(id_key_prefix); itr->Valid(); itr->Next()) { | |
| 521 if (!itr->status().ok()) { | |
| 522 HandleError(FROM_HERE, itr->status()); | |
| 523 ids->clear(); | |
| 524 return false; | |
| 525 } | |
| 526 | |
| 527 std::string unprefixed; | |
| 528 if (!RemovePrefix(itr->key().ToString(), id_key_prefix, &unprefixed)) | |
| 529 break; | |
| 530 | |
| 531 int64 resource_id; | |
| 532 if (!base::StringToInt64(unprefixed, &resource_id)) { | |
| 533 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); | |
| 534 ids->clear(); | |
| 535 return false; | |
| 536 } | |
| 537 ids->insert(resource_id); | |
| 538 } | |
| 539 return true; | |
| 540 } | |
| 541 | |
| 542 bool ServiceWorkerDatabase::WriteResourceIds(const char* id_key_prefix, | |
| 543 const std::set<int64>& ids) { | |
| 544 DCHECK(id_key_prefix); | |
| 545 if (ids.empty()) | |
| 546 return true; | |
| 547 leveldb::WriteBatch batch; | |
| 548 for (std::set<int64>::const_iterator itr = ids.begin(); | |
| 549 itr != ids.end(); ++itr) { | |
| 550 // Value should be empty. | |
| 551 batch.Put(CreateResourceIdKey(id_key_prefix, *itr), ""); | |
| 552 } | |
| 553 return WriteBatch(&batch); | |
| 554 } | |
| 555 | |
| 556 bool ServiceWorkerDatabase::DeleteResourceIds(const char* id_key_prefix, | |
| 557 const std::set<int64>& ids) { | |
| 558 DCHECK(id_key_prefix); | |
| 559 if (ids.empty()) | |
| 560 return true; | |
| 561 leveldb::WriteBatch batch; | |
| 562 for (std::set<int64>::const_iterator itr = ids.begin(); | |
| 563 itr != ids.end(); ++itr) { | |
| 564 batch.Delete(CreateResourceIdKey(id_key_prefix, *itr)); | |
| 565 } | |
| 566 return WriteBatch(&batch); | |
| 567 } | |
| 568 | |
| 454 bool ServiceWorkerDatabase::ReadDatabaseVersion(int64* db_version) { | 569 bool ServiceWorkerDatabase::ReadDatabaseVersion(int64* db_version) { |
| 455 std::string value; | 570 std::string value; |
| 456 leveldb::Status status = | 571 leveldb::Status status = |
| 457 db_->Get(leveldb::ReadOptions(), kDatabaseVersionKey, &value); | 572 db_->Get(leveldb::ReadOptions(), kDatabaseVersionKey, &value); |
| 458 if (status.IsNotFound()) { | 573 if (status.IsNotFound()) { |
| 459 // The database hasn't been initialized yet. | 574 // The database hasn't been initialized yet. |
| 460 *db_version = 0; | 575 *db_version = 0; |
| 461 return true; | 576 return true; |
| 462 } | 577 } |
| 463 if (!status.ok()) { | 578 if (!status.ok()) { |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 527 // TODO(nhiroki): Add an UMA histogram. | 642 // TODO(nhiroki): Add an UMA histogram. |
| 528 DLOG(ERROR) << "Failed at: " << from_here.ToString() | 643 DLOG(ERROR) << "Failed at: " << from_here.ToString() |
| 529 << " with error: " << status.ToString(); | 644 << " with error: " << status.ToString(); |
| 530 is_disabled_ = true; | 645 is_disabled_ = true; |
| 531 if (status.IsCorruption()) | 646 if (status.IsCorruption()) |
| 532 was_corruption_detected_ = true; | 647 was_corruption_detected_ = true; |
| 533 db_.reset(); | 648 db_.reset(); |
| 534 } | 649 } |
| 535 | 650 |
| 536 } // namespace content | 651 } // namespace content |
| OLD | NEW |