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"
7 #include "base/files/file_util.h"
8 #include "base/lazy_instance.h"
9 #include "base/location.h"
10 #include "base/logging.h"
11 #include "base/metrics/histogram.h"
12 #include "base/stl_util.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_split.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/stringprintf.h"
17 #include "content/browser/service_worker/service_worker_database.pb.h"
18 #include "content/browser/service_worker/service_worker_metrics.h"
19 #include "content/common/service_worker/service_worker_types.h"
20 #include "third_party/leveldatabase/env_chromium.h"
21 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
22 #include "third_party/leveldatabase/src/include/leveldb/db.h"
23 #include "third_party/leveldatabase/src/include/leveldb/env.h"
24 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
26 // LevelDB database schema
27 // =======================
30 // - int64 value is serialized as a string by base::Int64ToString().
31 // - GURL value is serialized as a string by GURL::spec().
33 // Version 1 (in sorted order)
34 // key: "INITDATA_DB_VERSION"
37 // key: "INITDATA_NEXT_REGISTRATION_ID"
38 // value: <int64 'next_available_registration_id'>
40 // key: "INITDATA_NEXT_RESOURCE_ID"
41 // value: <int64 'next_available_resource_id'>
43 // key: "INITDATA_NEXT_VERSION_ID"
44 // value: <int64 'next_available_version_id'>
46 // key: "INITDATA_UNIQUE_ORIGIN:" + <GURL 'origin'>
49 // key: "PRES:" + <int64 'purgeable_resource_id'>
52 // key: "REG:" + <GURL 'origin'> + '\x00' + <int64 'registration_id'>
53 // (ex. "REG:http://example.com\x00123456")
54 // value: <ServiceWorkerRegistrationData serialized as a string>
56 // key: "REG_HAS_USER_DATA:" + <std::string 'user_data_name'> + '\x00'
57 // + <int64 'registration_id'>
60 // key: "REG_USER_DATA:" + <int64 'registration_id'> + '\x00'
61 // + <std::string user_data_name>
62 // (ex. "REG_USER_DATA:123456\x00foo_bar")
63 // value: <std::string user_data>
65 // key: "RES:" + <int64 'version_id'> + '\x00' + <int64 'resource_id'>
66 // (ex. "RES:123456\x00654321")
67 // value: <ServiceWorkerResourceRecord serialized as a string>
69 // key: "URES:" + <int64 'uncommitted_resource_id'>
74 // key: "REGID_TO_ORIGIN:" + <int64 'registration_id'>
75 // value: <GURL 'origin'>
77 // key: "INITDATA_DISKCACHE_MIGRATION_NOT_NEEDED"
79 // - This entry represents that the diskcache uses the Simple backend and
80 // does not have to do diskcache migration (http://crbug.com/487482).
82 // key: "INITDATA_OLD_DISKCACHE_DELETION_NOT_NEEDED"
84 // - This entry represents that the old BlockFile diskcache was deleted
85 // after diskcache migration (http://crbug.com/487482).
90 const char kDatabaseVersionKey
[] = "INITDATA_DB_VERSION";
91 const char kNextRegIdKey
[] = "INITDATA_NEXT_REGISTRATION_ID";
92 const char kNextResIdKey
[] = "INITDATA_NEXT_RESOURCE_ID";
93 const char kNextVerIdKey
[] = "INITDATA_NEXT_VERSION_ID";
94 const char kUniqueOriginKey
[] = "INITDATA_UNIQUE_ORIGIN:";
95 const char kDiskCacheMigrationNotNeededKey
[] =
96 "INITDATA_DISKCACHE_MIGRATION_NOT_NEEDED";
97 const char kOldDiskCacheDeletionNotNeededKey
[] =
98 "INITDATA_OLD_DISKCACHE_DELETION_NOT_NEEDED";
100 const char kRegKeyPrefix
[] = "REG:";
101 const char kRegUserDataKeyPrefix
[] = "REG_USER_DATA:";
102 const char kRegHasUserDataKeyPrefix
[] = "REG_HAS_USER_DATA:";
103 const char kRegIdToOriginKeyPrefix
[] = "REGID_TO_ORIGIN:";
104 const char kResKeyPrefix
[] = "RES:";
105 const char kKeySeparator
= '\x00';
106 const char kEmptyValue
[] = "";
108 const char kUncommittedResIdKeyPrefix
[] = "URES:";
109 const char kPurgeableResIdKeyPrefix
[] = "PRES:";
111 const int64 kCurrentSchemaVersion
= 2;
113 class ServiceWorkerEnv
: public leveldb_env::ChromiumEnv
{
116 : ChromiumEnv("LevelDBEnv.ServiceWorker", false /* make_backup */) {}
119 base::LazyInstance
<ServiceWorkerEnv
>::Leaky g_service_worker_env
=
120 LAZY_INSTANCE_INITIALIZER
;
122 bool RemovePrefix(const std::string
& str
,
123 const std::string
& prefix
,
125 if (!base::StartsWith(str
, prefix
, base::CompareCase::SENSITIVE
))
128 *out
= str
.substr(prefix
.size());
132 std::string
CreateRegistrationKeyPrefix(const GURL
& origin
) {
133 return base::StringPrintf("%s%s%c", kRegKeyPrefix
,
134 origin
.GetOrigin().spec().c_str(), kKeySeparator
);
137 std::string
CreateRegistrationKey(int64 registration_id
,
138 const GURL
& origin
) {
139 return CreateRegistrationKeyPrefix(origin
)
140 .append(base::Int64ToString(registration_id
));
143 std::string
CreateResourceRecordKeyPrefix(int64 version_id
) {
144 return base::StringPrintf("%s%s%c",
146 base::Int64ToString(version_id
).c_str(),
150 std::string
CreateResourceRecordKey(int64 version_id
,
152 return CreateResourceRecordKeyPrefix(version_id
).append(
153 base::Int64ToString(resource_id
));
156 std::string
CreateUniqueOriginKey(const GURL
& origin
) {
157 return base::StringPrintf("%s%s", kUniqueOriginKey
,
158 origin
.GetOrigin().spec().c_str());
161 std::string
CreateResourceIdKey(const char* key_prefix
, int64 resource_id
) {
162 return base::StringPrintf(
163 "%s%s", key_prefix
, base::Int64ToString(resource_id
).c_str());
166 std::string
CreateUserDataKeyPrefix(int64 registration_id
) {
167 return base::StringPrintf("%s%s%c",
168 kRegUserDataKeyPrefix
,
169 base::Int64ToString(registration_id
).c_str(),
173 std::string
CreateUserDataKey(int64 registration_id
,
174 const std::string
& user_data_name
) {
175 return CreateUserDataKeyPrefix(registration_id
).append(user_data_name
);
178 std::string
CreateHasUserDataKeyPrefix(const std::string
& user_data_name
) {
179 return base::StringPrintf("%s%s%c", kRegHasUserDataKeyPrefix
,
180 user_data_name
.c_str(), kKeySeparator
);
183 std::string
CreateHasUserDataKey(int64 registration_id
,
184 const std::string
& user_data_name
) {
185 return CreateHasUserDataKeyPrefix(user_data_name
)
186 .append(base::Int64ToString(registration_id
));
189 std::string
CreateRegistrationIdToOriginKey(int64 registration_id
) {
190 return base::StringPrintf("%s%s", kRegIdToOriginKeyPrefix
,
191 base::Int64ToString(registration_id
).c_str());
194 void PutRegistrationDataToBatch(
195 const ServiceWorkerDatabase::RegistrationData
& input
,
196 leveldb::WriteBatch
* batch
) {
199 // Convert RegistrationData to ServiceWorkerRegistrationData.
200 ServiceWorkerRegistrationData data
;
201 data
.set_registration_id(input
.registration_id
);
202 data
.set_scope_url(input
.scope
.spec());
203 data
.set_script_url(input
.script
.spec());
204 data
.set_version_id(input
.version_id
);
205 data
.set_is_active(input
.is_active
);
206 data
.set_has_fetch_handler(input
.has_fetch_handler
);
207 data
.set_last_update_check_time(input
.last_update_check
.ToInternalValue());
208 data
.set_resources_total_size_bytes(input
.resources_total_size_bytes
);
211 bool success
= data
.SerializeToString(&value
);
213 GURL origin
= input
.scope
.GetOrigin();
214 batch
->Put(CreateRegistrationKey(data
.registration_id(), origin
), value
);
217 void PutResourceRecordToBatch(
218 const ServiceWorkerDatabase::ResourceRecord
& input
,
220 leveldb::WriteBatch
* batch
) {
222 DCHECK_GE(input
.size_bytes
, 0);
224 // Convert ResourceRecord to ServiceWorkerResourceRecord.
225 ServiceWorkerResourceRecord record
;
226 record
.set_resource_id(input
.resource_id
);
227 record
.set_url(input
.url
.spec());
228 record
.set_size_bytes(input
.size_bytes
);
231 bool success
= record
.SerializeToString(&value
);
233 batch
->Put(CreateResourceRecordKey(version_id
, input
.resource_id
), value
);
236 void PutUniqueOriginToBatch(const GURL
& origin
,
237 leveldb::WriteBatch
* batch
) {
238 // Value should be empty.
239 batch
->Put(CreateUniqueOriginKey(origin
), "");
242 void PutPurgeableResourceIdToBatch(int64 resource_id
,
243 leveldb::WriteBatch
* batch
) {
244 // Value should be empty.
245 batch
->Put(CreateResourceIdKey(kPurgeableResIdKeyPrefix
, resource_id
), "");
248 ServiceWorkerDatabase::Status
ParseId(
249 const std::string
& serialized
,
253 if (!base::StringToInt64(serialized
, &id
) || id
< 0)
254 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED
;
256 return ServiceWorkerDatabase::STATUS_OK
;
259 ServiceWorkerDatabase::Status
ParseDatabaseVersion(
260 const std::string
& serialized
,
263 const int kFirstValidVersion
= 1;
265 if (!base::StringToInt64(serialized
, &version
) ||
266 version
< kFirstValidVersion
) {
267 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED
;
269 if (kCurrentSchemaVersion
< version
) {
270 DLOG(ERROR
) << "ServiceWorkerDatabase has newer schema version"
271 << " than the current latest version: "
272 << version
<< " vs " << kCurrentSchemaVersion
;
273 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED
;
276 return ServiceWorkerDatabase::STATUS_OK
;
279 ServiceWorkerDatabase::Status
ParseRegistrationData(
280 const std::string
& serialized
,
281 ServiceWorkerDatabase::RegistrationData
* out
) {
283 ServiceWorkerRegistrationData data
;
284 if (!data
.ParseFromString(serialized
))
285 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED
;
287 GURL
scope_url(data
.scope_url());
288 GURL
script_url(data
.script_url());
289 if (!scope_url
.is_valid() ||
290 !script_url
.is_valid() ||
291 scope_url
.GetOrigin() != script_url
.GetOrigin()) {
292 DLOG(ERROR
) << "Scope URL '" << data
.scope_url() << "' and/or script url '"
294 << "' are invalid or have mismatching origins.";
295 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED
;
298 // Convert ServiceWorkerRegistrationData to RegistrationData.
299 out
->registration_id
= data
.registration_id();
300 out
->scope
= scope_url
;
301 out
->script
= script_url
;
302 out
->version_id
= data
.version_id();
303 out
->is_active
= data
.is_active();
304 out
->has_fetch_handler
= data
.has_fetch_handler();
305 out
->last_update_check
=
306 base::Time::FromInternalValue(data
.last_update_check_time());
307 out
->resources_total_size_bytes
= data
.resources_total_size_bytes();
309 return ServiceWorkerDatabase::STATUS_OK
;
312 ServiceWorkerDatabase::Status
ParseResourceRecord(
313 const std::string
& serialized
,
314 ServiceWorkerDatabase::ResourceRecord
* out
) {
316 ServiceWorkerResourceRecord record
;
317 if (!record
.ParseFromString(serialized
))
318 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED
;
320 GURL
url(record
.url());
322 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED
;
324 // Convert ServiceWorkerResourceRecord to ResourceRecord.
325 out
->resource_id
= record
.resource_id();
327 out
->size_bytes
= record
.size_bytes();
328 return ServiceWorkerDatabase::STATUS_OK
;
331 ServiceWorkerDatabase::Status
LevelDBStatusToStatus(
332 const leveldb::Status
& status
) {
334 return ServiceWorkerDatabase::STATUS_OK
;
335 else if (status
.IsNotFound())
336 return ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND
;
337 else if (status
.IsIOError())
338 return ServiceWorkerDatabase::STATUS_ERROR_IO_ERROR
;
339 else if (status
.IsCorruption())
340 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED
;
342 return ServiceWorkerDatabase::STATUS_ERROR_FAILED
;
345 int64_t AccumulateResourceSizeInBytes(
346 const std::vector
<ServiceWorkerDatabase::ResourceRecord
>& resources
) {
347 int64_t total_size_bytes
= 0;
348 for (const auto& resource
: resources
)
349 total_size_bytes
+= resource
.size_bytes
;
350 return total_size_bytes
;
355 const char* ServiceWorkerDatabase::StatusToString(
356 ServiceWorkerDatabase::Status status
) {
358 case ServiceWorkerDatabase::STATUS_OK
:
359 return "Database OK";
360 case ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND
:
361 return "Database not found";
362 case ServiceWorkerDatabase::STATUS_ERROR_IO_ERROR
:
363 return "Database IO error";
364 case ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED
:
365 return "Database corrupted";
366 case ServiceWorkerDatabase::STATUS_ERROR_FAILED
:
367 return "Database operation failed";
368 case ServiceWorkerDatabase::STATUS_ERROR_MAX
:
370 return "Database unknown error";
373 return "Database unknown error";
376 ServiceWorkerDatabase::RegistrationData::RegistrationData()
377 : registration_id(kInvalidServiceWorkerRegistrationId
),
378 version_id(kInvalidServiceWorkerVersionId
),
380 has_fetch_handler(false),
381 resources_total_size_bytes(0) {
384 ServiceWorkerDatabase::RegistrationData::~RegistrationData() {
387 ServiceWorkerDatabase::ServiceWorkerDatabase(const base::FilePath
& path
)
389 next_avail_registration_id_(0),
390 next_avail_resource_id_(0),
391 next_avail_version_id_(0),
392 state_(UNINITIALIZED
),
393 skip_writing_diskcache_migration_state_on_init_for_testing_(false) {
394 sequence_checker_
.DetachFromSequence();
397 ServiceWorkerDatabase::~ServiceWorkerDatabase() {
398 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
402 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::GetNextAvailableIds(
403 int64
* next_avail_registration_id
,
404 int64
* next_avail_version_id
,
405 int64
* next_avail_resource_id
) {
406 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
407 DCHECK(next_avail_registration_id
);
408 DCHECK(next_avail_version_id
);
409 DCHECK(next_avail_resource_id
);
411 Status status
= LazyOpen(false);
412 if (IsNewOrNonexistentDatabase(status
)) {
413 *next_avail_registration_id
= 0;
414 *next_avail_version_id
= 0;
415 *next_avail_resource_id
= 0;
418 if (status
!= STATUS_OK
)
421 status
= ReadNextAvailableId(kNextRegIdKey
, &next_avail_registration_id_
);
422 if (status
!= STATUS_OK
)
424 status
= ReadNextAvailableId(kNextVerIdKey
, &next_avail_version_id_
);
425 if (status
!= STATUS_OK
)
427 status
= ReadNextAvailableId(kNextResIdKey
, &next_avail_resource_id_
);
428 if (status
!= STATUS_OK
)
431 *next_avail_registration_id
= next_avail_registration_id_
;
432 *next_avail_version_id
= next_avail_version_id_
;
433 *next_avail_resource_id
= next_avail_resource_id_
;
437 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::IsDiskCacheMigrationNeeded(
438 bool* migration_needed
) {
439 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
441 Status status
= LazyOpen(false);
442 if (IsNewOrNonexistentDatabase(status
)) {
443 *migration_needed
= false;
446 if (status
!= STATUS_OK
)
450 status
= LevelDBStatusToStatus(db_
->Get(
451 leveldb::ReadOptions(), kDiskCacheMigrationNotNeededKey
, &value
));
452 if (status
== STATUS_ERROR_NOT_FOUND
) {
453 *migration_needed
= true;
454 HandleReadResult(FROM_HERE
, STATUS_OK
);
457 if (status
!= STATUS_OK
) {
458 HandleReadResult(FROM_HERE
, status
);
462 *migration_needed
= false;
463 HandleReadResult(FROM_HERE
, status
);
467 ServiceWorkerDatabase::Status
468 ServiceWorkerDatabase::SetDiskCacheMigrationNotNeeded() {
469 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
471 Status status
= LazyOpen(true);
472 if (status
!= STATUS_OK
)
475 leveldb::WriteBatch batch
;
476 batch
.Put(kDiskCacheMigrationNotNeededKey
, kEmptyValue
);
477 return WriteBatch(&batch
);
480 ServiceWorkerDatabase::Status
481 ServiceWorkerDatabase::IsOldDiskCacheDeletionNeeded(bool* deletion_needed
) {
482 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
484 Status status
= LazyOpen(false);
485 if (IsNewOrNonexistentDatabase(status
)) {
486 *deletion_needed
= false;
489 if (status
!= STATUS_OK
)
493 status
= LevelDBStatusToStatus(db_
->Get(
494 leveldb::ReadOptions(), kOldDiskCacheDeletionNotNeededKey
, &value
));
495 if (status
== STATUS_ERROR_NOT_FOUND
) {
496 *deletion_needed
= true;
497 HandleReadResult(FROM_HERE
, STATUS_OK
);
500 if (status
!= STATUS_OK
) {
501 HandleReadResult(FROM_HERE
, status
);
505 *deletion_needed
= false;
506 HandleReadResult(FROM_HERE
, status
);
510 ServiceWorkerDatabase::Status
511 ServiceWorkerDatabase::SetOldDiskCacheDeletionNotNeeded() {
512 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
514 Status status
= LazyOpen(true);
515 if (status
!= STATUS_OK
)
518 leveldb::WriteBatch batch
;
519 batch
.Put(kOldDiskCacheDeletionNotNeededKey
, kEmptyValue
);
520 return WriteBatch(&batch
);
523 ServiceWorkerDatabase::Status
524 ServiceWorkerDatabase::GetOriginsWithRegistrations(std::set
<GURL
>* origins
) {
525 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
526 DCHECK(origins
->empty());
528 Status status
= LazyOpen(false);
529 if (IsNewOrNonexistentDatabase(status
))
531 if (status
!= STATUS_OK
)
534 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
535 for (itr
->Seek(kUniqueOriginKey
); itr
->Valid(); itr
->Next()) {
536 status
= LevelDBStatusToStatus(itr
->status());
537 if (status
!= STATUS_OK
) {
538 HandleReadResult(FROM_HERE
, status
);
543 std::string origin_str
;
544 if (!RemovePrefix(itr
->key().ToString(), kUniqueOriginKey
, &origin_str
))
547 GURL
origin(origin_str
);
548 if (!origin
.is_valid()) {
549 status
= STATUS_ERROR_CORRUPTED
;
550 HandleReadResult(FROM_HERE
, status
);
555 origins
->insert(origin
);
558 HandleReadResult(FROM_HERE
, status
);
562 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::GetRegistrationsForOrigin(
564 std::vector
<RegistrationData
>* registrations
,
565 std::vector
<std::vector
<ResourceRecord
>>* opt_resources_list
) {
566 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
567 DCHECK(registrations
->empty());
569 Status status
= LazyOpen(false);
570 if (IsNewOrNonexistentDatabase(status
))
572 if (status
!= STATUS_OK
)
575 std::string prefix
= CreateRegistrationKeyPrefix(origin
);
576 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
577 for (itr
->Seek(prefix
); itr
->Valid(); itr
->Next()) {
578 status
= LevelDBStatusToStatus(itr
->status());
579 if (status
!= STATUS_OK
) {
580 HandleReadResult(FROM_HERE
, status
);
581 registrations
->clear();
582 if (opt_resources_list
)
583 opt_resources_list
->clear();
587 if (!RemovePrefix(itr
->key().ToString(), prefix
, NULL
))
590 RegistrationData registration
;
591 status
= ParseRegistrationData(itr
->value().ToString(), ®istration
);
592 if (status
!= STATUS_OK
) {
593 HandleReadResult(FROM_HERE
, status
);
594 registrations
->clear();
595 if (opt_resources_list
)
596 opt_resources_list
->clear();
599 registrations
->push_back(registration
);
601 if (opt_resources_list
) {
602 std::vector
<ResourceRecord
> resources
;
603 status
= ReadResourceRecords(registration
.version_id
, &resources
);
604 if (status
!= STATUS_OK
) {
605 HandleReadResult(FROM_HERE
, status
);
606 registrations
->clear();
607 opt_resources_list
->clear();
610 opt_resources_list
->push_back(resources
);
614 HandleReadResult(FROM_HERE
, status
);
618 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::GetAllRegistrations(
619 std::vector
<RegistrationData
>* registrations
) {
620 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
621 DCHECK(registrations
->empty());
623 Status status
= LazyOpen(false);
624 if (IsNewOrNonexistentDatabase(status
))
626 if (status
!= STATUS_OK
)
629 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
630 for (itr
->Seek(kRegKeyPrefix
); itr
->Valid(); itr
->Next()) {
631 status
= LevelDBStatusToStatus(itr
->status());
632 if (status
!= STATUS_OK
) {
633 HandleReadResult(FROM_HERE
, status
);
634 registrations
->clear();
638 if (!RemovePrefix(itr
->key().ToString(), kRegKeyPrefix
, NULL
))
641 RegistrationData registration
;
642 status
= ParseRegistrationData(itr
->value().ToString(), ®istration
);
643 if (status
!= STATUS_OK
) {
644 HandleReadResult(FROM_HERE
, status
);
645 registrations
->clear();
648 registrations
->push_back(registration
);
651 HandleReadResult(FROM_HERE
, status
);
655 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::ReadRegistration(
656 int64 registration_id
,
658 RegistrationData
* registration
,
659 std::vector
<ResourceRecord
>* resources
) {
660 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
661 DCHECK(registration
);
664 Status status
= LazyOpen(false);
665 if (IsNewOrNonexistentDatabase(status
))
666 return STATUS_ERROR_NOT_FOUND
;
667 if (status
!= STATUS_OK
)
670 RegistrationData value
;
671 status
= ReadRegistrationData(registration_id
, origin
, &value
);
672 if (status
!= STATUS_OK
)
675 status
= ReadResourceRecords(value
.version_id
, resources
);
676 if (status
!= STATUS_OK
)
679 // ResourceRecord must contain the ServiceWorker's main script.
680 if (resources
->empty())
681 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED
;
683 *registration
= value
;
687 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::ReadRegistrationOrigin(
688 int64 registration_id
,
690 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
693 Status status
= LazyOpen(true);
694 if (IsNewOrNonexistentDatabase(status
))
695 return STATUS_ERROR_NOT_FOUND
;
696 if (status
!= STATUS_OK
)
700 status
= LevelDBStatusToStatus(
701 db_
->Get(leveldb::ReadOptions(),
702 CreateRegistrationIdToOriginKey(registration_id
), &value
));
703 if (status
!= STATUS_OK
) {
704 HandleReadResult(FROM_HERE
,
705 status
== STATUS_ERROR_NOT_FOUND
? STATUS_OK
: status
);
710 if (!parsed
.is_valid()) {
711 status
= STATUS_ERROR_CORRUPTED
;
712 HandleReadResult(FROM_HERE
, status
);
717 HandleReadResult(FROM_HERE
, STATUS_OK
);
721 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::WriteRegistration(
722 const RegistrationData
& registration
,
723 const std::vector
<ResourceRecord
>& resources
,
724 RegistrationData
* old_registration
,
725 std::vector
<int64
>* newly_purgeable_resources
) {
726 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
727 DCHECK(old_registration
);
728 DCHECK(!resources
.empty());
729 Status status
= LazyOpen(true);
730 old_registration
->version_id
= kInvalidServiceWorkerVersionId
;
731 if (status
!= STATUS_OK
)
734 leveldb::WriteBatch batch
;
735 BumpNextRegistrationIdIfNeeded(registration
.registration_id
, &batch
);
736 BumpNextVersionIdIfNeeded(registration
.version_id
, &batch
);
738 PutUniqueOriginToBatch(registration
.scope
.GetOrigin(), &batch
);
740 DCHECK_EQ(AccumulateResourceSizeInBytes(resources
),
741 registration
.resources_total_size_bytes
)
742 << "The total size in the registration must match the cumulative "
743 << "sizes of the resources.";
745 PutRegistrationDataToBatch(registration
, &batch
);
746 batch
.Put(CreateRegistrationIdToOriginKey(registration
.registration_id
),
747 registration
.scope
.GetOrigin().spec());
749 // Used for avoiding multiple writes for the same resource id or url.
750 std::set
<int64
> pushed_resources
;
751 std::set
<GURL
> pushed_urls
;
752 for (std::vector
<ResourceRecord
>::const_iterator itr
= resources
.begin();
753 itr
!= resources
.end(); ++itr
) {
754 if (!itr
->url
.is_valid())
755 return STATUS_ERROR_FAILED
;
757 // Duplicated resource id or url should not exist.
758 DCHECK(pushed_resources
.insert(itr
->resource_id
).second
);
759 DCHECK(pushed_urls
.insert(itr
->url
).second
);
761 PutResourceRecordToBatch(*itr
, registration
.version_id
, &batch
);
763 // Delete a resource from the uncommitted list.
764 batch
.Delete(CreateResourceIdKey(
765 kUncommittedResIdKeyPrefix
, itr
->resource_id
));
766 // Delete from the purgeable list in case this version was once deleted.
768 CreateResourceIdKey(kPurgeableResIdKeyPrefix
, itr
->resource_id
));
771 // Retrieve a previous version to sweep purgeable resources.
772 status
= ReadRegistrationData(registration
.registration_id
,
773 registration
.scope
.GetOrigin(),
775 if (status
!= STATUS_OK
&& status
!= STATUS_ERROR_NOT_FOUND
)
777 if (status
== STATUS_OK
) {
778 DCHECK_LT(old_registration
->version_id
, registration
.version_id
);
779 status
= DeleteResourceRecords(
780 old_registration
->version_id
, newly_purgeable_resources
, &batch
);
781 if (status
!= STATUS_OK
)
784 // Currently resource sharing across versions and registrations is not
785 // supported, so resource ids should not be overlapped between
786 // |registration| and |old_registration|.
787 std::set
<int64
> deleted_resources(newly_purgeable_resources
->begin(),
788 newly_purgeable_resources
->end());
789 DCHECK(base::STLSetIntersection
<std::set
<int64
> >(
790 pushed_resources
, deleted_resources
).empty());
793 return WriteBatch(&batch
);
796 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::UpdateVersionToActive(
797 int64 registration_id
,
798 const GURL
& origin
) {
799 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
800 Status status
= LazyOpen(false);
801 if (IsNewOrNonexistentDatabase(status
))
802 return STATUS_ERROR_NOT_FOUND
;
803 if (status
!= STATUS_OK
)
805 if (!origin
.is_valid())
806 return STATUS_ERROR_FAILED
;
808 RegistrationData registration
;
809 status
= ReadRegistrationData(registration_id
, origin
, ®istration
);
810 if (status
!= STATUS_OK
)
813 registration
.is_active
= true;
815 leveldb::WriteBatch batch
;
816 PutRegistrationDataToBatch(registration
, &batch
);
817 return WriteBatch(&batch
);
820 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::UpdateLastCheckTime(
821 int64 registration_id
,
823 const base::Time
& time
) {
824 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
825 Status status
= LazyOpen(false);
826 if (IsNewOrNonexistentDatabase(status
))
827 return STATUS_ERROR_NOT_FOUND
;
828 if (status
!= STATUS_OK
)
830 if (!origin
.is_valid())
831 return STATUS_ERROR_FAILED
;
833 RegistrationData registration
;
834 status
= ReadRegistrationData(registration_id
, origin
, ®istration
);
835 if (status
!= STATUS_OK
)
838 registration
.last_update_check
= time
;
840 leveldb::WriteBatch batch
;
841 PutRegistrationDataToBatch(registration
, &batch
);
842 return WriteBatch(&batch
);
845 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::DeleteRegistration(
846 int64 registration_id
,
848 RegistrationData
* deleted_version
,
849 std::vector
<int64
>* newly_purgeable_resources
) {
850 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
851 DCHECK(deleted_version
);
852 deleted_version
->version_id
= kInvalidServiceWorkerVersionId
;
853 Status status
= LazyOpen(false);
854 if (IsNewOrNonexistentDatabase(status
))
856 if (status
!= STATUS_OK
)
858 if (!origin
.is_valid())
859 return STATUS_ERROR_FAILED
;
861 leveldb::WriteBatch batch
;
863 // Remove |origin| from unique origins if a registration specified by
864 // |registration_id| is the only one for |origin|.
865 // TODO(nhiroki): Check the uniqueness by more efficient way.
866 std::vector
<RegistrationData
> registrations
;
867 status
= GetRegistrationsForOrigin(origin
, ®istrations
, nullptr);
868 if (status
!= STATUS_OK
)
871 if (registrations
.size() == 1 &&
872 registrations
[0].registration_id
== registration_id
) {
873 batch
.Delete(CreateUniqueOriginKey(origin
));
876 // Delete a registration specified by |registration_id|.
877 batch
.Delete(CreateRegistrationKey(registration_id
, origin
));
878 batch
.Delete(CreateRegistrationIdToOriginKey(registration_id
));
880 // Delete resource records and user data associated with the registration.
881 for (const auto& registration
: registrations
) {
882 if (registration
.registration_id
== registration_id
) {
883 *deleted_version
= registration
;
884 status
= DeleteResourceRecords(
885 registration
.version_id
, newly_purgeable_resources
, &batch
);
886 if (status
!= STATUS_OK
)
889 status
= DeleteUserDataForRegistration(registration_id
, &batch
);
890 if (status
!= STATUS_OK
)
896 return WriteBatch(&batch
);
899 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::ReadUserData(
900 int64 registration_id
,
901 const std::string
& user_data_name
,
902 std::string
* user_data
) {
903 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
904 DCHECK_NE(kInvalidServiceWorkerRegistrationId
, registration_id
);
905 DCHECK(!user_data_name
.empty());
908 Status status
= LazyOpen(false);
909 if (IsNewOrNonexistentDatabase(status
))
910 return STATUS_ERROR_NOT_FOUND
;
911 if (status
!= STATUS_OK
)
914 const std::string key
= CreateUserDataKey(registration_id
, user_data_name
);
915 status
= LevelDBStatusToStatus(
916 db_
->Get(leveldb::ReadOptions(), key
, user_data
));
917 HandleReadResult(FROM_HERE
,
918 status
== STATUS_ERROR_NOT_FOUND
? STATUS_OK
: status
);
922 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::WriteUserData(
923 int64 registration_id
,
925 const std::string
& user_data_name
,
926 const std::string
& user_data
) {
927 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
928 DCHECK_NE(kInvalidServiceWorkerRegistrationId
, registration_id
);
929 DCHECK(!user_data_name
.empty());
931 Status status
= LazyOpen(false);
932 if (IsNewOrNonexistentDatabase(status
))
933 return STATUS_ERROR_NOT_FOUND
;
934 if (status
!= STATUS_OK
)
937 // There should be the registration specified by |registration_id|.
938 RegistrationData registration
;
939 status
= ReadRegistrationData(registration_id
, origin
, ®istration
);
940 if (status
!= STATUS_OK
)
943 leveldb::WriteBatch batch
;
944 batch
.Put(CreateUserDataKey(registration_id
, user_data_name
), user_data
);
945 batch
.Put(CreateHasUserDataKey(registration_id
, user_data_name
), "");
946 return WriteBatch(&batch
);
949 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::DeleteUserData(
950 int64 registration_id
,
951 const std::string
& user_data_name
) {
952 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
953 DCHECK_NE(kInvalidServiceWorkerRegistrationId
, registration_id
);
954 DCHECK(!user_data_name
.empty());
956 Status status
= LazyOpen(false);
957 if (IsNewOrNonexistentDatabase(status
))
959 if (status
!= STATUS_OK
)
962 leveldb::WriteBatch batch
;
963 batch
.Delete(CreateUserDataKey(registration_id
, user_data_name
));
964 batch
.Delete(CreateHasUserDataKey(registration_id
, user_data_name
));
965 return WriteBatch(&batch
);
968 ServiceWorkerDatabase::Status
969 ServiceWorkerDatabase::ReadUserDataForAllRegistrations(
970 const std::string
& user_data_name
,
971 std::vector
<std::pair
<int64
, std::string
>>* user_data
) {
972 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
973 DCHECK(user_data
->empty());
975 Status status
= LazyOpen(false);
976 if (IsNewOrNonexistentDatabase(status
))
978 if (status
!= STATUS_OK
)
981 std::string key_prefix
= CreateHasUserDataKeyPrefix(user_data_name
);
982 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
983 for (itr
->Seek(key_prefix
); itr
->Valid(); itr
->Next()) {
984 status
= LevelDBStatusToStatus(itr
->status());
985 if (status
!= STATUS_OK
) {
986 HandleReadResult(FROM_HERE
, status
);
991 std::string registration_id_string
;
992 if (!RemovePrefix(itr
->key().ToString(), key_prefix
,
993 ®istration_id_string
)) {
997 int64 registration_id
;
998 status
= ParseId(registration_id_string
, ®istration_id
);
999 if (status
!= STATUS_OK
) {
1000 HandleReadResult(FROM_HERE
, status
);
1006 status
= LevelDBStatusToStatus(
1007 db_
->Get(leveldb::ReadOptions(),
1008 CreateUserDataKey(registration_id
, user_data_name
), &value
));
1009 if (status
!= STATUS_OK
) {
1010 HandleReadResult(FROM_HERE
, status
);
1014 user_data
->push_back(std::make_pair(registration_id
, value
));
1017 HandleReadResult(FROM_HERE
, status
);
1021 ServiceWorkerDatabase::Status
1022 ServiceWorkerDatabase::GetUncommittedResourceIds(std::set
<int64
>* ids
) {
1023 return ReadResourceIds(kUncommittedResIdKeyPrefix
, ids
);
1026 ServiceWorkerDatabase::Status
1027 ServiceWorkerDatabase::WriteUncommittedResourceIds(const std::set
<int64
>& ids
) {
1028 return WriteResourceIds(kUncommittedResIdKeyPrefix
, ids
);
1031 ServiceWorkerDatabase::Status
1032 ServiceWorkerDatabase::ClearUncommittedResourceIds(const std::set
<int64
>& ids
) {
1033 return DeleteResourceIds(kUncommittedResIdKeyPrefix
, ids
);
1036 ServiceWorkerDatabase::Status
1037 ServiceWorkerDatabase::GetPurgeableResourceIds(std::set
<int64
>* ids
) {
1038 return ReadResourceIds(kPurgeableResIdKeyPrefix
, ids
);
1041 ServiceWorkerDatabase::Status
1042 ServiceWorkerDatabase::WritePurgeableResourceIds(const std::set
<int64
>& ids
) {
1043 return WriteResourceIds(kPurgeableResIdKeyPrefix
, ids
);
1046 ServiceWorkerDatabase::Status
1047 ServiceWorkerDatabase::ClearPurgeableResourceIds(const std::set
<int64
>& ids
) {
1048 return DeleteResourceIds(kPurgeableResIdKeyPrefix
, ids
);
1051 ServiceWorkerDatabase::Status
1052 ServiceWorkerDatabase::PurgeUncommittedResourceIds(
1053 const std::set
<int64
>& ids
) {
1054 leveldb::WriteBatch batch
;
1055 Status status
= DeleteResourceIdsInBatch(
1056 kUncommittedResIdKeyPrefix
, ids
, &batch
);
1057 if (status
!= STATUS_OK
)
1059 status
= WriteResourceIdsInBatch(kPurgeableResIdKeyPrefix
, ids
, &batch
);
1060 if (status
!= STATUS_OK
)
1062 return WriteBatch(&batch
);
1065 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::DeleteAllDataForOrigins(
1066 const std::set
<GURL
>& origins
,
1067 std::vector
<int64
>* newly_purgeable_resources
) {
1068 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
1069 Status status
= LazyOpen(false);
1070 if (IsNewOrNonexistentDatabase(status
))
1072 if (status
!= STATUS_OK
)
1074 leveldb::WriteBatch batch
;
1076 for (const GURL
& origin
: origins
) {
1077 if (!origin
.is_valid())
1078 return STATUS_ERROR_FAILED
;
1080 // Delete from the unique origin list.
1081 batch
.Delete(CreateUniqueOriginKey(origin
));
1083 std::vector
<RegistrationData
> registrations
;
1084 status
= GetRegistrationsForOrigin(origin
, ®istrations
, nullptr);
1085 if (status
!= STATUS_OK
)
1088 // Delete registrations, resource records and user data.
1089 for (const RegistrationData
& data
: registrations
) {
1090 batch
.Delete(CreateRegistrationKey(data
.registration_id
, origin
));
1091 batch
.Delete(CreateRegistrationIdToOriginKey(data
.registration_id
));
1093 status
= DeleteResourceRecords(
1094 data
.version_id
, newly_purgeable_resources
, &batch
);
1095 if (status
!= STATUS_OK
)
1098 status
= DeleteUserDataForRegistration(data
.registration_id
, &batch
);
1099 if (status
!= STATUS_OK
)
1104 return WriteBatch(&batch
);
1107 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::DestroyDatabase() {
1108 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
1109 Disable(FROM_HERE
, STATUS_OK
);
1111 if (IsDatabaseInMemory()) {
1116 // Directly delete the database directory instead of leveldb::DestroyDB()
1117 // because the API does not delete the directory if there are unrelated files.
1118 // (https://code.google.com/p/chromium/issues/detail?id=468926#c24)
1119 Status status
= base::DeleteFile(path_
, true /* recursive */)
1121 : STATUS_ERROR_FAILED
;
1122 ServiceWorkerMetrics::RecordDestroyDatabaseResult(status
);
1126 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::LazyOpen(
1127 bool create_if_missing
) {
1128 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
1130 // Do not try to open a database if we tried and failed once.
1131 if (state_
== DISABLED
)
1132 return STATUS_ERROR_FAILED
;
1136 if (!create_if_missing
) {
1137 // Avoid opening a database if it does not exist at the |path_|.
1138 if (IsDatabaseInMemory() || !base::PathExists(path_
) ||
1139 base::IsDirectoryEmpty(path_
)) {
1140 return STATUS_ERROR_NOT_FOUND
;
1144 leveldb::Options options
;
1145 options
.create_if_missing
= create_if_missing
;
1146 options
.reuse_logs
= leveldb_env::kDefaultLogReuseOptionValue
;
1147 if (IsDatabaseInMemory()) {
1148 env_
.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
1149 options
.env
= env_
.get();
1151 options
.env
= g_service_worker_env
.Pointer();
1154 leveldb::DB
* db
= NULL
;
1155 Status status
= LevelDBStatusToStatus(
1156 leveldb::DB::Open(options
, path_
.AsUTF8Unsafe(), &db
));
1157 HandleOpenResult(FROM_HERE
, status
);
1158 if (status
!= STATUS_OK
) {
1160 // TODO(nhiroki): Should we retry to open the database?
1166 status
= ReadDatabaseVersion(&db_version
);
1167 if (status
!= STATUS_OK
)
1169 DCHECK_LE(0, db_version
);
1171 if (db_version
> 0 && db_version
< kCurrentSchemaVersion
) {
1172 switch (db_version
) {
1174 status
= UpgradeDatabaseSchemaFromV1ToV2();
1175 if (status
!= STATUS_OK
)
1178 // Intentionally fall-through to other version upgrade cases.
1180 // Either the database got upgraded to the current schema version, or some
1181 // upgrade step failed which would have caused this method to abort.
1182 DCHECK_EQ(db_version
, kCurrentSchemaVersion
);
1186 state_
= INITIALIZED
;
1190 bool ServiceWorkerDatabase::IsNewOrNonexistentDatabase(
1191 ServiceWorkerDatabase::Status status
) {
1192 if (status
== STATUS_ERROR_NOT_FOUND
)
1194 if (status
== STATUS_OK
&& state_
== UNINITIALIZED
)
1199 ServiceWorkerDatabase::Status
1200 ServiceWorkerDatabase::UpgradeDatabaseSchemaFromV1ToV2() {
1201 Status status
= STATUS_OK
;
1202 leveldb::WriteBatch batch
;
1204 // Version 2 introduced REGID_TO_ORIGIN, add for all existing registrations.
1205 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
1206 for (itr
->Seek(kRegKeyPrefix
); itr
->Valid(); itr
->Next()) {
1207 status
= LevelDBStatusToStatus(itr
->status());
1208 if (status
!= STATUS_OK
) {
1209 HandleReadResult(FROM_HERE
, status
);
1214 if (!RemovePrefix(itr
->key().ToString(), kRegKeyPrefix
, &key
))
1217 std::vector
<std::string
> parts
;
1218 base::SplitStringDontTrim(key
, kKeySeparator
, &parts
);
1219 if (parts
.size() != 2) {
1220 status
= STATUS_ERROR_CORRUPTED
;
1221 HandleReadResult(FROM_HERE
, status
);
1225 int64 registration_id
;
1226 status
= ParseId(parts
[1], ®istration_id
);
1227 if (status
!= STATUS_OK
) {
1228 HandleReadResult(FROM_HERE
, status
);
1232 batch
.Put(CreateRegistrationIdToOriginKey(registration_id
), parts
[0]);
1235 // Update schema version manually instead of relying on WriteBatch to make
1236 // sure each upgrade step only updates it to the actually correct version.
1237 batch
.Put(kDatabaseVersionKey
, base::Int64ToString(2));
1238 status
= LevelDBStatusToStatus(
1239 db_
->Write(leveldb::WriteOptions(), &batch
));
1240 HandleWriteResult(FROM_HERE
, status
);
1244 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::ReadNextAvailableId(
1246 int64
* next_avail_id
) {
1248 DCHECK(next_avail_id
);
1251 Status status
= LevelDBStatusToStatus(
1252 db_
->Get(leveldb::ReadOptions(), id_key
, &value
));
1253 if (status
== STATUS_ERROR_NOT_FOUND
) {
1254 // Nobody has gotten the next resource id for |id_key|.
1256 HandleReadResult(FROM_HERE
, STATUS_OK
);
1258 } else if (status
!= STATUS_OK
) {
1259 HandleReadResult(FROM_HERE
, status
);
1263 status
= ParseId(value
, next_avail_id
);
1264 HandleReadResult(FROM_HERE
, status
);
1268 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::ReadRegistrationData(
1269 int64 registration_id
,
1271 RegistrationData
* registration
) {
1272 DCHECK(registration
);
1274 const std::string key
= CreateRegistrationKey(registration_id
, origin
);
1276 Status status
= LevelDBStatusToStatus(
1277 db_
->Get(leveldb::ReadOptions(), key
, &value
));
1278 if (status
!= STATUS_OK
) {
1281 status
== STATUS_ERROR_NOT_FOUND
? STATUS_OK
: status
);
1285 status
= ParseRegistrationData(value
, registration
);
1286 HandleReadResult(FROM_HERE
, status
);
1290 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::ReadResourceRecords(
1292 std::vector
<ResourceRecord
>* resources
) {
1293 DCHECK(resources
->empty());
1295 Status status
= STATUS_OK
;
1296 const std::string prefix
= CreateResourceRecordKeyPrefix(version_id
);
1298 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
1299 for (itr
->Seek(prefix
); itr
->Valid(); itr
->Next()) {
1300 Status status
= LevelDBStatusToStatus(itr
->status());
1301 if (status
!= STATUS_OK
) {
1302 HandleReadResult(FROM_HERE
, status
);
1307 if (!RemovePrefix(itr
->key().ToString(), prefix
, NULL
))
1310 ResourceRecord resource
;
1311 status
= ParseResourceRecord(itr
->value().ToString(), &resource
);
1312 if (status
!= STATUS_OK
) {
1313 HandleReadResult(FROM_HERE
, status
);
1317 resources
->push_back(resource
);
1320 HandleReadResult(FROM_HERE
, status
);
1324 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::DeleteResourceRecords(
1326 std::vector
<int64
>* newly_purgeable_resources
,
1327 leveldb::WriteBatch
* batch
) {
1330 Status status
= STATUS_OK
;
1331 const std::string prefix
= CreateResourceRecordKeyPrefix(version_id
);
1333 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
1334 for (itr
->Seek(prefix
); itr
->Valid(); itr
->Next()) {
1335 status
= LevelDBStatusToStatus(itr
->status());
1336 if (status
!= STATUS_OK
) {
1337 HandleReadResult(FROM_HERE
, status
);
1341 const std::string key
= itr
->key().ToString();
1342 std::string unprefixed
;
1343 if (!RemovePrefix(key
, prefix
, &unprefixed
))
1347 status
= ParseId(unprefixed
, &resource_id
);
1348 if (status
!= STATUS_OK
) {
1349 HandleReadResult(FROM_HERE
, status
);
1353 // Remove a resource record.
1356 // Currently resource sharing across versions and registrations is not
1357 // supported, so we can purge this without caring about it.
1358 PutPurgeableResourceIdToBatch(resource_id
, batch
);
1359 newly_purgeable_resources
->push_back(resource_id
);
1362 HandleReadResult(FROM_HERE
, status
);
1366 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::ReadResourceIds(
1367 const char* id_key_prefix
,
1368 std::set
<int64
>* ids
) {
1369 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
1370 DCHECK(id_key_prefix
);
1371 DCHECK(ids
->empty());
1373 Status status
= LazyOpen(false);
1374 if (IsNewOrNonexistentDatabase(status
))
1376 if (status
!= STATUS_OK
)
1379 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
1380 for (itr
->Seek(id_key_prefix
); itr
->Valid(); itr
->Next()) {
1381 status
= LevelDBStatusToStatus(itr
->status());
1382 if (status
!= STATUS_OK
) {
1383 HandleReadResult(FROM_HERE
, status
);
1388 std::string unprefixed
;
1389 if (!RemovePrefix(itr
->key().ToString(), id_key_prefix
, &unprefixed
))
1393 status
= ParseId(unprefixed
, &resource_id
);
1394 if (status
!= STATUS_OK
) {
1395 HandleReadResult(FROM_HERE
, status
);
1399 ids
->insert(resource_id
);
1402 HandleReadResult(FROM_HERE
, status
);
1406 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::WriteResourceIds(
1407 const char* id_key_prefix
,
1408 const std::set
<int64
>& ids
) {
1409 leveldb::WriteBatch batch
;
1410 Status status
= WriteResourceIdsInBatch(id_key_prefix
, ids
, &batch
);
1411 if (status
!= STATUS_OK
)
1413 return WriteBatch(&batch
);
1416 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::WriteResourceIdsInBatch(
1417 const char* id_key_prefix
,
1418 const std::set
<int64
>& ids
,
1419 leveldb::WriteBatch
* batch
) {
1420 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
1421 DCHECK(id_key_prefix
);
1423 Status status
= LazyOpen(true);
1424 if (status
!= STATUS_OK
)
1429 for (std::set
<int64
>::const_iterator itr
= ids
.begin();
1430 itr
!= ids
.end(); ++itr
) {
1431 // Value should be empty.
1432 batch
->Put(CreateResourceIdKey(id_key_prefix
, *itr
), "");
1434 // std::set is sorted, so the last element is the largest.
1435 BumpNextResourceIdIfNeeded(*ids
.rbegin(), batch
);
1439 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::DeleteResourceIds(
1440 const char* id_key_prefix
,
1441 const std::set
<int64
>& ids
) {
1442 leveldb::WriteBatch batch
;
1443 Status status
= DeleteResourceIdsInBatch(id_key_prefix
, ids
, &batch
);
1444 if (status
!= STATUS_OK
)
1446 return WriteBatch(&batch
);
1449 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::DeleteResourceIdsInBatch(
1450 const char* id_key_prefix
,
1451 const std::set
<int64
>& ids
,
1452 leveldb::WriteBatch
* batch
) {
1453 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
1454 DCHECK(id_key_prefix
);
1456 Status status
= LazyOpen(false);
1457 if (IsNewOrNonexistentDatabase(status
))
1459 if (status
!= STATUS_OK
)
1462 for (std::set
<int64
>::const_iterator itr
= ids
.begin();
1463 itr
!= ids
.end(); ++itr
) {
1464 batch
->Delete(CreateResourceIdKey(id_key_prefix
, *itr
));
1469 ServiceWorkerDatabase::Status
1470 ServiceWorkerDatabase::DeleteUserDataForRegistration(
1471 int64 registration_id
,
1472 leveldb::WriteBatch
* batch
) {
1474 Status status
= STATUS_OK
;
1475 const std::string prefix
= CreateUserDataKeyPrefix(registration_id
);
1477 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
1478 for (itr
->Seek(prefix
); itr
->Valid(); itr
->Next()) {
1479 status
= LevelDBStatusToStatus(itr
->status());
1480 if (status
!= STATUS_OK
) {
1481 HandleReadResult(FROM_HERE
, status
);
1485 const std::string key
= itr
->key().ToString();
1486 std::string user_data_name
;
1487 if (!RemovePrefix(key
, prefix
, &user_data_name
))
1490 batch
->Delete(CreateHasUserDataKey(registration_id
, user_data_name
));
1495 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::ReadDatabaseVersion(
1496 int64
* db_version
) {
1498 Status status
= LevelDBStatusToStatus(
1499 db_
->Get(leveldb::ReadOptions(), kDatabaseVersionKey
, &value
));
1500 if (status
== STATUS_ERROR_NOT_FOUND
) {
1501 // The database hasn't been initialized yet.
1503 HandleReadResult(FROM_HERE
, STATUS_OK
);
1507 if (status
!= STATUS_OK
) {
1508 HandleReadResult(FROM_HERE
, status
);
1512 status
= ParseDatabaseVersion(value
, db_version
);
1513 HandleReadResult(FROM_HERE
, status
);
1517 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::WriteBatch(
1518 leveldb::WriteBatch
* batch
) {
1520 DCHECK_NE(DISABLED
, state_
);
1522 if (state_
== UNINITIALIZED
) {
1523 // Write database default values.
1524 batch
->Put(kDatabaseVersionKey
, base::Int64ToString(kCurrentSchemaVersion
));
1525 if (!skip_writing_diskcache_migration_state_on_init_for_testing_
)
1526 batch
->Put(kDiskCacheMigrationNotNeededKey
, kEmptyValue
);
1527 state_
= INITIALIZED
;
1530 Status status
= LevelDBStatusToStatus(
1531 db_
->Write(leveldb::WriteOptions(), batch
));
1532 HandleWriteResult(FROM_HERE
, status
);
1536 void ServiceWorkerDatabase::BumpNextRegistrationIdIfNeeded(
1537 int64 used_id
, leveldb::WriteBatch
* batch
) {
1539 if (next_avail_registration_id_
<= used_id
) {
1540 next_avail_registration_id_
= used_id
+ 1;
1541 batch
->Put(kNextRegIdKey
, base::Int64ToString(next_avail_registration_id_
));
1545 void ServiceWorkerDatabase::BumpNextResourceIdIfNeeded(
1546 int64 used_id
, leveldb::WriteBatch
* batch
) {
1548 if (next_avail_resource_id_
<= used_id
) {
1549 next_avail_resource_id_
= used_id
+ 1;
1550 batch
->Put(kNextResIdKey
, base::Int64ToString(next_avail_resource_id_
));
1554 void ServiceWorkerDatabase::BumpNextVersionIdIfNeeded(
1555 int64 used_id
, leveldb::WriteBatch
* batch
) {
1557 if (next_avail_version_id_
<= used_id
) {
1558 next_avail_version_id_
= used_id
+ 1;
1559 batch
->Put(kNextVerIdKey
, base::Int64ToString(next_avail_version_id_
));
1563 bool ServiceWorkerDatabase::IsOpen() {
1567 void ServiceWorkerDatabase::Disable(
1568 const tracked_objects::Location
& from_here
,
1570 if (status
!= STATUS_OK
) {
1571 DLOG(ERROR
) << "Failed at: " << from_here
.ToString()
1572 << " with error: " << StatusToString(status
);
1573 DLOG(ERROR
) << "ServiceWorkerDatabase is disabled.";
1579 void ServiceWorkerDatabase::HandleOpenResult(
1580 const tracked_objects::Location
& from_here
,
1582 if (status
!= STATUS_OK
)
1583 Disable(from_here
, status
);
1584 ServiceWorkerMetrics::CountOpenDatabaseResult(status
);
1587 void ServiceWorkerDatabase::HandleReadResult(
1588 const tracked_objects::Location
& from_here
,
1590 if (status
!= STATUS_OK
)
1591 Disable(from_here
, status
);
1592 ServiceWorkerMetrics::CountReadDatabaseResult(status
);
1595 void ServiceWorkerDatabase::HandleWriteResult(
1596 const tracked_objects::Location
& from_here
,
1598 if (status
!= STATUS_OK
)
1599 Disable(from_here
, status
);
1600 ServiceWorkerMetrics::CountWriteDatabaseResult(status
);
1603 bool ServiceWorkerDatabase::IsDatabaseInMemory() const {
1604 return path_
.empty();
1607 } // namespace content