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::SplitString(key
, std::string(1, kKeySeparator
),
1219 base::KEEP_WHITESPACE
, base::SPLIT_WANT_ALL
);
1220 if (parts
.size() != 2) {
1221 status
= STATUS_ERROR_CORRUPTED
;
1222 HandleReadResult(FROM_HERE
, status
);
1226 int64 registration_id
;
1227 status
= ParseId(parts
[1], ®istration_id
);
1228 if (status
!= STATUS_OK
) {
1229 HandleReadResult(FROM_HERE
, status
);
1233 batch
.Put(CreateRegistrationIdToOriginKey(registration_id
), parts
[0]);
1236 // Update schema version manually instead of relying on WriteBatch to make
1237 // sure each upgrade step only updates it to the actually correct version.
1238 batch
.Put(kDatabaseVersionKey
, base::Int64ToString(2));
1239 status
= LevelDBStatusToStatus(
1240 db_
->Write(leveldb::WriteOptions(), &batch
));
1241 HandleWriteResult(FROM_HERE
, status
);
1245 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::ReadNextAvailableId(
1247 int64
* next_avail_id
) {
1249 DCHECK(next_avail_id
);
1252 Status status
= LevelDBStatusToStatus(
1253 db_
->Get(leveldb::ReadOptions(), id_key
, &value
));
1254 if (status
== STATUS_ERROR_NOT_FOUND
) {
1255 // Nobody has gotten the next resource id for |id_key|.
1257 HandleReadResult(FROM_HERE
, STATUS_OK
);
1259 } else if (status
!= STATUS_OK
) {
1260 HandleReadResult(FROM_HERE
, status
);
1264 status
= ParseId(value
, next_avail_id
);
1265 HandleReadResult(FROM_HERE
, status
);
1269 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::ReadRegistrationData(
1270 int64 registration_id
,
1272 RegistrationData
* registration
) {
1273 DCHECK(registration
);
1275 const std::string key
= CreateRegistrationKey(registration_id
, origin
);
1277 Status status
= LevelDBStatusToStatus(
1278 db_
->Get(leveldb::ReadOptions(), key
, &value
));
1279 if (status
!= STATUS_OK
) {
1282 status
== STATUS_ERROR_NOT_FOUND
? STATUS_OK
: status
);
1286 status
= ParseRegistrationData(value
, registration
);
1287 HandleReadResult(FROM_HERE
, status
);
1291 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::ReadResourceRecords(
1293 std::vector
<ResourceRecord
>* resources
) {
1294 DCHECK(resources
->empty());
1296 Status status
= STATUS_OK
;
1297 const std::string prefix
= CreateResourceRecordKeyPrefix(version_id
);
1299 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
1300 for (itr
->Seek(prefix
); itr
->Valid(); itr
->Next()) {
1301 Status status
= LevelDBStatusToStatus(itr
->status());
1302 if (status
!= STATUS_OK
) {
1303 HandleReadResult(FROM_HERE
, status
);
1308 if (!RemovePrefix(itr
->key().ToString(), prefix
, NULL
))
1311 ResourceRecord resource
;
1312 status
= ParseResourceRecord(itr
->value().ToString(), &resource
);
1313 if (status
!= STATUS_OK
) {
1314 HandleReadResult(FROM_HERE
, status
);
1318 resources
->push_back(resource
);
1321 HandleReadResult(FROM_HERE
, status
);
1325 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::DeleteResourceRecords(
1327 std::vector
<int64
>* newly_purgeable_resources
,
1328 leveldb::WriteBatch
* batch
) {
1331 Status status
= STATUS_OK
;
1332 const std::string prefix
= CreateResourceRecordKeyPrefix(version_id
);
1334 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
1335 for (itr
->Seek(prefix
); itr
->Valid(); itr
->Next()) {
1336 status
= LevelDBStatusToStatus(itr
->status());
1337 if (status
!= STATUS_OK
) {
1338 HandleReadResult(FROM_HERE
, status
);
1342 const std::string key
= itr
->key().ToString();
1343 std::string unprefixed
;
1344 if (!RemovePrefix(key
, prefix
, &unprefixed
))
1348 status
= ParseId(unprefixed
, &resource_id
);
1349 if (status
!= STATUS_OK
) {
1350 HandleReadResult(FROM_HERE
, status
);
1354 // Remove a resource record.
1357 // Currently resource sharing across versions and registrations is not
1358 // supported, so we can purge this without caring about it.
1359 PutPurgeableResourceIdToBatch(resource_id
, batch
);
1360 newly_purgeable_resources
->push_back(resource_id
);
1363 HandleReadResult(FROM_HERE
, status
);
1367 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::ReadResourceIds(
1368 const char* id_key_prefix
,
1369 std::set
<int64
>* ids
) {
1370 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
1371 DCHECK(id_key_prefix
);
1372 DCHECK(ids
->empty());
1374 Status status
= LazyOpen(false);
1375 if (IsNewOrNonexistentDatabase(status
))
1377 if (status
!= STATUS_OK
)
1380 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
1381 for (itr
->Seek(id_key_prefix
); itr
->Valid(); itr
->Next()) {
1382 status
= LevelDBStatusToStatus(itr
->status());
1383 if (status
!= STATUS_OK
) {
1384 HandleReadResult(FROM_HERE
, status
);
1389 std::string unprefixed
;
1390 if (!RemovePrefix(itr
->key().ToString(), id_key_prefix
, &unprefixed
))
1394 status
= ParseId(unprefixed
, &resource_id
);
1395 if (status
!= STATUS_OK
) {
1396 HandleReadResult(FROM_HERE
, status
);
1400 ids
->insert(resource_id
);
1403 HandleReadResult(FROM_HERE
, status
);
1407 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::WriteResourceIds(
1408 const char* id_key_prefix
,
1409 const std::set
<int64
>& ids
) {
1410 leveldb::WriteBatch batch
;
1411 Status status
= WriteResourceIdsInBatch(id_key_prefix
, ids
, &batch
);
1412 if (status
!= STATUS_OK
)
1414 return WriteBatch(&batch
);
1417 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::WriteResourceIdsInBatch(
1418 const char* id_key_prefix
,
1419 const std::set
<int64
>& ids
,
1420 leveldb::WriteBatch
* batch
) {
1421 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
1422 DCHECK(id_key_prefix
);
1424 Status status
= LazyOpen(true);
1425 if (status
!= STATUS_OK
)
1430 for (std::set
<int64
>::const_iterator itr
= ids
.begin();
1431 itr
!= ids
.end(); ++itr
) {
1432 // Value should be empty.
1433 batch
->Put(CreateResourceIdKey(id_key_prefix
, *itr
), "");
1435 // std::set is sorted, so the last element is the largest.
1436 BumpNextResourceIdIfNeeded(*ids
.rbegin(), batch
);
1440 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::DeleteResourceIds(
1441 const char* id_key_prefix
,
1442 const std::set
<int64
>& ids
) {
1443 leveldb::WriteBatch batch
;
1444 Status status
= DeleteResourceIdsInBatch(id_key_prefix
, ids
, &batch
);
1445 if (status
!= STATUS_OK
)
1447 return WriteBatch(&batch
);
1450 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::DeleteResourceIdsInBatch(
1451 const char* id_key_prefix
,
1452 const std::set
<int64
>& ids
,
1453 leveldb::WriteBatch
* batch
) {
1454 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
1455 DCHECK(id_key_prefix
);
1457 Status status
= LazyOpen(false);
1458 if (IsNewOrNonexistentDatabase(status
))
1460 if (status
!= STATUS_OK
)
1463 for (std::set
<int64
>::const_iterator itr
= ids
.begin();
1464 itr
!= ids
.end(); ++itr
) {
1465 batch
->Delete(CreateResourceIdKey(id_key_prefix
, *itr
));
1470 ServiceWorkerDatabase::Status
1471 ServiceWorkerDatabase::DeleteUserDataForRegistration(
1472 int64 registration_id
,
1473 leveldb::WriteBatch
* batch
) {
1475 Status status
= STATUS_OK
;
1476 const std::string prefix
= CreateUserDataKeyPrefix(registration_id
);
1478 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
1479 for (itr
->Seek(prefix
); itr
->Valid(); itr
->Next()) {
1480 status
= LevelDBStatusToStatus(itr
->status());
1481 if (status
!= STATUS_OK
) {
1482 HandleReadResult(FROM_HERE
, status
);
1486 const std::string key
= itr
->key().ToString();
1487 std::string user_data_name
;
1488 if (!RemovePrefix(key
, prefix
, &user_data_name
))
1491 batch
->Delete(CreateHasUserDataKey(registration_id
, user_data_name
));
1496 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::ReadDatabaseVersion(
1497 int64
* db_version
) {
1499 Status status
= LevelDBStatusToStatus(
1500 db_
->Get(leveldb::ReadOptions(), kDatabaseVersionKey
, &value
));
1501 if (status
== STATUS_ERROR_NOT_FOUND
) {
1502 // The database hasn't been initialized yet.
1504 HandleReadResult(FROM_HERE
, STATUS_OK
);
1508 if (status
!= STATUS_OK
) {
1509 HandleReadResult(FROM_HERE
, status
);
1513 status
= ParseDatabaseVersion(value
, db_version
);
1514 HandleReadResult(FROM_HERE
, status
);
1518 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::WriteBatch(
1519 leveldb::WriteBatch
* batch
) {
1521 DCHECK_NE(DISABLED
, state_
);
1523 if (state_
== UNINITIALIZED
) {
1524 // Write database default values.
1525 batch
->Put(kDatabaseVersionKey
, base::Int64ToString(kCurrentSchemaVersion
));
1526 if (!skip_writing_diskcache_migration_state_on_init_for_testing_
)
1527 batch
->Put(kDiskCacheMigrationNotNeededKey
, kEmptyValue
);
1528 state_
= INITIALIZED
;
1531 Status status
= LevelDBStatusToStatus(
1532 db_
->Write(leveldb::WriteOptions(), batch
));
1533 HandleWriteResult(FROM_HERE
, status
);
1537 void ServiceWorkerDatabase::BumpNextRegistrationIdIfNeeded(
1538 int64 used_id
, leveldb::WriteBatch
* batch
) {
1540 if (next_avail_registration_id_
<= used_id
) {
1541 next_avail_registration_id_
= used_id
+ 1;
1542 batch
->Put(kNextRegIdKey
, base::Int64ToString(next_avail_registration_id_
));
1546 void ServiceWorkerDatabase::BumpNextResourceIdIfNeeded(
1547 int64 used_id
, leveldb::WriteBatch
* batch
) {
1549 if (next_avail_resource_id_
<= used_id
) {
1550 next_avail_resource_id_
= used_id
+ 1;
1551 batch
->Put(kNextResIdKey
, base::Int64ToString(next_avail_resource_id_
));
1555 void ServiceWorkerDatabase::BumpNextVersionIdIfNeeded(
1556 int64 used_id
, leveldb::WriteBatch
* batch
) {
1558 if (next_avail_version_id_
<= used_id
) {
1559 next_avail_version_id_
= used_id
+ 1;
1560 batch
->Put(kNextVerIdKey
, base::Int64ToString(next_avail_version_id_
));
1564 bool ServiceWorkerDatabase::IsOpen() {
1568 void ServiceWorkerDatabase::Disable(
1569 const tracked_objects::Location
& from_here
,
1571 if (status
!= STATUS_OK
) {
1572 DLOG(ERROR
) << "Failed at: " << from_here
.ToString()
1573 << " with error: " << StatusToString(status
);
1574 DLOG(ERROR
) << "ServiceWorkerDatabase is disabled.";
1580 void ServiceWorkerDatabase::HandleOpenResult(
1581 const tracked_objects::Location
& from_here
,
1583 if (status
!= STATUS_OK
)
1584 Disable(from_here
, status
);
1585 ServiceWorkerMetrics::CountOpenDatabaseResult(status
);
1588 void ServiceWorkerDatabase::HandleReadResult(
1589 const tracked_objects::Location
& from_here
,
1591 if (status
!= STATUS_OK
)
1592 Disable(from_here
, status
);
1593 ServiceWorkerMetrics::CountReadDatabaseResult(status
);
1596 void ServiceWorkerDatabase::HandleWriteResult(
1597 const tracked_objects::Location
& from_here
,
1599 if (status
!= STATUS_OK
)
1600 Disable(from_here
, status
);
1601 ServiceWorkerMetrics::CountWriteDatabaseResult(status
);
1604 bool ServiceWorkerDatabase::IsDatabaseInMemory() const {
1605 return path_
.empty();
1608 } // namespace content