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
3 // found in the LICENSE file.
5 #include "content/browser/service_worker/service_worker_database.h"
9 #include "base/file_util.h"
10 #include "base/location.h"
11 #include "base/logging.h"
12 #include "base/metrics/histogram.h"
13 #include "base/stl_util.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_split.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "content/browser/service_worker/service_worker_database.pb.h"
19 #include "content/common/service_worker/service_worker_types.h"
20 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
21 #include "third_party/leveldatabase/src/include/leveldb/db.h"
22 #include "third_party/leveldatabase/src/include/leveldb/env.h"
23 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
25 // LevelDB database schema
26 // =======================
29 // - int64 value is serialized as a string by base::Int64ToString().
30 // - GURL value is serialized as a string by GURL::spec().
32 // Version 1 (in sorted order)
33 // key: "INITDATA_DB_VERSION"
36 // key: "INITDATA_NEXT_REGISTRATION_ID"
37 // value: <int64 'next_available_registration_id'>
39 // key: "INITDATA_NEXT_RESOURCE_ID"
40 // value: <int64 'next_available_resource_id'>
42 // key: "INITDATA_NEXT_VERSION_ID"
43 // value: <int64 'next_available_version_id'>
45 // key: "INITDATA_UNIQUE_ORIGIN:" + <GURL 'origin'>
48 // key: "PRES:" + <int64 'purgeable_resource_id'>
51 // key: "REG:" + <GURL 'origin'> + '\x00' + <int64 'registration_id'>
52 // (ex. "REG:http://example.com\x00123456")
53 // value: <ServiceWorkerRegistrationData serialized as a string>
55 // key: "RES:" + <int64 'version_id'> + '\x00' + <int64 'resource_id'>
56 // (ex. "RES:123456\x00654321")
57 // value: <ServiceWorkerResourceRecord serialized as a string>
59 // key: "URES:" + <int64 'uncommitted_resource_id'>
66 const char kDatabaseVersionKey[] = "INITDATA_DB_VERSION";
67 const char kNextRegIdKey[] = "INITDATA_NEXT_REGISTRATION_ID";
68 const char kNextResIdKey[] = "INITDATA_NEXT_RESOURCE_ID";
69 const char kNextVerIdKey[] = "INITDATA_NEXT_VERSION_ID";
70 const char kUniqueOriginKey[] = "INITDATA_UNIQUE_ORIGIN:";
72 const char kRegKeyPrefix[] = "REG:";
73 const char kResKeyPrefix[] = "RES:";
74 const char kKeySeparator = '\x00';
76 const char kUncommittedResIdKeyPrefix[] = "URES:";
77 const char kPurgeableResIdKeyPrefix[] = "PRES:";
79 const int64 kCurrentSchemaVersion = 1;
82 const char kOpenResultHistogramLabel[] =
83 "ServiceWorker.Database.OpenResult";
84 const char kReadResultHistogramLabel[] =
85 "ServiceWorker.Database.ReadResult";
86 const char kWriteResultHistogramLabel[] =
87 "ServiceWorker.Database.WriteResult";
89 bool RemovePrefix(const std::string& str,
90 const std::string& prefix,
92 if (!StartsWithASCII(str, prefix, true))
95 *out = str.substr(prefix.size());
99 std::string CreateRegistrationKey(int64 registration_id,
100 const GURL& origin) {
101 return base::StringPrintf("%s%s%c%s",
103 origin.spec().c_str(),
105 base::Int64ToString(registration_id).c_str());
108 std::string CreateResourceRecordKeyPrefix(int64 version_id) {
109 return base::StringPrintf("%s%s%c",
111 base::Int64ToString(version_id).c_str(),
115 std::string CreateResourceRecordKey(int64 version_id,
117 return CreateResourceRecordKeyPrefix(version_id).append(
118 base::Int64ToString(resource_id));
121 std::string CreateUniqueOriginKey(const GURL& origin) {
122 return base::StringPrintf("%s%s", kUniqueOriginKey, origin.spec().c_str());
125 std::string CreateResourceIdKey(const char* key_prefix, int64 resource_id) {
126 return base::StringPrintf(
127 "%s%s", key_prefix, base::Int64ToString(resource_id).c_str());
130 void PutRegistrationDataToBatch(
131 const ServiceWorkerDatabase::RegistrationData& input,
132 leveldb::WriteBatch* batch) {
135 // Convert RegistrationData to ServiceWorkerRegistrationData.
136 ServiceWorkerRegistrationData data;
137 data.set_registration_id(input.registration_id);
138 data.set_scope_url(input.scope.spec());
139 data.set_script_url(input.script.spec());
140 data.set_version_id(input.version_id);
141 data.set_is_active(input.is_active);
142 data.set_has_fetch_handler(input.has_fetch_handler);
143 data.set_last_update_check_time(input.last_update_check.ToInternalValue());
146 bool success = data.SerializeToString(&value);
148 GURL origin = input.scope.GetOrigin();
149 batch->Put(CreateRegistrationKey(data.registration_id(), origin), value);
152 void PutResourceRecordToBatch(
153 const ServiceWorkerDatabase::ResourceRecord& input,
155 leveldb::WriteBatch* batch) {
158 // Convert ResourceRecord to ServiceWorkerResourceRecord.
159 ServiceWorkerResourceRecord record;
160 record.set_resource_id(input.resource_id);
161 record.set_url(input.url.spec());
164 bool success = record.SerializeToString(&value);
166 batch->Put(CreateResourceRecordKey(version_id, input.resource_id), value);
169 void PutUniqueOriginToBatch(const GURL& origin,
170 leveldb::WriteBatch* batch) {
171 // Value should be empty.
172 batch->Put(CreateUniqueOriginKey(origin), "");
175 void PutPurgeableResourceIdToBatch(int64 resource_id,
176 leveldb::WriteBatch* batch) {
177 // Value should be empty.
178 batch->Put(CreateResourceIdKey(kPurgeableResIdKeyPrefix, resource_id), "");
181 ServiceWorkerDatabase::Status ParseId(
182 const std::string& serialized,
186 if (!base::StringToInt64(serialized, &id) || id < 0)
187 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
189 return ServiceWorkerDatabase::STATUS_OK;
192 ServiceWorkerDatabase::Status ParseDatabaseVersion(
193 const std::string& serialized,
196 const int kFirstValidVersion = 1;
198 if (!base::StringToInt64(serialized, &version) ||
199 version < kFirstValidVersion) {
200 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
202 if (kCurrentSchemaVersion < version) {
203 DLOG(ERROR) << "ServiceWorkerDatabase has newer schema version"
204 << " than the current latest version: "
205 << version << " vs " << kCurrentSchemaVersion;
206 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
209 return ServiceWorkerDatabase::STATUS_OK;
212 ServiceWorkerDatabase::Status ParseRegistrationData(
213 const std::string& serialized,
214 ServiceWorkerDatabase::RegistrationData* out) {
216 ServiceWorkerRegistrationData data;
217 if (!data.ParseFromString(serialized))
218 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
220 GURL scope_url(data.scope_url());
221 GURL script_url(data.script_url());
222 if (!scope_url.is_valid() ||
223 !script_url.is_valid() ||
224 scope_url.GetOrigin() != script_url.GetOrigin()) {
225 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
228 // Convert ServiceWorkerRegistrationData to RegistrationData.
229 out->registration_id = data.registration_id();
230 out->scope = scope_url;
231 out->script = script_url;
232 out->version_id = data.version_id();
233 out->is_active = data.is_active();
234 out->has_fetch_handler = data.has_fetch_handler();
235 out->last_update_check =
236 base::Time::FromInternalValue(data.last_update_check_time());
237 return ServiceWorkerDatabase::STATUS_OK;
240 ServiceWorkerDatabase::Status ParseResourceRecord(
241 const std::string& serialized,
242 ServiceWorkerDatabase::ResourceRecord* out) {
244 ServiceWorkerResourceRecord record;
245 if (!record.ParseFromString(serialized))
246 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
248 GURL url(record.url());
250 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
252 // Convert ServiceWorkerResourceRecord to ResourceRecord.
253 out->resource_id = record.resource_id();
255 return ServiceWorkerDatabase::STATUS_OK;
258 ServiceWorkerDatabase::Status LevelDBStatusToStatus(
259 const leveldb::Status& status) {
261 return ServiceWorkerDatabase::STATUS_OK;
262 else if (status.IsNotFound())
263 return ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND;
264 else if (status.IsIOError())
265 return ServiceWorkerDatabase::STATUS_ERROR_IO_ERROR;
266 else if (status.IsCorruption())
267 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
269 return ServiceWorkerDatabase::STATUS_ERROR_FAILED;
272 const char* StatusToString(ServiceWorkerDatabase::Status status) {
274 case ServiceWorkerDatabase::STATUS_OK:
275 return "Database OK";
276 case ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND:
277 return "Database not found";
278 case ServiceWorkerDatabase::STATUS_ERROR_IO_ERROR:
279 return "Database IO error";
280 case ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED:
281 return "Database corrupted";
282 case ServiceWorkerDatabase::STATUS_ERROR_FAILED:
283 return "Database operation failed";
284 case ServiceWorkerDatabase::STATUS_ERROR_MAX:
286 return "Database unknown error";
289 return "Database unknown error";
294 ServiceWorkerDatabase::RegistrationData::RegistrationData()
295 : registration_id(kInvalidServiceWorkerRegistrationId),
296 version_id(kInvalidServiceWorkerVersionId),
298 has_fetch_handler(false) {
301 ServiceWorkerDatabase::RegistrationData::~RegistrationData() {
304 ServiceWorkerDatabase::ServiceWorkerDatabase(const base::FilePath& path)
306 next_avail_registration_id_(0),
307 next_avail_resource_id_(0),
308 next_avail_version_id_(0),
309 state_(UNINITIALIZED) {
310 sequence_checker_.DetachFromSequence();
313 ServiceWorkerDatabase::~ServiceWorkerDatabase() {
314 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
318 ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetNextAvailableIds(
319 int64* next_avail_registration_id,
320 int64* next_avail_version_id,
321 int64* next_avail_resource_id) {
322 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
323 DCHECK(next_avail_registration_id);
324 DCHECK(next_avail_version_id);
325 DCHECK(next_avail_resource_id);
327 Status status = LazyOpen(false);
328 if (IsNewOrNonexistentDatabase(status)) {
329 *next_avail_registration_id = 0;
330 *next_avail_version_id = 0;
331 *next_avail_resource_id = 0;
334 if (status != STATUS_OK)
337 status = ReadNextAvailableId(kNextRegIdKey, &next_avail_registration_id_);
338 if (status != STATUS_OK)
340 status = ReadNextAvailableId(kNextVerIdKey, &next_avail_version_id_);
341 if (status != STATUS_OK)
343 status = ReadNextAvailableId(kNextResIdKey, &next_avail_resource_id_);
344 if (status != STATUS_OK)
347 *next_avail_registration_id = next_avail_registration_id_;
348 *next_avail_version_id = next_avail_version_id_;
349 *next_avail_resource_id = next_avail_resource_id_;
353 ServiceWorkerDatabase::Status
354 ServiceWorkerDatabase::GetOriginsWithRegistrations(std::set<GURL>* origins) {
355 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
356 DCHECK(origins->empty());
358 Status status = LazyOpen(false);
359 if (IsNewOrNonexistentDatabase(status))
361 if (status != STATUS_OK)
364 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
365 for (itr->Seek(kUniqueOriginKey); itr->Valid(); itr->Next()) {
366 status = LevelDBStatusToStatus(itr->status());
367 if (status != STATUS_OK) {
368 HandleReadResult(FROM_HERE, status);
374 if (!RemovePrefix(itr->key().ToString(), kUniqueOriginKey, &origin))
376 origins->insert(GURL(origin));
379 HandleReadResult(FROM_HERE, status);
383 ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetRegistrationsForOrigin(
385 std::vector<RegistrationData>* registrations) {
386 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
387 DCHECK(registrations->empty());
389 Status status = LazyOpen(false);
390 if (IsNewOrNonexistentDatabase(status))
392 if (status != STATUS_OK)
395 // Create a key prefix for registrations.
396 std::string prefix = base::StringPrintf(
397 "%s%s%c", kRegKeyPrefix, origin.spec().c_str(), kKeySeparator);
399 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
400 for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
401 status = LevelDBStatusToStatus(itr->status());
402 if (status != STATUS_OK) {
403 HandleReadResult(FROM_HERE, status);
404 registrations->clear();
408 if (!RemovePrefix(itr->key().ToString(), prefix, NULL))
411 RegistrationData registration;
412 status = ParseRegistrationData(itr->value().ToString(), ®istration);
413 if (status != STATUS_OK) {
414 HandleReadResult(FROM_HERE, status);
415 registrations->clear();
418 registrations->push_back(registration);
421 HandleReadResult(FROM_HERE, status);
425 ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetAllRegistrations(
426 std::vector<RegistrationData>* registrations) {
427 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
428 DCHECK(registrations->empty());
430 Status status = LazyOpen(false);
431 if (IsNewOrNonexistentDatabase(status))
433 if (status != STATUS_OK)
436 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
437 for (itr->Seek(kRegKeyPrefix); itr->Valid(); itr->Next()) {
438 status = LevelDBStatusToStatus(itr->status());
439 if (status != STATUS_OK) {
440 HandleReadResult(FROM_HERE, status);
441 registrations->clear();
445 if (!RemovePrefix(itr->key().ToString(), kRegKeyPrefix, NULL))
448 RegistrationData registration;
449 status = ParseRegistrationData(itr->value().ToString(), ®istration);
450 if (status != STATUS_OK) {
451 HandleReadResult(FROM_HERE, status);
452 registrations->clear();
455 registrations->push_back(registration);
458 HandleReadResult(FROM_HERE, status);
462 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadRegistration(
463 int64 registration_id,
465 RegistrationData* registration,
466 std::vector<ResourceRecord>* resources) {
467 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
468 DCHECK(registration);
471 Status status = LazyOpen(false);
472 if (IsNewOrNonexistentDatabase(status) || status != STATUS_OK)
475 RegistrationData value;
476 status = ReadRegistrationData(registration_id, origin, &value);
477 if (status != STATUS_OK)
480 status = ReadResourceRecords(value.version_id, resources);
481 if (status != STATUS_OK)
484 *registration = value;
488 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteRegistration(
489 const RegistrationData& registration,
490 const std::vector<ResourceRecord>& resources,
491 std::vector<int64>* newly_purgeable_resources) {
492 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
493 Status status = LazyOpen(true);
494 if (status != STATUS_OK)
497 leveldb::WriteBatch batch;
498 BumpNextRegistrationIdIfNeeded(registration.registration_id, &batch);
499 BumpNextVersionIdIfNeeded(registration.version_id, &batch);
501 PutUniqueOriginToBatch(registration.scope.GetOrigin(), &batch);
502 PutRegistrationDataToBatch(registration, &batch);
504 // Used for avoiding multiple writes for the same resource id or url.
505 std::set<int64> pushed_resources;
506 std::set<GURL> pushed_urls;
507 for (std::vector<ResourceRecord>::const_iterator itr = resources.begin();
508 itr != resources.end(); ++itr) {
509 if (!itr->url.is_valid())
510 return STATUS_ERROR_FAILED;
512 // Duplicated resource id or url should not exist.
513 DCHECK(pushed_resources.insert(itr->resource_id).second);
514 DCHECK(pushed_urls.insert(itr->url).second);
516 PutResourceRecordToBatch(*itr, registration.version_id, &batch);
518 // Delete a resource from the uncommitted list.
519 batch.Delete(CreateResourceIdKey(
520 kUncommittedResIdKeyPrefix, itr->resource_id));
523 // Retrieve a previous version to sweep purgeable resources.
524 RegistrationData old_registration;
525 status = ReadRegistrationData(registration.registration_id,
526 registration.scope.GetOrigin(),
528 if (status != STATUS_OK && status != STATUS_ERROR_NOT_FOUND)
530 if (status == STATUS_OK) {
531 DCHECK_LT(old_registration.version_id, registration.version_id);
532 status = DeleteResourceRecords(
533 old_registration.version_id, newly_purgeable_resources, &batch);
534 if (status != STATUS_OK)
537 // Currently resource sharing across versions and registrations is not
538 // supported, so resource ids should not be overlapped between
539 // |registration| and |old_registration|.
540 std::set<int64> deleted_resources(newly_purgeable_resources->begin(),
541 newly_purgeable_resources->end());
542 DCHECK(base::STLSetIntersection<std::set<int64> >(
543 pushed_resources, deleted_resources).empty());
546 return WriteBatch(&batch);
549 ServiceWorkerDatabase::Status ServiceWorkerDatabase::UpdateVersionToActive(
550 int64 registration_id,
551 const GURL& origin) {
552 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
553 ServiceWorkerDatabase::Status status = LazyOpen(false);
554 if (IsNewOrNonexistentDatabase(status))
555 return STATUS_ERROR_NOT_FOUND;
556 if (status != STATUS_OK)
558 if (!origin.is_valid())
559 return STATUS_ERROR_FAILED;
561 RegistrationData registration;
562 status = ReadRegistrationData(registration_id, origin, ®istration);
563 if (status != STATUS_OK)
566 registration.is_active = true;
568 leveldb::WriteBatch batch;
569 PutRegistrationDataToBatch(registration, &batch);
570 return WriteBatch(&batch);
573 ServiceWorkerDatabase::Status ServiceWorkerDatabase::UpdateLastCheckTime(
574 int64 registration_id,
576 const base::Time& time) {
577 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
578 ServiceWorkerDatabase::Status status = LazyOpen(false);
579 if (IsNewOrNonexistentDatabase(status))
580 return STATUS_ERROR_NOT_FOUND;
581 if (status != STATUS_OK)
583 if (!origin.is_valid())
584 return STATUS_ERROR_FAILED;
586 RegistrationData registration;
587 status = ReadRegistrationData(registration_id, origin, ®istration);
588 if (status != STATUS_OK)
591 registration.last_update_check = time;
593 leveldb::WriteBatch batch;
594 PutRegistrationDataToBatch(registration, &batch);
595 return WriteBatch(&batch);
598 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteRegistration(
599 int64 registration_id,
601 std::vector<int64>* newly_purgeable_resources) {
602 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
603 Status status = LazyOpen(false);
604 if (IsNewOrNonexistentDatabase(status))
606 if (status != STATUS_OK)
608 if (!origin.is_valid())
609 return STATUS_ERROR_FAILED;
611 leveldb::WriteBatch batch;
613 // Remove |origin| from unique origins if a registration specified by
614 // |registration_id| is the only one for |origin|.
615 // TODO(nhiroki): Check the uniqueness by more efficient way.
616 std::vector<RegistrationData> registrations;
617 status = GetRegistrationsForOrigin(origin, ®istrations);
618 if (status != STATUS_OK)
621 if (registrations.size() == 1 &&
622 registrations[0].registration_id == registration_id) {
623 batch.Delete(CreateUniqueOriginKey(origin));
626 // Delete a registration specified by |registration_id|.
627 batch.Delete(CreateRegistrationKey(registration_id, origin));
629 // Delete resource records associated with the registration.
630 for (std::vector<RegistrationData>::const_iterator itr =
631 registrations.begin(); itr != registrations.end(); ++itr) {
632 if (itr->registration_id == registration_id) {
633 status = DeleteResourceRecords(
634 itr->version_id, newly_purgeable_resources, &batch);
635 if (status != STATUS_OK)
641 return WriteBatch(&batch);
644 ServiceWorkerDatabase::Status
645 ServiceWorkerDatabase::GetUncommittedResourceIds(std::set<int64>* ids) {
646 return ReadResourceIds(kUncommittedResIdKeyPrefix, ids);
649 ServiceWorkerDatabase::Status
650 ServiceWorkerDatabase::WriteUncommittedResourceIds(const std::set<int64>& ids) {
651 return WriteResourceIds(kUncommittedResIdKeyPrefix, ids);
654 ServiceWorkerDatabase::Status
655 ServiceWorkerDatabase::ClearUncommittedResourceIds(const std::set<int64>& ids) {
656 return DeleteResourceIds(kUncommittedResIdKeyPrefix, ids);
659 ServiceWorkerDatabase::Status
660 ServiceWorkerDatabase::GetPurgeableResourceIds(std::set<int64>* ids) {
661 return ReadResourceIds(kPurgeableResIdKeyPrefix, ids);
664 ServiceWorkerDatabase::Status
665 ServiceWorkerDatabase::WritePurgeableResourceIds(const std::set<int64>& ids) {
666 return WriteResourceIds(kPurgeableResIdKeyPrefix, ids);
669 ServiceWorkerDatabase::Status
670 ServiceWorkerDatabase::ClearPurgeableResourceIds(const std::set<int64>& ids) {
671 return DeleteResourceIds(kPurgeableResIdKeyPrefix, ids);
674 ServiceWorkerDatabase::Status
675 ServiceWorkerDatabase::PurgeUncommittedResourceIds(
676 const std::set<int64>& ids) {
677 leveldb::WriteBatch batch;
678 Status status = DeleteResourceIdsInBatch(
679 kUncommittedResIdKeyPrefix, ids, &batch);
680 if (status != STATUS_OK)
682 status = WriteResourceIdsInBatch(kPurgeableResIdKeyPrefix, ids, &batch);
683 if (status != STATUS_OK)
685 return WriteBatch(&batch);
688 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteAllDataForOrigin(
690 std::vector<int64>* newly_purgeable_resources) {
691 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
692 Status status = LazyOpen(false);
693 if (IsNewOrNonexistentDatabase(status))
695 if (status != STATUS_OK)
697 if (!origin.is_valid())
698 return STATUS_ERROR_FAILED;
700 leveldb::WriteBatch batch;
702 // Delete from the unique origin list.
703 batch.Delete(CreateUniqueOriginKey(origin));
705 std::vector<RegistrationData> registrations;
706 status = GetRegistrationsForOrigin(origin, ®istrations);
707 if (status != STATUS_OK)
710 // Delete registrations and resource records.
711 for (std::vector<RegistrationData>::const_iterator itr =
712 registrations.begin(); itr != registrations.end(); ++itr) {
713 batch.Delete(CreateRegistrationKey(itr->registration_id, origin));
714 status = DeleteResourceRecords(
715 itr->version_id, newly_purgeable_resources, &batch);
716 if (status != STATUS_OK)
720 return WriteBatch(&batch);
723 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DestroyDatabase() {
724 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
725 Disable(FROM_HERE, STATUS_OK);
726 return LevelDBStatusToStatus(
727 leveldb::DestroyDB(path_.AsUTF8Unsafe(), leveldb::Options()));
730 ServiceWorkerDatabase::Status ServiceWorkerDatabase::LazyOpen(
731 bool create_if_missing) {
732 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
734 // Do not try to open a database if we tried and failed once.
735 if (state_ == DISABLED)
736 return STATUS_ERROR_FAILED;
740 // When |path_| is empty, open a database in-memory.
741 bool use_in_memory_db = path_.empty();
743 if (!create_if_missing) {
744 // Avoid opening a database if it does not exist at the |path_|.
745 if (use_in_memory_db ||
746 !base::PathExists(path_) ||
747 base::IsDirectoryEmpty(path_)) {
748 return STATUS_ERROR_NOT_FOUND;
752 leveldb::Options options;
753 options.create_if_missing = create_if_missing;
754 if (use_in_memory_db) {
755 env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
756 options.env = env_.get();
759 leveldb::DB* db = NULL;
760 Status status = LevelDBStatusToStatus(
761 leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db));
762 HandleOpenResult(FROM_HERE, status);
763 if (status != STATUS_OK) {
765 // TODO(nhiroki): Should we retry to open the database?
771 status = ReadDatabaseVersion(&db_version);
772 if (status != STATUS_OK)
774 DCHECK_LE(0, db_version);
776 state_ = INITIALIZED;
780 bool ServiceWorkerDatabase::IsNewOrNonexistentDatabase(
781 ServiceWorkerDatabase::Status status) {
782 if (status == STATUS_ERROR_NOT_FOUND)
784 if (status == STATUS_OK && state_ == UNINITIALIZED)
789 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadNextAvailableId(
791 int64* next_avail_id) {
793 DCHECK(next_avail_id);
796 Status status = LevelDBStatusToStatus(
797 db_->Get(leveldb::ReadOptions(), id_key, &value));
798 if (status == STATUS_ERROR_NOT_FOUND) {
799 // Nobody has gotten the next resource id for |id_key|.
801 HandleReadResult(FROM_HERE, STATUS_OK);
803 } else if (status != STATUS_OK) {
804 HandleReadResult(FROM_HERE, status);
808 status = ParseId(value, next_avail_id);
809 HandleReadResult(FROM_HERE, status);
813 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadRegistrationData(
814 int64 registration_id,
816 RegistrationData* registration) {
817 DCHECK(registration);
819 const std::string key = CreateRegistrationKey(registration_id, origin);
821 Status status = LevelDBStatusToStatus(
822 db_->Get(leveldb::ReadOptions(), key, &value));
823 if (status != STATUS_OK) {
826 status == STATUS_ERROR_NOT_FOUND ? STATUS_OK : status);
830 status = ParseRegistrationData(value, registration);
831 HandleReadResult(FROM_HERE, status);
835 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadResourceRecords(
837 std::vector<ResourceRecord>* resources) {
838 DCHECK(resources->empty());
840 Status status = STATUS_OK;
841 const std::string prefix = CreateResourceRecordKeyPrefix(version_id);
843 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
844 for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
845 Status status = LevelDBStatusToStatus(itr->status());
846 if (status != STATUS_OK) {
847 HandleReadResult(FROM_HERE, status);
852 if (!RemovePrefix(itr->key().ToString(), prefix, NULL))
855 ResourceRecord resource;
856 status = ParseResourceRecord(itr->value().ToString(), &resource);
857 if (status != STATUS_OK) {
858 HandleReadResult(FROM_HERE, status);
862 resources->push_back(resource);
865 HandleReadResult(FROM_HERE, status);
869 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceRecords(
871 std::vector<int64>* newly_purgeable_resources,
872 leveldb::WriteBatch* batch) {
875 Status status = STATUS_OK;
876 const std::string prefix = CreateResourceRecordKeyPrefix(version_id);
878 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
879 for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
880 status = LevelDBStatusToStatus(itr->status());
881 if (status != STATUS_OK) {
882 HandleReadResult(FROM_HERE, status);
886 const std::string key = itr->key().ToString();
887 std::string unprefixed;
888 if (!RemovePrefix(key, prefix, &unprefixed))
892 status = ParseId(unprefixed, &resource_id);
893 if (status != STATUS_OK) {
894 HandleReadResult(FROM_HERE, status);
898 // Remove a resource record.
901 // Currently resource sharing across versions and registrations is not
902 // supported, so we can purge this without caring about it.
903 PutPurgeableResourceIdToBatch(resource_id, batch);
904 newly_purgeable_resources->push_back(resource_id);
907 HandleReadResult(FROM_HERE, status);
911 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadResourceIds(
912 const char* id_key_prefix,
913 std::set<int64>* ids) {
914 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
915 DCHECK(id_key_prefix);
916 DCHECK(ids->empty());
918 Status status = LazyOpen(false);
919 if (IsNewOrNonexistentDatabase(status))
921 if (status != STATUS_OK)
924 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
925 for (itr->Seek(id_key_prefix); itr->Valid(); itr->Next()) {
926 status = LevelDBStatusToStatus(itr->status());
927 if (status != STATUS_OK) {
928 HandleReadResult(FROM_HERE, status);
933 std::string unprefixed;
934 if (!RemovePrefix(itr->key().ToString(), id_key_prefix, &unprefixed))
938 status = ParseId(unprefixed, &resource_id);
939 if (status != STATUS_OK) {
940 HandleReadResult(FROM_HERE, status);
944 ids->insert(resource_id);
947 HandleReadResult(FROM_HERE, status);
951 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteResourceIds(
952 const char* id_key_prefix,
953 const std::set<int64>& ids) {
954 leveldb::WriteBatch batch;
955 Status status = WriteResourceIdsInBatch(id_key_prefix, ids, &batch);
956 if (status != STATUS_OK)
958 return WriteBatch(&batch);
961 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteResourceIdsInBatch(
962 const char* id_key_prefix,
963 const std::set<int64>& ids,
964 leveldb::WriteBatch* batch) {
965 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
966 DCHECK(id_key_prefix);
968 Status status = LazyOpen(true);
969 if (status != STATUS_OK)
972 for (std::set<int64>::const_iterator itr = ids.begin();
973 itr != ids.end(); ++itr) {
974 // Value should be empty.
975 batch->Put(CreateResourceIdKey(id_key_prefix, *itr), "");
980 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceIds(
981 const char* id_key_prefix,
982 const std::set<int64>& ids) {
983 leveldb::WriteBatch batch;
984 Status status = DeleteResourceIdsInBatch(id_key_prefix, ids, &batch);
985 if (status != STATUS_OK)
987 return WriteBatch(&batch);
990 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceIdsInBatch(
991 const char* id_key_prefix,
992 const std::set<int64>& ids,
993 leveldb::WriteBatch* batch) {
994 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
995 DCHECK(id_key_prefix);
997 Status status = LazyOpen(false);
998 if (IsNewOrNonexistentDatabase(status))
1000 if (status != STATUS_OK)
1003 for (std::set<int64>::const_iterator itr = ids.begin();
1004 itr != ids.end(); ++itr) {
1005 batch->Delete(CreateResourceIdKey(id_key_prefix, *itr));
1010 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadDatabaseVersion(
1011 int64* db_version) {
1013 Status status = LevelDBStatusToStatus(
1014 db_->Get(leveldb::ReadOptions(), kDatabaseVersionKey, &value));
1015 if (status == STATUS_ERROR_NOT_FOUND) {
1016 // The database hasn't been initialized yet.
1018 HandleReadResult(FROM_HERE, STATUS_OK);
1022 if (status != STATUS_OK) {
1023 HandleReadResult(FROM_HERE, status);
1027 status = ParseDatabaseVersion(value, db_version);
1028 HandleReadResult(FROM_HERE, status);
1032 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteBatch(
1033 leveldb::WriteBatch* batch) {
1035 DCHECK_NE(DISABLED, state_);
1037 if (state_ == UNINITIALIZED) {
1038 // Write the database schema version.
1039 batch->Put(kDatabaseVersionKey, base::Int64ToString(kCurrentSchemaVersion));
1040 state_ = INITIALIZED;
1043 Status status = LevelDBStatusToStatus(
1044 db_->Write(leveldb::WriteOptions(), batch));
1045 HandleWriteResult(FROM_HERE, status);
1049 void ServiceWorkerDatabase::BumpNextRegistrationIdIfNeeded(
1050 int64 used_id, leveldb::WriteBatch* batch) {
1052 if (next_avail_registration_id_ <= used_id) {
1053 next_avail_registration_id_ = used_id + 1;
1054 batch->Put(kNextRegIdKey, base::Int64ToString(next_avail_registration_id_));
1058 void ServiceWorkerDatabase::BumpNextVersionIdIfNeeded(
1059 int64 used_id, leveldb::WriteBatch* batch) {
1061 if (next_avail_version_id_ <= used_id) {
1062 next_avail_version_id_ = used_id + 1;
1063 batch->Put(kNextVerIdKey, base::Int64ToString(next_avail_version_id_));
1067 bool ServiceWorkerDatabase::IsOpen() {
1071 void ServiceWorkerDatabase::Disable(
1072 const tracked_objects::Location& from_here,
1074 if (status != STATUS_OK) {
1075 DLOG(ERROR) << "Failed at: " << from_here.ToString()
1076 << " with error: " << StatusToString(status);
1077 DLOG(ERROR) << "ServiceWorkerDatabase is disabled.";
1083 void ServiceWorkerDatabase::HandleOpenResult(
1084 const tracked_objects::Location& from_here,
1086 if (status != ServiceWorkerDatabase::STATUS_OK)
1087 Disable(from_here, status);
1088 UMA_HISTOGRAM_ENUMERATION(kOpenResultHistogramLabel,
1090 ServiceWorkerDatabase::STATUS_ERROR_MAX);
1093 void ServiceWorkerDatabase::HandleReadResult(
1094 const tracked_objects::Location& from_here,
1096 if (status != ServiceWorkerDatabase::STATUS_OK)
1097 Disable(from_here, status);
1098 UMA_HISTOGRAM_ENUMERATION(kReadResultHistogramLabel,
1100 ServiceWorkerDatabase::STATUS_ERROR_MAX);
1103 void ServiceWorkerDatabase::HandleWriteResult(
1104 const tracked_objects::Location& from_here,
1106 if (status != ServiceWorkerDatabase::STATUS_OK)
1107 Disable(from_here, status);
1108 UMA_HISTOGRAM_ENUMERATION(kWriteResultHistogramLabel,
1110 ServiceWorkerDatabase::STATUS_ERROR_MAX);
1113 } // namespace content